edavis10-object_daddy 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,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