Module: ActiveRecord::Scoping::Named::ClassMethods
- Defined in:
- lib/active_record/scoping/named.rb
Instance Method Summary collapse
-
#all ⇒ Object
Returns an
ActiveRecord::Relation
scope object. -
#scope(name, body, &block) ⇒ Object
Adds a class method for retrieving and querying objects.
-
#scope_attributes ⇒ Object
Collects attributes from scopes that should be applied when creating an AR instance for the particular class this is called on.
-
#scope_attributes? ⇒ Boolean
Are there default attributes associated with this scope?.
Instance Method Details
#all ⇒ Object
Returns an ActiveRecord::Relation
scope object.
posts = Post.all
posts.size # Fires "select count(*) from posts" and returns the count
posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
fruits = Fruit.all
fruits = fruits.where(color: 'red') if [:red_only]
fruits = fruits.limit(10) if limited?
You can define a scope that applies to all finders using ActiveRecord::Base.default_scope
.
24 25 26 27 28 29 30 31 32 |
# File 'lib/active_record/scoping/named.rb', line 24 def all if current_scope current_scope.clone else scope = relation scope.default_scoped = true scope end end |
#scope(name, body, &block) ⇒ Object
Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query, such as where(color: :red).select('shirts.*').includes(:washing_instructions)
.
class Shirt < ActiveRecord::Base
scope :red, -> { where(color: 'red') }
scope :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
end
The above calls to scope
define class methods Shirt.red
and Shirt.dry_clean_only
. Shirt.red
, in effect, represents the query Shirt.where(color: 'red')
.
You should always pass a callable object to the scopes defined with scope
. This ensures that the scope is re-evaluated each time it is called.
Note that this is simply ‘syntactic sugar’ for defining an actual class method:
class Shirt < ActiveRecord::Base
def self.red
where(color: 'red')
end
end
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.first
, Shirt.red.count
, Shirt.red.where(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 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 have extensions, just as with has_many
declarations:
class Shirt < ActiveRecord::Base
scope :red, -> { where(color: 'red') } do
def dom_id
'red_shirts'
end
end
end
Scopes can also be used while creating/building a record.
class Article < ActiveRecord::Base
scope :published, -> { where(published: true) }
end
Article.published.new.published # => true
Article.published.create.published # => true
Class methods on your model are automatically available on scopes. Assuming the following setup:
class Article < ActiveRecord::Base
scope :published, -> { where(published: true) }
scope :featured, -> { where(featured: true) }
def self.latest_article
order('published_at desc').first
end
def self.titles
pluck(:title)
end
end
We are able to call the methods like this:
Article.published.featured.latest_article
Article.featured.titles
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/active_record/scoping/named.rb', line 145 def scope(name, body, &block) extension = Module.new(&block) if block # Check body.is_a?(Relation) to prevent the relation actually being # loaded by respond_to? if body.is_a?(Relation) || !body.respond_to?(:call) ActiveSupport::Deprecation.warn( "Using #scope without passing a callable object is deprecated. For " \ "example `scope :red, where(color: 'red')` should be changed to " \ "`scope :red, -> { where(color: 'red') }`. There are numerous gotchas " \ "in the former usage and it makes the implementation more complicated " \ "and buggy. (If you prefer, you can just define a class method named " \ "`self.red`.)" ) end singleton_class.send(:define_method, name) do |*args| if body.respond_to?(:call) scope = all.scoping { body.call(*args) } scope = scope.extending(extension) if extension else scope = body end scope || all end end |
#scope_attributes ⇒ Object
Collects attributes from scopes that should be applied when creating an AR instance for the particular class this is called on.
36 37 38 39 40 41 42 43 44 |
# File 'lib/active_record/scoping/named.rb', line 36 def scope_attributes # :nodoc: if current_scope current_scope.scope_for_create else scope = relation scope.default_scoped = true scope.scope_for_create end end |
#scope_attributes? ⇒ Boolean
Are there default attributes associated with this scope?
47 48 49 |
# File 'lib/active_record/scoping/named.rb', line 47 def scope_attributes? # :nodoc: current_scope || default_scopes.any? end |