factory_bot 4.11.1 → 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 +785 -153
- data/LICENSE +1 -1
- data/NEWS.md +379 -0
- data/README.md +20 -30
- data/lib/factory_bot/aliases.rb +2 -2
- data/lib/factory_bot/attribute/association.rb +2 -2
- data/lib/factory_bot/attribute/dynamic.rb +3 -2
- data/lib/factory_bot/attribute.rb +4 -39
- data/lib/factory_bot/attribute_assigner.rb +24 -10
- data/lib/factory_bot/attribute_list.rb +3 -2
- data/lib/factory_bot/callback.rb +4 -11
- data/lib/factory_bot/configuration.rb +15 -19
- 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.rb +5 -5
- data/lib/factory_bot/declaration_list.rb +3 -3
- data/lib/factory_bot/decorator/attribute_hash.rb +1 -1
- data/lib/factory_bot/decorator/invocation_tracker.rb +2 -1
- data/lib/factory_bot/decorator.rb +20 -4
- data/lib/factory_bot/definition.rb +69 -21
- data/lib/factory_bot/definition_hierarchy.rb +1 -11
- data/lib/factory_bot/definition_proxy.rb +119 -64
- 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 +10 -11
- 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 -32
- 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/default.rb +13 -25
- data/lib/factory_bot/syntax/methods.rb +32 -9
- data/lib/factory_bot/syntax.rb +2 -2
- data/lib/factory_bot/trait.rb +7 -4
- data/lib/factory_bot/version.rb +1 -1
- data/lib/factory_bot.rb +71 -140
- metadata +46 -34
- data/NEWS +0 -306
- 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
@@ -4,7 +4,7 @@ module FactoryBot
|
|
4
4
|
include Enumerable
|
5
5
|
|
6
6
|
def initialize(name = nil, attributes = [])
|
7
|
-
@name
|
7
|
+
@name = name
|
8
8
|
@attributes = attributes
|
9
9
|
end
|
10
10
|
|
@@ -54,7 +54,8 @@ module FactoryBot
|
|
54
54
|
|
55
55
|
def ensure_attribute_not_self_referencing!(attribute)
|
56
56
|
if attribute.respond_to?(:factory) && attribute.factory == @name
|
57
|
-
|
57
|
+
message = "Self-referencing association '#{attribute.name}' in '#{attribute.factory}'"
|
58
|
+
raise AssociationDefinitionError, message
|
58
59
|
end
|
59
60
|
end
|
60
61
|
|
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
|
-
when 1, -1 then syntax_runner.instance_exec(instance, &block)
|
12
|
+
when 1, -1, -2 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
|
|
@@ -22,17 +21,11 @@ module FactoryBot
|
|
22
21
|
end
|
23
22
|
|
24
23
|
protected
|
24
|
+
|
25
25
|
attr_reader :block
|
26
26
|
|
27
27
|
private
|
28
28
|
|
29
|
-
def ensure_valid_callback_name!
|
30
|
-
unless FactoryBot.callback_names.include?(name)
|
31
|
-
raise InvalidCallbackNameError, "#{name} is not a valid callback name. " +
|
32
|
-
"Valid callback names are #{FactoryBot.callback_names.inspect}"
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
29
|
def syntax_runner
|
37
30
|
@syntax_runner ||= SyntaxRunner.new
|
38
31
|
end
|
@@ -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
|
@@ -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
|
[]
|
@@ -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
|
@@ -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,10 +6,11 @@ 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
|
13
|
+
ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
|
13
14
|
|
14
15
|
def __invoked_methods__
|
15
16
|
@invoked_methods.uniq
|
@@ -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)
|
@@ -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
|
@@ -27,7 +30,7 @@ module FactoryBot
|
|
27
30
|
end
|
28
31
|
|
29
32
|
def to_create(&block)
|
30
|
-
if
|
33
|
+
if block
|
31
34
|
@to_create = block
|
32
35
|
else
|
33
36
|
aggregate_from_traits_and_self(:to_create) { @to_create }.last
|
@@ -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
|
@@ -103,6 +111,20 @@ module FactoryBot
|
|
103
111
|
|
104
112
|
def base_traits
|
105
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
|
106
128
|
end
|
107
129
|
|
108
130
|
def additional_traits
|
@@ -110,17 +132,19 @@ module FactoryBot
|
|
110
132
|
end
|
111
133
|
|
112
134
|
def trait_by_name(name)
|
113
|
-
trait_for(name) ||
|
135
|
+
trait_for(name) || Internal.trait_by_name(name)
|
114
136
|
end
|
115
137
|
|
116
138
|
def trait_for(name)
|
117
|
-
defined_traits.
|
139
|
+
@defined_traits_by_name ||= defined_traits.each_with_object({}) { |t, memo| memo[t.name] ||= t }
|
140
|
+
@defined_traits_by_name[name.to_s]
|
118
141
|
end
|
119
142
|
|
120
143
|
def initialize_copy(source)
|
121
144
|
super
|
122
145
|
@attributes = nil
|
123
|
-
@compiled
|
146
|
+
@compiled = false
|
147
|
+
@defined_traits_by_name = nil
|
124
148
|
end
|
125
149
|
|
126
150
|
def aggregate_from_traits_and_self(method_name, &block)
|
@@ -129,8 +153,32 @@ module FactoryBot
|
|
129
153
|
[
|
130
154
|
base_traits.map(&method_name),
|
131
155
|
instance_exec(&block),
|
132
|
-
additional_traits.map(&method_name)
|
156
|
+
additional_traits.map(&method_name)
|
133
157
|
].flatten.compact
|
134
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
|
135
183
|
end
|
136
184
|
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)
|