edavis10-object_daddy 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,12 @@
1
+ unless ActiveRecord::Base.respond_to? :inherited_with_object_daddy
2
+ class ActiveRecord::Base
3
+ def self.inherited_with_object_daddy(subclass)
4
+ self.inherited_without_object_daddy(subclass)
5
+ subclass.send(:include, ObjectDaddy) unless subclass < ObjectDaddy
6
+ end
7
+
8
+ class << self
9
+ alias_method_chain :inherited, :object_daddy
10
+ end
11
+ end
12
+ end
@@ -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,894 @@
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\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
+ # conditionally do Rails tests, if we were included as a plugin
569
+ if File.exists?("#{File.dirname(__FILE__)}/../../../../config/environment.rb")
570
+
571
+ setup_rails_database
572
+
573
+ class Foo < ActiveRecord::Base
574
+ has_many :frobnitzes, :class_name => 'Frobnitz'
575
+ end
576
+
577
+ class Bar < ActiveRecord::Base
578
+ end
579
+
580
+ class Thing < ActiveRecord::Base
581
+ has_many :frobnitzes, :class_name => 'Frobnitz'
582
+ end
583
+
584
+ class Frobnitz < ActiveRecord::Base
585
+ belongs_to :foo
586
+ belongs_to :bar
587
+ belongs_to :thing
588
+ belongs_to :bango, :class_name => 'Blah', :foreign_key => 'bangbang_id'
589
+ belongs_to :blotto, :class_name => 'YaModel', :foreign_key => 'blitblot_id'
590
+ validates_presence_of :foo
591
+ validates_presence_of :thing_id
592
+ validates_presence_of :bangbang_id
593
+ validates_presence_of :blotto
594
+ validates_presence_of :name
595
+ validates_presence_of :title, :on => :create, :message => "can't be blank"
596
+ validates_format_of :title, :with => /^\d+$/
597
+ end
598
+
599
+ class SubFrobnitz < Frobnitz
600
+ validates_presence_of :bar
601
+ end
602
+
603
+ class Blah < ActiveRecord::Base
604
+ end
605
+
606
+ class YaModel < ActiveRecord::Base
607
+ end
608
+
609
+ class ProtectedAttribute < ActiveRecord::Base
610
+ attr_accessible :public_name
611
+ end
612
+
613
+ describe ObjectDaddy, "when integrated with Rails" do
614
+ it "should provide a means of generating and saving a class instance" do
615
+ Frobnitz.should respond_to(:generate)
616
+ end
617
+
618
+ it "should provide a means of generating and saving a class instance while raising exceptions" do
619
+ Frobnitz.should respond_to(:generate!)
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 "and a block is passed to generate!" do
640
+ it "should yield the instance to the block" do
641
+ yielded_object = nil
642
+ YaModel.generate! do |obj|
643
+ yielded_object = obj
644
+ end
645
+ YaModel.should === yielded_object
646
+ end
647
+
648
+ it "should save the instance before yielding" do
649
+ instance = Frobnitz.new
650
+ YaModel.generate! do |obj|
651
+ obj.should_not be_new_record
652
+ end
653
+ end
654
+ end
655
+
656
+ describe 'giving an exemplar path for an ActiveRecord model' do
657
+ it 'should check if a spec directory exists' do
658
+ File.expects(:directory?).with(File.join(RAILS_ROOT, 'spec'))
659
+ File.expects(:directory?).with(File.join(RAILS_ROOT, 'test'))
660
+ Frobnitz.exemplar_path.should == []
661
+ end
662
+
663
+ describe 'if a spec directory exists' do
664
+ before :each do
665
+ File.expects(:directory?).with(File.join(RAILS_ROOT, 'spec')).returns(true)
666
+ File.expects(:directory?).with(File.join(RAILS_ROOT, 'test')).returns(false)
667
+ end
668
+
669
+ it 'should return the spec directory string in an array' do
670
+ Frobnitz.exemplar_path.should == [File.join(RAILS_ROOT, 'spec', 'exemplars')]
671
+ end
672
+ end
673
+
674
+ describe 'if a spec directory does not exist' do
675
+ before :each do
676
+ File.expects(:directory?).with(File.join(RAILS_ROOT, 'spec')).returns(false)
677
+ File.expects(:directory?).with(File.join(RAILS_ROOT, 'test')).returns(true)
678
+ end
679
+
680
+ it 'should return the test directory string in an array' do
681
+ Frobnitz.exemplar_path.should == [File.join(RAILS_ROOT, 'test', 'exemplars')]
682
+ end
683
+ end
684
+
685
+ describe 'if both directories exist' do
686
+ before :each do
687
+ File.expects(:directory?).with(File.join(RAILS_ROOT, 'spec')).returns(true)
688
+ File.expects(:directory?).with(File.join(RAILS_ROOT, 'test')).returns(true)
689
+ end
690
+
691
+ it 'should return both directory strings in an array' do
692
+ Frobnitz.exemplar_path.should == [ File.join(RAILS_ROOT, 'spec', 'exemplars'), File.join(RAILS_ROOT, 'test', 'exemplars')]
693
+ end
694
+ end
695
+
696
+ end
697
+
698
+ describe 'when an association is required by name' do
699
+ it 'should generate an instance for the association' do
700
+ foo = Foo.create(:name => 'some foo')
701
+ Foo.expects(:generate).returns(foo)
702
+ Frobnitz.spawn
703
+ end
704
+
705
+ it 'should assign an instance for the association' do
706
+ foo = Foo.create(:name => 'some foo')
707
+ Foo.stubs(:generate).returns(foo)
708
+ Frobnitz.spawn.foo.should == foo
709
+ end
710
+
711
+ it 'should generate an instance for the association using specified foreign key and class name values' do
712
+ ya_model = YaModel.create(:name => 'ya model')
713
+ YaModel.expects(:generate).returns(ya_model)
714
+ Frobnitz.spawn
715
+ end
716
+
717
+ it 'should assign an instance for the association using specified foreign key and class name values' do
718
+ ya_model = YaModel.create(:name => 'ya model')
719
+ YaModel.stubs(:generate).returns(ya_model)
720
+ Frobnitz.spawn.blotto.should == ya_model
721
+ end
722
+
723
+ it 'should use the parent object when generating an instance through a has_many association' do
724
+ foo = Foo.create(:name => 'some foo')
725
+ frob = foo.frobnitzes.generate
726
+ frob.foo.should == foo
727
+ end
728
+
729
+ it 'should not generate an instance if the attribute is overridden by nil' do
730
+ Foo.expects(:generate).never
731
+ Frobnitz.spawn(:foo => nil)
732
+ end
733
+
734
+ it 'should not assign an instance if the attribute is overridden by nil' do
735
+ Frobnitz.spawn(:foo => nil).foo.should be_nil
736
+ end
737
+
738
+ it 'should not generate an instance if the attribute (*_id) is overridden' do
739
+ foo = Foo.create(:name => 'some foo')
740
+ Foo.expects(:generate).never
741
+ Frobnitz.spawn(:foo_id => foo.id)
742
+ end
743
+
744
+ it 'should use the given attribute (*_id) instead of assigning a new association object' do
745
+ foo = Foo.create(:name => 'some foo')
746
+ Frobnitz.spawn(:foo_id => foo.id).foo.should == foo
747
+ end
748
+ end
749
+
750
+ describe 'when an association is required by ID' do
751
+ it 'should generate an instance for the association' do
752
+ thing = Thing.create(:name => 'some thing')
753
+ Thing.expects(:generate).returns(thing)
754
+ Frobnitz.spawn
755
+ end
756
+
757
+ it 'should assign an instance for the association' do
758
+ thing = Thing.create(:name => 'some thing')
759
+ Thing.stubs(:generate).returns(thing)
760
+ Frobnitz.spawn.thing.should == thing
761
+ end
762
+
763
+ it 'should generate an instance for the association using specified foreign key and class name values' do
764
+ blah = Blah.create(:bam => 'blah')
765
+ Blah.expects(:generate).returns(blah)
766
+ Frobnitz.spawn
767
+ end
768
+
769
+ it 'should assign an instance for the association using specified foreign key and class name values' do
770
+ blah = Blah.create(:bam => 'blah')
771
+ Blah.stubs(:generate).returns(blah)
772
+ Frobnitz.spawn.bango.should == blah
773
+ end
774
+
775
+ it 'should use the parent object when generating an instance through a has_many association' do
776
+ thing = Thing.create(:name => 'some thing')
777
+ frob = thing.frobnitzes.generate
778
+ frob.thing.should == thing
779
+ end
780
+
781
+ it 'should not generate an instance if the attribute is overridden by nil' do
782
+ Thing.expects(:generate).never
783
+ Frobnitz.spawn(:thing_id => nil)
784
+ end
785
+
786
+ it 'should not assign an instance if the attribute is overridden by nil' do
787
+ Frobnitz.spawn(:thing_id => nil).thing.should be_nil
788
+ end
789
+
790
+ it 'should not generate an instance if the association is overridden' do
791
+ thing = Thing.create(:name => 'some thing')
792
+ Thing.expects(:generate).never
793
+ Frobnitz.spawn(:thing => thing)
794
+ end
795
+
796
+ it 'should use the given association object instead of assigning a new one' do
797
+ thing = Thing.create(:name => 'some thing')
798
+ Frobnitz.spawn(:thing => thing).thing.should == thing
799
+ end
800
+ end
801
+
802
+ it 'should handle a belongs_to association required through inheritance' do
803
+ thing = Thing.create(:name => 'some thing')
804
+ Thing.expects(:generate).returns(thing)
805
+ SubFrobnitz.spawn
806
+ end
807
+
808
+ it 'should include belongs_to associations required by the subclass' do
809
+ bar = Bar.create
810
+ Bar.expects(:generate).returns(bar)
811
+ SubFrobnitz.spawn
812
+ end
813
+
814
+ it 'should not include belongs_to associations required by the subclass at the parent class level' do
815
+ Bar.expects(:generate).never
816
+ Frobnitz.spawn
817
+ end
818
+
819
+ it "should not generate instances of belongs_to associations which are not required by a presence_of validator" do
820
+ Bar.expects(:generate).never
821
+ Frobnitz.spawn
822
+ end
823
+
824
+ it "should not generate any values for attributes that do not have generators" do
825
+ Frobnitz.spawn.name.should be_nil
826
+ end
827
+
828
+ it "should use specified values for attributes that do not have generators" do
829
+ Frobnitz.spawn(:name => 'test').name.should == 'test'
830
+ end
831
+
832
+ it "should use specified values for attributes that would otherwise be generated" do
833
+ Foo.expects(:generate).never
834
+ foo = Foo.new
835
+ Frobnitz.spawn(:foo => foo).foo.should == foo
836
+ end
837
+
838
+ it 'should pass the supplied validator options to the real validator method' do
839
+ Blah.validates_presence_of :bam, :if => lambda { false }
840
+ Blah.new.should be_valid
841
+ end
842
+
843
+ it "should ignore optional arguments to presence_of validators" do
844
+ Frobnitz.presence_validated_attributes.should have_key(:title)
845
+ end
846
+
847
+ it "should return an unsaved record if spawning" do
848
+ Thing.spawn.should be_new_record
849
+ end
850
+
851
+ it "should return a saved record if generating" do
852
+ Thing.generate.should_not be_new_record
853
+ end
854
+
855
+ it 'should return a saved record if generating while raising exceptions' do
856
+ Thing.generate!.should_not be_new_record
857
+ end
858
+
859
+ it "should not fail if trying to generate and save an invalid object" do
860
+ lambda { Frobnitz.generate(:title => 'bob') }.should_not raise_error(ActiveRecord::RecordInvalid)
861
+ end
862
+
863
+ it "should return an invalid object if trying to generate and save an invalid object" do
864
+ Frobnitz.generate(:title => 'bob').should_not be_valid
865
+ end
866
+
867
+ it "should fail if trying to generate and save an invalid object while raising acceptions" do
868
+ lambda { Frobnitz.generate!(:title => 'bob') }.should raise_error(ActiveRecord::RecordInvalid)
869
+ end
870
+
871
+ it "should return a valid object if generate and save succeeds" do
872
+ Frobnitz.generate(:title => '5', :name => 'blah').should be_valid
873
+ end
874
+
875
+ it 'should allow attributes to be overriden with string keys' do
876
+ Frobnitz.generator_for :name => 'thing'
877
+ Frobnitz.generate('name' => 'boo').name.should == 'boo'
878
+ end
879
+
880
+ describe "supporting mass-assignment protected attributes" do
881
+ it "should allow setting a value for a non-protected attribute" do
882
+ ProtectedAttribute.generate!(:public_name => 'no_worries').public_name.should == 'no_worries'
883
+ end
884
+
885
+ it "should have a protected attribute, which is not set when using regular create!" do
886
+ ProtectedAttribute.create!(:private_name => 'protected name').private_name.should == nil
887
+ end
888
+
889
+ it "should allow setting a value for a protected attribute" do
890
+ ProtectedAttribute.generate!(:private_name => 'protected name').private_name.should == 'protected name'
891
+ end
892
+ end
893
+ end
894
+ end