thoughtbot--factory_bot_rails/lib/generators/factory_bot/model/model_generator.rb

100 lines
2.2 KiB
Ruby
Raw Normal View History

require 'generators/factory_bot'
require 'factory_bot_rails'
module FactoryBot
module Generators
class ModelGenerator < Base
argument(
:attributes,
type: :array,
default: [],
banner: "field:type field:type"
)
class_option(
:dir,
type: :string,
default: "test/factories",
desc: "The directory or file root where factories belong"
)
class_option(
:suffix,
type: :string,
default: nil,
desc: "Suffix to add factory file"
)
def create_fixture_file
if File.exist?(factories_file)
insert_factory_into_existing_file
else
create_factory_file
end
end
private
def factories_file
options[:dir] + ".rb"
end
def insert_factory_into_existing_file
insert_into_file(
factories_file,
factory_definition,
after: "FactoryBot.define do\n"
)
end
def create_factory_file
file = File.join(options[:dir], "#{filename}.rb")
create_file(file, single_file_factory_definition)
end
def factory_definition
<<-RUBY
factory :#{singular_table_name}#{explicit_class_option} do
2014-10-30 12:58:52 +00:00
#{factory_attributes.gsub(/^/, " ")}
end
RUBY
end
def single_file_factory_definition
<<-RUBY
FactoryBot.define do
2014-10-30 12:58:52 +00:00
#{factory_definition.chomp}
end
RUBY
end
def factory_attributes
attributes.map do |attribute|
"#{attribute.name} { #{attribute.default.inspect} }"
end.join("\n")
end
def filename
if factory_bot_options[:filename_proc].present?
factory_bot_options[:filename_proc].call(table_name)
else
[table_name, filename_suffix].compact.join('_')
end
end
def filename_suffix
factory_bot_options[:suffix] || options[:suffix]
end
def factory_bot_options
generators.options[:factory_bot] || {}
end
def generators
Allow reloading of factory definitions Closes #236 This commit uses ActiveSupport's FileUpdateChecker to allow reloading FactoryBot of definitions whenever a file in `FactoryBot.definition_file_paths` gets updated. This is similar to reloading for [I18n](https://github.com/rails/rails/blob/ced104d57997c7bceef3d1e6c8a713431363c3bb/activesupport/lib/active_support/i18n_railtie.rb#L60-L70) and [react rails](https://github.com/reactjs/react-rails/blob/83b6175460b6fd19d667854ebea4777d8c73705a/lib/react/rails/railtie.rb#L35-L41) This allows us to get rid of any Spring-specific logic in the railtie, since [Spring hooks into the application reloader](https://github.com/rails/spring/blob/0c711ff10b4ad7dcc34282b5e08573c2ce1e668a/lib/spring/application.rb#L161). This partly solves #211, since we no longer call `FactoryBot.reload` at all in `after_initialize`. Instead, we will only call it when one of the files in `definition_file_paths` gets updated. I say it partly solves #211 because once a definition file gets updated, reloading would still give warnings about redefining any constants in the definition files. I wonder how common it is to define constants in the same file as factory definitions. It's probably better to keep constants in the autoload path, allowing Rails to handle unloading and reloading them in development. I would want to see some specific examples before worrying too much about it. I would also like to offer a better way to configure the definition file paths (see #165 and #166) and possibly an option to opt out of loading definitions at all (could help with issues like #192). A couple of quirks here: * the to_prepare block could potentially get [called multiple times](https://github.com/rails/rails/issues/28108). It shouldn't matter, since `execute_if_updated` is a no-op if no updates have been made. * I noticed that the first time I call `reload!` in the console the factory definitions get reloaded even when I made no updates to the definition files. I think it is related to [this](https://github.com/rails/rails/blob/f7c5a8ce262cf16ad8d7c0b0d631a4b88afec414/activesupport/lib/active_support/evented_file_update_checker.rb#L18). After the first call `reload!` works as expected, only reloading the factory definitions if the definition files were updated. * Rails uses execute rather than execute_if_updated for the [route reloader](https://github.com/rails/rails/blob/master/railties/lib/rails/application/finisher.rb#L133) and for the [watchable file reloader](https://github.com/rails/rails/blob/master/railties/lib/rails/application/finisher.rb#L173). This means that changes to factory definitions will cause reloading of the whole application. I think this is fine.
2018-07-16 22:31:25 +00:00
config = FactoryBotRails::Railtie.config
config.respond_to?(:app_generators) ? config.app_generators : config.generators
end
end
end
end