object-daddy 1.0.0

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