factory_bot 4.10.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/.yardopts +1 -0
- data/CONTRIBUTING.md +52 -13
- data/GETTING_STARTED.md +613 -182
- data/LICENSE +1 -1
- data/NEWS.md +358 -0
- data/README.md +30 -26
- data/lib/factory_bot.rb +71 -140
- data/lib/factory_bot/aliases.rb +2 -2
- data/lib/factory_bot/attribute.rb +4 -39
- 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 +24 -10
- data/lib/factory_bot/attribute_list.rb +3 -2
- data/lib/factory_bot/callback.rb +3 -10
- data/lib/factory_bot/configuration.rb +15 -19
- data/lib/factory_bot/declaration.rb +5 -5
- data/lib/factory_bot/declaration/association.rb +14 -1
- data/lib/factory_bot/declaration/dynamic.rb +3 -1
- data/lib/factory_bot/declaration/implicit.rb +7 -2
- data/lib/factory_bot/declaration_list.rb +3 -3
- data/lib/factory_bot/decorator.rb +5 -5
- data/lib/factory_bot/decorator/attribute_hash.rb +1 -1
- data/lib/factory_bot/decorator/invocation_tracker.rb +1 -1
- data/lib/factory_bot/definition.rb +49 -20
- data/lib/factory_bot/definition_hierarchy.rb +1 -11
- data/lib/factory_bot/definition_proxy.rb +125 -43
- 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 +9 -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 -37
- 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.rb +2 -2
- data/lib/factory_bot/syntax/default.rb +12 -24
- data/lib/factory_bot/syntax/methods.rb +32 -9
- data/lib/factory_bot/trait.rb +7 -4
- data/lib/factory_bot/version.rb +1 -1
- metadata +50 -65
- data/.autotest +0 -9
- data/.gitignore +0 -10
- data/.rspec +0 -3
- data/.simplecov +0 -4
- data/.travis.yml +0 -58
- data/Appraisals +0 -23
- data/Gemfile +0 -8
- data/Gemfile.lock +0 -103
- data/NEWS +0 -298
- data/Rakefile +0 -36
- data/cucumber.yml +0 -1
- data/factory_bot.gemspec +0 -36
- data/factory_girl.gemspec +0 -40
- data/gemfiles/3.2.gemfile +0 -10
- data/gemfiles/3.2.gemfile.lock +0 -107
- data/gemfiles/4.0.gemfile +0 -10
- data/gemfiles/4.0.gemfile.lock +0 -107
- data/gemfiles/4.1.gemfile +0 -10
- data/gemfiles/4.1.gemfile.lock +0 -106
- data/gemfiles/4.2.gemfile +0 -10
- data/gemfiles/4.2.gemfile.lock +0 -106
- data/gemfiles/5.0.gemfile +0 -10
- data/gemfiles/5.0.gemfile.lock +0 -104
- data/gemfiles/5.1.gemfile +0 -10
- data/gemfiles/5.1.gemfile.lock +0 -104
- 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
- data/lib/factory_girl.rb +0 -5
@@ -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
|
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
|
|
@@ -11,12 +14,12 @@ module FactoryBot
|
|
11
14
|
# Raised when attempting to register a sequence from a dynamic attribute block
|
12
15
|
class SequenceAbuseError < RuntimeError; end
|
13
16
|
|
14
|
-
# Raised when defining an
|
15
|
-
# * Defining an attribute which has a name ending in "="
|
16
|
-
# * Defining an attribute with both a static and lazy value
|
17
|
-
# * Defining an attribute twice in the same factory
|
17
|
+
# Raised when defining an attribute twice in the same factory
|
18
18
|
class AttributeDefinitionError < RuntimeError; end
|
19
19
|
|
20
|
+
# Raised when attempting to pass a block to an association definition
|
21
|
+
class AssociationDefinitionError < RuntimeError; end
|
22
|
+
|
20
23
|
# Raised when a method is defined in a factory or trait with arguments
|
21
24
|
class MethodDefinitionError < RuntimeError; end
|
22
25
|
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "active_support/core_ext/hash/except"
|
2
|
+
require "active_support/core_ext/class/attribute"
|
3
3
|
|
4
4
|
module FactoryBot
|
5
5
|
# @api private
|
@@ -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,11 +33,9 @@ module FactoryBot
|
|
33
33
|
@build_strategy.association(runner)
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
@instance = object_instance
|
38
|
-
end
|
36
|
+
attr_writer :instance
|
39
37
|
|
40
|
-
def method_missing(method_name, *args, &block)
|
38
|
+
def method_missing(method_name, *args, &block) # rubocop:disable Style/MethodMissingSuper
|
41
39
|
if @instance.respond_to?(method_name)
|
42
40
|
@instance.send(method_name, *args, &block)
|
43
41
|
else
|
@@ -45,7 +43,7 @@ module FactoryBot
|
|
45
43
|
end
|
46
44
|
end
|
47
45
|
|
48
|
-
def respond_to_missing?(method_name,
|
46
|
+
def respond_to_missing?(method_name, _include_private = false)
|
49
47
|
@instance.respond_to?(method_name) || SyntaxRunner.new.respond_to?(method_name)
|
50
48
|
end
|
51
49
|
|
@@ -66,7 +64,7 @@ module FactoryBot
|
|
66
64
|
end
|
67
65
|
|
68
66
|
def self.define_attribute(name, &block)
|
69
|
-
if
|
67
|
+
if instance_methods(false).include?(name) || private_instance_methods(false).include?(name)
|
70
68
|
undef_method(name)
|
71
69
|
end
|
72
70
|
|
@@ -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
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "active_support/core_ext/hash/keys"
|
2
|
+
require "active_support/inflector"
|
3
3
|
|
4
4
|
module FactoryBot
|
5
5
|
# @api private
|
@@ -8,16 +8,16 @@ 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
|
@@ -84,14 +84,14 @@ 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
|
91
91
|
end
|
92
92
|
|
93
93
|
def with_traits(traits)
|
94
|
-
|
94
|
+
clone.tap do |factory_with_traits|
|
95
95
|
factory_with_traits.append_traits traits
|
96
96
|
end
|
97
97
|
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
|
|
@@ -25,7 +25,7 @@ module FactoryBot
|
|
25
25
|
factory: factory
|
26
26
|
}
|
27
27
|
|
28
|
-
ActiveSupport::Notifications.instrument(
|
28
|
+
ActiveSupport::Notifications.instrument("factory_bot.run_factory", instrumentation_payload) do
|
29
29
|
factory.run(runner_strategy, @overrides, &block)
|
30
30
|
end
|
31
31
|
end
|
@@ -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) }
|
@@ -16,7 +16,7 @@ module FactoryBot
|
|
16
16
|
load("#{path}.rb") if File.exist?("#{path}.rb")
|
17
17
|
|
18
18
|
if File.directory? path
|
19
|
-
Dir[File.join(path,
|
19
|
+
Dir[File.join(path, "**", "*.rb")].sort.each do |file|
|
20
20
|
load file
|
21
21
|
end
|
22
22
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module FactoryBot
|
2
|
+
# @api private
|
3
|
+
module Internal
|
4
|
+
class << self
|
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
|
18
|
+
|
19
|
+
def configuration
|
20
|
+
@configuration ||= Configuration.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def reset_configuration
|
24
|
+
@configuration = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def register_inline_sequence(sequence)
|
28
|
+
inline_sequences.push(sequence)
|
29
|
+
end
|
30
|
+
|
31
|
+
def rewind_inline_sequences
|
32
|
+
inline_sequences.each(&:rewind)
|
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
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/factory_bot/linter.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module FactoryBot
|
2
2
|
class Linter
|
3
|
-
|
4
|
-
def initialize(factories, linting_strategy, factory_strategy = :create)
|
3
|
+
def initialize(factories, strategy: :create, traits: false, verbose: false)
|
5
4
|
@factories_to_lint = factories
|
6
|
-
@
|
7
|
-
@
|
5
|
+
@factory_strategy = strategy
|
6
|
+
@traits = traits
|
7
|
+
@verbose = verbose
|
8
8
|
@invalid_factories = calculate_invalid_factories
|
9
9
|
end
|
10
10
|
|
@@ -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.
|
23
|
-
errors =
|
22
|
+
factories_to_lint.each_with_object(Hash.new([])) do |factory, result|
|
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
|
@@ -37,6 +36,13 @@ module FactoryBot
|
|
37
36
|
"* #{location} - #{message} (#{@wrapped_error.class.name})"
|
38
37
|
end
|
39
38
|
|
39
|
+
def verbose_message
|
40
|
+
<<~MESSAGE
|
41
|
+
#{message}
|
42
|
+
#{@wrapped_error.backtrace.join("\n ")}
|
43
|
+
MESSAGE
|
44
|
+
end
|
45
|
+
|
40
46
|
def location
|
41
47
|
@factory.name
|
42
48
|
end
|
@@ -53,12 +59,20 @@ module FactoryBot
|
|
53
59
|
end
|
54
60
|
end
|
55
61
|
|
62
|
+
def lint(factory)
|
63
|
+
if @traits
|
64
|
+
lint_factory(factory) + lint_traits(factory)
|
65
|
+
else
|
66
|
+
lint_factory(factory)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
56
70
|
def lint_factory(factory)
|
57
71
|
result = []
|
58
72
|
begin
|
59
73
|
FactoryBot.public_send(factory_strategy, factory.name)
|
60
|
-
rescue =>
|
61
|
-
result |= [FactoryError.new(
|
74
|
+
rescue => e
|
75
|
+
result |= [FactoryError.new(e, factory)]
|
62
76
|
end
|
63
77
|
result
|
64
78
|
end
|
@@ -66,32 +80,31 @@ module FactoryBot
|
|
66
80
|
def lint_traits(factory)
|
67
81
|
result = []
|
68
82
|
factory.definition.defined_traits.map(&:name).each do |trait_name|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
result |=
|
73
|
-
[FactoryTraitError.new(error, factory, trait_name)]
|
74
|
-
end
|
83
|
+
FactoryBot.public_send(factory_strategy, factory.name, trait_name)
|
84
|
+
rescue => e
|
85
|
+
result |= [FactoryTraitError.new(e, factory, trait_name)]
|
75
86
|
end
|
76
87
|
result
|
77
88
|
end
|
78
89
|
|
79
|
-
def lint_factory_and_traits(factory)
|
80
|
-
errors = lint_factory(factory)
|
81
|
-
errors |= lint_traits(factory)
|
82
|
-
errors
|
83
|
-
end
|
84
|
-
|
85
90
|
def error_message
|
86
|
-
lines = invalid_factories.map
|
87
|
-
exceptions.map(
|
88
|
-
|
91
|
+
lines = invalid_factories.map { |_factory, exceptions|
|
92
|
+
exceptions.map(&error_message_type)
|
93
|
+
}.flatten
|
89
94
|
|
90
|
-
|
91
|
-
The following factories are invalid:
|
95
|
+
<<~ERROR_MESSAGE.strip
|
96
|
+
The following factories are invalid:
|
92
97
|
|
93
|
-
#{lines.join("\n")}
|
98
|
+
#{lines.join("\n")}
|
94
99
|
ERROR_MESSAGE
|
95
100
|
end
|
101
|
+
|
102
|
+
def error_message_type
|
103
|
+
if @verbose
|
104
|
+
:verbose_message
|
105
|
+
else
|
106
|
+
:message
|
107
|
+
end
|
108
|
+
end
|
96
109
|
end
|
97
110
|
end
|
@@ -10,9 +10,18 @@ module FactoryBot
|
|
10
10
|
delegate :defined_traits, :callbacks, :attributes, :constructor,
|
11
11
|
:to_create, to: :definition
|
12
12
|
|
13
|
-
def compile
|
14
|
-
|
15
|
-
|
16
|
-
def
|
13
|
+
def compile
|
14
|
+
end
|
15
|
+
|
16
|
+
def class_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def evaluator_class
|
20
|
+
FactoryBot::Evaluator
|
21
|
+
end
|
22
|
+
|
23
|
+
def hierarchy_class
|
24
|
+
FactoryBot::DefinitionHierarchy
|
25
|
+
end
|
17
26
|
end
|
18
27
|
end
|