acts_as_state_machine is a great plugin really useful when you want to add constraints and behavior to your model objects.
Note : Those who don’t know what this plugin is all about should stop reading right there or risk being completely lost.
One thing that seems impossible to do with the plugin is to have variable “initial states” for an object. Say for example that you have a User model that needs to act as a state machine. New users have to fill up a signup form to gain access to the application. These new users will be “pending” until an administrator approve them (enabled) or refuse them (disabled)
class User < ActiveRecord::Base acts_as_state_machine :initial => :pending state :pending state :enabled state :disabled event :enable do transitions :from => :pending, :to => :enabled transitions :from => :disabled, :to => :enabled end event :disable do transitions :from => :pending, :to => :disabled transitions :from => :enabled, :to => :disabled end end
So far so good. But what if an administrator can also create a new user? In this case we would’nt want the user to be “pending”, we want him to be “enabled” right away.
I experimented a similar scenario, so I tried a few things to bypass the initial state :
#FAILED ATTEMPT #1 def create user = User.new(params[:user]) user.state = :enabled user.save end
Too bad, so I tried this :
#FAILED ATTEMPT #2 def create User.initial_state = :enabled user = User.new(params[:user]) user.save end
Of course, the following worked :
#SUCCESSFUL BUT SUCKY ATTEMPT def create user = User.new(params[:user]) user.save user.enable! end
Yuk… there had to be something better.
Finally, I learned that initial_state was an inheritable attribute. I didn’t know what those were all about so I did some googling. The only good explanation I found is this one.
Anyway, I was able to override the initial state of my user object by doing this :
#SUCCESSFUL ATTEMPT! def create User.write_inheritable_attribute :initial_state, :enabled user = User.new(params[:user]) user.save end
It’s not pretty I know, but at least it gets the job done. I wonder if the fact that you cannot “easily” change the initial state programmatically is a missing feature that should be added in a future version… or if it’s only my understanding of a “state machine” that sucks. Any thoughts?