rails - find with condition in rails 4 - sql

I recently upgraded my rails to Rails 4.1.6.
This query used to work :
#user = User.find(:all, :conditions => { :name => 'batman' })
Now I get this error message:
Couldn't find all Users with 'id': (all, {:conditions=>{:name=>"batman"}}) (found 0 results, but was looking for 2)
When I check the logs I can see that rails is trying to do a completely different query :
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" IN ('all', '---
:conditions:
:name: batman
')
It looks like, it's trying to get all the users with the id "all" and "{:conditions=>{:name=>"batman"}}". Please help.
UPDATE:
My real question behind that was I want to get a specific user and add to it his cars, only the cars that are blue. For example this is my query, the user id is 20.
#user = User.joins(:cars).find(20, :cars => {:color => "blue"})
But I get this error:
Couldn't find all Users with 'id': (20, {:cars=>{:color=>"blue"}})
(found 41 results, but was looking for 2)

You should definitely read this ActiveRecord Query Interface quide
User.where(name: "batman")

Some others already pointed out: The query syntax changed. Try this:
#user = User.joins(:cars).where(:cars => { :color => "blue" }).find(20)
Note that this will raise an exception if that record is not found, to return an array empty instead call:
#user = User.joins(:cars).where(:id => 20, :cars => { :color => "blue" })
I suggest to read: http://guides.rubyonrails.org/active_record_querying.html
If you want to load the user even if he does not have any cars and than display only his blue cars, I would do it like this:
#user = User.find(20) # returns the user
#user.cars.where(:color => 'blue') # returns the user's blue cars (or an empty array)

The find method is deprecated in this version of Rails (see the reference).
Instead, you must use the where method.
In your case, you should write #user = User(:name => 'batman') or #user = User(name: 'batman')

Related

How to make tests with find and date conditions?

I'm trying to develop some tests for a method which is responsible for retrieve some users created after some date. I don't know how to mock tests for it. The method is the following:
def user_list
render :nothing => true, :status => 422 if params[:time_param].blank?
time = Time.parse(params[:time_param])
#users = User.find(:all, :select => 'id, login, email',
:conditions => ["created_at > ?", time])
render :json => { :users => #users }
end
end
This is my spec:
describe UsersController do
context "when receiving time parameter" do
before (:each) do
#time_param = "2013-01-25 00:01:00"
user1 = mock_model(User, :created_at => Time.parse('2013-01-25 00:00:00'))
user2 = mock_model(User, :created_at => Time.parse('2013-01-25 00:01:00'))
user3 = mock_model(User, :created_at => Time.parse('2013-01-25 00:02:00'))
#users = []
#users << user1 << user2 << user3
end
it "should retrieve crimes after 00:01:00 time" do
User.stub(:find).with(:all, :select => 'id, login, email').and_return(#users)
get :user_list, { :time_param => #time_param }
JSON.parse(response.body)["users"].size.should eq 1
end
end
end
The problem is that it always returns all users despite of returning just one. (the last one). Where am I mistaking?
Help me =)
You are not testing what you have to test there, on a controller spec you only need to test that the method that you want is called with the parameters that you want, in your case, you have to test that the User model receives :find with parameters :all, :select => 'id, login, email', :conditions => ["created_at > ?", time] (with time the value that should be there.
Also, that logic does not belong to the controller, you should have a class method on User, something like select_for_json(date) to wrap around that find method (you can find a better name for it)
Then your controller becomes:
def user_list
render :nothing => true, :status => 422 if params[:time_param].blank?
time = Time.parse(params[:time_param])
#users = User.select_for_json(time)
render :json => { :users => #users }
end
your spec would be
before(:each) do
#users = mock(:users)
#time_param = "2013-01-25 00:01:00"
end
it "retrieve users for json" do
User.should_receive(:select_for_json).once.with(#time).and_return(#users)
get :user_list, { :time_param => #time }
assigns(:users).should == #users
end
that way you are sure that your action does what it does and the spec is A LOT faster since you are not creating users
then you can test that method on the model specs, there you have to create some users, invoke that method and check the users returned (don't stub/mock anything on your model spec)
Your stub call is telling find to ignore what it thought it was supposed to do and return #users instead. It will not attempt to match the conditions.
Unfortunately, to do your test I think you're going to have to allow the find to execute through your database which means you can't use mock_models. You probably will want to do either User.create(...) or FactoryGirl.create(:user) (or some other factory / fixture).
Of course doing it this way, you may hit MassAssignment issues if you use attr_accessible or attr_protected, but those are easy enough to stub out.
I hope that helps.

Rails 3 ActiveRecord Query questions

I've implemented "following" function. Showing "people user A is following" was simple, but showing "people who are following user A" is giving me troubles.
I have follow_links, which have id, user_id, and follow_you_id column. When user A begins following user B, the columns will be like (user_id = A, follow_you_id = B).
To show users that A(#user) is following, I can simply do
#follow_yous = #user.follow_yous
But I'm not sure how to show users who are following A(#user)
To do this, I first found all the related links.
#follow_links = FollowLink.where(:follow_you_id => #user.id)
Now I thought I could just do #follow_mes = #follow_links.users, but it says user is an undefined method. So I guess I can either call user.follow_yous or follow_you.users.
My next approach was
#follow_links = FollowLink.where(:follow_you_id => #user.id)
#follow_mes = User.where(:id => #user.id, :include => #follow_links)
I intended to find all the User objects that had the provided #follow_links objects, but I think the syntax was wrong. I couldn't find a decent solution after a bit of research. I'd appreciate any help.
Update:
FollowLink model
belongs_to :user
belongs_to :follow_you, :class_name => "User"
You can use joins like this:
#users = User.joins(:follow_links).where(:follow_links => { :follow_you_id => #user.id })
you can use following:
#follow_links = FollowLink.where(:follow_you_id => #user.id)
#follow_links.collect(&:user) # :user should be the name of your relation to user in your followlink model
=> [User1, User2,...]

Devise Gem and the Geocoder Gem

I am using geocoder and the devise gem. And i am trying to get coupons near user's location
Coupon Model
def index
if params[:search].present?
#coupons = Coupon.near([user_long, user_lat], 50, :order => :distance)
else
#coupons = Coupon.all
end
end
Application Helper
I have defined the user_long and user_lat
def user_long
current_user.longitude
end
def user_lat
current_user.latitude
end
Devise Gem
I have tried to use the devise gem helper to get the values like so
Coupon Model
def index
if params[:search].present?
#coupons = Coupon.near([current_user.longitude, current_user.latitude], 50, :order => :distance)
else
#coupons = Coupon.all
end
end
I am hitting the walls and celling with this. Can someone help out, i know this is newbie question for but i can't solve it so save my life?
You are not seeing anything on the index page because your #coupons array is empty:
#coupons = Coupon.near([current_user.longitude, current_user.latitude], 50, :order => :distance)
In development log (in the same window where rails server is running, if you are running the rails server from console), you should check out the SQL query generated for CouponsController#index action.
Assuming you defined your 'near' query like this:
class Coupon
scope :near, lambda { |longitude, latitude| some logic...
end
You can debug this 'near' method using "rails console" like this:
rails console
> Coupon.near(10, 20)
etc..
My mistake was that i that i had the longitude before that latitude, It was taking away the logic
This works.
def index
#coupons = Coupon.near([current_user.latitude, current_user.longitude], 50, :order => :distance)
end

Configuring rails database query so that blank string parameters are ignored

I'm making a rails application so that users can search a database of midi records and find midi files that correspond to the attributes that I've given them.
For example, a user might enter data into an html form for a midi file with name = "blah" composer= "buh" and difficulty = "insane".
This is all fine and well, except that I would like when the user enters no data for a field, that field is ignored when doing the select statement on the database.
Right now this is what my select statement looks like:
#midis=Midi.where(:name => params[:midi][:name],
:style => params[:midi][:style],
:numparts => params[:midi][:numparts],
:composer=> params[:midi][:composer],
:difficulty => params[:midi[:difficulty])
This works as expected, but if for example he/she leaves :composer blank, the composer field should not considered at all. This is probably a simple syntax thing but i wasn't able to find any pages on it.
Thanks very much!
Not sure if Arel supports that directly, but you could always do something like:
conditions = {
:name => params[:midi][:name],
:style => params[:midi][:style],
:numparts => params[:midi][:numparts],
:composer=> params[:midi][:composer],
:difficulty => params[:midi[:difficulty]
}
#midis=Midi.where(conditions.select{|k,v| v.present?})
Try this:
# Select the key/value pairs which are actually set and then convert the array back to Hash
c = Hash[{
:name => params[:midi][:name],
:style => params[:midi][:style],
:numparts => params[:midi][:numparts],
:composer => params[:midi][:composer],
:difficulty => params[:midi][:difficulty]
}.select{|k, v| v.present?}]
Midi.where(c)

Will_Paginate and order clause not working

I'm calling a pretty simple function, and can't seem to figure out whats going on. (I'm using rails 3.0.3 and the master branch of 'will_paginate' gem). I have the following code:
results = Article.search(params) # returns an array of articles
#search_results = results.paginate :page => params[:page], :per_page=>8, :order => order_clause
No matter what I make the order_clause (for example 'article_title desc' and 'article_title asc'), the results are always the same in the same order. So when I check using something like #search_results[0], the element is always the same. In my view, they are obviously always the same as well. Am I totally missing something?
I'm sure its something silly, but I've been banging my head against the wall all night. Any help would be much appreciated!
Edited to Add: The search clause does the following:
def self.search(params)
full_text_search(params[:query].to_s).
category_search(params[:article_category].blank? ? '' : params[:article_category][:name]).
payout_search(params[:payout_direction], params[:payout_value]).
length_search(params[:length_direction], params[:length_value]).
pending.
distinct.
all
end
where each of these guys is a searchlogic based function like this:
#scopes
scope :text_search, lambda {|query|
{
:joins => "INNER JOIN users ON users.id IN (articles.writer_id, articles.buyer_id)",
:conditions => ["(articles.article_title LIKE :query) OR
(articles.description LIKE :query) OR
(users.first_name LIKE :query) OR
(users.last_name LIKE :query)", { :query => "%#{query}%" }]
}
}
scope :distinct, :select => "distinct articles.*"
#methods
def self.payout_search(dir, val)
return no_op if val.blank?
send("payment_amount_#{dir.gsub(/\s+/,'').underscore}", val)
end
def self.length_search(dir, val)
return no_op if val.blank?
send("min_words_#{dir.gsub(/\s+/,'').underscore}", val)
end
Thanks.
If you look at the example from the will_paginate github page you can spot one important difference between their use of the :order clause and yours:
#posts = Post.paginate :page => params[:page], :order => 'created_at DESC'
This calls paginate on the Post object (with no objects being selected yet - no SQL has been executed before paginate comes along). This is different in your example: as you state in the first line of code "returns an array of articles". The simplest I can come up with showing the problem is
results = Model.limit(5).all
#results = results.paginate :order => :doesnt_matter_anymore
won't sort, but this will:
results = Model.limit(5)
#results = results.paginate :order => :matters
It should suffice to take the all out of the search method. It makes ActiveRecord actually perform the SQL query when calling this method. Will_paginate will do that for you when you call paginate (if you let it...). Check out the section on Lazy Loading in this post about Active Record Query Interface 3.0