factory_bot 1.0.0.alpha
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 +7 -0
- data/.autotest +9 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.simplecov +4 -0
- data/.travis.yml +41 -0
- data/.yardopts +5 -0
- data/Appraisals +19 -0
- data/CONTRIBUTING.md +60 -0
- data/GETTING_STARTED.md +1387 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +123 -0
- data/LICENSE +19 -0
- data/NAME.md +11 -0
- data/NEWS +285 -0
- data/README.md +102 -0
- data/Rakefile +36 -0
- data/cucumber.yml +1 -0
- data/factory_bot.gemspec +38 -0
- data/factory_girl.gemspec +41 -0
- data/gemfiles/3.2.gemfile +10 -0
- data/gemfiles/3.2.gemfile.lock +112 -0
- data/gemfiles/4.0.gemfile +10 -0
- data/gemfiles/4.0.gemfile.lock +112 -0
- data/gemfiles/4.1.gemfile +10 -0
- data/gemfiles/4.1.gemfile.lock +111 -0
- data/gemfiles/4.2.gemfile +10 -0
- data/gemfiles/4.2.gemfile.lock +111 -0
- data/gemfiles/5.0.gemfile +10 -0
- data/gemfiles/5.0.gemfile.lock +110 -0
- data/lib/factory_bot.rb +154 -0
- data/lib/factory_bot/aliases.rb +18 -0
- data/lib/factory_bot/attribute.rb +62 -0
- data/lib/factory_bot/attribute/association.rb +27 -0
- data/lib/factory_bot/attribute/dynamic.rb +24 -0
- data/lib/factory_bot/attribute/sequence.rb +16 -0
- data/lib/factory_bot/attribute/static.rb +16 -0
- data/lib/factory_bot/attribute_assigner.rb +97 -0
- data/lib/factory_bot/attribute_list.rb +67 -0
- data/lib/factory_bot/callback.rb +40 -0
- data/lib/factory_bot/callbacks_observer.rb +21 -0
- data/lib/factory_bot/configuration.rb +37 -0
- data/lib/factory_bot/declaration.rb +23 -0
- data/lib/factory_bot/declaration/association.rb +28 -0
- data/lib/factory_bot/declaration/dynamic.rb +26 -0
- data/lib/factory_bot/declaration/implicit.rb +33 -0
- data/lib/factory_bot/declaration/static.rb +26 -0
- data/lib/factory_bot/declaration_list.rb +49 -0
- data/lib/factory_bot/decorator.rb +21 -0
- data/lib/factory_bot/decorator/attribute_hash.rb +16 -0
- data/lib/factory_bot/decorator/class_key_hash.rb +28 -0
- data/lib/factory_bot/decorator/disallows_duplicates_registry.rb +13 -0
- data/lib/factory_bot/decorator/invocation_tracker.rb +19 -0
- data/lib/factory_bot/decorator/new_constructor.rb +12 -0
- data/lib/factory_bot/definition.rb +136 -0
- data/lib/factory_bot/definition_hierarchy.rb +48 -0
- data/lib/factory_bot/definition_proxy.rb +174 -0
- data/lib/factory_bot/errors.rb +25 -0
- data/lib/factory_bot/evaluation.rb +23 -0
- data/lib/factory_bot/evaluator.rb +82 -0
- data/lib/factory_bot/evaluator_class_definer.rb +20 -0
- data/lib/factory_bot/factory.rb +162 -0
- data/lib/factory_bot/factory_runner.rb +33 -0
- data/lib/factory_bot/find_definitions.rb +25 -0
- data/lib/factory_bot/linter.rb +97 -0
- data/lib/factory_bot/null_factory.rb +18 -0
- data/lib/factory_bot/null_object.rb +24 -0
- data/lib/factory_bot/registry.rb +38 -0
- data/lib/factory_bot/reload.rb +8 -0
- data/lib/factory_bot/sequence.rb +62 -0
- data/lib/factory_bot/strategy/attributes_for.rb +13 -0
- data/lib/factory_bot/strategy/build.rb +15 -0
- data/lib/factory_bot/strategy/create.rb +18 -0
- data/lib/factory_bot/strategy/null.rb +11 -0
- data/lib/factory_bot/strategy/stub.rb +108 -0
- data/lib/factory_bot/strategy_calculator.rb +26 -0
- data/lib/factory_bot/strategy_syntax_method_registrar.rb +54 -0
- data/lib/factory_bot/syntax.rb +7 -0
- data/lib/factory_bot/syntax/default.rb +76 -0
- data/lib/factory_bot/syntax/methods.rb +111 -0
- data/lib/factory_bot/syntax_runner.rb +6 -0
- data/lib/factory_bot/trait.rb +30 -0
- data/lib/factory_bot/version.rb +3 -0
- data/lib/factory_girl.rb +5 -0
- data/lib/factory_girl/version.rb +1 -0
- metadata +302 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module FactoryBot
|
2
|
+
class << self
|
3
|
+
attr_accessor :aliases
|
4
|
+
end
|
5
|
+
|
6
|
+
self.aliases = [
|
7
|
+
[/(.+)_id/, '\1'],
|
8
|
+
[/(.*)/, '\1_id']
|
9
|
+
]
|
10
|
+
|
11
|
+
def self.aliases_for(attribute)
|
12
|
+
aliases.map do |(pattern, replace)|
|
13
|
+
if pattern.match(attribute.to_s)
|
14
|
+
attribute.to_s.sub(pattern, replace).to_sym
|
15
|
+
end
|
16
|
+
end.compact << attribute
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'factory_bot/attribute/static'
|
2
|
+
require 'factory_bot/attribute/dynamic'
|
3
|
+
require 'factory_bot/attribute/association'
|
4
|
+
require 'factory_bot/attribute/sequence'
|
5
|
+
|
6
|
+
module FactoryBot
|
7
|
+
# @api private
|
8
|
+
class Attribute
|
9
|
+
attr_reader :name, :ignored
|
10
|
+
|
11
|
+
def initialize(name, ignored)
|
12
|
+
@name = name.to_sym
|
13
|
+
@ignored = ignored
|
14
|
+
ensure_non_attribute_writer!
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_proc
|
18
|
+
-> { }
|
19
|
+
end
|
20
|
+
|
21
|
+
def association?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def alias_for?(attr)
|
26
|
+
FactoryBot.aliases_for(attr).include?(name)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def ensure_non_attribute_writer!
|
32
|
+
NonAttributeWriterValidator.new(@name).validate!
|
33
|
+
end
|
34
|
+
|
35
|
+
class NonAttributeWriterValidator
|
36
|
+
def initialize(method_name)
|
37
|
+
@method_name = method_name.to_s
|
38
|
+
@method_name_setter_match = @method_name.match(/(.*)=$/)
|
39
|
+
end
|
40
|
+
|
41
|
+
def validate!
|
42
|
+
if method_is_writer?
|
43
|
+
raise AttributeDefinitionError, error_message
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def method_is_writer?
|
50
|
+
!!@method_name_setter_match
|
51
|
+
end
|
52
|
+
|
53
|
+
def attribute_name
|
54
|
+
@method_name_setter_match[1]
|
55
|
+
end
|
56
|
+
|
57
|
+
def error_message
|
58
|
+
"factory_bot uses '#{attribute_name} value' syntax rather than '#{attribute_name} = value'"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module FactoryBot
|
2
|
+
class Attribute
|
3
|
+
# @api private
|
4
|
+
class Association < Attribute
|
5
|
+
attr_reader :factory
|
6
|
+
|
7
|
+
def initialize(name, factory, overrides)
|
8
|
+
super(name, false)
|
9
|
+
@factory = factory
|
10
|
+
@overrides = overrides
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_proc
|
14
|
+
factory = @factory
|
15
|
+
overrides = @overrides
|
16
|
+
traits_and_overrides = [factory, overrides].flatten
|
17
|
+
factory_name = traits_and_overrides.shift
|
18
|
+
|
19
|
+
-> { association(factory_name, *traits_and_overrides) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def association?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module FactoryBot
|
2
|
+
class Attribute
|
3
|
+
# @api private
|
4
|
+
class Dynamic < Attribute
|
5
|
+
def initialize(name, ignored, block)
|
6
|
+
super(name, ignored)
|
7
|
+
@block = block
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_proc
|
11
|
+
block = @block
|
12
|
+
|
13
|
+
-> {
|
14
|
+
value = case block.arity
|
15
|
+
when 1, -1 then instance_exec(self, &block)
|
16
|
+
else instance_exec(&block)
|
17
|
+
end
|
18
|
+
raise SequenceAbuseError if FactoryBot::Sequence === value
|
19
|
+
value
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module FactoryBot
|
2
|
+
class Attribute
|
3
|
+
# @api private
|
4
|
+
class Sequence < Attribute
|
5
|
+
def initialize(name, sequence, ignored)
|
6
|
+
super(name, ignored)
|
7
|
+
@sequence = sequence
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_proc
|
11
|
+
sequence = @sequence
|
12
|
+
-> { FactoryBot.generate(sequence) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module FactoryBot
|
2
|
+
# @api private
|
3
|
+
class AttributeAssigner
|
4
|
+
def initialize(evaluator, build_class, &instance_builder)
|
5
|
+
@build_class = build_class
|
6
|
+
@instance_builder = instance_builder
|
7
|
+
@evaluator = evaluator
|
8
|
+
@attribute_list = evaluator.class.attribute_list
|
9
|
+
@attribute_names_assigned = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def object
|
13
|
+
@evaluator.instance = build_class_instance
|
14
|
+
build_class_instance.tap do |instance|
|
15
|
+
attributes_to_set_on_instance.each do |attribute|
|
16
|
+
instance.public_send("#{attribute}=", get(attribute))
|
17
|
+
@attribute_names_assigned << attribute
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def hash
|
23
|
+
@evaluator.instance = build_hash
|
24
|
+
|
25
|
+
attributes_to_set_on_hash.inject({}) do |result, attribute|
|
26
|
+
result[attribute] = get(attribute)
|
27
|
+
result
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def method_tracking_evaluator
|
34
|
+
@method_tracking_evaluator ||= Decorator::AttributeHash.new(decorated_evaluator, attribute_names_to_assign)
|
35
|
+
end
|
36
|
+
|
37
|
+
def decorated_evaluator
|
38
|
+
Decorator::InvocationTracker.new(
|
39
|
+
Decorator::NewConstructor.new(@evaluator, @build_class)
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def methods_invoked_on_evaluator
|
44
|
+
method_tracking_evaluator.__invoked_methods__
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_class_instance
|
48
|
+
@build_class_instance ||= method_tracking_evaluator.instance_exec(&@instance_builder)
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_hash
|
52
|
+
@build_hash ||= NullObject.new(hash_instance_methods_to_respond_to)
|
53
|
+
end
|
54
|
+
|
55
|
+
def get(attribute_name)
|
56
|
+
@evaluator.send(attribute_name)
|
57
|
+
end
|
58
|
+
|
59
|
+
def attributes_to_set_on_instance
|
60
|
+
(attribute_names_to_assign - @attribute_names_assigned - methods_invoked_on_evaluator).uniq
|
61
|
+
end
|
62
|
+
|
63
|
+
def attributes_to_set_on_hash
|
64
|
+
attribute_names_to_assign - association_names
|
65
|
+
end
|
66
|
+
|
67
|
+
def attribute_names_to_assign
|
68
|
+
@attribute_names_to_assign ||= non_ignored_attribute_names + override_names - ignored_attribute_names - alias_names_to_ignore
|
69
|
+
end
|
70
|
+
|
71
|
+
def non_ignored_attribute_names
|
72
|
+
@attribute_list.non_ignored.names
|
73
|
+
end
|
74
|
+
|
75
|
+
def ignored_attribute_names
|
76
|
+
@attribute_list.ignored.names
|
77
|
+
end
|
78
|
+
|
79
|
+
def association_names
|
80
|
+
@attribute_list.associations.names
|
81
|
+
end
|
82
|
+
|
83
|
+
def override_names
|
84
|
+
@evaluator.__override_names__
|
85
|
+
end
|
86
|
+
|
87
|
+
def hash_instance_methods_to_respond_to
|
88
|
+
@attribute_list.names + override_names + @build_class.instance_methods
|
89
|
+
end
|
90
|
+
|
91
|
+
def alias_names_to_ignore
|
92
|
+
@attribute_list.non_ignored.flat_map do |attribute|
|
93
|
+
override_names.map { |override| attribute.name if attribute.alias_for?(override) && attribute.name != override && !ignored_attribute_names.include?(override) }
|
94
|
+
end.compact
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module FactoryBot
|
2
|
+
# @api private
|
3
|
+
class AttributeList
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize(name = nil, attributes = [])
|
7
|
+
@name = name
|
8
|
+
@attributes = attributes
|
9
|
+
end
|
10
|
+
|
11
|
+
def define_attribute(attribute)
|
12
|
+
ensure_attribute_not_self_referencing! attribute
|
13
|
+
ensure_attribute_not_defined! attribute
|
14
|
+
|
15
|
+
add_attribute attribute
|
16
|
+
end
|
17
|
+
|
18
|
+
def each(&block)
|
19
|
+
@attributes.each(&block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def names
|
23
|
+
map(&:name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def associations
|
27
|
+
AttributeList.new(@name, select(&:association?))
|
28
|
+
end
|
29
|
+
|
30
|
+
def ignored
|
31
|
+
AttributeList.new(@name, select(&:ignored))
|
32
|
+
end
|
33
|
+
|
34
|
+
def non_ignored
|
35
|
+
AttributeList.new(@name, reject(&:ignored))
|
36
|
+
end
|
37
|
+
|
38
|
+
def apply_attributes(attributes_to_apply)
|
39
|
+
attributes_to_apply.each { |attribute| add_attribute(attribute) }
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def add_attribute(attribute)
|
45
|
+
@attributes << attribute
|
46
|
+
attribute
|
47
|
+
end
|
48
|
+
|
49
|
+
def ensure_attribute_not_defined!(attribute)
|
50
|
+
if attribute_defined?(attribute.name)
|
51
|
+
raise AttributeDefinitionError, "Attribute already defined: #{attribute.name}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def ensure_attribute_not_self_referencing!(attribute)
|
56
|
+
if attribute.respond_to?(:factory) && attribute.factory == @name
|
57
|
+
raise AssociationDefinitionError, "Self-referencing association '#{attribute.name}' in '#{attribute.factory}'"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def attribute_defined?(attribute_name)
|
62
|
+
@attributes.any? do |attribute|
|
63
|
+
attribute.name == attribute_name
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module FactoryBot
|
2
|
+
class Callback
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(name, block)
|
6
|
+
@name = name.to_sym
|
7
|
+
@block = block
|
8
|
+
ensure_valid_callback_name!
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(instance, evaluator)
|
12
|
+
case block.arity
|
13
|
+
when 1, -1 then syntax_runner.instance_exec(instance, &block)
|
14
|
+
when 2 then syntax_runner.instance_exec(instance, evaluator, &block)
|
15
|
+
else syntax_runner.instance_exec(&block)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def ==(other)
|
20
|
+
name == other.name &&
|
21
|
+
block == other.block
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
attr_reader :block
|
26
|
+
|
27
|
+
private
|
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
|
+
def syntax_runner
|
37
|
+
@syntax_runner ||= SyntaxRunner.new
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module FactoryBot
|
2
|
+
# @api private
|
3
|
+
class CallbacksObserver
|
4
|
+
def initialize(callbacks, evaluator)
|
5
|
+
@callbacks = callbacks
|
6
|
+
@evaluator = evaluator
|
7
|
+
end
|
8
|
+
|
9
|
+
def update(name, result_instance)
|
10
|
+
callbacks_by_name(name).each do |callback|
|
11
|
+
callback.run(result_instance, @evaluator)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def callbacks_by_name(name)
|
18
|
+
@callbacks.select { |callback| callback.name == name }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module FactoryBot
|
2
|
+
# @api private
|
3
|
+
class Configuration
|
4
|
+
attr_reader :factories, :sequences, :traits, :strategies, :callback_names
|
5
|
+
|
6
|
+
attr_accessor :allow_class_lookup, :use_parent_strategy
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@factories = Decorator::DisallowsDuplicatesRegistry.new(Registry.new('Factory'))
|
10
|
+
@sequences = Decorator::DisallowsDuplicatesRegistry.new(Registry.new('Sequence'))
|
11
|
+
@traits = Decorator::DisallowsDuplicatesRegistry.new(Registry.new('Trait'))
|
12
|
+
@strategies = Registry.new('Strategy')
|
13
|
+
@callback_names = Set.new
|
14
|
+
@definition = Definition.new
|
15
|
+
|
16
|
+
@allow_class_lookup = true
|
17
|
+
|
18
|
+
to_create { |instance| instance.save! }
|
19
|
+
initialize_with { new }
|
20
|
+
end
|
21
|
+
|
22
|
+
delegate :to_create, :skip_create, :constructor, :before, :after,
|
23
|
+
:callback, :callbacks, to: :@definition
|
24
|
+
|
25
|
+
def initialize_with(&block)
|
26
|
+
@definition.define_constructor(&block)
|
27
|
+
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
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'factory_bot/declaration/static'
|
2
|
+
require 'factory_bot/declaration/dynamic'
|
3
|
+
require 'factory_bot/declaration/association'
|
4
|
+
require 'factory_bot/declaration/implicit'
|
5
|
+
|
6
|
+
module FactoryBot
|
7
|
+
# @api private
|
8
|
+
class Declaration
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
def initialize(name, ignored = false)
|
12
|
+
@name = name
|
13
|
+
@ignored = ignored
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_attributes
|
17
|
+
build
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
attr_reader :ignored
|
22
|
+
end
|
23
|
+
end
|