diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb index a7d1bcca765a5..71cc8f0321c80 100644 --- a/activerecord/lib/active_record/associations/preloader.rb +++ b/activerecord/lib/active_record/associations/preloader.rb @@ -185,7 +185,7 @@ def records_by_owner # and attach it to a relation. The class returned implements a `run` method # that accepts a preloader. def preloader_for(reflection, owners) - if owners.first.association(reflection.name).loaded? + if owners.all? { |o| o.association(reflection.name).loaded? } return AlreadyLoaded end reflection.check_preloadable! diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index f9ce64456c7f8..cd8f8e18e99a0 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -626,6 +626,21 @@ def test_eager_with_has_many_through_an_sti_join_model assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments } end + def test_preloading_with_has_one_through_an_sti_with_after_initialize + author_a = Author.create!(name: "A") + author_b = Author.create!(name: "B") + post_a = StiPost.create!(author: author_a, title: "TITLE", body: "BODY") + post_b = SpecialPost.create!(author: author_b, title: "TITLE", body: "BODY") + comment_a = SpecialComment.create!(post: post_a, body: "TEST") + comment_b = SpecialComment.create!(post: post_b, body: "TEST") + reset_callbacks(StiPost, :initialize) do + StiPost.after_initialize { author } + SpecialComment.where(id: [comment_a.id, comment_b.id]).includes(:author).each do |comment| + assert comment.author + end + end + end + def test_preloading_has_many_through_with_implicit_source authors = Author.includes(:very_special_comments).to_a assert_no_queries do diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index e92aa71f2d1b6..3428c8bcbbd8b 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -2830,7 +2830,7 @@ def self.name end test "prevent double insertion of new object when the parent association loaded in the after save callback" do - reset_callbacks(:save, Bulb) do + reset_callbacks(Bulb, :save) do Bulb.after_save { |record| record.car.bulbs.load } car = Car.create! @@ -2841,7 +2841,7 @@ def self.name end test "prevent double firing the before save callback of new object when the parent association saved in the callback" do - reset_callbacks(:save, Bulb) do + reset_callbacks(Bulb, :save) do count = 0 Bulb.before_save { |record| record.car.save && count += 1 } @@ -2960,7 +2960,7 @@ def test_ids_reader_memoization end def test_loading_association_in_validate_callback_doesnt_affect_persistence - reset_callbacks(:validation, Bulb) do + reset_callbacks(Bulb, :validation) do Bulb.after_validation { |record| record.car.bulbs.load } car = Car.create!(name: "Car") @@ -2996,18 +2996,4 @@ def test_has_many_preloading_with_duplicate_records def force_signal37_to_load_all_clients_of_firm companies(:first_firm).clients_of_firm.load_target end - - def reset_callbacks(kind, klass) - old_callbacks = {} - old_callbacks[klass] = klass.send("_#{kind}_callbacks").dup - klass.subclasses.each do |subclass| - old_callbacks[subclass] = subclass.send("_#{kind}_callbacks").dup - end - yield - ensure - klass.send("_#{kind}_callbacks=", old_callbacks[klass]) - klass.subclasses.each do |subclass| - subclass.send("_#{kind}_callbacks=", old_callbacks[subclass]) - end - end end diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb index 669e176dcbbdc..25b150443b6d8 100644 --- a/activerecord/test/cases/associations/inverse_associations_test.rb +++ b/activerecord/test/cases/associations/inverse_associations_test.rb @@ -545,13 +545,6 @@ def test_inverse_instance_should_be_set_before_initialize_callbacks_are_run assert_predicate Man.joins(:interests).includes(:interests).first.interests, :any? end end - - def reset_callbacks(target, type) - old_callbacks = target.send(:get_callbacks, type).deep_dup - yield - ensure - target.send(:set_callbacks, type, old_callbacks) if old_callbacks - end end class InverseBelongsToTests < ActiveRecord::TestCase diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb index 524cc2dff5b75..3504a91ff5218 100644 --- a/activerecord/test/cases/test_case.rb +++ b/activerecord/test/cases/test_case.rb @@ -79,6 +79,20 @@ def has_column?(model, column_name) model.reset_column_information model.column_names.include?(column_name.to_s) end + + def reset_callbacks(klass, kind) + old_callbacks = {} + old_callbacks[klass] = klass.send("_#{kind}_callbacks").dup + klass.subclasses.each do |subclass| + old_callbacks[subclass] = subclass.send("_#{kind}_callbacks").dup + end + yield + ensure + klass.send("_#{kind}_callbacks=", old_callbacks[klass]) + klass.subclasses.each do |subclass| + subclass.send("_#{kind}_callbacks=", old_callbacks[subclass]) + end + end end class PostgreSQLTestCase < TestCase diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb index f0f057670957f..2b8ef5eb89984 100644 --- a/activerecord/test/models/comment.rb +++ b/activerecord/test/models/comment.rb @@ -59,6 +59,7 @@ def to_s end class SpecialComment < Comment + has_one :author, through: :post default_scope { where(deleted_at: nil) } def self.what_are_you