acts_as_state_machine and variable initial states

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)

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 :

Too bad, so I tried this :

Of course, the following worked :

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 :

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?

  • macournoyer

    Hey Frank,

    I think the best way is:

    def create
    user =[:user])

    enable! calls save, no hacks and pretty explicit.
    Changing a class attribute like this is not good practice and would prevent your app from being multi-thread amongst other things.

  • Frank

    Marc, thanks for your input. Feel a bit silly now ;)

    However I still wonder why it isn’t possible to bypass the initial state specified in the model. I’d prefer if I could just send the "state" attribute among the rest like that : => "blabla", :state => "enabled")

  • Frank

    Hmm… interesting. I started experiencing some weird deadlock errors as soon as I tried what you said Marc. I am too lazy to check the plugin code but I would guess it does something special with the “!” events. Or maybe it’s something completely unrelated… still checking the issue.

  • Cyberfox

    I believe the reason you can’t do that is that you’d be jumping to a middle state of a state machine. Essentially you’d be bypassing any code that was set up to happen during transitions.

    The system needs to trigger when you transition from (for example) :free to :paid, so it can provision you with the appropriate features.

    In order to trigger on transition between states, you have to have a consistent starting state.

    Think of it as stateful integrity, a term that has only two Google hits, so I consider myself to have made it up just now, for just this purpose. :)

    – Morgan

  • Frank


    good call, it makes sense. So basically I should redesign my “user state machine” to have just 1 initial state.

    Maybe have the following states :

    All new users could be : new (initial)
    Unapproved users : unapproved
    Enabled users : enabled
    Disabled users : disabled

    And the events :
    approve! (from :unapproved to :enabled)
    enable! (from :new to :enabled)
    disable! (from :enabled to :disabled and from :unapproved to :disabled)

    I guess that would be better than having more than 1 initial states. Any thoughts?

  • pravs

    Hello Frank,
    I am bit confused
    Isn’t it good way to use
    “User.write_inheritable_attribute :initial_state, :enabled”
    Because I want to assign the specific state (e.g. :xyz) initially only in few cases. And in other cases when i migrate from any other state to :xyz and have to perform some events which are not required in first case.
    Therefore I cannot use! as this performs all the event actions when I do this.

    So can anyone suggest me a good way to do this.
    “User.write_inheritable_attribute :initial_state, :xyz” serves my purpose but as someone has already said its not a good practice.