Skip to content

Commit

Permalink
Ensure factory_bot only loads after initialization
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
composerinteralia and garciadanny committed Sep 20, 2019
1 parent 064838c commit 474a4b3
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Appraisals
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ appraise "rails6.0" do
gem "jbuilder"
gem "listen"
gem "puma", "~> 3.11"
gem "sass-rails", "~> 5"
gem "sass-rails"
gem "selenium-webdriver"
gem "spring"
gem "spring-watcher-listen"
Expand Down
42 changes: 42 additions & 0 deletions features/reloading.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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
When I successfully run `bundle exec rails new testapp -m ../../features/support/rails_template`
And I cd to "testapp"
And I add "factory_bot_rails" from this project as a dependency
And I add "test-unit" as a dependency
And I run `bundle install` with a clean environment
And I run `bundle exec rake db:migrate` with a clean environment
And I write to "app/models/user.rb" with:
"""
class User
TRANSLATION = I18n.translate("translation_key")
end
"""
And I write to "config/locales/en.yml" with:
"""
en:
translation_key: "translation_value"
"""
And I write to "test/factories.rb" with:
"""
FactoryBot.define do
factory :user do
User::TRANSLATION
end
end
"""
And I write to "test/unit/user_test.rb" with:
"""
require 'test_helper'
class UserTest < ActiveSupport::TestCase
test "use factory" do
user = FactoryBot.build(:user)
assert_equal "translation_value", User::TRANSLATION
end
end
"""
And I run `bundle exec rake test` with a clean environment
Then the output should contain "1 runs, 1 assertions"
And the output should not contain "Failure:"
2 changes: 1 addition & 1 deletion gemfiles/rails6.0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ gem "capybara"
gem "jbuilder"
gem "listen"
gem "puma", "~> 3.11"
gem "sass-rails", "~> 5"
gem "sass-rails"
gem "selenium-webdriver"
gem "spring"
gem "spring-watcher-listen"
Expand Down
6 changes: 5 additions & 1 deletion lib/factory_bot_rails/reloader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ def reloader_class
end

def register_reloader(reloader)
closed_over_app = app

config.to_prepare do
reloader.execute
if closed_over_app.initialized?
reloader.execute
end
end

app.reloaders << reloader
Expand Down

0 comments on commit 474a4b3

Please sign in to comment.