I have a method which is meant to return a number of items from the database based on a set of criteria:
scope :expired_not_marked, lambda { |client|
items = where('items.status > 0 AND items.expires_at < ? AND items.expired_at IS NULL AND (winning_bid_id IS NULL OR winner_id IS NULL)', Time.now)
unless client.nil?
items.where('items.client_id = ?', client.id)
end
}
It’s being called as Item.expired_not_marked nil. When I run this from the IRB I get a lot of results but it shows the SQL query being executed as:
SELECT `items`.* FROM `items`
It’s pretty obviously that was not the original author’s intent. As a result the same items are being processed over and over.
Why is this broken, and how do I fix it. The where clause seems correct. The above method is within the item.rb model.
Your problem is that your
lambdasometimes returnsniland a scope that returnsnilwon’t do anything useful.The
lambdawill return the value of its last expression. In your case, that expression will be theunless. So ifclientis notnil, it will return this:and all will be good. But if
client.nil?is true, theunlesswill evaluate toniland your scope will returnnil. I think you’d be better off with something like this:That way you always have a clear, explicit, and well defined return value.
The ActiveRecord Query Interface Guide recommends that you use class methods for scopes that take arguments:
so you could also do this if the
lambdaapproach is too noisy:You don’t have to use a class method of course. And you don’t have to break the query into a bunch of little
wherecalls for each component but it might be easier to read this way.