mconnell-object_daddy 0.4.2

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.
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), "..", "init")
@@ -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,848 @@
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 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\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.blah.should == 'blah'
520
+ end
521
+
522
+ it 'should let subclass generators override parent generators' do
523
+ File.open(@subfile_name, 'w') do |f|
524
+ f.puts "class SubWidget\ngenerator_for :blah do |prev| 'blip'; end\nend\n"
525
+ end
526
+ SubWidget.spawn.blah.should == 'blip'
527
+ end
528
+ end
529
+
530
+ describe 'using generators called directly' do
531
+ it 'should use generators from the parent class' do
532
+ @class.generator_for :blah do |prev| 'blah'; end
533
+ @subclass.spawn.blah.should == 'blah'
534
+ end
535
+
536
+ it 'should let subclass generators override parent generators' do
537
+ pending 'figuring out what to do about this, including deciding whether or not this is even important' do
538
+ @class.generator_for :blah do |prev| 'blah'; end
539
+ # p @class
540
+ # p @subclass
541
+ # @subclass.send(:gather_exemplars)
542
+ # p @subclass.generators
543
+ @subclass.generator_for :blah do |prev| 'blip'; end
544
+ # @subclass.send(:gather_exemplars)
545
+ # p @subclass.generators
546
+ # p @subclass.generators[:blah][:generator][:block].call
547
+ # @subclass.send(:gather_exemplars)
548
+ @subclass.spawn.blah.should == 'blip'
549
+ end
550
+ end
551
+ end
552
+ end
553
+ end
554
+
555
+ # conditionally do Rails tests, if we were included as a plugin
556
+ if File.exists?("#{File.dirname(__FILE__)}/../../../../config/environment.rb")
557
+
558
+ setup_rails_database
559
+
560
+ class Foo < ActiveRecord::Base
561
+ has_many :frobnitzes, :class_name => 'Frobnitz'
562
+ end
563
+
564
+ class Bar < ActiveRecord::Base
565
+ end
566
+
567
+ class Thing < ActiveRecord::Base
568
+ has_many :frobnitzes, :class_name => 'Frobnitz'
569
+ end
570
+
571
+ class Frobnitz < ActiveRecord::Base
572
+ belongs_to :foo
573
+ belongs_to :bar
574
+ belongs_to :thing
575
+ belongs_to :bango, :class_name => 'Blah', :foreign_key => 'bangbang_id'
576
+ belongs_to :blotto, :class_name => 'YaModel', :foreign_key => 'blitblot_id'
577
+ validates_presence_of :foo
578
+ validates_presence_of :thing_id
579
+ validates_presence_of :bangbang_id
580
+ validates_presence_of :blotto
581
+ validates_presence_of :name
582
+ validates_presence_of :title, :on => :create, :message => "can't be blank"
583
+ validates_format_of :title, :with => /^\d+$/
584
+ end
585
+
586
+ class SubFrobnitz < Frobnitz
587
+ validates_presence_of :bar
588
+ end
589
+
590
+ class Blah < ActiveRecord::Base
591
+ end
592
+
593
+ class YaModel < ActiveRecord::Base
594
+ end
595
+
596
+ describe ObjectDaddy, "when integrated with Rails" do
597
+ it "should provide a means of generating and saving a class instance" do
598
+ Frobnitz.should respond_to(:generate)
599
+ end
600
+
601
+ it "should provide a means of generating and saving a class instance while raising exceptions" do
602
+ Frobnitz.should respond_to(:generate!)
603
+ end
604
+
605
+ describe "and a block is passed to generate" do
606
+ it "should yield the instance to the block" do
607
+ yielded_object = nil
608
+ YaModel.generate do |obj|
609
+ yielded_object = obj
610
+ end
611
+ YaModel.should === yielded_object
612
+ end
613
+
614
+ it "should save the instance before yielding" do
615
+ instance = Frobnitz.new
616
+ YaModel.generate do |obj|
617
+ obj.should_not be_new_record
618
+ end
619
+ end
620
+ end
621
+
622
+ describe "and a block is passed to generate!" do
623
+ it "should yield the instance to the block" do
624
+ yielded_object = nil
625
+ YaModel.generate! do |obj|
626
+ yielded_object = obj
627
+ end
628
+ YaModel.should === yielded_object
629
+ end
630
+
631
+ it "should save the instance before yielding" do
632
+ instance = Frobnitz.new
633
+ YaModel.generate! do |obj|
634
+ obj.should_not be_new_record
635
+ end
636
+ end
637
+ end
638
+
639
+ describe 'giving an exemplar path for an ActiveRecord model' do
640
+ it 'should check if a spec directory exists' do
641
+ File.expects(:directory?).with(File.join(RAILS_ROOT, 'spec'))
642
+ Frobnitz.exemplar_path
643
+ end
644
+
645
+ describe 'if a spec directory exists' do
646
+ before :each do
647
+ File.stubs(:directory?).returns(true)
648
+ end
649
+
650
+ it 'should use the spec directory' do
651
+ Frobnitz.exemplar_path.should == File.join(RAILS_ROOT, 'spec', 'exemplars')
652
+ end
653
+ end
654
+
655
+ describe 'if a spec directory does not exist' do
656
+ before :each do
657
+ File.stubs(:directory?).returns(false)
658
+ end
659
+
660
+ it 'should use the test directory' do
661
+ Frobnitz.exemplar_path.should == File.join(RAILS_ROOT, 'test', 'exemplars')
662
+ end
663
+ end
664
+ end
665
+
666
+ describe 'when an association is required by name' do
667
+ it 'should generate an instance for the association' do
668
+ foo = Foo.create(:name => 'some foo')
669
+ Foo.expects(:generate).returns(foo)
670
+ Frobnitz.spawn
671
+ end
672
+
673
+ it 'should assign an instance for the association' do
674
+ foo = Foo.create(:name => 'some foo')
675
+ Foo.stubs(:generate).returns(foo)
676
+ Frobnitz.spawn.foo.should == foo
677
+ end
678
+
679
+ it 'should generate an instance for the association using specified foreign key and class name values' do
680
+ ya_model = YaModel.create(:name => 'ya model')
681
+ YaModel.expects(:generate).returns(ya_model)
682
+ Frobnitz.spawn
683
+ end
684
+
685
+ it 'should assign an instance for the association using specified foreign key and class name values' do
686
+ ya_model = YaModel.create(:name => 'ya model')
687
+ YaModel.stubs(:generate).returns(ya_model)
688
+ Frobnitz.spawn.blotto.should == ya_model
689
+ end
690
+
691
+ it 'should use the parent object when generating an instance through a has_many association' do
692
+ foo = Foo.create(:name => 'some foo')
693
+ frob = foo.frobnitzes.generate
694
+ frob.foo.should == foo
695
+ end
696
+
697
+ it 'should not generate an instance if the attribute is overridden by nil' do
698
+ Foo.expects(:generate).never
699
+ Frobnitz.spawn(:foo => nil)
700
+ end
701
+
702
+ it 'should not assign an instance if the attribute is overridden by nil' do
703
+ Frobnitz.spawn(:foo => nil).foo.should be_nil
704
+ end
705
+
706
+ it 'should not generate an instance if the attribute (*_id) is overridden' do
707
+ foo = Foo.create(:name => 'some foo')
708
+ Foo.expects(:generate).never
709
+ Frobnitz.spawn(:foo_id => foo.id)
710
+ end
711
+
712
+ it 'should use the given attribute (*_id) instead of assigning a new association object' do
713
+ foo = Foo.create(:name => 'some foo')
714
+ Frobnitz.spawn(:foo_id => foo.id).foo.should == foo
715
+ end
716
+ end
717
+
718
+ describe 'when an association is required by ID' do
719
+ it 'should generate an instance for the association' do
720
+ thing = Thing.create(:name => 'some thing')
721
+ Thing.expects(:generate).returns(thing)
722
+ Frobnitz.spawn
723
+ end
724
+
725
+ it 'should assign an instance for the association' do
726
+ thing = Thing.create(:name => 'some thing')
727
+ Thing.stubs(:generate).returns(thing)
728
+ Frobnitz.spawn.thing.should == thing
729
+ end
730
+
731
+ it 'should generate an instance for the association using specified foreign key and class name values' do
732
+ blah = Blah.create(:bam => 'blah')
733
+ Blah.expects(:generate).returns(blah)
734
+ Frobnitz.spawn
735
+ end
736
+
737
+ it 'should assign an instance for the association using specified foreign key and class name values' do
738
+ blah = Blah.create(:bam => 'blah')
739
+ Blah.stubs(:generate).returns(blah)
740
+ Frobnitz.spawn.bango.should == blah
741
+ end
742
+
743
+ it 'should use the parent object when generating an instance through a has_many association' do
744
+ thing = Thing.create(:name => 'some thing')
745
+ frob = thing.frobnitzes.generate
746
+ frob.thing.should == thing
747
+ end
748
+
749
+ it 'should not generate an instance if the attribute is overridden by nil' do
750
+ Thing.expects(:generate).never
751
+ Frobnitz.spawn(:thing_id => nil)
752
+ end
753
+
754
+ it 'should not assign an instance if the attribute is overridden by nil' do
755
+ Frobnitz.spawn(:thing_id => nil).thing.should be_nil
756
+ end
757
+
758
+ it 'should not generate an instance if the association is overridden' do
759
+ thing = Thing.create(:name => 'some thing')
760
+ Thing.expects(:generate).never
761
+ Frobnitz.spawn(:thing => thing)
762
+ end
763
+
764
+ it 'should use the given association object instead of assigning a new one' do
765
+ thing = Thing.create(:name => 'some thing')
766
+ Frobnitz.spawn(:thing => thing).thing.should == thing
767
+ end
768
+ end
769
+
770
+ it 'should handle a belongs_to association required through inheritance' do
771
+ thing = Thing.create(:name => 'some thing')
772
+ Thing.expects(:generate).returns(thing)
773
+ SubFrobnitz.spawn
774
+ end
775
+
776
+ it 'should include belongs_to associations required by the subclass' do
777
+ bar = Bar.create
778
+ Bar.expects(:generate).returns(bar)
779
+ SubFrobnitz.spawn
780
+ end
781
+
782
+ it 'should not include belongs_to associations required by the subclass at the parent class level' do
783
+ Bar.expects(:generate).never
784
+ Frobnitz.spawn
785
+ end
786
+
787
+ it "should not generate instances of belongs_to associations which are not required by a presence_of validator" do
788
+ Bar.expects(:generate).never
789
+ Frobnitz.spawn
790
+ end
791
+
792
+ it "should not generate any values for attributes that do not have generators" do
793
+ Frobnitz.spawn.name.should be_nil
794
+ end
795
+
796
+ it "should use specified values for attributes that do not have generators" do
797
+ Frobnitz.spawn(:name => 'test').name.should == 'test'
798
+ end
799
+
800
+ it "should use specified values for attributes that would otherwise be generated" do
801
+ Foo.expects(:generate).never
802
+ foo = Foo.new
803
+ Frobnitz.spawn(:foo => foo).foo.should == foo
804
+ end
805
+
806
+ it 'should pass the supplied validator options to the real validator method' do
807
+ Blah.validates_presence_of :bam, :if => lambda { false }
808
+ Blah.new.should be_valid
809
+ end
810
+
811
+ it "should ignore optional arguments to presence_of validators" do
812
+ Frobnitz.presence_validated_attributes.should have_key(:title)
813
+ end
814
+
815
+ it "should return an unsaved record if spawning" do
816
+ Thing.spawn.should be_new_record
817
+ end
818
+
819
+ it "should return a saved record if generating" do
820
+ Thing.generate.should_not be_new_record
821
+ end
822
+
823
+ it 'should return a saved record if generating while raising exceptions' do
824
+ Thing.generate!.should_not be_new_record
825
+ end
826
+
827
+ it "should not fail if trying to generate and save an invalid object" do
828
+ lambda { Frobnitz.generate(:title => 'bob') }.should_not raise_error(ActiveRecord::RecordInvalid)
829
+ end
830
+
831
+ it "should return an invalid object if trying to generate and save an invalid object" do
832
+ Frobnitz.generate(:title => 'bob').should_not be_valid
833
+ end
834
+
835
+ it "should fail if trying to generate and save an invalid object while raising acceptions" do
836
+ lambda { Frobnitz.generate!(:title => 'bob') }.should raise_error(ActiveRecord::RecordInvalid)
837
+ end
838
+
839
+ it "should return a valid object if generate and save succeeds" do
840
+ Frobnitz.generate(:title => '5', :name => 'blah').should be_valid
841
+ end
842
+
843
+ it 'should allow attributes to be overriden with string keys' do
844
+ Frobnitz.generator_for :name => 'thing'
845
+ Frobnitz.generate('name' => 'boo').name.should == 'boo'
846
+ end
847
+ end
848
+ end