Closes#14235c071d42 fixed most kwarg deprecation warnings coming from within
factory_bot, but using the Ruby 3-style argument forwarding does not
handle every case.
To handle the case of `initialize_with` being used with a method that
takes a final hash argument, this commit adds an additional branch to
this code to use `ruby2_keywords` for Ruby >= 2.7 and < 3.0. On 3.0 we
use the 3.0-style forwarding.
I added a tests that was outputting a deprecation warning on Ruby 2.7
before this change, but that no longer outputs the warning after this
change.
Yes, this is a bit of a mess, but I don't see a better way. I got
inspiration here from this [fantastic blog post on argument delegating
with Ruby 2.7 and 3][delegating].
[delegating]: https://eregon.me/blog/2019/11/10/the-delegation-challenge-of-ruby27.html#the-delegation-challenge.
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>
Fixes 1155
Defining an attribute called 'attributes', then referring to that
attribute in `initialize_with` was causing `AttributeHash#attributes`
to call itself recursively.
Most of this was fixed by adding the `attribute-defined-statically-cop`
branch of `thoughtbot/rubocop-rspec` to the Gemfile and running:
```sh
rubocop \
--require rubocop-rspec \
--only FactoryBot/AttributeDefinedStatically \
--auto-correct
```
I had to update the cucumber tests manually, and I realized our changes
don't handle `ignore` blocks or blocks with arity 1 that use the yielded
DefinitionProxy. I will update
https://github.com/rubocop-hq/rubocop-rspec/pull/666to handle these
cases.
Why?
When the decorated @component is also a decorated object, and
FactoryGirl::Decorator#method_missing calls @component#send and a block
is present, the block needs to be sent appropriately.
Closes#816
By setting:
FactoryGirl.duplicate_attribute_assignment_from_initialize_with =
false
This turns off duplicate assignment of attributes accessed from
initialize_with. This means any attributes accessed from the factory and
assigned in the initialize_with block won't be subsequently set after
the object has been instantiated.
This will be the default functionality in 4.0.
Closes#345
Factory Girl now allows factories to override object instantiation. This
means factories can use factory methods (e.g. methods other than new) as
well as pass arguments explicitly.
factory :user do
ignore do
things { ["thing 1", "thing 2"] }
end
initialize_with { User.construct_with_things(things) }
end
factory :report_generator do
ignore do
name { "Generic Report" }
data { {:foo => "bar", :baz => "buzz"} }
end
initialize_with { ReportGenerator.new(name, data) }
end
Whitespace
Code recommendations