Plugins - Acts As State Machine

StarAdd to favorites

Summary

Adds state machine functionality to any model. This makes it much easier to model complex constraints and behaviours.

Example

Here’s a picture of the state machine we want: http://www-128.ibm.com/developerworks/java/library/j-cb03137/figure1.gif

And here’s what the code would look like:

  class Nonprofit < ActiveRecord::Base

    acts_as_state_machine :initial => :created, :column => 'status'

    # These are all of the states for the existing system.
    state :submitted
    state :processing
    state :nonprofit_reviewing
    state :accepted

    event :accept do
      transitions :from => :processing, :to => :accepted
      transitions :from => :nonprofit_reviewing, :to => :accepted
    end

    event :receive do
      transitions :from => :submitted, :to => :processing
    end

    # either a CTP  or nonprofit user edits the entry, requiring a review
    event :send_for_review do
      transitions :from => :processing, :to => :nonprofit_reviewing
      transitions :from => :nonprofit_reviewing, :to => :processing
      transitions :from => :accepted, :to => :nonprofit_reviewing
    end

  end

(Image/example taken from an IBM article: http://www-128.ibm.com/developerworks/java/library/j-cb03137/index.html)

Scott Barron

http://rubyi.st/2006/1/21/acts-as-state-machine

http://elitists.textdriven.com/svn/plugins/acts_as_state_machine

Rails' (MIT)

  • Currently 4.5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Model

Tags

Comments

Add a comment
Xin 5 May 2008

Thanks for the great plugin!

I would be beneficial having the following features: 1) specify next action for a state. This can be used to explain to the user what action is waiting to happen. i.e. for submitted state, it's waiting to be reviewed.

2) Order states so can be shown in a drop down list.

I'll have a go at implementing these when I get some time.

Nate Clark 1 May 2008

Great plugin ... we've been using it a lot. I thought I'd share this one little enhancement that has been useful to me. It allows you to call <code>possible_events</code> on any state machine object and you will get back an array of events that can be called, considering the object's state.

Here's the code, it goes in actsas_statemachine.rb in the InstanceMethods module.

<pre>

Returns all possible events that can be called on the object in it's current state, as a Ruby symbol

def possible_events returning Array.new do |events| self.class.readinheritableattribute(:transitiontable).eachpair do |event, value| events << event if value.detect{|transition| transition.from == current_state } end end end </pre>

bill 28 Apr 2008

Sorry, the system reformatted my text. Those long initializers are supposed to be shaped like a matrix.

It's a State Transition Table translated directly to code. Wikipedia has an article on State Transition Tables with examples.

Bill 24 Apr 2008

I did one of these in Java last week. Yours is extremely similar.

After the base (what you have) I made a revision that turned out to be fairly easy, you might want to consider something like it.

I actually used a state table to define the transitions, rather than your manual transition calls.

So you still define all the states and all the events, but then you create the transitions like this:

a, b, c,

e1, b, x, x, e2, x, c, x, e3, x, a, a

In Java I had to put all that into an array, so the whole thing ende up looking somewhat like this:

State a, b, c; Event e1, e2, e3; Object x=null;

new StateEngine(new Object[]={ a, b, c, e1, b, x, x, e2, x, c, x, e3, x, a, a });

My own little DSL; but in ruby I bet you could clean that up a bit. I'd love to get rid of the comas, and it wouldn't hurt to have colons after the events. My ideal would have been to have the table look like this:

states: a b c e1: b x x e2: x c x e3: x a a

I would have made it look like that in java (by assigning that whole mess to a string, I think), but the JVM I'm working on doesn't support reflection.

gregh 22 Apr 2008

I'm looking for something that supports creating validations back to the user if they try to make an update that doesn't satisfy state criteria. For example if I have a model for personal expenses ("Expense") and that if I want to transition it from PAID to BANKRECONCILED then I was to make sure that the BANKRECONCILED_DATE field is populated before allowing this to proceed. So perhaps I really want something like:

  • User updates an Expense and changes the status to BANKRECONCILED but doesn't add a BANKRECONCILED_DATE
  • model update sees change in status and calls the "bank_reconciled!" action
  • this action either works, or else provides a set of validations back

Is this a reasonable approach? Or do people think it would be best to handle this with the normal Rails basic validation approach (i.e. and not use "acts_as_statemachine")

J. Pablo Fernández 28 Mar 2008

The home is no longer valid.

jkaplan 9 Jan 2008

Thanks for the great work Scott. Shouldn't the runinitialstateactions method be attached to the beforecreate callback (after setinitialstate)? Otherwise, one can't attach logic to the model that affects its attributes without saving the object at least twice.

Chetan Patil 30 Nov 2007

Hi, I added a couple of enhancements which may be useful to others:

  • An on_transition callback for doing some work while transitioning.
  • Being able to specify a next state while firing an event. More details at http://justbarebones.blogspot.com/2007/11/actsasstatemachine-enhancements.html

  • c

Search Plugins

Query syntax

Plugins by Category

Sponsors

Rails Kits: Get Code. Get Moving.
Recruiting software

Have a comment?