In an introductory chapter about DSL, Martin Fowler mentions the state machine model. Now here is how you can implement a simple finite state machine. A FSM DSL can be defined for example like this (if we assign states, the first state is used as an initial value):
class StateMachineDSL
attr_accessor :state
def initialize
@states, @events = [], {}
end
def states
@states
end
def states=(values)
@states, @state = values, values.first
end
def event(name, transition)
@events[name] = transition
self.class.send(:define_method,"#{name}!") { change_state(name) }
end
def transitions
@events.reject{ |key,value| !value.keys.include?(@state.to_sym) }.keys
end
def change_state(event)
@state = @events[event][@state]
end
end
If we use the block technique mentioned before to apply the DSL in Ruby, we get the following class:
class MyClass
def self.apply_dsl(a_class, &block)
obj = a_class.new
yield obj if block_given?
return obj
end
end
Finally, the FSM can be defined in the DSL:
obj = MyClass.apply_dsl(StateMachineDSL) do |state_machine|
state_machine.states = [:submitted, :reviewing, :accepted, :rejected]
state_machine.event :submit, {:submitted => :reviewing}
state_machine.event :accept, {:reviewing => :accepted}
state_machine.event :reject, {:reviewing => :rejected}
state_machine.event :revise, {:rejected => :reviewing}
end
obj.state
=> :submitted
obj.submit!
=> :reviewing
obj.accept!
=> :accepted