SimpleMachine is module for Ruby which injects simple state machine behavior in any class that includes it. It can be defined on multiple fields in the same class
Install it with “gem install simple_machine” then require ‘simple_machine’ in your project, or if you use Bundler add this to your Gemfile:
gem "simple_machine"
After requiring ‘simple_machine’ inject state machine in your class like this:
require 'simple_machine' class Phone include SimpleMachine implement_state_machine_for :my_state do initial_state :off other_states :ready, :dialing, :busy allow_transition :turn_on, :from => :off, :to => :ready allow_transition :dial, :from => :ready, :to => :dialing allow_transition :hangup, :from => :dialing, :to => :ready allow_transition :hangup, :from => :busy, :to => :ready allow_transition :turn_off, :from => :ready, :to => :off do # self is set to phone instance puts my_state #=> :ready # do something before transition ... true # state will be changed to :off if block returns true end after_transition do # self is set to phone instance puts "New state is '#{my_state}'" end end end
This chunk of code produces following effects:
Phone.my_state_default_state #=> :off phone = Phone.new phone.my_state #=> :off phone.my_state_machine.allowed_transitions #=> [:turn_on] phone.my_state_machine.can_dial? #=> false phone.my_state_machine.dial #=> raises exception: 'Invalid transition #dial from 'off' state' phone.my_state_machine.can_turn_on? #=> true phone.my_state_machine.turn_on #=> :ready phone.my_state #=> :ready
You can also implement transition guards in your class like this:
class Phone def guard_for_dial_on_my_state; false; end end
In this case even if dial
is allowed transition from ready
state this is what will happen:
phone.my_state #=> :ready phone.my_state_machine.allowed_transitions #=> [:hangup] phone.my_state.machine.can_dial? #=> false phone.my_state_machine.dial #=> raises exception: 'Unable to dial due to guard'
After each transition callback is invoked if it is defined:
class Phone include SimpleMachine implement_state_machine_for :my_state do # ... after_transition do # self is set to phone instance puts "New state is '#{my_state}'" end end end
If you want to run tests you will need ‘rspec’ and ‘mocha’ gems
This library was made without any pretend to be big-enterprise-sega-mega solution for State machine, but I still hope you will find this small library useful in some cases (I already do :). If you have any issues or ideas feel free to fork the project and send a pull request.