Rails: When defining scopes with class methods, don't use `self`

Updated . Posted . Visible to the public.

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.

Henning Koch
Last edit
Michael Leimstädtner
Keywords
relation, activerecord
License
Source code in this card is licensed under the MIT License.
Posted by Henning Koch to makandra dev (2011-02-02 10:49)