factory_girl 1.3.3 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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