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

166 lines
3.8 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
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_given?
@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) }
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)
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
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