masa-iwasaki-factory_girl 1.2.1.1 → 1.2.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/README.rdoc +56 -9
  2. data/Rakefile +19 -11
  3. data/lib/factory_girl.rb +2 -0
  4. data/lib/factory_girl/attribute/association.rb +2 -0
  5. data/lib/factory_girl/attribute/callback.rb +16 -0
  6. data/lib/factory_girl/attribute/dynamic.rb +1 -1
  7. data/lib/factory_girl/factory.rb +64 -44
  8. data/lib/factory_girl/proxy.rb +19 -0
  9. data/lib/factory_girl/proxy/build.rb +1 -0
  10. data/lib/factory_girl/proxy/create.rb +53 -1
  11. data/lib/factory_girl/proxy/stub.rb +7 -6
  12. data/lib/factory_girl/step_definitions.rb +54 -0
  13. data/spec/factory_girl/aliases_spec.rb +29 -0
  14. data/spec/factory_girl/attribute/association_spec.rb +29 -0
  15. data/spec/factory_girl/attribute/callback_spec.rb +23 -0
  16. data/spec/factory_girl/attribute/dynamic_spec.rb +49 -0
  17. data/spec/factory_girl/attribute/static_spec.rb +29 -0
  18. data/spec/factory_girl/attribute_spec.rb +30 -0
  19. data/spec/factory_girl/factory_spec.rb +582 -0
  20. data/spec/factory_girl/proxy/attributes_for_spec.rb +52 -0
  21. data/spec/factory_girl/proxy/build_spec.rb +81 -0
  22. data/spec/factory_girl/proxy/create_spec.rb +97 -0
  23. data/spec/factory_girl/proxy/stub_spec.rb +79 -0
  24. data/spec/factory_girl/proxy_spec.rb +84 -0
  25. data/spec/factory_girl/sequence_spec.rb +66 -0
  26. data/spec/factory_girl/syntax/blueprint_spec.rb +35 -0
  27. data/spec/factory_girl/syntax/generate_spec.rb +58 -0
  28. data/spec/factory_girl/syntax/make_spec.rb +35 -0
  29. data/spec/factory_girl/syntax/sham_spec.rb +36 -0
  30. data/spec/integration_spec.rb +304 -0
  31. data/spec/models.rb +43 -0
  32. data/spec/spec_helper.rb +18 -0
  33. metadata +49 -9
data/README.rdoc CHANGED
@@ -1,5 +1,5 @@
1
1
  = What this fork added to original factory_girl
2
- == Factory#find_or_create
2
+ == Caching
3
3
  By default, the association created by factory_girl is based on Factory#create. This means there would be twice creation for project_1 in the code shown below.
4
4
 
5
5
  # Factory definitions
@@ -26,9 +26,19 @@ By default, the association created by factory_girl is based on Factory#create.
26
26
  end
27
27
  end
28
28
 
29
- This fork added Factory#find_or_create. This method keeps an instance which has already created. This means you would not have the creation of a same factory and the code shown above turns green.
29
+ This fork added @@instances to Factory::Proxy::Create. @@instances keeps instances which has already created. This means you would not have the creation of a same factory and the code shown above turns green.
30
30
 
31
- This fork also changed default_strategy from create to find_or_create. This change affects the definitions written in some syntax. If you are using bluprint or sham syntax, you might need to use "Factory.create(factory_name)" explicityly instead of "Facotry(factory_name)".
31
+ == How to enable caching of instances
32
+ At default, all instances will be cached. But you can toggle this option by calling these methods:
33
+
34
+ - Factory::Proxy::Create.disable_cache
35
+ - Factory::Proxy::Create.enable_cache
36
+
37
+ == Other changes
38
+ These modification affects the definitions written in some syntax.
39
+
40
+ - If you are using bluprint or sham syntax, you might need to use "Factory.create(factory_name)" explicityly instead of "Facotry(factory_name)"
41
+ - With generate syntas, Factory.generate will raise an exception as same as Factory.generate!
32
42
 
33
43
  = factory_girl
34
44
 
@@ -39,14 +49,13 @@ factory_girl is a fixtures replacement with a straightforward definition syntax,
39
49
  Github: http://github.com/thoughtbot/factory_girl/tree/master
40
50
 
41
51
  Gem:
42
- gem install thoughtbot-factory_girl --source http://gems.github.com
52
+ gem install factory_girl --source http://gemcutter.org
43
53
 
44
- Note: if you install factory_girl using the gem from Github, you'll need this
54
+ Note: if you install factory_girl using the gem from Gemcutter, you'll need this
45
55
  in your environment.rb if you want to use Rails 2.1+'s dependency manager:
46
56
 
47
- config.gem "thoughtbot-factory_girl",
48
- :lib => "factory_girl",
49
- :source => "http://gems.github.com"
57
+ config.gem "factory_girl",
58
+ :source => "http://gemcutter.org"
50
59
 
51
60
  == Defining factories
52
61
 
@@ -75,6 +84,8 @@ Each factory has a name and a set of attributes. The name is used to guess the c
75
84
 
76
85
  It is highly recommended that you have one factory for each class that provides the simplest set of attributes necessary to create an instance of that class. If you're creating ActiveRecord objects, that means that you should only provide attributes that are required through validations and that do not have defaults. Other factories can be created through inheritance to cover common scenarios for each class.
77
86
 
87
+ Attempting to define multiple factories with the same name will raise an error.
88
+
78
89
  Factories can either be defined anywhere, but will automatically be loaded if they are defined in files at the following locations:
79
90
 
80
91
  test/factories.rb
@@ -112,7 +123,7 @@ The default strategy can be overriden:
112
123
 
113
124
  user = Factory(:user)
114
125
 
115
- No matter which startegy is used, it's possible to override the defined attributes by passing a hash:
126
+ No matter which strategy is used, it's possible to override the defined attributes by passing a hash:
116
127
 
117
128
  # Build a User instance and override the first_name property
118
129
  user = Factory.build(:user, :first_name => 'Joe')
@@ -224,6 +235,41 @@ a particular factory:
224
235
  f.sequence(:email) {|n| "person#{n}@example.com" }
225
236
  end
226
237
 
238
+ == Callbacks
239
+
240
+ Factory_girl makes available three callbacks for injecting some code:
241
+
242
+ * after_build - called after a factory is built (via Factory.build)
243
+ * after_create - called after a factory is saved (via Factory.create)
244
+ * after_stub - called after a factory is stubbed (via Factory.stub)
245
+
246
+ Examples:
247
+
248
+ # Define a factory that calls the generate_hashed_password method after it is built
249
+ Factory.define :user do |u|
250
+ u.after_build { |user| do_something_to(user) }
251
+ end
252
+
253
+ Note that you'll have an instance of the user in the block. This can be useful.
254
+
255
+ You can also define multiple types of callbacks on the same factory:
256
+
257
+ Factory.define :user do |u|
258
+ u.after_build { |user| do_something_to(user) }
259
+ u.after_create { |user| do_something_else_to(user) }
260
+ end
261
+
262
+ Factories can also define any number of the same kind of callback. These callbacks will be executed in the order they are specified:
263
+
264
+ Factory.define :user do |u|
265
+ u.after_create { this_runs_first }
266
+ u.after_create { then_this }
267
+ end
268
+
269
+ Calling Factory.create will invoke both after_build and after_create callbacks.
270
+
271
+ Also, like standard attributes, child factories will inherit (and can define additional) callbacks from their parent factory.
272
+
227
273
  == Alternate Syntaxes
228
274
 
229
275
  Users' tastes for syntax vary dramatically, but most users are looking for a common feature set. Because of this, factory_girl supports "syntax layers" which provide alternate interfaces. See Factory::Syntax for information about the various layers available.
@@ -250,6 +296,7 @@ factory_girl was written by Joe Ferris with contributions from several authors,
250
296
  * Jon Yurek
251
297
  * Josh Nichols
252
298
  * Josh Owens
299
+ * Nate Sutton
253
300
 
254
301
  The syntax layers are derived from software written by the following authors:
255
302
  * Pete Yandell
data/Rakefile CHANGED
@@ -6,9 +6,10 @@ require 'rcov/rcovtask'
6
6
  require 'date'
7
7
 
8
8
  require 'spec/rake/spectask'
9
+ require 'cucumber/rake/task'
9
10
 
10
- desc 'Default: run the specs.'
11
- task :default => :spec
11
+ desc 'Default: run the specs and features.'
12
+ task :default => [:spec, :features]
12
13
 
13
14
  Spec::Rake::SpecTask.new do |t|
14
15
  t.spec_opts = ['--options', "spec/spec.opts"]
@@ -36,13 +37,14 @@ task :sync_docs => 'rdoc' do
36
37
  end
37
38
 
38
39
  spec = Gem::Specification.new do |s|
39
- s.name = %q{factory_girl}
40
- s.version = "1.2.0"
41
- s.summary = %q{factory_girl provides a framework and DSL for defining and
42
- using model instance factories.}
43
- s.description = %q{factory_girl provides a framework and DSL for defining and
44
- using factories - less error-prone, more explicit, and
45
- all-around easier to work with than fixtures.}
40
+ s.name = %q{masa-iwasaki-factory_girl}
41
+ s.version = "1.2.3.1"
42
+ s.summary = %q{factory_girl with singleton support}
43
+ s.description = %q{This forked factory_girl has been added a singleton
44
+ support. You can specify ':singleton => true' option
45
+ on factory definition and an instance created by this
46
+ definition will be an singleton instance.
47
+ }
46
48
 
47
49
  s.files = FileList['[A-Z]*', 'lib/**/*.rb', 'spec/**/*.rb']
48
50
  s.require_path = 'lib'
@@ -52,8 +54,9 @@ spec = Gem::Specification.new do |s|
52
54
  s.extra_rdoc_files = ["README.rdoc"]
53
55
  s.rdoc_options = ['--line-numbers', "--main", "README.rdoc"]
54
56
 
55
- s.authors = ["Joe Ferris"]
56
- s.email = %q{jferris@thoughtbot.com}
57
+ s.authors = ["Masatoshi Iwasaki"]
58
+ s.email = %q{mstshiwasaki@gmail.com}
59
+ s.homepage = "http://github.com/masa-iwasaki/factory_girl"
57
60
 
58
61
  s.platform = Gem::Platform::RUBY
59
62
  end
@@ -72,3 +75,8 @@ task :gemspec do
72
75
  f.write spec.to_ruby
73
76
  end
74
77
  end
78
+
79
+ Cucumber::Rake::Task.new(:features) do |t|
80
+ t.fork = true
81
+ t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'progress')]
82
+ end
data/lib/factory_girl.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'active_support'
2
+ require 'digest'
2
3
  require 'factory_girl/proxy'
3
4
  require 'factory_girl/proxy/build'
4
5
  require 'factory_girl/proxy/create'
@@ -9,6 +10,7 @@ require 'factory_girl/attribute'
9
10
  require 'factory_girl/attribute/static'
10
11
  require 'factory_girl/attribute/dynamic'
11
12
  require 'factory_girl/attribute/association'
13
+ require 'factory_girl/attribute/callback'
12
14
  require 'factory_girl/sequence'
13
15
  require 'factory_girl/aliases'
14
16
 
@@ -3,6 +3,8 @@ class Factory
3
3
 
4
4
  class Association < Attribute #:nodoc:
5
5
 
6
+ attr_reader :factory
7
+
6
8
  def initialize(name, factory, overrides)
7
9
  super(name)
8
10
  @factory = factory
@@ -0,0 +1,16 @@
1
+ class Factory
2
+ class Attribute #:nodoc:
3
+
4
+ class Callback < Attribute #:nodoc:
5
+ def initialize(name, block)
6
+ @name = name.to_sym
7
+ @block = block
8
+ end
9
+
10
+ def add_to(proxy)
11
+ proxy.add_callback(name, @block)
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -8,7 +8,7 @@ class Factory
8
8
  end
9
9
 
10
10
  def add_to(proxy)
11
- value = @block.call(proxy)
11
+ value = @block.arity.zero? ? @block.call : @block.call(proxy)
12
12
  if Factory::Sequence === value
13
13
  raise SequenceAbuseError
14
14
  end
@@ -4,13 +4,17 @@ class Factory
4
4
  class AssociationDefinitionError < RuntimeError
5
5
  end
6
6
 
7
+ # Raised when a callback is defined that has an invalid name
8
+ class InvalidCallbackNameError < RuntimeError
9
+ end
10
+
11
+ # Raised when a factory is defined with the same name as a previously-defined factory.
12
+ class DuplicateDefinitionError < RuntimeError
13
+ end
14
+
7
15
  class << self
8
16
  attr_accessor :factories #:nodoc:
9
17
 
10
- # An array of used in find_or_create to keep AR instances created
11
- # by Proxy::Create
12
- attr_accessor :instances
13
-
14
18
  # An Array of strings specifying locations that should be searched for
15
19
  # factory definitions. By default, factory_girl will attempt to require
16
20
  # "factories," "test/factories," and "spec/factories." Only the first
@@ -19,10 +23,10 @@ class Factory
19
23
  end
20
24
 
21
25
  self.factories = {}
22
- self.instances = {}
23
26
  self.definition_file_paths = %w(factories test/factories spec/factories)
24
27
 
25
28
  attr_reader :factory_name #:nodoc:
29
+ attr_reader :singleton #:nodoc:
26
30
  attr_reader :attributes #:nodoc:
27
31
 
28
32
  # Defines a new factory that can be used by the build strategies (create and
@@ -52,8 +56,20 @@ class Factory
52
56
  if parent = options.delete(:parent)
53
57
  instance.inherit_from(Factory.factory_by_name(parent))
54
58
  end
59
+ if singleton = options.delete(:singleton)
60
+ instance.singleton = true
61
+ end
62
+ if self.factories[instance.factory_name]
63
+ raise DuplicateDefinitionError, "Factory already defined: #{name}"
64
+ end
65
+
55
66
  self.factories[instance.factory_name] = instance
56
67
  end
68
+
69
+ # Call Proxy::Create.clear_caches
70
+ def self.clear_caches
71
+ ::Factory::Proxy::Create.clear_caches
72
+ end
57
73
 
58
74
  def class_name #:nodoc:
59
75
  @options[:class] || factory_name
@@ -64,13 +80,14 @@ class Factory
64
80
  end
65
81
 
66
82
  def default_strategy #:nodoc:
67
- @options[:default_strategy] || :find_or_create
83
+ @options[:default_strategy] || :create
68
84
  end
69
85
 
70
86
  def initialize (name, options = {}) #:nodoc:
71
87
  assert_valid_options(options)
72
88
  @factory_name = factory_name_for(name)
73
89
  @options = options
90
+ @singleton = false
74
91
  @attributes = []
75
92
  end
76
93
 
@@ -189,6 +206,25 @@ class Factory
189
206
  add_attribute(name) { s.next }
190
207
  end
191
208
 
209
+ def after_build(&block)
210
+ callback(:after_build, &block)
211
+ end
212
+
213
+ def after_create(&block)
214
+ callback(:after_create, &block)
215
+ end
216
+
217
+ def after_stub(&block)
218
+ callback(:after_stub, &block)
219
+ end
220
+
221
+ def callback(name, &block)
222
+ unless [:after_build, :after_create, :after_stub].include?(name.to_sym)
223
+ raise InvalidCallbackNameError, "#{name} is not a valid callback name. Valid callback names are :after_build, :after_create, and :after_stub"
224
+ end
225
+ @attributes << Attribute::Callback.new(name.to_sym, block)
226
+ end
227
+
192
228
  # Generates and returns a Hash of attributes from this factory. Attributes
193
229
  # can be individually overridden by passing in a Hash of attribute => value
194
230
  # pairs.
@@ -241,37 +277,7 @@ class Factory
241
277
  def self.create (name, overrides = {})
242
278
  factory_by_name(name).run(Proxy::Create, overrides)
243
279
  end
244
-
245
- # Returns an object if an instance from this factory was already created.
246
- # Or call self.create and register an instance to the list of instances.
247
- #
248
- # Arguments:
249
- # * name: +Symbol+ or +String+
250
- # The name of the factory that should be used.
251
- # * overrides: +Hash+
252
- # Attributes to overwrite for this instance.
253
- #
254
- # Returns: +Object+
255
- # A saved instance of the class this factory generates, with generated
256
- # attributes assigned.
257
- def self.find_or_create(name, overrides = {})
258
- if self.instances[name].nil?
259
- self.instances[name] = self.create(name, overrides)
260
- else
261
- # Need reloading in terms of tearing down of fixtures on each test
262
- begin
263
- self.instances[name].reload
264
- rescue ActiveRecord::RecordNotFound
265
- self.instances[name] = self.create(name, overrides)
266
- rescue
267
- # TODO: Better handling for other exceptions if it is needed
268
- self.instances[name] = self.create(name, overrides)
269
- end
270
- end
271
-
272
- self.instances[name]
273
- end
274
-
280
+
275
281
  # Generates and returns an object with all attributes from this factory
276
282
  # stubbed out. Attributes can be individually overridden by passing in a Hash
277
283
  # of attribute => value pairs.
@@ -287,7 +293,7 @@ class Factory
287
293
  def self.stub (name, overrides = {})
288
294
  factory_by_name(name).run(Proxy::Stub, overrides)
289
295
  end
290
-
296
+
291
297
  # Executes the default strategy for the given factory. This is usually create,
292
298
  # but it can be overridden for each factory.
293
299
  #
@@ -325,15 +331,29 @@ class Factory
325
331
  attribute.add_to(proxy)
326
332
  end
327
333
  end
334
+ proxy.factory_name = factory_name
335
+ proxy.singleton = singleton
328
336
  proxy.result
329
337
  end
330
338
 
331
- private
332
-
333
339
  def self.factory_by_name (name)
334
340
  factories[name.to_sym] or raise ArgumentError.new("No such factory: #{name.to_s}")
335
341
  end
336
342
 
343
+ def human_name(*args, &block)
344
+ if args.size == 0 && block.nil?
345
+ factory_name.to_s.gsub('_', ' ')
346
+ else
347
+ add_attribute(:human_name, *args, &block)
348
+ end
349
+ end
350
+
351
+ def associations
352
+ attributes.select {|attribute| attribute.is_a?(Attribute::Association) }
353
+ end
354
+
355
+ private
356
+
337
357
  def class_for (class_or_to_s)
338
358
  if class_or_to_s.respond_to?(:to_sym)
339
359
  Object.const_get(variable_name_to_class_name(class_or_to_s))
@@ -351,7 +371,7 @@ class Factory
351
371
  end
352
372
 
353
373
  def attribute_defined? (name)
354
- !@attributes.detect {|attr| attr.name == name }.nil?
374
+ !@attributes.detect {|attr| attr.name == name && !attr.is_a?(Factory::Attribute::Callback) }.nil?
355
375
  end
356
376
 
357
377
  def assert_valid_options(options)
@@ -361,7 +381,7 @@ class Factory
361
381
  end
362
382
  assert_valid_strategy(options[:default_strategy]) if options[:default_strategy]
363
383
  end
364
-
384
+
365
385
  def assert_valid_strategy(strategy)
366
386
  unless Factory::Proxy.const_defined? variable_name_to_class_name(strategy)
367
387
  raise ArgumentError, "Unknown strategy: #{strategy}"
@@ -381,14 +401,14 @@ class Factory
381
401
  def variable_name_to_class_name(name)
382
402
  name.to_s.
383
403
  gsub(/\/(.?)/) { "::#{$1.upcase}" }.
384
- gsub(/(?:^|_)(.)/) { $1.upcase }
404
+ gsub(/(?:^|_)(.)/) { $1.upcase }
385
405
  end
386
406
 
387
407
  # From ActiveSupport
388
408
  def symbolize_keys(hash)
389
409
  hash.inject({}) do |options, (key, value)|
390
410
  options[(key.to_sym rescue key) || key] = value
391
- options
411
+ options
392
412
  end
393
413
  end
394
414
 
@@ -1,6 +1,11 @@
1
1
  class Factory
2
2
 
3
3
  class Proxy #:nodoc:
4
+
5
+ attr_reader :callbacks
6
+ attr_accessor :factory_name
7
+ attr_accessor :singleton
8
+
4
9
  def initialize(klass)
5
10
  end
6
11
 
@@ -14,6 +19,20 @@ class Factory
14
19
  def associate(name, factory, attributes)
15
20
  end
16
21
 
22
+ def add_callback(name, block)
23
+ @callbacks ||= {}
24
+ @callbacks[name] ||= []
25
+ @callbacks[name] << block
26
+ end
27
+
28
+ def run_callbacks(name)
29
+ if @callbacks && @callbacks[name]
30
+ @callbacks[name].each do |block|
31
+ block.arity.zero? ? block.call : block.call(@instance)
32
+ end
33
+ end
34
+ end
35
+
17
36
  # Generates an association using the current build strategy.
18
37
  #
19
38
  # Arguments: