factory_bot 5.2.0 → 6.2.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/CONTRIBUTING.md +58 -13
- data/GETTING_STARTED.md +684 -136
- data/NEWS.md +30 -2
- data/README.md +4 -10
- data/lib/factory_bot.rb +19 -53
- data/lib/factory_bot/aliases.rb +3 -3
- data/lib/factory_bot/attribute/association.rb +2 -2
- data/lib/factory_bot/attribute/dynamic.rb +2 -2
- data/lib/factory_bot/attribute_assigner.rb +9 -10
- data/lib/factory_bot/attribute_list.rb +1 -1
- data/lib/factory_bot/callback.rb +3 -11
- data/lib/factory_bot/configuration.rb +7 -7
- data/lib/factory_bot/declaration.rb +1 -1
- data/lib/factory_bot/declaration/association.rb +23 -6
- data/lib/factory_bot/declaration_list.rb +2 -2
- data/lib/factory_bot/decorator.rb +18 -6
- data/lib/factory_bot/decorator/invocation_tracker.rb +1 -0
- data/lib/factory_bot/definition.rb +61 -16
- data/lib/factory_bot/definition_proxy.rb +63 -5
- data/lib/factory_bot/enum.rb +27 -0
- data/lib/factory_bot/evaluator.rb +6 -7
- 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 +3 -3
- data/lib/factory_bot/find_definitions.rb +1 -1
- data/lib/factory_bot/internal.rb +12 -25
- data/lib/factory_bot/linter.rb +8 -12
- data/lib/factory_bot/null_factory.rb +11 -5
- data/lib/factory_bot/null_object.rb +2 -6
- data/lib/factory_bot/registry.rb +2 -2
- data/lib/factory_bot/reload.rb +0 -1
- data/lib/factory_bot/sequence.rb +5 -5
- data/lib/factory_bot/strategy/null.rb +4 -2
- data/lib/factory_bot/strategy/stub.rb +6 -2
- data/lib/factory_bot/syntax/default.rb +7 -7
- data/lib/factory_bot/trait.rb +2 -2
- data/lib/factory_bot/version.rb +1 -1
- metadata +12 -40
@@ -1,19 +1,21 @@
|
|
1
1
|
module FactoryBot
|
2
2
|
# @api private
|
3
3
|
class Definition
|
4
|
-
attr_reader :defined_traits, :declarations, :name
|
4
|
+
attr_reader :defined_traits, :declarations, :name, :registered_enums
|
5
5
|
|
6
6
|
def initialize(name, base_traits = [])
|
7
|
-
@name
|
8
|
-
@declarations
|
9
|
-
@callbacks
|
10
|
-
@defined_traits
|
11
|
-
@
|
12
|
-
@
|
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
|
13
14
|
@additional_traits = []
|
14
|
-
@constructor
|
15
|
-
@attributes
|
16
|
-
@compiled
|
15
|
+
@constructor = nil
|
16
|
+
@attributes = nil
|
17
|
+
@compiled = false
|
18
|
+
@expanded_enum_traits = false
|
17
19
|
end
|
18
20
|
|
19
21
|
delegate :declare_attribute, to: :declarations
|
@@ -28,7 +30,7 @@ module FactoryBot
|
|
28
30
|
end
|
29
31
|
|
30
32
|
def to_create(&block)
|
31
|
-
if
|
33
|
+
if block
|
32
34
|
@to_create = block
|
33
35
|
else
|
34
36
|
aggregate_from_traits_and_self(:to_create) { @to_create }.last
|
@@ -43,12 +45,14 @@ module FactoryBot
|
|
43
45
|
aggregate_from_traits_and_self(:callbacks) { @callbacks }
|
44
46
|
end
|
45
47
|
|
46
|
-
def compile
|
48
|
+
def compile(klass = nil)
|
47
49
|
unless @compiled
|
50
|
+
expand_enum_traits(klass) unless klass.nil?
|
51
|
+
|
48
52
|
declarations.attributes
|
49
53
|
|
50
54
|
defined_traits.each do |defined_trait|
|
51
|
-
base_traits.each
|
55
|
+
base_traits.each { |bt| bt.define_trait defined_trait }
|
52
56
|
additional_traits.each { |at| at.define_trait defined_trait }
|
53
57
|
end
|
54
58
|
|
@@ -81,6 +85,10 @@ module FactoryBot
|
|
81
85
|
@defined_traits.add(trait)
|
82
86
|
end
|
83
87
|
|
88
|
+
def register_enum(enum)
|
89
|
+
@registered_enums << enum
|
90
|
+
end
|
91
|
+
|
84
92
|
def define_constructor(&block)
|
85
93
|
@constructor = block
|
86
94
|
end
|
@@ -95,7 +103,6 @@ module FactoryBot
|
|
95
103
|
|
96
104
|
def callback(*names, &block)
|
97
105
|
names.each do |name|
|
98
|
-
FactoryBot::Internal.register_callback(name)
|
99
106
|
add_callback(Callback.new(name, block))
|
100
107
|
end
|
101
108
|
end
|
@@ -104,6 +111,20 @@ module FactoryBot
|
|
104
111
|
|
105
112
|
def base_traits
|
106
113
|
@base_traits.map { |name| trait_by_name(name) }
|
114
|
+
rescue KeyError => error
|
115
|
+
raise error_with_definition_name(error)
|
116
|
+
end
|
117
|
+
|
118
|
+
def error_with_definition_name(error)
|
119
|
+
message = error.message
|
120
|
+
message.insert(
|
121
|
+
message.index("\nDid you mean?") || message.length,
|
122
|
+
" referenced within \"#{name}\" definition"
|
123
|
+
)
|
124
|
+
|
125
|
+
error.class.new(message).tap do |new_error|
|
126
|
+
new_error.set_backtrace(error.backtrace)
|
127
|
+
end
|
107
128
|
end
|
108
129
|
|
109
130
|
def additional_traits
|
@@ -122,7 +143,7 @@ module FactoryBot
|
|
122
143
|
def initialize_copy(source)
|
123
144
|
super
|
124
145
|
@attributes = nil
|
125
|
-
@compiled
|
146
|
+
@compiled = false
|
126
147
|
@defined_traits_by_name = nil
|
127
148
|
end
|
128
149
|
|
@@ -132,8 +153,32 @@ module FactoryBot
|
|
132
153
|
[
|
133
154
|
base_traits.map(&method_name),
|
134
155
|
instance_exec(&block),
|
135
|
-
additional_traits.map(&method_name)
|
156
|
+
additional_traits.map(&method_name)
|
136
157
|
].flatten.compact
|
137
158
|
end
|
159
|
+
|
160
|
+
def expand_enum_traits(klass)
|
161
|
+
return if @expanded_enum_traits
|
162
|
+
|
163
|
+
if automatically_register_defined_enums?(klass)
|
164
|
+
automatically_register_defined_enums(klass)
|
165
|
+
end
|
166
|
+
|
167
|
+
registered_enums.each do |enum|
|
168
|
+
traits = enum.build_traits(klass)
|
169
|
+
traits.each { |trait| define_trait(trait) }
|
170
|
+
end
|
171
|
+
|
172
|
+
@expanded_enum_traits = true
|
173
|
+
end
|
174
|
+
|
175
|
+
def automatically_register_defined_enums(klass)
|
176
|
+
klass.defined_enums.each_key { |name| register_enum(Enum.new(name)) }
|
177
|
+
end
|
178
|
+
|
179
|
+
def automatically_register_defined_enums?(klass)
|
180
|
+
FactoryBot.automatically_define_enum_traits &&
|
181
|
+
klass.respond_to?(:defined_enums)
|
182
|
+
end
|
138
183
|
end
|
139
184
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module FactoryBot
|
2
2
|
class DefinitionProxy
|
3
|
-
UNPROXIED_METHODS = %w
|
3
|
+
UNPROXIED_METHODS = %w[
|
4
4
|
__send__
|
5
5
|
__id__
|
6
6
|
nil?
|
@@ -13,7 +13,7 @@ module FactoryBot
|
|
13
13
|
raise
|
14
14
|
caller
|
15
15
|
method
|
16
|
-
|
16
|
+
].freeze
|
17
17
|
|
18
18
|
(instance_methods + private_instance_methods).each do |method|
|
19
19
|
undef_method(method) unless UNPROXIED_METHODS.include?(method.to_s)
|
@@ -24,8 +24,8 @@ module FactoryBot
|
|
24
24
|
attr_reader :child_factories
|
25
25
|
|
26
26
|
def initialize(definition, ignore = false)
|
27
|
-
@definition
|
28
|
-
@ignore
|
27
|
+
@definition = definition
|
28
|
+
@ignore = ignore
|
29
29
|
@child_factories = []
|
30
30
|
end
|
31
31
|
|
@@ -152,7 +152,7 @@ module FactoryBot
|
|
152
152
|
if block_given?
|
153
153
|
raise AssociationDefinitionError.new(
|
154
154
|
"Unexpected block passed to '#{name}' association "\
|
155
|
-
"in '#{@definition.name}' factory"
|
155
|
+
"in '#{@definition.name}' factory"
|
156
156
|
)
|
157
157
|
else
|
158
158
|
declaration = Declaration::Association.new(name, *options)
|
@@ -176,6 +176,64 @@ module FactoryBot
|
|
176
176
|
@definition.define_trait(Trait.new(name, &block))
|
177
177
|
end
|
178
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
|
+
|
179
237
|
def initialize_with(&block)
|
180
238
|
@definition.define_constructor(&block)
|
181
239
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module FactoryBot
|
2
|
+
# @api private
|
3
|
+
class Enum
|
4
|
+
def initialize(attribute_name, values = nil)
|
5
|
+
@attribute_name = attribute_name
|
6
|
+
@values = values
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_traits(klass)
|
10
|
+
enum_values(klass).map do |trait_name, value|
|
11
|
+
build_trait(trait_name, @attribute_name, value || trait_name)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def enum_values(klass)
|
18
|
+
@values || klass.send(@attribute_name.to_s.pluralize)
|
19
|
+
end
|
20
|
+
|
21
|
+
def build_trait(trait_name, attribute_name, value)
|
22
|
+
Trait.new(trait_name) do
|
23
|
+
add_attribute(attribute_name) { value }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -7,7 +7,7 @@ module FactoryBot
|
|
7
7
|
class_attribute :attribute_lists
|
8
8
|
|
9
9
|
private_instance_methods.each do |method|
|
10
|
-
undef_method(method) unless method
|
10
|
+
undef_method(method) unless method.match?(/^__|initialize/)
|
11
11
|
end
|
12
12
|
|
13
13
|
def initialize(build_strategy, overrides = {})
|
@@ -23,9 +23,9 @@ module FactoryBot
|
|
23
23
|
|
24
24
|
def association(factory_name, *traits_and_overrides)
|
25
25
|
overrides = traits_and_overrides.extract_options!
|
26
|
-
strategy_override = overrides.fetch(:strategy)
|
26
|
+
strategy_override = overrides.fetch(:strategy) {
|
27
27
|
FactoryBot.use_parent_strategy ? @build_strategy.class : :create
|
28
|
-
|
28
|
+
}
|
29
29
|
|
30
30
|
traits_and_overrides += [overrides.except(:strategy)]
|
31
31
|
|
@@ -33,17 +33,16 @@ module FactoryBot
|
|
33
33
|
@build_strategy.association(runner)
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
@instance = object_instance
|
38
|
-
end
|
36
|
+
attr_accessor :instance
|
39
37
|
|
40
|
-
def method_missing(method_name, *args, &block) # rubocop:disable Style/MethodMissingSuper
|
38
|
+
def method_missing(method_name, *args, &block) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
|
41
39
|
if @instance.respond_to?(method_name)
|
42
40
|
@instance.send(method_name, *args, &block)
|
43
41
|
else
|
44
42
|
SyntaxRunner.new.send(method_name, *args, &block)
|
45
43
|
end
|
46
44
|
end
|
45
|
+
ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
|
47
46
|
|
48
47
|
def respond_to_missing?(method_name, _include_private = false)
|
49
48
|
@instance.respond_to?(method_name) || SyntaxRunner.new.respond_to?(method_name)
|
@@ -3,7 +3,7 @@ module FactoryBot
|
|
3
3
|
class EvaluatorClassDefiner
|
4
4
|
def initialize(attributes, parent_class)
|
5
5
|
@parent_class = parent_class
|
6
|
-
@attributes
|
6
|
+
@attributes = attributes
|
7
7
|
|
8
8
|
attributes.each do |attribute|
|
9
9
|
evaluator_class.define_attribute(attribute.name, &attribute.to_proc)
|
data/lib/factory_bot/factory.rb
CHANGED
@@ -8,23 +8,23 @@ module FactoryBot
|
|
8
8
|
|
9
9
|
def initialize(name, options = {})
|
10
10
|
assert_valid_options(options)
|
11
|
-
@name
|
12
|
-
@parent
|
13
|
-
@aliases
|
14
|
-
@class_name
|
15
|
-
@definition
|
16
|
-
@compiled
|
11
|
+
@name = name.respond_to?(:to_sym) ? name.to_sym : name.to_s.underscore.to_sym
|
12
|
+
@parent = options[:parent]
|
13
|
+
@aliases = options[:aliases] || []
|
14
|
+
@class_name = options[:class]
|
15
|
+
@definition = Definition.new(@name, options[:traits] || [])
|
16
|
+
@compiled = false
|
17
17
|
end
|
18
18
|
|
19
19
|
delegate :add_callback, :declare_attribute, :to_create, :define_trait, :constructor,
|
20
|
-
|
20
|
+
:defined_traits, :inherit_traits, :append_traits, to: :@definition
|
21
21
|
|
22
22
|
def build_class
|
23
23
|
@build_class ||= if class_name.is_a? Class
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
class_name
|
25
|
+
else
|
26
|
+
class_name.to_s.camelize.constantize
|
27
|
+
end
|
28
28
|
end
|
29
29
|
|
30
30
|
def run(build_strategy, overrides, &block)
|
@@ -84,7 +84,7 @@ module FactoryBot
|
|
84
84
|
unless @compiled
|
85
85
|
parent.compile
|
86
86
|
parent.defined_traits.each { |trait| define_trait(trait) }
|
87
|
-
@definition.compile
|
87
|
+
@definition.compile(build_class)
|
88
88
|
build_hierarchy
|
89
89
|
@compiled = true
|
90
90
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module FactoryBot
|
2
2
|
class FactoryRunner
|
3
3
|
def initialize(name, strategy, traits_and_overrides)
|
4
|
-
@name
|
4
|
+
@name = name
|
5
5
|
@strategy = strategy
|
6
6
|
|
7
7
|
@overrides = traits_and_overrides.extract_options!
|
8
|
-
@traits
|
8
|
+
@traits = traits_and_overrides
|
9
9
|
end
|
10
10
|
|
11
11
|
def run(runner_strategy = @strategy, &block)
|
@@ -22,7 +22,7 @@ module FactoryBot
|
|
22
22
|
strategy: runner_strategy,
|
23
23
|
traits: @traits,
|
24
24
|
overrides: @overrides,
|
25
|
-
factory: factory
|
25
|
+
factory: factory
|
26
26
|
}
|
27
27
|
|
28
28
|
ActiveSupport::Notifications.instrument("factory_bot.run_factory", instrumentation_payload) do
|
@@ -7,7 +7,7 @@ module FactoryBot
|
|
7
7
|
attr_accessor :definition_file_paths
|
8
8
|
end
|
9
9
|
|
10
|
-
self.definition_file_paths = %w
|
10
|
+
self.definition_file_paths = %w[factories test/factories spec/factories]
|
11
11
|
|
12
12
|
def self.find_definitions
|
13
13
|
absolute_definition_file_paths = definition_file_paths.map { |path| File.expand_path(path) }
|
data/lib/factory_bot/internal.rb
CHANGED
@@ -3,19 +3,18 @@ module FactoryBot
|
|
3
3
|
module Internal
|
4
4
|
class << self
|
5
5
|
delegate :after,
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
to: :configuration
|
6
|
+
:before,
|
7
|
+
:callbacks,
|
8
|
+
:constructor,
|
9
|
+
:factories,
|
10
|
+
:initialize_with,
|
11
|
+
:inline_sequences,
|
12
|
+
:sequences,
|
13
|
+
:skip_create,
|
14
|
+
:strategies,
|
15
|
+
:to_create,
|
16
|
+
:traits,
|
17
|
+
to: :configuration
|
19
18
|
|
20
19
|
def configuration
|
21
20
|
@configuration ||= Configuration.new
|
@@ -87,18 +86,6 @@ module FactoryBot
|
|
87
86
|
register_strategy(:build_stubbed, FactoryBot::Strategy::Stub)
|
88
87
|
register_strategy(:null, FactoryBot::Strategy::Null)
|
89
88
|
end
|
90
|
-
|
91
|
-
def register_default_callbacks
|
92
|
-
register_callback(:after_create)
|
93
|
-
register_callback(:after_build)
|
94
|
-
register_callback(:after_stub)
|
95
|
-
register_callback(:before_create)
|
96
|
-
end
|
97
|
-
|
98
|
-
def register_callback(name)
|
99
|
-
name = name.to_sym
|
100
|
-
callback_names << name
|
101
|
-
end
|
102
89
|
end
|
103
90
|
end
|
104
91
|
end
|