* Improve documentation for collection associations
This commit makes `Evaluator#association` and `Evaluator#instance`
officially part of the public API of factory\_bot. Folks were already
using these methods, and all of the examples here were inspired by
examples in the various issues mentioned below.
This commit also adds and updates some tests to match the examples in
the documentation, to ensure that these examples will continue to work
as expected.
Closes#1268 by adding documentation for both `Evaluator#association`
and `Evaluator#instance`. The documentation on interconnected
associations is also relevant to #1063, #1255, and #1309.
Closes#1304 by providing multiple alternative approaches to creating
collection associations, and adding a note about reloading the record in
the callback example. This had also come up back in #549.
Closes#458 by offering `Evaluator#association` as a more flexible way
to build collection associations. This has come up many times over the
years in many different forms, including #426, #487, #640, #1022, #1150,
and #1360.
closes#1309
It has come up several times over the years that folks want direct
access to the evaluator instance, in most cases to build up more complex
networks of associations. Some folks are already doing this with the
less public `@instance` instance variable.
Given that this is such a minimal change, and it makes the library more
flexible for these more complex use cases, this commit adds an
`attr_reader` for the evaluator instance. Now folks can reference it
without reaching into the private api.
Documentation to follow as part of
https://github.com/thoughtbot/factory_bot/issues/1268
Closes#1391
Along the same lines as #1286, this commit raises a more helpful error
if somebody passes an implicit declaration as an association argument.
Before this commit, an association with an implicit trait passed as an
override attribute:
```rb
person factory: :user, invalid_attribute: implicit_trait
```
Would raise an error `KeyError: Trait not registered: "implicit_trait"`.
This is potentially confusing, since the author probably didn't intend
to define an implicit trait.
After this commit, this will raise a more helpful error:
```
ArgumentError: Association 'person' received an invalid attribute override.
Did you mean? 'invalid_attribute}: :implicit_trait}'
```
Closes https://github.com/thoughtbot/factory_bot/issues/1227
In this code we are passing an implicit declaration `user`, rather than
the symbol `:user`:
```rb
factory :post do
author factory: user
end
```
In https://github.com/thoughtbot/factory_bot/issues/1227 we improved the
error message from:
```rb
undefined method 'name' for :post:Symbol
```
to:
```rb
Trait not registered: user
```
But this still doesn't make it totally obvious what the error was.
Why are we trying to register a user trait?
With this PR we update the error message to:
```rb
Association 'author' received an invalid factory argument.
Did you mean? 'factory: :user'
```
to make use of did_you_mean gem.
The did_you_mean gem only supports NameError, NoMethodError
and KeyError. However, for NameError the message does also need
to match a certain format and we can not use a custom message
like 'Factory not registered ...'. Therefore using KeyError
is to only logical conclusion.
The did_you_mean gem makes use of the receiver attributes, but
in Ruby > 2.5 it is not possible to set the receiver and key attributes
on a KeyError (they are only set when the KeyError is raised in C).
We explored monkey patching KeyError for earlier versions of Ruby, but
it was a problematic solution.
Instead we can rescue the original KeyError, take the message from it,
which will already include the did_you_mean message, then customize
the message and re-raise a new KeyError with that customized message.
Starting in Ruby 2.6 this will not be necessary anymore
https://bugs.ruby-lang.org/issues/14313, so maybe we can get rid of it
for FactoryBot 6 or 7.
Fixes#992
Co-authored-by: Daniel Colson <danieljamescolson@gmail.com>
Closes#970
In this code we are passing an implicit declaration `user`, rather than
the symbol `:user`:
```rb
factory :post do
author factory: user
end
```
This will raise a confusing error:
`undefined method 'name' for :comment:Symbol`.
This is coming from the implicit declaration `#==` method,
called on lib/factory_bot/attribute_list.rb#56.
The method wasn't ever designed to compare against
objects of different classes. I added some tests for all the declaration
classes to handle being compared against other kinds of objects.
Co-authored-by: Dusan Orlovic <duleorlovic@gmail.com>