Module: ActiveRecord::Scoping::Named::ClassMethods
- Defined in:
- lib/active_record/scoping/named.rb
Instance Method Summary collapse
-
#all ⇒ Object
Returns an
ActiveRecord::Relationscope 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 |