[Fix #51720] Infer association klass as top level if model has same demodularized name

This commit is contained in:
Joshua Young 2024-05-03 13:25:57 +10:00
parent 6a283acf4c
commit 0516eafda2
4 changed files with 53 additions and 2 deletions

View File

@ -1,3 +1,17 @@
* Fix inference of association model on nested models with the same demodularized name.
E.g. with the following setup:
```ruby
class Nested::Post < ApplicationRecord
has_one :post, through: :other
end
```
Before, `#post` would infer the model as `Nested::Post`, but now it correctly infers `Post`.
*Joshua Young*
* Add public method for checking if a table is ignored by the schema cache.
Previously, an application would need to reimplement `ignored_table?` from the schema cache class to check if a table was set to be ignored. This adds a public method to support this and updates the schema cache to use that directly.

View File

@ -428,13 +428,17 @@ module ActiveRecord
# a new association object. Use +build_association+ or +create_association+
# instead. This allows plugins to hook into association object creation.
def klass
@klass ||= compute_class(class_name)
@klass ||= compute_class(compute_name(class_name))
end
def compute_class(name)
name.constantize
end
def compute_name(name) # :nodoc:
active_record.name.demodulize == name ? "::#{name}" : name
end
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
# and +other_aggregation+ has an options hash assigned to it.
def ==(other_aggregation)
@ -994,7 +998,7 @@ module ActiveRecord
end
def klass
@klass ||= delegate_reflection.compute_class(class_name)
@klass ||= delegate_reflection.compute_class(compute_name(class_name))
end
# Returns the source of the through reflection. It checks both a singularized

View File

@ -30,6 +30,9 @@ require "models/recipe"
require "models/user_with_invalid_relation"
require "models/hardback"
require "models/sharded/comment"
require "models/admin"
require "models/admin/user"
require "models/user"
class ReflectionTest < ActiveRecord::TestCase
include ActiveRecord::Reflection
@ -181,6 +184,30 @@ class ReflectionTest < ActiveRecord::TestCase
end
end
def test_reflection_klass_with_same_demodularized_name
reflection = ActiveRecord::Reflection.create(
:has_one,
:user,
nil,
{},
Admin::User
)
assert_equal User, reflection.klass
end
def test_reflection_klass_with_same_demodularized_different_modularized_name
reflection = ActiveRecord::Reflection.create(
:has_one,
:user,
nil,
{ class_name: "Nested::User" },
Admin::User
)
assert_equal Nested::User, reflection.klass
end
def test_aggregation_reflection
reflection_for_address = AggregateReflection.new(
:address, nil, { mapping: [ %w(address_street street), %w(address_city city), %w(address_country country) ] }, Customer

View File

@ -23,3 +23,9 @@ end
class UserWithNotification < User
after_create -> { Notification.create! message: "A new user has been created." }
end
module Nested
class User < ActiveRecord::Base
self.table_name = "users"
end
end