Undefined method '>' for nil:NilClass <NoMethodError>

Netorica picture Netorica · Nov 3, 2013 · Viewed 78.7k times · Source

Ok I do have the following code

 def update_state_actions
    states.each do |state|
      @state_turns[state.id] -= 1 if @state_turns[state.id] > 0 && state.auto_removal_timing == 1
    end
  end

now in the line of...

 @state_turns[state.id] -= 1 if @state_turns[state.id] > 0 && state.auto_removal_timing == 1

it says the error

in 'block update_state_actions' : Undefined method '>' for nil:NilClass <NoMethodError>

what is the cause of the error? how come > is considered as a method but it is a logical operator?

Answer

Arup Rakshit picture Arup Rakshit · Nov 3, 2013

how come > is considered as a method but it is a logical operator?

There is no problem with that. In Ruby, when you write an expression like 1 + 2, internally it is understood as 1.+( 2 ): Calling method #+ on the receiver 1 with 2 as a single argument. Another way to understand the same is, that you are sending the message [ :+, 2 ] to the object 1.

what is the cause of the error?

Now in your case, @state_turns[ state.id ] returns nil for some reason. So the expression @state_turns[state.id] > 0 becomes nil > 0, which, as I said earlier, is understood as calling #> method on nil. But you can check that NilClass, to which nil belongs, has no instance method #> defined on it:

NilClass.instance_methods.include? :> # => false
nil.respond_to? :> # => false

The NoMethodError exception is therefore a legitimate error. By raising this error, Ruby protects you: It tells you early that your @state_turns[ state.id ] is not what you assume it to be. That way, you can correct your errors earlier, and be a more efficient programmer. Also, Ruby exceptions can be rescued with begin ... rescue ... end statement. Ruby exceptions are generally very friendly and useful objects, and you should learn how to define your custom exceptions in your software projects.

To extend this discussion a bit more, let's look at from where your error is coming. When you write an expression like nil > 10, which is actually nil.>( 10 ), Ruby starts searching for #> method in the lookup chain of nil. You can see the lookup chain by typing:

    nil.singleton_class.ancestors #=> [NilClass, Object, Kernel, BasicObject]

The method will be searched in each module of the ancestor chain: First, Ruby will check whether #> is defined on NilClass, then on Object, then Kernel, and finally, BasicObject. If #> is not found in any of them, Ruby will continue by trying method_missing methods, again in order on all the modules of the lookup chain. If even method_missing does not handle the :> message, NoMethodError exception will be raised. To demonstrate, let's define #method_missing method in Object by inserting a custom message, that will appear instead of NoMethodError:

class Object
  def method_missing( name, *args )
    puts "There is no method '##{name}' defined on #{self.class}, you dummy!"
  end
end

[ 1, 2, 3 ][ 3 ] > 2 
#=> There is no method '#>' defined on NilClass, you dummy!

Why doesn't it says like NullPointerException

There is no such exception in Ruby. Check the Ruby's Exception class.