Plugins - Acts As State Machine
Add to favoritesSummary
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)
http://rubyi.st/2006/1/21/acts-as-state-machine
http://elitists.textdriven.com/svn/plugins/acts_as_state_machine
Rails' (MIT)
Model


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.
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>
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.
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:
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.
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:
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")
The home is no longer valid.
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.
Hi, I added a couple of enhancements which may be useful to others:
Being able to specify a next state while firing an event. More details at http://justbarebones.blogspot.com/2007/11/actsasstatemachine-enhancements.html
c