require 'test_helper' class Water attr_accessor(:state_of_material) attr_accessor(:temperature) include FSM define_fsm do # now define all the states # you can add :enter / :exit callbacks (callback can be a String, Symbol or Proc) # these callbacks are triggered on any transition from/to this state and do not receive any arguments states(:gas, :liquid) state(:solid, :enter => :on_enter_solid, :exit => :on_exit_solid) # define all valid transitions (name, from, to). This will define a method with the given name. # you can define :event callback which is called only on this transition and receives the arguments passed to the # transition method transition(:heat_up, :solid, :liquid, :event => :on_heat_up, :guard => :guard_solid_to_liquid) transition(:heat_up, :liquid, :gas, :event => :on_heat_up, :guard => :guard_liquid_to_gas) transition(:cool_down, :gas, :liquid, :event => :on_cool_down, :guard => :guard_gas_to_liquid) transition(:cool_down, :liquid, :solid, :event => :on_cool_down, :guard => :guard_liquid_to_solid) # define the attribute which is used to store the state (defaults to :state) state_attribute(:state_of_material) # define the initial state (defaults to the first state defined - :gas in this sample) initial(:solid) end def initialize(temperature = -20) self.temperature = temperature end private def on_enter_solid() end def on_exit_solid() end def guard_solid_to_liquid(delta) self.temperature + delta >= 0 end def guard_liquid_to_gas(delta) self.temperature + delta >= 100 end def guard_gas_to_liquid(delta) self.temperature - delta < 100 end def guard_liquid_to_solid(delta) self.temperature - delta < 0 end def on_heat_up(delta) self.temperature += delta end def on_cool_down(delta) self.temperature -= delta end end class WaterSampleTest < Test::Unit::TestCase context 'Water' do should 'cycle through material states' do w = Water.new assert_equal(:solid, w.state_of_material) assert w.heat_up(30) assert_equal(:liquid, w.state_of_material) assert !w.heat_up(10) assert w.heat_up(90) assert_equal(:gas, w.state_of_material) assert w.cool_down(50) assert_equal(:liquid, w.state_of_material) assert w.cool_down(70) assert_equal(:solid, w.state_of_material) assert_raise(FSM::InvalidStateTransition) do w.cool_down end end end end