Skip to content

Commit 2a34b5d

Browse files
Pass key and receiver to custom key errors (thoughtbot#1561)
did_you_mean tests started failing on Ruby 3.2 because of https://bugs.ruby-lang.org/issues/18564. This gets did_you_mean working again for Ruby 3.2 by adding the original KeyError's key and receiver over to the custom KeyError. That way the did_you_mean can get added to the custom KeyError via the new detailed_message.
1 parent 2cf84e9 commit 2a34b5d

5 files changed

Lines changed: 55 additions & 21 deletions

File tree

lib/factory_bot/definition.rb

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,26 @@ def base_traits
115115
raise error_with_definition_name(error)
116116
end
117117

118-
def error_with_definition_name(error)
119-
message = error.message
120-
message.insert(
121-
message.index("\nDid you mean?") || message.length,
122-
" referenced within \"#{name}\" definition"
123-
)
124-
125-
error.class.new(message).tap do |new_error|
126-
new_error.set_backtrace(error.backtrace)
118+
# detailed_message introduced in Ruby 3.2 for cleaner integration with
119+
# did_you_mean. See https://bugs.ruby-lang.org/issues/18564
120+
if KeyError.method_defined?(:detailed_message)
121+
def error_with_definition_name(error)
122+
message = error.message + " referenced within \"#{name}\" definition"
123+
124+
error.class.new(message, key: error.key, receiver: error.receiver)
125+
.tap { |new_error| new_error.set_backtrace(error.backtrace) }
126+
end
127+
else
128+
def error_with_definition_name(error)
129+
message = error.message
130+
message.insert(
131+
message.index("\nDid you mean?") || message.length,
132+
" referenced within \"#{name}\" definition"
133+
)
134+
135+
error.class.new(message).tap do |new_error|
136+
new_error.set_backtrace(error.backtrace)
137+
end
127138
end
128139
end
129140

lib/factory_bot/registry.rb

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,21 @@ def registered?(name)
3939

4040
def key_error_with_custom_message(key_error)
4141
message = key_error.message.sub("key not found", "#{@name} not registered")
42-
error = KeyError.new(message)
43-
error.set_backtrace(key_error.backtrace)
44-
error
42+
new_key_error(message, key_error).tap do |error|
43+
error.set_backtrace(key_error.backtrace)
44+
end
45+
end
46+
47+
# detailed_message introduced in Ruby 3.2 for cleaner integration with
48+
# did_you_mean. See https://bugs.ruby-lang.org/issues/18564
49+
if KeyError.method_defined?(:detailed_message)
50+
def new_key_error(message, key_error)
51+
KeyError.new(message, key: key_error.key, receiver: key_error.receiver)
52+
end
53+
else
54+
def new_key_error(message, _)
55+
KeyError.new(message)
56+
end
4557
end
4658
end
4759
end

spec/acceptance/traits_spec.rb

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -324,13 +324,7 @@ def build_user_factory_with_admin_trait(trait_name)
324324
end
325325
end
326326

327-
expect { FactoryBot.build(:user) }.to raise_error(
328-
KeyError,
329-
<<~MSG.strip
330-
Trait not registered: "not_quite" referenced within "user" definition
331-
Did you mean? "not_quit"
332-
MSG
333-
)
327+
expect { FactoryBot.build(:user) }.to raise_did_you_mean_error
334328
end
335329
end
336330

spec/factory_bot/registry_spec.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@
3333
registered_object = double(:registered_object)
3434
registry.register(:factory_bot, registered_object)
3535

36-
expect { registry.find(:factory_bit) }
37-
.to raise_error(KeyError, /Did you mean\? "factory_bot"/)
36+
expect { registry.find(:factory_bit) }.to raise_did_you_mean_error
3837
end
3938

4039
it "adds and returns the object registered" do
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
RSpec::Matchers.define :raise_did_you_mean_error do
2+
supports_block_expectations
3+
4+
match do |actual|
5+
# detailed_message introduced in Ruby 3.2 for cleaner integration with
6+
# did_you_mean. See https://bugs.ruby-lang.org/issues/18564
7+
matcher = if KeyError.method_defined?(:detailed_message)
8+
raise_error(
9+
an_instance_of(KeyError)
10+
.and(having_attributes(detailed_message: /Did you mean\?/))
11+
)
12+
else
13+
raise_error(KeyError, /Did you mean\?/)
14+
end
15+
16+
expect(actual).to matcher
17+
end
18+
end

0 commit comments

Comments
 (0)