Permalink
Browse files

Fixes Issue #13466.

Changed the call to a scope block to be evaluated with instance_eval.
The result is that ScopeRegistry can use the actual class instead of base_class when
caching scopes so queries made by classes with a common ancestor won't leak scopes.
  • Loading branch information...
1 parent 9ed0cf5 commit 9c3afdc327132c7f1f4d05eebc0c05b715442e7d @jefflai2 jefflai2 committed Feb 10, 2014
View
@@ -1,3 +1,14 @@
+* Changed scoped blocks to be executed with `instance_eval`
+
+ Named scopes (i.e. using STI) were previously cached according to
+ base class so scoped queries made by classes with a common ancestor would
+ leak. Changed the way scope blocks were called so inheritance rules are
+ followed during the call and scopes are cached correctly.
+
+ Fixes #13466.
+
+ *Jefferson Lai*
+
* Save `has_one` association even if the record doesn't changed.
Fixes #14407.
@@ -11,11 +11,11 @@ module Scoping
module ClassMethods
def current_scope #:nodoc:
- ScopeRegistry.value_for(:current_scope, base_class.to_s)
+ ScopeRegistry.value_for(:current_scope, self.to_s)
end
def current_scope=(scope) #:nodoc:
- ScopeRegistry.set_value_for(:current_scope, base_class.to_s, scope)
+ ScopeRegistry.set_value_for(:current_scope, self.to_s, scope)
end
end
@@ -148,9 +148,13 @@ def scope(name, body, &block)
extension = Module.new(&block) if block
singleton_class.send(:define_method, name) do |*args|
- scope = all.scoping { body.call(*args) }
+ if body.respond_to?(:to_proc)
+ scope = all.scoping { instance_exec(*args, &body) }
+ else
+ # Body is given as an object instead of a block, so invoke call()
+ scope = all.scoping { body.call(*args) }
+ end
scope = scope.extending(extension) if extension
-
scope || all
end
end
@@ -2,12 +2,13 @@
require 'models/post'
require 'models/topic'
require 'models/comment'
+require 'models/rating'
require 'models/reply'
require 'models/author'
require 'models/developer'
class NamedScopingTest < ActiveRecord::TestCase
- fixtures :posts, :authors, :topics, :comments, :author_addresses
+ fixtures :posts, :authors, :topics, :comments, :author_addresses, :ratings
def test_implements_enumerable
assert !Topic.all.empty?
@@ -115,6 +116,10 @@ def test_scope_with_STI
assert_equal 1,SpecialPost.containing_the_letter_a.count
end
+ def test_scope_subquery_with_STI
+ assert_nothing_raised { VerySpecialComment.special_parent(SpecialRating.first).count }
+ end
+
def test_has_many_through_associations_have_access_to_scopes
assert_not_equal Comment.containing_the_letter_e, authors(:david).comments
assert !Comment.containing_the_letter_e.empty?
@@ -2,13 +2,23 @@ normal_comment_rating:
id: 1
comment_id: 8
value: 1
+ type: Rating
special_comment_rating:
id: 2
comment_id: 6
value: 1
+ type: Rating
sub_special_comment_rating:
id: 3
comment_id: 12
value: 1
+ type: Rating
+
+special_rating:
+ id: 4
+ comment_id: 10
+ value: 1
+ type: SpecialRating
+ special_comment_id: 3
@@ -35,4 +35,5 @@ class SubSpecialComment < SpecialComment
end
class VerySpecialComment < Comment
+ scope :special_parent, -> (special_rating) { where parent_id: special_rating.special_comment.id }
end
@@ -2,3 +2,7 @@ class Rating < ActiveRecord::Base
belongs_to :comment
has_many :taggings, :as => :taggable
end
+
+class SpecialRating < Rating
+ belongs_to :special_comment
+end
@@ -579,7 +579,9 @@ def create_table(*args, &block)
create_table :ratings, force: true do |t|
t.integer :comment_id
+ t.integer :special_comment_id
t.integer :value
+ t.string :type
end
create_table :readers, force: true do |t|

0 comments on commit 9c3afdc

Please sign in to comment.