Read more

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

Henning Koch
February 02, 2011Software engineer at makandra GmbH

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
Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

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.

Posted by Henning Koch to makandra dev (2011-02-02 11:49)