factory_girl 2.3.2 → 2.4.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.
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