ehedberg-object_daddy 0.3.1

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.
@@ -0,0 +1,123 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'fileutils'
3
+
4
+ describe 'the plugin install.rb script' do
5
+ before :each do
6
+ FileUtils.stubs(:mkdir)
7
+ self.stubs(:puts).returns(true)
8
+ end
9
+
10
+ def do_install
11
+ eval File.read(File.join(File.dirname(__FILE__), *%w[.. install.rb ]))
12
+ end
13
+
14
+ describe 'when there is a spec directory under RAILS_ROOT' do
15
+ before :each do
16
+ File.stubs(:directory?).with('./../../../spec').returns(true)
17
+ end
18
+
19
+ describe 'and there is a spec/exemplars directory under RAILS_ROOT' do
20
+ before :each do
21
+ File.stubs(:directory?).with('./../../../spec/exemplars').returns(true)
22
+ end
23
+
24
+ it 'should not create any new directories' do
25
+ FileUtils.expects(:mkdir).never
26
+ do_install
27
+ end
28
+ end
29
+
30
+ describe 'but there is no spec/exemplars directory under RAILS_ROOT' do
31
+ before :each do
32
+ File.stubs(:directory?).with('./../../../spec/exemplars').returns(false)
33
+ end
34
+
35
+ it 'should create a spec/exemplars directory under RAILS_ROOT' do
36
+ FileUtils.expects(:mkdir).with('./../../../spec/exemplars')
37
+ do_install
38
+ end
39
+ end
40
+ end
41
+
42
+ describe 'when there is no spec directory under RAILS_ROOT' do
43
+ before :each do
44
+ File.stubs(:directory?).with('./../../../spec').returns(false)
45
+ end
46
+
47
+ describe 'and there is a test directory under RAILS_ROOT' do
48
+ before :each do
49
+ File.stubs(:directory?).with('./../../../test').returns(true)
50
+ end
51
+
52
+ describe 'and there is a test/exemplars directory under RAILS_ROOT' do
53
+ before :each do
54
+ File.stubs(:directory?).with('./../../../test/exemplars').returns(true)
55
+ end
56
+
57
+ it 'should not create any new directories' do
58
+ FileUtils.expects(:mkdir).never
59
+ do_install
60
+ end
61
+ end
62
+
63
+ describe 'but there is no test/exemplars directory under RAILS_ROOT' do
64
+ before :each do
65
+ File.stubs(:directory?).with('./../../../test/exemplars').returns(false)
66
+ end
67
+
68
+ it 'should create a test/exemplars directory under RAILS_ROOT' do
69
+ FileUtils.expects(:mkdir).with('./../../../test/exemplars')
70
+ do_install
71
+ end
72
+ end
73
+ end
74
+
75
+ describe 'and there is no test directory under RAILS_ROOT' do
76
+ before :each do
77
+ File.stubs(:directory?).with('./../../../test').returns(false)
78
+ end
79
+
80
+ it 'should create a spec directory under RAILS_ROOT' do
81
+ FileUtils.expects(:mkdir).with('./../../../spec')
82
+ do_install
83
+ end
84
+
85
+ it 'should create a spec/exemplars directory under RAILS_ROOT' do
86
+ FileUtils.expects(:mkdir).with('./../../../spec/exemplars')
87
+ do_install
88
+ end
89
+ end
90
+ end
91
+
92
+ it 'displays the content of the plugin README file' do
93
+ self.stubs(:readme_contents).returns('README CONTENTS')
94
+ self.expects(:puts).with('README CONTENTS')
95
+ do_install
96
+ end
97
+
98
+ describe 'readme_contents' do
99
+ it 'should work without arguments' do
100
+ do_install
101
+ lambda { readme_contents }.should_not raise_error(ArgumentError)
102
+ end
103
+
104
+ it 'should accept no arguments' do
105
+ do_install
106
+ lambda { readme_contents(:foo) }.should raise_error(ArgumentError)
107
+ end
108
+
109
+ it 'should read the plugin README file' do
110
+ do_install
111
+ File.stubs(:join).returns('/path/to/README')
112
+ IO.expects(:read).with('/path/to/README')
113
+ readme_contents
114
+ end
115
+
116
+ it 'should return the contents of the plugin README file' do
117
+ do_install
118
+ File.stubs(:join).returns('/path/to/README')
119
+ IO.stubs(:read).with('/path/to/README').returns('README CONTENTS')
120
+ readme_contents.should == 'README CONTENTS'
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,775 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'ostruct'
3
+ require 'object_daddy'
4
+
5
+ describe ObjectDaddy, "when included into a class" do
6
+ before(:each) do
7
+ @class = Class.new
8
+ @class.send(:include, ObjectDaddy)
9
+ end
10
+
11
+ it "should provide a means of spawning a class instance" do
12
+ @class.should respond_to(:spawn)
13
+ end
14
+
15
+ it "should not provide a means of generating and saving a class instance" do
16
+ @class.should_not respond_to(:generate)
17
+ end
18
+
19
+ it "should not provide a means of generating and saving a class instance while raising exceptions" do
20
+ @class.should_not respond_to(:generate!)
21
+ end
22
+
23
+ it "should provide a means of registering a generator to assist in creating class instances" do
24
+ @class.should respond_to(:generator_for)
25
+ end
26
+ end
27
+
28
+ describe ObjectDaddy, "when registering a generator method" do
29
+ before(:each) do
30
+ @class = Class.new(OpenStruct)
31
+ @class.send(:include, ObjectDaddy)
32
+ end
33
+
34
+ it "should fail unless an attribute name is provided" do
35
+ lambda { @class.generator_for }.should raise_error(ArgumentError)
36
+ end
37
+
38
+ it "should fail if an attribute is specified that already has a generator" do
39
+ @class.generator_for :foo do |prev| end
40
+ lambda { @class.generator_for :foo do |prev| end }.should raise_error(ArgumentError)
41
+ end
42
+
43
+ it "should be agnostic to attribute names specified as symbols or strings" do
44
+ @class.generator_for :foo do |prev| end
45
+ lambda { @class.generator_for 'foo' do |prev| end }.should raise_error(ArgumentError)
46
+ end
47
+
48
+ it "should keep generators registered for different target classes separate" do
49
+ @class2 = Class.new
50
+ @class2.send :include, ObjectDaddy
51
+ @class2.generator_for :foo do |prev| end
52
+ lambda { @class.generator_for 'foo' do |prev| end }.should_not raise_error
53
+ end
54
+
55
+ it "should succeed if a generator block is provided" do
56
+ lambda { @class.generator_for :foo do |prev| end }.should_not raise_error
57
+ end
58
+
59
+ it "should not fail if a generator block doesn't handle a previous value" do
60
+ lambda { @class.generator_for :foo, :first => 'baz' do end }.should_not raise_error(ArgumentError)
61
+ end
62
+
63
+ it "should not fail if a generator block specifically doesn't handle a previous value" do
64
+ lambda { @class.generator_for :foo, :first => 'baz' do || end }.should_not raise_error(ArgumentError)
65
+ end
66
+
67
+ it "should fail if a generator block expects more than one argument" do
68
+ lambda { @class.generator_for :foo, :first => 'baz' do |x, y| end }.should raise_error(ArgumentError)
69
+ end
70
+
71
+ it "should allow an initial value with a block argument" do
72
+ lambda { @class.generator_for :foo, :start => 'baz' do |prev| end }.should_not raise_error
73
+ end
74
+
75
+ it "should succeed if a generator class is provided" do
76
+ @generator = Class.new
77
+ @generator.stubs(:next)
78
+ lambda { @class.generator_for :foo, :class => @generator }.should_not raise_error
79
+ end
80
+
81
+ it "should fail if a generator class is specified which doesn't have a next method" do
82
+ @generator = Class.new
83
+ lambda { @class.generator_for :foo, :class => @generator }.should raise_error(ArgumentError)
84
+ end
85
+
86
+ it "should succeed if a generator method name is provided" do
87
+ @class.stubs(:method_name)
88
+ lambda { @class.generator_for :foo, :method => :method_name }.should_not raise_error
89
+ end
90
+
91
+ it "should not fail if a non-existent generator method name is provided" do
92
+ lambda { @class.generator_for :foo, :method => :fake_method }.should_not raise_error(ArgumentError)
93
+ end
94
+
95
+ it "should allow an initial value with a method argument" do
96
+ @class.stubs(:method_name)
97
+ lambda { @class.generator_for :foo, :start => 'baz', :method => :method_name }.should_not raise_error
98
+ end
99
+
100
+ it 'should succeed if a value is provided' do
101
+ lambda { @class.generator_for :foo, 'value' }.should_not raise_error(ArgumentError)
102
+ end
103
+
104
+ it 'should succeed when given an attr => value hash' do
105
+ lambda { @class.generator_for :foo => 'value' }.should_not raise_error(ArgumentError)
106
+ end
107
+
108
+ it 'should fail when given an attr => value hash with multiple attrs' do
109
+ lambda { @class.generator_for :foo => 'value', :bar => 'other value' }.should raise_error(ArgumentError)
110
+ end
111
+
112
+ it "should fail unless a generator block, generator class, generator method, or value is provided" do
113
+ lambda { @class.generator_for 'foo' }.should raise_error(ArgumentError)
114
+ end
115
+ end
116
+
117
+ describe ObjectDaddy, 'recording the registration of a generator method' do
118
+ before(:each) do
119
+ ObjectDaddy::ClassMethods.send(:public, :record_generator_for)
120
+ @class = Class.new(OpenStruct)
121
+ @class.send(:include, ObjectDaddy)
122
+ end
123
+
124
+ it 'should accept a handle and generator' do
125
+ lambda { @class.record_generator_for('handle', 'generator') }.should_not raise_error(ArgumentError)
126
+ end
127
+
128
+ it 'should require generator' do
129
+ lambda { @class.record_generator_for('handle') }.should raise_error(ArgumentError)
130
+ end
131
+
132
+ it 'should require a handle' do
133
+ lambda { @class.record_generator_for }.should raise_error(ArgumentError)
134
+ end
135
+
136
+ it 'should save the generator' do
137
+ @class.record_generator_for('handle', 'generator')
138
+ @class.generators['handle'][:generator].should == 'generator'
139
+ end
140
+
141
+ it 'should save the class that specified the generator' do
142
+ @class.record_generator_for('handle', 'generator')
143
+ @class.generators['handle'][:source].should == @class
144
+ end
145
+
146
+ it 'should fail if the handle has already been recorded' do
147
+ @class.record_generator_for('handle', 'generator')
148
+ lambda { @class.record_generator_for('handle', 'generator 2') }.should raise_error
149
+ end
150
+
151
+ it 'should not fail if the handle has not already been recorded' do
152
+ lambda { @class.record_generator_for('handle', 'generator') }.should_not raise_error
153
+ end
154
+ end
155
+
156
+ describe ObjectDaddy, 'when registering exemplars' do
157
+ before :each do
158
+ @class = Class.new(OpenStruct)
159
+ @class.send(:include, ObjectDaddy)
160
+ @file_path = File.join(File.dirname(__FILE__), 'tmp')
161
+ @file_name = File.join(@file_path, 'widget_exemplar.rb')
162
+ @class.stubs(:exemplar_path).returns(@file_path)
163
+ @class.stubs(:name).returns('Widget')
164
+ end
165
+
166
+ describe 'before exemplars have been registered' do
167
+ before :each do
168
+ @class.stubs(:exemplars_generated).returns(false)
169
+ end
170
+
171
+ it "should look for exemplars for the target class in the standard exemplar path" do
172
+ @class.expects(:exemplar_path).returns(@file_path)
173
+ @class.gather_exemplars
174
+ end
175
+
176
+ it "should look for an exemplar for the target class, based on the class's name" do
177
+ @class.expects(:name).returns('Widget')
178
+ @class.gather_exemplars
179
+ end
180
+
181
+ it "should register any generators found in the exemplar for the target class" do
182
+ # we are using the concrete Widget class here because otherwise it's difficult to have our exemplar file work in our class
183
+ begin
184
+ # a dummy class, useful for testing the actual loading of exemplar files
185
+ Widget = Class.new(OpenStruct) { include ObjectDaddy }
186
+ File.open(@file_name, 'w') {|f| f.puts "class Widget\ngenerator_for :foo\nend\n"}
187
+ Widget.stubs(:exemplar_path).returns(@file_path)
188
+ Widget.expects(:generator_for)
189
+ Widget.gather_exemplars
190
+ ensure
191
+ # clean up test data file
192
+ File.unlink(@file_name) if File.exists?(@file_name)
193
+ Object.send(:remove_const, :Widget)
194
+ end
195
+ end
196
+
197
+ it 'should record that exemplars have been registered' do
198
+ @class.expects(:exemplars_generated=).with(true)
199
+ @class.gather_exemplars
200
+ end
201
+ end
202
+
203
+ describe 'after exemplars have been registered' do
204
+ before :each do
205
+ @class.stubs(:exemplars_generated).returns(true)
206
+ end
207
+
208
+ it "should not look for exemplars for the target class in the standard exemplar path" do
209
+ @class.expects(:exemplar_path).never
210
+ @class.gather_exemplars
211
+ end
212
+
213
+ it "should not look for an exemplar for the target class, based on the class's name" do
214
+ @class.expects(:name).never
215
+ @class.gather_exemplars
216
+ end
217
+
218
+ it 'should register no generators' do
219
+ # we are using the concrete Widget class here because otherwise it's difficult to have our exemplar file work in our class
220
+ begin
221
+ # a dummy class, useful for testing the actual loading of exemplar files
222
+ Widget = Class.new(OpenStruct) { include ObjectDaddy }
223
+ File.open(@file_name, 'w') {|f| f.puts "class Widget\ngenerator_for :foo\nend\n"}
224
+ Widget.stubs(:exemplar_path).returns(@file_path)
225
+ Widget.stubs(:exemplars_generated).returns(true)
226
+ Widget.expects(:generator_for).never
227
+ Widget.gather_exemplars
228
+ ensure
229
+ # clean up test data file
230
+ File.unlink(@file_name) if File.exists?(@file_name)
231
+ Object.send(:remove_const, :Widget)
232
+ end
233
+ end
234
+
235
+ it 'should not record that exemplars have been registered' do
236
+ @class.expects(:exemplars_generated=).never
237
+ @class.gather_exemplars
238
+ end
239
+ end
240
+
241
+ it "should register no generators if no exemplar for the target class is available" do
242
+ @class.expects(:generator_for).never
243
+ @class.gather_exemplars
244
+ end
245
+ end
246
+
247
+ describe ObjectDaddy, "when spawning a class instance" do
248
+ before(:each) do
249
+ @class = Class.new(OpenStruct)
250
+ @class.send(:include, ObjectDaddy)
251
+ @file_path = File.join(File.dirname(__FILE__), 'tmp')
252
+ @file_name = File.join(@file_path, 'widget_exemplar.rb')
253
+ @class.stubs(:exemplar_path).returns(@file_path)
254
+ @class.stubs(:name).returns('Widget')
255
+ end
256
+
257
+ it "should yield the instance to a block if given" do
258
+ yielded_object = nil
259
+ @class.spawn do |obj|
260
+ yielded_object = obj
261
+ end
262
+ @class.should === yielded_object
263
+ end
264
+
265
+ it "should register exemplars for the target class" do
266
+ @class.expects(:gather_exemplars)
267
+ @class.spawn
268
+ end
269
+
270
+ it "should allow attributes to be overridden" do
271
+ @class.spawn(:foo => 'xyzzy').foo.should == 'xyzzy'
272
+ end
273
+
274
+ it "should use any generators registered with blocks" do
275
+ @class.generator_for :foo do |prev| "foo"; end
276
+ @class.spawn.foo.should == 'foo'
277
+ end
278
+
279
+ it "should not use a block generator for an attribute that has been overridden" do
280
+ @class.generator_for :foo do |prev| "foo"; end
281
+ @class.spawn(:foo => 'xyzzy').foo.should == 'xyzzy'
282
+ end
283
+
284
+ it "should use any generators registered with generator method names" do
285
+ @class.stubs(:generator_method).returns('bar')
286
+ @class.generator_for :foo, :method => :generator_method
287
+ @class.spawn.foo.should == 'bar'
288
+ end
289
+
290
+ it 'should fail if a generator is registered with a non-existent method name' do
291
+ @class.generator_for :foo, :method => :nonexistent_metho
292
+ lambda { @class.spawn.foo }.should raise_error
293
+ end
294
+
295
+ it "should not use a method generator for an attribute that has been overridden" do
296
+ @class.stubs(:generator_method).returns('bar')
297
+ @class.generator_for :foo, :method => :generator_method
298
+ @class.spawn(:foo => 'xyzzy').foo.should == 'xyzzy'
299
+ end
300
+
301
+ it "should use any generators registered with generator classes" do
302
+ @generator_class = Class.new do
303
+ def self.next() 'baz' end
304
+ end
305
+ @class.generator_for :foo, :class => @generator_class
306
+ @class.spawn.foo.should == 'baz'
307
+ end
308
+
309
+ it "should not use a class generator for an attribute that has been overridden" do
310
+ @generator_class = Class.new do
311
+ def self.next() 'baz' end
312
+ end
313
+ @class.generator_for :foo, :class => @generator_class
314
+ @class.spawn(:foo => 'xyzzy').foo.should == 'xyzzy'
315
+ end
316
+
317
+ it "should return the initial value first if one was registered for a block generator" do
318
+ @class.generator_for :foo, :start => 'frobnitz' do |prev| "foo"; end
319
+ @class.spawn.foo.should == 'frobnitz'
320
+ end
321
+
322
+ it "should return the block applied to the initial value on the second call if an initial value was registered for a block generator" do
323
+ @class.generator_for :foo, :start => 'frobnitz' do |prev| prev + 'a'; end
324
+ @class.spawn
325
+ @class.spawn.foo.should == 'frobnitza'
326
+ end
327
+
328
+ it "should return the block applied to the previous value when repeatedly calling a block generator" do
329
+ @class.generator_for :foo do |prev| prev ? prev.succ : 'test'; end
330
+ @class.spawn
331
+ @class.spawn.foo.should == 'tesu'
332
+ end
333
+
334
+ it 'should not require a block if an initial value is given' do
335
+ lambda { @class.generator_for :foo, :start => 'crapola' }.should_not raise_error(ArgumentError)
336
+ end
337
+
338
+ it 'should default the generator to increment the value if an initial value is given' do
339
+ @class.generator_for :foo, :start => 'crapola'
340
+ @class.spawn
341
+ @class.spawn.foo.should == 'crapolb'
342
+ end
343
+
344
+ it "should return the initial value first if one was registered for a method generator" do
345
+ @class.instance_eval do
346
+ def self.generator_value_method(prev)
347
+ 'foo'
348
+ end
349
+ end
350
+
351
+ @class.generator_for :foo, :start => 'frobnitz', :method => :generator_value_method
352
+ @class.spawn.foo.should == 'frobnitz'
353
+ end
354
+
355
+ it "should return the method applied to the initial value on the second call if an initial value was registered for a method generator" do
356
+ @class.instance_eval do
357
+ def self.generator_value_method(prev)
358
+ prev.succ
359
+ end
360
+ end
361
+
362
+ @class.generator_for :foo, :start => 'frobnitz', :method => :generator_value_method
363
+ @class.spawn
364
+ @class.spawn.foo.should == 'frobniua'
365
+ end
366
+
367
+ it "should return the method applied to the previous value when repeatedly calling a method generator" do
368
+ @class.instance_eval do
369
+ def self.generator_value_method(prev)
370
+ if prev
371
+ prev.succ
372
+ else
373
+ 'test'
374
+ end
375
+ end
376
+ end
377
+
378
+ @class.generator_for :foo, :method => :generator_value_method
379
+ @class.spawn
380
+ @class.spawn.foo.should == 'tesu'
381
+ end
382
+
383
+ it 'should use the return value for a block generator that takes no argument' do
384
+ x = 5
385
+ @class.generator_for(:foo) { x }
386
+ @class.spawn.foo.should == x
387
+ end
388
+
389
+ it 'should use the return value for a block generator that explicitly takes no argument' do
390
+ x = 5
391
+ @class.generator_for(:foo) { || x }
392
+ @class.spawn.foo.should == x
393
+ end
394
+
395
+ it 'should use the supplied value for the generated value' do
396
+ x = 5
397
+ @class.generator_for :foo, x
398
+ @class.spawn.foo.should == x
399
+ end
400
+
401
+ it 'should use the supplied attr => value value for the generated value' do
402
+ x = 5
403
+ @class.generator_for :foo => x
404
+ @class.spawn.foo.should == x
405
+ end
406
+
407
+ it "should call the normal target class constructor" do
408
+ @class.expects(:new)
409
+ @class.spawn
410
+ end
411
+
412
+ it 'should not generate a value for an attribute that has been specified as nil' do
413
+ @class.generator_for :foo => 5
414
+ @class.spawn(:foo => nil).foo.should be_nil
415
+ end
416
+
417
+ it 'should not generate a value for an attribute that has been specified as false' do
418
+ @class.generator_for :foo => 5
419
+ @class.spawn(:foo => false).foo.should be(false)
420
+ end
421
+
422
+ describe 'for a subclass' do
423
+ before :each do
424
+ @subclass = Class.new(@class)
425
+ @subclass.send(:include, ObjectDaddy)
426
+ @subfile_path = File.join(File.dirname(__FILE__), 'tmp')
427
+ @subfile_name = File.join(@file_path, 'sub_widget_exemplar.rb')
428
+ @subclass.stubs(:exemplar_path).returns(@file_path)
429
+ @subclass.stubs(:name).returns('SubWidget')
430
+ end
431
+
432
+ describe 'using generators from files' do
433
+ before :each do
434
+ Widget = Class.new(OpenStruct) { include ObjectDaddy }
435
+ SubWidget = Class.new(Widget) { include ObjectDaddy }
436
+
437
+ Widget.stubs(:exemplar_path).returns(@file_path)
438
+ SubWidget.stubs(:exemplar_path).returns(@subfile_path)
439
+
440
+ File.open(@file_name, 'w') do |f|
441
+ f.puts "class Widget\ngenerator_for :blah do |prev| 'blah'; end\nend\n"
442
+ end
443
+ end
444
+
445
+ after :each do
446
+ [@file_name, @subfile_name].each { |file| File.unlink(file) if File.exists?(file) }
447
+ [:Widget, :SubWidget].each { |const| Object.send(:remove_const, const) }
448
+ end
449
+
450
+ it 'should use generators from the parent class' do
451
+ SubWidget.spawn.blah.should == 'blah'
452
+ end
453
+
454
+ it 'should let subclass generators override parent generators' do
455
+ File.open(@subfile_name, 'w') do |f|
456
+ f.puts "class SubWidget\ngenerator_for :blah do |prev| 'blip'; end\nend\n"
457
+ end
458
+ SubWidget.spawn.blah.should == 'blip'
459
+ end
460
+ end
461
+
462
+ describe 'using generators called directly' do
463
+ it 'should use generators from the parent class' do
464
+ @class.generator_for :blah do |prev| 'blah'; end
465
+ @subclass.spawn.blah.should == 'blah'
466
+ end
467
+
468
+ it 'should let subclass generators override parent generators' do
469
+ pending 'figuring out what to do about this, including deciding whether or not this is even important' do
470
+ @class.generator_for :blah do |prev| 'blah'; end
471
+ # p @class
472
+ # p @subclass
473
+ # @subclass.send(:gather_exemplars)
474
+ # p @subclass.generators
475
+ @subclass.generator_for :blah do |prev| 'blip'; end
476
+ # @subclass.send(:gather_exemplars)
477
+ # p @subclass.generators
478
+ # p @subclass.generators[:blah][:generator][:block].call
479
+ # @subclass.send(:gather_exemplars)
480
+ @subclass.spawn.blah.should == 'blip'
481
+ end
482
+ end
483
+ end
484
+ end
485
+ end
486
+
487
+ # conditionally do Rails tests, if we were included as a plugin
488
+ if File.exists?("#{File.dirname(__FILE__)}/../../../../config/environment.rb")
489
+
490
+ setup_rails_database
491
+
492
+ class Foo < ActiveRecord::Base
493
+ has_many :frobnitzes, :class_name => 'Frobnitz'
494
+ end
495
+
496
+ class Bar < ActiveRecord::Base
497
+ end
498
+
499
+ class Thing < ActiveRecord::Base
500
+ has_many :frobnitzes, :class_name => 'Frobnitz'
501
+ end
502
+
503
+ class Frobnitz < ActiveRecord::Base
504
+ belongs_to :foo
505
+ belongs_to :bar
506
+ belongs_to :thing
507
+ belongs_to :bango, :class_name => 'Blah', :foreign_key => 'bangbang_id'
508
+ belongs_to :blotto, :class_name => 'YaModel', :foreign_key => 'blitblot_id'
509
+ validates_presence_of :foo
510
+ validates_presence_of :thing_id
511
+ validates_presence_of :bangbang_id
512
+ validates_presence_of :blotto
513
+ validates_presence_of :name
514
+ validates_presence_of :title, :on => :create, :message => "can't be blank"
515
+ validates_format_of :title, :with => /^\d+$/
516
+ end
517
+
518
+ class SubFrobnitz < Frobnitz
519
+ validates_presence_of :bar
520
+ end
521
+
522
+ class Blah < ActiveRecord::Base
523
+ end
524
+
525
+ class YaModel < ActiveRecord::Base
526
+ end
527
+
528
+ describe ObjectDaddy, "when integrated with Rails" do
529
+ it "should provide a means of generating and saving a class instance" do
530
+ Frobnitz.should respond_to(:generate)
531
+ end
532
+
533
+ it "should provide a means of generating and saving a class instance while raising exceptions" do
534
+ Frobnitz.should respond_to(:generate!)
535
+ end
536
+
537
+ describe "and a block is passed to generate" do
538
+ it "should yield the instance to the block" do
539
+ yielded_object = nil
540
+ YaModel.generate do |obj|
541
+ yielded_object = obj
542
+ end
543
+ YaModel.should === yielded_object
544
+ end
545
+
546
+ it "should save the instance before yielding" do
547
+ instance = Frobnitz.new
548
+ YaModel.generate do |obj|
549
+ obj.should_not be_new_record
550
+ end
551
+ end
552
+ end
553
+
554
+ describe "and a block is passed to generate!" do
555
+ it "should yield the instance to the block" do
556
+ yielded_object = nil
557
+ YaModel.generate! do |obj|
558
+ yielded_object = obj
559
+ end
560
+ YaModel.should === yielded_object
561
+ end
562
+
563
+ it "should save the instance before yielding" do
564
+ instance = Frobnitz.new
565
+ YaModel.generate! do |obj|
566
+ obj.should_not be_new_record
567
+ end
568
+ end
569
+ end
570
+
571
+ describe 'giving an exemplar path for an ActiveRecord model' do
572
+ it 'should check if a spec directory exists' do
573
+ File.expects(:directory?).with(File.join(RAILS_ROOT, 'spec'))
574
+ Frobnitz.exemplar_path
575
+ end
576
+
577
+ describe 'if a spec directory exists' do
578
+ before :each do
579
+ File.stubs(:directory?).returns(true)
580
+ end
581
+
582
+ it 'should use the spec directory' do
583
+ Frobnitz.exemplar_path.should == File.join(RAILS_ROOT, 'spec', 'exemplars')
584
+ end
585
+ end
586
+
587
+ describe 'if a spec directory does not exist' do
588
+ before :each do
589
+ File.stubs(:directory?).returns(false)
590
+ end
591
+
592
+ it 'should use the test directory' do
593
+ Frobnitz.exemplar_path.should == File.join(RAILS_ROOT, 'test', 'exemplars')
594
+ end
595
+ end
596
+ end
597
+
598
+ describe 'when an association is required by name' do
599
+ it 'should generate an instance for the association' do
600
+ foo = Foo.create(:name => 'some foo')
601
+ Foo.expects(:generate).returns(foo)
602
+ Frobnitz.spawn
603
+ end
604
+
605
+ it 'should assign an instance for the association' do
606
+ foo = Foo.create(:name => 'some foo')
607
+ Foo.stubs(:generate).returns(foo)
608
+ Frobnitz.spawn.foo.should == foo
609
+ end
610
+
611
+ it 'should generate an instance for the association using specified foreign key and class name values' do
612
+ ya_model = YaModel.create(:name => 'ya model')
613
+ YaModel.expects(:generate).returns(ya_model)
614
+ Frobnitz.spawn
615
+ end
616
+
617
+ it 'should assign an instance for the association using specified foreign key and class name values' do
618
+ ya_model = YaModel.create(:name => 'ya model')
619
+ YaModel.stubs(:generate).returns(ya_model)
620
+ Frobnitz.spawn.blotto.should == ya_model
621
+ end
622
+
623
+ it 'should use the parent object when generating an instance through a has_many association' do
624
+ foo = Foo.create(:name => 'some foo')
625
+ frob = foo.frobnitzes.generate
626
+ frob.foo.should == foo
627
+ end
628
+
629
+ it 'should not generate an instance if the attribute is overridden by nil' do
630
+ Foo.expects(:generate).never
631
+ Frobnitz.spawn(:foo => nil)
632
+ end
633
+
634
+ it 'should not assign an instance if the attribute is overridden by nil' do
635
+ Frobnitz.spawn(:foo => nil).foo.should be_nil
636
+ end
637
+
638
+ it 'should not generate an instance if the attribute (*_id) is overridden' do
639
+ foo = Foo.create(:name => 'some foo')
640
+ Foo.expects(:generate).never
641
+ Frobnitz.spawn(:foo_id => foo.id)
642
+ end
643
+
644
+ it 'should use the given attribute (*_id) instead of assigning a new association object' do
645
+ foo = Foo.create(:name => 'some foo')
646
+ Frobnitz.spawn(:foo_id => foo.id).foo.should == foo
647
+ end
648
+ end
649
+
650
+ describe 'when an association is required by ID' do
651
+ it 'should generate an instance for the association' do
652
+ thing = Thing.create(:name => 'some thing')
653
+ Thing.expects(:generate).returns(thing)
654
+ Frobnitz.spawn
655
+ end
656
+
657
+ it 'should assign an instance for the association' do
658
+ thing = Thing.create(:name => 'some thing')
659
+ Thing.stubs(:generate).returns(thing)
660
+ Frobnitz.spawn.thing.should == thing
661
+ end
662
+
663
+ it 'should generate an instance for the association using specified foreign key and class name values' do
664
+ blah = Blah.create(:bam => 'blah')
665
+ Blah.expects(:generate).returns(blah)
666
+ Frobnitz.spawn
667
+ end
668
+
669
+ it 'should assign an instance for the association using specified foreign key and class name values' do
670
+ blah = Blah.create(:bam => 'blah')
671
+ Blah.stubs(:generate).returns(blah)
672
+ Frobnitz.spawn.bango.should == blah
673
+ end
674
+
675
+ it 'should use the parent object when generating an instance through a has_many association' do
676
+ thing = Thing.create(:name => 'some thing')
677
+ frob = thing.frobnitzes.generate
678
+ frob.thing.should == thing
679
+ end
680
+
681
+ it 'should not generate an instance if the attribute is overridden by nil' do
682
+ Thing.expects(:generate).never
683
+ Frobnitz.spawn(:thing_id => nil)
684
+ end
685
+
686
+ it 'should not assign an instance if the attribute is overridden by nil' do
687
+ Frobnitz.spawn(:thing_id => nil).thing.should be_nil
688
+ end
689
+
690
+ it 'should not generate an instance if the association is overridden' do
691
+ thing = Thing.create(:name => 'some thing')
692
+ Thing.expects(:generate).never
693
+ Frobnitz.spawn(:thing => thing)
694
+ end
695
+
696
+ it 'should use the given association object instead of assigning a new one' do
697
+ thing = Thing.create(:name => 'some thing')
698
+ Frobnitz.spawn(:thing => thing).thing.should == thing
699
+ end
700
+ end
701
+
702
+ it 'should handle a belongs_to association required through inheritance' do
703
+ thing = Thing.create(:name => 'some thing')
704
+ Thing.expects(:generate).returns(thing)
705
+ SubFrobnitz.spawn
706
+ end
707
+
708
+ it 'should include belongs_to associations required by the subclass' do
709
+ bar = Bar.create
710
+ Bar.expects(:generate).returns(bar)
711
+ SubFrobnitz.spawn
712
+ end
713
+
714
+ it 'should not include belongs_to associations required by the subclass at the parent class level' do
715
+ Bar.expects(:generate).never
716
+ Frobnitz.spawn
717
+ end
718
+
719
+ it "should not generate instances of belongs_to associations which are not required by a presence_of validator" do
720
+ Bar.expects(:generate).never
721
+ Frobnitz.spawn
722
+ end
723
+
724
+ it "should not generate any values for attributes that do not have generators" do
725
+ Frobnitz.spawn.name.should be_nil
726
+ end
727
+
728
+ it "should use specified values for attributes that do not have generators" do
729
+ Frobnitz.spawn(:name => 'test').name.should == 'test'
730
+ end
731
+
732
+ it "should use specified values for attributes that would otherwise be generated" do
733
+ Foo.expects(:generate).never
734
+ foo = Foo.new
735
+ Frobnitz.spawn(:foo => foo).foo.should == foo
736
+ end
737
+
738
+ it 'should pass the supplied validator options to the real validator method' do
739
+ Blah.validates_presence_of :bam, :if => lambda { false }
740
+ Blah.new.should be_valid
741
+ end
742
+
743
+ it "should ignore optional arguments to presence_of validators" do
744
+ Frobnitz.presence_validated_attributes.should have_key(:title)
745
+ end
746
+
747
+ it "should return an unsaved record if spawning" do
748
+ Thing.spawn.should be_new_record
749
+ end
750
+
751
+ it "should return a saved record if generating" do
752
+ Thing.generate.should_not be_new_record
753
+ end
754
+
755
+ it 'should return a saved record if generating while raising exceptions' do
756
+ Thing.generate!.should_not be_new_record
757
+ end
758
+
759
+ it "should not fail if trying to generate and save an invalid object" do
760
+ lambda { Frobnitz.generate(:title => 'bob') }.should_not raise_error(ActiveRecord::RecordInvalid)
761
+ end
762
+
763
+ it "should return an invalid object if trying to generate and save an invalid object" do
764
+ Frobnitz.generate(:title => 'bob').should_not be_valid
765
+ end
766
+
767
+ it "should fail if trying to generate and save an invalid object while raising acceptions" do
768
+ lambda { Frobnitz.generate!(:title => 'bob') }.should raise_error(ActiveRecord::RecordInvalid)
769
+ end
770
+
771
+ it "should return a valid object if generate and save succeeds" do
772
+ Frobnitz.generate(:title => '5', :name => 'blah').should be_valid
773
+ end
774
+ end
775
+ end