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.