2017-10-20 15:20:28 -04:00
|
|
|
module FactoryBot
|
2012-05-05 02:31:31 -04:00
|
|
|
# @api private
|
2011-10-28 17:01:27 -04:00
|
|
|
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
|
2011-10-28 17:01:27 -04:00
|
|
|
|
2018-08-04 10:06:57 -04:00
|
|
|
def initialize(name, base_traits = [])
|
2020-06-05 15:15:18 -04:00
|
|
|
@name = name
|
|
|
|
@declarations = DeclarationList.new(name)
|
|
|
|
@callbacks = []
|
|
|
|
@defined_traits = Set.new
|
|
|
|
@registered_enums = []
|
|
|
|
@to_create = nil
|
|
|
|
@base_traits = base_traits
|
2012-01-17 23:15:41 -05:00
|
|
|
@additional_traits = []
|
2020-06-05 15:15:18 -04:00
|
|
|
@constructor = nil
|
|
|
|
@attributes = nil
|
|
|
|
@compiled = false
|
2011-10-28 17:01:27 -04:00
|
|
|
end
|
|
|
|
|
2012-03-09 17:20:38 -05:00
|
|
|
delegate :declare_attribute, to: :declarations
|
2011-10-30 15:45:00 -04:00
|
|
|
|
|
|
|
def attributes
|
2012-06-12 23:11:55 -04:00
|
|
|
@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
|
2011-10-30 15:45:00 -04:00
|
|
|
end
|
|
|
|
|
2012-06-12 23:11:55 -04:00
|
|
|
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 }
|
2011-10-30 15:45:00 -04:00
|
|
|
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)
|
2012-06-12 23:11:55 -04:00
|
|
|
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?
|
|
|
|
|
2012-06-12 23:11:55 -04:00
|
|
|
declarations.attributes
|
|
|
|
|
|
|
|
defined_traits.each do |defined_trait|
|
2020-06-05 15:15:18 -04:00
|
|
|
base_traits.each { |bt| bt.define_trait defined_trait }
|
2019-10-25 13:03:09 -04:00
|
|
|
additional_traits.each { |at| at.define_trait defined_trait }
|
2012-06-12 23:11:55 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
@compiled = true
|
|
|
|
end
|
2012-01-17 23:15:41 -05:00
|
|
|
end
|
|
|
|
|
2011-10-30 15:45:00 -04:00
|
|
|
def overridable
|
|
|
|
declarations.overridable
|
|
|
|
self
|
|
|
|
end
|
2011-10-28 17:01:27 -04:00
|
|
|
|
2011-10-28 23:01:50 -04:00
|
|
|
def inherit_traits(new_traits)
|
2012-05-06 16:56:37 -04:00
|
|
|
@base_traits += new_traits
|
|
|
|
end
|
|
|
|
|
|
|
|
def append_traits(new_traits)
|
2012-01-17 23:15:41 -05:00
|
|
|
@additional_traits += new_traits
|
2011-10-28 23:01:50 -04:00
|
|
|
end
|
|
|
|
|
2011-10-28 17:01:27 -04:00
|
|
|
def add_callback(callback)
|
|
|
|
@callbacks << callback
|
|
|
|
end
|
|
|
|
|
2012-05-12 00:42:44 -04:00
|
|
|
def skip_create
|
2018-09-27 21:35:05 -04:00
|
|
|
@to_create = ->(instance) {}
|
2012-05-12 00:42:44 -04:00
|
|
|
end
|
|
|
|
|
2011-10-28 17:01:27 -04:00
|
|
|
def define_trait(trait)
|
2013-12-11 08:53:05 -05:00
|
|
|
@defined_traits.add(trait)
|
2011-10-28 17:01:27 -04:00
|
|
|
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
|
|
|
|
|
2012-01-20 13:04:48 -05:00
|
|
|
def define_constructor(&block)
|
|
|
|
@constructor = block
|
|
|
|
end
|
|
|
|
|
2013-02-08 11:00:22 -05:00
|
|
|
def before(*names, &block)
|
2013-12-14 22:33:15 -05:00
|
|
|
callback(*names.map { |name| "before_#{name}" }, &block)
|
2013-02-08 11:00:22 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def after(*names, &block)
|
2013-12-14 22:33:15 -05:00
|
|
|
callback(*names.map { |name| "after_#{name}" }, &block)
|
2013-02-08 11:00:22 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def callback(*names, &block)
|
|
|
|
names.each do |name|
|
|
|
|
add_callback(Callback.new(name, block))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-10-28 23:01:50 -04:00
|
|
|
private
|
|
|
|
|
2012-01-17 23:15:41 -05:00
|
|
|
def base_traits
|
|
|
|
@base_traits.map { |name| trait_by_name(name) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def additional_traits
|
|
|
|
@additional_traits.map { |name| trait_by_name(name) }
|
|
|
|
end
|
|
|
|
|
2011-10-28 17:01:27 -04:00
|
|
|
def trait_by_name(name)
|
2019-04-19 16:01:32 -04:00
|
|
|
trait_for(name) || Internal.trait_by_name(name)
|
2011-10-28 17:01:27 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def trait_for(name)
|
2019-09-30 23:53:04 -04:00
|
|
|
@defined_traits_by_name ||= defined_traits.each_with_object({}) { |t, memo| memo[t.name] ||= t }
|
|
|
|
@defined_traits_by_name[name.to_s]
|
2011-10-28 17:01:27 -04:00
|
|
|
end
|
2012-06-12 23:11:55 -04:00
|
|
|
|
|
|
|
def initialize_copy(source)
|
|
|
|
super
|
|
|
|
@attributes = nil
|
2020-06-05 15:15:18 -04:00
|
|
|
@compiled = false
|
2019-09-30 23:53:04 -04:00
|
|
|
@defined_traits_by_name = nil
|
2012-06-12 23:11:55 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def aggregate_from_traits_and_self(method_name, &block)
|
|
|
|
compile
|
|
|
|
|
2016-04-02 15:42:40 -04:00
|
|
|
[
|
|
|
|
base_traits.map(&method_name),
|
|
|
|
instance_exec(&block),
|
2020-06-05 15:15:18 -04:00
|
|
|
additional_traits.map(&method_name)
|
2016-04-02 15:42:40 -04:00
|
|
|
].flatten.compact
|
2012-06-12 23:11:55 -04:00
|
|
|
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
|
2011-10-28 17:01:27 -04:00
|
|
|
end
|
|
|
|
end
|