acts_as_state_machine: bug?! obj.save doesn't work...

Hi all

I'm trying to understand, what acts_as_state_machine really does (I use it because restful_authentication uses it).

restful_authentication defines the following stuff:

  acts_as_state_machine :initial => :pending   state :passive   state :pending, :enter => :make_activation_code   state :active, :enter => :do_activate   state :suspended   state :deleted, :enter => :do_delete

  event :register do     transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| !(u.crypted_password.blank? && u.password.blank?) }   end

  event :activate do     transitions :from => :pending, :to => :active   end

  event :suspend do     transitions :from => [:passive, :pending, :active], :to => :suspended   end

  event :delete do     transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted   end

  event :unsuspend do     transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| !u.activated_at.blank? }     transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| !u.activation_code.blank? }     transitions :from => :suspended, :to => :passive   end

I played a bit with the console...

josh$ script/console Loading development environment (Rails 2.1.0)

record = User.new({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' })

=> #<User id: nil, first_name: nil, last_name: nil, login: "quire", email: "quire@example.com", remember_token: nil, crypted_password: nil, password_reset_code: nil, salt: nil, activation_code: nil, remember_token_expires_at: nil, activated_at: nil, deleted_at: nil, state: "passive", created_at: nil, updated_at: nil>

Why is the state "passive"? Is the state of an unsaved object always "passive"?

record.save

=> true

record

=> #<User id: 4, first_name: nil, last_name: nil, login: "quire", email: "quire@example.com", remember_token: nil, crypted_password: "5670fb5c84b89d64ef405b315e4337304f88dc2b", password_reset_code: nil, salt: "a6ff544223bf2a7653651ea7f29888a195155c8d", activation_code: "43745d2e79d7aab4dfe2b4a3bd64d8ac577aeafe", remember_token_expires_at: nil, activated_at: nil, deleted_at: nil, state: "pending", created_at: "2009-04-20 20:19:15", updated_at: "2009-04-20 20:19:15">

Looks good so far... But when looking at the database entry, the activation_code actually is NULL! Let's prove this:

record.reload

=> #<User id: 4, first_name: nil, last_name: nil, login: "quire", email: "quire@example.com", remember_token: nil, crypted_password: "5670fb5c84b89d64ef405b315e4337304f88dc2b", password_reset_code: nil, salt: "a6ff544223bf2a7653651ea7f29888a195155c8d", activation_code: nil, remember_token_expires_at: nil, activated_at: nil, deleted_at: nil, state: "pending", created_at: "2009-04-20 20:19:15", updated_at: "2009-04-20 20:19:15">

Tadaah! This is a serious bug, isn't it? The User object only works correct when using "register!" instead of save:

josh$ script/console Loading development environment (Rails 2.1.0)

record = User.new({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' })

=> #<User id: nil, first_name: nil, last_name: nil, login: "quire", email: "quire@example.com", remember_token: nil, crypted_password: nil, password_reset_code: nil, salt: nil, activation_code: nil, remember_token_expires_at: nil, activated_at: nil, deleted_at: nil, state: "passive", created_at: nil, updated_at: nil>

record.register!

=> true

record.reload

=> #<User id: 5, first_name: nil, last_name: nil, login: "quire", email: "quire@example.com", remember_token: nil, crypted_password: "465f2d6572f47e9adec58022d938b134e570077b", password_reset_code: nil, salt: "293aaa4b1a391f472803767c93c07bf7966e9141", activation_code: "1129116e89f989fee4dccb7eb38946c8e16af93a", remember_token_expires_at: nil, activated_at: nil, deleted_at: nil, state: "pending", created_at: "2009-04-20 20:22:03", updated_at: "2009-04-20 20:22:03">

Can anyone approve this? In my oppinion, save should have exactly the same effect like "register!"... but it definitely doesn't.

Thanks for your opinion. Josh

Not sure I understand your problem...

event :register do      transitions :from => :passive,                       :to => :pending,                       :guard => Proc.new {|u|                            !(u.crypted_password.blank? &&                              u.password.blank?) } end

sort of tells me that UsersController#register causes a state transition from :passive to :pending

and

event :activate do   transitions :from => :pending, :to => :active end

UsersController#activate causes a state transition from :pending

I don't see any mention of UsersController#save anywhere in either restful_authentication/lib/... or in the controllers and views created using script/generate authenticated User Sessions --options...

Perhaps the AASM library is buggy. I haven't used it in ages, but I seem to recall some issue of it not calling the enter transition unless the event was manually triggered. That's what the behavior above suggests.

If the library is giving you hassles, then generate the authentication code without the aasm flag. This code was submitted to me by someone else, and I don't have much experience with it.

Rick Olson wrote:

Perhaps the AASM library is buggy. I haven't used it in ages, but I seem to recall some issue of it not calling the enter transition unless the event was manually triggered. That's what the behavior above suggests.

If the library is giving you hassles, then generate the authentication code without the aasm flag. This code was submitted to me by someone else, and I don't have much experience with it.

First of all thanks a lot for taking this serious and answering me upon my request, Rick. :slight_smile:

What do you suggest? Should I try to find the bug in AASM and submit a fix? I don't like to use workarounds for evidently buggy software... :wink:

You might want to look at this a little closer before you submit a bug report. For instance, if you make two changes:

1) FILE: app/controllers/users_controller.rb     IN: def create

You can now follow the sequence:

record = User.new({ :login => 'quire', :email => 'quire@example.com', :password => 'enquire', :password_confirmation => 'enquire' })

=> #<User id: nil, login: "quire", name: "", email: "quire@example.com", crypted_password: nil, salt: nil, created_at: nil, updated_at: nil, remember_token: nil, remember_token_expires_at: nil, activation_code: nil, activated_at: nil, state: "passive", deleted_at: nil>

record.save

=> true

record

=> #<User id: nil, login: "quire", name: "", email: "quire@example.com", crypted_password: nil, salt: nil, created_at: nil, updated_at: nil, remember_token: nil, remember_token_expires_at: nil, activation_code: "8508423584b5c775a60939f9d1966653cd8ea493", activated_at: nil, state: "pending", deleted_at: nil>

Actually, you forgot to reload the model, because THAT caused the problem...

And it seems that we don't use the same version of the restful_authentication plugin, because mine doesn't contain a file called aasm_roles.rb; take a look at the attached screen...

Attachments: http://www.ruby-forum.com/attachment/3603/restful_authentication.jpg

Actually, I didn't forget. The model wasn't available for reload because, by masking ActiveRecord#save with UsersController#save, there was nothing to reload.

But yes, version is probably going to make any more discussion difficult.

Here's where I go: http://github.com/technoweenie/restful-authentication/tree/master.

This site is actively supported, maybe you should move there.