diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 6e77493de9..db754a02e6 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -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. diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb index 3e43591672..fca4f1c9d3 100644 --- a/activerecord/lib/active_record/scoping.rb +++ b/activerecord/lib/active_record/scoping.rb @@ -11,11 +11,11 @@ module ActiveRecord 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 diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index 49cadb66d0..826b710e92 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -148,9 +148,13 @@ module ActiveRecord 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 diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb index f0ad9ebb8a..f66b76a7ad 100644 --- a/activerecord/test/cases/scoping/named_scoping_test.rb +++ b/activerecord/test/cases/scoping/named_scoping_test.rb @@ -2,12 +2,13 @@ require "cases/helper" 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 @@ class NamedScopingTest < ActiveRecord::TestCase 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? diff --git a/activerecord/test/fixtures/ratings.yml b/activerecord/test/fixtures/ratings.yml index 34e208efa3..2b45c5080e 100644 --- a/activerecord/test/fixtures/ratings.yml +++ b/activerecord/test/fixtures/ratings.yml @@ -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 diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb index ede5fbd0c6..6a9dfacf56 100644 --- a/activerecord/test/models/comment.rb +++ b/activerecord/test/models/comment.rb @@ -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 diff --git a/activerecord/test/models/rating.rb b/activerecord/test/models/rating.rb index 25a52c4ad7..5409230c2e 100644 --- a/activerecord/test/models/rating.rb +++ b/activerecord/test/models/rating.rb @@ -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 diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index b44e72a67c..cbcdf93673 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -579,7 +579,9 @@ ActiveRecord::Schema.define do 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|