diff options
3 files changed, 27 insertions, 11 deletions
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index ca8c7794e0..d2934c830a 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -19,7 +19,6 @@ module ActiveRecord # HasManyThroughAssociation + ThroughAssociation class Association #:nodoc: attr_reader :owner, :target, :reflection - attr_accessor :inversed delegate :options, to: :reflection @@ -67,7 +66,7 @@ module ActiveRecord # # Note that if the target has not been loaded, it is not considered stale. def stale_target? - !inversed && loaded? && @stale_state != stale_state + !@inversed && loaded? && @stale_state != stale_state end # Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+. @@ -98,23 +97,24 @@ module ActiveRecord # Set the inverse association, if possible def set_inverse_instance(record) - if invertible_for?(record) - inverse = record.association(inverse_reflection_for(record).name) - inverse.target = owner - inverse.inversed = true + if inverse = inverse_association_for(record) + inverse.inversed_from(owner) end record end # Remove the inverse association, if possible def remove_inverse_instance(record) - if invertible_for?(record) - inverse = record.association(inverse_reflection_for(record).name) - inverse.target = nil - inverse.inversed = false + if inverse = inverse_association_for(record) + inverse.inversed_from(nil) end end + def inversed_from(record) + self.target = record + @inversed = !!record + end + # Returns the class of the target. belongs_to polymorphic overrides this to look at the # polymorphic_type field on the owner. def klass @@ -240,6 +240,12 @@ module ActiveRecord end end + def inverse_association_for(record) + if invertible_for?(record) + record.association(inverse_reflection_for(record).name) + end + end + # Can be redefined by subclasses, notably polymorphic belongs_to # The record parameter is necessary to support polymorphic inverses as we must check for # the association in the specific class of the record. diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index c8716741b0..775559e224 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -16,7 +16,7 @@ module ActiveRecord end end - def target=(record) + def inversed_from(record) replace_keys(record) super end @@ -53,6 +53,8 @@ module ActiveRecord decrement_counters end + replace_keys(record) + self.target = record end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 5011a9bbde..b3f5de4c5f 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -44,6 +44,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_raise(frozen_error_class) { client.firm = Firm.new(name: "Firm") } end + def test_eager_loading_wont_mutate_owner_record + client = Client.eager_load(:firm_with_basic_id).first + assert_not_predicate client, :firm_id_came_from_user? + + client = Client.preload(:firm_with_basic_id).first + assert_not_predicate client, :firm_id_came_from_user? + end + def test_missing_attribute_error_is_raised_when_no_foreign_key_attribute assert_raises(ActiveModel::MissingAttributeError) { Client.select(:id).first.firm } end |