From ec1949e79280873e38f14f2d2e0916344a64e6ef Mon Sep 17 00:00:00 2001 From: Joseph Palermo Date: Thu, 30 Jul 2009 00:03:02 -0700 Subject: [PATCH] jp - :with_chained_scopes option added. Tests for both new and old functionality. --- activerecord/lib/active_record/named_scope.rb | 15 +++++++++++++-- activerecord/test/cases/named_scope_test.rb | 8 ++++++++ activerecord/test/models/topic.rb | 7 +++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index bbe2d1f..b086019 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -53,7 +53,7 @@ module ActiveRecord # then elton.shirts.red.dry_clean_only will return all of Elton's red, dry clean # only shirts. # - # Named \scopes can also be procedural: + # Named \scopes can also be procedural, a Proc can be passed instead of a hash, or it can be passed in a :proc option: # # class Shirt < ActiveRecord::Base # named_scope :colored, lambda { |color| @@ -63,6 +63,9 @@ module ActiveRecord # # In this example, Shirt.colored('puce') finds all puce shirts. # + # When chaining named scopes, if you would like the Proc from one scope to be executed using the finder scopes higher up the chain you can use the + # :with_chained_scopes option. In this case you must pass the Proc using the :proc option. + # # Named \scopes can also have extensions, just as with has_many declarations: # # class Shirt < ActiveRecord::Base @@ -90,7 +93,15 @@ module ActiveRecord scopes[name] = lambda do |parent_scope, *args| Scope.new(parent_scope, case options when Hash - options + if options.key?(:proc) + if options[:with_chained_scopes] && Scope === parent_scope + with_scope(:find => parent_scope.proxy_options) { options[:proc].call(*args) } + else + options[:proc].call(*args) + end + else + options + end when Proc options.call(*args) end, &block) diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index f4fdc9a..42aa7b5 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -354,6 +354,14 @@ class NamedScopeTest < ActiveRecord::TestCase assert_equal [posts(:sti_comments)], Post.with_special_comments.with_post(4).all.uniq end + def test_finds_inside_lambdas_should_not_use_chained_scopes + assert Topic.rejected.with_replies_using_find.include?(topics(:first)) + end + + def test_methods_invoked_within_scopes_should_respect_scope + assert_equal [], Topic.approved.by_rejected_ids.proxy_options[:conditions][:id] + end + def test_named_scopes_batch_finders assert_equal 3, Topic.approved.count diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index 9594dc3..98e5a2e 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -36,6 +36,13 @@ class Topic < ActiveRecord::Base named_scope :named_extension, :extend => NamedExtension named_scope :multiple_extensions, :extend => [MultipleExtensionTwo, MultipleExtensionOne] + named_scope :with_replies_using_find, lambda { + ids = Topic.find(:all, :conditions => ['parent_id IS NOT NULL']).collect{|t| t.parent_id}.uniq + { :conditions => { :id => ids } } + } + + named_scope :by_rejected_ids, :with_chained_scopes => true, :proc => lambda {{ :conditions => { :id => all(:conditions => {:approved => false}).map(&:id) } }} + has_many :replies, :dependent => :destroy, :foreign_key => "parent_id" has_many :replies_with_primary_key, :class_name => "Reply", :dependent => :destroy, :primary_key => "title", :foreign_key => "parent_title" serialize :content -- 1.6.0.4