Ruby state library that lets you define state-specific functionality
Ruby
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
lib
test
.gitignore
.travis.yml
Gemfile
Gemfile.lock
MIT_LICENCE
README.textile
Rakefile
VERSION

README.textile

Solid State – Stateful Ruby objects with a twist

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.

Example

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

Notes

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.

Possible Issues

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.

Project Info

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