method

named_scope

named_scope(name, options = {}, &block)
public

Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query, such as :conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions.

  class Shirt < ActiveRecord::Base
    named_scope :red, :conditions => {:color => 'red'}
    named_scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true]
  end

The above calls to named_scope define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red, in effect, represents the query Shirt.find(:all, :conditions =&gt; {:color =&gt; 'red'}).

Unlike Shirt.find(...), however, the object returned by Shirt.red is not an Array; it resembles the association object constructed by a has_many declaration. For instance, you can invoke Shirt.red.find(:first), Shirt.red.count, Shirt.red.find(:all, :conditions =&gt; {:size =&gt; 'small'}). Also, just as with the association objects, named \scopes act like an Array, implementing Enumerable; Shirt.red.each(&amp;block), Shirt.red.first, and Shirt.red.inject(memo, &amp;block) all behave as if Shirt.red really was an Array.

These named \scopes are composable. For instance, Shirt.red.dry_clean_only will produce all shirts that are both red and dry clean only. Nested finds and calculations also work with these compositions: Shirt.red.dry_clean_only.count returns the number of garments for which these criteria obtain. Similarly with Shirt.red.dry_clean_only.average(:thread_count).

All \scopes are available as class methods on the ActiveRecord::Base descendant upon which the \scopes were defined. But they are also available to has_many associations. If,

  class Person < ActiveRecord::Base
    has_many :shirts
  end

then elton.shirts.red.dry_clean_only will return all of Elton’s red, dry clean only shirts.

Named \scopes can also be procedural:

  class Shirt < ActiveRecord::Base
    named_scope :colored, lambda { |color|
      { :conditions => { :color => color } }
    }
  end

In this example, Shirt.colored('puce') finds all puce shirts.

Named \scopes can also have extensions, just as with has_many declarations:

  class Shirt < ActiveRecord::Base
    named_scope :red, :conditions => {:color => 'red'} do
      def dom_id
        'red_shirts'
      end
    end
  end

For testing complex named \scopes, you can examine the scoping options using the proxy_options method on the proxy itself.

  class Shirt < ActiveRecord::Base
    named_scope :colored, lambda { |color|
      { :conditions => { :color => color } }
    }
  end

  expected_options = { :conditions => { :colored => 'red' } }
  assert_equal expected_options, Shirt.colored('red').proxy_options

15Notes

Passing find() arguments

cyu · Jun 27, 200815 thanks

I you need to pass additional arguments to a scope (e.g. limit), do this:

Shirt.colored('red').all(:limit => 10)

Use lambda to avoid caching of generated query

DavidBackeus · Mar 10, 20098 thanks

If you're using a named_scope that includes a changing variable you need to wrap it in a lambda to avoid the query being cached and thus becoming unaffected by future changes to the variable, example:

named_scope :translated, :conditions => { :locale => I18n.locale }

Will always return the same locale after the first hit even though I18n.locale might change. So do this instead:

named_scope :translated, lambda { { :conditions => { :locale => I18n.locale } } }

Ugly, but at least it's working as we expect it...

acts_as_state_machine named scopes

dmantilla · Dec 12, 20087 thanks

If you are using the acts_as_state_machine plugin, this will generate all named scopes for your various states.

Place it after the acts_as_state_machine and state declarations.

class Task < ActiveRecord::Base
acts_as_state_machine :initial => :waiting
state :waiting
state :running
state :finished    

states.each { |s| named_scope s, :conditions => { :state => s.to_s } }
end

Then doing a Task.waiting will return the corresponding tasks.

Passing optional arguments with defaults to a named_scope

insane-dreamer · Mar 21, 20094 thanks

An easy way to do this. (This also shows how you can use joins in a named_scope as well.)

Class User << ActiveRecord::Base
belongs_to :semester 

named_scope :year, lambda { |*year|
if year.empty? || year.first.nil?
  { :joins => :semester, :conditions => ["year = #{CURRENT_SEMESTER}"]}
else
  { :joins => :semester, :conditions => ["year = #{year}"]}
end
}

end

You can then call:

User.year     # defaults to CURRENT_SEMESTER constant
User.year()  # same as above
User.year(nil)  # same as above; useful if passing a param value that may or may not exist, ie, param[:year]
User.year(2010)

Extract the aggregated scoping options

mikael · Feb 16, 20103 thanks

If you want to get the aggregated scoping options of a chain of named scopes use ActiveRecord::Base.current_scoped_methods

It works in the fashion of:

Shirt.red.medium.alphabetical.current_scoped_methods
# ==>
{
:create => {}, 
:find => {
  :conditions => {:color => 'red', :size => 'medium'}, 
  :order => 'shirts.name ASC'
} 
}

How using Array methods

sebthemonster · May 18, 20102 thanks

It's not possible to use Array methods with a scope because it's not an Array but an ActiveRecord::NamedScope::Scope :

this Article.promotion.sum(&:price) doesn't run.

But you can use the to_a method to transform the ActiveRecord::NamedScope::Scope to an Array :

Article.promotion.to_a.sum(&:price)

Generating empty conditions

insane-dreamer · Mar 21, 20091 thank

In some cases, you might find it useful for your lamba to generate empty conditions based on the passed parameter.

Class Article << ActiveRecord::Base

named_scope :category, lambda { |cat|
if cat == :all
  { :conditions => {} }
else
  { :conditions => { :category_id => cat } }
end  
}

end

Allows you to call something like this:

categories = user_is_admin ? :all : @current_category
Article.category(categories)

Mostly useful when chaining named_scopes together. Avoids more complicated if statements.

AASM named scopes

ishka · Mar 4, 20101 thank

If you are using the aasm plugin/gem, this will generate all named scopes for your various states.

==== Code example

Class Article <  ActiveRecord::Base

include AASM

aasm_initial_state :created

aasm_state :published
aasm_state :unpublished
aasm_state :deleted
aasm_state :created


aasm_event :publish do
  transitions :to => :published, :from => [:created]
end

aasm_event :unpublish do
  transitions :to => :unpublished, :from => [:created, :published]
end

aasm_event :delete do
  transitions :to => :deleted, :from => [:published, :unpublished]
end

aasm_states.each { |s| named_scope s, :conditions => { :state => s.to_s } }  

end

named_scopes and Acts as State Machine

inkdeep · Mar 22, 20101 thank

As of AASM 2.1 named_scope(s) are defined by default for each state of a model.

Testing Named Scopes

railsway-worker · Aug 4, 20101 thank

Thanks for the example of testing named_scopes. Being new to Rails, I struggled to find examples that I could understand. Here is another "simple" test for a named_scope

Mine differs slightly from the one above in that I had to remove a set of {} in the :conditions in my test to avoid an "odd number list for Hash" error. I also replace the param-binding "?" with the number I expect to send in as an argument. My test would did know what args[0] was. I got an "undefined local variable" error.

The named scope in my model:

named_scope :up_to_and_including_year, lambda{ |*args| {
      :conditions => ["to_char(grad_dt1,'YYYY') <= ?", args[0]]
}}

The test:

test "named_scope :up_to_and_including_year" do expected_options = { :conditions => ["to_char(grad_dt1,'YYYY') <= ?", '2010'] } assert_equal expected_options, Sso::CourseTaken.up_to_and_including_year('2010').proxy_options end

using joins, group, having

tom2cjp · Sep 21, 20101 thank

==== Code example

named_scope :red, :joins =>   [:color_lis => :color], :group => "color.id", :having => ["color.xx IS NULL"]

A named_scope also responds to model class methods

lazylester · Oct 31, 20101 thank

for instance

class Student < ActiveRecord::Base
   named_scope :sophomore, :conditions => 'year=2'

   def self.eligible_to_vote
     select{|s| s.age >= 18}
   end

end

ss = Student.sophomore.eligible_to_vote

Anyone know the order the scopes assemble conditions?

actsasflinn · Oct 30, 2008

It seems like last scope = first condition in sql. Can anyone confirm?

Remember, named_scope returns an array

insane-dreamer · Mar 21, 2009

named_scope always returns a named_scope object, that acts like an array, even if you're using it to only find one record. So if you're trying to perform an association on the results of a named_scope, use the first method to return the model object and not the named_scope object.

Ie:

user = User.my_name_scope
user.articles   # assuming User has_many Articles

will return an error. use this instead:

user = User.my_named_scope.first
user.articles

(Of course this is a poor example because what you should be doing is performing the named_scope on Article with user as the condition, instead of on User. But if you do need to use the results of a named_scope to perform an association call, you have to do it this way to avoid an error.)

some gotchas

wiseleyb · Feb 9, 2011

Works

named_scope :public, :conditions => "public = true"

Works

PUBLIC_CONDITIONS = "public = true"
named_scope :public, :conditions => SomeModel::PUBLIC_CONDITIONS

Works

named_scope :public, lamba { {:conditions => SomeModel.public_conditions} }
def self.public_conditions
"public = true"
end

Doesn't work

named_scope :public, :conditions => SomeModel.public_conditions
def self.public_conditions
"public = true"
end