mconnell-object_daddy 0.4.2

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