h1. State-Fu h2. What is it? State-Fu is: * an unique toolkit for state-oriented programming * a rich DSL for describing workflows, rules engines and behaviour * something you've probably wanted for a long time but didn't know it It lets you describe: * series of discrete states * events which can change the current state * rules about when these events can occur * behaviours which occur when they do Other libraries exist for ruby which do some or all of these things. "What's different about State-Fu?", you may ask. Those libraries you've played with are toys. They're made of plastic. State-Fu 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] State-Fu 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: State-Fu 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
    end

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

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

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

  my_doc = Document.new

  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
  my_doc.status.name      => :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
  my_doc.status.name      => :draft      # see? still a draft.
  my_doc.author = "Susan"                # so let's satisfy it ...
  my_doc.publish!                        # and try again.
  "new feed!"                            # aha - our event hook fires!
  my_doc.status.name      => :published  # and the state has been updated.

A few of the features which set State-Fu apart for more ambitious work are: * a lovely, simple and flexible API gives you plenty of choices * use an ActiveRecord field for state persistence, or just an attribute - or use both, on the same class, for different workflows * customising the persistence mechanism (eg to use a Rails session, or a text file) is as easy as defining a getter and setter method * define any number of workflows on the same object / model, or re-use them across multiple classes * events can transition from / to any number of states * drive application behaviour with a rich set of event hooks * define behaviour as methods on your objects, or keep it all in the state machine itself * requirements determine at runtime whether a particular state transition can occur, and if not, can tell a user what they must do to satisfy the requirements. * requirement failure messages can be generated at runtime, making use of whatever application and state-machine context they need * transitions can be halted mid-execution, and you can actually determine why, and where from * in every event hook, requirement filter, and other method calls, you have complete and tidy access to your classes, the state machine, and the transition context. Use real ruby code anywhere, without breathing through a straw! * extend State-Fu with helper modules, or raw blocks of ruby code, to model your problem domain - globally, per state machine / workflow, or for an individual event transition * store arbitrary meta-data on any component of State-Fu - a simple but extremely powerful tool for integration * designed for transparency, introspection and ease of debugging, which means a dynamic, powerful system you can actually use without headaches * magically generate diagrams of state machines / workflows with graphviz * fast, lightweight and useful enough to use in any ruby project - works with Rails but does not require it. State-Fu works with any modern Ruby ( 1.8.6, 1.8.7, and 1.9.1) fn1. No disrespect intended to the authors of other similar libraries - some of whom I've borrowed an idea or two, and some useful criticism, from. They're stand-up guys, all of them. It's the truth though. I'd like to thank Ryan Allen in particular for his Workflow library, which I previously forked, piled hundreds of lines of code into and renamed Stateful (now deprecated). Some of his ideas (for example the ability to store metadata easily on *everything* ) have been instrumental in State-Fu's design. I'd also like to tip my hat at John Barnette, who's own (coincidentally named) Stateful set a very high standard with an exceptionally elegant API. h2. 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 http://gems.github.com

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 folder is currently the best source of documentation. If you have questions, feature request or ideas, please join the "google group":http://groups.google.com/group/state-fu A note about ActiveSupport: StateFu will use ActiveSupport if it is already loaded. If not, it will load its own (very heavily cut down) 'lite' version. This means 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. Also see the "issue tracker":http://github.com/davidlee/state-fu/issues And the "build monitor":http://runcoderun.com/davidlee/state-fu/ And the "RDoc":http://rdoc.info/projects/davidlee/state-fu/ , which needs a bit of love.