factory_bot 5.0.2 → 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/CONTRIBUTING.md +52 -13
- data/GETTING_STARTED.md +453 -78
- data/NEWS.md +40 -0
- data/README.md +17 -16
- data/lib/factory_bot.rb +21 -93
- 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 -1
- data/lib/factory_bot/attribute_assigner.rb +8 -9
- data/lib/factory_bot/attribute_list.rb +1 -1
- data/lib/factory_bot/callback.rb +2 -10
- data/lib/factory_bot/configuration.rb +6 -6
- data/lib/factory_bot/declaration.rb +1 -1
- data/lib/factory_bot/declaration/association.rb +30 -2
- data/lib/factory_bot/declaration/implicit.rb +4 -1
- 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 +10 -3
- data/lib/factory_bot/definition.rb +51 -18
- data/lib/factory_bot/definition_hierarchy.rb +1 -11
- data/lib/factory_bot/definition_proxy.rb +77 -12
- data/lib/factory_bot/enum.rb +27 -0
- data/lib/factory_bot/errors.rb +3 -0
- data/lib/factory_bot/evaluator.rb +20 -12
- data/lib/factory_bot/evaluator_class_definer.rb +1 -1
- data/lib/factory_bot/factory.rb +13 -13
- data/lib/factory_bot/factory_runner.rb +4 -4
- data/lib/factory_bot/find_definitions.rb +1 -1
- data/lib/factory_bot/internal.rb +68 -1
- data/lib/factory_bot/linter.rb +9 -13
- data/lib/factory_bot/null_factory.rb +10 -4
- data/lib/factory_bot/null_object.rb +2 -6
- data/lib/factory_bot/registry.rb +4 -4
- data/lib/factory_bot/reload.rb +1 -2
- data/lib/factory_bot/sequence.rb +5 -5
- data/lib/factory_bot/strategy/null.rb +4 -2
- data/lib/factory_bot/strategy/stub.rb +16 -5
- data/lib/factory_bot/strategy_calculator.rb +1 -1
- data/lib/factory_bot/strategy_syntax_method_registrar.rb +12 -1
- data/lib/factory_bot/syntax/default.rb +11 -23
- 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 +9 -36
@@ -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
|
@@ -145,7 +145,7 @@ module FactoryBot
|
|
145
145
|
|
146
146
|
def parent
|
147
147
|
if @parent
|
148
|
-
FactoryBot.factory_by_name(@parent)
|
148
|
+
FactoryBot::Internal.factory_by_name(@parent)
|
149
149
|
else
|
150
150
|
NullFactory.new
|
151
151
|
end
|
@@ -1,15 +1,15 @@
|
|
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)
|
12
|
-
factory = FactoryBot.factory_by_name(@name)
|
12
|
+
factory = FactoryBot::Internal.factory_by_name(@name)
|
13
13
|
|
14
14
|
factory.compile
|
15
15
|
|
@@ -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
@@ -2,7 +2,19 @@ module FactoryBot
|
|
2
2
|
# @api private
|
3
3
|
module Internal
|
4
4
|
class << self
|
5
|
-
delegate :
|
5
|
+
delegate :after,
|
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
|
6
18
|
|
7
19
|
def configuration
|
8
20
|
@configuration ||= Configuration.new
|
@@ -19,6 +31,61 @@ module FactoryBot
|
|
19
31
|
def rewind_inline_sequences
|
20
32
|
inline_sequences.each(&:rewind)
|
21
33
|
end
|
34
|
+
|
35
|
+
def register_trait(trait)
|
36
|
+
trait.names.each do |name|
|
37
|
+
traits.register(name, trait)
|
38
|
+
end
|
39
|
+
trait
|
40
|
+
end
|
41
|
+
|
42
|
+
def trait_by_name(name)
|
43
|
+
traits.find(name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def register_sequence(sequence)
|
47
|
+
sequence.names.each do |name|
|
48
|
+
sequences.register(name, sequence)
|
49
|
+
end
|
50
|
+
sequence
|
51
|
+
end
|
52
|
+
|
53
|
+
def sequence_by_name(name)
|
54
|
+
sequences.find(name)
|
55
|
+
end
|
56
|
+
|
57
|
+
def rewind_sequences
|
58
|
+
sequences.each(&:rewind)
|
59
|
+
rewind_inline_sequences
|
60
|
+
end
|
61
|
+
|
62
|
+
def register_factory(factory)
|
63
|
+
factory.names.each do |name|
|
64
|
+
factories.register(name, factory)
|
65
|
+
end
|
66
|
+
factory
|
67
|
+
end
|
68
|
+
|
69
|
+
def factory_by_name(name)
|
70
|
+
factories.find(name)
|
71
|
+
end
|
72
|
+
|
73
|
+
def register_strategy(strategy_name, strategy_class)
|
74
|
+
strategies.register(strategy_name, strategy_class)
|
75
|
+
StrategySyntaxMethodRegistrar.new(strategy_name).define_strategy_methods
|
76
|
+
end
|
77
|
+
|
78
|
+
def strategy_by_name(name)
|
79
|
+
strategies.find(name)
|
80
|
+
end
|
81
|
+
|
82
|
+
def register_default_strategies
|
83
|
+
register_strategy(:build, FactoryBot::Strategy::Build)
|
84
|
+
register_strategy(:create, FactoryBot::Strategy::Create)
|
85
|
+
register_strategy(:attributes_for, FactoryBot::Strategy::AttributesFor)
|
86
|
+
register_strategy(:build_stubbed, FactoryBot::Strategy::Stub)
|
87
|
+
register_strategy(:null, FactoryBot::Strategy::Null)
|
88
|
+
end
|
22
89
|
end
|
23
90
|
end
|
24
91
|
end
|
data/lib/factory_bot/linter.rb
CHANGED
@@ -19,17 +19,16 @@ module FactoryBot
|
|
19
19
|
attr_reader :factories_to_lint, :invalid_factories, :factory_strategy
|
20
20
|
|
21
21
|
def calculate_invalid_factories
|
22
|
-
factories_to_lint.
|
22
|
+
factories_to_lint.each_with_object(Hash.new([])) do |factory, result|
|
23
23
|
errors = lint(factory)
|
24
24
|
result[factory] |= errors unless errors.empty?
|
25
|
-
result
|
26
25
|
end
|
27
26
|
end
|
28
27
|
|
29
28
|
class FactoryError
|
30
29
|
def initialize(wrapped_error, factory)
|
31
30
|
@wrapped_error = wrapped_error
|
32
|
-
@factory
|
31
|
+
@factory = factory
|
33
32
|
end
|
34
33
|
|
35
34
|
def message
|
@@ -72,8 +71,8 @@ module FactoryBot
|
|
72
71
|
result = []
|
73
72
|
begin
|
74
73
|
FactoryBot.public_send(factory_strategy, factory.name)
|
75
|
-
rescue
|
76
|
-
result |= [FactoryError.new(
|
74
|
+
rescue => e
|
75
|
+
result |= [FactoryError.new(e, factory)]
|
77
76
|
end
|
78
77
|
result
|
79
78
|
end
|
@@ -81,20 +80,17 @@ module FactoryBot
|
|
81
80
|
def lint_traits(factory)
|
82
81
|
result = []
|
83
82
|
factory.definition.defined_traits.map(&:name).each do |trait_name|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
result |=
|
88
|
-
[FactoryTraitError.new(error, factory, trait_name)]
|
89
|
-
end
|
83
|
+
FactoryBot.public_send(factory_strategy, factory.name, trait_name)
|
84
|
+
rescue => e
|
85
|
+
result |= [FactoryTraitError.new(e, factory, trait_name)]
|
90
86
|
end
|
91
87
|
result
|
92
88
|
end
|
93
89
|
|
94
90
|
def error_message
|
95
|
-
lines = invalid_factories.map
|
91
|
+
lines = invalid_factories.map { |_factory, exceptions|
|
96
92
|
exceptions.map(&error_message_type)
|
97
|
-
|
93
|
+
}.flatten
|
98
94
|
|
99
95
|
<<~ERROR_MESSAGE.strip
|
100
96
|
The following factories are invalid:
|
@@ -10,12 +10,18 @@ module FactoryBot
|
|
10
10
|
delegate :defined_traits, :callbacks, :attributes, :constructor,
|
11
11
|
:to_create, to: :definition
|
12
12
|
|
13
|
-
def compile
|
13
|
+
def compile
|
14
|
+
end
|
14
15
|
|
15
|
-
def class_name
|
16
|
+
def class_name
|
17
|
+
end
|
16
18
|
|
17
|
-
def evaluator_class
|
19
|
+
def evaluator_class
|
20
|
+
FactoryBot::Evaluator
|
21
|
+
end
|
18
22
|
|
19
|
-
def hierarchy_class
|
23
|
+
def hierarchy_class
|
24
|
+
FactoryBot::DefinitionHierarchy
|
25
|
+
end
|
20
26
|
end
|
21
27
|
end
|
@@ -5,7 +5,7 @@ module FactoryBot
|
|
5
5
|
@methods_to_respond_to = methods_to_respond_to.map(&:to_s)
|
6
6
|
end
|
7
7
|
|
8
|
-
def method_missing(name, *args, &block)
|
8
|
+
def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing
|
9
9
|
if respond_to?(name)
|
10
10
|
nil
|
11
11
|
else
|
@@ -13,12 +13,8 @@ module FactoryBot
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
def respond_to?(method
|
16
|
+
def respond_to?(method)
|
17
17
|
@methods_to_respond_to.include? method.to_s
|
18
18
|
end
|
19
|
-
|
20
|
-
def respond_to_missing?(*)
|
21
|
-
false
|
22
|
-
end
|
23
19
|
end
|
24
20
|
end
|
data/lib/factory_bot/registry.rb
CHANGED
@@ -7,7 +7,7 @@ module FactoryBot
|
|
7
7
|
attr_reader :name
|
8
8
|
|
9
9
|
def initialize(name)
|
10
|
-
@name
|
10
|
+
@name = name
|
11
11
|
@items = ActiveSupport::HashWithIndifferentAccess.new
|
12
12
|
end
|
13
13
|
|
@@ -21,11 +21,11 @@ module FactoryBot
|
|
21
21
|
|
22
22
|
def find(name)
|
23
23
|
@items.fetch(name)
|
24
|
-
rescue KeyError =>
|
25
|
-
raise key_error_with_custom_message(
|
24
|
+
rescue KeyError => e
|
25
|
+
raise key_error_with_custom_message(e)
|
26
26
|
end
|
27
27
|
|
28
|
-
alias
|
28
|
+
alias [] find
|
29
29
|
|
30
30
|
def register(name, item)
|
31
31
|
@items[name] = item
|
data/lib/factory_bot/reload.rb
CHANGED
data/lib/factory_bot/sequence.rb
CHANGED
@@ -6,14 +6,14 @@ module FactoryBot
|
|
6
6
|
attr_reader :name
|
7
7
|
|
8
8
|
def initialize(name, *args, &proc)
|
9
|
-
@name
|
10
|
-
@proc
|
9
|
+
@name = name
|
10
|
+
@proc = proc
|
11
11
|
|
12
|
-
options
|
13
|
-
@value
|
12
|
+
options = args.extract_options!
|
13
|
+
@value = args.first || 1
|
14
14
|
@aliases = options.fetch(:aliases) { [] }
|
15
15
|
|
16
|
-
|
16
|
+
unless @value.respond_to?(:peek)
|
17
17
|
@value = EnumeratorAdapter.new(@value)
|
18
18
|
end
|
19
19
|
end
|
@@ -21,9 +21,13 @@ module FactoryBot
|
|
21
21
|
:update_attributes!,
|
22
22
|
:update_attributes,
|
23
23
|
:update_column,
|
24
|
-
:update_columns
|
24
|
+
:update_columns
|
25
25
|
].freeze
|
26
26
|
|
27
|
+
def self.next_id=(id)
|
28
|
+
@@next_id = id
|
29
|
+
end
|
30
|
+
|
27
31
|
def association(runner)
|
28
32
|
runner.run(:build_stubbed)
|
29
33
|
end
|
@@ -44,15 +48,17 @@ module FactoryBot
|
|
44
48
|
end
|
45
49
|
|
46
50
|
def stub_database_interaction_on_result(result_instance)
|
47
|
-
result_instance
|
51
|
+
if has_settable_id?(result_instance)
|
52
|
+
result_instance.id ||= next_id
|
53
|
+
end
|
48
54
|
|
49
55
|
result_instance.instance_eval do
|
50
56
|
def persisted?
|
51
|
-
|
57
|
+
true
|
52
58
|
end
|
53
59
|
|
54
60
|
def new_record?
|
55
|
-
|
61
|
+
false
|
56
62
|
end
|
57
63
|
|
58
64
|
def destroyed?
|
@@ -62,12 +68,17 @@ module FactoryBot
|
|
62
68
|
DISABLED_PERSISTENCE_METHODS.each do |write_method|
|
63
69
|
define_singleton_method(write_method) do |*args|
|
64
70
|
raise "stubbed models are not allowed to access the database - "\
|
65
|
-
"#{self.class}##{write_method}(#{args.join(
|
71
|
+
"#{self.class}##{write_method}(#{args.join(",")})"
|
66
72
|
end
|
67
73
|
end
|
68
74
|
end
|
69
75
|
end
|
70
76
|
|
77
|
+
def has_settable_id?(result_instance)
|
78
|
+
!result_instance.class.respond_to?(:primary_key) ||
|
79
|
+
result_instance.class.primary_key
|
80
|
+
end
|
81
|
+
|
71
82
|
def clear_changes_information(result_instance)
|
72
83
|
if result_instance.respond_to?(:clear_changes_information)
|
73
84
|
result_instance.clear_changes_information
|
@@ -11,6 +11,14 @@ module FactoryBot
|
|
11
11
|
define_pair_strategy_method
|
12
12
|
end
|
13
13
|
|
14
|
+
def self.with_index(block, index)
|
15
|
+
if block&.arity == 2
|
16
|
+
->(instance) { block.call(instance, index) }
|
17
|
+
else
|
18
|
+
block
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
14
22
|
private
|
15
23
|
|
16
24
|
def define_singular_strategy_method
|
@@ -29,7 +37,10 @@ module FactoryBot
|
|
29
37
|
raise ArgumentError, "count missing for #{strategy_name}_list"
|
30
38
|
end
|
31
39
|
|
32
|
-
Array.new(amount)
|
40
|
+
Array.new(amount) do |i|
|
41
|
+
block_with_index = StrategySyntaxMethodRegistrar.with_index(block, i)
|
42
|
+
send(strategy_name, name, *traits_and_overrides, &block_with_index)
|
43
|
+
end
|
33
44
|
end
|
34
45
|
end
|
35
46
|
|