Today I’m happy to announce the release of AlterEgo, a state-pattern library for Ruby. AlterEgo was born about a year ago, when I found a need to formalize state-based behavior in Ruby objects. I surveyed the existing libraries at the time (a set which, IIRC, consisted of just acts_as_state_machine), and found they didn’t do quite what I wanted. Other solutions were focused on defining states and transitions, not on providing the kind of delegation-based behavior switching that the Gang of Four State Pattern describes. So I wrote AlterEgo, and it has been serving us well at “MDLogix”:http://mdlogix.com ever since. I’ve been wanting to open-source it for a while, but this month I finally got around to getting approval and doing the necessary extraction and packaging.
There’s plenty of explanation and documentation at the “AlterEgo project site”:http://alter-ego.rubyforge.org , so I won’t go into depth here. In a nutshell, AlterEgo is a library you include into your classes in order to give them state-based “personalities”. Here’s the canonical example:
class TrafficLight include AlterEgo state :proceed, :default => true do handle :color do "green" end transition :to => :caution, :on => :cycle! end state :caution do handle :color do "yellow" end transition :to => :stop, :on => :cycle! end state :stop do handle :color do "red" end transition :to => :proceed, :on => :cycle! end end light = TrafficLight.new light.color # => "green" light.cycle! light.color # => "yellow" light.cycle! light.color # => "red" light.cycle! light.color # => "green"
Under the covers this is all accomplished with a proxy module and method forwarding. The underlying model, as well as much of the terminology used in the API, is based on the State Pattern as described in the book Design Patterns:
A “context” object – the class you want to have state-based behavior – always has a reference to one and only one “state” object. When “requests” – methods calls – are received, they are delegated to the current state object, which handles them in whatever way is appropriate for that state. In AlterEgo, we take advantage of Ruby’s dynamic nature to execute these “handlers” in the context of the original context object – so we can write them as if they were instance methods of the context object, with full access to instance variables, private methods, etc.
For more information, documentation, and information about how to contribute, see “the project site”:http://alter-ego.rubyforge.org.