Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure factory_bot only loads after initialization #347

Merged
merged 1 commit into from
Oct 2, 2019

Conversation

composerinteralia
Copy link
Collaborator

Fixes #336
Alternate solution to #343

The initialization process looks like this:

Rails runs all of the initializers

  1. Run the "factory_bot.set_factory_paths"
    initializer
  2. Run the "factory_bot.register_reloader"
    initializer, which sets up a prepare callback
  3. Run the :run_prepare_callbacks initializer
  4. This triggers the factory_bot prepare callback, which causes
    factory_bot to reload

Rails runs after_initialize callbacks

  1. I18n initializes
  2. factory_bot reloads again as described in Add back loading definitions in after_initialize #334

The double reloading of factory_bot in this initialization is not ideal,
but also shouldn't generally cause any problems on its own.

The problems people are having in #336 come from the fact that
I18n gets set up in an after_initialize callback, but factory_bot gets
reloaded before the after_initialize callbacks are triggered.
If the FactoryBot.define block references any code that uses I18n
translations as it loads, that code will raise an error (references
inside other factory_bot methods, or code that uses I18n translations
inside of methods still works fine, since the whole Rails initialization
process would be complete by the time any of that code runs).

This commit changes step 4 above to avoid reloading factory_bot before
the application has initialized.

Co-authored-by: Danny Garcia [email protected]

Fixes #336
Alternate solution to #343

The initialization process looks like this:

Rails runs all of the initializers
1. Run the ["factory_bot.set_factory_paths"][set_factory_paths]
initializer
2. Run the ["factory_bot.register_reloader"][register_reloader]
initializer, which sets up a [prepare callback][]
3. Run the [`:run_prepare_callbacks`][] initializer
4. This triggers the factory_bot [prepare callback][], which causes
factory\_bot to [reload][]

Rails runs `after_initialize` callbacks
1. [I18n initializes]
2. factory\_bot [reloads again][] as described in #334

The double reloading of factory_bot in this initialization is not ideal,
but also shouldn't generally cause any problems on its own.

The problems people are having in #336 come from the fact that
I18n gets set up in an `after_initialize` callback, but factory_bot gets
reloaded before the `after_initialize` callbacks are triggered.
If the `FactoryBot.define` block references any code that uses I18n
translations as it loads, that code will raise an error (references
inside other factory_bot methods, or code that uses I18n translations
inside of methods still works fine, since the whole Rails initialization
process would be complete by the time any of that code runs).

This commit changes step 4 above to avoid reloading factory_bot before
the application has initialized.

[set_factory_paths]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L17-L19
[register_reloader]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L21-L23
[prepare callback]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L34-L36
[`:run_prepare_callbacks`]: https://github.com/rails/rails/blob/5-2-stable/railties/lib/rails/application/finisher.rb#L62-L64
[reload]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L24-L26
[I18n initializes]: https://github.com/rails/rails/blob/13e2102517fafc8f8736fce5d57de901067202d0/activesupport/lib/active_support/i18n_railtie.rb#L16-L20
[reloads again]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/railtie.rb#L25-L27

Co-authored-by: Danny Garcia <[email protected]>
Copy link
Contributor

@aledustet aledustet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the description, this seems to solve the problem. I'm worried about any funkiness in initializers that someone might have in their configs that might get confusing by the double reload. But yet again there's no documentation about this so it shouldn't be a problem.

@@ -63,3 +63,45 @@ Feature:
And I run `spring stop` with a clean environment
Then the output should contain "1 runs, 1 assertions"
And the output should not contain "Failure:"

Scenario: Initializing the reloader with I18n support
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very self-explanatory and specific test 👏

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! I will merge this and close #343 for now.

@composerinteralia composerinteralia merged commit 1e55d45 into master Oct 2, 2019
@composerinteralia composerinteralia deleted the i18n-loading-bug-v2 branch October 2, 2019 22:37
composerinteralia added a commit that referenced this pull request May 22, 2020
Alternate fix for #336. We went with #347 instead because the solution
in this commit didn't work with Rails 4.2. Since we are no longer
supporting Rails 4.2, I think this is a better approach.

The original problem looked like this:

Rails runs all of the initializers
1. Run the ["factory_bot.set_factory_paths"][set_factory_paths]
initializer
2. Run the ["factory_bot.register_reloader"][register_reloader]
initializer, which sets up a [prepare callback][]
3. Run the [`:run_prepare_callbacks`][] initializer
4. This triggers the factory_bot [prepare callback][], which causes
factory\_bot to [reload][]

Rails runs `after_initialize` callbacks
1. [I18n initializes]
2. factory\_bot [reloads again][] as described in #334

Triggering the first factory_bot reload before initializing I18n could
cause an error in some cases.

We avoided the problem in #347 by adding a conditional to skip reloading
factory_bot before the application has initialized.

This commit, on the other hand, moves factory_bot reloading from a
prepare callback into an `after_initialize` callback. The initialization
process is now simplified to:

Rails runs all of the initializers
1. Run the ["factory_bot.set_factory_paths"][set_factory_paths]
2. Run the [`:run_prepare_callbacks`][] initializer, which no longer
involves factory_bot

Rails runs `after_intialize` callbacks
1. [I18n initializes]
2. factory_bot loads definitions for the first time. It then runs the
reloader to set up the prepare callback to reload factory_bot when the
application reloads (for example by calling `reload!` in the console),
and to register the reloader to trigger reloads when any factory_bot
definition files change.

[set_factory_paths]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L17-L19
[register_reloader]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L21-L23
[prepare callback]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L34-L36
[`:run_prepare_callbacks`]: https://github.com/rails/rails/blob/5-2-stable/railties/lib/rails/application/finisher.rb#L62-L64
[reload]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L24-L26
[I18n initializes]: https://github.com/rails/rails/blob/13e2102517fafc8f8736fce5d57de901067202d0/activesupport/lib/active_support/i18n_railtie.rb#L16-L20
[reloads again]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/railtie.rb#L25-L27
composerinteralia added a commit that referenced this pull request May 22, 2020
Alternate fix for #336. We went with #347 instead because the solution
in this commit didn't work with Rails 4.2. Since we are no longer
supporting Rails 4.2, I think this is a better approach.

The original problem looked like this:

Rails runs all of the initializers
1. Run the ["factory_bot.set_factory_paths"][set_factory_paths]
initializer
2. Run the ["factory_bot.register_reloader"][register_reloader]
initializer, which sets up a [prepare callback][]
3. Run the [`:run_prepare_callbacks`][] initializer
4. This triggers the factory_bot [prepare callback][], which causes
factory\_bot to [reload][]

Rails runs `after_initialize` callbacks
1. [I18n initializes]
2. factory\_bot [reloads again][] as described in #334

Triggering the first factory_bot reload before initializing I18n could
cause an error in some cases.

We avoided the problem in #347 by adding a conditional to skip reloading
factory_bot before the application has initialized.

This commit, on the other hand, moves factory_bot reloading from a
prepare callback into an `after_initialize` callback. The initialization
process is now simplified to:

Rails runs all of the initializers
1. Run the ["factory_bot.set_factory_paths"][set_factory_paths]
2. Run the [`:run_prepare_callbacks`][] initializer, which no longer
involves factory_bot

Rails runs `after_intialize` callbacks
1. [I18n initializes]
2. factory_bot loads definitions for the first time. It then runs the
reloader to set up the prepare callback to reload factory_bot when the
application reloads (for example by calling `reload!` in the console),
and to register the reloader to trigger reloads when any factory_bot
definition files change.

[set_factory_paths]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L17-L19
[register_reloader]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L21-L23
[prepare callback]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L34-L36
[`:run_prepare_callbacks`]: https://github.com/rails/rails/blob/5-2-stable/railties/lib/rails/application/finisher.rb#L62-L64
[reload]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L24-L26
[I18n initializes]: https://github.com/rails/rails/blob/13e2102517fafc8f8736fce5d57de901067202d0/activesupport/lib/active_support/i18n_railtie.rb#L16-L20
[reloads again]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/railtie.rb#L25-L27
composerinteralia added a commit that referenced this pull request May 23, 2020
Alternate fix for #336. We went with #347 instead because the solution
in this commit didn't work with Rails 4.2. Since we are no longer
supporting Rails 4.2, I think this is a better approach.

The original problem looked like this:

Rails runs all of the initializers
1. Run the ["factory_bot.set_factory_paths"][set_factory_paths]
initializer
2. Run the ["factory_bot.register_reloader"][register_reloader]
initializer, which sets up a [prepare callback][]
3. Run the [`:run_prepare_callbacks`][] initializer
4. This triggers the factory_bot [prepare callback][], which causes
factory\_bot to [reload][]

Rails runs `after_initialize` callbacks
1. [I18n initializes]
2. factory\_bot [reloads again][] as described in #334

Triggering the first factory_bot reload before initializing I18n could
cause an error in some cases.

We avoided the problem in #347 by adding a conditional to skip reloading
factory_bot before the application has initialized.

This commit, on the other hand, moves factory_bot reloading from a
prepare callback into an `after_initialize` callback. The initialization
process is now simplified to:

Rails runs all of the initializers
1. Run the ["factory_bot.set_factory_paths"][set_factory_paths]
2. Run the [`:run_prepare_callbacks`][] initializer, which no longer
involves factory_bot

Rails runs `after_intialize` callbacks
1. [I18n initializes]
2. factory_bot loads definitions for the first time. It then runs the
reloader to set up the prepare callback to reload factory_bot when the
application reloads (for example by calling `reload!` in the console),
and to register the reloader to trigger reloads when any factory_bot
definition files change.

[set_factory_paths]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L17-L19
[register_reloader]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L21-L23
[prepare callback]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L34-L36
[`:run_prepare_callbacks`]: https://github.com/rails/rails/blob/5-2-stable/railties/lib/rails/application/finisher.rb#L62-L64
[reload]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L24-L26
[I18n initializes]: https://github.com/rails/rails/blob/13e2102517fafc8f8736fce5d57de901067202d0/activesupport/lib/active_support/i18n_railtie.rb#L16-L20
[reloads again]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/railtie.rb#L25-L27
composerinteralia added a commit that referenced this pull request May 23, 2020
Alternate fix for #336. We went with #347 instead because the solution
in this commit didn't work with Rails 4.2. Since we are no longer
supporting Rails 4.2, I think this is a better approach.

The original problem looked like this:

Rails runs all of the initializers
1. Run the ["factory_bot.set_factory_paths"][set_factory_paths]
initializer
2. Run the ["factory_bot.register_reloader"][register_reloader]
initializer, which sets up a [prepare callback][]
3. Run the [`:run_prepare_callbacks`][] initializer
4. This triggers the factory_bot [prepare callback][], which causes
factory\_bot to [reload][]

Rails runs `after_initialize` callbacks
1. [I18n initializes]
2. factory\_bot [reloads again][] as described in #334

Triggering the first factory_bot reload before initializing I18n could
cause an error in some cases.

We avoided the problem in #347 by adding a conditional to skip reloading
factory_bot before the application has initialized.

This commit, on the other hand, moves factory_bot reloading from a
prepare callback into an `after_initialize` callback. The initialization
process is now simplified to:

Rails runs all of the initializers
1. Run the ["factory_bot.set_factory_paths"][set_factory_paths]
2. Run the [`:run_prepare_callbacks`][] initializer, which no longer
involves factory_bot

Rails runs `after_intialize` callbacks
1. [I18n initializes]
2. factory_bot loads definitions for the first time. It then runs the
reloader to set up the prepare callback to reload factory_bot when the
application reloads (for example by calling `reload!` in the console),
and to register the reloader to trigger reloads when any factory_bot
definition files change.

[set_factory_paths]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L17-L19
[register_reloader]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L21-L23
[prepare callback]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L34-L36
[`:run_prepare_callbacks`]: https://github.com/rails/rails/blob/5-2-stable/railties/lib/rails/application/finisher.rb#L62-L64
[reload]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L24-L26
[I18n initializes]: https://github.com/rails/rails/blob/13e2102517fafc8f8736fce5d57de901067202d0/activesupport/lib/active_support/i18n_railtie.rb#L16-L20
[reloads again]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/railtie.rb#L25-L27
composerinteralia added a commit that referenced this pull request Jun 15, 2020
Alternate fix for #336. We went with #347 instead because the solution
in this commit didn't work with Rails 4.2. Since we are no longer
supporting Rails 4.2, I think this is a better approach.

The original problem looked like this:

Rails runs all of the initializers
1. Run the ["factory_bot.set_factory_paths"][set_factory_paths]
initializer
2. Run the ["factory_bot.register_reloader"][register_reloader]
initializer, which sets up a [prepare callback][]
3. Run the [`:run_prepare_callbacks`][] initializer
4. This triggers the factory_bot [prepare callback][], which causes
factory\_bot to [reload][]

Rails runs `after_initialize` callbacks
1. [I18n initializes]
2. factory\_bot [reloads again][] as described in #334

Triggering the first factory_bot reload before initializing I18n could
cause an error in some cases.

We avoided the problem in #347 by adding a conditional to skip reloading
factory_bot before the application has initialized.

This commit, on the other hand, moves factory_bot reloading from a
prepare callback into an `after_initialize` callback. The initialization
process is now simplified to:

Rails runs all of the initializers
1. Run the ["factory_bot.set_factory_paths"][set_factory_paths]
2. Run the [`:run_prepare_callbacks`][] initializer, which no longer
involves factory_bot

Rails runs `after_intialize` callbacks
1. [I18n initializes]
2. factory_bot loads definitions for the first time. It then runs the
reloader to set up the prepare callback to reload factory_bot when the
application reloads (for example by calling `reload!` in the console),
and to register the reloader to trigger reloads when any factory_bot
definition files change.

[set_factory_paths]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L17-L19
[register_reloader]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L21-L23
[prepare callback]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L34-L36
[`:run_prepare_callbacks`]: https://github.com/rails/rails/blob/5-2-stable/railties/lib/rails/application/finisher.rb#L62-L64
[reload]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L24-L26
[I18n initializes]: https://github.com/rails/rails/blob/13e2102517fafc8f8736fce5d57de901067202d0/activesupport/lib/active_support/i18n_railtie.rb#L16-L20
[reloads again]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/railtie.rb#L25-L27
composerinteralia added a commit that referenced this pull request Jun 15, 2020
Alternate fix for #336. We went with #347 instead because the solution
in this commit didn't work with Rails 4.2. Since we are no longer
supporting Rails 4.2, I think this is a better approach.

The original problem looked like this:

Rails runs all of the initializers
1. Run the ["factory_bot.set_factory_paths"][set_factory_paths]
initializer
2. Run the ["factory_bot.register_reloader"][register_reloader]
initializer, which sets up a [prepare callback][]
3. Run the [`:run_prepare_callbacks`][] initializer
4. This triggers the factory_bot [prepare callback][], which causes
factory\_bot to [reload][]

Rails runs `after_initialize` callbacks
1. [I18n initializes]
2. factory\_bot [reloads again][] as described in #334

Triggering the first factory_bot reload before initializing I18n could
cause an error in some cases.

We avoided the problem in #347 by adding a conditional to skip reloading
factory_bot before the application has initialized.

This commit, on the other hand, moves factory_bot reloading from a
prepare callback into an `after_initialize` callback. The initialization
process is now simplified to:

Rails runs all of the initializers
1. Run the ["factory_bot.set_factory_paths"][set_factory_paths]
2. Run the [`:run_prepare_callbacks`][] initializer, which no longer
involves factory_bot

Rails runs `after_intialize` callbacks
1. [I18n initializes]
2. factory_bot loads definitions for the first time. It then runs the
reloader to set up the prepare callback to reload factory_bot when the
application reloads (for example by calling `reload!` in the console),
and to register the reloader to trigger reloads when any factory_bot
definition files change.

[set_factory_paths]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L17-L19
[register_reloader]: https://github.com/thoughtbot/factory_bot_rails/blob/3815aae2b9e4a5c5c3027a2ee8851f44b7c6a4da/lib/factory_bot_rails/railtie.rb#L21-L23
[prepare callback]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L34-L36
[`:run_prepare_callbacks`]: https://github.com/rails/rails/blob/5-2-stable/railties/lib/rails/application/finisher.rb#L62-L64
[reload]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/reloader.rb#L24-L26
[I18n initializes]: https://github.com/rails/rails/blob/13e2102517fafc8f8736fce5d57de901067202d0/activesupport/lib/active_support/i18n_railtie.rb#L16-L20
[reloads again]: https://github.com/thoughtbot/factory_bot_rails/blob/master/lib/factory_bot_rails/railtie.rb#L25-L27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Issues with custom error message translations
2 participants