diff options
-rw-r--r-- | activerecord/CHANGELOG.md | 7 | ||||
-rw-r--r-- | activerecord/lib/active_record/associations/association.rb | 9 | ||||
-rw-r--r-- | activerecord/test/cases/associations/belongs_to_associations_test.rb | 18 |
3 files changed, 31 insertions, 3 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 3351013b16..0ec3902eac 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,10 @@ +* Correctly thrown `ActiveRecord::AssociationTypeMismatch` when assigning + a wrong value to a namespaced association. + + Fixes #20545 + + *Diego Carrion* + * `validates_absence_of` respects `marked_for_destruction?`. Fixes #20449. diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index 930f678ae8..7c729676a7 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -211,9 +211,12 @@ module ActiveRecord # the kind of the class of the associated objects. Meant to be used as # a sanity check when you are about to assign an associated record. def raise_on_type_mismatch!(record) - unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize) - message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})" - raise ActiveRecord::AssociationTypeMismatch, message + unless record.is_a?(reflection.klass) + fresh_class = reflection.class_name.safe_constantize + unless fresh_class && record.is_a?(fresh_class) + message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})" + raise ActiveRecord::AssociationTypeMismatch, message + end end end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 039cc46b0b..7726829be4 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -19,6 +19,8 @@ require 'models/invoice' require 'models/line_item' require 'models/column' require 'models/record' +require 'models/admin' +require 'models/admin/user' class BelongsToAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :developers, :projects, :topics, @@ -151,6 +153,22 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_raise(ActiveRecord::AssociationTypeMismatch) { Account.find(1).firm = Project.find(1) } end + def test_type_mismatch_with_namespaced_class + assert_nil defined?(Region), "This test should be done with a only namespaced class" + ActiveRecord::Base.connection.create_table(:admin_regions) { |t| t.string :name } + ActiveRecord::Base.connection.add_column :admin_users, :region_id, :integer + Admin.const_set "RegionalUser", Class.new(Admin::User) { belongs_to(:region) } + Admin.const_set "Region", Class.new(ActiveRecord::Base) + e = assert_raise(ActiveRecord::AssociationTypeMismatch) { Admin::RegionalUser.new(region: 'wrong value') } + assert_match(/^Region\([^)]+\) expected, got String\([^)]+\)$/, e.message) + ensure + Admin.send :remove_const, "Region" if Admin.const_defined?("Region") + Admin.send :remove_const, "RegionalUser" if Admin.const_defined?("RegionalUser") + connection = ActiveRecord::Base.connection + connection.remove_column :admin_users, :region_id if connection.column_exists?(:admin_users, :region_id) + connection.drop_table :admin_regions if connection.table_exists?(:admin_regions) + end + def test_natural_assignment apple = Firm.create("name" => "Apple") citibank = Account.create("credit_limit" => 10) |