Most stateful libraries that exist for Ruby deal with keeping track of a state variable, which is a symbol or string stating what state said object is currently in. This works well with libraries like ActiveRecord where you’re usually simply interested in data. But what if you want to change the functionality according to what state the object is in? With tools like ActsAsStateMachine, you’ll still need to pepper your methods with checks on which state the object is in:
if state == :this
elsif state == :that
else
...
end
Enter solid_state. The Ruby state machine library that lets you define state-specific functionality. But enough yammering, nothing can describe a system like a simple example.
Please note: this library is not a full state machine. See the Notes section below.
Lets say you have a simple AI that needs to act differently according to what state it’s currently in. We’ll define the states :pursue, :scared, and :idle.
class AI
include SolidState
state :pursue do
def update
# Chase the target!
end
end
state :scared do
def update
# Run away from the target!
end
end
state :idle do
def update
# Look for a new target
end
end
starting_state :idle
end
This one example shows almost the entirity of solid_state’s simple API. Include the SolidState module, define your states, and optionally set a starting state. To using this class is simple:
ai = AI.new
ai.current_state # => :idle
ai.update # => looking for a target...
# Target found!
ai.change_state! :pursue
ai.update # => Rawr! Chasing target
# I'm hurt!
ai.change_state! :scared
ai.update # => Run away and find help!
ai.change_state! :dead # => InvalidStateError ... aw
This also works seemlessly with subclasses. For example:
class Scavenger < AI
state :scavange do
def update
# Scavange!
end
end
end
ai = Scavanger.new
ai.current_state # => :idle
ai.change_state! :scavange
ai.update # => Scavaging!
a.change_state! :idle
a.update
To be fair, other state machine libraries do offer this functionality. I’m do this to be a learning experience and to make as bare-bones a state machine system as I can, for when you don’t need a full state machine (transitions, validation, transition direction enforcement, etc).
A similar library to this is state-ology which uses the Mixology gem to pull Modules in and out of the system when you change states. Functionality wise it’s pretty much equal to solid_state, but I wanted to build it in pure Ruby.
If you’re looking for a fully comprehensive state machine library, state_machine is the most detailed I’ve found yet and probably can do anything you need to do.
Individual states are implemented underneath as inner Classes that subclass the current Class. This means they get access to all public and protected methods in the outer Class, but at the same time if there are state methods with the same name as methods on the outer class, the state methods will never get called.
Install via gems: gem install solid_state
Code hosted on Github: http://github.com/jasonroelofs/solid_state
Issues on Github: http://github.com/jasonroelofs/solid_state/issues