It was a fun experiment, now thoroughly unmaintained and with little chance it will ever be updated.

What is it?

StateFu is another Ruby state machine.

What is a state machine?

Finite state machines are a model for program behaviour; like object-oriented programming, they provide an abstract way to think about a domain.

In a finite state machine, there are a number of discrete states. Only one state may be occupied at any given time (hence the “finite”).

States are linked together by events, and there are rules which govern when or how transitions between states can occur. Actions may be fired on entry to or exit from a state, or when a certain transition occurs.

Why is StateFu different to the other twenty state machines for Ruby?

State machines are potentially a powerful way to simplify and structure a lot of problems. They can be used to:

StateFu was written from the ground up with one goal in mind: to be over-engineered. It is designed to make truly ambitious use of state machines not only viable, but strongly advantageous in many situations.

It is designed in the very opposite vein to the intentional minimalism of most ruby state machine projects; it is tasked with taking on a great deal of complexity and functionality, and abstracting it behind a nice DSL, so that the code which you have to maintain is shorter and clearer.

StateFu allows you to:

Still not sold?

StateFu is forged from a reassuringly dense but unidentifiable metal which comes only from the rarest of meteorites, and it ticks when you hold it up to your ear.1

It is elegant, powerful and transparent enough that you can use it to drive substantial parts of your application, and actually want to do so.

It is designed as a library for authors, as well as users, of libraries: StateFu goes to great lengths to impose very few limits on your ability to introspect, manipulate and extend the core features.

It is also delightfully elegant and easy to use for simple things:

  class Document < ActiveRecord::Base
    include StateFu

    def update_rss
      puts "new feed!"
      # ... do something here

    machine( :status ) do
      state :draft do
        event :publish, :to => :published

      state :published do
        on_entry :update_rss
        requires :author  # a database column

      event :delete, :from => :ALL, :to => :deleted do
        execute :destroy

      # save all states once transition is complete.
      # this wants to be last, as it iterates over each state which is
      # already defined.
      states do
        accepted { save! }

  my_doc =

  my_doc.status                          # returns a StateFu::Binding, which lets us access the 'Fu
  my_doc.status.state     => 'draft'     # if this wasn't already a database column or attribute, an
                                         # attribute has been created to keep track of the state      => :draft      # the name of the current_state (defaults to the first defined)
  my_doc.status.publish!                 # raised =>  StateFu::RequirementError: [:author]
                                         # the author requirement prevented the transition      => :draft      # see? still a draft. = "Susan"                # so let's satisfy it ...
  my_doc.publish!                        # and try again.
  "new feed!"                            # aha - our event hook fires!      => :published  # and the state has been updated.

StateFu works with any modern Ruby ( 1.8.6, 1.8.7, and 1.9.1)

Getting started

You can either clone the repository in the usual fashion (eg to yourapp/vendor/plugins/state-fu), or use StateFu as a gem.

To install as a gem:

gem install davidlee-state-fu -s

To require it in your ruby project:

require 'rubygems'
require 'state-fu'

To install the dependencies for running specs:

 sudo gem install rspec rr
 rake             # run the specs
 rake spec:doc    # generate specdocs
 rake doc         # generate rdocs
 rake build       # build the gem locally
 rake install     # install it

Now you can simply include StateFu in any class you wish to make stateful.

The spec/ and features/ folders are currently one of the best source of documentation. The documentation is gradually evolving to catch up with the features, but if you have any questions I’m happy to help you get started.

If you have questions, feature request or ideas, please join the google group or send me a message on GitHub.

A note about ActiveSupport

StateFu will use ActiveSupport if it is already loaded. If not, it will load its own (heavily trimmed) ‘lite’ version.

In most projects this will behave transparently, but it does mean that if you require StateFu before other libraries which require ActiveSupport (e.g. ActiveRecord), you may have to explicitly require 'activesupport' before loading the dependent libraries.

So if you plan to use ActiveSupport in a stand-alone project with StateFu, you should require it before StateFu.

Addditional Resources

Also see the issue tracker

And the build monitor

And the RDoc

StateFu is not a complete BPM (Business Process Management) platform

It’s worth noting that StateFu is at it’s core a state machine, which strives to be powerful enough to be able to drive many kinds of application behaviour.

It is not, however, a classical workflow engine on par with Ruote. In StateFu the basic units with which “workflows” are built are states and events; Ruote takes a higher level view, dealing with processes and participants. As a result, it’s capable of directly implementing these design patterns:

Whereas StateFu cannot, for example, readily model forking / merging of processes (nor does it handles scheduling, process management, etc.

The author of Ruote, the Ruby Workflow Engine, outlines the difference pretty clearly here:

If your application can be described with StateFu, you’ll likely find it simpler to get running and work with; if not, you may find Ruote, or a combination of the two, suits your needs perfectly.