factory_bot 4.10.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/CONTRIBUTING.md +52 -13
  4. data/GETTING_STARTED.md +613 -182
  5. data/LICENSE +1 -1
  6. data/NEWS.md +358 -0
  7. data/README.md +30 -26
  8. data/lib/factory_bot.rb +71 -140
  9. data/lib/factory_bot/aliases.rb +2 -2
  10. data/lib/factory_bot/attribute.rb +4 -39
  11. data/lib/factory_bot/attribute/association.rb +2 -2
  12. data/lib/factory_bot/attribute/dynamic.rb +2 -1
  13. data/lib/factory_bot/attribute_assigner.rb +24 -10
  14. data/lib/factory_bot/attribute_list.rb +3 -2
  15. data/lib/factory_bot/callback.rb +3 -10
  16. data/lib/factory_bot/configuration.rb +15 -19
  17. data/lib/factory_bot/declaration.rb +5 -5
  18. data/lib/factory_bot/declaration/association.rb +14 -1
  19. data/lib/factory_bot/declaration/dynamic.rb +3 -1
  20. data/lib/factory_bot/declaration/implicit.rb +7 -2
  21. data/lib/factory_bot/declaration_list.rb +3 -3
  22. data/lib/factory_bot/decorator.rb +5 -5
  23. data/lib/factory_bot/decorator/attribute_hash.rb +1 -1
  24. data/lib/factory_bot/decorator/invocation_tracker.rb +1 -1
  25. data/lib/factory_bot/definition.rb +49 -20
  26. data/lib/factory_bot/definition_hierarchy.rb +1 -11
  27. data/lib/factory_bot/definition_proxy.rb +125 -43
  28. data/lib/factory_bot/enum.rb +27 -0
  29. data/lib/factory_bot/errors.rb +7 -4
  30. data/lib/factory_bot/evaluation.rb +1 -1
  31. data/lib/factory_bot/evaluator.rb +9 -11
  32. data/lib/factory_bot/evaluator_class_definer.rb +1 -1
  33. data/lib/factory_bot/factory.rb +12 -12
  34. data/lib/factory_bot/factory_runner.rb +4 -4
  35. data/lib/factory_bot/find_definitions.rb +2 -2
  36. data/lib/factory_bot/internal.rb +91 -0
  37. data/lib/factory_bot/linter.rb +41 -28
  38. data/lib/factory_bot/null_factory.rb +13 -4
  39. data/lib/factory_bot/null_object.rb +2 -6
  40. data/lib/factory_bot/registry.rb +17 -8
  41. data/lib/factory_bot/reload.rb +2 -3
  42. data/lib/factory_bot/sequence.rb +5 -6
  43. data/lib/factory_bot/strategy/stub.rb +37 -37
  44. data/lib/factory_bot/strategy_calculator.rb +1 -1
  45. data/lib/factory_bot/strategy_syntax_method_registrar.rb +13 -2
  46. data/lib/factory_bot/syntax.rb +2 -2
  47. data/lib/factory_bot/syntax/default.rb +12 -24
  48. data/lib/factory_bot/syntax/methods.rb +32 -9
  49. data/lib/factory_bot/trait.rb +7 -4
  50. data/lib/factory_bot/version.rb +1 -1
  51. metadata +50 -65
  52. data/.autotest +0 -9
  53. data/.gitignore +0 -10
  54. data/.rspec +0 -3
  55. data/.simplecov +0 -4
  56. data/.travis.yml +0 -58
  57. data/Appraisals +0 -23
  58. data/Gemfile +0 -8
  59. data/Gemfile.lock +0 -103
  60. data/NEWS +0 -298
  61. data/Rakefile +0 -36
  62. data/cucumber.yml +0 -1
  63. data/factory_bot.gemspec +0 -36
  64. data/factory_girl.gemspec +0 -40
  65. data/gemfiles/3.2.gemfile +0 -10
  66. data/gemfiles/3.2.gemfile.lock +0 -107
  67. data/gemfiles/4.0.gemfile +0 -10
  68. data/gemfiles/4.0.gemfile.lock +0 -107
  69. data/gemfiles/4.1.gemfile +0 -10
  70. data/gemfiles/4.1.gemfile.lock +0 -106
  71. data/gemfiles/4.2.gemfile +0 -10
  72. data/gemfiles/4.2.gemfile.lock +0 -106
  73. data/gemfiles/5.0.gemfile +0 -10
  74. data/gemfiles/5.0.gemfile.lock +0 -104
  75. data/gemfiles/5.1.gemfile +0 -10
  76. data/gemfiles/5.1.gemfile.lock +0 -104
  77. data/lib/factory_bot/attribute/static.rb +0 -16
  78. data/lib/factory_bot/declaration/static.rb +0 -26
  79. data/lib/factory_bot/decorator/class_key_hash.rb +0 -28
  80. data/lib/factory_girl.rb +0 -5
@@ -1,21 +1,25 @@
1
1
  module FactoryBot
2
2
  # @api private
3
3
  class Configuration
4
- attr_reader :factories, :sequences, :traits, :strategies, :callback_names
5
-
6
- attr_accessor :allow_class_lookup, :use_parent_strategy
4
+ attr_reader(
5
+ :callback_names,
6
+ :factories,
7
+ :inline_sequences,
8
+ :sequences,
9
+ :strategies,
10
+ :traits
11
+ )
7
12
 
8
13
  def initialize
9
- @factories = Decorator::DisallowsDuplicatesRegistry.new(Registry.new('Factory'))
10
- @sequences = Decorator::DisallowsDuplicatesRegistry.new(Registry.new('Sequence'))
11
- @traits = Decorator::DisallowsDuplicatesRegistry.new(Registry.new('Trait'))
12
- @strategies = Registry.new('Strategy')
14
+ @factories = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Factory"))
15
+ @sequences = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Sequence"))
16
+ @traits = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Trait"))
17
+ @strategies = Registry.new("Strategy")
13
18
  @callback_names = Set.new
14
- @definition = Definition.new
15
-
16
- @allow_class_lookup = true
19
+ @definition = Definition.new(:configuration)
20
+ @inline_sequences = []
17
21
 
18
- to_create { |instance| instance.save! }
22
+ to_create(&:save!)
19
23
  initialize_with { new }
20
24
  end
21
25
 
@@ -25,13 +29,5 @@ module FactoryBot
25
29
  def initialize_with(&block)
26
30
  @definition.define_constructor(&block)
27
31
  end
28
-
29
- def duplicate_attribute_assignment_from_initialize_with
30
- false
31
- end
32
-
33
- def duplicate_attribute_assignment_from_initialize_with=(value)
34
- ActiveSupport::Deprecation.warn 'Assignment of duplicate_attribute_assignment_from_initialize_with is unnecessary as this is now default behavior in FactoryBot 4.0; this line can be removed', caller
35
- end
36
32
  end
37
33
  end
@@ -1,7 +1,6 @@
1
- require 'factory_bot/declaration/static'
2
- require 'factory_bot/declaration/dynamic'
3
- require 'factory_bot/declaration/association'
4
- require 'factory_bot/declaration/implicit'
1
+ require "factory_bot/declaration/dynamic"
2
+ require "factory_bot/declaration/association"
3
+ require "factory_bot/declaration/implicit"
5
4
 
6
5
  module FactoryBot
7
6
  # @api private
@@ -9,7 +8,7 @@ module FactoryBot
9
8
  attr_reader :name
10
9
 
11
10
  def initialize(name, ignored = false)
12
- @name = name
11
+ @name = name
13
12
  @ignored = ignored
14
13
  end
15
14
 
@@ -18,6 +17,7 @@ module FactoryBot
18
17
  end
19
18
 
20
19
  protected
20
+
21
21
  attr_reader :ignored
22
22
  end
23
23
  end
@@ -10,19 +10,32 @@ module FactoryBot
10
10
  end
11
11
 
12
12
  def ==(other)
13
- name == other.name &&
13
+ self.class == other.class &&
14
+ name == other.name &&
14
15
  options == other.options
15
16
  end
16
17
 
17
18
  protected
19
+
18
20
  attr_reader :options
19
21
 
20
22
  private
21
23
 
22
24
  def build
25
+ ensure_factory_is_not_a_declaration!
26
+
23
27
  factory_name = @overrides[:factory] || name
24
28
  [Attribute::Association.new(name, factory_name, [@traits, @overrides.except(:factory)].flatten)]
25
29
  end
30
+
31
+ def ensure_factory_is_not_a_declaration!
32
+ if @overrides[:factory].is_a?(Declaration)
33
+ raise ArgumentError.new(<<~MSG)
34
+ Association '#{name}' received an invalid factory argument.
35
+ Did you mean? 'factory: :#{@overrides[:factory].name}'
36
+ MSG
37
+ end
38
+ end
26
39
  end
27
40
  end
28
41
  end
@@ -8,12 +8,14 @@ module FactoryBot
8
8
  end
9
9
 
10
10
  def ==(other)
11
- name == other.name &&
11
+ self.class == other.class &&
12
+ name == other.name &&
12
13
  ignored == other.ignored &&
13
14
  block == other.block
14
15
  end
15
16
 
16
17
  protected
18
+
17
19
  attr_reader :block
18
20
 
19
21
  private
@@ -8,12 +8,14 @@ module FactoryBot
8
8
  end
9
9
 
10
10
  def ==(other)
11
- name == other.name &&
11
+ self.class == other.class &&
12
+ name == other.name &&
12
13
  factory == other.factory &&
13
14
  ignored == other.ignored
14
15
  end
15
16
 
16
17
  protected
18
+
17
19
  attr_reader :factory
18
20
 
19
21
  private
@@ -21,8 +23,11 @@ module FactoryBot
21
23
  def build
22
24
  if FactoryBot.factories.registered?(name)
23
25
  [Attribute::Association.new(name, name, {})]
24
- elsif FactoryBot.sequences.registered?(name)
26
+ elsif FactoryBot::Internal.sequences.registered?(name)
25
27
  [Attribute::Sequence.new(name, name, @ignored)]
28
+ elsif @factory.name.to_s == name.to_s
29
+ message = "Self-referencing trait '#{@name}'"
30
+ raise TraitDefinitionError, message
26
31
  else
27
32
  @factory.inherit_traits([name])
28
33
  []
@@ -5,8 +5,8 @@ module FactoryBot
5
5
 
6
6
  def initialize(name = nil)
7
7
  @declarations = []
8
- @name = name
9
- @overridable = false
8
+ @name = name
9
+ @overridable = false
10
10
  end
11
11
 
12
12
  def declare_attribute(declaration)
@@ -39,7 +39,7 @@ module FactoryBot
39
39
  end
40
40
 
41
41
  def to_attributes
42
- @declarations.inject([]) { |result, declaration| result += declaration.to_attributes }
42
+ @declarations.reduce([]) { |result, declaration| result + declaration.to_attributes }
43
43
  end
44
44
 
45
45
  def overridable?
@@ -6,16 +6,16 @@ module FactoryBot
6
6
  @component = component
7
7
  end
8
8
 
9
- def method_missing(name, *args, &block)
9
+ def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissingSuper
10
10
  @component.send(name, *args, &block)
11
11
  end
12
12
 
13
- def send(symbol, *args, &block)
14
- __send__(symbol, *args, &block)
13
+ def respond_to_missing?(name, include_private = false)
14
+ @component.respond_to?(name, true) || super
15
15
  end
16
16
 
17
- def self.const_missing(name)
18
- ::Object.const_get(name)
17
+ def send(symbol, *args, &block)
18
+ __send__(symbol, *args, &block)
19
19
  end
20
20
  end
21
21
  end
@@ -8,7 +8,7 @@ module FactoryBot
8
8
 
9
9
  def attributes
10
10
  @attributes.each_with_object({}) do |attribute_name, result|
11
- result[attribute_name] = send(attribute_name)
11
+ result[attribute_name] = @component.send(attribute_name)
12
12
  end
13
13
  end
14
14
  end
@@ -6,7 +6,7 @@ module FactoryBot
6
6
  @invoked_methods = []
7
7
  end
8
8
 
9
- def method_missing(name, *args, &block)
9
+ def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing
10
10
  @invoked_methods << name
11
11
  super
12
12
  end
@@ -1,18 +1,20 @@
1
1
  module FactoryBot
2
2
  # @api private
3
3
  class Definition
4
- attr_reader :defined_traits, :declarations
5
-
6
- def initialize(name = nil, base_traits = [])
7
- @declarations = DeclarationList.new(name)
8
- @callbacks = []
9
- @defined_traits = Set.new
10
- @to_create = nil
11
- @base_traits = base_traits
4
+ attr_reader :defined_traits, :declarations, :name, :registered_enums
5
+
6
+ def initialize(name, base_traits = [])
7
+ @name = name
8
+ @declarations = DeclarationList.new(name)
9
+ @callbacks = []
10
+ @defined_traits = Set.new
11
+ @registered_enums = []
12
+ @to_create = nil
13
+ @base_traits = base_traits
12
14
  @additional_traits = []
13
- @constructor = nil
14
- @attributes = nil
15
- @compiled = false
15
+ @constructor = nil
16
+ @attributes = nil
17
+ @compiled = false
16
18
  end
17
19
 
18
20
  delegate :declare_attribute, to: :declarations
@@ -42,13 +44,15 @@ module FactoryBot
42
44
  aggregate_from_traits_and_self(:callbacks) { @callbacks }
43
45
  end
44
46
 
45
- def compile
47
+ def compile(klass = nil)
46
48
  unless @compiled
49
+ expand_enum_traits(klass) unless klass.nil?
50
+
47
51
  declarations.attributes
48
52
 
49
53
  defined_traits.each do |defined_trait|
50
- base_traits.each { |bt| bt.define_trait defined_trait }
51
- additional_traits.each { |bt| bt.define_trait defined_trait }
54
+ base_traits.each { |bt| bt.define_trait defined_trait }
55
+ additional_traits.each { |at| at.define_trait defined_trait }
52
56
  end
53
57
 
54
58
  @compiled = true
@@ -73,13 +77,17 @@ module FactoryBot
73
77
  end
74
78
 
75
79
  def skip_create
76
- @to_create = ->(instance) { }
80
+ @to_create = ->(instance) {}
77
81
  end
78
82
 
79
83
  def define_trait(trait)
80
84
  @defined_traits.add(trait)
81
85
  end
82
86
 
87
+ def register_enum(enum)
88
+ @registered_enums << enum
89
+ end
90
+
83
91
  def define_constructor(&block)
84
92
  @constructor = block
85
93
  end
@@ -94,7 +102,6 @@ module FactoryBot
94
102
 
95
103
  def callback(*names, &block)
96
104
  names.each do |name|
97
- FactoryBot.register_callback(name)
98
105
  add_callback(Callback.new(name, block))
99
106
  end
100
107
  end
@@ -110,17 +117,19 @@ module FactoryBot
110
117
  end
111
118
 
112
119
  def trait_by_name(name)
113
- trait_for(name) || FactoryBot.trait_by_name(name)
120
+ trait_for(name) || Internal.trait_by_name(name)
114
121
  end
115
122
 
116
123
  def trait_for(name)
117
- defined_traits.detect { |trait| trait.name == name }
124
+ @defined_traits_by_name ||= defined_traits.each_with_object({}) { |t, memo| memo[t.name] ||= t }
125
+ @defined_traits_by_name[name.to_s]
118
126
  end
119
127
 
120
128
  def initialize_copy(source)
121
129
  super
122
130
  @attributes = nil
123
- @compiled = false
131
+ @compiled = false
132
+ @defined_traits_by_name = nil
124
133
  end
125
134
 
126
135
  def aggregate_from_traits_and_self(method_name, &block)
@@ -129,8 +138,28 @@ module FactoryBot
129
138
  [
130
139
  base_traits.map(&method_name),
131
140
  instance_exec(&block),
132
- additional_traits.map(&method_name),
141
+ additional_traits.map(&method_name)
133
142
  ].flatten.compact
134
143
  end
144
+
145
+ def expand_enum_traits(klass)
146
+ if automatically_register_defined_enums?(klass)
147
+ automatically_register_defined_enums(klass)
148
+ end
149
+
150
+ registered_enums.each do |enum|
151
+ traits = enum.build_traits(klass)
152
+ traits.each { |trait| define_trait(trait) }
153
+ end
154
+ end
155
+
156
+ def automatically_register_defined_enums(klass)
157
+ klass.defined_enums.each_key { |name| register_enum(Enum.new(name)) }
158
+ end
159
+
160
+ def automatically_register_defined_enums?(klass)
161
+ FactoryBot.automatically_define_enum_traits &&
162
+ klass.respond_to?(:defined_enums)
163
+ end
135
164
  end
136
165
  end
@@ -1,16 +1,6 @@
1
1
  module FactoryBot
2
2
  class DefinitionHierarchy
3
- def callbacks
4
- FactoryBot.callbacks
5
- end
6
-
7
- def constructor
8
- FactoryBot.constructor
9
- end
10
-
11
- def to_create
12
- FactoryBot.to_create
13
- end
3
+ delegate :callbacks, :constructor, :to_create, to: Internal
14
4
 
15
5
  def self.build_from_definition(definition)
16
6
  build_to_create(&definition.to_create)
@@ -1,6 +1,19 @@
1
1
  module FactoryBot
2
2
  class DefinitionProxy
3
- UNPROXIED_METHODS = %w(__send__ __id__ nil? send object_id extend instance_eval initialize block_given? raise caller method)
3
+ UNPROXIED_METHODS = %w[
4
+ __send__
5
+ __id__
6
+ nil?
7
+ send
8
+ object_id
9
+ extend
10
+ instance_eval
11
+ initialize
12
+ block_given?
13
+ raise
14
+ caller
15
+ method
16
+ ].freeze
4
17
 
5
18
  (instance_methods + private_instance_methods).each do |method|
6
19
  undef_method(method) unless UNPROXIED_METHODS.include?(method.to_s)
@@ -11,8 +24,8 @@ module FactoryBot
11
24
  attr_reader :child_factories
12
25
 
13
26
  def initialize(definition, ignore = false)
14
- @definition = definition
15
- @ignore = ignore
27
+ @definition = definition
28
+ @ignore = ignore
16
29
  @child_factories = []
17
30
  end
18
31
 
@@ -21,42 +34,21 @@ module FactoryBot
21
34
  raise FactoryBot::MethodDefinitionError, message
22
35
  end
23
36
 
24
- # Adds an attribute that should be assigned on generated instances for this
25
- # factory.
26
- #
27
- # This method should be called with either a value or block, but not both. If
28
- # called with a block, the attribute will be generated "lazily," whenever an
29
- # instance is generated. Lazy attribute blocks will not be called if that
37
+ # Adds an attribute to the factory.
38
+ # The attribute value will be generated "lazily"
39
+ # by calling the block whenever an instance is generated.
40
+ # The block will not be called if the
30
41
  # attribute is overridden for a specific instance.
31
42
  #
32
- # When defining lazy attributes, an instance of FactoryBot::Strategy will
33
- # be yielded, allowing associations to be built using the correct build
34
- # strategy.
35
- #
36
43
  # Arguments:
37
44
  # * name: +Symbol+ or +String+
38
45
  # The name of this attribute. This will be assigned using "name=" for
39
46
  # generated instances.
40
- # * value: +Object+
41
- # If no block is given, this value will be used for this attribute.
42
- def add_attribute(name, value = nil, &block)
43
- raise AttributeDefinitionError, 'Both value and block given' if value && block_given?
44
-
45
- declaration = if block_given?
46
- Declaration::Dynamic.new(name, @ignore, block)
47
- else
48
- Declaration::Static.new(name, value, @ignore)
49
- end
50
-
47
+ def add_attribute(name, &block)
48
+ declaration = Declaration::Dynamic.new(name, @ignore, block)
51
49
  @definition.declare_attribute(declaration)
52
50
  end
53
51
 
54
- def ignore(&block)
55
- ActiveSupport::Deprecation.warn "`#ignore` is deprecated and will be "\
56
- "removed in 5.0. Please use `#transient` instead."
57
- transient(&block)
58
- end
59
-
60
52
  def transient(&block)
61
53
  proxy = DefinitionProxy.new(@definition, true)
62
54
  proxy.instance_eval(&block)
@@ -66,40 +58,48 @@ module FactoryBot
66
58
  # attribute, so that:
67
59
  #
68
60
  # factory :user do
69
- # name 'Billy Idol'
61
+ # name { 'Billy Idol' }
70
62
  # end
71
63
  #
72
64
  # and:
73
65
  #
74
66
  # factory :user do
75
- # add_attribute :name, 'Billy Idol'
67
+ # add_attribute(:name) { 'Billy Idol' }
76
68
  # end
77
69
  #
78
70
  # are equivalent.
79
71
  #
80
- # If no argument or block is given, factory_bot will look for a sequence
81
- # or association with the same name. This means that:
72
+ # If no argument or block is given, factory_bot will first look for an
73
+ # association, then for a sequence, and finally for a trait with the same
74
+ # name. This means that given an "admin" trait, an "email" sequence, and an
75
+ # "account" factory:
82
76
  #
83
- # factory :user do
84
- # email { create(:email) }
77
+ # factory :user, traits: [:admin] do
78
+ # email { generate(:email) }
85
79
  # association :account
86
80
  # end
87
81
  #
88
82
  # and:
89
83
  #
90
84
  # factory :user do
85
+ # admin
91
86
  # email
92
87
  # account
93
88
  # end
94
89
  #
95
90
  # are equivalent.
96
- def method_missing(name, *args, &block)
97
- if args.empty? && block.nil?
98
- @definition.declare_attribute(Declaration::Implicit.new(name, @definition, @ignore))
99
- elsif args.first.respond_to?(:has_key?) && args.first.has_key?(:factory)
100
- association(name, *args)
91
+ def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing, Style/MethodMissingSuper
92
+ association_options = args.first
93
+
94
+ if association_options.nil?
95
+ __declare_attribute__(name, block)
96
+ elsif __valid_association_options?(association_options)
97
+ association(name, association_options)
101
98
  else
102
- add_attribute(name, *args, &block)
99
+ raise NoMethodError.new(<<~MSG)
100
+ undefined method '#{name}' in '#{@definition.name}' factory
101
+ Did you mean? '#{name} { #{association_options.inspect} }'
102
+ MSG
103
103
  end
104
104
  end
105
105
 
@@ -121,6 +121,7 @@ module FactoryBot
121
121
  # Except that no globally available sequence will be defined.
122
122
  def sequence(name, *args, &block)
123
123
  sequence = Sequence.new(name, *args, &block)
124
+ FactoryBot::Internal.register_inline_sequence(sequence)
124
125
  add_attribute(name) { increment_sequence(sequence) }
125
126
  end
126
127
 
@@ -148,7 +149,15 @@ module FactoryBot
148
149
  # name of the factory. For example, a "user" association will by
149
150
  # default use the "user" factory.
150
151
  def association(name, *options)
151
- @definition.declare_attribute(Declaration::Association.new(name, *options))
152
+ if block_given?
153
+ raise AssociationDefinitionError.new(
154
+ "Unexpected block passed to '#{name}' association "\
155
+ "in '#{@definition.name}' factory"
156
+ )
157
+ else
158
+ declaration = Declaration::Association.new(name, *options)
159
+ @definition.declare_attribute(declaration)
160
+ end
152
161
  end
153
162
 
154
163
  def to_create(&block)
@@ -167,8 +176,81 @@ module FactoryBot
167
176
  @definition.define_trait(Trait.new(name, &block))
168
177
  end
169
178
 
179
+ # Creates traits for enumerable values.
180
+ #
181
+ # Example:
182
+ # factory :task do
183
+ # traits_for_enum :status, [:started, :finished]
184
+ # end
185
+ #
186
+ # Equivalent to:
187
+ # factory :task do
188
+ # trait :started do
189
+ # status { :started }
190
+ # end
191
+ #
192
+ # trait :finished do
193
+ # status { :finished }
194
+ # end
195
+ # end
196
+ #
197
+ # Example:
198
+ # factory :task do
199
+ # traits_for_enum :status, {started: 1, finished: 2}
200
+ # end
201
+ #
202
+ # Example:
203
+ # class Task
204
+ # def statuses
205
+ # {started: 1, finished: 2}
206
+ # end
207
+ # end
208
+ #
209
+ # factory :task do
210
+ # traits_for_enum :status
211
+ # end
212
+ #
213
+ # Both equivalent to:
214
+ # factory :task do
215
+ # trait :started do
216
+ # status { 1 }
217
+ # end
218
+ #
219
+ # trait :finished do
220
+ # status { 2 }
221
+ # end
222
+ # end
223
+ #
224
+ #
225
+ # Arguments:
226
+ # attribute_name: +Symbol+ or +String+
227
+ # the name of the attribute these traits will set the value of
228
+ # values: +Array+, +Hash+, or other +Enumerable+
229
+ # An array of trait names, or a mapping of trait names to values for
230
+ # those traits. When this argument is not provided, factory_bot will
231
+ # attempt to get the values by calling the pluralized `attribute_name`
232
+ # class method.
233
+ def traits_for_enum(attribute_name, values = nil)
234
+ @definition.register_enum(Enum.new(attribute_name, values))
235
+ end
236
+
170
237
  def initialize_with(&block)
171
238
  @definition.define_constructor(&block)
172
239
  end
240
+
241
+ private
242
+
243
+ def __declare_attribute__(name, block)
244
+ if block.nil?
245
+ declaration = Declaration::Implicit.new(name, @definition, @ignore)
246
+ @definition.declare_attribute(declaration)
247
+ else
248
+ add_attribute(name, &block)
249
+ end
250
+ end
251
+
252
+ def __valid_association_options?(options)
253
+ options.respond_to?(:has_key?) && options.has_key?(:factory)
254
+ end
173
255
  end
174
256
  end