HttpParty::Response and try(:parsed_response) weird behavior - ruby-on-rails

I've tried to use ActiveSupport's (2.3-stable) try() on an instance of HttpParty::Response. I've got a pretty strange behavior:
> ro.parsed_response
{"error"=>"RecordInvalid", "description"=>"Record validation errors", "details"=>{"email"=> [{"description"=>"Email: foo#bar.com is already taken by another user"}]}}
> ro.try(:parsed_response)
NoMethodError Exception: undefined method `parsed_response' for #<Hash:0x11397d5d8>
In the first example, I send the parsed_response message to ro by using the dot notation. It works fine. In the second one, I try to call it using ActiveSupport's try(), and it (surprisingly) raises the NoMethodError exception.
Shouldn't try() have returned nil in this case? And why doesn't it find the "parsed_response" method, if I can call it using the dot notation, as seen in the first example?
Thanks in advance!

Have no idea how, but seems like ro object was "transfered" into a hash that has been sent a parsed_response method.
In Rails 2.3#ruby 1.8 undefined method for object raises an exception.
Besides, docs stay only for calling try on nil object and nothing about the object that doesn't have a specific method:
Unlike that method however, a NoMethodError exception will not be
raised and nil will be returned instead, if the receiving object is a
nil object or NilClass.

Related

Why is the .try method terminating my program?

I am capturing a string date from params that should be in the form of YYYY-DD-MM. If the parameter is an invalid date, I want nil to be assigned to minimum. Like if the param was 2013-40-50 for example. I am able to do so successfully with :
minimum = Date.try(:parse, (params[:newer_than])) if params[:newer_than].present?
If the date is of the correct format, it is assigned to minimum, otherwise nil is. However, in the case where it is nil, the program terminates and I get ArgumentError - invalid date.
The whole idea of me using .try is that if it in fact fails, it's okay, move along. I am handling the case if it were nil afterward. The error is thrown on the line assigning to minimum.
How can achieve that?
It's the way you're using the call.
The try call is not like a begin/rescue. It just tries to call the function if it exists on that object. If the function does not exist, no biggie, it won't call the function and just proceed. Otherwise, it calls it and works as it normally would.
In this case, calling try does you no good, as you know for sure that function exists for date. What's happening is the parse function is throwing an error.
What you should do instead is go ahead and call Date.parse within a begin/rescue block, and catch those particular errors (ArgumentError) that could be thrown by the parse function. You could also pre-validate the string (using regex, for example) to ensure it's in the proper format before calling parse.
Either way, parse does not do any validation for you, so either you can check it before hand or begin/rescue the errors it throws.
You can use rescue for your goal:
minimum = begin
Date.parse(params[:newer_than])
rescue ArgumentError, TypeError
nil
end
According to docs, try just like the regular Ruby Object#send if methos defined on object, so it does not prevent errors like ArgumentError and TypeError.

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.

How can I get last only if more than one?

In rails I have this:
page_classes.split(/\s/).last
Sometimes page_classes doesn't contain any whitespace and I get the error:
undefined method `last' for nil:NilClass
How can I get the last unless there is only one?
try this
page_classes.split(/\s/).try(:last)
The problem is about your page_classes object.
page_classes.split("\s").last
Calling .last will ALWAYS work with an array (even empty). If you have probems, it means your page_classes object is nil or not a string that can be processed by split().

Rails JSON conversion error

I'm getting a strange error when trying to convert my object to json for an API connection. The following details my experience.
If I call
JSON.generate(self)
the output is
{"validation_context":null,"errors":{},"params":{"number":"123","name":"test"}}
I only need the params in my json object and when I call
JSON.generate(self.params) # or the next line
JSON.generate(#params) #params has been set on the object as an accessor
I get
undefined method `merge' for #<JSON::Ext::Generator::State:0x1043f1a38>
For some reason params is not considered a Hash. It serializes ok when I'm getting the parent object but fails otherwise. How can I serialize just the params?
Turns out I found a relatively simple solution.
Rather than
JSON.generate(object_to_serialize)
Using
object_to_serialize.to_json
Will work as intended.

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