thoughtbot-factory_girl 1.1.1 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog ADDED
@@ -0,0 +1,16 @@
1
+ 1.1.2 (July 30, 2008)
2
+ Improved error handling for invalid and undefined factories/attributes
3
+ Improved handling of strings vs symbols vs classes
4
+ Added a prettier syntax for handling associations
5
+ Updated documentation and fixed compatibility with Rails 2.1
6
+
7
+ 1.1.1 (June 23, 2008)
8
+ The attribute "name" no longer requires using #add_attribute
9
+
10
+ 1.1.0 (June 3, 2008)
11
+ Added support for dependent attributes
12
+ Fixed the attributes_for build strategy to not build associations
13
+ Added support for sequences
14
+
15
+ 1.0.0 (May 31, 208)
16
+ First version
data/README.textile CHANGED
@@ -12,6 +12,13 @@ Github: "Page":http://github.com/thoughtbot/factory_girl/tree/master "Clone":git
12
12
 
13
13
  Gem: <pre>gem install thoughtbot-factory_girl --source http://gems.github.com</pre>
14
14
 
15
+ Note: if you install factory_girl using the gem from Github, you'll need this
16
+ in your environment.rb if you want to use Rails 2.1's dependency manager:
17
+
18
+ config.gem "thoughtbot-factory_girl",
19
+ :lib => "factory_girl",
20
+ :source => "http://gems.github.com"
21
+
15
22
  h2. Defining factories
16
23
 
17
24
  <pre><code># This will guess the User class
@@ -88,6 +95,21 @@ Factory.build(:post)
88
95
  post.new_record? # => true
89
96
  post.author.new_record # => true</code></pre>
90
97
 
98
+ Because this pattern is so common, a prettier syntax is available for defining
99
+ associations:
100
+
101
+ <pre><code># The following definitions are equivilent:
102
+ Factory.define :post do |p|
103
+ p.author {|a| a.association(:user) }
104
+ end
105
+
106
+ Factory.define :post do |p|
107
+ p.association :author, :factory => :user
108
+ end</code></pre>
109
+
110
+ If the factory name is the same as the association name, the factory name can
111
+ be left out.
112
+
91
113
 
92
114
  h2. Sequences
93
115
 
data/Rakefile CHANGED
@@ -31,7 +31,7 @@ end
31
31
 
32
32
  spec = Gem::Specification.new do |s|
33
33
  s.name = %q{factory_girl}
34
- s.version = "1.1.1"
34
+ s.version = "1.1.2"
35
35
  s.summary = %q{factory_girl provides a framework and DSL for defining and
36
36
  using model instance factories.}
37
37
  s.description = %q{factory_girl provides a framework and DSL for defining and
data/lib/factory_girl.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'activesupport'
1
+ require 'active_support'
2
2
  require 'factory_girl/factory'
3
3
  require 'factory_girl/attribute_proxy'
4
4
  require 'factory_girl/sequence'
@@ -1,5 +1,8 @@
1
1
  class Factory
2
-
2
+
3
+ class AttributeDefinitionError < RuntimeError
4
+ end
5
+
3
6
  cattr_accessor :factories, :sequences #:nodoc:
4
7
  self.factories = {}
5
8
  self.sequences = {}
@@ -22,7 +25,7 @@ class Factory
22
25
  def self.define (name, options = {})
23
26
  instance = Factory.new(name, options)
24
27
  yield(instance)
25
- self.factories[name] = instance
28
+ self.factories[instance.factory_name] = instance
26
29
  end
27
30
 
28
31
  # Defines a new sequence that can be used to generate unique values in a specific format.
@@ -61,12 +64,12 @@ class Factory
61
64
  end
62
65
 
63
66
  def build_class #:nodoc:
64
- @build_class ||= @options[:class] || factory_name.to_s.classify.constantize
67
+ @build_class ||= class_for(@options[:class] || factory_name)
65
68
  end
66
69
 
67
70
  def initialize (name, options = {}) #:nodoc:
68
71
  options.assert_valid_keys(:class)
69
- @factory_name = name
72
+ @factory_name = factory_name_for(name)
70
73
  @options = options
71
74
 
72
75
  @static_attributes = {}
@@ -93,6 +96,14 @@ class Factory
93
96
  # value: (Object)
94
97
  # If no block is given, this value will be used for this attribute.
95
98
  def add_attribute (name, value = nil, &block)
99
+ name = name.to_sym
100
+
101
+ if name.to_s =~ /=$/
102
+ raise AttributeDefinitionError,
103
+ "factory_girl uses 'f.#{name.to_s.chop} #{value}' syntax " +
104
+ "rather than 'f.#{name} #{value}'"
105
+ end
106
+
96
107
  if block_given?
97
108
  unless value.nil?
98
109
  raise ArgumentError, "Both value and block given"
@@ -122,6 +133,35 @@ class Factory
122
133
  add_attribute(name, *args, &block)
123
134
  end
124
135
 
136
+ # Adds an attribute that builds an association. The associated instance will
137
+ # be built using the same build strategy as the parent instance.
138
+ #
139
+ # Example:
140
+ # Factory.define :user do |f|
141
+ # f.name 'Joey'
142
+ # end
143
+ #
144
+ # Factory.define :post do |f|
145
+ # f.association :author, :factory => :user
146
+ # end
147
+ #
148
+ # Arguments:
149
+ # name: (Symbol)
150
+ # The name of this attribute.
151
+ # options: (Hash)
152
+ # factory: (Symbol)
153
+ # The name of the factory to use when building the associated instance.
154
+ # If no name is given, the name of the attribute is assumed to be the
155
+ # name of the factory. For example, a "user" association will by
156
+ # default use the "user" factory.
157
+ def association (name, options = {})
158
+ name = name.to_sym
159
+ options = options.symbolize_keys
160
+ association_factory = options[:factory] || name
161
+
162
+ add_attribute(name) {|a| a.association(association_factory) }
163
+ end
164
+
125
165
  def attributes_for (attrs = {}) #:nodoc:
126
166
  build_attributes_hash(attrs, :attributes_for)
127
167
  end
@@ -188,7 +228,7 @@ class Factory
188
228
  private
189
229
 
190
230
  def factory_by_name (name)
191
- factories[name] or raise ArgumentError.new("No such factory: #{name.inspect}")
231
+ factories[name.to_sym] or raise ArgumentError.new("No such factory: #{name.to_s}")
192
232
  end
193
233
 
194
234
  end
@@ -196,6 +236,7 @@ class Factory
196
236
  private
197
237
 
198
238
  def build_attributes_hash (override, strategy)
239
+ override = override.symbolize_keys
199
240
  result = @static_attributes.merge(override)
200
241
  @lazy_attribute_names.each do |name|
201
242
  proxy = AttributeProxy.new(self, name, strategy, result)
@@ -213,4 +254,20 @@ class Factory
213
254
  instance
214
255
  end
215
256
 
257
+ def class_for (class_or_to_s)
258
+ if class_or_to_s.respond_to?(:to_sym)
259
+ class_or_to_s.to_s.classify.constantize
260
+ else
261
+ class_or_to_s
262
+ end
263
+ end
264
+
265
+ def factory_name_for (class_or_to_s)
266
+ if class_or_to_s.respond_to?(:to_sym)
267
+ class_or_to_s.to_sym
268
+ else
269
+ class_or_to_s.to_s.underscore.to_sym
270
+ end
271
+ end
272
+
216
273
  end
data/test/factory_test.rb CHANGED
@@ -26,12 +26,13 @@ class FactoryTest < Test::Unit::TestCase
26
26
  setup do
27
27
  @name = :user
28
28
  @factory = mock('factory')
29
+ @factory.stubs(:factory_name).returns(@name)
29
30
  @options = { :class => 'magic' }
30
31
  Factory.stubs(:new).returns(@factory)
31
32
  end
32
33
 
33
34
  should "create a new factory using the specified name and options" do
34
- Factory.expects(:new).with(@name, @options)
35
+ Factory.expects(:new).with(@name, @options).returns(@factory)
35
36
  Factory.define(@name, @options) {|f| }
36
37
  end
37
38
 
@@ -51,6 +52,14 @@ class FactoryTest < Test::Unit::TestCase
51
52
  end
52
53
 
53
54
  end
55
+
56
+ should "raise an error when defining a factory when using attribute setters" do
57
+ assert_raise Factory::AttributeDefinitionError do
58
+ Factory.define(:user) do |f|
59
+ f.name = 'test'
60
+ end
61
+ end
62
+ end
54
63
 
55
64
  context "defining a sequence" do
56
65
 
@@ -167,6 +176,46 @@ class FactoryTest < Test::Unit::TestCase
167
176
 
168
177
  end
169
178
 
179
+ context "when adding an association without a factory name" do
180
+
181
+ setup do
182
+ @factory = Factory.new(:post)
183
+ @name = :user
184
+ @factory.association(@name)
185
+ Post.any_instance.stubs(:user=)
186
+ end
187
+
188
+ should "add an attribute with the name of the association" do
189
+ assert @factory.attributes_for.key?(@name)
190
+ end
191
+
192
+ should "create a block that builds the association" do
193
+ Factory.expects(:build).with(@name, {})
194
+ @factory.build
195
+ end
196
+
197
+ end
198
+
199
+ context "when adding an association with a factory name" do
200
+
201
+ setup do
202
+ @factory = Factory.new(:post)
203
+ @name = :author
204
+ @factory_name = :user
205
+ @factory.association(@name, :factory => @factory_name)
206
+ end
207
+
208
+ should "add an attribute with the name of the association" do
209
+ assert @factory.attributes_for.key?(@name)
210
+ end
211
+
212
+ should "create a block that builds the association" do
213
+ Factory.expects(:build).with(@factory_name, {})
214
+ @factory.build
215
+ end
216
+
217
+ end
218
+
170
219
  should "add an attribute using the method name when passed an undefined method" do
171
220
  @attr = :first_name
172
221
  @value = 'Sugar'
@@ -180,6 +229,11 @@ class FactoryTest < Test::Unit::TestCase
180
229
  end
181
230
  end
182
231
 
232
+ should "allow attributes to be added with strings as names" do
233
+ @factory.add_attribute('name', 'value')
234
+ assert_equal 'value', @factory.attributes_for[:name]
235
+ end
236
+
183
237
  context "when overriding generated attributes with a hash" do
184
238
 
185
239
  setup do
@@ -198,6 +252,12 @@ class FactoryTest < Test::Unit::TestCase
198
252
  @factory.attributes_for(@hash)
199
253
  end
200
254
 
255
+ should "override a symbol parameter with a string parameter" do
256
+ @factory.add_attribute(@attr, 'The price is wrong, Bob!')
257
+ @hash = { @attr.to_s => @value }
258
+ assert_equal @value, @factory.attributes_for(@hash)[@attr]
259
+ end
260
+
201
261
  end
202
262
 
203
263
  should "guess the build class from the factory name" do
@@ -217,6 +277,36 @@ class FactoryTest < Test::Unit::TestCase
217
277
 
218
278
  end
219
279
 
280
+ context "when defined with a class instead of a name" do
281
+
282
+ setup do
283
+ @class = ArgumentError
284
+ @name = :argument_error
285
+ @factory = Factory.new(@class)
286
+ end
287
+
288
+ should "guess the name from the class" do
289
+ assert_equal @name, @factory.factory_name
290
+ end
291
+
292
+ should "use the class as the build class" do
293
+ assert_equal @class, @factory.build_class
294
+ end
295
+
296
+ end
297
+ context "when defined with a custom class name" do
298
+
299
+ setup do
300
+ @class = ArgumentError
301
+ @factory = Factory.new(:author, :class => :argument_error)
302
+ end
303
+
304
+ should "use the specified class as the build class" do
305
+ assert_equal @class, @factory.build_class
306
+ end
307
+
308
+ end
309
+
220
310
  context "with some attributes added" do
221
311
 
222
312
  setup do
@@ -267,6 +357,33 @@ class FactoryTest < Test::Unit::TestCase
267
357
 
268
358
  end
269
359
 
360
+ context "a factory with a string for a name" do
361
+
362
+ setup do
363
+ @name = :user
364
+ @factory = Factory.new(@name.to_s) {}
365
+ end
366
+
367
+ should "convert the string to a symbol" do
368
+ assert_equal @name, @factory.factory_name
369
+ end
370
+
371
+ end
372
+
373
+ context "a factory defined with a string name" do
374
+
375
+ setup do
376
+ Factory.factories = {}
377
+ @name = :user
378
+ @factory = Factory.define(@name.to_s) {}
379
+ end
380
+
381
+ should "store the factory using a symbol" do
382
+ assert_equal @factory, Factory.factories[@name]
383
+ end
384
+
385
+ end
386
+
270
387
  context "Factory class" do
271
388
 
272
389
  setup do
@@ -296,6 +413,11 @@ class FactoryTest < Test::Unit::TestCase
296
413
  assert_raise(ArgumentError) { Factory.send(method, :bogus) }
297
414
  end
298
415
 
416
+ should "recognize either 'name' or :name for Factory.#{method}" do
417
+ assert_nothing_raised { Factory.send(method, @name.to_s) }
418
+ assert_nothing_raised { Factory.send(method, @name.to_sym) }
419
+ end
420
+
299
421
  end
300
422
 
301
423
  should "call the create method from the top-level Factory() method" do
@@ -3,16 +3,16 @@ require(File.join(File.dirname(__FILE__), 'test_helper'))
3
3
  class IntegrationTest < Test::Unit::TestCase
4
4
 
5
5
  def setup
6
- Factory.define :user do |f|
6
+ Factory.define :user, :class => 'user' do |f|
7
7
  f.first_name 'Jimi'
8
8
  f.last_name 'Hendrix'
9
9
  f.admin false
10
10
  f.email {|a| "#{a.first_name}.#{a.last_name}@example.com".downcase }
11
11
  end
12
12
 
13
- Factory.define :post do |f|
13
+ Factory.define Post do |f|
14
14
  f.name 'Test Post'
15
- f.author {|a| a.association(:user) }
15
+ f.association :author, :factory => :user
16
16
  end
17
17
 
18
18
  Factory.define :admin, :class => User do |f|
@@ -79,7 +79,7 @@ class IntegrationTest < Test::Unit::TestCase
79
79
  context "a created instance" do
80
80
 
81
81
  setup do
82
- @instance = Factory.create(:post)
82
+ @instance = Factory.create('post')
83
83
  end
84
84
 
85
85
  should "be saved" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thoughtbot-factory_girl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Ferris
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-06-23 00:00:00 -07:00
12
+ date: 2008-07-30 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -30,6 +30,7 @@ extensions: []
30
30
  extra_rdoc_files:
31
31
  - README.textile
32
32
  files:
33
+ - Changelog
33
34
  - LICENSE
34
35
  - Rakefile
35
36
  - README.textile