named_scope
- v1.0.0
- v1.1.0
- v1.1.1
- v1.1.2
- v1.1.3
- v1.1.4
- v1.1.5
- v1.1.6
- v1.2.0
- v1.2.1
- v1.2.2
- v1.2.3
- v1.2.4
- v1.2.5
- v1.2.6
- v2.0.0
- v2.0.1
- v2.0.2
- v2.0.3
- 2.1.0 (0)
- 2.2.1 (-38)
- 2.3.2 (0)
- 2.3.4 (0)
- What's this?
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 => {:color => '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 => {:size => 'small'}). Also, just as with the association objects, named \scopes act like an Array, implementing Enumerable; Shirt.red.each(&block), Shirt.red.first, and Shirt.red.inject(memo, &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 descendent 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
automatically generate scopes for model states
or better known as "throw on some more tasty meta-programming" :). Given an example of a model which has a state (String) which must from a set of defined values, e.g. pending, approved, denied.
class User < ActiveRecord::Base STATES = [ 'pending', 'approved', 'denied' ] validates_inclusion_of :state, :in => STATES # Define a named scope for each state in STATES STATES.each { |s| named_scope s, :conditions => { :state => s } } end
This automatically defines a named_scope for each of the model states without having to define a named_scope manually for each state (nice and DRY).
Passing find() arguments
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
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
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
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)
Generating empty conditions
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.
Anyone know the order the scopes assemble conditions?
It seems like last scope = first condition in sql. Can anyone confirm?
Remember, named_scope returns an array
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.)
Extract the aggregated scoping options
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' } }
AASM named scopes
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

