Why isn't there a NilReferenceError in ruby? - ruby-on-rails

Why is NoMethodError not differentiated for nil in Ruby?
Calling a method on nil is an extremely common error and is usually caused by incorrect data being provided to the program. A NoMethodError on any other class usually implies an error in the code itself (e.g. why were you calling reconnect on a Document? There is likely an error in the code).
What problems are created if I add the following code to my project?
NilReferenceError = Class.new(NoMethodError)
class NilClass
def method_missing(symbol, *args)
raise NilReferenceError, "undefined method `#{symbol}' for nil:NilClass", caller
end
end
I want to do this because when I am triaging exceptions, a NilReferenceError is likely to be caused by bad data and the root cause is likely in another location (validation of input, for example). By contrast, a NoMethodError is likely to be a programming error rooted exactly at the line of the exception (easier to fix and also highly likely to happen 100% of the time).
What are the negative effects of adding code like that to my project?

I think this is just habits from other programming languages. In ruby, nil is a first class object, like an integer, a hash or your own class object.
After you see the "NoMethodError: undefined method xxx for nil:NilClass" error once or twice, you get used to it.

There is nothing wrong with monkeypatching nil to show a more descriptive error message, but it's not going to solve the root cause of the problem, which is coding practice that permits and propagates nil values.
Consider the following very contrived example:
def do_something(input)
object = fetch_something_with(input[element])
do_something_with(object)
end
A couple of ways this might blow up:
input hash does not contain element, and passes nil into fetch_something_with
fetch_something_with returns nil (by design or upon failure), which gets passed into do_something_with
An alternative approach might be:
def do_something(input)
object = fetch_something_with(validated_input)
object && return do_something_with(object)
end
def validated_input(input)
input.fetch(element) # fetch raises an exception if the value is not present
end
A bit more code, but it gives us peace of mind that this code isn't going to silently pass nil down the line to fail at some later point.
Of course, it doesn't make sense to be this paranoid in every single method, but it is good practice to have well thought out boundaries in your code, either at method or object level. Having nil slip by frequently is a sign that these borders need some strengthening.

Do you mean that, when doing something like b=nil; b.say_hello;, ruby will give you "undefined method `say_hello' for nil:NilClass (NoMethodError)" instead of something like (as in your claim) "undefined method `say_hello' for nil:NilClass (NilReferenceError)"?
Ruby is not wrong for this since nil is an object like other objects. It has methods like to_s, so ruby can't ban any call by just raising an exception saying "because it is nil, you cannot do anything. I will give you a NilReferenceError".
You can surely do as your code above if you know that what you are doing may prevent ruby's default behavior.

Related

how to solve NoMethodError efficiently (rails)?

I am new to rails and notice a very odd pattern. I thought some error messages in Django were obscenely cryptic, but building my second app in rails I notice my errors come up NoMethodError more than 90% of the time.
How do rails people tell the difference between all these errors with the same name?
What does NoMethodError mean at it's core? It seems like what you're calling in the template is misspelled, or you're accessing attributes that don't exist?
Where can this error happen? Is the only possible cause in the template/view (the html.erb file)? Or can a bad call in the controller and whatnot cause same error?
Also, what is the best debugger gem to alleviate these issues? Reading the full trace isn't too helpful for beginners at least for a while, I;d like a first hand account of what debugger someone uses instead of reading hype
Thank you kindly
NoMethodError means you are calling a method on an object, but that object doesn't provide such method.
This is a quite bad error in the sense that is reveals a poorly designed and tested application. It generally means your code is not behaving as you are expected.
The way to reduce such errors is:
Make sure that when you are writing the code you are taking care of the various edge cases that may happen, not just the correct path. In other words, you need to take care of validating what's going on and making sure that if something is not successful (e.g. the user is not supplying all the input requested) your application will handle the case gracefully.
Make sure you write automatic test cases that covers the behavior of each method in your codebase
Keep track of the errors. When an error occurs, write a test to reproduce the behavior, fix the code and check the test passes. That will reduce the risk of regression.
This is not a Rails specific error actually. I'll try to explain what's happening at its core.
Ruby is a language that functions through message passing. Objects communicate by sending messages to each other.
The message needs to be defined as a method on the object to respond to it. This can be directly defined on the object itself, the object's class, the object's class's parents/ancestors or through included modules.
class MyObject
def some_method
puts "Yay!"
end
end
> MyObject.new.some_method
Yay!
Objects can define method_missing to handle unexpected messages.
class MyObject
def method_missing(name, *args, &block)
puts name
end
end
> MyObject.new.some_undefined_method
some_undefined_method
Without the method_missing handler, the object will raise a NoMethodError
class MyObject
end
> MyObject.new.some_undefined_method
NoMethodError: undefined method 'some_undefined_method' for #<MyObject...>
So what does this look like in Rails?
$ rails generate model User name:string
Produces this
# models/user.rb
class User < ActiveRecord::Base
end
Which has the following methods implemented among others (by ActiveRecord)
def name
end
def name=(value)
end
When you do the following:
# create a new User object
> user = User.new
#<User ... >
# call the 'name=' method on the User object
> user.name = "name"
"name"
# call the 'name' method on the User object. Note that these are 2 different methods
> user.name
"name"
> user.some_undefined_method
NoMethodError: undefined method 'some_undefined_method' for #<User...>
You'll see the same results whether you're calling it in your console, your model, your controller or in the view as they're all running the same Ruby code.
ERB view templates are slightly different in that what you enter is only evaluated as Ruby code when it's between <% %> or <%= %>. Otherwise, it gets written out to the page as text.
How do rails people tell the difference between all these errors with
the same name?
We usually look at the stack trace that comes back with the response (in development mode) as well as looking in the logs. In a dev environment, I am running my server in a console where I can scroll through the request.
What does NoMethodError mean at it's core? It seems like what you're
calling in the template is misspelled, or you're accessing attributes
that don't exist?
Due to dynamic coupling nature of Ruby. When Ruby determines that an object doesn't have a method by the name of the one that was called. It looks for a method by the name of "method_missing" within that object. If it's not defined then the super one is called which has the default behaviour of raising an exception. Rails leverages this mechanism heavily in it's internal dispatching
Where can this error happen? Is the only possible cause in the
template/view (the html.erb file)? Or can a bad call in the controller
and whatnot cause same error?
This error can happen wherever you have Ruby code, It has nothing to do with rails
Also, what is the best debugger gem to alleviate these issues? Reading
the full trace isn't too helpful for beginners at least for a while,
I;d like a first hand account of what debugger someone uses instead of
reading hype
An Invaluable tool for debugging is the gem 'pry'. It has many useful plug-able tools that greatly simplify debugging

How to Handle Nil Errors in Rails?

What is the correct way to deal with nil errors in Rails? I often get errors like:
NoMethodError (undefined method `questions' for nil:NilClass):
For example, let's say I have chapters in my app which each have a question. I would like to call the question from the previous chapter from within the current question, so I write the following code in question.rb:
def previous_question
self.chapter.previous.question
end
This might cause the above-mentioned error, so I write a method in the model to check if this will lead to a nil result:
def has_previous_question?
self.chapter and self.chapter.previous and self.chapter.previous.question
end
If I make sure to call that before calling previous_question, it can work, but it looks ridiculous. Is there a better way to deal with nil errors in Rails?
I could not tell this is THE right way but it is another way of handling this kind of situation:
def previous_question
self.chapter.previous.try(:question)
end
That way there won't be any error, if there is no previous chapter, the method will simply return nil.
If you want to return something else in case it is actually nil, you can write:
def previous_question
self.chapter.previous.try(:question) || returning_this_value_instead
end
Side note: you don't need to use self in that kind of situation:
def previous_question
chapter.previous.try(:question) || returning_this_value_instead
end
One of my favorite view rendering methods:
http://apidock.com/rails/Object/try
Interesting approach is using nil objects, although they are not suitable for every situation...

Why does is_a? fail to match type, and results in split throwing an exception?

While running RSpec and FactoryGirl, it automatically converts any :id param into a fixnum, whereas, in real life, it gets passed as a string. Code I have that processes that :id as a composite primary key tries to call split on params[:id]. That results in an exception, like so:
NoMethodError:
undefined method 'split' for 234:Fixnum
When trying to call something like this:
#myinstance = MyClass.find(params[:id].split("-").map{|x| x.to_i})
In an attempt to get around this issue, I have added a simple type check:
if params[:id].is_a? Fixnum
#myinstance = MyClass.find(params[:id])
else
#myinstance = MyClass.find(params[:id].split("-").map{|x| x.to_i})
end
Yet, this does not work as I expect it to. The if statement evaluates to false, and the split is still attempted, resulting in the same error. How is it possible that Ruby recognizes params[:id] as a Fixnum in the "else" logic, yet fails to evaluate to true in the if statement?
It's sheer logic: params[:id] is a String or nil.
Oh... I must edit my answer:
When you do controller specs, the params you send are sent as-is and not stringified...
That's a pain but it should be fixed sooner or later.
So in your specs, stringify your params to be closer to reality.
That's a really important point: don't adapt your code to your specs, do the opposite.

Rails Exception in Controller executes querying instance variables?

In my Rails 3.0.11 app, we have very simple code in a controller:
def index
#record = Record.valid # scope around 80,000 records
asdfasdfsa # consider this is a typo to raise NameError Exception
end
The interesting thing is that when it came to the typo, the app seems to query/execute the #record instance variable first before raising an exception. The query costs almost 1 min to get records. So in browser, the page hanges for a long while before coming into an exception template.
If I replace #record with a local variable "record", the querying doesn't happen at all. Anyone knows what it is going on?
As #Khronos points out, it's due to the error message and evaluating the variable, but it's not to_s, it's #inspect.
in actiondispatch/middleware/templates/rescues/diagnostic.erb it calls <%=h #exception.message %> to display the error. A quick jaunt into irb provided this tidbit:
class Object ; def inspect; "foo" ; end ; end
=> nil
a=Exception.new(Object)
=> #<Exception: #<Exception:0x10d8a4108>>
a.message
=> foo
So I think #exception.message will call inspect on the exception, which in turn probably calls inspect on the controller. While it enumerates the entire object during inspect it runs the query, but when it runs to_s I think it drops all that for just the object id.
I'm still a bit fuzzy, but it does at least have to do with the exception and inspect.
See my blog post Ruby's Inspect Considered Harmful for details about this very issue. In short, though:
NameError calls inspect when formatting its error message
The default implementation of inspect calls inspect on all instance variables recursively
NameError throws away the result of inspect if it is longer than 65 characters
For us, this meant that a typo in a View caused Rails to hang for 20 minutes while Ruby built a huge, 20MB string and then proceeded to throw it away
It took us 7 months to get a trivial fix for this into Rails core
In short, I consider the behaviour of NameError to be a heinous bug within the Ruby interpreter. I can think of no sane reason for this implementation.
This is a side effect of the exception handling code.
Think of the behavior you're seeing in your two cases.
Instance Variable -
You've assigned the query to an instance variable of the controller. You then throw an exception and as part of that exception handling rails will call to_s on the controller, which then forces the query to execute as by default it shows all the instance variables.
Local Variable -
You've assigned the query to a local variable of the controller. When the exception is thrown in this case the local variable is just thrown away.
I find it good practice to always override to_s on objects where creating string representations of the structure could be expensive and/or spammy in the ruby console.

Ruby: Dynamically calling available methods raising undefined method (metaprogramming)

I have an Activerecord object called Foo:
Foo.attribute_names.each do |attribute|
puts Foo.find(:all)[0].method(attribute.to_sym).call
end
Here I'm calling all attributes on this model (ie, querying for each column value).
However, sometimes, I'll get an undefined method error.
How can ActiveRecord::Base#attribute_names return an attribute name that when converted into its own method call, raises an undefined method error?
Keep in mind this only happens on certain objects for only certain methods. I can't identify a pattern.
Thank you.
The NoMethodError should be telling you which method does not exist for what object. Is it possible that your find returns no record? In that case, [][0] is nil and you will get a NoMethodError for sure.
I would use .fetch(0) instead of [0], and you will get a KeyError if ever there is no element with index 0.
Note: no need for to_sym; all builtin methods accept name methods as strings or symbols (both in 1.8 and 1.9)
Maybe something to do with access? Like if a class has an attr_protected attribute, or something along that line. Or for attributes that are not database columns, which have no accessors defined?

Resources