how to solve NoMethodError efficiently (rails)? - ruby-on-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

Related

Can access ActiveRecord object, but not its attributes

I'm rendering a view in Rails using form_for and nested_form_fields. Here, #procedure_step is a record that has_many :procedure_step_actions, each of which belongs_to :error, which is a ProcedureError that has (among some relations to other models) an integer :code that I'm trying to access and print out to the page. Here's my template:
<%= form_for #procedure_step do |f| %>
<%= f.nested_fields_for :procedure_step_actions do |act| %>
<%= act.object.error.code %>
<% end %>
<% end %>
When I run this, I get undefined method 'code' for nil:NilClass. Okay, so my relations are messed up and I can't access act.object.error, right? Changing my template to display that instead yields #<ProcedureError:0x0000000ece02a8>, which is what one would expect of a functioning relation. Dumping its contents to the screen using debug shows all the attributes of the record, including code, but I still can't access it with the original template! Clearly act.object.error is not nil, so Rails telling me that act.object.error is nil doesn't make any sense to me.
Frustrated, I tried to work around the problem by using act.object.error.to_json. This printed the correct JSON for the record with all its attributes. Using JSON.load() on this gave me a correct Hash of all the attributes, but using [:code] to try to access the code gives me undefined method '[]' for nil:NilClass. Again, I know that object isn't nil, but Rails still refuses to allow me to access it.
Running out of ideas, I tried to use regular expressions to pull the code out of the raw JSON string. /"code":([0-9]+)/.match(act.object.error.to_json) returned #<MatchData "\"code\":69" 1:"69">, which is right. I used [1] to try to access the code number that was matched, but again I got undefined method '[]' for nil:NilClass.
Enough with ActiveRecord, I thought to myself. I decided to turn to raw SQL queries. I got the ID of the error in question using act.object.error_id, then printed that to the screen first to make sure I could access it. Luckily, I could. Then I inserted it into my SQL query with "... WHERE id = #{act.object.error_id}". I refreshed the page again and was greeted with a SQL error. It showed the final SQL query string I had generated, but it ended with WHERE id =. The ID of the error didn't get added to the string. ProcedureError.find(action.object.error_id) gave a similar error.
I'm totally out of ideas. What could possibly be preventing me from accessing one simple integer in so many different ways?
There are at least a couple of issues here. The first is that you probably want to be using fields_for, rather than nested_fields_for, if you're using 4.x.
The second is similar to what the first answer has indicated. You have a nested fields form, which allows you to nest one level in, but you are trying to nest two levels in. By addressing your law of demeter violation you should be able to make some more progress.
Debugging things like this you can get more information by throwing in a binding.pry or byebug right in your erb.
<%- binding.pry %>
Then reload the page. Your server will be stopped at that point in your code and you can play with variable values to learn more about what's going on.
One thing I can see right off the bat is you are violating Law of Demeter here
act.object.error.code
The form object obviously has to stay but you can delegate access to the subobjects by making a method on the procedure_step which can help with handling nulls, and other error cases.
Try delegating that first as I'm not sure if the scope that is created by nested_forms_for will allow the ActiveRecord::Relation object to perform properly. I'll double check locally.
A delegation might look like the following
class ProcedureStepActions
belongs_to :error
def error_code
#error.code
end
end
EDIT:
Other things that might be helpful are the version of Ruby and Rails you are using and any other additional gems or libraries.

Why isn't there a NilReferenceError in ruby?

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.

Currently executing code

I'm looking for way to see currently executing code. My reason is: I have ruby classes, that are been monkey patched by lots of patches, so its close to impossible to track which methods were changed. Is there any way to check what code has been loaded into memory and currently executing?
Ask for more details if you need
Another way besides the debugger mentioned by tessi, would be to use ruby-prof to benchmark the whole application and analyze the generated tree.
Every called method is in there, so the desired or not desired versions as well.
Getting the Origin of a Method
In ruby 1.9 and newer, you can open a debugger/console at any point and ask the method for it's source location.
For example, when looking for the definition of the admin? method of my User class I can do the following:
user = User.first
=> #<User id: 1, ...>
user.method(:admin?).source_location
=> ["/Users/tessi/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activemodel-4.0.13/lib/active_model/attribute_methods.rb", 382]
It tells me that the admin? method is defined in ActiveModel in the file above and in line 382 of that file.
In a similar way you could iterate over all methods of your class and check the origin of the methods:
user.methods.map {|method_name| user.method(method_name).source_location}
Monkey Patching
This still works with patched classes. When opening a rails console, I can edit my User class and look at the source_location again:
class User < ActiveRecord::Base
def admin?
puts 'the patched admin? method'
super
end
end
User.first.method(:admin?).source_location
=> ["(pry)",2]
Now, the method's location is in my console (pry) at statement 2. This works, because my patch creates a new Method-object which replaces the old method in the method dictionary of the User class. The new method object returns a different source_location.

Race Condition in Ruby on Rails

I'm running into a weird bug on Heroku, which I believe may be a race condition, and I'm looking for any sort of advice for solving it.
My application has a model that calls an external API (Twilio, if you're curious) after it's created. In this call, it passes a url to be called back once the third party completes their work. Like this:
class TextMessage < ActiveRecord::Base
after_create :send_sms
def send_sms
call.external.third.party.api(
:callback => sent_text_message_path(self)
)
end
end
Then I have a controller to handle the callback:
class TextMessagesController < ActiveController::Base
def sent
#textmessage = TextMessage.find(params[:id])
#textmessage.sent = true
#textmessage.save
end
end
The problem is that the third party is reporting that they're getting a 404 error on the callback because the model hasn't been created yet. Our own logs confirm this:
2014-03-13T18:12:10.291730+00:00 app[web.1]: ActiveRecord::RecordNotFound (Couldn't find TextMessage with id=32)
We checked and the ID is correct. What's even crazier is that we threw in a puts to log when the model is created and this is what we get:
2014-03-13T18:15:22.192733+00:00 app[web.1]: TextMessage created with ID 35.
2014-03-13T18:15:22.192791+00:00 app[web.1]: ActiveRecord::RecordNotFound (Couldn't find TextMessage with id=35)
Notice the timestamps. These things seem to be happening 58 milliseconds apart, so I'm thinking it's a race condition. We're using Heroku (at least for staging) so I think it might be their virtual Postgres databases that are the problem.
Has anyone had this sort of problem before? If so, how did you fix it? Are there any recommendations?
after_create is processed within the database transaction saving the text message. Therefore the callback that hits another controller cannot read the text message. It is not a good idea to have an external call within a database transaction, because the transaction blocks parts of the database for the whole time the slow external request takes.
The simples solution is to replace after_save with after_commit (see: http://apidock.com/rails/ActiveRecord/Transactions/ClassMethods/after_commit)
Since callbacks tend to become hard to understand (and may lead to problems when testing), I would prefer to make the call explicit by calling another method. Perhaps something like this:
# use instead of .save
def save_and_sent_sms
save and sent_sms
end
Perhaps you want to sent the sms in the background, so it does not slow down the web request for the user. Search for the gems delayed_job or resque for more information.
Do you have master/slave database where you always write to master but read from slave? This sounds like the db replication lag.
We solved such problems by forcing a read being made from the master database in the specific controller action.
Another way would be to call send_sms after the replication has been finished.

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.

Resources