Why does this Ruby statement throw an exception? (Arrays/Bools) - ruby-on-rails

I'm not a Ruby guy, I just play one on television. I have to modify someone's old Cron job to pull down some JSON and convert it into objects.
Here's the code
raw_json = Net::HTTP.get(URI.parse("url removed to protect the innocent"))
tags = ActiveSupport::JSON.decode(raw_json)
puts tags.count
tags.count will accurately trace as 5, but THAT LINE immediately causes a crash as follows:
5 #the accurate count!
rake aborted!
undefined method `count' for false:FalseClass
What is the dealio?

What is the contents of raw_json? What appears to be happening is that ActiveSupport::JSON#decode is returning false (hence undefined method 'count' for false:FalseClass). I think JSON#decode only returns false when given an empty string, which would mean HTTP#get is returning an empty string. Check on raw_json and see if it contains what you expect.

so I have no idea what is going on here, but JSON.decode should give you a hash, which doesn't have a count method. It does have a size method though
tags.size
if that doesn't work, try doing p tags, or puts tags.class.name to try and figure out what you are working with

Apparently tags is false , which may mean that your Net::HTTP.get failed (I guess your URL is wrong).
Try to print tags to see what it is. (I guess anyway, that you should use a valid URI)

The problem is that:
>> ActiveSupport::JSON::decode("")
=> false
>> ActiveSupport::JSON::decode("false")
=> false
This is a very strange behavior.

Related

Get objects in date range when one of the dates is NULL in Rails 5

I am developing a Rails 5 app and at one part I want to get all object that are inside a given date range:
geo_locations.where(:created_at => #ride.first_at..#ride.last_at)
But in some cases first_at or last_at are NULL. If one of them is NULL I get the error:
ArgumentError (bad value for range)
In this case I would like to simply get an empty result. If first_at and last_at are NULL I do not get an error.
How can I avoid this error?
A couple of ways of doing it:
geo_locations.where(:created_at => #ride.first_at..#ride.last_at) rescue []
OR
return [] unless #ride.first_at && #ride.last_at
geo_locations.where(:created_at => #ride.first_at..#ride.last_at)
I would suggest second option as it reduces the risk of rescuing other unknown errors/exceptions
Not explicitly stated, but I assume you want an ActiveRecord::Relation as your return value still, and not an Array (as in Md. Farhan Memon's answer)? If so, you can use an ActiveRecord::NullRelation to achieve this by using ActiveRecord::QueryMethods#none.
if #ride.first_at && #ride.last_at
geo_locations.where(created_at: #ride.first_at..#ride.last_at)
else
geo_locations.none
end
This way, you'll always get an empty result back after executing this query. Also, the query remains chainable as an ActiveRecord::Relation before executing it, regardless of which branch is taken in the conditional. Which is nice because it keeps the return type consistent!

Checking for nested param in Rails 4

In my controller, I used to be able to say:
if params[:business][:branch]
After Rails 4, when I try the same I get:
NoMethodError: undefined method `[]' for nil:NilClass
This is the only way I can find to do it in a single line now.
params.has_key?(:business) ? params[:business].has_key?(:branch_id) : false
Kind of verbose.
There are many possible answers, one of which you proposed in your question.
I really love the Hash#fetch method. It returns the value associated with a Hash key, and optionally allows you to supply a default to return in case the key is missing. With that, you can make a construct like this:
if params.fetch(:business, {}).fetch(:branch, false)
# do stuff
end
This way you don't even need any conditionals or Hash key presence checks in your code.
I am a fan of
if params[:business][:branch].present?
..
end
just because it keeps the params[sym] form so it's easier to read.
You can also use blank? http://api.rubyonrails.org/classes/Object.html#method-i-blank-3F
unless params[:one].blank? && params[:two].blank?
will return true if its empty or nil
also... that will not work if you are testing boolean values.. since
>> false.blank?
=> true
in that case you could use
unless params[:one].to_s.blank? && params[:two].to_s.blank?

Trouble Accessing Ruby Array and Hash Value

I am making an api call and receiving the following response (it's long, so I'm showing the important part):
... "fields":{"count"_1:["0"],"count_2":["5"]} ...
when I do:
call["fields"]["count_1"]
It returns
["0"]
I need it to give me just the integer. I tried:
call["fields"]["count_1"][0]
And I also tried:
call["fields"]["count_1"][0].to_i
I'm running this in Rails and it gives me the error:
undefined method `[]' for nil:NilClass
But it's not working.
Try as below using String#to_i
call["fields"]["count_1"][0].to_i # => 0
Some tips:
Try wrapping the API response in JSON.parse(...). That is, if you're not making the call via a gem that already does this. This might require 'json'.
Try call['fields']['count_1'].first.to_i
Do some debugging: check the value of call.class, call['fields'].class and call['fields']['count_1'].class. The last one should definitly be an Array.
Add an if clause to check if call['fields'][['count_1'].is_empty?.
Look for typos :)
For some reason the API call was making the zeros nil instead of zero. Thanks for all your help.

Rails 3.0 Model without Validation does not get saved

Looks like I messed up my code in a very subtle way... suddenly one of my models doesn't save anymore.
First thing I tried was to see if the same commands work in the Rails console, and to my surprise it showed the record(s) not being valid.
To narrow-down the problem, I commented-out all validations and tried again -- but it still did not work!
m = MyModel.find 123
=> record which looks OK to me
m.valid?
=> false
m.errors
=> #<OrderedHash {}> # An EMPTY hash??? Is that a Rails bug?
Has anybody seen something like this before?
Answering my own question.
Apparently one of my before_validation methods returned false - ugh - stupid mistake!!
Here's what the ActiveRecord documentation has to say on that topic:
before_validation* returning statements
If the returning value of a before_validation callback can be evaluated to false, the process will be aborted and Base#save will return false. If ActiveRecord::Validations#save! is called it will raise a ActiveRecord::RecordInvalid exception. Nothing will be appended to the errors object.
Yeah, it works as advertised!! :)
See also: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

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.

Resources