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