The Rails State Machine
Ruby on Rails recently added a built-in ActiveRecord::StateMachine implementation and even more recently tied it in to ActiveRecord. And, for being a built-in library, it’s pretty damned fully-featured. Really, if this one doesn’t do it for you, then you probably need to write it yourself, anyway.
And, if you’re thinking that you don’t know what a state machine is, or think that you’ve never used one, think again. If you’ve ever used the restful_authentication plugin, you’ve probably used a state machine. In that library, Users can be pending, active, inactive &mdash It’s all the same object, just acting differently depending on its current situation. That, in a nutshell, is a state machine.
Let’s start with an example that most people might recognize:
class TrafficLight < ActiveRecord::Base
include ActiveRecord::StateMachine
state_machine do
state :red
state :green
state :yellow
event :change_color do
transitions :to => :red, :from => [:yellow], :on_transition => :catch_runners
transitions :to => :yellow, :from => [:green]
transitions :to => :green, :from => [:red]
end
end
def catch_runners
puts "That'll be $250."
end
end
light = TrafficLight.new
light.current_state #=> :red
light.change_color! #=> true
light.current_state #=> :green
light.change_color! #=> true
light.current_state #=> :yellow
light.change_color! #=> true
"That'll be $250."
So, that’s a basic state machine. The light changes from red to green to yellow to red. That’s it. It’s a boring life for a stop light. Certainly, this isn’t a glamorous example, but there are some interesting points here:
- The initial state for the TrafficLight is red, which is the first state defined.
- On a successful transition to red (from yellow), the local
catch_runnersmethod is executed. - The model acts differently depending on its current state, for instance, the
change_color!method has a different action depending on the current color of the light.
And that’s not all. The built-in StateMachine also supports success callbacks per event, enter and exit callbacks per state, guards on transitions, and a lot more:
event :sample, :success => :we_win do; ...; end
state :open, :enter => [:alert_twitter, :send_emails], :exit => :alert_twitter
event :close do # You may only close the store if the safe is locked!! transitions :to => :closed, :from => :open, :guard => :safe_locked? end
So, if you’ve ever used the AASM plugin, you’ll probably feel right at home here with the ActiveRecord::StateMachine. I’m finding myself quite comfortable. In fact, I just might relocate.
- Nathaniel Bibler