1
0
Fork 0
mirror of https://github.com/thoughtbot/factory_bot.git synced 2022-11-09 11:43:51 -05:00
thoughtbot--factory_bot/lib/factory_bot/definition.rb

185 lines
4.3 KiB
Ruby
Raw Normal View History

module FactoryBot
2012-05-05 02:31:31 -04:00
# @api private
class Definition
Add functionality for enum traits (#1380) ## Enum traits Given a Rails model with an enum attribute: ```rb class Task < ActiveRecord::Base enum status: {queued: 0, started: 1, finished: 2} end ``` It is common for people to define traits for each possible value of the enum: ```rb FactoryBot.define do factory :task do trait :queued do status { :queued } end trait :started do status { :started } end trait :finished do status { :finished } end end end ``` With this commit, those trait definitions are no longer necessary—they are defined automatically by factory_bot. If automatically defining traits for enum attributes on every factory is not desired, it is possible to disable the feature by setting `FactoryBot.automatically_define_enum_traits = false` (see commit: [Allow opting out of automatically defining traits](https://github.com/thoughtbot/factory_bot/pull/1380/commits/5a20017351b08ce2ec9918d799e187e9eaa3ec32)). In that case, it is still possible to explicitly define traits for an enum attribute in a particular factory: ```rb FactoryBot.automatically_define_enum_traits = false FactoryBot.define do factory :task do traits_for_enum(:status) end end ``` It is also possible to use this feature for other enumerable values, not specifically tied to ActiveRecord enum attributes: ```rb class Task attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum(:status, ["queued", "started", "finished"]) end end ``` The second argument here can be an enumerable object, including a Hash or Array. Closes #1049 Co-authored-by: Lance Johnson <lancejjohnson@gmail.com> Co-authored-by: PoTa <pota@mosfet.hu> Co-authored-by: Frida Casas <fridacasas.fc@gmail.com> Co-authored-by: Daniel Colson <danieljamescolson@gmail.com>
2020-05-01 17:50:51 -04:00
attr_reader :defined_traits, :declarations, :name, :registered_enums
def initialize(name, base_traits = [])
@name = name
@declarations = DeclarationList.new(name)
@callbacks = []
@defined_traits = Set.new
@registered_enums = []
@to_create = nil
@base_traits = base_traits
@additional_traits = []
@constructor = nil
@attributes = nil
@compiled = false
Ensure enum traits only get expanded once Fixes #1411 Before this commit, anybody with Active Record enum attributes using factory_bot 6.0 would have seen an increase in memory every time they used any trait. This behavior can be seen with the following benchmark: ```rb require "bundler/inline" gemfile(true) do source "https://rubygems.org" git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem "factory_bot", "~> 6.0.1" gem "activerecord" gem "sqlite3" end require "active_record" require "factory_bot" require "logger" ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :posts, force: true do |t| t.integer :status end end class Post < ActiveRecord::Base enum status: ("a".."z").to_a end FactoryBot.define do factory :post do trait :x end end require "benchmark" TIMES = 50 FactoryBot.build(:post) Benchmark.bm do |bm| bm.report("false, false") do FactoryBot.automatically_define_enum_traits = false TIMES.times do FactoryBot.build(:post) end end bm.report("true, false") do FactoryBot.automatically_define_enum_traits = true TIMES.times do FactoryBot.build(:post) end end bm.report("false, true") do FactoryBot.automatically_define_enum_traits = false TIMES.times do FactoryBot.build(:post, :x) end end bm.report("true, true") do FactoryBot.automatically_define_enum_traits = true TIMES.times do FactoryBot.build(:post, :x) end end end ``` The first three cases work as expected, but in the last case, with ` FactoryBot.automatically_define_enum_traits = true` and the `:x` trait passed in at run time, the behavior before this commit was as follows: 1. When passing in traits, the [factory runner] calls [factory.with_traits], which clones the factory and then applies the traits to the clone. 2. Cloning the factory also clones the factory's definition 3. The cloned definition retains some of the state of the original definition, most notably [`@defined_traits` and `@registered_enums`][shared state], but it sets `@compiled` back to false. 4. When the [definition recompiles], it [re-registers enums] into `@register_enums`, duplicating what is already there. For each registered enum, it [adds the relevant traits] to @defined_traits, again duplicating what was already there. 5. This gets worse and worse every time a trait gets used, increasing the size of `@defined_traits` exponentially until the program grids to a halt. With this commit, we keep an additional piece of state to ensure we only register and apply enum traits in the original definition. When any cloned definitions recompile, they will skip these steps. [factory runner]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/factory_runner.rb#L16-L18 [factory.with_traits]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/factory.rb#L93-L97 [shared state]: https://github.com/thoughtbot/factory_bot/blob/master/lib/factory_bot/definition.rb#L47-L4://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/definition.rb#L10-L11 [definition recompiles]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/definition.rb#L47-L49 [re-registers enums]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/definition.rb#L146-L148 [adds the relevant traits]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/definition.rb#L150-L153
2020-06-19 23:34:21 -04:00
@expanded_enum_traits = false
end
2012-03-09 17:20:38 -05:00
delegate :declare_attribute, to: :declarations
def attributes
@attributes ||= AttributeList.new.tap do |attribute_list|
attribute_lists = aggregate_from_traits_and_self(:attributes) { declarations.attributes }
attribute_lists.each do |attributes|
attribute_list.apply_attributes attributes
end
end
end
def to_create(&block)
if block
@to_create = block
else
aggregate_from_traits_and_self(:to_create) { @to_create }.last
end
end
def constructor
aggregate_from_traits_and_self(:constructor) { @constructor }.last
end
def callbacks
aggregate_from_traits_and_self(:callbacks) { @callbacks }
end
Add functionality for enum traits (#1380) ## Enum traits Given a Rails model with an enum attribute: ```rb class Task < ActiveRecord::Base enum status: {queued: 0, started: 1, finished: 2} end ``` It is common for people to define traits for each possible value of the enum: ```rb FactoryBot.define do factory :task do trait :queued do status { :queued } end trait :started do status { :started } end trait :finished do status { :finished } end end end ``` With this commit, those trait definitions are no longer necessary—they are defined automatically by factory_bot. If automatically defining traits for enum attributes on every factory is not desired, it is possible to disable the feature by setting `FactoryBot.automatically_define_enum_traits = false` (see commit: [Allow opting out of automatically defining traits](https://github.com/thoughtbot/factory_bot/pull/1380/commits/5a20017351b08ce2ec9918d799e187e9eaa3ec32)). In that case, it is still possible to explicitly define traits for an enum attribute in a particular factory: ```rb FactoryBot.automatically_define_enum_traits = false FactoryBot.define do factory :task do traits_for_enum(:status) end end ``` It is also possible to use this feature for other enumerable values, not specifically tied to ActiveRecord enum attributes: ```rb class Task attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum(:status, ["queued", "started", "finished"]) end end ``` The second argument here can be an enumerable object, including a Hash or Array. Closes #1049 Co-authored-by: Lance Johnson <lancejjohnson@gmail.com> Co-authored-by: PoTa <pota@mosfet.hu> Co-authored-by: Frida Casas <fridacasas.fc@gmail.com> Co-authored-by: Daniel Colson <danieljamescolson@gmail.com>
2020-05-01 17:50:51 -04:00
def compile(klass = nil)
unless @compiled
Add functionality for enum traits (#1380) ## Enum traits Given a Rails model with an enum attribute: ```rb class Task < ActiveRecord::Base enum status: {queued: 0, started: 1, finished: 2} end ``` It is common for people to define traits for each possible value of the enum: ```rb FactoryBot.define do factory :task do trait :queued do status { :queued } end trait :started do status { :started } end trait :finished do status { :finished } end end end ``` With this commit, those trait definitions are no longer necessary—they are defined automatically by factory_bot. If automatically defining traits for enum attributes on every factory is not desired, it is possible to disable the feature by setting `FactoryBot.automatically_define_enum_traits = false` (see commit: [Allow opting out of automatically defining traits](https://github.com/thoughtbot/factory_bot/pull/1380/commits/5a20017351b08ce2ec9918d799e187e9eaa3ec32)). In that case, it is still possible to explicitly define traits for an enum attribute in a particular factory: ```rb FactoryBot.automatically_define_enum_traits = false FactoryBot.define do factory :task do traits_for_enum(:status) end end ``` It is also possible to use this feature for other enumerable values, not specifically tied to ActiveRecord enum attributes: ```rb class Task attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum(:status, ["queued", "started", "finished"]) end end ``` The second argument here can be an enumerable object, including a Hash or Array. Closes #1049 Co-authored-by: Lance Johnson <lancejjohnson@gmail.com> Co-authored-by: PoTa <pota@mosfet.hu> Co-authored-by: Frida Casas <fridacasas.fc@gmail.com> Co-authored-by: Daniel Colson <danieljamescolson@gmail.com>
2020-05-01 17:50:51 -04:00
expand_enum_traits(klass) unless klass.nil?
declarations.attributes
defined_traits.each do |defined_trait|
base_traits.each { |bt| bt.define_trait defined_trait }
additional_traits.each { |at| at.define_trait defined_trait }
end
@compiled = true
end
end
def overridable
declarations.overridable
self
end
def inherit_traits(new_traits)
@base_traits += new_traits
end
def append_traits(new_traits)
@additional_traits += new_traits
end
def add_callback(callback)
@callbacks << callback
end
def skip_create
@to_create = ->(instance) {}
end
def define_trait(trait)
@defined_traits.add(trait)
end
Add functionality for enum traits (#1380) ## Enum traits Given a Rails model with an enum attribute: ```rb class Task < ActiveRecord::Base enum status: {queued: 0, started: 1, finished: 2} end ``` It is common for people to define traits for each possible value of the enum: ```rb FactoryBot.define do factory :task do trait :queued do status { :queued } end trait :started do status { :started } end trait :finished do status { :finished } end end end ``` With this commit, those trait definitions are no longer necessary—they are defined automatically by factory_bot. If automatically defining traits for enum attributes on every factory is not desired, it is possible to disable the feature by setting `FactoryBot.automatically_define_enum_traits = false` (see commit: [Allow opting out of automatically defining traits](https://github.com/thoughtbot/factory_bot/pull/1380/commits/5a20017351b08ce2ec9918d799e187e9eaa3ec32)). In that case, it is still possible to explicitly define traits for an enum attribute in a particular factory: ```rb FactoryBot.automatically_define_enum_traits = false FactoryBot.define do factory :task do traits_for_enum(:status) end end ``` It is also possible to use this feature for other enumerable values, not specifically tied to ActiveRecord enum attributes: ```rb class Task attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum(:status, ["queued", "started", "finished"]) end end ``` The second argument here can be an enumerable object, including a Hash or Array. Closes #1049 Co-authored-by: Lance Johnson <lancejjohnson@gmail.com> Co-authored-by: PoTa <pota@mosfet.hu> Co-authored-by: Frida Casas <fridacasas.fc@gmail.com> Co-authored-by: Daniel Colson <danieljamescolson@gmail.com>
2020-05-01 17:50:51 -04:00
def register_enum(enum)
@registered_enums << enum
end
def define_constructor(&block)
@constructor = block
end
def before(*names, &block)
callback(*names.map { |name| "before_#{name}" }, &block)
end
def after(*names, &block)
callback(*names.map { |name| "after_#{name}" }, &block)
end
def callback(*names, &block)
names.each do |name|
add_callback(Callback.new(name, block))
end
end
private
def base_traits
@base_traits.map { |name| trait_by_name(name) }
rescue KeyError => error
raise error_with_definition_name(error)
end
def error_with_definition_name(error)
message = error.message
message.insert(
message.index("\nDid you mean?") || message.length,
" referenced within \"#{name}\" definition"
)
error.class.new(message).tap do |new_error|
new_error.set_backtrace(error.backtrace)
end
end
def additional_traits
@additional_traits.map { |name| trait_by_name(name) }
end
def trait_by_name(name)
trait_for(name) || Internal.trait_by_name(name)
end
def trait_for(name)
@defined_traits_by_name ||= defined_traits.each_with_object({}) { |t, memo| memo[t.name] ||= t }
@defined_traits_by_name[name.to_s]
end
def initialize_copy(source)
super
@attributes = nil
@compiled = false
@defined_traits_by_name = nil
end
def aggregate_from_traits_and_self(method_name, &block)
compile
[
base_traits.map(&method_name),
instance_exec(&block),
additional_traits.map(&method_name)
].flatten.compact
end
Add functionality for enum traits (#1380) ## Enum traits Given a Rails model with an enum attribute: ```rb class Task < ActiveRecord::Base enum status: {queued: 0, started: 1, finished: 2} end ``` It is common for people to define traits for each possible value of the enum: ```rb FactoryBot.define do factory :task do trait :queued do status { :queued } end trait :started do status { :started } end trait :finished do status { :finished } end end end ``` With this commit, those trait definitions are no longer necessary—they are defined automatically by factory_bot. If automatically defining traits for enum attributes on every factory is not desired, it is possible to disable the feature by setting `FactoryBot.automatically_define_enum_traits = false` (see commit: [Allow opting out of automatically defining traits](https://github.com/thoughtbot/factory_bot/pull/1380/commits/5a20017351b08ce2ec9918d799e187e9eaa3ec32)). In that case, it is still possible to explicitly define traits for an enum attribute in a particular factory: ```rb FactoryBot.automatically_define_enum_traits = false FactoryBot.define do factory :task do traits_for_enum(:status) end end ``` It is also possible to use this feature for other enumerable values, not specifically tied to ActiveRecord enum attributes: ```rb class Task attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum(:status, ["queued", "started", "finished"]) end end ``` The second argument here can be an enumerable object, including a Hash or Array. Closes #1049 Co-authored-by: Lance Johnson <lancejjohnson@gmail.com> Co-authored-by: PoTa <pota@mosfet.hu> Co-authored-by: Frida Casas <fridacasas.fc@gmail.com> Co-authored-by: Daniel Colson <danieljamescolson@gmail.com>
2020-05-01 17:50:51 -04:00
def expand_enum_traits(klass)
Ensure enum traits only get expanded once Fixes #1411 Before this commit, anybody with Active Record enum attributes using factory_bot 6.0 would have seen an increase in memory every time they used any trait. This behavior can be seen with the following benchmark: ```rb require "bundler/inline" gemfile(true) do source "https://rubygems.org" git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem "factory_bot", "~> 6.0.1" gem "activerecord" gem "sqlite3" end require "active_record" require "factory_bot" require "logger" ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :posts, force: true do |t| t.integer :status end end class Post < ActiveRecord::Base enum status: ("a".."z").to_a end FactoryBot.define do factory :post do trait :x end end require "benchmark" TIMES = 50 FactoryBot.build(:post) Benchmark.bm do |bm| bm.report("false, false") do FactoryBot.automatically_define_enum_traits = false TIMES.times do FactoryBot.build(:post) end end bm.report("true, false") do FactoryBot.automatically_define_enum_traits = true TIMES.times do FactoryBot.build(:post) end end bm.report("false, true") do FactoryBot.automatically_define_enum_traits = false TIMES.times do FactoryBot.build(:post, :x) end end bm.report("true, true") do FactoryBot.automatically_define_enum_traits = true TIMES.times do FactoryBot.build(:post, :x) end end end ``` The first three cases work as expected, but in the last case, with ` FactoryBot.automatically_define_enum_traits = true` and the `:x` trait passed in at run time, the behavior before this commit was as follows: 1. When passing in traits, the [factory runner] calls [factory.with_traits], which clones the factory and then applies the traits to the clone. 2. Cloning the factory also clones the factory's definition 3. The cloned definition retains some of the state of the original definition, most notably [`@defined_traits` and `@registered_enums`][shared state], but it sets `@compiled` back to false. 4. When the [definition recompiles], it [re-registers enums] into `@register_enums`, duplicating what is already there. For each registered enum, it [adds the relevant traits] to @defined_traits, again duplicating what was already there. 5. This gets worse and worse every time a trait gets used, increasing the size of `@defined_traits` exponentially until the program grids to a halt. With this commit, we keep an additional piece of state to ensure we only register and apply enum traits in the original definition. When any cloned definitions recompile, they will skip these steps. [factory runner]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/factory_runner.rb#L16-L18 [factory.with_traits]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/factory.rb#L93-L97 [shared state]: https://github.com/thoughtbot/factory_bot/blob/master/lib/factory_bot/definition.rb#L47-L4://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/definition.rb#L10-L11 [definition recompiles]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/definition.rb#L47-L49 [re-registers enums]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/definition.rb#L146-L148 [adds the relevant traits]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/definition.rb#L150-L153
2020-06-19 23:34:21 -04:00
return if @expanded_enum_traits
Add functionality for enum traits (#1380) ## Enum traits Given a Rails model with an enum attribute: ```rb class Task < ActiveRecord::Base enum status: {queued: 0, started: 1, finished: 2} end ``` It is common for people to define traits for each possible value of the enum: ```rb FactoryBot.define do factory :task do trait :queued do status { :queued } end trait :started do status { :started } end trait :finished do status { :finished } end end end ``` With this commit, those trait definitions are no longer necessary—they are defined automatically by factory_bot. If automatically defining traits for enum attributes on every factory is not desired, it is possible to disable the feature by setting `FactoryBot.automatically_define_enum_traits = false` (see commit: [Allow opting out of automatically defining traits](https://github.com/thoughtbot/factory_bot/pull/1380/commits/5a20017351b08ce2ec9918d799e187e9eaa3ec32)). In that case, it is still possible to explicitly define traits for an enum attribute in a particular factory: ```rb FactoryBot.automatically_define_enum_traits = false FactoryBot.define do factory :task do traits_for_enum(:status) end end ``` It is also possible to use this feature for other enumerable values, not specifically tied to ActiveRecord enum attributes: ```rb class Task attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum(:status, ["queued", "started", "finished"]) end end ``` The second argument here can be an enumerable object, including a Hash or Array. Closes #1049 Co-authored-by: Lance Johnson <lancejjohnson@gmail.com> Co-authored-by: PoTa <pota@mosfet.hu> Co-authored-by: Frida Casas <fridacasas.fc@gmail.com> Co-authored-by: Daniel Colson <danieljamescolson@gmail.com>
2020-05-01 17:50:51 -04:00
if automatically_register_defined_enums?(klass)
automatically_register_defined_enums(klass)
end
registered_enums.each do |enum|
traits = enum.build_traits(klass)
traits.each { |trait| define_trait(trait) }
end
Ensure enum traits only get expanded once Fixes #1411 Before this commit, anybody with Active Record enum attributes using factory_bot 6.0 would have seen an increase in memory every time they used any trait. This behavior can be seen with the following benchmark: ```rb require "bundler/inline" gemfile(true) do source "https://rubygems.org" git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem "factory_bot", "~> 6.0.1" gem "activerecord" gem "sqlite3" end require "active_record" require "factory_bot" require "logger" ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :posts, force: true do |t| t.integer :status end end class Post < ActiveRecord::Base enum status: ("a".."z").to_a end FactoryBot.define do factory :post do trait :x end end require "benchmark" TIMES = 50 FactoryBot.build(:post) Benchmark.bm do |bm| bm.report("false, false") do FactoryBot.automatically_define_enum_traits = false TIMES.times do FactoryBot.build(:post) end end bm.report("true, false") do FactoryBot.automatically_define_enum_traits = true TIMES.times do FactoryBot.build(:post) end end bm.report("false, true") do FactoryBot.automatically_define_enum_traits = false TIMES.times do FactoryBot.build(:post, :x) end end bm.report("true, true") do FactoryBot.automatically_define_enum_traits = true TIMES.times do FactoryBot.build(:post, :x) end end end ``` The first three cases work as expected, but in the last case, with ` FactoryBot.automatically_define_enum_traits = true` and the `:x` trait passed in at run time, the behavior before this commit was as follows: 1. When passing in traits, the [factory runner] calls [factory.with_traits], which clones the factory and then applies the traits to the clone. 2. Cloning the factory also clones the factory's definition 3. The cloned definition retains some of the state of the original definition, most notably [`@defined_traits` and `@registered_enums`][shared state], but it sets `@compiled` back to false. 4. When the [definition recompiles], it [re-registers enums] into `@register_enums`, duplicating what is already there. For each registered enum, it [adds the relevant traits] to @defined_traits, again duplicating what was already there. 5. This gets worse and worse every time a trait gets used, increasing the size of `@defined_traits` exponentially until the program grids to a halt. With this commit, we keep an additional piece of state to ensure we only register and apply enum traits in the original definition. When any cloned definitions recompile, they will skip these steps. [factory runner]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/factory_runner.rb#L16-L18 [factory.with_traits]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/factory.rb#L93-L97 [shared state]: https://github.com/thoughtbot/factory_bot/blob/master/lib/factory_bot/definition.rb#L47-L4://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/definition.rb#L10-L11 [definition recompiles]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/definition.rb#L47-L49 [re-registers enums]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/definition.rb#L146-L148 [adds the relevant traits]: https://github.com/thoughtbot/factory_bot/blob/0785796f0823528fd36d1c3e4a3df4511b9876e4/lib/factory_bot/definition.rb#L150-L153
2020-06-19 23:34:21 -04:00
@expanded_enum_traits = true
Add functionality for enum traits (#1380) ## Enum traits Given a Rails model with an enum attribute: ```rb class Task < ActiveRecord::Base enum status: {queued: 0, started: 1, finished: 2} end ``` It is common for people to define traits for each possible value of the enum: ```rb FactoryBot.define do factory :task do trait :queued do status { :queued } end trait :started do status { :started } end trait :finished do status { :finished } end end end ``` With this commit, those trait definitions are no longer necessary—they are defined automatically by factory_bot. If automatically defining traits for enum attributes on every factory is not desired, it is possible to disable the feature by setting `FactoryBot.automatically_define_enum_traits = false` (see commit: [Allow opting out of automatically defining traits](https://github.com/thoughtbot/factory_bot/pull/1380/commits/5a20017351b08ce2ec9918d799e187e9eaa3ec32)). In that case, it is still possible to explicitly define traits for an enum attribute in a particular factory: ```rb FactoryBot.automatically_define_enum_traits = false FactoryBot.define do factory :task do traits_for_enum(:status) end end ``` It is also possible to use this feature for other enumerable values, not specifically tied to ActiveRecord enum attributes: ```rb class Task attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum(:status, ["queued", "started", "finished"]) end end ``` The second argument here can be an enumerable object, including a Hash or Array. Closes #1049 Co-authored-by: Lance Johnson <lancejjohnson@gmail.com> Co-authored-by: PoTa <pota@mosfet.hu> Co-authored-by: Frida Casas <fridacasas.fc@gmail.com> Co-authored-by: Daniel Colson <danieljamescolson@gmail.com>
2020-05-01 17:50:51 -04:00
end
def automatically_register_defined_enums(klass)
klass.defined_enums.each_key { |name| register_enum(Enum.new(name)) }
end
def automatically_register_defined_enums?(klass)
FactoryBot.automatically_define_enum_traits &&
klass.respond_to?(:defined_enums)
end
end
end