Skip to content

Commit 8d46b2d

Browse files
Rescue NameError for association class name only
This prevents converting a `NameError` from incidental code into a "friendly" `NameError` for the association model class. For example, in a scenario like: ```ruby # app/models/post.rb class Post < ActiveRecord::Base belongs_to :author end ``` ```ruby # app/models/author.rb class Author < ActiveRecord::Base validates_presence :name end ``` This changes the error message from: > Missing model class Author for the Post#author association. You can > specify a different model class with the :class_name option. to: > undefined method 'validates_presence' for Author:Class > > Did you mean? validates_presence_of Fixes rails#46532.
1 parent d9b59a0 commit 8d46b2d

File tree

2 files changed

+43
-5
lines changed

2 files changed

+43
-5
lines changed

activerecord/lib/active_record/reflection.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -425,10 +425,14 @@ def compute_class(name)
425425

426426
begin
427427
klass = active_record.send(:compute_type, name)
428-
rescue NameError
429-
message = "Missing model class #{name} for the #{active_record}##{self.name} association."
430-
message += " You can specify a different model class with the :class_name option." unless options[:class_name]
431-
raise NameError, message
428+
rescue NameError => error
429+
if error.name.match?(/(?:\A|::)#{name}\z/)
430+
message = "Missing model class #{name} for the #{active_record}##{self.name} association."
431+
message += " You can specify a different model class with the :class_name option." unless options[:class_name]
432+
raise NameError.new(message, name)
433+
else
434+
raise
435+
end
432436
end
433437

434438
unless klass < ActiveRecord::Base
@@ -619,7 +623,9 @@ def automatic_inverse_of
619623

620624
begin
621625
reflection = klass._reflect_on_association(inverse_name)
622-
rescue NameError
626+
rescue NameError => error
627+
raise unless error.name.to_s == class_name
628+
623629
# Give up: we couldn't compute the klass type so we won't be able
624630
# to find any associations either.
625631
reflection = false

activerecord/test/cases/reflection_test.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ def test_reflection_klass_not_found_with_no_class_name_option
143143
UserWithInvalidRelation.reflect_on_association(:not_a_class).klass
144144
end
145145

146+
assert_equal "NotAClass", error.name
146147
assert_match %r/missing/i, error.message
147148
assert_match "NotAClass", error.message
148149
assert_match "UserWithInvalidRelation#not_a_class", error.message
@@ -154,6 +155,7 @@ def test_reflection_klass_not_found_with_pointer_to_non_existent_class_name
154155
UserWithInvalidRelation.reflect_on_association(:class_name_provided_not_a_class).klass
155156
end
156157

158+
assert_equal "NotAClass", error.name
157159
assert_match %r/missing/i, error.message
158160
assert_match %r/\bNotAClass\b/, error.message
159161
assert_match "UserWithInvalidRelation#class_name_provided_not_a_class", error.message
@@ -560,11 +562,41 @@ def test_reflect_on_association_accepts_strings
560562
end
561563
end
562564

565+
def test_name_error_from_incidental_code_is_not_converted_to_name_error_for_association
566+
UserWithInvalidRelation.stub(:const_missing, proc { oops }) do
567+
reflection = UserWithInvalidRelation.reflect_on_association(:not_a_class)
568+
569+
error = assert_raises(NameError) do
570+
reflection.klass
571+
end
572+
573+
assert_equal :oops, error.name
574+
assert_match "oops", error.message
575+
assert_no_match "NotAClass", error.message
576+
assert_no_match "not_a_class", error.message
577+
end
578+
end
579+
563580
def test_automatic_inverse_suppresses_name_error_for_association
564581
reflection = UserWithInvalidRelation.reflect_on_association(:not_a_class)
565582
assert_not reflection.dup.has_inverse? # dup to prevent global memoization
566583
end
567584

585+
def test_automatic_inverse_does_not_suppress_name_error_from_incidental_code
586+
UserWithInvalidRelation.stub(:const_missing, proc { oops }) do
587+
reflection = UserWithInvalidRelation.reflect_on_association(:not_a_class)
588+
589+
error = assert_raises(NameError) do
590+
reflection.dup.has_inverse? # dup to prevent global memoization
591+
end
592+
593+
assert_equal :oops, error.name
594+
assert_match "oops", error.message
595+
assert_no_match "NotAClass", error.message
596+
assert_no_match "not_a_class", error.message
597+
end
598+
end
599+
568600
private
569601
def assert_reflection(klass, association, options)
570602
assert reflection = klass.reflect_on_association(association)

0 commit comments

Comments
 (0)