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.