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#1202
We have fixed the bulk of the RuboCop TODOs. The main TODO remaining
(line length) involves a large number of files,
and I don't think it is worth trying to update them all at once. I did
fix a few of the worst offenders so we could bring the max down a bit.
We can gradually bring this number down as we fix more of the
violations.
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.
Instead of calling before_create, after_build, after_create, or
after_stub, you can now call:
before(:create) {|instance| instance.name = "overridden!" }
after(:create) {|instance| instance.name = "overridden!" }
after(:build) {|instance| instance.name = "overridden!" }
after(:stub) {|instance| instance.name = "overridden!" }
Additionally, you can declare callbacks longhand:
callback(:after_stub) {|instance| instance.name = "overridden!" }
This allows for custom callbacks to be defined:
callback(:custom_callback) {|instance| instance.name = "overridden!" }
Which can then be used from a custom strategy:
class CustomStrategy
def association(runner); end
def result(evaluation)
evaluation.object.tap do |instance|
evaluation.notify(:custom_callback, instance)
end
end
end
FactoryGirl.register_strategy(:custom, CustomStrategy)
This would allow for calling:
FactoryGirl.custom(:user)
Which would return the user instance but execute the :custom_callback callback
on the user instance first.
This adds `FactoryGirl.modify`, which allows for reopening of factories
that've been defined elsewhere. Modifying a factory won't remove or
change callbacks, only attributes.