Cannot access data in active record using dot notation - ruby-on-rails

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

Related

lambda select map difference in rails 4

Servernode is Rails Model Class.
I found something strange to me when I try to use lambda.
aaa = lambda {|node| node.available="Available"}
Servernode.select(&:aaa)
=> It can return array of ModelObject which meets the condition
But, when I try to
bbb = lambda {|node| node if node.available="Available"}
Servernode.map(&:bbb)
NoMethodError: undefined method `map' for #<Class:0x000000067759b0>
The result is not what I expected and don't know why?
Thanks for help!
This is because select is an ActiveRecord::QueryObject method. I think you've confused it with the select method for Ruby Enumerable. The map method is only available for enumerables as well.

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.

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.

getting ID of activerecord model

Current Code
#current_site_name = 'SITENAME'
#current_site = Site.where(:name => #current_site_name)
#current_site_id = #current_site.id
Basically, the name column in our Site model is unique. We are selecting a row using the site name as a parameter.
I am returned: <ActiveRecord::Relation:0x59426bc1>
I understand that I cannot simply ask for #current_site.id but is there any way to put the ID of the single activerecord into the variable #current_site_id
Again, there will always be one active record.
As you've seen, where will return an ActiveRecord::Relation (a set of rows), even if there's only one row that matches your criteria. You can make #current_site be assigned to the row you want by changing the line to this:
#current_site = Site.where(:name => #current_site_name).first
Hmm... This is quite strange.
Site.where(:name => #current_site_name).methods.include? :id #=> false
but
Site.where(:name => #current_site_name).instance_methods.include? :id #=> true
However,
Site.where(:name => #current_site_name).instance_eval "id" #=> NameError
and
Site.where(:name => #current_site_name).methods.include? "self.id" #=> NoMethodError
That said, a workaround assuming in your case, only one record should be returned is
Site.where(:name => #current_site_name).instance_eval('find_first').id
The find_first method is protected so we have to jump scope in its execution to avoid another NoMethodError
Just to throw another solution out there, you can make use of Rails' dynamic finders that are created for your models.
#current_site = Site.find_by_name(#current_site_name)
This returns a single record (and thus doesn't require the use of first) and might be seen as cleaner code in some people's opinion.

Rails 3 - Undefined method on certain collections

I have a search method written for my model Link.
I've been able to called this method without error until implementing voting. For example, these all work:
Link.search(params[:search])
current_user.links.search(params[:search])
current_account.links.search(params[:search])
The following does not work:
#links = current_user.votes.collect {|vote| vote.voteable}
#favorites = #links.search(params[:search])
and return this error:
undefined method `search' for #<Array:0x00000006919ac8>
I've done some testing, to see if my class is wrong, in the console:
links = user.votes.map {|vote| vote.voteable}
links.class
=> Array
links.first.class
=> Link
This should be no different than my working examples:
user.links.class
=> Array
user.links.first.class
=> Link
I thought maybe the error was from me calling search on an array and not a link. But in previous examples I'm also calling it on an array.
I'm using vote_fu to handle the voting thus the vote/voteable.
The search function or scope that you have defined is defined on the Link object and is usable in Link relations, but it is not defined on a simple array, which is what is getting returned from the first collect example. Here is a simple distinction:
class User
scope :search, lambda{ |name| where(name: name) }
end
User.search('Kombo').all # Returns an array of the SQL result run against the DB
User.all.search('Kombo') # NoMethodError: undefined method `search' for #<Array:0x000001079b15b0>
In your first example, Link.search(params[:search]), you are performing the equivalent of User.search.all, and User is a scoped ActiveRecord relation/object, which means it can continue to be combined with other scopes, like where, limit and group. In the second example, #links = current_user.votes.collect {|vote| vote.voteable}, collect is acting on such a relation and is returning a simple array which can no longer be acted upon with these scoped functions. The second example is like doing User.all.search.
It's confusing because both of these examples resolve to an Array eventually, but the difference is what is happening before that resolution to an Array, and when you are actually calling the search function. To get around this you'll have to actually call the search scope or function on an ActiveRecord object, like Link or an ActiveRecord Relation like current_user.links, but you won't be able to call it on a result. Just to clarify:
Link.search(params[:search]) # will work
Link.all.search(params[:search]) # will not work
current_user.links.search(params[:search]) # will work
current_user.links.all.search(params[:search]) # will not work
current_account.links.search(params[:search]) # will work
current_account.links.all.search(params[:search]) # will not work
When you call .collect you are implicitly calling .all, which breaks the scope chain. The following two commands are equivalent in that respect:
#links = current_user.votes.collect {|vote| vote.voteable}
#links = current_user.votes.all.collect {|vote| vote.voteable}

Resources