object-daddy 1.0.0

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,1024 @@
1
+ require '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 read from all paths when exemplar_path returns an array" do
198
+ # we are using the concrete Widget class here because otherwise it's difficult to have our exemplar file work in our class
199
+ begin
200
+ # a dummy class, useful for testing the actual loading of exemplar files
201
+ Widget = Class.new(OpenStruct) { include ObjectDaddy }
202
+ File.open(@file_name, 'w') {|f| f.puts "class Widget\ngenerator_for :foo\nend\n"}
203
+ other_filename = 'widget_exemplar.rb'
204
+ File.open(other_filename, 'w') {|f| f.puts "class Widget\ngenerator_for :foo\nend\n"}
205
+ Widget.stubs(:exemplar_path).returns(['.', @file_path])
206
+ Widget.expects(:generator_for).times(2)
207
+ Widget.gather_exemplars
208
+ ensure
209
+ # clean up test data file
210
+ File.unlink(@file_name) if File.exists?(@file_name)
211
+ File.unlink(other_filename) if File.exists?(other_filename)
212
+ Object.send(:remove_const, :Widget)
213
+ end
214
+ end
215
+
216
+
217
+ it 'should record that exemplars have been registered' do
218
+ @class.expects(:exemplars_generated=).with(true)
219
+ @class.gather_exemplars
220
+ end
221
+ end
222
+
223
+ describe 'after exemplars have been registered' do
224
+ before :each do
225
+ @class.stubs(:exemplars_generated).returns(true)
226
+ end
227
+
228
+ it "should not look for exemplars for the target class in the standard exemplar path" do
229
+ @class.expects(:exemplar_path).never
230
+ @class.gather_exemplars
231
+ end
232
+
233
+ it "should not look for an exemplar for the target class, based on the class's name" do
234
+ @class.expects(:name).never
235
+ @class.gather_exemplars
236
+ end
237
+
238
+ it 'should register no generators' do
239
+ # we are using the concrete Widget class here because otherwise it's difficult to have our exemplar file work in our class
240
+ begin
241
+ # a dummy class, useful for testing the actual loading of exemplar files
242
+ Widget = Class.new(OpenStruct) { include ObjectDaddy }
243
+ File.open(@file_name, 'w') {|f| f.puts "class Widget\ngenerator_for :foo\nend\n"}
244
+ Widget.stubs(:exemplar_path).returns(@file_path)
245
+ Widget.stubs(:exemplars_generated).returns(true)
246
+ Widget.expects(:generator_for).never
247
+ Widget.gather_exemplars
248
+ ensure
249
+ # clean up test data file
250
+ File.unlink(@file_name) if File.exists?(@file_name)
251
+ Object.send(:remove_const, :Widget)
252
+ end
253
+ end
254
+
255
+ it 'should not record that exemplars have been registered' do
256
+ @class.expects(:exemplars_generated=).never
257
+ @class.gather_exemplars
258
+ end
259
+ end
260
+
261
+ it "should register no generators if no exemplar for the target class is available" do
262
+ @class.expects(:generator_for).never
263
+ @class.gather_exemplars
264
+ end
265
+ end
266
+
267
+ describe ObjectDaddy, "when spawning a class instance" do
268
+ before(:each) do
269
+ @class = Class.new(OpenStruct)
270
+ @class.send(:include, ObjectDaddy)
271
+ @file_path = [File.join(File.dirname(__FILE__), 'tmp')]
272
+ @file_name = File.join(@file_path, 'widget_exemplar.rb')
273
+ @class.stubs(:exemplar_path).returns(@file_path)
274
+ @class.stubs(:name).returns('Widget')
275
+ end
276
+
277
+ it "should yield the instance to a block if given" do
278
+ yielded_object = nil
279
+ @class.spawn do |obj|
280
+ yielded_object = obj
281
+ end
282
+ @class.should === yielded_object
283
+ end
284
+
285
+ it "should register exemplars for the target class" do
286
+ @class.expects(:gather_exemplars)
287
+ @class.spawn
288
+ end
289
+
290
+ it "should allow attributes to be overridden" do
291
+ @class.spawn(:foo => 'xyzzy').foo.should == 'xyzzy'
292
+ end
293
+
294
+ it "should use any generators registered with blocks" do
295
+ @class.generator_for :foo do |prev| "foo"; end
296
+ @class.spawn.foo.should == 'foo'
297
+ end
298
+
299
+ it "should not use a block generator for an attribute that has been overridden" do
300
+ @class.generator_for :foo do |prev| "foo"; end
301
+ @class.spawn(:foo => 'xyzzy').foo.should == 'xyzzy'
302
+ end
303
+
304
+ it "should use any generators registered with generator method names" do
305
+ @class.stubs(:generator_method).returns('bar')
306
+ @class.generator_for :foo, :method => :generator_method
307
+ @class.spawn.foo.should == 'bar'
308
+ end
309
+
310
+ it 'should fail if a generator is registered with a non-existent method name' do
311
+ @class.generator_for :foo, :method => :nonexistent_metho
312
+ lambda { @class.spawn.foo }.should raise_error
313
+ end
314
+
315
+ it "should not use a method generator for an attribute that has been overridden" do
316
+ @class.stubs(:generator_method).returns('bar')
317
+ @class.generator_for :foo, :method => :generator_method
318
+ @class.spawn(:foo => 'xyzzy').foo.should == 'xyzzy'
319
+ end
320
+
321
+ it "should use any generators registered with generator classes" do
322
+ @generator_class = Class.new do
323
+ def self.next() 'baz' end
324
+ end
325
+ @class.generator_for :foo, :class => @generator_class
326
+ @class.spawn.foo.should == 'baz'
327
+ end
328
+
329
+ it "should not use a class generator for an attribute that has been overridden" do
330
+ @generator_class = Class.new do
331
+ def self.next() 'baz' end
332
+ end
333
+ @class.generator_for :foo, :class => @generator_class
334
+ @class.spawn(:foo => 'xyzzy').foo.should == 'xyzzy'
335
+ end
336
+
337
+ it "should return the initial value first if one was registered for a block generator" do
338
+ @class.generator_for :foo, :start => 'frobnitz' do |prev| "foo"; end
339
+ @class.spawn.foo.should == 'frobnitz'
340
+ end
341
+
342
+ 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
343
+ @class.generator_for :foo, :start => 'frobnitz' do |prev| prev + 'a'; end
344
+ @class.spawn
345
+ @class.spawn.foo.should == 'frobnitza'
346
+ end
347
+
348
+ it "should return the block applied to the previous value when repeatedly calling a block generator" do
349
+ @class.generator_for :foo do |prev| prev ? prev.succ : 'test'; end
350
+ @class.spawn
351
+ @class.spawn.foo.should == 'tesu'
352
+ end
353
+
354
+ it 'should not require a block if an initial value is given' do
355
+ lambda { @class.generator_for :foo, :start => 'crapola' }.should_not raise_error(ArgumentError)
356
+ end
357
+
358
+ it 'should default the generator to increment the value if an initial value is given' do
359
+ @class.generator_for :foo, :start => 'crapola'
360
+ @class.spawn
361
+ @class.spawn.foo.should == 'crapolb'
362
+ end
363
+
364
+ it "should return the initial value first if one was registered for a method generator" do
365
+ @class.instance_eval do
366
+ def self.generator_value_method(prev)
367
+ 'foo'
368
+ end
369
+ end
370
+
371
+ @class.generator_for :foo, :start => 'frobnitz', :method => :generator_value_method
372
+ @class.spawn.foo.should == 'frobnitz'
373
+ end
374
+
375
+ 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
376
+ @class.instance_eval do
377
+ def self.generator_value_method(prev)
378
+ prev.succ
379
+ end
380
+ end
381
+
382
+ @class.generator_for :foo, :start => 'frobnitz', :method => :generator_value_method
383
+ @class.spawn
384
+ @class.spawn.foo.should == 'frobniua'
385
+ end
386
+
387
+ it "should return the method applied to the previous value when repeatedly calling a method generator" do
388
+ @class.instance_eval do
389
+ def self.generator_value_method(prev)
390
+ if prev
391
+ prev.succ
392
+ else
393
+ 'test'
394
+ end
395
+ end
396
+ end
397
+
398
+ @class.generator_for :foo, :method => :generator_value_method
399
+ @class.spawn
400
+ @class.spawn.foo.should == 'tesu'
401
+ end
402
+
403
+ it 'should use the return value for a block generator that takes no argument' do
404
+ x = 5
405
+ @class.generator_for(:foo) { x }
406
+ @class.spawn.foo.should == x
407
+ end
408
+
409
+ it 'should use the return value for a block generator that explicitly takes no argument' do
410
+ x = 5
411
+ @class.generator_for(:foo) { || x }
412
+ @class.spawn.foo.should == x
413
+ end
414
+
415
+ it 'should use the supplied value for the generated value' do
416
+ x = 5
417
+ @class.generator_for :foo, x
418
+ @class.spawn.foo.should == x
419
+ end
420
+
421
+ it 'should use the supplied attr => value value for the generated value' do
422
+ x = 5
423
+ @class.generator_for :foo => x
424
+ @class.spawn.foo.should == x
425
+ end
426
+
427
+ it "should call the normal target class constructor" do
428
+ @class.expects(:new)
429
+ @class.spawn
430
+ end
431
+
432
+ it 'should not generate a value for an attribute that has been specified as nil' do
433
+ @class.generator_for :foo => 5
434
+ @class.spawn(:foo => nil).foo.should be_nil
435
+ end
436
+
437
+ it 'should not generate a value for an attribute that has been specified as false' do
438
+ @class.generator_for :foo => 5
439
+ @class.spawn(:foo => false).foo.should be(false)
440
+ end
441
+
442
+ describe 'for an abstract parent class' do
443
+ before :each do
444
+ Widget = Class.new(OpenStruct) { include ObjectDaddy }
445
+ SubWidget = Class.new(Widget) {include ObjectDaddy }
446
+ Widget.stubs(:exemplar_path).returns(@file_path)
447
+ SubWidget.stubs(:exemplar_path).returns([File.join(@file_path, 'sub_widget_exemplar.rb')])
448
+ end
449
+
450
+ after :each do
451
+ [:Widget, :SubWidget].each { |const| Object.send(:remove_const, const) }
452
+ end
453
+
454
+ it 'should generate an instance of a specified concrete subclass (specced using a symbol)' do
455
+ Widget.generates_subclass :SubWidget
456
+ Widget.spawn.should be_instance_of(SubWidget)
457
+ end
458
+
459
+ it 'should generate an instance of a specified concrete subclass (specced using a string)' do
460
+ Widget.generates_subclass 'SubWidget'
461
+ Widget.spawn.should be_instance_of(SubWidget)
462
+ end
463
+
464
+ it 'should generate an instance of a specified concrete subclass and yield to a block if given' do
465
+ yielded_object = nil
466
+ Widget.generates_subclass :SubWidget
467
+ Widget.spawn do |obj|
468
+ yielded_object = obj
469
+ end
470
+ yielded_object.should be_instance_of(SubWidget)
471
+ end
472
+
473
+ describe 'using exemplar files' do
474
+ before :each do
475
+ File.open(@file_name, 'w') do |f|
476
+ f.puts "class Widget\ngenerates_subclass 'SubWidget'\nend"
477
+ end
478
+ end
479
+
480
+ after :each do
481
+ File.unlink @file_name
482
+ end
483
+
484
+ it 'should generate an instance fo the specified concrete subclass' do
485
+ Widget.spawn.should be_instance_of(SubWidget)
486
+ end
487
+ end
488
+ end
489
+
490
+ describe 'for a subclass' do
491
+ before :each do
492
+ @subclass = Class.new(@class)
493
+ @subclass.send(:include, ObjectDaddy)
494
+ @subfile_path = [File.join(File.dirname(__FILE__), 'tmp')]
495
+ @subfile_name = File.join(@file_path, 'sub_widget_exemplar.rb')
496
+ @subclass.stubs(:exemplar_path).returns(@file_path)
497
+ @subclass.stubs(:name).returns('SubWidget')
498
+ end
499
+
500
+ describe 'using generators from files' do
501
+ before :each do
502
+ Widget = Class.new(OpenStruct) { include ObjectDaddy }
503
+ SubWidget = Class.new(Widget) { include ObjectDaddy }
504
+
505
+ Widget.stubs(:exemplar_path).returns(@file_path)
506
+ SubWidget.stubs(:exemplar_path).returns(@subfile_path)
507
+
508
+ File.open(@file_name, 'w') do |f|
509
+ f.puts "class Widget\ngenerator_for :blah do |prev| 'blah'; end\ngenerator_for :other do |prev| 'bang'; end\nend\n"
510
+ end
511
+ end
512
+
513
+ after :each do
514
+ [@file_name, @subfile_name].each { |file| File.unlink(file) if File.exists?(file) }
515
+ [:Widget, :SubWidget].each { |const| Object.send(:remove_const, const) }
516
+ end
517
+
518
+ it 'should use generators from the parent class' do
519
+ SubWidget.spawn.other.should == 'bang'
520
+ SubWidget.spawn.blah.should == 'blah'
521
+ end
522
+
523
+ describe 'with subclass generators' do
524
+ before :each do
525
+ File.open(@subfile_name, 'w') do |f|
526
+ f.puts "class SubWidget\ngenerator_for :blah do |prev| 'blip'; end\nend\n"
527
+ end
528
+ end
529
+
530
+ it 'should still use generators from the parent class' do
531
+ SubWidget.spawn.other.should == 'bang'
532
+ end
533
+
534
+ it 'should allow overriding parent generators' do
535
+ SubWidget.spawn.blah.should == 'blip'
536
+ end
537
+ end
538
+ end
539
+
540
+ describe 'using generators called directly' do
541
+ before :each do
542
+ @class.generator_for :other do |prev| 'bang'; end
543
+ @class.generator_for :blah do |prev| 'blah'; end
544
+ end
545
+
546
+ it 'should use generators from the parent class' do
547
+ @subclass.spawn.other.should == 'bang'
548
+ @subclass.spawn.blah.should == 'blah'
549
+ end
550
+
551
+ describe 'with subclass generators' do
552
+ before :each do
553
+ @subclass.generator_for :blah do |prev| 'blip'; end
554
+ end
555
+
556
+ it 'should still use generators from the parent class' do
557
+ @subclass.spawn.other.should == 'bang'
558
+ end
559
+
560
+ it 'should allow overriding parent generators' do
561
+ @subclass.spawn.blah.should == 'blip'
562
+ end
563
+ end
564
+ end
565
+ end
566
+ end
567
+
568
+ setup_rails_database
569
+
570
+ class Foo < ActiveRecord::Base
571
+ has_many :frobnitzes, :class_name => 'Frobnitz'
572
+ end
573
+
574
+ class Baz < ActiveRecord::Base
575
+ has_many :frobnitzes, :class_name => 'Frobnitz'
576
+ end
577
+
578
+ class Bar < ActiveRecord::Base
579
+ end
580
+
581
+ class Thing < ActiveRecord::Base
582
+ has_many :frobnitzes, :class_name => 'Frobnitz'
583
+ end
584
+
585
+ class Splort < ActiveRecord::Base
586
+ has_many :frobnitzes, :class_name => 'Frobnitz'
587
+ end
588
+
589
+ class Frobnitz < ActiveRecord::Base
590
+ belongs_to :foo
591
+ belongs_to :baz
592
+ belongs_to :bar
593
+ belongs_to :thing
594
+ belongs_to :splort
595
+ belongs_to :bango, :class_name => 'Blah', :foreign_key => 'bangbang_id'
596
+ belongs_to :plunk, :class_name => 'Boog', :foreign_key => 'wakka_id'
597
+ belongs_to :blotto, :class_name => 'YaModel', :foreign_key => 'blitblot_id'
598
+ belongs_to :biscuit, :class_name => 'Cork', :foreign_key => 'poolie_id'
599
+ validates_presence_of :foo
600
+ validates :baz, :presence => true
601
+ validates_presence_of :thing_id
602
+ validates :splort_id, :presence => true
603
+ validates_presence_of :bangbang_id
604
+ validates :wakka_id, :presence => true
605
+ validates_presence_of :blotto
606
+ validates :biscuit, :presence => true
607
+ validates_presence_of :name
608
+ validates_presence_of :title, :on => :create, :message => "can't be blank"
609
+ validates_format_of :title, :with => /^\d+$/
610
+ end
611
+
612
+ class SubFrobnitz < Frobnitz
613
+ validates_presence_of :bar
614
+ end
615
+
616
+ class Boog < ActiveRecord::Base
617
+ end
618
+
619
+ class Blah < ActiveRecord::Base
620
+ end
621
+
622
+ class YaModel < ActiveRecord::Base
623
+ end
624
+
625
+ class Cork < ActiveRecord::Base
626
+ end
627
+
628
+ class ProtectedAttribute < ActiveRecord::Base
629
+ attr_accessible :public_name
630
+ end
631
+
632
+ describe ObjectDaddy, "when integrated with Rails" do
633
+ before :each do
634
+ Rails.stubs(:root).returns("somedir")
635
+ end
636
+
637
+ it "should provide a means of generating and saving a class instance" do
638
+ Frobnitz.should respond_to(:generate)
639
+ end
640
+
641
+ it "should provide a means of generating and saving a class instance while raising exceptions" do
642
+ Frobnitz.should respond_to(:generate!)
643
+ end
644
+
645
+ describe "and a block is passed to generate" do
646
+ it "should yield the instance to the block" do
647
+ yielded_object = nil
648
+ YaModel.generate do |obj|
649
+ yielded_object = obj
650
+ end
651
+ YaModel.should === yielded_object
652
+ end
653
+
654
+ it "should save the instance before yielding" do
655
+ instance = Frobnitz.new
656
+ YaModel.generate do |obj|
657
+ obj.should_not be_new_record
658
+ end
659
+ end
660
+ end
661
+
662
+ describe "and a block is passed to generate!" do
663
+ it "should yield the instance to the block" do
664
+ yielded_object = nil
665
+ YaModel.generate! do |obj|
666
+ yielded_object = obj
667
+ end
668
+ YaModel.should === yielded_object
669
+ end
670
+
671
+ it "should save the instance before yielding" do
672
+ instance = Frobnitz.new
673
+ YaModel.generate! do |obj|
674
+ obj.should_not be_new_record
675
+ end
676
+ end
677
+ end
678
+
679
+ describe 'giving an exemplar path for an ActiveRecord model' do
680
+ it 'should check if a spec directory exists' do
681
+ File.expects(:directory?).with(File.join(Rails.root, 'spec'))
682
+ File.expects(:directory?).with(File.join(Rails.root, 'test'))
683
+ Frobnitz.exemplar_path.should == []
684
+ end
685
+
686
+ describe 'if a spec directory exists' do
687
+ before :each do
688
+ File.expects(:directory?).with(File.join(Rails.root, 'spec')).returns(true)
689
+ File.expects(:directory?).with(File.join(Rails.root, 'test')).returns(false)
690
+ end
691
+
692
+ it 'should return the spec directory string in an array' do
693
+ Frobnitz.exemplar_path.should == [File.join(Rails.root, 'spec', 'exemplars')]
694
+ end
695
+ end
696
+
697
+ describe 'if a spec directory does not exist' do
698
+ before :each do
699
+ File.expects(:directory?).with(File.join(Rails.root, 'spec')).returns(false)
700
+ File.expects(:directory?).with(File.join(Rails.root, 'test')).returns(true)
701
+ end
702
+
703
+ it 'should return the test directory string in an array' do
704
+ Frobnitz.exemplar_path.should == [File.join(Rails.root, 'test', 'exemplars')]
705
+ end
706
+ end
707
+
708
+ describe 'if both directories exist' do
709
+ before :each do
710
+ File.expects(:directory?).with(File.join(Rails.root, 'spec')).returns(true)
711
+ File.expects(:directory?).with(File.join(Rails.root, 'test')).returns(true)
712
+ end
713
+
714
+ it 'should return both directory strings in an array' do
715
+ Frobnitz.exemplar_path.should == [ File.join(Rails.root, 'spec', 'exemplars'), File.join(Rails.root, 'test', 'exemplars')]
716
+ end
717
+ end
718
+
719
+ end
720
+
721
+ describe 'when an association is required by name' do
722
+ context "when required by validates_presence_of" do
723
+ it 'should generate an instance for the association' do
724
+ foo = Foo.create(:name => 'some foo')
725
+ Foo.expects(:generate).returns(foo)
726
+ Frobnitz.spawn
727
+ end
728
+
729
+ it 'should assign an instance for the association' do
730
+ foo = Foo.create(:name => 'some foo')
731
+ Foo.stubs(:generate).returns(foo)
732
+ Frobnitz.spawn.foo.should == foo
733
+ end
734
+
735
+ it 'should generate an instance for the association using specified foreign key and class name values' do
736
+ ya_model = YaModel.create(:name => 'ya model')
737
+ YaModel.expects(:generate).returns(ya_model)
738
+ Frobnitz.spawn
739
+ end
740
+
741
+ it 'should assign an instance for the association using specified foreign key and class name values' do
742
+ ya_model = YaModel.create(:name => 'ya model')
743
+ YaModel.stubs(:generate).returns(ya_model)
744
+ Frobnitz.spawn.blotto.should == ya_model
745
+ end
746
+
747
+ it 'should use the parent object when generating an instance through a has_many association' do
748
+ foo = Foo.create(:name => 'some foo')
749
+ frob = foo.frobnitzes.generate
750
+ frob.foo.should == foo
751
+ end
752
+
753
+ it 'should not generate an instance if the attribute is overridden by nil' do
754
+ Foo.expects(:generate).never
755
+ Frobnitz.spawn(:foo => nil)
756
+ end
757
+
758
+ it 'should not assign an instance if the attribute is overridden by nil' do
759
+ Frobnitz.spawn(:foo => nil).foo.should be_nil
760
+ end
761
+
762
+ it 'should not generate an instance if the attribute (*_id) is overridden' do
763
+ foo = Foo.create(:name => 'some foo')
764
+ Foo.expects(:generate).never
765
+ Frobnitz.spawn(:foo_id => foo.id)
766
+ end
767
+
768
+ it 'should use the given attribute (*_id) instead of assigning a new association object' do
769
+ foo = Foo.create(:name => 'some foo')
770
+ Frobnitz.spawn(:foo_id => foo.id).foo.should == foo
771
+ end
772
+ end
773
+
774
+ context "when required by validates :presence => true" do
775
+ it 'should generate an instance for the association' do
776
+ baz = Baz.create(:name => 'some baz')
777
+ Baz.expects(:generate).returns(baz)
778
+ Frobnitz.spawn
779
+ end
780
+
781
+ it 'should assign an instance for the association' do
782
+ baz = Baz.create(:name => 'some baz')
783
+ Baz.stubs(:generate).returns(baz)
784
+ Frobnitz.spawn.baz.should == baz
785
+ end
786
+
787
+ it 'should generate an instance for the association using specified foreign key and class name values' do
788
+ cork = Cork.create(:name => 'ya model')
789
+ Cork.expects(:generate).returns(cork)
790
+ Frobnitz.spawn
791
+ end
792
+
793
+ it 'should assign an instance for the association using specified foreign key and class name values' do
794
+ cork = Cork.create(:name => 'ya model')
795
+ Cork.stubs(:generate).returns(cork)
796
+ Frobnitz.spawn.biscuit.should == cork
797
+ end
798
+
799
+ it 'should use the parent object when generating an instance through a has_many association' do
800
+ baz = Baz.create(:name => 'some baz')
801
+ frob = baz.frobnitzes.generate
802
+ frob.baz.should == baz
803
+ end
804
+
805
+ it 'should not generate an instance if the attribute is overridden by nil' do
806
+ Baz.expects(:generate).never
807
+ Frobnitz.spawn(:baz => nil)
808
+ end
809
+
810
+ it 'should not assign an instance if the attribute is overridden by nil' do
811
+ Frobnitz.spawn(:baz => nil).baz.should be_nil
812
+ end
813
+
814
+ it 'should not generate an instance if the attribute (*_id) is overridden' do
815
+ baz = Baz.create(:name => 'some baz')
816
+ Baz.expects(:generate).never
817
+ Frobnitz.spawn(:baz_id => baz.id)
818
+ end
819
+
820
+ it 'should use the given attribute (*_id) instead of assigning a new association object' do
821
+ baz = Baz.create(:name => 'some baz')
822
+ Frobnitz.spawn(:baz_id => baz.id).baz.should == baz
823
+ end
824
+ end
825
+ end
826
+
827
+ describe 'when an association is required by ID' do
828
+ context "when required by validates_presence_of" do
829
+ it 'should generate an instance for the association' do
830
+ thing = Thing.create(:name => 'some thing')
831
+ Thing.expects(:generate).returns(thing)
832
+ Frobnitz.spawn
833
+ end
834
+
835
+ it 'should assign an instance for the association' do
836
+ thing = Thing.create(:name => 'some thing')
837
+ Thing.stubs(:generate).returns(thing)
838
+ Frobnitz.spawn.thing.should == thing
839
+ end
840
+
841
+ it 'should generate an instance for the association using specified foreign key and class name values' do
842
+ blah = Blah.create(:bam => 'blah')
843
+ Blah.expects(:generate).returns(blah)
844
+ Frobnitz.spawn
845
+ end
846
+
847
+ it 'should assign an instance for the association using specified foreign key and class name values' do
848
+ blah = Blah.create(:bam => 'blah')
849
+ Blah.stubs(:generate).returns(blah)
850
+ Frobnitz.spawn.bango.should == blah
851
+ end
852
+
853
+ it 'should use the parent object when generating an instance through a has_many association' do
854
+ thing = Thing.create(:name => 'some thing')
855
+ frob = thing.frobnitzes.generate
856
+ frob.thing.should == thing
857
+ end
858
+
859
+ it 'should not generate an instance if the attribute is overridden by nil' do
860
+ Thing.expects(:generate).never
861
+ Frobnitz.spawn(:thing_id => nil)
862
+ end
863
+
864
+ it 'should not assign an instance if the attribute is overridden by nil' do
865
+ Frobnitz.spawn(:thing_id => nil).thing.should be_nil
866
+ end
867
+
868
+ it 'should not generate an instance if the association is overridden' do
869
+ thing = Thing.create(:name => 'some thing')
870
+ Thing.expects(:generate).never
871
+ Frobnitz.spawn(:thing => thing)
872
+ end
873
+
874
+ it 'should use the given association object instead of assigning a new one' do
875
+ thing = Thing.create(:name => 'some thing')
876
+ Frobnitz.spawn(:thing => thing).thing.should == thing
877
+ end
878
+ end
879
+
880
+ context "when required by validates :presence => true" do
881
+ it 'should generate an instance for the association' do
882
+ splort = Splort.create(:name => 'some splort')
883
+ Splort.expects(:generate).returns(splort)
884
+ Frobnitz.spawn
885
+ end
886
+
887
+ it 'should assign an instance for the association' do
888
+ splort = Splort.create(:name => 'some splort')
889
+ Splort.stubs(:generate).returns(splort)
890
+ Frobnitz.spawn.splort.should == splort
891
+ end
892
+
893
+ it 'should generate an instance for the association using specified foreign key and class name values' do
894
+ blah = Boog.create(:bam => 'blah')
895
+ Boog.expects(:generate).returns(blah)
896
+ Frobnitz.spawn
897
+ end
898
+
899
+ it 'should assign an instance for the association using specified foreign key and class name values' do
900
+ blah = Boog.create(:bam => 'blah')
901
+ Boog.stubs(:generate).returns(blah)
902
+ Frobnitz.spawn.plunk.should == blah
903
+ end
904
+
905
+ it 'should use the parent object when generating an instance through a has_many association' do
906
+ splort = Splort.create(:name => 'some splort')
907
+ frob = splort.frobnitzes.generate
908
+ frob.splort.should == splort
909
+ end
910
+
911
+ it 'should not generate an instance if the attribute is overridden by nil' do
912
+ Splort.expects(:generate).never
913
+ Frobnitz.spawn(:splort_id => nil)
914
+ end
915
+
916
+ it 'should not assign an instance if the attribute is overridden by nil' do
917
+ Frobnitz.spawn(:splort_id => nil).splort.should be_nil
918
+ end
919
+
920
+ it 'should not generate an instance if the association is overridden' do
921
+ splort = Splort.create(:name => 'some splort')
922
+ Splort.expects(:generate).never
923
+ Frobnitz.spawn(:splort => splort)
924
+ end
925
+
926
+ it 'should use the given association object instead of assigning a new one' do
927
+ splort = Splort.create(:name => 'some splort')
928
+ Frobnitz.spawn(:splort => splort).splort.should == splort
929
+ end
930
+ end
931
+ end
932
+
933
+ it 'should handle a belongs_to association required through inheritance' do
934
+ thing = Thing.create(:name => 'some thing')
935
+ Thing.expects(:generate).returns(thing)
936
+ SubFrobnitz.spawn
937
+ end
938
+
939
+ it 'should include belongs_to associations required by the subclass' do
940
+ bar = Bar.create
941
+ Bar.expects(:generate).returns(bar)
942
+ SubFrobnitz.spawn
943
+ end
944
+
945
+ it 'should not include belongs_to associations required by the subclass at the parent class level' do
946
+ Bar.expects(:generate).never
947
+ Frobnitz.spawn
948
+ end
949
+
950
+ it "should not generate instances of belongs_to associations which are not required by a presence_of validator" do
951
+ Bar.expects(:generate).never
952
+ Frobnitz.spawn
953
+ end
954
+
955
+ it "should not generate any values for attributes that do not have generators" do
956
+ Frobnitz.spawn.name.should be_nil
957
+ end
958
+
959
+ it "should use specified values for attributes that do not have generators" do
960
+ Frobnitz.spawn(:name => 'test').name.should == 'test'
961
+ end
962
+
963
+ it "should use specified values for attributes that would otherwise be generated" do
964
+ Foo.expects(:generate).never
965
+ foo = Foo.new
966
+ Frobnitz.spawn(:foo => foo).foo.should == foo
967
+ end
968
+
969
+ it 'should pass the supplied validator options to the real validator method' do
970
+ Blah.validates_presence_of :bam, :if => lambda { false }
971
+ Blah.new.should be_valid
972
+ end
973
+
974
+ it "should ignore optional arguments to presence_of validators" do
975
+ Frobnitz.presence_validated_attributes.should have_key(:title)
976
+ end
977
+
978
+ it "should return an unsaved record if spawning" do
979
+ Thing.spawn.should be_new_record
980
+ end
981
+
982
+ it "should return a saved record if generating" do
983
+ Thing.generate.should_not be_new_record
984
+ end
985
+
986
+ it 'should return a saved record if generating while raising exceptions' do
987
+ Thing.generate!.should_not be_new_record
988
+ end
989
+
990
+ it "should not fail if trying to generate and save an invalid object" do
991
+ lambda { Frobnitz.generate(:title => 'bob') }.should_not raise_error(ActiveRecord::RecordInvalid)
992
+ end
993
+
994
+ it "should return an invalid object if trying to generate and save an invalid object" do
995
+ Frobnitz.generate(:title => 'bob').should_not be_valid
996
+ end
997
+
998
+ it "should fail if trying to generate and save an invalid object while raising acceptions" do
999
+ lambda { Frobnitz.generate!(:title => 'bob') }.should raise_error(ActiveRecord::RecordInvalid)
1000
+ end
1001
+
1002
+ it "should return a valid object if generate and save succeeds" do
1003
+ Frobnitz.generate(:title => '5', :name => 'blah').should be_valid
1004
+ end
1005
+
1006
+ it 'should allow attributes to be overriden with string keys' do
1007
+ Frobnitz.generator_for :name => 'thing'
1008
+ Frobnitz.generate('name' => 'boo').name.should == 'boo'
1009
+ end
1010
+
1011
+ describe "supporting mass-assignment protected attributes" do
1012
+ it "should allow setting a value for a non-protected attribute" do
1013
+ ProtectedAttribute.generate!(:public_name => 'no_worries').public_name.should == 'no_worries'
1014
+ end
1015
+
1016
+ it "should have a protected attribute, which is not set when using regular create!" do
1017
+ ProtectedAttribute.create!(:private_name => 'protected name').private_name.should == nil
1018
+ end
1019
+
1020
+ it "should allow setting a value for a protected attribute" do
1021
+ ProtectedAttribute.generate!(:private_name => 'protected name').private_name.should == 'protected name'
1022
+ end
1023
+ end
1024
+ end