aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb28
-rw-r--r--activerecord/lib/active_record/scoping/named.rb8
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb22
-rw-r--r--activerecord/test/cases/scoping/relation_scoping_test.rb14
-rw-r--r--activerecord/test/models/comment.rb4
5 files changed, 70 insertions, 6 deletions
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index fa32cc5553..b16fca7dc9 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -13,22 +13,38 @@ module ActiveRecord
end
def associated_records_by_owner(preloader)
+ already_loaded = owners.first.association(through_reflection.name).loaded?
through_scope = through_scope()
- preloader.preload(owners,
- through_reflection.name,
- through_scope)
+ unless already_loaded
+ preloader.preload(owners, through_reflection.name, through_scope)
+ end
through_records = owners.map do |owner|
center = owner.association(through_reflection.name).target
[owner, Array(center)]
end
- reset_association(owners, through_reflection.name, through_scope)
+ if already_loaded
+ if source_type = reflection.options[:source_type]
+ through_records.map! do |owner, center|
+ center = center.select do |record|
+ record[reflection.foreign_type] == source_type
+ end
+ [owner, center]
+ end
+ end
+ else
+ reset_association(owners, through_reflection.name, through_scope)
+ end
middle_records = through_records.flat_map(&:last)
- reflection_scope = reflection_scope() if reflection.scope
+ if preload_scope
+ reflection_scope = reflection_scope().merge(preload_scope)
+ elsif reflection.scope
+ reflection_scope = reflection_scope()
+ end
preloaders = preloader.preload(middle_records,
source_reflection.name,
@@ -58,6 +74,8 @@ module ActiveRecord
rhs_records
end
end
+ end.tap do
+ reset_association(middle_records, source_reflection.name, preload_scope)
end
end
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index 6fa096c1fe..310af72c41 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -24,8 +24,14 @@ module ActiveRecord
# You can define a scope that applies to all finders using
# {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope].
def all
+ current_scope = self.current_scope
+
if current_scope
- current_scope.clone
+ if self == current_scope.klass
+ current_scope.clone
+ else
+ relation.merge!(current_scope)
+ end
else
default_scoped
end
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index 4cf5a9ffc4..65d30d011b 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -579,6 +579,28 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert !c.post_taggings.empty?
end
+ def test_polymorphic_has_many_through_when_through_association_has_not_loaded
+ cake_designer = CakeDesigner.create!(chef: Chef.new)
+ drink_designer = DrinkDesigner.create!(chef: Chef.new)
+ department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef])
+ Hotel.create!(departments: [department])
+ hotel = Hotel.includes(:cake_designers, :drink_designers).take
+
+ assert_equal [cake_designer], hotel.cake_designers
+ assert_equal [drink_designer], hotel.drink_designers
+ end
+
+ def test_polymorphic_has_many_through_when_through_association_has_already_loaded
+ cake_designer = CakeDesigner.create!(chef: Chef.new)
+ drink_designer = DrinkDesigner.create!(chef: Chef.new)
+ department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef])
+ Hotel.create!(departments: [department])
+ hotel = Hotel.includes(:chefs, :cake_designers, :drink_designers).take
+
+ assert_equal [cake_designer], hotel.cake_designers
+ assert_equal [drink_designer], hotel.drink_designers
+ end
+
def test_polymorphic_has_many_through_joined_different_table_twice
cake_designer = CakeDesigner.create!(chef: Chef.new)
drink_designer = DrinkDesigner.create!(chef: Chef.new)
diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb
index f3b84d88c2..116f8e83aa 100644
--- a/activerecord/test/cases/scoping/relation_scoping_test.rb
+++ b/activerecord/test/cases/scoping/relation_scoping_test.rb
@@ -240,6 +240,20 @@ class RelationScopingTest < ActiveRecord::TestCase
assert_nil SpecialComment.current_scope
end
+ def test_scoping_respects_current_class
+ Comment.unscoped do
+ assert_equal "a comment...", Comment.all.what_are_you
+ assert_equal "a special comment...", SpecialComment.all.what_are_you
+ end
+ end
+
+ def test_scoping_respects_sti_constraint
+ Comment.unscoped do
+ assert_equal comments(:greetings), Comment.find(1)
+ assert_raises(ActiveRecord::RecordNotFound) { SpecialComment.find(1) }
+ end
+ end
+
def test_circular_joins_with_scoping_does_not_crash
posts = Post.joins(comments: :post).scoping do
Post.first(10)
diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb
index 740aa593ac..5ab433f2d9 100644
--- a/activerecord/test/models/comment.rb
+++ b/activerecord/test/models/comment.rb
@@ -60,6 +60,10 @@ end
class SpecialComment < Comment
default_scope { where(deleted_at: nil) }
+
+ def self.what_are_you
+ "a special comment..."
+ end
end
class SubSpecialComment < SpecialComment