factory_girl 2.3.2 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +5 -3
- data/Appraisals +4 -0
- data/CONTRIBUTION_GUIDELINES.md +1 -1
- data/Changelog +13 -0
- data/GETTING_STARTED.md +362 -286
- data/Gemfile.lock +1 -1
- data/README.md +8 -8
- data/features/factory_girl_steps.feature +4 -0
- data/features/support/factories.rb +19 -0
- data/gemfiles/2.1.gemfile.lock +1 -1
- data/gemfiles/2.3.gemfile.lock +1 -1
- data/gemfiles/3.0.gemfile.lock +1 -1
- data/gemfiles/3.1.gemfile.lock +1 -1
- data/gemfiles/3.2.gemfile +7 -0
- data/gemfiles/3.2.gemfile.lock +90 -0
- data/lib/factory_girl.rb +4 -0
- data/lib/factory_girl/attribute.rb +1 -9
- data/lib/factory_girl/attribute/association.rb +2 -2
- data/lib/factory_girl/attribute/dynamic.rb +2 -2
- data/lib/factory_girl/attribute/sequence.rb +1 -1
- data/lib/factory_girl/attribute/static.rb +1 -1
- data/lib/factory_girl/attribute_assigner.rb +73 -0
- data/lib/factory_girl/attribute_list.rb +6 -15
- data/lib/factory_girl/callback.rb +2 -2
- data/lib/factory_girl/evaluator.rb +57 -0
- data/lib/factory_girl/evaluator_class_definer.rb +34 -0
- data/lib/factory_girl/factory.rb +31 -80
- data/lib/factory_girl/null_factory.rb +2 -1
- data/lib/factory_girl/null_object.rb +19 -0
- data/lib/factory_girl/proxy.rb +6 -87
- data/lib/factory_girl/proxy/attributes_for.rb +2 -7
- data/lib/factory_girl/proxy/build.rb +4 -3
- data/lib/factory_girl/proxy/create.rb +6 -7
- data/lib/factory_girl/proxy/stub.rb +19 -18
- data/lib/factory_girl/step_definitions.rb +4 -3
- data/lib/factory_girl/version.rb +1 -1
- data/spec/acceptance/aliases_spec.rb +19 -0
- data/spec/acceptance/attributes_for_spec.rb +10 -1
- data/spec/acceptance/attributes_from_instance_spec.rb +53 -0
- data/spec/acceptance/build_spec.rb +8 -0
- data/spec/acceptance/build_stubbed_spec.rb +9 -0
- data/spec/acceptance/create_spec.rb +8 -0
- data/spec/factory_girl/attribute/association_spec.rb +5 -4
- data/spec/factory_girl/attribute/dynamic_spec.rb +9 -10
- data/spec/factory_girl/attribute/sequence_spec.rb +1 -2
- data/spec/factory_girl/attribute/static_spec.rb +1 -2
- data/spec/factory_girl/attribute_list_spec.rb +14 -4
- data/spec/factory_girl/evaluator_class_definer_spec.rb +54 -0
- data/spec/factory_girl/factory_spec.rb +23 -16
- data/spec/factory_girl/null_factory_spec.rb +2 -1
- data/spec/factory_girl/null_object_spec.rb +8 -0
- data/spec/factory_girl/proxy/attributes_for_spec.rb +8 -24
- data/spec/factory_girl/proxy/build_spec.rb +1 -7
- data/spec/factory_girl/proxy/create_spec.rb +2 -27
- data/spec/factory_girl/proxy/stub_spec.rb +14 -13
- data/spec/factory_girl/proxy_spec.rb +1 -33
- data/spec/support/shared_examples/proxy.rb +22 -45
- metadata +133 -177
@@ -0,0 +1,34 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class EvaluatorClassDefiner
|
3
|
+
def initialize(attributes, callbacks, parent_class)
|
4
|
+
@parent_class = parent_class
|
5
|
+
@callbacks = callbacks
|
6
|
+
@attributes = attributes
|
7
|
+
|
8
|
+
attributes.each do |attribute|
|
9
|
+
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.callbacks ||= []
|
16
|
+
klass.callbacks += @callbacks
|
17
|
+
klass.attribute_lists ||= []
|
18
|
+
klass.attribute_lists += [@attributes]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def define_attribute(attribute_name, attribute_proc)
|
25
|
+
evaluator_class.send(:define_method, attribute_name) do
|
26
|
+
if @cached_attributes.key?(attribute_name)
|
27
|
+
@cached_attributes[attribute_name]
|
28
|
+
else
|
29
|
+
@cached_attributes[attribute_name] = instance_exec(&attribute_proc)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/factory_girl/factory.rb
CHANGED
@@ -13,6 +13,7 @@ module FactoryGirl
|
|
13
13
|
@class_name = options[:class]
|
14
14
|
@default_strategy = options[:default_strategy]
|
15
15
|
@definition = Definition.new(@name)
|
16
|
+
@compiled = false
|
16
17
|
|
17
18
|
inherit_traits(options[:traits] || [])
|
18
19
|
end
|
@@ -26,25 +27,27 @@ module FactoryGirl
|
|
26
27
|
end
|
27
28
|
|
28
29
|
def build_class #:nodoc:
|
29
|
-
class_name.
|
30
|
+
@build_class ||= if class_name.is_a? Class
|
31
|
+
class_name
|
32
|
+
else
|
33
|
+
class_name.to_s.camelize.constantize
|
34
|
+
end
|
30
35
|
end
|
31
36
|
|
32
37
|
def default_strategy #:nodoc:
|
33
|
-
@default_strategy || parent.default_strategy
|
38
|
+
@default_strategy || parent.default_strategy
|
34
39
|
end
|
35
40
|
|
36
41
|
def run(proxy_class, overrides, &block) #:nodoc:
|
37
42
|
block ||= lambda {|result| result }
|
43
|
+
compile
|
38
44
|
|
39
|
-
|
40
|
-
:attributes => attributes,
|
41
|
-
:callbacks => callbacks,
|
42
|
-
:to_create => to_create,
|
43
|
-
:build_class => build_class,
|
44
|
-
:proxy_class => proxy_class
|
45
|
-
}
|
45
|
+
proxy = proxy_class.new
|
46
46
|
|
47
|
-
|
47
|
+
evaluator = evaluator_class.new(proxy, overrides.symbolize_keys)
|
48
|
+
attribute_assigner = AttributeAssigner.new(build_class, evaluator)
|
49
|
+
|
50
|
+
proxy.result(attribute_assigner, to_create).tap(&block)
|
48
51
|
end
|
49
52
|
|
50
53
|
def human_names
|
@@ -52,7 +55,7 @@ module FactoryGirl
|
|
52
55
|
end
|
53
56
|
|
54
57
|
def associations
|
55
|
-
attributes.
|
58
|
+
attributes.associations
|
56
59
|
end
|
57
60
|
|
58
61
|
# Names for this factory, including aliases.
|
@@ -85,9 +88,12 @@ module FactoryGirl
|
|
85
88
|
end
|
86
89
|
|
87
90
|
def compile
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
+
unless @compiled
|
92
|
+
parent.defined_traits.each {|trait| define_trait(trait) }
|
93
|
+
parent.compile
|
94
|
+
@definition.compile
|
95
|
+
@compiled = true
|
96
|
+
end
|
91
97
|
end
|
92
98
|
|
93
99
|
def with_traits(traits)
|
@@ -102,24 +108,29 @@ module FactoryGirl
|
|
102
108
|
@class_name || parent.class_name || name
|
103
109
|
end
|
104
110
|
|
111
|
+
def evaluator_class
|
112
|
+
@evaluator_class ||= EvaluatorClassDefiner.new(attributes, callbacks, parent.evaluator_class).evaluator_class
|
113
|
+
end
|
114
|
+
|
105
115
|
def attributes
|
106
116
|
compile
|
107
117
|
AttributeList.new(@name).tap do |list|
|
108
|
-
|
109
|
-
list.apply_attributes
|
118
|
+
processing_order.each do |factory|
|
119
|
+
list.apply_attributes factory.attributes
|
110
120
|
end
|
111
|
-
|
112
|
-
list.apply_attributes(@definition.attributes)
|
113
|
-
list.apply_attributes(parent.attributes)
|
114
121
|
end
|
115
122
|
end
|
116
123
|
|
117
124
|
def callbacks
|
118
|
-
|
125
|
+
processing_order.map {|factory| factory.callbacks }.flatten
|
119
126
|
end
|
120
127
|
|
121
128
|
private
|
122
129
|
|
130
|
+
def processing_order
|
131
|
+
[traits.reverse, @definition].flatten
|
132
|
+
end
|
133
|
+
|
123
134
|
def assert_valid_options(options)
|
124
135
|
options.assert_valid_keys(:class, :parent, :default_strategy, :aliases, :traits)
|
125
136
|
|
@@ -142,65 +153,5 @@ module FactoryGirl
|
|
142
153
|
super
|
143
154
|
@definition = @definition.clone
|
144
155
|
end
|
145
|
-
|
146
|
-
class Runner
|
147
|
-
def initialize(options = {})
|
148
|
-
@attributes = options[:attributes]
|
149
|
-
@callbacks = options[:callbacks]
|
150
|
-
@to_create = options[:to_create]
|
151
|
-
@build_class = options[:build_class]
|
152
|
-
@proxy_class = options[:proxy_class]
|
153
|
-
|
154
|
-
@overrides = {}
|
155
|
-
end
|
156
|
-
|
157
|
-
def run(overrides = {})
|
158
|
-
@overrides = overrides.symbolize_keys
|
159
|
-
|
160
|
-
apply_attributes
|
161
|
-
apply_remaining_overrides
|
162
|
-
|
163
|
-
proxy.result(@to_create)
|
164
|
-
end
|
165
|
-
|
166
|
-
private
|
167
|
-
|
168
|
-
def apply_attributes
|
169
|
-
@attributes.each do |attribute|
|
170
|
-
if overrides_for_attribute(attribute).any?
|
171
|
-
handle_attribute_with_overrides(attribute)
|
172
|
-
else
|
173
|
-
handle_attribute_without_overrides(attribute)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
def apply_remaining_overrides
|
179
|
-
@overrides.each { |attr, val| add_static_attribute(attr, val) }
|
180
|
-
end
|
181
|
-
|
182
|
-
def overrides_for_attribute(attribute)
|
183
|
-
@overrides.select { |attr, val| attribute.alias_for?(attr) }
|
184
|
-
end
|
185
|
-
|
186
|
-
def handle_attribute_with_overrides(attribute)
|
187
|
-
overrides_for_attribute(attribute).each do |attr, val|
|
188
|
-
add_static_attribute(attr, val, attribute.ignored)
|
189
|
-
@overrides.delete(attr)
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
def add_static_attribute(attr, val, ignored = false)
|
194
|
-
Attribute::Static.new(attr, val, ignored).add_to(proxy)
|
195
|
-
end
|
196
|
-
|
197
|
-
def handle_attribute_without_overrides(attribute)
|
198
|
-
attribute.add_to(proxy)
|
199
|
-
end
|
200
|
-
|
201
|
-
def proxy
|
202
|
-
@proxy ||= @proxy_class.new(@build_class, @callbacks)
|
203
|
-
end
|
204
|
-
end
|
205
156
|
end
|
206
157
|
end
|
@@ -9,7 +9,8 @@ module FactoryGirl
|
|
9
9
|
delegate :defined_traits, :callbacks, :attributes, :to => :definition
|
10
10
|
|
11
11
|
def compile; end
|
12
|
-
def default_strategy; end
|
13
12
|
def class_name; end
|
13
|
+
def default_strategy; :create; end
|
14
|
+
def evaluator_class; FactoryGirl::Evaluator; end
|
14
15
|
end
|
15
16
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
if defined?(::BasicObject)
|
3
|
+
class NullObject < ::BasicObject
|
4
|
+
def method_missing(*args)
|
5
|
+
nil
|
6
|
+
end
|
7
|
+
end
|
8
|
+
else
|
9
|
+
class NullObject
|
10
|
+
instance_methods.each do |m|
|
11
|
+
undef_method(m) if m.to_s !~ /(?:^__|^nil\?$|^send$|^object_id$)/
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(*args)
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/factory_girl/proxy.rb
CHANGED
@@ -3,23 +3,11 @@ require "factory_girl/proxy/build"
|
|
3
3
|
require "factory_girl/proxy/create"
|
4
4
|
require "factory_girl/proxy/attributes_for"
|
5
5
|
require "factory_girl/proxy/stub"
|
6
|
+
require "observer"
|
6
7
|
|
7
8
|
module FactoryGirl
|
8
9
|
class Proxy #:nodoc:
|
9
|
-
|
10
|
-
@callbacks = process_callbacks(callbacks)
|
11
|
-
@proxy = ObjectWrapper.new(klass)
|
12
|
-
end
|
13
|
-
|
14
|
-
delegate :get, :set, :set_ignored, :to => :@proxy
|
15
|
-
|
16
|
-
def run_callbacks(name)
|
17
|
-
if @callbacks[name]
|
18
|
-
@callbacks[name].each do |callback|
|
19
|
-
callback.run(result_instance, self)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
10
|
+
include Observable
|
23
11
|
|
24
12
|
# Generates an association using the current build strategy.
|
25
13
|
#
|
@@ -56,7 +44,7 @@ module FactoryGirl
|
|
56
44
|
def association(name, overrides = {})
|
57
45
|
end
|
58
46
|
|
59
|
-
def result(to_create)
|
47
|
+
def result(attribute_assigner, to_create)
|
60
48
|
raise NotImplementedError, "Strategies must return a result"
|
61
49
|
end
|
62
50
|
|
@@ -68,78 +56,9 @@ module FactoryGirl
|
|
68
56
|
|
69
57
|
private
|
70
58
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
def process_callbacks(callbacks)
|
76
|
-
callbacks.inject({}) do |result, callback|
|
77
|
-
result[callback.name] ||= []
|
78
|
-
result[callback.name] << callback
|
79
|
-
result
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def result_instance
|
84
|
-
@proxy.object
|
85
|
-
end
|
86
|
-
|
87
|
-
def result_hash
|
88
|
-
@proxy.to_hash
|
89
|
-
end
|
90
|
-
|
91
|
-
class ObjectWrapper
|
92
|
-
def initialize(klass)
|
93
|
-
@klass = klass
|
94
|
-
@attributes = []
|
95
|
-
@cached_attribute_values = {}
|
96
|
-
end
|
97
|
-
|
98
|
-
def to_hash
|
99
|
-
@attributes.inject({}) do |result, attribute|
|
100
|
-
result[attribute] = get(attribute)
|
101
|
-
result
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def object
|
106
|
-
@object ||= @klass.new
|
107
|
-
assign_object_attributes
|
108
|
-
@object
|
109
|
-
end
|
110
|
-
|
111
|
-
def set(attribute, value)
|
112
|
-
define_attribute(attribute, value)
|
113
|
-
@attributes << attribute.name
|
114
|
-
end
|
115
|
-
|
116
|
-
def set_ignored(attribute, value)
|
117
|
-
define_attribute(attribute, value)
|
118
|
-
end
|
119
|
-
|
120
|
-
def get(attribute)
|
121
|
-
@cached_attribute_values[attribute] ||= anonymous_instance.send(attribute)
|
122
|
-
end
|
123
|
-
|
124
|
-
private
|
125
|
-
|
126
|
-
def define_attribute(attribute, value)
|
127
|
-
anonymous_class.send(:define_method, attribute.name, value)
|
128
|
-
end
|
129
|
-
|
130
|
-
def assign_object_attributes
|
131
|
-
(@attributes - @cached_attribute_values.keys).each do |attribute|
|
132
|
-
@object.send("#{attribute}=", get(attribute))
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def anonymous_class
|
137
|
-
@anonymous_class ||= Class.new
|
138
|
-
end
|
139
|
-
|
140
|
-
def anonymous_instance
|
141
|
-
@anonymous_instance ||= anonymous_class.new
|
142
|
-
end
|
59
|
+
def run_callbacks(name, result_instance)
|
60
|
+
changed
|
61
|
+
notify_observers(name, result_instance)
|
143
62
|
end
|
144
63
|
end
|
145
64
|
end
|
@@ -1,13 +1,8 @@
|
|
1
1
|
module FactoryGirl
|
2
2
|
class Proxy #:nodoc:
|
3
3
|
class AttributesFor < Proxy #:nodoc:
|
4
|
-
def
|
5
|
-
|
6
|
-
super
|
7
|
-
end
|
8
|
-
|
9
|
-
def result(to_create)
|
10
|
-
result_hash
|
4
|
+
def result(attribute_assigner, to_create)
|
5
|
+
attribute_assigner.hash
|
11
6
|
end
|
12
7
|
end
|
13
8
|
end
|
@@ -6,9 +6,10 @@ module FactoryGirl
|
|
6
6
|
factory.run(get_method(overrides[:method]), overrides.except(:method))
|
7
7
|
end
|
8
8
|
|
9
|
-
def result(to_create)
|
10
|
-
|
11
|
-
|
9
|
+
def result(attribute_assigner, to_create)
|
10
|
+
attribute_assigner.object.tap do |result_instance|
|
11
|
+
run_callbacks(:after_build, result_instance)
|
12
|
+
end
|
12
13
|
end
|
13
14
|
|
14
15
|
private
|
@@ -1,13 +1,12 @@
|
|
1
1
|
module FactoryGirl
|
2
2
|
class Proxy #:nodoc:
|
3
3
|
class Create < Build #:nodoc:
|
4
|
-
def result(to_create)
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
result_instance
|
4
|
+
def result(attribute_assigner, to_create)
|
5
|
+
attribute_assigner.object.tap do |result_instance|
|
6
|
+
run_callbacks(:after_build, result_instance)
|
7
|
+
to_create[result_instance]
|
8
|
+
run_callbacks(:after_create, result_instance)
|
9
|
+
end
|
11
10
|
end
|
12
11
|
end
|
13
12
|
end
|
@@ -3,8 +3,25 @@ module FactoryGirl
|
|
3
3
|
class Stub < Proxy #:nodoc:
|
4
4
|
@@next_id = 1000
|
5
5
|
|
6
|
-
def
|
7
|
-
|
6
|
+
def association(factory_name, overrides = {})
|
7
|
+
factory = FactoryGirl.factory_by_name(factory_name)
|
8
|
+
factory.run(Proxy::Stub, overrides.except(:method))
|
9
|
+
end
|
10
|
+
|
11
|
+
def result(attribute_assigner, to_create)
|
12
|
+
attribute_assigner.object.tap do |result_instance|
|
13
|
+
stub_database_interaction_on_result(result_instance)
|
14
|
+
run_callbacks(:after_stub, result_instance)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def next_id
|
21
|
+
@@next_id += 1
|
22
|
+
end
|
23
|
+
|
24
|
+
def stub_database_interaction_on_result(result_instance)
|
8
25
|
result_instance.id = next_id
|
9
26
|
result_instance.instance_eval do
|
10
27
|
def persisted?
|
@@ -40,22 +57,6 @@ module FactoryGirl
|
|
40
57
|
end
|
41
58
|
end
|
42
59
|
end
|
43
|
-
|
44
|
-
def association(factory_name, overrides = {})
|
45
|
-
factory = FactoryGirl.factory_by_name(factory_name)
|
46
|
-
factory.run(Proxy::Stub, overrides.except(:method))
|
47
|
-
end
|
48
|
-
|
49
|
-
def result(to_create)
|
50
|
-
run_callbacks(:after_stub)
|
51
|
-
result_instance
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def next_id
|
57
|
-
@@next_id += 1
|
58
|
-
end
|
59
60
|
end
|
60
61
|
end
|
61
62
|
end
|