factory_bot 5.2.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.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +52 -13
- data/GETTING_STARTED.md +415 -65
- data/NEWS.md +9 -2
- 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 +1 -1
- data/lib/factory_bot/attribute_assigner.rb +9 -10
- data/lib/factory_bot/attribute_list.rb +1 -1
- data/lib/factory_bot/callback.rb +2 -10
- data/lib/factory_bot/configuration.rb +7 -7
- data/lib/factory_bot/declaration.rb +1 -1
- data/lib/factory_bot/declaration_list.rb +2 -2
- data/lib/factory_bot/decorator.rb +0 -4
- data/lib/factory_bot/definition.rb +41 -15
- data/lib/factory_bot/definition_proxy.rb +63 -5
- data/lib/factory_bot/enum.rb +27 -0
- data/lib/factory_bot/evaluator.rb +4 -6
- 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 +6 -6
- data/lib/factory_bot/trait.rb +1 -1
- data/lib/factory_bot/version.rb +1 -1
- metadata +9 -37
data/NEWS.md
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
# News
|
2
2
|
|
3
|
+
## 6.0.0 (June 18, 2020)
|
4
|
+
* Added: automatic definition of traits for Active Record enum attributes, enabled by default
|
5
|
+
* Added: `traits_for_enum` method to define traits for non-Active Record enums
|
6
|
+
* Added: `build_stubbed_starting_id=` option to define the starting id for `build_stubbed`
|
7
|
+
* Removed: deprecated methods on the top-level `FactoryBot` module meant only for internal use
|
8
|
+
* Removed: support for EOL versions of Ruby (2.3, 2.4) and Rails (4.2)
|
9
|
+
|
3
10
|
## 5.2.0 (April 24, 2020)
|
4
11
|
* Added: Pass index to block for `*_list` methods
|
5
|
-
* Deprecated: top-level
|
12
|
+
* Deprecated: methods on the top-level `FactoryBot` module meant only for internal use: `callbacks`, `configuration`, `constructor`, `initialize_with`, `register_sequence`, `resent_configuration`, `skip_create`, `to_create`
|
6
13
|
|
7
14
|
## 5.1.2 (March 25, 2020)
|
8
15
|
* Fixed: Ruby 2.7 keyword deprecation warning in FactoryBot.lint
|
@@ -17,7 +24,7 @@
|
|
17
24
|
* Fixed: avoid undefining inherited evaluator methods
|
18
25
|
* Fixed: avoid stubbing id for records without a primary key
|
19
26
|
* Fixed: raise a helpful error for self-referencing traits to avoid a `SystemStackError`
|
20
|
-
* Deprecated: top-level
|
27
|
+
* Deprecated: methods on the top-level `FactoryBot` module meant only for internal use: `allow_class_lookup`, `allow_class_lookup`=, `register_trait`, `trait_by_name`, `traits`, `sequence_by_name`, `sequences`, `factory_by_name`, `register_factory`, `callback_names`, `register_callback`, `register_default_callbacks`, `register_default_strategies`, `strategies`
|
21
28
|
|
22
29
|
## 5.0.2 (February 22, 2019)
|
23
30
|
* Bugfix: raise "Trait not registered" error when passing invalid trait arguments
|
data/lib/factory_bot.rb
CHANGED
@@ -32,6 +32,7 @@ require "factory_bot/declaration"
|
|
32
32
|
require "factory_bot/sequence"
|
33
33
|
require "factory_bot/attribute_list"
|
34
34
|
require "factory_bot/trait"
|
35
|
+
require "factory_bot/enum"
|
35
36
|
require "factory_bot/aliases"
|
36
37
|
require "factory_bot/definition"
|
37
38
|
require "factory_bot/definition_proxy"
|
@@ -48,11 +49,14 @@ require "factory_bot/linter"
|
|
48
49
|
require "factory_bot/version"
|
49
50
|
|
50
51
|
module FactoryBot
|
51
|
-
Deprecation = ActiveSupport::Deprecation.new("
|
52
|
+
Deprecation = ActiveSupport::Deprecation.new("7.0", "factory_bot")
|
52
53
|
|
53
54
|
mattr_accessor :use_parent_strategy, instance_accessor: false
|
54
55
|
self.use_parent_strategy = true
|
55
56
|
|
57
|
+
mattr_accessor :automatically_define_enum_traits, instance_accessor: false
|
58
|
+
self.automatically_define_enum_traits = true
|
59
|
+
|
56
60
|
# Look for errors in factories and (optionally) their traits.
|
57
61
|
# Parameters:
|
58
62
|
# factories - which factories to lint; omit for all factories
|
@@ -66,60 +70,22 @@ module FactoryBot
|
|
66
70
|
Linter.new(factories_to_lint, **options).lint!
|
67
71
|
end
|
68
72
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
:register_callback,
|
78
|
-
:register_default_callbacks,
|
79
|
-
:register_default_strategies,
|
80
|
-
:register_factory,
|
81
|
-
:register_sequence,
|
82
|
-
:register_strategy,
|
83
|
-
:register_trait,
|
84
|
-
:reset_configuration,
|
85
|
-
:rewind_sequences,
|
86
|
-
:sequence_by_name,
|
87
|
-
:sequences,
|
88
|
-
:skip_create,
|
89
|
-
:strategies,
|
90
|
-
:strategy_by_name,
|
91
|
-
:to_create,
|
92
|
-
:trait_by_name,
|
93
|
-
:traits,
|
94
|
-
to: Internal
|
95
|
-
|
96
|
-
attr_accessor :allow_class_lookup
|
73
|
+
# Set the starting value for ids when using the build_stubbed strategy
|
74
|
+
#
|
75
|
+
# Arguments:
|
76
|
+
# * starting_id +Integer+
|
77
|
+
# The new starting id value.
|
78
|
+
def self.build_stubbed_starting_id=(starting_id)
|
79
|
+
Strategy::Stub.next_id = starting_id - 1
|
80
|
+
end
|
97
81
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
:factory_by_name,
|
105
|
-
:initialize_with,
|
106
|
-
:register_callback,
|
107
|
-
:register_default_callbacks,
|
108
|
-
:register_default_strategies,
|
109
|
-
:register_factory,
|
110
|
-
:register_sequence,
|
111
|
-
:register_trait,
|
112
|
-
:reset_configuration,
|
113
|
-
:sequence_by_name,
|
114
|
-
:sequences,
|
115
|
-
:skip_create,
|
116
|
-
:strategies,
|
117
|
-
:to_create,
|
118
|
-
:trait_by_name,
|
119
|
-
:traits,
|
120
|
-
deprecator: Deprecation
|
82
|
+
class << self
|
83
|
+
delegate :factories,
|
84
|
+
:register_strategy,
|
85
|
+
:rewind_sequences,
|
86
|
+
:strategy_by_name,
|
87
|
+
to: Internal
|
121
88
|
end
|
122
89
|
end
|
123
90
|
|
124
91
|
FactoryBot::Internal.register_default_strategies
|
125
|
-
FactoryBot::Internal.register_default_callbacks
|
data/lib/factory_bot/aliases.rb
CHANGED
@@ -5,14 +5,14 @@ module FactoryBot
|
|
5
5
|
|
6
6
|
self.aliases = [
|
7
7
|
[/(.+)_id/, '\1'],
|
8
|
-
[/(.*)/, '\1_id']
|
8
|
+
[/(.*)/, '\1_id']
|
9
9
|
]
|
10
10
|
|
11
11
|
def self.aliases_for(attribute)
|
12
|
-
aliases.map
|
12
|
+
aliases.map { |(pattern, replace)|
|
13
13
|
if pattern.match(attribute.to_s)
|
14
14
|
attribute.to_s.sub(pattern, replace).to_sym
|
15
15
|
end
|
16
|
-
|
16
|
+
}.compact << attribute
|
17
17
|
end
|
18
18
|
end
|
@@ -6,12 +6,12 @@ module FactoryBot
|
|
6
6
|
|
7
7
|
def initialize(name, factory, overrides)
|
8
8
|
super(name, false)
|
9
|
-
@factory
|
9
|
+
@factory = factory
|
10
10
|
@overrides = overrides
|
11
11
|
end
|
12
12
|
|
13
13
|
def to_proc
|
14
|
-
factory
|
14
|
+
factory = @factory
|
15
15
|
overrides = @overrides
|
16
16
|
traits_and_overrides = [factory, overrides].flatten
|
17
17
|
factory_name = traits_and_overrides.shift
|
@@ -2,10 +2,10 @@ module FactoryBot
|
|
2
2
|
# @api private
|
3
3
|
class AttributeAssigner
|
4
4
|
def initialize(evaluator, build_class, &instance_builder)
|
5
|
-
@build_class
|
6
|
-
@instance_builder
|
7
|
-
@evaluator
|
8
|
-
@attribute_list
|
5
|
+
@build_class = build_class
|
6
|
+
@instance_builder = instance_builder
|
7
|
+
@evaluator = evaluator
|
8
|
+
@attribute_list = evaluator.class.attribute_list
|
9
9
|
@attribute_names_assigned = []
|
10
10
|
end
|
11
11
|
|
@@ -22,9 +22,8 @@ module FactoryBot
|
|
22
22
|
def hash
|
23
23
|
@evaluator.instance = build_hash
|
24
24
|
|
25
|
-
attributes_to_set_on_hash.
|
25
|
+
attributes_to_set_on_hash.each_with_object({}) do |attribute, result|
|
26
26
|
result[attribute] = get(attribute)
|
27
|
-
result
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
@@ -33,13 +32,13 @@ module FactoryBot
|
|
33
32
|
def method_tracking_evaluator
|
34
33
|
@method_tracking_evaluator ||= Decorator::AttributeHash.new(
|
35
34
|
decorated_evaluator,
|
36
|
-
attribute_names_to_assign
|
35
|
+
attribute_names_to_assign
|
37
36
|
)
|
38
37
|
end
|
39
38
|
|
40
39
|
def decorated_evaluator
|
41
40
|
Decorator::InvocationTracker.new(
|
42
|
-
Decorator::NewConstructor.new(@evaluator, @build_class)
|
41
|
+
Decorator::NewConstructor.new(@evaluator, @build_class)
|
43
42
|
)
|
44
43
|
end
|
45
44
|
|
@@ -96,11 +95,11 @@ module FactoryBot
|
|
96
95
|
end
|
97
96
|
|
98
97
|
def alias_names_to_ignore
|
99
|
-
@attribute_list.non_ignored.flat_map
|
98
|
+
@attribute_list.non_ignored.flat_map { |attribute|
|
100
99
|
override_names.map do |override|
|
101
100
|
attribute.name if ignorable_alias?(attribute, override)
|
102
101
|
end
|
103
|
-
|
102
|
+
}.compact
|
104
103
|
end
|
105
104
|
|
106
105
|
def ignorable_alias?(attribute, override)
|
data/lib/factory_bot/callback.rb
CHANGED
@@ -3,16 +3,15 @@ module FactoryBot
|
|
3
3
|
attr_reader :name
|
4
4
|
|
5
5
|
def initialize(name, block)
|
6
|
-
@name
|
6
|
+
@name = name.to_sym
|
7
7
|
@block = block
|
8
|
-
ensure_valid_callback_name!
|
9
8
|
end
|
10
9
|
|
11
10
|
def run(instance, evaluator)
|
12
11
|
case block.arity
|
13
12
|
when 1, -1 then syntax_runner.instance_exec(instance, &block)
|
14
13
|
when 2 then syntax_runner.instance_exec(instance, evaluator, &block)
|
15
|
-
else
|
14
|
+
else syntax_runner.instance_exec(&block)
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
@@ -27,13 +26,6 @@ module FactoryBot
|
|
27
26
|
|
28
27
|
private
|
29
28
|
|
30
|
-
def ensure_valid_callback_name!
|
31
|
-
unless FactoryBot::Internal.callback_names.include?(name)
|
32
|
-
raise InvalidCallbackNameError, "#{name} is not a valid callback name. " +
|
33
|
-
"Valid callback names are #{FactoryBot::Internal.callback_names.inspect}"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
29
|
def syntax_runner
|
38
30
|
@syntax_runner ||= SyntaxRunner.new
|
39
31
|
end
|
@@ -7,16 +7,16 @@ module FactoryBot
|
|
7
7
|
:inline_sequences,
|
8
8
|
:sequences,
|
9
9
|
:strategies,
|
10
|
-
:traits
|
10
|
+
:traits
|
11
11
|
)
|
12
12
|
|
13
13
|
def initialize
|
14
|
-
@factories
|
15
|
-
@sequences
|
16
|
-
@traits
|
17
|
-
@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")
|
18
18
|
@callback_names = Set.new
|
19
|
-
@definition
|
19
|
+
@definition = Definition.new(:configuration)
|
20
20
|
@inline_sequences = []
|
21
21
|
|
22
22
|
to_create(&:save!)
|
@@ -24,7 +24,7 @@ module FactoryBot
|
|
24
24
|
end
|
25
25
|
|
26
26
|
delegate :to_create, :skip_create, :constructor, :before, :after,
|
27
|
-
|
27
|
+
:callback, :callbacks, to: :@definition
|
28
28
|
|
29
29
|
def initialize_with(&block)
|
30
30
|
@definition.define_constructor(&block)
|
@@ -1,19 +1,20 @@
|
|
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
|
17
18
|
end
|
18
19
|
|
19
20
|
delegate :declare_attribute, to: :declarations
|
@@ -43,12 +44,14 @@ module FactoryBot
|
|
43
44
|
aggregate_from_traits_and_self(:callbacks) { @callbacks }
|
44
45
|
end
|
45
46
|
|
46
|
-
def compile
|
47
|
+
def compile(klass = nil)
|
47
48
|
unless @compiled
|
49
|
+
expand_enum_traits(klass) unless klass.nil?
|
50
|
+
|
48
51
|
declarations.attributes
|
49
52
|
|
50
53
|
defined_traits.each do |defined_trait|
|
51
|
-
base_traits.each
|
54
|
+
base_traits.each { |bt| bt.define_trait defined_trait }
|
52
55
|
additional_traits.each { |at| at.define_trait defined_trait }
|
53
56
|
end
|
54
57
|
|
@@ -81,6 +84,10 @@ module FactoryBot
|
|
81
84
|
@defined_traits.add(trait)
|
82
85
|
end
|
83
86
|
|
87
|
+
def register_enum(enum)
|
88
|
+
@registered_enums << enum
|
89
|
+
end
|
90
|
+
|
84
91
|
def define_constructor(&block)
|
85
92
|
@constructor = block
|
86
93
|
end
|
@@ -95,7 +102,6 @@ module FactoryBot
|
|
95
102
|
|
96
103
|
def callback(*names, &block)
|
97
104
|
names.each do |name|
|
98
|
-
FactoryBot::Internal.register_callback(name)
|
99
105
|
add_callback(Callback.new(name, block))
|
100
106
|
end
|
101
107
|
end
|
@@ -122,7 +128,7 @@ module FactoryBot
|
|
122
128
|
def initialize_copy(source)
|
123
129
|
super
|
124
130
|
@attributes = nil
|
125
|
-
@compiled
|
131
|
+
@compiled = false
|
126
132
|
@defined_traits_by_name = nil
|
127
133
|
end
|
128
134
|
|
@@ -132,8 +138,28 @@ module FactoryBot
|
|
132
138
|
[
|
133
139
|
base_traits.map(&method_name),
|
134
140
|
instance_exec(&block),
|
135
|
-
additional_traits.map(&method_name)
|
141
|
+
additional_traits.map(&method_name)
|
136
142
|
].flatten.compact
|
137
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
|
138
164
|
end
|
139
165
|
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
|