Edge State Machine
Edge State Machine is a complete state machine solution. It offers support for ActiveRecord, Mongoid and MongoMapper for persistence.
<img src=“https://secure.travis-ci.org/danpersa/edge-state-machine.png”/>
Supported Features
-
Multiple state machines per class each of them acting independently
-
Find errors in state machine definitions as early as possible
-
Transition guards
-
Multiple actions executed on transitions
-
Multiple actions executed on entering and exiting a state
-
No other dependencies for non-persistent state machines
-
Minimal dependencies for persistent ones
Installation
If you’re using Rails + ActiveRecord + Bundler
# in your Gemfile
gem "edge-state-machine", :require => ["edge-state-machine", "active_record/edge-state-machine"]
# in your AR models that will use the state machine
include ::EdgeStateMachine
include ActiveRecord::EdgeStateMachine
state_machine do
state :available # first one is initial state
state :out_of_stock
state :discontinue
event :discontinue do
transitions :to => :discontinue, :from => [:available, :out_of_stock], :on_transition => :do_discontinue
end
event :out_of_stock do
transitions :to => :out_of_stock, :from => [:available, :discontinue]
end
event :available do
transitions :to => :available, :from => [:out_of_stock], :on_transition => :send_alerts
end
end
If you’re using Rails + Mongoid + Bundler
# in your Gemfile
gem "edge-state-machine", :require => ["edge-state-machine", "mongoid/edge-state-machine"]
# in your AR models that will use the state machine
include ::EdgeStateMachine
include Mongoid::EdgeStateMachine
state_machine do
state :available # first one is initial state
state :out_of_stock
state :discontinue
event :discontinue do
transitions :to => :discontinue, :from => [:available, :out_of_stock], :on_transition => :do_discontinue
end
event :out_of_stock do
transitions :to => :out_of_stock, :from => [:available, :discontinue]
end
event :available do
transitions :to => :available, :from => [:out_of_stock], :on_transition => :send_alerts
end
end
If you’re using Rails + MongoMapper + Bundler
# in your Gemfile
gem "edge-state-machine", :require => ["edge-state-machine", "mongo_mapper/edge-state-machine"]
# in your models that will use the state machine
include ::EdgeStateMachine
include MongoMapper::EdgeStateMachine
state_machine do
state :available # first one is initial state
state :out_of_stock
state :discontinue
event :discontinue do
transitions :to => :discontinue, :from => [:available, :out_of_stock], :on_transition => :do_discontinue
end
event :out_of_stock do
transitions :to => :out_of_stock, :from => [:available, :discontinue]
end
event :available do
transitions :to => :available, :from => [:out_of_stock], :on_transition => :send_alerts
end
end
State Machine Examples
Microwave State Machine
class Microwave
state_machine :microwave do # name should be optional, if the name is not present, it should have a default name
# we give state machines names, so we can pun many machines inside a class
initial_state :unplugged # initial state should be optional, if the initial state is not present, the initial state will be the first defined state
state :unplugged
state :plugged
state :door_opened do
enter :light_on # enter should be executed on entering the state
exit :light_off # exit method should be executed on exiting the state
end
state :door_closed
state :started_in_grill_mode do
enter lambda { |t| p "Entering hate" } # should have support for Procs
exit :grill_off
end
state :started do
enter :microwaves_on
exit :microwaves_off
end
event :plug_in do
transition :from => :unplugged, :to => :plugged
end
event :open_door do
transition :from => :plugged, :to => :door_opened
end
event :close_door do
transition :from => :door_opened, :to => :door_closed,
:on_transition => :put_food_in_the_microwave # we can put many actions in an Array for the on_transition parameter
end
event :start do
transition :from => :door_closed, :to => [:started, :started_in_grill_mode],
:on_transition => :start_spinning_the_food,
:guard => :grill_button_pressed? # the method grill_button_pressed? should choose the next state
end
event :stop do
transition :from => [:started, :started_in_grill_mode], :to => :door_closed
end
end
end
Dice State Machine
class Dice
state_machine do
state :one
state :two
state :three
state :four
state :five
state :six
event :roll do
transition :from => [:one, :two, :three, :four, :five, :six],
:to => [:one, :two, :three, :four, :five, :six],
:guard => :roll_result,
:on_transition => :display_dice_rolling_animation
end
end
def roll_result
# return one of the states
end
def display_dice_rolling_animation
# draw the new position of the dice
end
end
class User
state_machine do
state :pending # first one is initial state
state :active
state :blocked # the user in this state can't sign in
event :activate do
transition :from => [:pending], :to => :active, :on_transition => :do_activate
end
event :block do
transition :from => [:pending, :active], :to => :blocked
end
end
end
Other Examples
For other (more complex) examples, please check the following links:
Notes
For classes with multiple state machines, the state names, machine names must be unique per class.
The same thing with the event names.
Credits
The gem is based on Rick Olson’s code of ActiveModel::StateMachine, axed from ActiveModel in this commit.
And on Krzysiek Heród’s gem, Transitions, which added Mongoid support.