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