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?