Why does `where` give an "undefined method" error in Rails? - ruby-on-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

Related

Rails model results to hash - variable as a key issue

I've tried to put results from my vote model in a hash for further usage, but I don't know how to create a hash key from a variable in Ruby. See example below:
def create_hash_array(campaign_votes)
target_hash = Hash.new
campaign_votes.each_with_index do |cv,i|
target_hash[cv.content_id] = {} if i == 0
if target_hash[cv.content_id].member?(cv.vote_button_id)
target_hash[cv.content_id][cv.vote_button_id] = (target_hash[cv.content_id][cv.vote_button_id]).to_i + 1
else
target_hash[cv.content_id] = {cv.vote_button_id => nil}
end
end
target_hash
end
Usually I got an error:
undefined method `member?' for nil:NilClass
but it comes from unrecognized target_hash[cv.content_id], how can I make does variable to be recognized target_hash[cv.content_id] ??
I think your code can be boiled down to this:
def create_hash_array(campaign_votes)
target_hash = Hash.new { |h,k| h[k] = Hash.new(0) }
campaign_votes.each do |cv|
target_hash[cv.content_id][cv.vote_button_id] += 1
end
target_hash
end
There's multiple problems here, many to do with getting all tangled up in the process. You initialize the element of the target_hash structure only on the 0 index position, yet each campaign_vote could have different content_id values, meaning you're missing out on those.
This approach creates a single auto-generating Hash that will populate keys with counter hashes, that is hashes defaulting to 0. That means you can always navigate them and += 1 will work because of the default.
This approach is pretty common in Ruby, especially Hash.new(0), which is super handy for doing simple counters of arbitrary objects.

Array in Ruby on rails, undefined method `minimum'

#click = Missing.select{|d| d.click < 10000}
find = #click.minimum(:id)
#data = Missing.where(id: find)
dataSize = #data.size
#renderData = #data[dataSize - 1]
render :json => #renderData
Errors:
undefined method `minimum' for #<Array:0x00000004acd678>
Error code : find = #click.minimum(:id)
I have no idea why it is wrong.
render :json = #click is working.
find = Missing.minimum(:id) is working.
#click = Missing.select{|d| d.click < 10000} is returning an array of active record instances. This is why minimum can not be called against an array.
If you want the first record in the #click set then use #click.first.id. If you want the smallest (min) id then use #click.map(&:id).min. Looking at your code i'm not sure what you are trying to achieve?
On a side note, i'd avoid calling Missing.select{|d| d.click < 10000} because that is doing a full table scan on your Missing model's table. As Ruby Racer says, better to query with active record.
#click is an array, not an association.
Why don't you use activerecord query to get your find?
find = Missing.where('click < 10000').minimum(:id)

Rails/Active Record- how to use 'take' if there is any matching record

I have a query that goes:
ModalType.where(id: modal['modal_id']).take.template
The problem is that i might run in cases where the query does not find any id: modal['modal_id'], then I get the error
undefined method `template' for nil:NilClass
How can I write something that would 'take' BUT only if where(id: modal_id) returns something (=is found) ?
With Ruby < 2.3:
object = ModalType.where(id: modal['modal_id']).take
if object
object.template
else
# I don't know. it's up to you
end
or
ModalType.where(id: modal['modal_id']).take.try(:template)
With Ruby >= 2.3 you can use safe navigation operator:
template = ModalType.where(id: modal['modal_id']).take&.template

Rails: Using loops inside a scope

I have this project I'm working on where I have a checklist. This checklist has an event_id, and then 2 fields for each task that much be done, one for the user_id that completed it, and one for the date. First what I do is grab all the checklists, which gives me an acitverecord::relation of all the checklists. Then I want to get only the completed checklists.
def index
#check_lists = CheckList.joins(:booking).joins(:contract)
#check_lists = #check_lists.complete if params[:status] == "complete"
#check_lists = #check_lists.incomplete if params[:status] == "incomplete"
end
My problem is once I call the #check_lists.complete, does it automatically give me the array in the scope in the model? Would I have to pass something in the method to get the array and run through it? Then once I have the activerecord::relation how do I run a loop through it?
scope :complete, { }
scope :complete, -> { where(complete: true) }
for example, would give you an ActiveRecord::Relation. This would be the same as CheckList.where(complete: true). You can convert this relation to an array of results using all.
CheckList.complete #=> ActiveRecord::Relation
CheckList.complete.all #=> Array
In Rails 4, all will return an ActiveRecord::Relation as well, so it will be necessary to use to_a to convert it to an array, but for now all will return an Array.

#user.visit_count => undefined method

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

Resources