factory_girl 1.3.3 → 2.0.0.beta1

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 (53) hide show
  1. data/README.rdoc +68 -60
  2. data/features/support/test.db +0 -0
  3. data/lib/factory_girl.rb +6 -12
  4. data/lib/factory_girl/aliases.rb +2 -31
  5. data/lib/factory_girl/attribute.rb +1 -1
  6. data/lib/factory_girl/attribute/association.rb +1 -1
  7. data/lib/factory_girl/attribute/callback.rb +1 -1
  8. data/lib/factory_girl/attribute/dynamic.rb +3 -3
  9. data/lib/factory_girl/attribute/static.rb +1 -1
  10. data/lib/factory_girl/definition_proxy.rb +180 -0
  11. data/lib/factory_girl/deprecated.rb +18 -0
  12. data/lib/factory_girl/factory.rb +120 -355
  13. data/lib/factory_girl/find_definitions.rb +25 -0
  14. data/lib/factory_girl/proxy.rb +4 -6
  15. data/lib/factory_girl/proxy/attributes_for.rb +1 -1
  16. data/lib/factory_girl/proxy/build.rb +7 -5
  17. data/lib/factory_girl/proxy/create.rb +1 -1
  18. data/lib/factory_girl/proxy/stub.rb +11 -5
  19. data/lib/factory_girl/rails2.rb +1 -1
  20. data/lib/factory_girl/sequence.rb +5 -40
  21. data/lib/factory_girl/step_definitions.rb +7 -7
  22. data/lib/factory_girl/syntax.rb +7 -7
  23. data/lib/factory_girl/syntax/blueprint.rb +5 -4
  24. data/lib/factory_girl/syntax/default.rb +31 -0
  25. data/lib/factory_girl/syntax/generate.rb +13 -8
  26. data/lib/factory_girl/syntax/make.rb +8 -6
  27. data/lib/factory_girl/syntax/sham.rb +11 -8
  28. data/lib/factory_girl/syntax/vintage.rb +196 -0
  29. data/lib/factory_girl/version.rb +4 -0
  30. data/spec/acceptance/acceptance_spec.rb +43 -60
  31. data/spec/acceptance/syntax/blueprint_spec.rb +1 -5
  32. data/spec/acceptance/syntax/generate_spec.rb +1 -4
  33. data/spec/acceptance/syntax/make_spec.rb +1 -4
  34. data/spec/acceptance/syntax/sham_spec.rb +9 -7
  35. data/spec/acceptance/syntax/vintage_spec.rb +184 -0
  36. data/spec/factory_girl/aliases_spec.rb +5 -5
  37. data/spec/factory_girl/attribute/association_spec.rb +3 -3
  38. data/spec/factory_girl/attribute/callback_spec.rb +3 -3
  39. data/spec/factory_girl/attribute/dynamic_spec.rb +20 -9
  40. data/spec/factory_girl/attribute/static_spec.rb +5 -5
  41. data/spec/factory_girl/attribute_spec.rb +5 -5
  42. data/spec/factory_girl/definition_proxy_spec.rb +138 -0
  43. data/spec/factory_girl/deprecated_spec.rb +66 -0
  44. data/spec/factory_girl/factory_spec.rb +283 -566
  45. data/spec/factory_girl/find_definitions_spec.rb +89 -0
  46. data/spec/factory_girl/proxy/attributes_for_spec.rb +2 -2
  47. data/spec/factory_girl/proxy/build_spec.rb +17 -12
  48. data/spec/factory_girl/proxy/create_spec.rb +17 -12
  49. data/spec/factory_girl/proxy/stub_spec.rb +6 -5
  50. data/spec/factory_girl/proxy_spec.rb +2 -2
  51. data/spec/factory_girl/sequence_spec.rb +15 -38
  52. data/spec/spec_helper.rb +4 -0
  53. metadata +28 -11
@@ -0,0 +1,18 @@
1
+ module Factory
2
+ def self.method_missing(name, *args, &block)
3
+ if FactoryGirl.respond_to?(name)
4
+ $stderr.puts "DEPRECATION WARNING: Change Factory.#{name} to FactoryGirl.#{name}"
5
+ FactoryGirl.send(name, *args, &block)
6
+ else
7
+ super(name, *args, &block)
8
+ end
9
+ end
10
+
11
+ def self.const_missing(name)
12
+ if FactoryGirl.const_defined?(name)
13
+ FactoryGirl.const_get(name)
14
+ else
15
+ super(name)
16
+ end
17
+ end
18
+ end
@@ -1,412 +1,177 @@
1
- class Factory
2
- undef :id if Factory.instance_methods.include?('id')
3
- undef :type if Factory.instance_methods.include?('type')
4
-
5
- # Raised when a factory is defined that attempts to instantiate itself.
6
- class AssociationDefinitionError < RuntimeError
7
- end
8
-
9
- # Raised when a callback is defined that has an invalid name
10
- class InvalidCallbackNameError < RuntimeError
11
- end
12
-
13
- # Raised when a factory is defined with the same name as a previously-defined factory.
14
- class DuplicateDefinitionError < RuntimeError
15
- end
16
-
1
+ module FactoryGirl
17
2
  class << self
18
3
  attr_accessor :factories #:nodoc:
19
-
20
- # An Array of strings specifying locations that should be searched for
21
- # factory definitions. By default, factory_girl will attempt to require
22
- # "factories," "test/factories," and "spec/factories." Only the first
23
- # existing file will be loaded.
24
- attr_accessor :definition_file_paths
25
4
  end
26
5
 
27
6
  self.factories = {}
28
- self.definition_file_paths = %w(factories test/factories spec/factories)
29
7
 
30
- attr_reader :factory_name #:nodoc:
31
- attr_reader :attributes #:nodoc:
8
+ def self.factory_by_name(name)
9
+ factories[name.to_sym] or raise ArgumentError.new("No such factory: #{name.to_s}")
10
+ end
32
11
 
33
- # Defines a new factory that can be used by the build strategies (create and
34
- # build) to build new objects.
35
- #
36
- # Arguments:
37
- # * name: +Symbol+ or +String+
38
- # A unique name used to identify this factory.
39
- # * options: +Hash+
40
- #
41
- # Options:
42
- # * class: +Symbol+, +Class+, or +String+
43
- # The class that will be used when generating instances for this factory. If not specified, the class will be guessed from the factory name.
44
- # * parent: +Symbol+
45
- # The parent factory. If specified, the attributes from the parent
46
- # factory will be copied to the current one with an ability to override
47
- # them.
48
- # * default_strategy: +Symbol+
49
- # The strategy that will be used by the Factory shortcut method.
50
- # Defaults to :create.
51
- #
52
- # Yields: +Factory+
53
- # The newly created factory.
54
- def self.define (name, options = {})
55
- instance = Factory.new(name, options)
56
- yield(instance)
57
- if parent = options.delete(:parent)
58
- instance.inherit_from(Factory.factory_by_name(parent))
59
- end
60
- if self.factories[instance.factory_name]
12
+ def self.register_factory(factory, options = {})
13
+ name = options[:as] || factory.name
14
+ if self.factories[name]
61
15
  raise DuplicateDefinitionError, "Factory already defined: #{name}"
62
16
  end
63
- self.factories[instance.factory_name] = instance
64
- end
65
-
66
- def class_name #:nodoc:
67
- @options[:class] || factory_name
17
+ self.factories[name] = factory
68
18
  end
69
19
 
70
- def build_class #:nodoc:
71
- @build_class ||= class_for(class_name)
20
+ # Raised when a factory is defined that attempts to instantiate itself.
21
+ class AssociationDefinitionError < RuntimeError
72
22
  end
73
23
 
74
- def default_strategy #:nodoc:
75
- @options[:default_strategy] || :create
24
+ # Raised when a callback is defined that has an invalid name
25
+ class InvalidCallbackNameError < RuntimeError
76
26
  end
77
27
 
78
- def initialize (name, options = {}) #:nodoc:
79
- assert_valid_options(options)
80
- @factory_name = factory_name_for(name)
81
- @options = options
82
- @attributes = []
28
+ # Raised when a factory is defined with the same name as a previously-defined factory.
29
+ class DuplicateDefinitionError < RuntimeError
83
30
  end
84
31
 
85
- def inherit_from(parent) #:nodoc:
86
- @options[:class] ||= parent.class_name
87
- @options[:default_strategy] ||= parent.default_strategy
32
+ class Factory
33
+ attr_reader :name #:nodoc:
34
+ attr_reader :attributes #:nodoc:
88
35
 
89
- new_attributes = []
90
- parent.attributes.each do |attribute|
91
- unless attribute_defined?(attribute.name)
92
- new_attributes << attribute.clone
93
- end
36
+ def factory_name
37
+ puts "WARNING: factory.factory_name is deprecated. Use factory.name instead."
38
+ name
94
39
  end
95
- @attributes.unshift *new_attributes
96
- end
97
40
 
98
- # Adds an attribute that should be assigned on generated instances for this
99
- # factory.
100
- #
101
- # This method should be called with either a value or block, but not both. If
102
- # called with a block, the attribute will be generated "lazily," whenever an
103
- # instance is generated. Lazy attribute blocks will not be called if that
104
- # attribute is overridden for a specific instance.
105
- #
106
- # When defining lazy attributes, an instance of Factory::Proxy will
107
- # be yielded, allowing associations to be built using the correct build
108
- # strategy.
109
- #
110
- # Arguments:
111
- # * name: +Symbol+ or +String+
112
- # The name of this attribute. This will be assigned using :"#{name}=" for
113
- # generated instances.
114
- # * value: +Object+
115
- # If no block is given, this value will be used for this attribute.
116
- def add_attribute (name, value = nil, &block)
117
- if block_given?
118
- if value
119
- raise AttributeDefinitionError, "Both value and block given"
120
- else
121
- attribute = Attribute::Dynamic.new(name, block)
122
- end
123
- else
124
- attribute = Attribute::Static.new(name, value)
41
+ def class_name #:nodoc:
42
+ @options[:class] || name
125
43
  end
126
44
 
127
- if attribute_defined?(attribute.name)
128
- raise AttributeDefinitionError, "Attribute already defined: #{name}"
45
+ def build_class #:nodoc:
46
+ @build_class ||= class_for(class_name)
129
47
  end
130
48
 
131
- @attributes << attribute
132
- end
133
-
134
- # Calls add_attribute using the missing method name as the name of the
135
- # attribute, so that:
136
- #
137
- # Factory.define :user do |f|
138
- # f.name 'Billy Idol'
139
- # end
140
- #
141
- # and:
142
- #
143
- # Factory.define :user do |f|
144
- # f.add_attribute :name, 'Billy Idol'
145
- # end
146
- #
147
- # are equivilent.
148
- def method_missing (name, *args, &block)
149
- add_attribute(name, *args, &block)
150
- end
151
-
152
- # Adds an attribute that builds an association. The associated instance will
153
- # be built using the same build strategy as the parent instance.
154
- #
155
- # Example:
156
- # Factory.define :user do |f|
157
- # f.name 'Joey'
158
- # end
159
- #
160
- # Factory.define :post do |f|
161
- # f.association :author, :factory => :user
162
- # end
163
- #
164
- # Arguments:
165
- # * name: +Symbol+
166
- # The name of this attribute.
167
- # * options: +Hash+
168
- #
169
- # Options:
170
- # * factory: +Symbol+ or +String+
171
- # The name of the factory to use when building the associated instance.
172
- # If no name is given, the name of the attribute is assumed to be the
173
- # name of the factory. For example, a "user" association will by
174
- # default use the "user" factory.
175
- def association (name, options = {})
176
- factory_name = options.delete(:factory) || name
177
- if factory_name_for(factory_name) == self.factory_name
178
- raise AssociationDefinitionError, "Self-referencing association '#{name}' in factory '#{self.factory_name}'"
49
+ def default_strategy #:nodoc:
50
+ @options[:default_strategy] || :create
179
51
  end
180
- @attributes << Attribute::Association.new(name, factory_name, options)
181
- end
182
-
183
- # Adds an attribute that will have unique values generated by a sequence with
184
- # a specified format.
185
- #
186
- # The result of:
187
- # Factory.define :user do |f|
188
- # f.sequence(:email) { |n| "person#{n}@example.com" }
189
- # end
190
- #
191
- # Is equal to:
192
- # Factory.sequence(:email) { |n| "person#{n}@example.com" }
193
- #
194
- # Factory.define :user do |f|
195
- # f.email { Factory.next(:email) }
196
- # end
197
- #
198
- # Except that no globally available sequence will be defined.
199
- def sequence (name, &block)
200
- s = Sequence.new(&block)
201
- add_attribute(name) { s.next }
202
- end
203
-
204
- def after_build(&block)
205
- callback(:after_build, &block)
206
- end
207
52
 
208
- def after_create(&block)
209
- callback(:after_create, &block)
210
- end
53
+ def initialize(name, options = {}) #:nodoc:
54
+ assert_valid_options(options)
55
+ @name = factory_name_for(name)
56
+ @options = options
57
+ @attributes = []
58
+ end
211
59
 
212
- def after_stub(&block)
213
- callback(:after_stub, &block)
214
- end
60
+ def inherit_from(parent) #:nodoc:
61
+ @options[:class] ||= parent.class_name
62
+ @options[:default_strategy] ||= parent.default_strategy
215
63
 
216
- def callback(name, &block)
217
- unless [:after_build, :after_create, :after_stub].include?(name.to_sym)
218
- raise InvalidCallbackNameError, "#{name} is not a valid callback name. Valid callback names are :after_build, :after_create, and :after_stub"
64
+ new_attributes = []
65
+ parent.attributes.each do |attribute|
66
+ unless attribute_defined?(attribute.name)
67
+ new_attributes << attribute.clone
68
+ end
69
+ end
70
+ @attributes.unshift *new_attributes
219
71
  end
220
- @attributes << Attribute::Callback.new(name.to_sym, block)
221
- end
222
72
 
223
- # Generates and returns a Hash of attributes from this factory. Attributes
224
- # can be individually overridden by passing in a Hash of attribute => value
225
- # pairs.
226
- #
227
- # Arguments:
228
- # * name: +Symbol+ or +String+
229
- # The name of the factory that should be used.
230
- # * overrides: +Hash+
231
- # Attributes to overwrite for this set.
232
- #
233
- # Returns: +Hash+
234
- # A set of attributes that can be used to build an instance of the class
235
- # this factory generates.
236
- def self.attributes_for (name, overrides = {})
237
- factory_by_name(name).run(Proxy::AttributesFor, overrides)
238
- end
73
+ def define_attribute(attribute)
74
+ name = attribute.name
75
+ # TODO: move these checks into Attribute
76
+ if attribute_defined?(name)
77
+ raise AttributeDefinitionError, "Attribute already defined: #{name}"
78
+ end
79
+ if attribute.respond_to?(:factory) && attribute.factory == self.name
80
+ raise AssociationDefinitionError, "Self-referencing association '#{name}' in factory '#{self.name}'"
81
+ end
82
+ @attributes << attribute
83
+ end
239
84
 
240
- # Generates and returns an instance from this factory. Attributes can be
241
- # individually overridden by passing in a Hash of attribute => value pairs.
242
- #
243
- # Arguments:
244
- # * name: +Symbol+ or +String+
245
- # The name of the factory that should be used.
246
- # * overrides: +Hash+
247
- # Attributes to overwrite for this instance.
248
- #
249
- # Returns: +Object+
250
- # An instance of the class this factory generates, with generated attributes
251
- # assigned.
252
- def self.build (name, overrides = {})
253
- factory_by_name(name).run(Proxy::Build, overrides)
254
- end
85
+ def add_callback(name, &block)
86
+ unless [:after_build, :after_create, :after_stub].include?(name.to_sym)
87
+ raise InvalidCallbackNameError, "#{name} is not a valid callback name. Valid callback names are :after_build, :after_create, and :after_stub"
88
+ end
89
+ @attributes << Attribute::Callback.new(name.to_sym, block)
90
+ end
255
91
 
256
- # Generates, saves, and returns an instance from this factory. Attributes can
257
- # be individually overridden by passing in a Hash of attribute => value
258
- # pairs.
259
- #
260
- # Instances are saved using the +save!+ method, so ActiveRecord models will
261
- # raise ActiveRecord::RecordInvalid exceptions for invalid attribute sets.
262
- #
263
- # Arguments:
264
- # * name: +Symbol+ or +String+
265
- # The name of the factory that should be used.
266
- # * overrides: +Hash+
267
- # Attributes to overwrite for this instance.
268
- #
269
- # Returns: +Object+
270
- # A saved instance of the class this factory generates, with generated
271
- # attributes assigned.
272
- def self.create (name, overrides = {})
273
- factory_by_name(name).run(Proxy::Create, overrides)
274
- end
92
+ def run (proxy_class, overrides) #:nodoc:
93
+ proxy = proxy_class.new(build_class)
94
+ overrides = symbolize_keys(overrides)
95
+ overrides.each {|attr, val| proxy.set(attr, val) }
96
+ passed_keys = overrides.keys.collect {|k| FactoryGirl.aliases_for(k) }.flatten
97
+ @attributes.each do |attribute|
98
+ unless passed_keys.include?(attribute.name)
99
+ attribute.add_to(proxy)
100
+ end
101
+ end
102
+ proxy.result
103
+ end
275
104
 
276
- # Generates and returns an object with all attributes from this factory
277
- # stubbed out. Attributes can be individually overridden by passing in a Hash
278
- # of attribute => value pairs.
279
- #
280
- # Arguments:
281
- # * name: +Symbol+ or +String+
282
- # The name of the factory that should be used.
283
- # * overrides: +Hash+
284
- # Attributes to overwrite for this instance.
285
- #
286
- # Returns: +Object+
287
- # An object with generated attributes stubbed out.
288
- def self.stub (name, overrides = {})
289
- factory_by_name(name).run(Proxy::Stub, overrides)
290
- end
105
+ def human_name(*args, &block)
106
+ name.to_s.gsub('_', ' ')
107
+ end
291
108
 
292
- # Executes the default strategy for the given factory. This is usually create,
293
- # but it can be overridden for each factory.
294
- #
295
- # Arguments:
296
- # * name: +Symbol+ or +String+
297
- # The name of the factory that should be used.
298
- # * overrides: +Hash+
299
- # Attributes to overwrite for this instance.
300
- #
301
- # Returns: +Object+
302
- # The result of the default strategy.
303
- def self.default_strategy (name, overrides = {})
304
- self.send(factory_by_name(name).default_strategy, name, overrides)
305
- end
109
+ def associations
110
+ attributes.select {|attribute| attribute.is_a?(Attribute::Association) }
111
+ end
306
112
 
307
- def self.find_definitions #:nodoc:
308
- definition_file_paths.each do |path|
309
- full_path = File.expand_path(path)
310
- require("#{full_path}.rb") if File.exists?("#{full_path}.rb")
113
+ private
311
114
 
312
- if File.directory?(full_path)
313
- Dir[File.join(full_path, '*.rb')].each do |file|
314
- require(file)
115
+ def class_for (class_or_to_s)
116
+ if class_or_to_s.respond_to?(:to_sym)
117
+ class_name = variable_name_to_class_name(class_or_to_s)
118
+ class_name.split('::').inject(Object) do |object, string|
119
+ object.const_get(string)
315
120
  end
121
+ else
122
+ class_or_to_s
316
123
  end
317
124
  end
318
- end
319
125
 
320
- def run (proxy_class, overrides) #:nodoc:
321
- proxy = proxy_class.new(build_class)
322
- overrides = symbolize_keys(overrides)
323
- overrides.each {|attr, val| proxy.set(attr, val) }
324
- passed_keys = overrides.keys.collect {|k| Factory.aliases_for(k) }.flatten
325
- @attributes.each do |attribute|
326
- unless passed_keys.include?(attribute.name)
327
- attribute.add_to(proxy)
126
+ def factory_name_for(class_or_to_s)
127
+ if class_or_to_s.respond_to?(:to_sym)
128
+ class_or_to_s.to_sym
129
+ else
130
+ class_name_to_variable_name(class_or_to_s).to_sym
328
131
  end
329
132
  end
330
- proxy.result
331
- end
332
133
 
333
- def self.factory_by_name (name)
334
- factories[name.to_sym] or raise ArgumentError.new("No such factory: #{name.to_s}")
335
- end
336
-
337
- def human_name(*args, &block)
338
- if args.size == 0 && block.nil?
339
- factory_name.to_s.gsub('_', ' ')
340
- else
341
- add_attribute(:human_name, *args, &block)
134
+ def attribute_defined? (name)
135
+ !@attributes.detect {|attr| attr.name == name && !attr.is_a?(Attribute::Callback) }.nil?
342
136
  end
343
- end
344
-
345
- def associations
346
- attributes.select {|attribute| attribute.is_a?(Attribute::Association) }
347
- end
348
137
 
349
- private
350
-
351
- def class_for (class_or_to_s)
352
- if class_or_to_s.respond_to?(:to_sym)
353
- class_name = variable_name_to_class_name(class_or_to_s)
354
- class_name.split('::').inject(Object) do |object, string|
355
- object.const_get(string)
138
+ def assert_valid_options(options)
139
+ invalid_keys = options.keys - [:class, :parent, :default_strategy]
140
+ unless invalid_keys == []
141
+ raise ArgumentError, "Unknown arguments: #{invalid_keys.inspect}"
356
142
  end
357
- else
358
- class_or_to_s
143
+ assert_valid_strategy(options[:default_strategy]) if options[:default_strategy]
359
144
  end
360
- end
361
145
 
362
- def factory_name_for (class_or_to_s)
363
- if class_or_to_s.respond_to?(:to_sym)
364
- class_or_to_s.to_sym
365
- else
366
- class_name_to_variable_name(class_or_to_s).to_sym
146
+ def assert_valid_strategy(strategy)
147
+ unless Proxy.const_defined? variable_name_to_class_name(strategy)
148
+ raise ArgumentError, "Unknown strategy: #{strategy}"
149
+ end
367
150
  end
368
- end
369
151
 
370
- def attribute_defined? (name)
371
- !@attributes.detect {|attr| attr.name == name && !attr.is_a?(Factory::Attribute::Callback) }.nil?
372
- end
373
-
374
- def assert_valid_options(options)
375
- invalid_keys = options.keys - [:class, :parent, :default_strategy]
376
- unless invalid_keys == []
377
- raise ArgumentError, "Unknown arguments: #{invalid_keys.inspect}"
152
+ # Based on ActiveSupport's underscore inflector
153
+ def class_name_to_variable_name(name)
154
+ name.to_s.gsub(/::/, '/').
155
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
156
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
157
+ tr("-", "_").
158
+ downcase
378
159
  end
379
- assert_valid_strategy(options[:default_strategy]) if options[:default_strategy]
380
- end
381
160
 
382
- def assert_valid_strategy(strategy)
383
- unless Factory::Proxy.const_defined? variable_name_to_class_name(strategy)
384
- raise ArgumentError, "Unknown strategy: #{strategy}"
161
+ # Based on ActiveSupport's camelize inflector
162
+ def variable_name_to_class_name(name)
163
+ name.to_s.
164
+ gsub(/\/(.?)/) { "::#{$1.upcase}" }.
165
+ gsub(/(?:^|_)(.)/) { $1.upcase }
385
166
  end
386
- end
387
-
388
- # Based on ActiveSupport's underscore inflector
389
- def class_name_to_variable_name(name)
390
- name.to_s.gsub(/::/, '/').
391
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
392
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
393
- tr("-", "_").
394
- downcase
395
- end
396
167
 
397
- # Based on ActiveSupport's camelize inflector
398
- def variable_name_to_class_name(name)
399
- name.to_s.
400
- gsub(/\/(.?)/) { "::#{$1.upcase}" }.
401
- gsub(/(?:^|_)(.)/) { $1.upcase }
402
- end
403
-
404
- # From ActiveSupport
405
- def symbolize_keys(hash)
406
- hash.inject({}) do |options, (key, value)|
407
- options[(key.to_sym rescue key) || key] = value
408
- options
168
+ # From ActiveSupport
169
+ def symbolize_keys(hash)
170
+ hash.inject({}) do |options, (key, value)|
171
+ options[(key.to_sym rescue key) || key] = value
172
+ options
173
+ end
409
174
  end
410
- end
411
175
 
176
+ end
412
177
  end