Managing States using state_machine
Often times the state of an object is tracked through boolean attributes, but this solution doesn’t scale well since each new state requires a new column in the table along with logic to govern how the states change. If you have a model which you need to track the states of, the state_machine gem can be a great solution. It stores the state of the object in a string-type column (
state in the example below), and adding states and transitions is as easy as adding a couple lines of code to the model.
Here I’ve created a ruby model to describe my dogs.
So with this in place we can fire up irb and create a new instance of the Dog class:
As you can see, since we set
initial: :sleeping as an argument in the call to
state_machine all new instances are created with the state of
"sleeping". One of the nice features of
state_machine is how easy it is to define events and how states should change as a result of calling an event. For example,
gonzo is sleeping now, so let’s see if we can go for a walk:
state_machine provides nice helper methods named
can_<event name>? in order to check if calling the event has a transition defined for the current state. Since gonzo’s asleep, he can’t go for a walk, so if we try calling
gonzo.walk we get false. First things first, we have to wake him up before taking him on a walk:
When declairing a state, you can pass it a block to define other methods as I’ve done with the
hungry method. Since a dog is willing to have a cheese-snack any time of day, we can use the following to return true for that method call when in any states other than “sleeping”.
While this example is obviously a simple case, it’s easy to see how this approach scales well to more complicated state relationships. The
state_machine gem integrates with a number of libraries including ActiveModel classes, ActiveRecord models, DataMapper resources and Mongoid models. Another feature that some may like is its support for generating a directed graph using the graphviz library. If you ever need to track the state of a record,
state_machine is definitely worth a look. What’s your preferred method for tracking states?
Image is my own (it’s Gonzo).