1
0
Fork 0
mirror of https://github.com/thoughtbot/factory_bot.git synced 2022-11-09 11:43:51 -05:00

Fix ruby 2.7 kwargs warning

Ruby 2.7 deprecated passing kwargs when the method expects a hash or passing a hash when the method expects kwargs. In factory_bot, this creates the warning:

```
/Users/hparker/code/factory_bot/lib/factory_bot/decorator/new_constructor.rb:9: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/Users/hparker/code/factory_bot/spec/acceptance/initialize_with_spec.rb:220: warning: The called method `initialize' is defined here
```

We can fix this warning by updating the syntax. We need to include `**kwargs` in the `method_missing` calls when we are on ruby 2.7 or later.
In decorator.rb, adding `**kwargs` alone doesn't work since adding `**kwargs` can change what arguments remain in the `args`.

In this case we have to class eval the method if we are running ruby 2.7. This way the syntax is valid in previous versions and we can use the `...` operator which allows us to avoid changing the arguments passed on in method missing.

Co-authored-by: Lee Quarella <leequarella@gmail.com>
This commit is contained in:
Adam Hess 2020-06-23 09:54:55 -07:00 committed by Daniel Colson
parent 468536ff65
commit 5c071d42fd
4 changed files with 45 additions and 16 deletions

View file

@ -6,17 +6,29 @@ module FactoryBot
@component = component @component = component
end end
def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissingSuper if ::Gem::Version.new(::RUBY_VERSION) >= ::Gem::Version.new("2.7")
@component.send(name, *args, &block) class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
def method_missing(...) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
@component.send(...)
end end
def respond_to_missing?(name, include_private = false) def send(...)
@component.respond_to?(name, true) || super __send__(...)
end
RUBY
else
def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
@component.send(name, *args, &block)
end end
def send(symbol, *args, &block) def send(symbol, *args, &block)
__send__(symbol, *args, &block) __send__(symbol, *args, &block)
end end
end
def respond_to_missing?(name, include_private = false)
@component.respond_to?(name, true) || super
end
def self.const_missing(name) def self.const_missing(name)
::Object.const_get(name) ::Object.const_get(name)

View file

@ -6,10 +6,17 @@ module FactoryBot
@invoked_methods = [] @invoked_methods = []
end end
if ::Gem::Version.new(::RUBY_VERSION) >= ::Gem::Version.new("2.7")
def method_missing(name, *args, **kwargs, &block) # rubocop:disable Style/MissingRespondToMissing
@invoked_methods << name
super
end
else
def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing
@invoked_methods << name @invoked_methods << name
super super
end end
end
def __invoked_methods__ def __invoked_methods__
@invoked_methods.uniq @invoked_methods.uniq

View file

@ -35,13 +35,23 @@ module FactoryBot
attr_writer :instance attr_writer :instance
def method_missing(method_name, *args, &block) # rubocop:disable Style/MethodMissingSuper if ::Gem::Version.new(::RUBY_VERSION) >= ::Gem::Version.new("2.7")
def method_missing(method_name, *args, **kwargs, &block) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
if @instance.respond_to?(method_name)
@instance.send(method_name, *args, **kwargs, &block)
else
SyntaxRunner.new.send(method_name, *args, **kwargs, &block)
end
end
else
def method_missing(method_name, *args, &block) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
if @instance.respond_to?(method_name) if @instance.respond_to?(method_name)
@instance.send(method_name, *args, &block) @instance.send(method_name, *args, &block)
else else
SyntaxRunner.new.send(method_name, *args, &block) SyntaxRunner.new.send(method_name, *args, &block)
end end
end end
end
def respond_to_missing?(method_name, _include_private = false) def respond_to_missing?(method_name, _include_private = false)
@instance.respond_to?(method_name) || SyntaxRunner.new.respond_to?(method_name) @instance.respond_to?(method_name) || SyntaxRunner.new.respond_to?(method_name)

View file

@ -201,7 +201,7 @@ describe "initialize_with has access to all attributes for construction" do
name { email.gsub(/@.+/, "") } name { email.gsub(/@.+/, "") }
initialize_with { new(attributes) } initialize_with { new(**attributes) }
end end
end end
@ -225,7 +225,7 @@ describe "initialize_with with an 'attributes' attribute" do
FactoryBot.define do FactoryBot.define do
factory :user do factory :user do
attributes { {name: "Daniel"} } attributes { {name: "Daniel"} }
initialize_with { new(attributes) } initialize_with { new(**attributes) }
end end
end end