factory_bot 5.0.0 → 5.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/GETTING_STARTED.md +16 -16
- data/LICENSE +1 -1
- data/NEWS.md +347 -0
- data/README.md +13 -6
- data/lib/factory_bot.rb +43 -79
- data/lib/factory_bot/attribute/dynamic.rb +1 -0
- data/lib/factory_bot/attribute_assigner.rb +1 -1
- data/lib/factory_bot/callback.rb +2 -2
- data/lib/factory_bot/configuration.rb +10 -2
- data/lib/factory_bot/declaration/association.rb +11 -0
- data/lib/factory_bot/declaration/implicit.rb +4 -1
- data/lib/factory_bot/decorator.rb +1 -1
- data/lib/factory_bot/decorator/invocation_tracker.rb +1 -1
- data/lib/factory_bot/definition.rb +6 -4
- data/lib/factory_bot/definition_proxy.rb +16 -10
- data/lib/factory_bot/errors.rb +3 -0
- data/lib/factory_bot/evaluator.rb +2 -2
- data/lib/factory_bot/factory.rb +1 -1
- data/lib/factory_bot/factory_runner.rb +2 -2
- data/lib/factory_bot/internal.rb +102 -0
- data/lib/factory_bot/linter.rb +4 -4
- data/lib/factory_bot/null_factory.rb +1 -1
- data/lib/factory_bot/registry.rb +2 -2
- data/lib/factory_bot/reload.rb +3 -3
- data/lib/factory_bot/strategy/stub.rb +10 -3
- data/lib/factory_bot/strategy_calculator.rb +1 -1
- data/lib/factory_bot/syntax/default.rb +5 -5
- data/lib/factory_bot/syntax/methods.rb +3 -3
- data/lib/factory_bot/trait.rb +5 -3
- data/lib/factory_bot/version.rb +1 -1
- metadata +12 -11
- data/NEWS +0 -323
data/README.md
CHANGED
@@ -5,7 +5,7 @@ factory_bot is a fixtures replacement with a straightforward definition syntax,
|
|
5
5
|
If you want to use factory_bot with Rails, see
|
6
6
|
[factory_bot_rails](https://github.com/thoughtbot/factory_bot_rails).
|
7
7
|
|
8
|
-
_[Interested in the history of the project name?]
|
8
|
+
_[Interested in the history of the project name?][NAME]_
|
9
9
|
|
10
10
|
|
11
11
|
### Transitioning from factory\_girl?
|
@@ -59,9 +59,13 @@ More Information
|
|
59
59
|
* [Issues](https://github.com/thoughtbot/factory_bot/issues)
|
60
60
|
* [GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS](https://robots.thoughtbot.com/)
|
61
61
|
|
62
|
-
|
62
|
+
[GETTING_STARTED]: https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md
|
63
|
+
[NAME]: https://github.com/thoughtbot/factory_bot/blob/master/NAME.md
|
63
64
|
|
64
|
-
|
65
|
+
Useful Tools
|
66
|
+
------------
|
67
|
+
|
68
|
+
* [FactoryTrace](https://github.com/djezzzl/factory_trace) - helps to find unused factories and traits.
|
65
69
|
|
66
70
|
Contributing
|
67
71
|
------------
|
@@ -75,14 +79,17 @@ community](https://github.com/thoughtbot/factory_bot/graphs/contributors).
|
|
75
79
|
License
|
76
80
|
-------
|
77
81
|
|
78
|
-
factory_bot is Copyright © 2008-
|
82
|
+
factory_bot is Copyright © 2008-2019 Joe Ferris and thoughtbot. It is free
|
79
83
|
software, and may be redistributed under the terms specified in the
|
80
|
-
[LICENSE]
|
84
|
+
[LICENSE] file.
|
85
|
+
|
86
|
+
[LICENSE]: https://github.com/thoughtbot/factory_bot/blob/master/LICENSE
|
87
|
+
|
81
88
|
|
82
89
|
About thoughtbot
|
83
90
|
----------------
|
84
91
|
|
85
|
-
![thoughtbot](https://
|
92
|
+
![thoughtbot](https://thoughtbot.com/brand_assets/93:44.svg)
|
86
93
|
|
87
94
|
factory_bot is maintained and funded by thoughtbot, inc.
|
88
95
|
The names and logos for thoughtbot are trademarks of thoughtbot, inc.
|
data/lib/factory_bot.rb
CHANGED
@@ -45,16 +45,17 @@ require "factory_bot/decorator/invocation_tracker"
|
|
45
45
|
require "factory_bot/decorator/new_constructor"
|
46
46
|
require "factory_bot/linter"
|
47
47
|
require "factory_bot/version"
|
48
|
+
require "factory_bot/internal"
|
48
49
|
|
49
50
|
module FactoryBot
|
50
|
-
|
51
|
+
Deprecation = ActiveSupport::Deprecation.new("6.0", "factory_bot")
|
51
52
|
|
52
53
|
def self.configuration
|
53
|
-
|
54
|
+
Internal.configuration
|
54
55
|
end
|
55
56
|
|
56
57
|
def self.reset_configuration
|
57
|
-
|
58
|
+
Internal.reset_configuration
|
58
59
|
end
|
59
60
|
|
60
61
|
mattr_accessor :use_parent_strategy, instance_accessor: false
|
@@ -70,92 +71,55 @@ module FactoryBot
|
|
70
71
|
def self.lint(*args)
|
71
72
|
options = args.extract_options!
|
72
73
|
factories_to_lint = args[0] || FactoryBot.factories
|
73
|
-
Linter.new(factories_to_lint, options).lint!
|
74
|
+
Linter.new(factories_to_lint, **options).lint!
|
74
75
|
end
|
75
76
|
|
76
77
|
class << self
|
77
|
-
delegate :
|
78
|
+
delegate :callbacks,
|
79
|
+
:callback_names,
|
80
|
+
:constructor,
|
81
|
+
:factories,
|
82
|
+
:initialize_with,
|
78
83
|
:sequences,
|
79
|
-
:
|
80
|
-
:callbacks,
|
84
|
+
:skip_create,
|
81
85
|
:strategies,
|
82
|
-
:callback_names,
|
83
86
|
:to_create,
|
84
|
-
:
|
85
|
-
:initialize_with,
|
86
|
-
:constructor,
|
87
|
+
:traits,
|
87
88
|
to: :configuration
|
88
89
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
|
104
|
-
def self.register_sequence(sequence)
|
105
|
-
sequence.names.each do |name|
|
106
|
-
sequences.register(name, sequence)
|
107
|
-
end
|
108
|
-
sequence
|
109
|
-
end
|
110
|
-
|
111
|
-
def self.sequence_by_name(name)
|
112
|
-
sequences.find(name)
|
113
|
-
end
|
114
|
-
|
115
|
-
def self.rewind_sequences
|
116
|
-
sequences.each(&:rewind)
|
117
|
-
end
|
118
|
-
|
119
|
-
def self.register_trait(trait)
|
120
|
-
trait.names.each do |name|
|
121
|
-
traits.register(name, trait)
|
122
|
-
end
|
123
|
-
trait
|
124
|
-
end
|
125
|
-
|
126
|
-
def self.trait_by_name(name)
|
127
|
-
traits.find(name)
|
128
|
-
end
|
90
|
+
delegate :factory_by_name,
|
91
|
+
:register_callback,
|
92
|
+
:register_default_callbacks,
|
93
|
+
:register_default_strategies,
|
94
|
+
:register_factory,
|
95
|
+
:register_sequence,
|
96
|
+
:register_strategy,
|
97
|
+
:register_trait,
|
98
|
+
:rewind_sequences,
|
99
|
+
:sequence_by_name,
|
100
|
+
:strategy_by_name,
|
101
|
+
:trait_by_name,
|
102
|
+
to: Internal
|
129
103
|
|
130
|
-
|
131
|
-
strategies.register(strategy_name, strategy_class)
|
132
|
-
StrategySyntaxMethodRegistrar.new(strategy_name).define_strategy_methods
|
133
|
-
end
|
134
|
-
|
135
|
-
def self.strategy_by_name(name)
|
136
|
-
strategies.find(name)
|
137
|
-
end
|
138
|
-
|
139
|
-
def self.register_default_strategies
|
140
|
-
register_strategy(:build, FactoryBot::Strategy::Build)
|
141
|
-
register_strategy(:create, FactoryBot::Strategy::Create)
|
142
|
-
register_strategy(:attributes_for, FactoryBot::Strategy::AttributesFor)
|
143
|
-
register_strategy(:build_stubbed, FactoryBot::Strategy::Stub)
|
144
|
-
register_strategy(:null, FactoryBot::Strategy::Null)
|
145
|
-
end
|
146
|
-
|
147
|
-
def self.register_default_callbacks
|
148
|
-
register_callback(:after_build)
|
149
|
-
register_callback(:after_create)
|
150
|
-
register_callback(:after_stub)
|
151
|
-
register_callback(:before_create)
|
152
|
-
end
|
104
|
+
attr_accessor :allow_class_lookup
|
153
105
|
|
154
|
-
|
155
|
-
|
156
|
-
|
106
|
+
deprecate :allow_class_lookup,
|
107
|
+
:allow_class_lookup=,
|
108
|
+
:callback_names,
|
109
|
+
:factory_by_name,
|
110
|
+
:register_callback,
|
111
|
+
:register_default_callbacks,
|
112
|
+
:register_default_strategies,
|
113
|
+
:register_factory,
|
114
|
+
:register_trait,
|
115
|
+
:sequence_by_name,
|
116
|
+
:sequences,
|
117
|
+
:strategies,
|
118
|
+
:trait_by_name,
|
119
|
+
:traits,
|
120
|
+
deprecator: Deprecation
|
157
121
|
end
|
158
122
|
end
|
159
123
|
|
160
|
-
FactoryBot.register_default_strategies
|
161
|
-
FactoryBot.register_default_callbacks
|
124
|
+
FactoryBot::Internal.register_default_strategies
|
125
|
+
FactoryBot::Internal.register_default_callbacks
|
@@ -2,7 +2,7 @@ module FactoryBot
|
|
2
2
|
# @api private
|
3
3
|
class AttributeAssigner
|
4
4
|
def initialize(evaluator, build_class, &instance_builder)
|
5
|
-
@build_class
|
5
|
+
@build_class = build_class
|
6
6
|
@instance_builder = instance_builder
|
7
7
|
@evaluator = evaluator
|
8
8
|
@attribute_list = evaluator.class.attribute_list
|
data/lib/factory_bot/callback.rb
CHANGED
@@ -28,9 +28,9 @@ module FactoryBot
|
|
28
28
|
private
|
29
29
|
|
30
30
|
def ensure_valid_callback_name!
|
31
|
-
unless FactoryBot.callback_names.include?(name)
|
31
|
+
unless FactoryBot::Internal.callback_names.include?(name)
|
32
32
|
raise InvalidCallbackNameError, "#{name} is not a valid callback name. " +
|
33
|
-
"Valid callback names are #{FactoryBot.callback_names.inspect}"
|
33
|
+
"Valid callback names are #{FactoryBot::Internal.callback_names.inspect}"
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -1,7 +1,14 @@
|
|
1
1
|
module FactoryBot
|
2
2
|
# @api private
|
3
3
|
class Configuration
|
4
|
-
attr_reader
|
4
|
+
attr_reader(
|
5
|
+
:callback_names,
|
6
|
+
:factories,
|
7
|
+
:inline_sequences,
|
8
|
+
:sequences,
|
9
|
+
:strategies,
|
10
|
+
:traits,
|
11
|
+
)
|
5
12
|
|
6
13
|
def initialize
|
7
14
|
@factories = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Factory"))
|
@@ -10,13 +17,14 @@ module FactoryBot
|
|
10
17
|
@strategies = Registry.new("Strategy")
|
11
18
|
@callback_names = Set.new
|
12
19
|
@definition = Definition.new(:configuration)
|
20
|
+
@inline_sequences = []
|
13
21
|
|
14
22
|
to_create(&:save!)
|
15
23
|
initialize_with { new }
|
16
24
|
end
|
17
25
|
|
18
26
|
delegate :to_create, :skip_create, :constructor, :before, :after,
|
19
|
-
|
27
|
+
:callback, :callbacks, to: :@definition
|
20
28
|
|
21
29
|
def initialize_with(&block)
|
22
30
|
@definition.define_constructor(&block)
|
@@ -22,9 +22,20 @@ module FactoryBot
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def build
|
25
|
+
ensure_factory_is_not_a_declaration!
|
26
|
+
|
25
27
|
factory_name = @overrides[:factory] || name
|
26
28
|
[Attribute::Association.new(name, factory_name, [@traits, @overrides.except(:factory)].flatten)]
|
27
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
|
28
39
|
end
|
29
40
|
end
|
30
41
|
end
|
@@ -23,8 +23,11 @@ module FactoryBot
|
|
23
23
|
def build
|
24
24
|
if FactoryBot.factories.registered?(name)
|
25
25
|
[Attribute::Association.new(name, name, {})]
|
26
|
-
elsif FactoryBot.sequences.registered?(name)
|
26
|
+
elsif FactoryBot::Internal.sequences.registered?(name)
|
27
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
|
28
31
|
else
|
29
32
|
@factory.inherit_traits([name])
|
30
33
|
[]
|
@@ -6,7 +6,7 @@ module FactoryBot
|
|
6
6
|
@component = component
|
7
7
|
end
|
8
8
|
|
9
|
-
def method_missing(name, *args, &block) # rubocop:disable Style/
|
9
|
+
def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissingSuper
|
10
10
|
@component.send(name, *args, &block)
|
11
11
|
end
|
12
12
|
|
@@ -49,7 +49,7 @@ module FactoryBot
|
|
49
49
|
|
50
50
|
defined_traits.each do |defined_trait|
|
51
51
|
base_traits.each { |bt| bt.define_trait defined_trait }
|
52
|
-
additional_traits.each { |
|
52
|
+
additional_traits.each { |at| at.define_trait defined_trait }
|
53
53
|
end
|
54
54
|
|
55
55
|
@compiled = true
|
@@ -95,7 +95,7 @@ module FactoryBot
|
|
95
95
|
|
96
96
|
def callback(*names, &block)
|
97
97
|
names.each do |name|
|
98
|
-
FactoryBot.register_callback(name)
|
98
|
+
FactoryBot::Internal.register_callback(name)
|
99
99
|
add_callback(Callback.new(name, block))
|
100
100
|
end
|
101
101
|
end
|
@@ -111,17 +111,19 @@ module FactoryBot
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def trait_by_name(name)
|
114
|
-
trait_for(name) ||
|
114
|
+
trait_for(name) || Internal.trait_by_name(name)
|
115
115
|
end
|
116
116
|
|
117
117
|
def trait_for(name)
|
118
|
-
defined_traits.
|
118
|
+
@defined_traits_by_name ||= defined_traits.each_with_object({}) { |t, memo| memo[t.name] ||= t }
|
119
|
+
@defined_traits_by_name[name.to_s]
|
119
120
|
end
|
120
121
|
|
121
122
|
def initialize_copy(source)
|
122
123
|
super
|
123
124
|
@attributes = nil
|
124
125
|
@compiled = false
|
126
|
+
@defined_traits_by_name = nil
|
125
127
|
end
|
126
128
|
|
127
129
|
def aggregate_from_traits_and_self(method_name, &block)
|
@@ -88,15 +88,18 @@ module FactoryBot
|
|
88
88
|
# end
|
89
89
|
#
|
90
90
|
# are equivalent.
|
91
|
-
def method_missing(name, *args, &block) # rubocop:disable Style/
|
92
|
-
|
91
|
+
def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing, Style/MethodMissingSuper, Metrics/LineLength
|
92
|
+
association_options = args.first
|
93
|
+
|
94
|
+
if association_options.nil?
|
93
95
|
__declare_attribute__(name, block)
|
94
|
-
elsif
|
95
|
-
association(name,
|
96
|
+
elsif __valid_association_options?(association_options)
|
97
|
+
association(name, association_options)
|
96
98
|
else
|
97
|
-
raise NoMethodError.new(
|
98
|
-
|
99
|
-
|
99
|
+
raise NoMethodError.new(<<~MSG)
|
100
|
+
undefined method '#{name}' in '#{@definition.name}' factory
|
101
|
+
Did you mean? '#{name} { #{association_options.inspect} }'
|
102
|
+
MSG
|
100
103
|
end
|
101
104
|
end
|
102
105
|
|
@@ -117,9 +120,8 @@ module FactoryBot
|
|
117
120
|
#
|
118
121
|
# Except that no globally available sequence will be defined.
|
119
122
|
def sequence(name, *args, &block)
|
120
|
-
|
121
|
-
|
122
|
-
FactoryBot.register_sequence(sequence)
|
123
|
+
sequence = Sequence.new(name, *args, &block)
|
124
|
+
FactoryBot::Internal.register_inline_sequence(sequence)
|
123
125
|
add_attribute(name) { increment_sequence(sequence) }
|
124
126
|
end
|
125
127
|
|
@@ -188,5 +190,9 @@ module FactoryBot
|
|
188
190
|
add_attribute(name, &block)
|
189
191
|
end
|
190
192
|
end
|
193
|
+
|
194
|
+
def __valid_association_options?(options)
|
195
|
+
options.respond_to?(:has_key?) && options.has_key?(:factory)
|
196
|
+
end
|
191
197
|
end
|
192
198
|
end
|
data/lib/factory_bot/errors.rb
CHANGED
@@ -2,6 +2,9 @@ module FactoryBot
|
|
2
2
|
# Raised when a factory is defined that attempts to instantiate itself.
|
3
3
|
class AssociationDefinitionError < RuntimeError; end
|
4
4
|
|
5
|
+
# Raised when a trait is defined that references itself.
|
6
|
+
class TraitDefinitionError < RuntimeError; end
|
7
|
+
|
5
8
|
# Raised when a callback is defined that has an invalid name
|
6
9
|
class InvalidCallbackNameError < RuntimeError; end
|
7
10
|
|
@@ -37,7 +37,7 @@ module FactoryBot
|
|
37
37
|
@instance = object_instance
|
38
38
|
end
|
39
39
|
|
40
|
-
def method_missing(method_name, *args, &block) # rubocop:disable Style/
|
40
|
+
def method_missing(method_name, *args, &block) # rubocop:disable Style/MethodMissingSuper
|
41
41
|
if @instance.respond_to?(method_name)
|
42
42
|
@instance.send(method_name, *args, &block)
|
43
43
|
else
|
@@ -66,7 +66,7 @@ module FactoryBot
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def self.define_attribute(name, &block)
|
69
|
-
if
|
69
|
+
if instance_methods(false).include?(name) || private_instance_methods(false).include?(name)
|
70
70
|
undef_method(name)
|
71
71
|
end
|
72
72
|
|
data/lib/factory_bot/factory.rb
CHANGED
@@ -5,11 +5,11 @@ module FactoryBot
|
|
5
5
|
@strategy = strategy
|
6
6
|
|
7
7
|
@overrides = traits_and_overrides.extract_options!
|
8
|
-
@traits = traits_and_overrides
|
8
|
+
@traits = traits_and_overrides
|
9
9
|
end
|
10
10
|
|
11
11
|
def run(runner_strategy = @strategy, &block)
|
12
|
-
factory = FactoryBot.factory_by_name(@name)
|
12
|
+
factory = FactoryBot::Internal.factory_by_name(@name)
|
13
13
|
|
14
14
|
factory.compile
|
15
15
|
|