diff options
Diffstat (limited to 'activerecord/lib/active_record/named_scope.rb')
-rw-r--r-- | activerecord/lib/active_record/named_scope.rb | 55 |
1 files changed, 31 insertions, 24 deletions
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 0e560418dc..d291632260 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -2,12 +2,18 @@ require 'active_support/core_ext/array' require 'active_support/core_ext/hash/except' require 'active_support/core_ext/kernel/singleton_class' require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/class/attribute' module ActiveRecord # = Active Record Named \Scopes module NamedScope extend ActiveSupport::Concern + included do + class_attribute :scopes + self.scopes = {} + end + module ClassMethods # Returns an anonymous \scope. # @@ -20,10 +26,10 @@ module ActiveRecord # fruits = fruits.limit(10) if limited? # # Anonymous \scopes tend to be useful when procedurally generating complex - # queries, where passing intermediate values (\scopes) around as first-class + # queries, where passing intermediate values (\scopes) around as first-class # objects is convenient. # - # You can define a \scope that applies to all finders using + # You can define a \scope that applies to all finders using # ActiveRecord::Base.default_scope. def scoped(options = nil) if options @@ -33,10 +39,6 @@ module ActiveRecord end end - def scopes - read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {}) - end - # Adds a class method for retrieving and querying objects. A \scope represents a narrowing of a database query, # such as <tt>where(:color => :red).select('shirts.*').includes(:washing_instructions)</tt>. # @@ -48,20 +50,20 @@ module ActiveRecord # The above calls to <tt>scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red, # in effect, represents the query <tt>Shirt.where(:color => 'red')</tt>. # - # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it - # resembles the association object constructed by a <tt>has_many</tt> declaration. For instance, - # you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>, <tt>Shirt.red.where(:size => 'small')</tt>. - # Also, just as with the association objects, named \scopes act like an Array, implementing Enumerable; + # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it + # resembles the association object constructed by a <tt>has_many</tt> declaration. For instance, + # you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>, <tt>Shirt.red.where(:size => 'small')</tt>. + # Also, just as with the association objects, named \scopes act like an Array, implementing Enumerable; # <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt> # all behave as if Shirt.red really was an Array. # - # These named \scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce + # These named \scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce # all shirts that are both red and dry clean only. - # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> - # returns the number of garments for which these criteria obtain. Similarly with + # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> + # returns the number of garments for which these criteria obtain. Similarly with # <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>. # - # All \scopes are available as class methods on the ActiveRecord::Base descendant upon which + # All \scopes are available as class methods on the ActiveRecord::Base descendant upon which # the \scopes were defined. But they are also available to <tt>has_many</tt> associations. If, # # class Person < ActiveRecord::Base @@ -88,14 +90,22 @@ module ActiveRecord # end # end # end - def scope(name, scope_options = {}, &block) + # + # 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 + def scope(name, scope_options = {}) name = name.to_sym valid_scope_name?(name) + extension = Module.new(&Proc.new) if block_given? - extension = Module.new(&block) if block_given? - - scopes[name] = lambda do |*args| - options = scope_options.is_a?(Proc) ? scope_options.call(*args) : scope_options + scope_proc = lambda do |*args| + options = scope_options.respond_to?(:call) ? scope_options.call(*args) : scope_options relation = if options.is_a?(Hash) scoped.apply_finder_options(options) @@ -108,12 +118,9 @@ module ActiveRecord extension ? relation.extending(extension) : relation end - singleton_class.send(:redefine_method, name, &scopes[name]) - end + self.scopes = self.scopes.merge name => scope_proc - def named_scope(*args, &block) - ActiveSupport::Deprecation.warn("Base.named_scope has been deprecated, please use Base.scope instead", caller) - scope(*args, &block) + singleton_class.send(:redefine_method, name, &scopes[name]) end protected |