factory_bot 4.10.0 → 6.1.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.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/CONTRIBUTING.md +52 -13
- data/GETTING_STARTED.md +630 -182
- data/LICENSE +1 -1
- data/NEWS.md +372 -0
- data/README.md +29 -31
- data/lib/factory_bot.rb +71 -140
- data/lib/factory_bot/aliases.rb +2 -2
- data/lib/factory_bot/attribute.rb +4 -39
- data/lib/factory_bot/attribute/association.rb +2 -2
- data/lib/factory_bot/attribute/dynamic.rb +2 -1
- data/lib/factory_bot/attribute_assigner.rb +24 -10
- data/lib/factory_bot/attribute_list.rb +3 -2
- data/lib/factory_bot/callback.rb +3 -10
- data/lib/factory_bot/configuration.rb +15 -19
- data/lib/factory_bot/declaration.rb +5 -5
- data/lib/factory_bot/declaration/association.rb +33 -3
- data/lib/factory_bot/declaration/dynamic.rb +3 -1
- data/lib/factory_bot/declaration/implicit.rb +7 -2
- data/lib/factory_bot/declaration_list.rb +3 -3
- data/lib/factory_bot/decorator.rb +20 -4
- data/lib/factory_bot/decorator/attribute_hash.rb +1 -1
- data/lib/factory_bot/decorator/invocation_tracker.rb +10 -3
- data/lib/factory_bot/definition.rb +54 -20
- data/lib/factory_bot/definition_hierarchy.rb +1 -11
- data/lib/factory_bot/definition_proxy.rb +125 -43
- data/lib/factory_bot/enum.rb +27 -0
- data/lib/factory_bot/errors.rb +7 -4
- data/lib/factory_bot/evaluation.rb +1 -1
- data/lib/factory_bot/evaluator.rb +23 -15
- data/lib/factory_bot/evaluator_class_definer.rb +1 -1
- data/lib/factory_bot/factory.rb +12 -12
- data/lib/factory_bot/factory_runner.rb +4 -4
- data/lib/factory_bot/find_definitions.rb +2 -2
- data/lib/factory_bot/internal.rb +91 -0
- data/lib/factory_bot/linter.rb +41 -28
- data/lib/factory_bot/null_factory.rb +13 -4
- data/lib/factory_bot/null_object.rb +2 -6
- data/lib/factory_bot/registry.rb +17 -8
- data/lib/factory_bot/reload.rb +2 -3
- data/lib/factory_bot/sequence.rb +5 -6
- data/lib/factory_bot/strategy/stub.rb +37 -37
- data/lib/factory_bot/strategy_calculator.rb +1 -1
- data/lib/factory_bot/strategy_syntax_method_registrar.rb +13 -2
- data/lib/factory_bot/syntax.rb +2 -2
- data/lib/factory_bot/syntax/default.rb +12 -24
- data/lib/factory_bot/syntax/methods.rb +32 -9
- data/lib/factory_bot/trait.rb +7 -4
- data/lib/factory_bot/version.rb +1 -1
- metadata +50 -65
- data/.autotest +0 -9
- data/.gitignore +0 -10
- data/.rspec +0 -3
- data/.simplecov +0 -4
- data/.travis.yml +0 -58
- data/Appraisals +0 -23
- data/Gemfile +0 -8
- data/Gemfile.lock +0 -103
- data/NEWS +0 -298
- data/Rakefile +0 -36
- data/cucumber.yml +0 -1
- data/factory_bot.gemspec +0 -36
- data/factory_girl.gemspec +0 -40
- data/gemfiles/3.2.gemfile +0 -10
- data/gemfiles/3.2.gemfile.lock +0 -107
- data/gemfiles/4.0.gemfile +0 -10
- data/gemfiles/4.0.gemfile.lock +0 -107
- data/gemfiles/4.1.gemfile +0 -10
- data/gemfiles/4.1.gemfile.lock +0 -106
- data/gemfiles/4.2.gemfile +0 -10
- data/gemfiles/4.2.gemfile.lock +0 -106
- data/gemfiles/5.0.gemfile +0 -10
- data/gemfiles/5.0.gemfile.lock +0 -104
- data/gemfiles/5.1.gemfile +0 -10
- data/gemfiles/5.1.gemfile.lock +0 -104
- data/lib/factory_bot/attribute/static.rb +0 -16
- data/lib/factory_bot/declaration/static.rb +0 -26
- data/lib/factory_bot/decorator/class_key_hash.rb +0 -28
- 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
|
5
|
-
|
6
|
-
|
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
|
10
|
-
@sequences
|
11
|
-
@traits
|
12
|
-
@strategies
|
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
|
15
|
-
|
16
|
-
@allow_class_lookup = true
|
19
|
+
@definition = Definition.new(:configuration)
|
20
|
+
@inline_sequences = []
|
17
21
|
|
18
|
-
to_create
|
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
|
2
|
-
require
|
3
|
-
require
|
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
|
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
|
@@ -6,22 +6,52 @@ module FactoryBot
|
|
6
6
|
super(name, false)
|
7
7
|
@options = options.dup
|
8
8
|
@overrides = options.extract_options!
|
9
|
+
@factory_name = @overrides.delete(:factory) || name
|
9
10
|
@traits = options
|
10
11
|
end
|
11
12
|
|
12
13
|
def ==(other)
|
13
|
-
|
14
|
+
self.class == other.class &&
|
15
|
+
name == other.name &&
|
14
16
|
options == other.options
|
15
17
|
end
|
16
18
|
|
17
19
|
protected
|
20
|
+
|
18
21
|
attr_reader :options
|
19
22
|
|
20
23
|
private
|
21
24
|
|
25
|
+
attr_reader :factory_name, :overrides, :traits
|
26
|
+
|
22
27
|
def build
|
23
|
-
|
24
|
-
|
28
|
+
raise_if_arguments_are_declarations!
|
29
|
+
|
30
|
+
[
|
31
|
+
Attribute::Association.new(
|
32
|
+
name,
|
33
|
+
factory_name,
|
34
|
+
[traits, overrides].flatten
|
35
|
+
)
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
def raise_if_arguments_are_declarations!
|
40
|
+
if factory_name.is_a?(Declaration)
|
41
|
+
raise ArgumentError.new(<<~MSG)
|
42
|
+
Association '#{name}' received an invalid factory argument.
|
43
|
+
Did you mean? 'factory: :#{factory_name.name}'
|
44
|
+
MSG
|
45
|
+
end
|
46
|
+
|
47
|
+
overrides.each do |attribute, value|
|
48
|
+
if value.is_a?(Declaration)
|
49
|
+
raise ArgumentError.new(<<~MSG)
|
50
|
+
Association '#{name}' received an invalid attribute override.
|
51
|
+
Did you mean? '#{attribute}: :#{value.name}'
|
52
|
+
MSG
|
53
|
+
end
|
54
|
+
end
|
25
55
|
end
|
26
56
|
end
|
27
57
|
end
|
@@ -8,12 +8,14 @@ module FactoryBot
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def ==(other)
|
11
|
-
|
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
|
9
|
-
@overridable
|
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.
|
42
|
+
@declarations.reduce([]) { |result, declaration| result + declaration.to_attributes }
|
43
43
|
end
|
44
44
|
|
45
45
|
def overridable?
|
@@ -6,12 +6,28 @@ module FactoryBot
|
|
6
6
|
@component = component
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
if ::Gem::Version.new(::RUBY_VERSION) >= ::Gem::Version.new("2.7")
|
10
|
+
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
11
|
+
def method_missing(...) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
|
12
|
+
@component.send(...)
|
13
|
+
end
|
14
|
+
|
15
|
+
def send(...)
|
16
|
+
__send__(...)
|
17
|
+
end
|
18
|
+
RUBY
|
19
|
+
else
|
20
|
+
def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
|
21
|
+
@component.send(name, *args, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def send(symbol, *args, &block)
|
25
|
+
__send__(symbol, *args, &block)
|
26
|
+
end
|
11
27
|
end
|
12
28
|
|
13
|
-
def
|
14
|
-
|
29
|
+
def respond_to_missing?(name, include_private = false)
|
30
|
+
@component.respond_to?(name, true) || super
|
15
31
|
end
|
16
32
|
|
17
33
|
def self.const_missing(name)
|
@@ -6,9 +6,16 @@ module FactoryBot
|
|
6
6
|
@invoked_methods = []
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
if ::Gem::Version.new(::RUBY_VERSION) >= ::Gem::Version.new("2.7")
|
10
|
+
def method_missing(name, *args, **kwargs, &block) # rubocop:disable Style/MissingRespondToMissing
|
11
|
+
@invoked_methods << name
|
12
|
+
super
|
13
|
+
end
|
14
|
+
else
|
15
|
+
def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing
|
16
|
+
@invoked_methods << name
|
17
|
+
super
|
18
|
+
end
|
12
19
|
end
|
13
20
|
|
14
21
|
def __invoked_methods__
|
@@ -1,18 +1,21 @@
|
|
1
1
|
module FactoryBot
|
2
2
|
# @api private
|
3
3
|
class Definition
|
4
|
-
attr_reader :defined_traits, :declarations
|
5
|
-
|
6
|
-
def initialize(name
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@
|
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
|
14
|
-
@attributes
|
15
|
-
@compiled
|
15
|
+
@constructor = nil
|
16
|
+
@attributes = nil
|
17
|
+
@compiled = false
|
18
|
+
@expanded_enum_traits = false
|
16
19
|
end
|
17
20
|
|
18
21
|
delegate :declare_attribute, to: :declarations
|
@@ -42,13 +45,15 @@ module FactoryBot
|
|
42
45
|
aggregate_from_traits_and_self(:callbacks) { @callbacks }
|
43
46
|
end
|
44
47
|
|
45
|
-
def compile
|
48
|
+
def compile(klass = nil)
|
46
49
|
unless @compiled
|
50
|
+
expand_enum_traits(klass) unless klass.nil?
|
51
|
+
|
47
52
|
declarations.attributes
|
48
53
|
|
49
54
|
defined_traits.each do |defined_trait|
|
50
|
-
base_traits.each
|
51
|
-
additional_traits.each { |
|
55
|
+
base_traits.each { |bt| bt.define_trait defined_trait }
|
56
|
+
additional_traits.each { |at| at.define_trait defined_trait }
|
52
57
|
end
|
53
58
|
|
54
59
|
@compiled = true
|
@@ -73,13 +78,17 @@ module FactoryBot
|
|
73
78
|
end
|
74
79
|
|
75
80
|
def skip_create
|
76
|
-
@to_create = ->(instance) {
|
81
|
+
@to_create = ->(instance) {}
|
77
82
|
end
|
78
83
|
|
79
84
|
def define_trait(trait)
|
80
85
|
@defined_traits.add(trait)
|
81
86
|
end
|
82
87
|
|
88
|
+
def register_enum(enum)
|
89
|
+
@registered_enums << enum
|
90
|
+
end
|
91
|
+
|
83
92
|
def define_constructor(&block)
|
84
93
|
@constructor = block
|
85
94
|
end
|
@@ -94,7 +103,6 @@ module FactoryBot
|
|
94
103
|
|
95
104
|
def callback(*names, &block)
|
96
105
|
names.each do |name|
|
97
|
-
FactoryBot.register_callback(name)
|
98
106
|
add_callback(Callback.new(name, block))
|
99
107
|
end
|
100
108
|
end
|
@@ -110,17 +118,19 @@ module FactoryBot
|
|
110
118
|
end
|
111
119
|
|
112
120
|
def trait_by_name(name)
|
113
|
-
trait_for(name) ||
|
121
|
+
trait_for(name) || Internal.trait_by_name(name)
|
114
122
|
end
|
115
123
|
|
116
124
|
def trait_for(name)
|
117
|
-
defined_traits.
|
125
|
+
@defined_traits_by_name ||= defined_traits.each_with_object({}) { |t, memo| memo[t.name] ||= t }
|
126
|
+
@defined_traits_by_name[name.to_s]
|
118
127
|
end
|
119
128
|
|
120
129
|
def initialize_copy(source)
|
121
130
|
super
|
122
131
|
@attributes = nil
|
123
|
-
@compiled
|
132
|
+
@compiled = false
|
133
|
+
@defined_traits_by_name = nil
|
124
134
|
end
|
125
135
|
|
126
136
|
def aggregate_from_traits_and_self(method_name, &block)
|
@@ -129,8 +139,32 @@ module FactoryBot
|
|
129
139
|
[
|
130
140
|
base_traits.map(&method_name),
|
131
141
|
instance_exec(&block),
|
132
|
-
additional_traits.map(&method_name)
|
142
|
+
additional_traits.map(&method_name)
|
133
143
|
].flatten.compact
|
134
144
|
end
|
145
|
+
|
146
|
+
def expand_enum_traits(klass)
|
147
|
+
return if @expanded_enum_traits
|
148
|
+
|
149
|
+
if automatically_register_defined_enums?(klass)
|
150
|
+
automatically_register_defined_enums(klass)
|
151
|
+
end
|
152
|
+
|
153
|
+
registered_enums.each do |enum|
|
154
|
+
traits = enum.build_traits(klass)
|
155
|
+
traits.each { |trait| define_trait(trait) }
|
156
|
+
end
|
157
|
+
|
158
|
+
@expanded_enum_traits = true
|
159
|
+
end
|
160
|
+
|
161
|
+
def automatically_register_defined_enums(klass)
|
162
|
+
klass.defined_enums.each_key { |name| register_enum(Enum.new(name)) }
|
163
|
+
end
|
164
|
+
|
165
|
+
def automatically_register_defined_enums?(klass)
|
166
|
+
FactoryBot.automatically_define_enum_traits &&
|
167
|
+
klass.respond_to?(:defined_enums)
|
168
|
+
end
|
135
169
|
end
|
136
170
|
end
|
@@ -1,16 +1,6 @@
|
|
1
1
|
module FactoryBot
|
2
2
|
class DefinitionHierarchy
|
3
|
-
|
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
|
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
|
15
|
-
@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
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
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
|
-
|
41
|
-
|
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
|
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
|
81
|
-
#
|
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 {
|
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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
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
|
-
|
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
|