troessner/transitions

NoMethodError: undefined method `call_action' for nil:NilClass

jmonegro opened this issue · 12 comments

Hi, I'm getting this error when trying to fire an event:

NoMethodError: undefined method `call_action' for nil:NilClass
    from /Users/jmonegro/.rvm/gems/ruby-1.9.3-p194/gems/transitions-0.1.0/lib/transitions/machine.rb:47:in `fire_event'
    from /Users/jmonegro/.rvm/gems/ruby-1.9.3-p194/gems/transitions-0.1.0/lib/transitions/event.rb:31:in `block in initialize'
    from (irb):1
    from /Users/jmonegro/.rvm/gems/ruby-1.9.3-p194/gems/railties-3.2.7/lib/rails/commands/console.rb:47:in `start'
    from /Users/jmonegro/.rvm/gems/ruby-1.9.3-p194/gems/railties-3.2.7/lib/rails/commands/console.rb:8:in `start'
    from /Users/jmonegro/.rvm/gems/ruby-1.9.3-p194/gems/railties-3.2.7/lib/rails/commands.rb:41:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

Here's my code:

  class Order < ActiveRecord::Base
    include ActiveModel::Transitions

    state_machine do

      state "Order Received"
      state "Order Shipped"

      event :ship, success: :notify_customer do
        transitions :to => "Order Shipped", :from => "Order Received"
      end

    end

    private

    def notify_customer
      true
    end

  end

And trying to do order.ship!

Any pointers?

Thanks!

Thanks for the report - I edited your submission to make it look nicer using GH's markdown

```Ruby
# your_code 
```

Now, to the problem: Yes, you're right, this is a problem in the gem that went unnoticed because apparently you are the first person ever to use strings with whitespace in it as state names..:)

Quickfix: Use symbols only:

class Order < ActiveRecord::Base
  include ActiveModel::Transitions

  state_machine do

    state :received
    state :shipped

    event :ship do
      transitions :to => :shipped, :from => :received
    end
  end
end

I will look into this problem asap.

@jmonegro

In a nutshell: Don't use strings but symbols. Right now the gem expects symbols, nothing else.

I could update the gem so that it works with strings as well but this would mean a LOT of ugly code calling "to_sym" and type checks all over the place (even with using HashWithIndifferentAccess from ActiveSupport).

In addition, whitespace would never work since transitions possibly generates methods e.g.

def received?

which means that whitespace is prohibited anyway.

If you want to use a different display name for states than the state name itself you need to set up a mapping from state_name to display_name for yourself (there is a display_name functionality in the gem but it looks like it is broken).

I will make this clearer in the documenation, thanks for reporting this -> closed.

Addendum:

This means that you will need to rewrite your example to:

  state_machine do
      state :order_shipped
      state :order_received

      event :ship, success: :notify_customer do
        transitions :to => :order_shipped, :from => :order_received
      end
    end

Got it. I resorted to using "string symbols" (i.e. :'String Status') which
works for my purposes and implementation.

Thanks!

On Mon, Jul 30, 2012 at 9:12 AM, Timo Rößner <
reply@reply.github.com

wrote:

@jmonegro

In a nutshell: Don't use strings but symbols. Right now the gem expects
symbols, nothing else.

I could update the gem so that it works with strings as well but this
would mean a LOT of ugly code calling "to_sym" and type checks all over the
place (even with using HashWithIndifferentAccess from ActiveSupport).

In addition, whitespace would never work since transitions possibly
generates methods e.g.

def received?

which means that whitespace is prohibited anyway.

If you want to use a different display name for states than the state name
itself you need to set up a mapping from state_name to display_name for
yourself (there is a display_name functionality in the gem but it looks
like it is broken).

I will make this clearer in the documenation, thanks for reporting this ->
closed.


Reply to this email directly or view it on GitHub:
#60 (comment)

  • Joel

I just wanted to mention that this error can also occur in another scenario even if you are using symbols. I was writing some tests and decided to change the state machine state names, but I forgot that my test scaffold was still creating a mock record with the old state name. When it came time to transition the object's state, this same error is thrown.

@homanchou - Would you be able to give a glimpse into the before/after of the specific test you were working with that use the state machine?

I have a worker model that has a state machine:

  state_machine :auto_scopes => true do
    state :running # initial state
    state :completed #success
    state :aborted #errored out

    event :complete do
      transitions :from => [:running], :to => :completed
    end

    event :abort do
      transitions :from => [:running], :to => :aborted, :on_transition => [:send_admin_email]
    end

  end

But in the test I forgot that I renamed state 'in_progress' to 'running', so my test was doing:

worker = Sweeper.create(name:'remove_files', state:'in_progress')
worker.complete!

I would see an error like: "undefined method `call_action' for nil:NilClass"

That was kind of confusing for me. Probably a more helpful error might be "Invalid transition state 'in_progress' is not a defined state"

@troessner Might be able to give some more insight/options on the type of error that could be thrown. It is a tad confusing like you said.

This specific error does remind me of a different (but has some crossover) issue ( #120) . In the other issue, there was invalid data within the database (invalid state). The same type of thing was exposed via your testing. Your test just happened to have invalid data this time! :)

Yup, it was my own fault for having bad data in the DB. I just noticed this in the README: "The explicitly specified state must be one of the states listed in the state definition below, otherwise transitions will raise a rather unhelpful exception like "NoMethodError: undefined method `call_action' for nil:NilClass" (there's a ticket to fix this already: #112)" , I remember reading that when I first started using the gem but didn't know what that was about. Now I do, I was totally bitten by it! :)

Actually, that reminds me. I think I helped reword that part of the README in PR, but back then I thought it was only regarding the explicitly defined INITIAL state had to be one of the states defined. But actually that goes for states that are in the DB as well.

@homanchou you're absolutely right, the error message IS absolutely confusing.

That was kind of confusing for me. Probably a more helpful error might be "Invalid transition state 'in_progress' is not a defined state"

I agree. Pull request welcome.:)
If you're not up to it, can you please open up a corresponding issue? I'll look into it once I find some time on my hands.

I encountered the same error when I typoed the name of the target state in an event. If I get the chance in the next week or so I'll do a fix.

I just ran into the same issue like @homanchou. A proper error message would be really nice.