#user.visit_count => undefined method - ruby-on-rails

I am doing what I thought was something very simple - finding a user and incrementing an integer.
#user = User.where("created_at > ?", Time.now.midnight).select(:visit_count)
#user.visit_count += 1
#user.save(:validate=>false)
I get the following error:
undefined method `visit_count' for [#<ActiveLink visit_count: 1>]:ActiveRecord::Relation
This seems like a Rails 3 thing - where am I going wrong?

Your query always returns multiple results as an Array.
Just add .first to be sure that you only pick the first result.
#user = User.where("created_at > ?", Time.now.midnight).select(:visit_count).first
If you want to update many records at the same time, look at update_all method :
http://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-update_all

Related

Why does `where` give an "undefined method" error in Rails?

I'm trying to get all the feedbacks with a specific attribute. I am using this code:
def index
feedbacks = Feedback.all
if params[:tag]
#average_customer_rating = feedbacks.where('buyer_feedback_date is not null').rated(Feedback::FROM_BUYERS).average(:buyer_rating) || 0
#products = Product.includes(:images).tagged_with(params[:tag]).order('DESC').limit(22)
else
#products = Product.includes(:images).all
#average_customer_rating = feedbacks.where('buyer_feedback_date is not null').rated(Feedback::FROM_BUYERS).average(:buyer_rating) || 0
end
end
and Rails shows this error:
undefined method `where' for []:Array
Why can't I use where here and how do I fix it?
In Rails 3.x, all returns an array:
feedbacks = Feedback.all # <- an array
feedbacks.where(...) # <- fails
To get an ActiveRecord::Relation, you have to use scoped:
feedbacks = Feedback.scoped # <- an ActiveRecord::Relation
feedbacks.where(...) # <- works
See Working with scopes for more examples.
Note that this distinction is not needed in Rails 4 anymore – scoped was deprecated in favor of all which now returns an ActiveRecord::Relation.
Why can't I use where on an array?
Because Array class does not have instance method called where.
Since feedbacks.class is Array, you get an error.
You have two options:
Define feedbacks as instance of ActiveRecord::Relation and use where
Not using where but appropriate methods from Array class.
If Feedback is your ActiveRecord model, you could probably use Feedback.where('buyer_feedback_date is not null').
feedbacks = Feedback.all
Why would you query every entry in this table to get
feedbacks.where('buyer_feedback_date is not null').rated(Feedback::FROM_BUYERS).average(:buyer_rating) || 0
Try this =>
#average_customer_rating =Feedback.where('buyer_feedback_date is not null').rated(Feedback::FROM_BUYERS).average(:buyer_rating) || 0
else
#average_customer_rating = Feedback.where('buyer_feedback_date is not null').rated(Feedback::FROM_BUYERS).average(:buyer_rating) || 0
Should scope it to cust_feedback

Undefined method '+' for nilclass with rails model (active record)

I have a simple model with some integer values (I think, I set them to integer in the migration file at least) and I'm just trying to increment them with a member function of the model but when I try to add to them I'm getting the error "Undefined method `+' for nil:NilClass)
Any tips here??
def take()
#total -= 1
User.find(#poster_id).lifetime -= 1
end
def give()
#total += 1
....
nothing more to it really, it's just simple not working. do I need to cast these somehow? I made sure to initialize the values to 0 upon each instantiation of the model class
Firstly, if total is a column in database (and you say it is), then within the instance method of the class you should access it as total, not #total. Secondly, if you want to update the total attribute you should, well, update it :)
def take # notice no () - they are optional
update(total: self.total - 1)
end
def give
update(total: self.total + 1)
end
Analogically with poster_id (if, again, it is a column in db) you would do:
user = User.find(poster_id) # notice not #poster_id
user.update(lifetime: user.lifetime - 1)

How can I limit an existing Rails AR query by a certain time frame?

I have a dashboard(esque) view in a Rails app which is showing some data in similar ways but broken out into many time periods.
I have some code in my controller like so:
#issues_this_month = Issue.where('issues.created_at BETWEEN ? AND ?', DateTime.now.in_time_zone.beginning_of_month, DateTime.now.in_time_zone.end_of_month)
and I also want to create a variables which shows issues this year and issues all time so I have this code:
#issues_this_year = Issue.where('issues.created_at BETWEEN ? AND ?', DateTime.now.in_time_zone.beginning_of_year, DateTime.now.in_time_zone.end_of_year)
I am curious if someone can think of a good way of doing one query, and from that inferring the date ranges all while avoiding the extra queries. Should I pass the results to a helper method and do the logic there?
in the model... you can define
def date
self.created_at.to_date
end
then in the controller
start = Date.today.beginning_of_year
end = Date.today.end_of_year
#issues_this_year = Issue.where(create_at: start..end).group_by(&:date)
now you have a hash of [month_1, {issues that exist in month_1}, month_2, {issues that exist in month_2}, etc]. play with it in the console to find the proper keys... #issues_this_year.keys
How about defining a method like
def self.in_timeframe(start_time=DateTime.now.in_time_zone.beginning_of_month,
end_time=DateTime.now.in_time_zone.end_of_month)
Issue.where('issues.created_at BETWEEN ? AND ?', start_time, end_time)
end
You can now invoke this as follows:
Issue.in_timeframe # For issues in the month
Issue.in_timeframe(x,y) # For issues within x-y timeframe
If you want the data in a single query, you could do stuff like:
def self.in_timeframes(time_frames)
data = {}
times_frames.each do |time_frame|
data[time_frame[:name]] = Issue.in_timeframe(time_frame[:srtart]. time_frame[:end])
end
data
end
You can invoke the above method using:
time_frames = [{:name=>"month"},
{:name=>"x-y", :start=>x, :end=>y}]
Issue.in_timeframes(time_frames)

Rails - wrong number of arguments (2 for 0..1) error

I am having trouble with some code inside an application I am working on.
With the following code:
#herbivores=Deer.find(:all,:conditions =>['state like?', '%' + params[:number]+'%'])
#herbi=#herbivores.find(:all,:conditions =>['city like?', '%bad%'])
I receive the error:
wrong number of arguments (2 for 0..1)
Can anybody explain what is happening?
Use the query API to keep the correct scope, and also do this more cleanly since where is chainable:
#herbivores=Deer.where('state like ?', '%' + params[:number]+'%')
#herbi=#herbivores.where('city like ?', '%bad%')
You can also chain these directly without an intermediate variable:
#herbi = Deer.where('state like ?', "%#{params[:number]}%").where('city like ?', "%bad%")
Or you can merge it into one method call:
#herbi = Deer.where('state like ? AND city like ?', "%#{params[:number]}%", "%bad%")
I believe what is happening is that you are treating #herbivores like its a model that you can find on, but it is an Array of Deer records so is not a model.
EDIT:
Purhaps you want:
#herbivores=Deer.find(:all,:conditions =>['state like ?', "%#{params[:number]}%"])
#herbivores.each do |herbi|
if herbi.city == 'bad'
puts "bad city in state #{ani.state}"
end
end

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