pluginaweek / state_machine

Adds support for creating state machines for attributes on any Ruby class
http://www.pluginaweek.org
MIT License
3.74k stars 507 forks source link

Undefined method `state` #296

Open jrust opened 10 years ago

jrust commented 10 years ago

We are having an issue when a model (User) is occasionally failing to save because state_machine validations are being triggered for it even though the model does not define a state machine. I've tried to find how it could be happening and am completely stumped since the state machine callbacks are only set up for objects with a state machine and so the lines that show in the backtrace should never be invoked for a User. The fact that it happens intermittently and so far only in production seems to imply it's something strange with the object instance. It happens on both rails 3 & 4, and seems to have surfaced when we moved to ruby 2. Here is an example backtrace:

NoMethodError: undefined method `state' for #<User:0x000000088beb58>
…ctivemodel-4.0.2/lib/active_model/attribute_methods.rb: 439:in `method_missing'
…0/gems/activemodel-4.0.2/lib/active_model/validator.rb: 151:in `block in validate'
…0/gems/activemodel-4.0.2/lib/active_model/validator.rb: 150:in `each'
…0/gems/activemodel-4.0.2/lib/active_model/validator.rb: 150:in `validate'
…gems/activemodel-4.0.2/lib/active_model/validations.rb: 373:in `run_validations!'
…emodel-4.0.2/lib/active_model/validations/callbacks.rb: 106:in `block in run_validations!'
…e-1.2.0/lib/state_machine/integrations/active_model.rb: 514:in `block in around_validation'
…chine-1.2.0/lib/state_machine/transition_collection.rb: 150:in `block in run_actions'
…chine-1.2.0/lib/state_machine/transition_collection.rb: 170:in `catch_exceptions'
…chine-1.2.0/lib/state_machine/transition_collection.rb: 148:in `run_actions'
…chine-1.2.0/lib/state_machine/transition_collection.rb: 133:in `run_callbacks'
…chine-1.2.0/lib/state_machine/transition_collection.rb: 212:in `run_callbacks'
…chine-1.2.0/lib/state_machine/transition_collection.rb:  63:in `block (2 levels) in perform'
…chine-1.2.0/lib/state_machine/transition_collection.rb:  63:in `catch'
…chine-1.2.0/lib/state_machine/transition_collection.rb:  63:in `block in perform'
…chine-1.2.0/lib/state_machine/transition_collection.rb: 186:in `within_transaction'
…chine-1.2.0/lib/state_machine/transition_collection.rb:  62:in `perform'
…e-1.2.0/lib/state_machine/integrations/active_model.rb: 514:in `around_validation'
…emodel-4.0.2/lib/active_model/validations/callbacks.rb: 106:in `run_validations!'
…gems/activemodel-4.0.2/lib/active_model/validations.rb: 314:in `valid?'
…-1.2.0/lib/state_machine/integrations/active_record.rb: 483:in `block in save'
…-1.2.0/lib/state_machine/integrations/active_record.rb: 502:in `block (2 levels) in around_save'
…chine-1.2.0/lib/state_machine/transition_collection.rb: 150:in `block in run_actions'
…chine-1.2.0/lib/state_machine/transition_collection.rb: 170:in `catch_exceptions'
…chine-1.2.0/lib/state_machine/transition_collection.rb: 148:in `run_actions'
…chine-1.2.0/lib/state_machine/transition_collection.rb: 133:in `run_callbacks'
…chine-1.2.0/lib/state_machine/transition_collection.rb: 212:in `run_callbacks'
…chine-1.2.0/lib/state_machine/transition_collection.rb:  63:in `block (2 levels) in perform'
…chine-1.2.0/lib/state_machine/transition_collection.rb:  63:in `catch'
…chine-1.2.0/lib/state_machine/transition_collection.rb:  63:in `block in perform'
…chine-1.2.0/lib/state_machine/transition_collection.rb: 186:in `within_transaction'
…chine-1.2.0/lib/state_machine/transition_collection.rb:  62:in `perform'
…-1.2.0/lib/state_machine/integrations/active_record.rb: 502:in `block in around_save'
…-1.2.0/lib/state_machine/integrations/active_record.rb: 530:in `block in transaction'
…-1.2.0/lib/state_machine/integrations/active_record.rb: 529:in `transaction'
…-1.2.0/lib/state_machine/integrations/active_record.rb: 501:in `around_save'
…-1.2.0/lib/state_machine/integrations/active_record.rb: 483:in `save'
…1219211816/app/controllers/user_profiles_controller.rb:  38:in `update'
seuros commented 10 years ago

Can you create another application with the issue so we can test it?

jrust commented 10 years ago

Yes I can try to do that while I'm working on trying to reproduce it. Am I right in my assumption that the around_save method shouldn't even be in the backtrace of a class that does not have a state_machine defined? I'm trying to make sure that I'm barking up the right tree in looking into state machine. Thanks.

jrust commented 10 years ago

Tracked it down! Had some metaprogramming that was inadvertently adding a state_machine dynamically:

model.auditable.class.state_machine(:state)

When that code was invoked it would add a state_machine to the auditable class, in this case User. I don't know if it's intended behavior that you can dynamically add a state machine to any class via the StateMachine::MacroMethods.state_machine method. If that is intended then nothing much that can be done, if it's not then you could consider making StateMachine::MacroMethods.state_machine private. e.g.:

    private
    def state_machine(*args, &block)
      StateMachine::Machine.find_or_create(self, *args, &block)
    end

And from the console:

[2] pry(main)> User.state_machine
NoMethodError: private method `state_machine' called for #<Class:0x007f8c7d563810>

I'll leave this open in case you want to change, but feel free to close if it's intended behavior.

the8472 commented 10 years ago

instead of doing model.auditable.class.state_machine(:state) you can do

clazz = model.auditable.class
machine = clazz.state_machines[:state] if clazz < StateMachine::InstanceMethods 
jrust commented 10 years ago

Thabks, that's more reliable than the responds_to check I was using.

On Friday, December 27, 2013, the8472 wrote:

instead of doing model.auditable.class.state_machine(:state) you can do

clazz = model.auditable.classmachine = clazz.state_machines[:state] if clazz < StateMachine::InstanceMethods

— Reply to this email directly or view it on GitHubhttps://github.com/pluginaweek/state_machine/issues/296#issuecomment-31290693 .