Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Undefined method state #296

Open
jrust opened this issue Dec 26, 2013 · 5 comments
Open

Undefined method state #296

jrust opened this issue Dec 26, 2013 · 5 comments

Comments

@jrust
Copy link

jrust commented Dec 26, 2013

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
Copy link

seuros commented Dec 26, 2013

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

@jrust
Copy link
Author

jrust commented Dec 26, 2013

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
Copy link
Author

jrust commented Dec 27, 2013

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
Copy link

the8472 commented Dec 28, 2013

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
Copy link
Author

jrust commented Dec 28, 2013

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//issues/296#issuecomment-31290693
.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants