ffactory_girl 4.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.autotest +9 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.simplecov +4 -0
- data/.travis.yml +38 -0
- data/.yardopts +5 -0
- data/Appraisals +19 -0
- data/CONTRIBUTING.md +60 -0
- data/GETTING_STARTED.md +1391 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +116 -0
- data/LICENSE +19 -0
- data/NAME.md +11 -0
- data/NEWS +290 -0
- data/README.md +98 -0
- data/Rakefile +36 -0
- data/cucumber.yml +1 -0
- data/factory_girl.gemspec +36 -0
- data/gemfiles/3.2.gemfile +10 -0
- data/gemfiles/3.2.gemfile.lock +105 -0
- data/gemfiles/4.0.gemfile +10 -0
- data/gemfiles/4.0.gemfile.lock +105 -0
- data/gemfiles/4.1.gemfile +10 -0
- data/gemfiles/4.1.gemfile.lock +104 -0
- data/gemfiles/4.2.gemfile +10 -0
- data/gemfiles/4.2.gemfile.lock +104 -0
- data/gemfiles/5.0.gemfile +10 -0
- data/gemfiles/5.0.gemfile.lock +103 -0
- data/lib/factory_girl/aliases.rb +18 -0
- data/lib/factory_girl/attribute/association.rb +27 -0
- data/lib/factory_girl/attribute/dynamic.rb +24 -0
- data/lib/factory_girl/attribute/sequence.rb +16 -0
- data/lib/factory_girl/attribute/static.rb +16 -0
- data/lib/factory_girl/attribute.rb +62 -0
- data/lib/factory_girl/attribute_assigner.rb +97 -0
- data/lib/factory_girl/attribute_list.rb +67 -0
- data/lib/factory_girl/callback.rb +40 -0
- data/lib/factory_girl/callbacks_observer.rb +21 -0
- data/lib/factory_girl/configuration.rb +37 -0
- data/lib/factory_girl/declaration/association.rb +28 -0
- data/lib/factory_girl/declaration/dynamic.rb +26 -0
- data/lib/factory_girl/declaration/implicit.rb +33 -0
- data/lib/factory_girl/declaration/static.rb +26 -0
- data/lib/factory_girl/declaration.rb +23 -0
- data/lib/factory_girl/declaration_list.rb +49 -0
- data/lib/factory_girl/decorator/attribute_hash.rb +16 -0
- data/lib/factory_girl/decorator/class_key_hash.rb +28 -0
- data/lib/factory_girl/decorator/disallows_duplicates_registry.rb +13 -0
- data/lib/factory_girl/decorator/invocation_tracker.rb +19 -0
- data/lib/factory_girl/decorator/new_constructor.rb +12 -0
- data/lib/factory_girl/decorator.rb +21 -0
- data/lib/factory_girl/definition.rb +136 -0
- data/lib/factory_girl/definition_hierarchy.rb +48 -0
- data/lib/factory_girl/definition_proxy.rb +174 -0
- data/lib/factory_girl/errors.rb +25 -0
- data/lib/factory_girl/evaluation.rb +27 -0
- data/lib/factory_girl/evaluator.rb +82 -0
- data/lib/factory_girl/evaluator_class_definer.rb +20 -0
- data/lib/factory_girl/factory.rb +163 -0
- data/lib/factory_girl/factory_runner.rb +33 -0
- data/lib/factory_girl/find_definitions.rb +25 -0
- data/lib/factory_girl/linter.rb +97 -0
- data/lib/factory_girl/null_factory.rb +18 -0
- data/lib/factory_girl/null_object.rb +24 -0
- data/lib/factory_girl/registry.rb +38 -0
- data/lib/factory_girl/reload.rb +8 -0
- data/lib/factory_girl/sequence.rb +62 -0
- data/lib/factory_girl/strategy/attributes_for.rb +13 -0
- data/lib/factory_girl/strategy/build.rb +15 -0
- data/lib/factory_girl/strategy/create.rb +18 -0
- data/lib/factory_girl/strategy/null.rb +11 -0
- data/lib/factory_girl/strategy/stub.rb +111 -0
- data/lib/factory_girl/strategy_calculator.rb +26 -0
- data/lib/factory_girl/strategy_syntax_method_registrar.rb +54 -0
- data/lib/factory_girl/syntax/default.rb +76 -0
- data/lib/factory_girl/syntax/methods.rb +111 -0
- data/lib/factory_girl/syntax.rb +7 -0
- data/lib/factory_girl/syntax_runner.rb +6 -0
- data/lib/factory_girl/trait.rb +30 -0
- data/lib/factory_girl/version.rb +3 -0
- data/lib/factory_girl.rb +156 -0
- metadata +271 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
# @api private
|
3
|
+
class Definition
|
4
|
+
attr_reader :defined_traits, :declarations
|
5
|
+
|
6
|
+
def initialize(name = nil, base_traits = [])
|
7
|
+
@declarations = DeclarationList.new(name)
|
8
|
+
@callbacks = []
|
9
|
+
@defined_traits = Set.new
|
10
|
+
@to_create = nil
|
11
|
+
@base_traits = base_traits
|
12
|
+
@additional_traits = []
|
13
|
+
@constructor = nil
|
14
|
+
@attributes = nil
|
15
|
+
@compiled = false
|
16
|
+
end
|
17
|
+
|
18
|
+
delegate :declare_attribute, to: :declarations
|
19
|
+
|
20
|
+
def attributes
|
21
|
+
@attributes ||= AttributeList.new.tap do |attribute_list|
|
22
|
+
attribute_lists = aggregate_from_traits_and_self(:attributes) { declarations.attributes }
|
23
|
+
attribute_lists.each do |attributes|
|
24
|
+
attribute_list.apply_attributes attributes
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_create(&block)
|
30
|
+
if block_given?
|
31
|
+
@to_create = block
|
32
|
+
else
|
33
|
+
aggregate_from_traits_and_self(:to_create) { @to_create }.last
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def constructor
|
38
|
+
aggregate_from_traits_and_self(:constructor) { @constructor }.last
|
39
|
+
end
|
40
|
+
|
41
|
+
def callbacks
|
42
|
+
aggregate_from_traits_and_self(:callbacks) { @callbacks }
|
43
|
+
end
|
44
|
+
|
45
|
+
def compile
|
46
|
+
unless @compiled
|
47
|
+
declarations.attributes
|
48
|
+
|
49
|
+
defined_traits.each do |defined_trait|
|
50
|
+
base_traits.each { |bt| bt.define_trait defined_trait }
|
51
|
+
additional_traits.each { |bt| bt.define_trait defined_trait }
|
52
|
+
end
|
53
|
+
|
54
|
+
@compiled = true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def overridable
|
59
|
+
declarations.overridable
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def inherit_traits(new_traits)
|
64
|
+
@base_traits += new_traits
|
65
|
+
end
|
66
|
+
|
67
|
+
def append_traits(new_traits)
|
68
|
+
@additional_traits += new_traits
|
69
|
+
end
|
70
|
+
|
71
|
+
def add_callback(callback)
|
72
|
+
@callbacks << callback
|
73
|
+
end
|
74
|
+
|
75
|
+
def skip_create
|
76
|
+
@to_create = ->(instance) { }
|
77
|
+
end
|
78
|
+
|
79
|
+
def define_trait(trait)
|
80
|
+
@defined_traits.add(trait)
|
81
|
+
end
|
82
|
+
|
83
|
+
def define_constructor(&block)
|
84
|
+
@constructor = block
|
85
|
+
end
|
86
|
+
|
87
|
+
def before(*names, &block)
|
88
|
+
callback(*names.map { |name| "before_#{name}" }, &block)
|
89
|
+
end
|
90
|
+
|
91
|
+
def after(*names, &block)
|
92
|
+
callback(*names.map { |name| "after_#{name}" }, &block)
|
93
|
+
end
|
94
|
+
|
95
|
+
def callback(*names, &block)
|
96
|
+
names.each do |name|
|
97
|
+
FactoryGirl.register_callback(name)
|
98
|
+
add_callback(Callback.new(name, block))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def base_traits
|
105
|
+
@base_traits.map { |name| trait_by_name(name) }
|
106
|
+
end
|
107
|
+
|
108
|
+
def additional_traits
|
109
|
+
@additional_traits.map { |name| trait_by_name(name) }
|
110
|
+
end
|
111
|
+
|
112
|
+
def trait_by_name(name)
|
113
|
+
trait_for(name) || FactoryGirl.trait_by_name(name)
|
114
|
+
end
|
115
|
+
|
116
|
+
def trait_for(name)
|
117
|
+
defined_traits.detect { |trait| trait.name == name }
|
118
|
+
end
|
119
|
+
|
120
|
+
def initialize_copy(source)
|
121
|
+
super
|
122
|
+
@attributes = nil
|
123
|
+
@compiled = false
|
124
|
+
end
|
125
|
+
|
126
|
+
def aggregate_from_traits_and_self(method_name, &block)
|
127
|
+
compile
|
128
|
+
|
129
|
+
[
|
130
|
+
base_traits.map(&method_name),
|
131
|
+
instance_exec(&block),
|
132
|
+
additional_traits.map(&method_name),
|
133
|
+
].flatten.compact
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class DefinitionHierarchy
|
3
|
+
def callbacks
|
4
|
+
FactoryGirl.callbacks
|
5
|
+
end
|
6
|
+
|
7
|
+
def constructor
|
8
|
+
FactoryGirl.constructor
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_create
|
12
|
+
FactoryGirl.to_create
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.build_from_definition(definition)
|
16
|
+
build_to_create(&definition.to_create)
|
17
|
+
build_constructor(&definition.constructor)
|
18
|
+
add_callbacks definition.callbacks
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.add_callbacks(callbacks)
|
22
|
+
if callbacks.any?
|
23
|
+
define_method :callbacks do
|
24
|
+
super() + callbacks
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
private_class_method :add_callbacks
|
29
|
+
|
30
|
+
def self.build_constructor(&block)
|
31
|
+
if block
|
32
|
+
define_method(:constructor) do
|
33
|
+
block
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
private_class_method :build_constructor
|
38
|
+
|
39
|
+
def self.build_to_create(&block)
|
40
|
+
if block
|
41
|
+
define_method(:to_create) do
|
42
|
+
block
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
private_class_method :build_to_create
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class DefinitionProxy
|
3
|
+
UNPROXIED_METHODS = %w(__send__ __id__ nil? send object_id extend instance_eval initialize block_given? raise caller method)
|
4
|
+
|
5
|
+
(instance_methods + private_instance_methods).each do |method|
|
6
|
+
undef_method(method) unless UNPROXIED_METHODS.include?(method.to_s)
|
7
|
+
end
|
8
|
+
|
9
|
+
delegate :before, :after, :callback, to: :@definition
|
10
|
+
|
11
|
+
attr_reader :child_factories
|
12
|
+
|
13
|
+
def initialize(definition, ignore = false)
|
14
|
+
@definition = definition
|
15
|
+
@ignore = ignore
|
16
|
+
@child_factories = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def singleton_method_added(name)
|
20
|
+
message = "Defining methods in blocks (trait or factory) is not supported (#{name})"
|
21
|
+
raise FactoryGirl::MethodDefinitionError, message
|
22
|
+
end
|
23
|
+
|
24
|
+
# Adds an attribute that should be assigned on generated instances for this
|
25
|
+
# factory.
|
26
|
+
#
|
27
|
+
# This method should be called with either a value or block, but not both. If
|
28
|
+
# called with a block, the attribute will be generated "lazily," whenever an
|
29
|
+
# instance is generated. Lazy attribute blocks will not be called if that
|
30
|
+
# attribute is overridden for a specific instance.
|
31
|
+
#
|
32
|
+
# When defining lazy attributes, an instance of FactoryGirl::Strategy will
|
33
|
+
# be yielded, allowing associations to be built using the correct build
|
34
|
+
# strategy.
|
35
|
+
#
|
36
|
+
# Arguments:
|
37
|
+
# * name: +Symbol+ or +String+
|
38
|
+
# The name of this attribute. This will be assigned using "name=" for
|
39
|
+
# generated instances.
|
40
|
+
# * value: +Object+
|
41
|
+
# If no block is given, this value will be used for this attribute.
|
42
|
+
def add_attribute(name, value = nil, &block)
|
43
|
+
raise AttributeDefinitionError, 'Both value and block given' if value && block_given?
|
44
|
+
|
45
|
+
declaration = if block_given?
|
46
|
+
Declaration::Dynamic.new(name, @ignore, block)
|
47
|
+
else
|
48
|
+
Declaration::Static.new(name, value, @ignore)
|
49
|
+
end
|
50
|
+
|
51
|
+
@definition.declare_attribute(declaration)
|
52
|
+
end
|
53
|
+
|
54
|
+
def ignore(&block)
|
55
|
+
ActiveSupport::Deprecation.warn "`#ignore` is deprecated and will be "\
|
56
|
+
"removed in 5.0. Please use `#transient` instead."
|
57
|
+
transient(&block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def transient(&block)
|
61
|
+
proxy = DefinitionProxy.new(@definition, true)
|
62
|
+
proxy.instance_eval(&block)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Calls add_attribute using the missing method name as the name of the
|
66
|
+
# attribute, so that:
|
67
|
+
#
|
68
|
+
# factory :user do
|
69
|
+
# name 'Billy Idol'
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# and:
|
73
|
+
#
|
74
|
+
# factory :user do
|
75
|
+
# add_attribute :name, 'Billy Idol'
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# are equivalent.
|
79
|
+
#
|
80
|
+
# If no argument or block is given, factory_girl will look for a sequence
|
81
|
+
# or association with the same name. This means that:
|
82
|
+
#
|
83
|
+
# factory :user do
|
84
|
+
# email { create(:email) }
|
85
|
+
# association :account
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# and:
|
89
|
+
#
|
90
|
+
# factory :user do
|
91
|
+
# email
|
92
|
+
# account
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# are equivalent.
|
96
|
+
def method_missing(name, *args, &block)
|
97
|
+
if args.empty? && block.nil?
|
98
|
+
@definition.declare_attribute(Declaration::Implicit.new(name, @definition, @ignore))
|
99
|
+
elsif args.first.respond_to?(:has_key?) && args.first.has_key?(:factory)
|
100
|
+
association(name, *args)
|
101
|
+
else
|
102
|
+
add_attribute(name, *args, &block)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Adds an attribute that will have unique values generated by a sequence with
|
107
|
+
# a specified format.
|
108
|
+
#
|
109
|
+
# The result of:
|
110
|
+
# factory :user do
|
111
|
+
# sequence(:email) { |n| "person#{n}@example.com" }
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# Is equal to:
|
115
|
+
# sequence(:email) { |n| "person#{n}@example.com" }
|
116
|
+
#
|
117
|
+
# factory :user do
|
118
|
+
# email { FactoryGirl.generate(:email) }
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
# Except that no globally available sequence will be defined.
|
122
|
+
def sequence(name, *args, &block)
|
123
|
+
sequence = Sequence.new(name, *args, &block)
|
124
|
+
add_attribute(name) { increment_sequence(sequence) }
|
125
|
+
end
|
126
|
+
|
127
|
+
# Adds an attribute that builds an association. The associated instance will
|
128
|
+
# be built using the same build strategy as the parent instance.
|
129
|
+
#
|
130
|
+
# Example:
|
131
|
+
# factory :user do
|
132
|
+
# name 'Joey'
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# factory :post do
|
136
|
+
# association :author, factory: :user
|
137
|
+
# end
|
138
|
+
#
|
139
|
+
# Arguments:
|
140
|
+
# * name: +Symbol+
|
141
|
+
# The name of this attribute.
|
142
|
+
# * options: +Hash+
|
143
|
+
#
|
144
|
+
# Options:
|
145
|
+
# * factory: +Symbol+ or +String+
|
146
|
+
# The name of the factory to use when building the associated instance.
|
147
|
+
# If no name is given, the name of the attribute is assumed to be the
|
148
|
+
# name of the factory. For example, a "user" association will by
|
149
|
+
# default use the "user" factory.
|
150
|
+
def association(name, *options)
|
151
|
+
@definition.declare_attribute(Declaration::Association.new(name, *options))
|
152
|
+
end
|
153
|
+
|
154
|
+
def to_create(&block)
|
155
|
+
@definition.to_create(&block)
|
156
|
+
end
|
157
|
+
|
158
|
+
def skip_create
|
159
|
+
@definition.skip_create
|
160
|
+
end
|
161
|
+
|
162
|
+
def factory(name, options = {}, &block)
|
163
|
+
@child_factories << [name, options, block]
|
164
|
+
end
|
165
|
+
|
166
|
+
def trait(name, &block)
|
167
|
+
@definition.define_trait(Trait.new(name, &block))
|
168
|
+
end
|
169
|
+
|
170
|
+
def initialize_with(&block)
|
171
|
+
@definition.define_constructor(&block)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
# Raised when a factory is defined that attempts to instantiate itself.
|
3
|
+
class AssociationDefinitionError < RuntimeError; end
|
4
|
+
|
5
|
+
# Raised when a callback is defined that has an invalid name
|
6
|
+
class InvalidCallbackNameError < RuntimeError; end
|
7
|
+
|
8
|
+
# Raised when a factory is defined with the same name as a previously-defined factory.
|
9
|
+
class DuplicateDefinitionError < RuntimeError; end
|
10
|
+
|
11
|
+
# Raised when attempting to register a sequence from a dynamic attribute block
|
12
|
+
class SequenceAbuseError < RuntimeError; end
|
13
|
+
|
14
|
+
# Raised when defining an invalid attribute:
|
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
|
18
|
+
class AttributeDefinitionError < RuntimeError; end
|
19
|
+
|
20
|
+
# Raised when a method is defined in a factory or trait with arguments
|
21
|
+
class MethodDefinitionError < RuntimeError; end
|
22
|
+
|
23
|
+
# Raised when any factory is considered invalid
|
24
|
+
class InvalidFactoryError < RuntimeError; end
|
25
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'observer'
|
2
|
+
|
3
|
+
module FactoryGirl
|
4
|
+
class Evaluation
|
5
|
+
include Observable
|
6
|
+
|
7
|
+
def initialize(evaluator, attribute_assigner, to_create)
|
8
|
+
@evaluator = evaluator
|
9
|
+
@attribute_assigner = attribute_assigner
|
10
|
+
@to_create = to_create
|
11
|
+
end
|
12
|
+
|
13
|
+
delegate :object, :hash, to: :@attribute_assigner
|
14
|
+
|
15
|
+
def create(result_instance)
|
16
|
+
case @to_create.arity
|
17
|
+
when 2 then @to_create[result_instance, @evaluator]
|
18
|
+
else @to_create[result_instance]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def notify(name, result_instance)
|
23
|
+
changed
|
24
|
+
notify_observers(name, result_instance)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'active_support/core_ext/hash/except'
|
2
|
+
require 'active_support/core_ext/class/attribute'
|
3
|
+
|
4
|
+
module FactoryGirl
|
5
|
+
# @api private
|
6
|
+
class Evaluator
|
7
|
+
class_attribute :attribute_lists
|
8
|
+
|
9
|
+
private_instance_methods.each do |method|
|
10
|
+
undef_method(method) unless method =~ /^__|initialize/
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(build_strategy, overrides = {})
|
14
|
+
@build_strategy = build_strategy
|
15
|
+
@overrides = overrides
|
16
|
+
@cached_attributes = overrides
|
17
|
+
@instance = nil
|
18
|
+
|
19
|
+
@overrides.each do |name, value|
|
20
|
+
singleton_class.define_attribute(name) { value }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def association(factory_name, *traits_and_overrides)
|
25
|
+
overrides = traits_and_overrides.extract_options!
|
26
|
+
strategy_override = overrides.fetch(:strategy) do
|
27
|
+
FactoryGirl.use_parent_strategy ? @build_strategy.class : :create
|
28
|
+
end
|
29
|
+
|
30
|
+
traits_and_overrides += [overrides.except(:strategy)]
|
31
|
+
|
32
|
+
runner = FactoryRunner.new(factory_name, strategy_override, traits_and_overrides)
|
33
|
+
@build_strategy.association(runner)
|
34
|
+
end
|
35
|
+
|
36
|
+
def instance=(object_instance)
|
37
|
+
@instance = object_instance
|
38
|
+
end
|
39
|
+
|
40
|
+
def method_missing(method_name, *args, &block)
|
41
|
+
if @instance.respond_to?(method_name)
|
42
|
+
@instance.send(method_name, *args, &block)
|
43
|
+
else
|
44
|
+
SyntaxRunner.new.send(method_name, *args, &block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def respond_to_missing?(method_name, include_private = false)
|
49
|
+
@instance.respond_to?(method_name) || SyntaxRunner.new.respond_to?(method_name)
|
50
|
+
end
|
51
|
+
|
52
|
+
def __override_names__
|
53
|
+
@overrides.keys
|
54
|
+
end
|
55
|
+
|
56
|
+
def increment_sequence(sequence)
|
57
|
+
sequence.next(self)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.attribute_list
|
61
|
+
AttributeList.new.tap do |list|
|
62
|
+
attribute_lists.each do |attribute_list|
|
63
|
+
list.apply_attributes attribute_list.to_a
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.define_attribute(name, &block)
|
69
|
+
if method_defined?(name) || private_method_defined?(name)
|
70
|
+
undef_method(name)
|
71
|
+
end
|
72
|
+
|
73
|
+
define_method(name) do
|
74
|
+
if @cached_attributes.key?(name)
|
75
|
+
@cached_attributes[name]
|
76
|
+
else
|
77
|
+
@cached_attributes[name] = instance_exec(&block)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
# @api private
|
3
|
+
class EvaluatorClassDefiner
|
4
|
+
def initialize(attributes, parent_class)
|
5
|
+
@parent_class = parent_class
|
6
|
+
@attributes = attributes
|
7
|
+
|
8
|
+
attributes.each do |attribute|
|
9
|
+
evaluator_class.define_attribute(attribute.name, &attribute.to_proc)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def evaluator_class
|
14
|
+
@evaluator_class ||= Class.new(@parent_class).tap do |klass|
|
15
|
+
klass.attribute_lists ||= []
|
16
|
+
klass.attribute_lists += [@attributes]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'active_support/core_ext/hash/keys'
|
2
|
+
require 'active_support/inflector'
|
3
|
+
|
4
|
+
module FactoryGirl
|
5
|
+
# @api private
|
6
|
+
class Factory
|
7
|
+
attr_reader :name, :definition
|
8
|
+
|
9
|
+
def initialize(name, options = {})
|
10
|
+
assert_valid_options(options)
|
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
|
+
end
|
18
|
+
|
19
|
+
delegate :add_callback, :declare_attribute, :to_create, :define_trait, :constructor,
|
20
|
+
:defined_traits, :inherit_traits, :append_traits, to: :@definition
|
21
|
+
|
22
|
+
def build_class
|
23
|
+
@build_class ||= if class_name.is_a? Class
|
24
|
+
class_name
|
25
|
+
else
|
26
|
+
class_name.to_s.camelize.constantize
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def run(build_strategy, overrides, &block)
|
31
|
+
block ||= ->(result) { result }
|
32
|
+
compile
|
33
|
+
|
34
|
+
strategy = StrategyCalculator.new(build_strategy).strategy.new
|
35
|
+
|
36
|
+
evaluator = evaluator_class.new(strategy, overrides.symbolize_keys)
|
37
|
+
attribute_assigner = AttributeAssigner.new(evaluator, build_class, &compiled_constructor)
|
38
|
+
|
39
|
+
evaluation =
|
40
|
+
Evaluation.new(evaluator, attribute_assigner, compiled_to_create)
|
41
|
+
evaluation.add_observer(CallbacksObserver.new(callbacks, evaluator))
|
42
|
+
|
43
|
+
strategy.result(evaluation).tap(&block)
|
44
|
+
end
|
45
|
+
|
46
|
+
def human_names
|
47
|
+
names.map { |name| name.to_s.humanize.downcase }
|
48
|
+
end
|
49
|
+
|
50
|
+
def associations
|
51
|
+
evaluator_class.attribute_list.associations
|
52
|
+
end
|
53
|
+
|
54
|
+
# Names for this factory, including aliases.
|
55
|
+
#
|
56
|
+
# Example:
|
57
|
+
#
|
58
|
+
# factory :user, aliases: [:author] do
|
59
|
+
# # ...
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# FactoryGirl.create(:author).class
|
63
|
+
# # => User
|
64
|
+
#
|
65
|
+
# Because an attribute defined without a value or block will build an
|
66
|
+
# association with the same name, this allows associations to be defined
|
67
|
+
# without factories, such as:
|
68
|
+
#
|
69
|
+
# factory :user, aliases: [:author] do
|
70
|
+
# # ...
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# factory :post do
|
74
|
+
# author
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# FactoryGirl.create(:post).author.class
|
78
|
+
# # => User
|
79
|
+
def names
|
80
|
+
[name] + @aliases
|
81
|
+
end
|
82
|
+
|
83
|
+
def compile
|
84
|
+
unless @compiled
|
85
|
+
parent.compile
|
86
|
+
parent.defined_traits.each { |trait| define_trait(trait) }
|
87
|
+
@definition.compile
|
88
|
+
build_hierarchy
|
89
|
+
@compiled = true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def with_traits(traits)
|
94
|
+
self.clone.tap do |factory_with_traits|
|
95
|
+
factory_with_traits.append_traits traits
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
protected
|
100
|
+
|
101
|
+
def class_name
|
102
|
+
@class_name || parent.class_name || name
|
103
|
+
end
|
104
|
+
|
105
|
+
def evaluator_class
|
106
|
+
@evaluator_class ||= EvaluatorClassDefiner.new(attributes, parent.evaluator_class).evaluator_class
|
107
|
+
end
|
108
|
+
|
109
|
+
def attributes
|
110
|
+
compile
|
111
|
+
AttributeList.new(@name).tap do |list|
|
112
|
+
list.apply_attributes definition.attributes
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def hierarchy_class
|
117
|
+
@hierarchy_class ||= Class.new(parent.hierarchy_class)
|
118
|
+
end
|
119
|
+
|
120
|
+
def hierarchy_instance
|
121
|
+
@hierarchy_instance ||= hierarchy_class.new
|
122
|
+
end
|
123
|
+
|
124
|
+
def build_hierarchy
|
125
|
+
hierarchy_class.build_from_definition definition
|
126
|
+
end
|
127
|
+
|
128
|
+
def callbacks
|
129
|
+
hierarchy_instance.callbacks
|
130
|
+
end
|
131
|
+
|
132
|
+
def compiled_to_create
|
133
|
+
hierarchy_instance.to_create
|
134
|
+
end
|
135
|
+
|
136
|
+
def compiled_constructor
|
137
|
+
hierarchy_instance.constructor
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def assert_valid_options(options)
|
143
|
+
options.assert_valid_keys(:class, :parent, :aliases, :traits)
|
144
|
+
end
|
145
|
+
|
146
|
+
def parent
|
147
|
+
if @parent
|
148
|
+
FactoryGirl.factory_by_name(@parent)
|
149
|
+
else
|
150
|
+
NullFactory.new
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def initialize_copy(source)
|
155
|
+
super
|
156
|
+
@definition = @definition.clone
|
157
|
+
@evaluator_class = nil
|
158
|
+
@hierarchy_class = nil
|
159
|
+
@hierarchy_instance = nil
|
160
|
+
@compiled = false
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class FactoryRunner
|
3
|
+
def initialize(name, strategy, traits_and_overrides)
|
4
|
+
@name = name
|
5
|
+
@strategy = strategy
|
6
|
+
|
7
|
+
@overrides = traits_and_overrides.extract_options!
|
8
|
+
@traits = traits_and_overrides
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(runner_strategy = @strategy, &block)
|
12
|
+
factory = FactoryGirl.factory_by_name(@name)
|
13
|
+
|
14
|
+
factory.compile
|
15
|
+
|
16
|
+
if @traits.any?
|
17
|
+
factory = factory.with_traits(@traits)
|
18
|
+
end
|
19
|
+
|
20
|
+
instrumentation_payload = {
|
21
|
+
name: @name,
|
22
|
+
strategy: runner_strategy,
|
23
|
+
traits: @traits,
|
24
|
+
overrides: @overrides,
|
25
|
+
factory: factory
|
26
|
+
}
|
27
|
+
|
28
|
+
ActiveSupport::Notifications.instrument('factory_girl.run_factory', instrumentation_payload) do
|
29
|
+
factory.run(runner_strategy, @overrides, &block)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|