Sometimes it is useful to define a named scope by implementing a static method with the scope's name on the scoped class. For instance, when a method should decide which existing scope should be the next link in the scope chain. Take this class for example:
class Meal < ActiveRecord::Base
named_scope :for_date, lambda { |date| :conditions => { :date => date }}
named_scope :with_meat, :conditions => { :meat => true }
named_scope :without_meat, :conditions => { :meat => false }
def self.suitable_for(user)
if user.vegetarian?
without_meat
else
with_meat
end
end
end
You can now say:
Meal.for_date(Date.today).suitable_for(current_user)
The suitable_for
method will now pick the correct meals depending on whether the user is a vegetarian or a carnivore.
Don't use self when defining scopes as class methods
In Rails 2 and 5 (not 3, not sure about 4) there is one caveat you should know about: Don't ever use self
in a class method that is supposed to be used as a scope. Because of the way scopes work, this will discard any previous scopings in the chain link.
Say we want to change the suitable_for
method so carnivores get meals both with and without meat. This would be wrong:
def self.suitable_for(user)
if user.vegetarian?
without_meat
else
self
end
end
With this, the scope chain Meal.for_date(Date.today).suitable_for(current_user)
would return all meals, not only those of today. Do this instead:
def self.suitable_for(user)
if user.vegetarian?
without_meat
else
all # for Rails 2 use `scoped({})` instead of `all`
end
end
Note how we're returning #all
instead of self
. This ensures that we're preserving the upstream scope chain.