Why can't I access this value in an (OpenStruct) object? - ruby-on-rails

I have an (OpenStruct) object for which I can only seem to access some values. A simplified version of it is below:
#<Project::API::FormData::FormAnswer:0x007f94de911ed0
#form_answer=
#<OpenStruct
user_id=130,
timer_value=[{"foo" => "bar"}]
>
>
If I want to get user_id, that's easy
> answer.user_id
130
But what about timer_value?
> answer.timer_value
NoMethodError: undefined method `timer_value' for #<Project::API::FormData::FormAnswer:0x007f94de911ed0>
> answer.timer_value
NoMethodError: undefined method `timer_value' for #<Project::API::FormData::FormAnswer:0x007f94de911ed0>
maybe, for some reason, for this I need to access it through the form_answer first:
> answer.form_answer.timer_value
NoMethodError: undefined method `form_answer' for #<Project::API::FormData::FormAnswer:0x007f94de911ed0>
---
> answer.first.timer_value
NoMethodError: undefined method `first' for #<Project::API::FormData::FormAnswer:0x007f94de911ed0>
I'm not experienced with OpenStruct - is this the cause?

I have an (OpenStruct) object
No, you don't. You seem to have a PORO* that has a field/instance_variable of type OpenStruct. And given the fact that answer.user_id works, you likely have something like this in that class:
delegate :user_id, to: :#form_answer
That is, FormAnswer class re-exports a property of one of its internal fields. But does not re-export timer_value, for example. Nor does it expose #form_answer.
* plain old ruby object

Thanks Sergio, I found that just as you posted - is indeed an issue in where the class is defined - which I thought that I'd read through, but as ever read and read again. It seems whomever created the class left a bug monkeying up the method definitions:
fields.each do |field_name|
define_method(field_name) { #form_answer.public_send(field_name) }
end
Which is all good, but the fields array was malformed meaning only some values were coming through.
> answer.respond_to?(:timer_value)
true
Bingo!

Related

Rails 3 - NoMethodError: undefined method for nil:NilClass in each Iteration

I'm iterating over an array of instances of a Rails model. Here is my code:
product_details.each do |product_detail|
product_detail.label = Backend::ProductGroup.where(product_group_number: product_detail.product_group).first.label
end
The attribute 'label' from 'product_detail' isn't an attribute from my Rails ActiveRecord model. I added it with attr_accessor in my class definition. I did this, because I wanted to add this attribute dynamically, only when I need to do this. When I ran the code without the 'each' iteration in my rails console it works just fine. But when I execute the above code I get the following error message:
NoMethodError: undefined method 'label' for nil:NilClass
Did I do something obviously wrong?
Many thanks in advance.
You likely have several product_detail items that have no matching product_group. So calling .first on the empty collection returns nil. To get around the error, you can test if the product_group was found before proceeding:
product_details.each do |product_detail|
product_group = Backend::ProductGroup.where(product_group_number: product_detail.product_group).first
product_detail.label = product_group.label if product_group
end
You can also do this more efficiently like so:
group_labels = BackEnd::ProductGroup.
where(product_group_number: product_details.map(&:product_group)).
inject({}){|m, g| m[g.product_group_number] = g.label; m}
product_details.each do |product_detail|
product_detail.label = group_labels[product_detail.product_group]
end
This will result in a single database call to grab all related groups, and put the labels in a keyed hash for easy discovery and assignment.

How to catch an "undefined method `[]' for nil:NilClass" error?

I get an nested array from facebook via omniauth and wanna check if it's empty?/nil?/exists?
the depending line looks like:
unless omniauth['extra']['raw_info']['location']['name'].nil?
This should check if this part of the array is empty or exists.
But always this error was thrown:
undefined method `[]' for nil:NilClass
Do I check arrays wrong?
I tried it with "has_key" "nil?" "empty?" "exists?" "blank?"
But no one of these works!
Please help me, many thanks in advance!
Ideally you should check each nested level to see if it is nil, however, this will also work.
unless (omniauth['extra']['raw_info']['location']['name'] rescue nil).nil?
You can also rescue the NoMethodError specifically.
This error is raised because one of the hash values in the chain of omniauth['extra']['raw_info']['location']['name'].nil? returns nil and it is not the last call ['name'].
If for example omniauth['extra']['raw_info'] returns nil, you're actually trying to call nil['location'] which raises an error in ruby.
You can catch this error simply:
res = omniauth['extra']['raw_info']['location']['name'].nil? rescue true
unless res
#your code here
end
Please notice that the code block above will fill the variable res with true if the ['name'] hash value is nil or any other hash value in the chain returns nil.
A bit late to the party, but, as pointed in this answer, Ruby 2.3.0 introduced a new method called dig, which would return nil if one of the chained keys is nil. Your omniauth auth hash could then be presented as:
omniauth = {
...
"extra"=>{ "raw_info"=>
{ "location"=>"New York",
"gravatar_id"=>"123456789"}}
...
}
omniauth.dig('extra',
'raw_info',
'location',
'name',
'foo',
'bar',
'baz') #<= nil

Is ActiveRecord lying?

My ActiveRecord is not telling the truth. See for yourself:
> User.posts.class
=> Array
> User.posts.all.class
=> Array
> a = Array.new
> a.class
=> Array
> a.all
NoMethodError: undefined method `all' for []:Array
There is no Array#all method, so User.posts can't be an Array. What is going on here?
this is the way that associations are implemented in rails. they are lazy proxy objects.
have a look at the documentation for further infos on this: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/collection_proxy.rb#L25
Ruby has a notion of singleton methods. This means that objects can change the behaviour defined by their class. Read this.

Cannot access data in active record using dot notation

I created a model in Ruby and am stuck on a n00b issue. In Rails Console:
s = Survey.where(:keyword => 'foo')
=> [#]
s.inittxtmsg
NoMethodError: undefined method inittxtmsg' for #<ActiveRecord::Relation:0x10350f8f8>
from /Library/Ruby/Gems/1.8/gems/activerecord-3.0.5/lib/active_record/relation.rb:371:inmethod_missing'
from (irb):3
Shouldn't I be able to see the values by typing s.Survey_id, s.inittxtmsg, s.keyword, s.store?
Thank you!
Survey.where(:keyword => 'foo')
returns an array of results, so you are really calling .inittxtmsg on an array, which obviously doesn't exist.
You could do something like:
Survey.where(:keyword => 'foo').first.inittxtmsg, in which it is calling it on the actual model object.
Or if you know that there is only one survey with the keyword = foo... you can use the find method to only return a single model object:
s = Survery.find_by_keyword("foo")
s.inittxtmsg

Rails will return an object successfully, but complains that the object is NIL if I try to access an attribute of it

I am trying to populate a nested field, product_name, it an Item.
In my Item model:
class Item < ActiveRecord::Base
attr_writer :product_name
belongs_to :order
belongs_to :product
def product_name
#Product.find_by_id(self.product_id) #=> returns the product object
#self.product #=> returns the product object
#Product.find_by_id(self.product_id).name #=> complains that 'name' is being called on nil
#self.product.name #=> complains that 'name' is being called on nil
#"foooobar" #=> returns "foooobar"
end
end
Each of those commented lines behaves as described.
What I don't get is, how can something return an object successfully, but then complain that the object is nil when you access an attribute of it?
Here is the whole error:
NoMethodError in Orders#edit
Showing app/views/orders/_form.haml where line #18 raised:
undefined method `name' for nil:NilClass
Extracted source (around line #18):
17: = item.hidden_field :product_id
18: = item.text_field :product_name, :class => 'auto_complete_input'
19: = item.text_field :quantity, :size => 2
Thanks
You may want to take a look at the andand plugin at http://github.com/raganwald/andand.
Basically, what it does is handle the errors that may arise from trying to call a method on another that has the possibility of being nil.
Based solely on your example:
Product.find_by_id(self.product_id).name #=> complains that 'name' is being called on nil
An implementation of andand would look like:
Product.find(self.product_id).andand.name
And even if the first statement, Product.find(self.product_id), returns nil it would no longer fire the undefined method on nil error.
PS:
Again, based solely on your code, I can see that you're reproducing the functionality generated by using the :belongs_to association. Essentially, what it means is that you can use:
self.product
insead of:
Product.find_by_id(self.product_id)
It sounds like you're asking a really general question. If you're asking a totally different question than I'm hearing, ignore this. :)
Yes, Ruby is successfully returning an object, and the object is nil. In Ruby, a method can return any value, and nil is just another value that a method can return. Those statements are probably doing a query that isn't returning any results, so the method returns nil instead of the object you are expecting.
The longer answer is that Product.find_by_id(x) calls a generated find_by_ attribute method for the id field, which is the same as calling Product.find(:first, :conditions => ["id = ?", x]). If you look at the documentation for .find, you will notice that it says
Find first - This will return [blah blah black...] If no record can be matched, nil is returned.
Have you given proper relationship in your product model and item model, i think giving proper association can solve your error.

Resources