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.
Files changed (58) hide show
  1. data/.travis.yml +5 -3
  2. data/Appraisals +4 -0
  3. data/CONTRIBUTION_GUIDELINES.md +1 -1
  4. data/Changelog +13 -0
  5. data/GETTING_STARTED.md +362 -286
  6. data/Gemfile.lock +1 -1
  7. data/README.md +8 -8
  8. data/features/factory_girl_steps.feature +4 -0
  9. data/features/support/factories.rb +19 -0
  10. data/gemfiles/2.1.gemfile.lock +1 -1
  11. data/gemfiles/2.3.gemfile.lock +1 -1
  12. data/gemfiles/3.0.gemfile.lock +1 -1
  13. data/gemfiles/3.1.gemfile.lock +1 -1
  14. data/gemfiles/3.2.gemfile +7 -0
  15. data/gemfiles/3.2.gemfile.lock +90 -0
  16. data/lib/factory_girl.rb +4 -0
  17. data/lib/factory_girl/attribute.rb +1 -9
  18. data/lib/factory_girl/attribute/association.rb +2 -2
  19. data/lib/factory_girl/attribute/dynamic.rb +2 -2
  20. data/lib/factory_girl/attribute/sequence.rb +1 -1
  21. data/lib/factory_girl/attribute/static.rb +1 -1
  22. data/lib/factory_girl/attribute_assigner.rb +73 -0
  23. data/lib/factory_girl/attribute_list.rb +6 -15
  24. data/lib/factory_girl/callback.rb +2 -2
  25. data/lib/factory_girl/evaluator.rb +57 -0
  26. data/lib/factory_girl/evaluator_class_definer.rb +34 -0
  27. data/lib/factory_girl/factory.rb +31 -80
  28. data/lib/factory_girl/null_factory.rb +2 -1
  29. data/lib/factory_girl/null_object.rb +19 -0
  30. data/lib/factory_girl/proxy.rb +6 -87
  31. data/lib/factory_girl/proxy/attributes_for.rb +2 -7
  32. data/lib/factory_girl/proxy/build.rb +4 -3
  33. data/lib/factory_girl/proxy/create.rb +6 -7
  34. data/lib/factory_girl/proxy/stub.rb +19 -18
  35. data/lib/factory_girl/step_definitions.rb +4 -3
  36. data/lib/factory_girl/version.rb +1 -1
  37. data/spec/acceptance/aliases_spec.rb +19 -0
  38. data/spec/acceptance/attributes_for_spec.rb +10 -1
  39. data/spec/acceptance/attributes_from_instance_spec.rb +53 -0
  40. data/spec/acceptance/build_spec.rb +8 -0
  41. data/spec/acceptance/build_stubbed_spec.rb +9 -0
  42. data/spec/acceptance/create_spec.rb +8 -0
  43. data/spec/factory_girl/attribute/association_spec.rb +5 -4
  44. data/spec/factory_girl/attribute/dynamic_spec.rb +9 -10
  45. data/spec/factory_girl/attribute/sequence_spec.rb +1 -2
  46. data/spec/factory_girl/attribute/static_spec.rb +1 -2
  47. data/spec/factory_girl/attribute_list_spec.rb +14 -4
  48. data/spec/factory_girl/evaluator_class_definer_spec.rb +54 -0
  49. data/spec/factory_girl/factory_spec.rb +23 -16
  50. data/spec/factory_girl/null_factory_spec.rb +2 -1
  51. data/spec/factory_girl/null_object_spec.rb +8 -0
  52. data/spec/factory_girl/proxy/attributes_for_spec.rb +8 -24
  53. data/spec/factory_girl/proxy/build_spec.rb +1 -7
  54. data/spec/factory_girl/proxy/create_spec.rb +2 -27
  55. data/spec/factory_girl/proxy/stub_spec.rb +14 -13
  56. data/spec/factory_girl/proxy_spec.rb +1 -33
  57. data/spec/support/shared_examples/proxy.rb +22 -45
  58. 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
@@ -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.to_s.camelize.constantize
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 || :create
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
- runner_options = {
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
- block[Runner.new(runner_options).run(overrides)]
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.select {|attribute| attribute.association? }
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
- parent.defined_traits.each {|trait| define_trait(trait) }
89
- parent.compile
90
- @definition.compile
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
- traits.each do |trait|
109
- list.apply_attributes(trait.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
- [parent.callbacks, traits.map(&:callbacks).reverse, @definition.callbacks].flatten
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
@@ -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
- def initialize(klass, callbacks = [])
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 method_missing(method, *args, &block)
72
- get(method)
73
- end
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 set(attribute, value)
5
- return if attribute.is_a? Attribute::Association
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
- run_callbacks(:after_build)
11
- result_instance
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
- super
6
-
7
- to_create[result_instance]
8
-
9
- run_callbacks(:after_create)
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 initialize(klass, callbacks = [])
7
- super
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