From b77dd218ce845f01753d02fcbc2605c9a5ee93e1 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 2 Apr 2010 17:34:48 +0100 Subject: Add Relation extensions --- activerecord/lib/active_record/named_scope.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record/named_scope.rb') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 9abf979cd0..3456332227 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -133,19 +133,16 @@ module ActiveRecord delegate :scopes, :with_scope, :with_exclusive_scope, :scoped_methods, :scoped, :to => :klass def self.init(klass, options, &block) - relation = new(klass, klass.arel_table) + relation = new(klass, klass.arel_table, &block) scope = if options.is_a?(Hash) - klass.scoped.apply_finder_options(options.except(:extend)) + klass.scoped.apply_finder_options(options) else options ? klass.scoped.merge(options) : klass.scoped end relation = relation.merge(scope) - Array.wrap(options[:extend]).each {|extension| relation.send(:extend, extension) } if options.is_a?(Hash) - relation.send(:extend, Module.new(&block)) if block_given? - relation.current_scoped_methods_when_defined = klass.send(:current_scoped_methods) relation end -- cgit v1.2.3 From 0be31f85639cf2f536c558819ef3ee45ba7d83a3 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 2 Apr 2010 17:48:06 +0100 Subject: Scope#current_scoped_methods_when_defined is no longer needed --- activerecord/lib/active_record/named_scope.rb | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'activerecord/lib/active_record/named_scope.rb') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 3456332227..4203e36239 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -128,8 +128,6 @@ module ActiveRecord end class Scope < Relation - attr_accessor :current_scoped_methods_when_defined - delegate :scopes, :with_scope, :with_exclusive_scope, :scoped_methods, :scoped, :to => :klass def self.init(klass, options, &block) @@ -141,10 +139,7 @@ module ActiveRecord options ? klass.scoped.merge(options) : klass.scoped end - relation = relation.merge(scope) - - relation.current_scoped_methods_when_defined = klass.send(:current_scoped_methods) - relation + relation.merge(scope) end def first(*args) @@ -178,13 +173,7 @@ module ActiveRecord def method_missing(method, *args, &block) if klass.respond_to?(method) - with_scope(self) do - if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined) && !scopes.include?(method) - with_scope(current_scoped_methods_when_defined) { klass.send(method, *args, &block) } - else - klass.send(method, *args, &block) - end - end + with_scope(self) { klass.send(method, *args, &block) } else super end -- cgit v1.2.3 From ee07950c03bf8aab703191d73165eb206f9f55ef Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 2 Apr 2010 17:48:57 +0100 Subject: Scope#method_missing can safely rely on Relation#method_missing --- activerecord/lib/active_record/named_scope.rb | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'activerecord/lib/active_record/named_scope.rb') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 4203e36239..3efcff11ca 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -169,16 +169,6 @@ module ActiveRecord end end - private - - def method_missing(method, *args, &block) - if klass.respond_to?(method) - with_scope(self) { klass.send(method, *args, &block) } - else - super - end - end - end end -- cgit v1.2.3 From 62fe16932c9b7c3044017900114193e06814fd0c Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 2 Apr 2010 17:58:13 +0100 Subject: Make Relation#first and Relation#last behave like named scope's --- activerecord/lib/active_record/named_scope.rb | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'activerecord/lib/active_record/named_scope.rb') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 3efcff11ca..be26b1f47f 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -142,22 +142,6 @@ module ActiveRecord relation.merge(scope) end - def first(*args) - if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash)) - to_a.first(*args) - else - args.first.present? ? apply_finder_options(args.first).first : super - end - end - - def last(*args) - if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash)) - to_a.last(*args) - else - args.first.present? ? apply_finder_options(args.first).last : super - end - end - def ==(other) case other when Scope -- cgit v1.2.3 From cfa283201e079b4f700eb915490bcfa18451b11e Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 2 Apr 2010 18:57:01 +0100 Subject: Goodbye ActiveRecord::NamedScope::Scope --- activerecord/lib/active_record/named_scope.rb | 52 +++++++++------------------ 1 file changed, 17 insertions(+), 35 deletions(-) (limited to 'activerecord/lib/active_record/named_scope.rb') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index be26b1f47f..d56f7afb74 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -25,7 +25,8 @@ module ActiveRecord # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope. def scoped(options = {}, &block) if options.present? - Scope.init(self, options, &block) + relation = scoped.apply_finder_options(options) + block_given? ? relation.extending(Module.new(&block)) : relation else current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.clone end @@ -107,13 +108,22 @@ module ActiveRecord end scopes[name] = lambda do |parent_scope, *args| - Scope.init(parent_scope, case options - when Hash, Relation - options - when Proc - options.call(*args) - end, &block) + scope_options = case options + when Hash, Relation + options + when Proc + options.call(*args) + end + + relation = if scope_options.is_a?(Hash) + parent_scope.scoped.apply_finder_options(scope_options) + else + scope_options ? parent_scope.scoped.merge(scope_options) : parent_scope.scoped + end + + block_given? ? relation.extending(Module.new(&block)) : relation end + singleton_class.instance_eval do define_method name do |*args| scopes[name].call(self, *args) @@ -127,33 +137,5 @@ module ActiveRecord end end - class Scope < Relation - delegate :scopes, :with_scope, :with_exclusive_scope, :scoped_methods, :scoped, :to => :klass - - def self.init(klass, options, &block) - relation = new(klass, klass.arel_table, &block) - - scope = if options.is_a?(Hash) - klass.scoped.apply_finder_options(options) - else - options ? klass.scoped.merge(options) : klass.scoped - end - - relation.merge(scope) - end - - def ==(other) - case other - when Scope - to_sql == other.to_sql - when Relation - other == self - when Array - to_a == other.to_a - end - end - - end - end end -- cgit v1.2.3 From 684e4d39d60d87273ea74f9a076b3ea308fc2ffe Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 3 Apr 2010 01:22:42 +0100 Subject: Remove unnecessary argument for creating scopes --- activerecord/lib/active_record/named_scope.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record/named_scope.rb') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index d56f7afb74..86a18f8b6b 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -107,7 +107,7 @@ module ActiveRecord "Overwriting existing method #{self.name}.#{name}." end - scopes[name] = lambda do |parent_scope, *args| + scopes[name] = lambda do |*args| scope_options = case options when Hash, Relation options @@ -116,9 +116,9 @@ module ActiveRecord end relation = if scope_options.is_a?(Hash) - parent_scope.scoped.apply_finder_options(scope_options) + scoped.apply_finder_options(scope_options) else - scope_options ? parent_scope.scoped.merge(scope_options) : parent_scope.scoped + scope_options ? scoped.merge(scope_options) : scoped end block_given? ? relation.extending(Module.new(&block)) : relation @@ -126,7 +126,7 @@ module ActiveRecord singleton_class.instance_eval do define_method name do |*args| - scopes[name].call(self, *args) + scopes[name].call(*args) end end end -- cgit v1.2.3 From 41a2ba652a6b4b4eb3d12b9a2d55cb5b1eb5581a Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 3 Apr 2010 01:35:10 +0100 Subject: Improve named scope lambda --- activerecord/lib/active_record/named_scope.rb | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'activerecord/lib/active_record/named_scope.rb') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 86a18f8b6b..50c57783b2 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -99,7 +99,7 @@ module ActiveRecord # # expected_options = { :conditions => { :colored => 'red' } } # assert_equal expected_options, Shirt.colored('red').proxy_options - def scope(name, options = {}, &block) + def scope(name, scope_options = {}, &block) name = name.to_sym if !scopes[name] && respond_to?(name, true) @@ -108,19 +108,10 @@ module ActiveRecord end scopes[name] = lambda do |*args| - scope_options = case options - when Hash, Relation - options - when Proc - options.call(*args) - end - - relation = if scope_options.is_a?(Hash) - scoped.apply_finder_options(scope_options) - else - scope_options ? scoped.merge(scope_options) : scoped - end + options = scope_options.is_a?(Proc) ? scope_options.call(*args) : scope_options + relation = scoped + relation = options.is_a?(Hash) ? relation.apply_finder_options(options) : scoped.merge(options) if options block_given? ? relation.extending(Module.new(&block)) : relation end -- cgit v1.2.3 From c6372d604952a8eef16ce73a06814aa143b94779 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 3 Apr 2010 01:49:01 +0100 Subject: Improve scope docs --- activerecord/lib/active_record/named_scope.rb | 44 +++++++++------------------ 1 file changed, 14 insertions(+), 30 deletions(-) (limited to 'activerecord/lib/active_record/named_scope.rb') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 50c57783b2..632322b517 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -8,16 +8,15 @@ module ActiveRecord extend ActiveSupport::Concern module ClassMethods - # Returns a relation if invoked without any arguments. + # Returns an anonymous scope. # # posts = Post.scoped # 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 # - # Returns an anonymous named scope if any options are supplied. - # - # shirts = Shirt.scoped(:conditions => {:color => 'red'}) - # shirts = shirts.scoped(:include => :washing_instructions) + # fruits = Fruit.scoped + # fruits = fruits.where(:colour => 'red') if options[:red_only] + # 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 objects is convenient. @@ -37,21 +36,21 @@ module ActiveRecord end # 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. + # such as where(:color => :red).select('shirts.*').includes(:washing_instructions). # # class Shirt < ActiveRecord::Base - # scope :red, :conditions => {:color => 'red'} - # scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true] + # 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.find(:all, :conditions => {:color => 'red'}). + # in effect, represents the query Shirt.where(: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. + # 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 @@ -70,9 +69,7 @@ module ActiveRecord # Named \scopes can also be procedural: # # class Shirt < ActiveRecord::Base - # scope :colored, lambda { |color| - # { :conditions => { :color => color } } - # } + # scope :colored, lambda {|color| where(:color => color) } # end # # In this example, Shirt.colored('puce') finds all puce shirts. @@ -80,25 +77,12 @@ module ActiveRecord # Named \scopes can also have extensions, just as with has_many declarations: # # class Shirt < ActiveRecord::Base - # scope :red, :conditions => {:color => 'red'} do + # scope :red, where(: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 - # scope :colored, lambda { |color| - # { :conditions => { :color => color } } - # } - # end - # - # expected_options = { :conditions => { :colored => 'red' } } - # assert_equal expected_options, Shirt.colored('red').proxy_options def scope(name, scope_options = {}, &block) name = name.to_sym -- cgit v1.2.3