Conditional where in rails
T.L.D.R
You can pass blank
object to where
clause and it will return current relation as it is.
Long version
Sometimes, we need to apply where
clause conditionally. For eg. apply timeframe condition if timeframe is given in params. We can achieve this using simple if/unless.
def most_recent(from = nil, to = Date.today)
condition = Activity.where("created_at < ?", to)
condition.where("created_at > ?", from) unless from.blank?
end
This works but doesn’t look good. If we can chain this second conditional scope, it would be much better. Enter magic of Rails. If we read documentation of where
clause,
# === blank condition
#
# If the condition is any blank-ish object, then #where is a no-op and returns
# the current relation.
This is interesting. This means if we pass any blank
value to where clause, it returns the current relation
as it is.
Now we have to just write a method which will generate the query if condition is satisfied. It will return nil
otherwise resulting in a no-op
.
def most_recent(from = nil, to = Date.today)
Activity.where("created_at < ?", to).where(from_condition(from))
end
def from_condition(from)
"created_at > #{from}" unless from.blank?
end
This looks more cleaner than earlier solution. It will work the same way if we pass ''
, ""
, {}
, []
, false
or any other object that respond to blank as true instead of nil
.
Happy Hacking!