thorero 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/History.txt +1 -0
  2. data/LICENSE +20 -0
  3. data/Manifest +45 -0
  4. data/Manifest.txt +29 -0
  5. data/README.txt +3 -0
  6. data/Rakefile +180 -0
  7. data/lib/extlib.rb +32 -0
  8. data/lib/extlib/assertions.rb +8 -0
  9. data/lib/extlib/blank.rb +42 -0
  10. data/lib/extlib/class.rb +175 -0
  11. data/lib/extlib/hash.rb +410 -0
  12. data/lib/extlib/hook.rb +366 -0
  13. data/lib/extlib/inflection.rb +141 -0
  14. data/lib/extlib/lazy_array.rb +106 -0
  15. data/lib/extlib/logger.rb +202 -0
  16. data/lib/extlib/mash.rb +143 -0
  17. data/lib/extlib/module.rb +37 -0
  18. data/lib/extlib/object.rb +165 -0
  19. data/lib/extlib/object_space.rb +13 -0
  20. data/lib/extlib/pathname.rb +5 -0
  21. data/lib/extlib/pooling.rb +233 -0
  22. data/lib/extlib/rubygems.rb +38 -0
  23. data/lib/extlib/simple_set.rb +39 -0
  24. data/lib/extlib/string.rb +132 -0
  25. data/lib/extlib/struct.rb +8 -0
  26. data/lib/extlib/tasks/release.rb +11 -0
  27. data/lib/extlib/time.rb +12 -0
  28. data/lib/extlib/version.rb +3 -0
  29. data/lib/extlib/virtual_file.rb +10 -0
  30. data/spec/blank_spec.rb +85 -0
  31. data/spec/hash_spec.rb +524 -0
  32. data/spec/hook_spec.rb +1198 -0
  33. data/spec/inflection_spec.rb +50 -0
  34. data/spec/lazy_array_spec.rb +896 -0
  35. data/spec/mash_spec.rb +244 -0
  36. data/spec/module_spec.rb +58 -0
  37. data/spec/object_space_spec.rb +9 -0
  38. data/spec/object_spec.rb +98 -0
  39. data/spec/pooling_spec.rb +486 -0
  40. data/spec/simple_set_spec.rb +26 -0
  41. data/spec/spec_helper.rb +8 -0
  42. data/spec/string_spec.rb +200 -0
  43. data/spec/struct_spec.rb +12 -0
  44. data/spec/time_spec.rb +16 -0
  45. data/spec/virtual_file_spec.rb +21 -0
  46. data/thorero.gemspec +147 -0
  47. metadata +146 -0
data/spec/hook_spec.rb ADDED
@@ -0,0 +1,1198 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe Extlib::Hook do
4
+
5
+ before(:each) do
6
+ @module = Module.new do
7
+ def greet; greetings_from_module; end;
8
+ end
9
+
10
+ @class = Class.new do
11
+ include Extlib::Hook
12
+
13
+ def hookable; end;
14
+ def self.clakable; end;
15
+ def ambiguous; hi_mom!; end;
16
+ def self.ambiguous; hi_dad!; end;
17
+ end
18
+
19
+ @another_class = Class.new do
20
+ include Extlib::Hook
21
+ end
22
+
23
+ @other = Class.new do
24
+ include Extlib::Hook
25
+
26
+ def hookable; end
27
+ def self.clakable; end;
28
+ end
29
+
30
+ @class.register_instance_hooks :hookable
31
+ @class.register_class_hooks :clakable
32
+ end
33
+
34
+ #
35
+ # Specs out how hookable methods are registered
36
+ #
37
+ describe "explicit hookable method registration" do
38
+
39
+ describe "for class methods" do
40
+
41
+ it "shouldn't confuse instance method hooks and class method hooks" do
42
+ @class.register_instance_hooks :ambiguous
43
+ @class.register_class_hooks :ambiguous
44
+
45
+ @class.should_receive(:hi_dad!)
46
+ @class.ambiguous
47
+ end
48
+
49
+ it "should be able to register multiple hookable methods at once" do
50
+ %w(method_one method_two method_three).each do |method|
51
+ @another_class.class_eval %(def self.#{method}; end;)
52
+ end
53
+
54
+ @another_class.register_class_hooks :method_one, :method_two, :method_three
55
+ @another_class.class_hooks.keys.should include(:method_one)
56
+ @another_class.class_hooks.keys.should include(:method_two)
57
+ @another_class.class_hooks.keys.should include(:method_three)
58
+ end
59
+
60
+ it "should not allow a method that does not exist to be registered as hookable" do
61
+ lambda { @another_class.register_class_hooks :method_one }.should raise_error(ArgumentError)
62
+ end
63
+
64
+ it "should allow hooks to be registered on methods from module extensions" do
65
+ @class.extend(@module)
66
+ @class.register_class_hooks :greet
67
+ @class.class_hooks[:greet].should_not be_nil
68
+ end
69
+
70
+ it "should allow modules to register hooks in the self.extended method" do
71
+ @module.class_eval do
72
+ def self.extended(base)
73
+ base.register_class_hooks :greet
74
+ end
75
+ end
76
+ @class.extend(@module)
77
+ @class.class_hooks[:greet].should_not be_nil
78
+ end
79
+
80
+ it "should be able to register protected methods as hooks" do
81
+ @class.class_eval %{protected; def self.protected_hookable; end;}
82
+ lambda { @class.register_class_hooks(:protected_hookable) }.should_not raise_error(ArgumentError)
83
+ end
84
+
85
+ it "should not be able to register private methods as hooks" do
86
+ @class.class_eval %{class << self; private; def private_hookable; end; end;}
87
+ lambda { @class.register_class_hooks(:private_hookable) }.should raise_error(ArgumentError)
88
+ end
89
+
90
+ it "should allow advising methods ending in ? or !" do
91
+ @class.class_eval do
92
+ def self.hookable!; two!; end;
93
+ def self.hookable?; three!; end;
94
+ register_class_hooks :hookable!, :hookable?
95
+ end
96
+ @class.before_class_method(:hookable!) { one! }
97
+ @class.after_class_method(:hookable?) { four! }
98
+
99
+ @class.should_receive(:one!).once.ordered
100
+ @class.should_receive(:two!).once.ordered
101
+ @class.should_receive(:three!).once.ordered
102
+ @class.should_receive(:four!).once.ordered
103
+
104
+ @class.hookable!
105
+ @class.hookable?
106
+ end
107
+
108
+ it "should allow hooking methods ending in ?, ! or = with method hooks" do
109
+ @class.class_eval do
110
+ def self.before_hookable!; one!; end;
111
+ def self.hookable!; two!; end;
112
+ def self.hookable?; three!; end;
113
+ def self.after_hookable?; four!; end;
114
+ register_class_hooks :hookable!, :hookable?
115
+ end
116
+ @class.before_class_method(:hookable!, :before_hookable!)
117
+ @class.after_class_method(:hookable?, :after_hookable?)
118
+
119
+ @class.should_receive(:one!).once.ordered
120
+ @class.should_receive(:two!).once.ordered
121
+ @class.should_receive(:three!).once.ordered
122
+ @class.should_receive(:four!).once.ordered
123
+
124
+ @class.hookable!
125
+ @class.hookable?
126
+ end
127
+
128
+ it "should allow hooking methods that have single character names" do
129
+ @class.class_eval do
130
+ def self.a; end;
131
+ def self.b; end;
132
+ end
133
+
134
+ @class.before_class_method(:a) { omg! }
135
+ @class.before_class_method(:b) { hi2u! }
136
+
137
+ @class.should_receive(:omg!).once.ordered
138
+ @class.should_receive(:hi2u!).once.ordered
139
+ @class.a
140
+ @class.b
141
+ end
142
+ end
143
+
144
+ describe "for instance methods" do
145
+
146
+ it "shouldn't confuse instance method hooks and class method hooks" do
147
+ @class.register_instance_hooks :ambiguous
148
+ @class.register_class_hooks :ambiguous
149
+
150
+ inst = @class.new
151
+ inst.should_receive(:hi_mom!)
152
+ inst.ambiguous
153
+ end
154
+
155
+ it "should be able to register multiple hookable methods at once" do
156
+ %w(method_one method_two method_three).each do |method|
157
+ @another_class.send(:define_method, method) {}
158
+ end
159
+
160
+ @another_class.register_instance_hooks :method_one, :method_two, :method_three
161
+ @another_class.instance_hooks.keys.should include(:method_one)
162
+ @another_class.instance_hooks.keys.should include(:method_two)
163
+ @another_class.instance_hooks.keys.should include(:method_three)
164
+ end
165
+
166
+ it "should not allow a method that does not exist to be registered as hookable" do
167
+ lambda { @another_class.register_instance_hooks :method_one }.should raise_error(ArgumentError)
168
+ end
169
+
170
+ it "should allow hooks to be registered on included module methods" do
171
+ @class.send(:include, @module)
172
+ @class.register_instance_hooks :greet
173
+ @class.instance_hooks[:greet].should_not be_nil
174
+ end
175
+
176
+ it "should allow modules to register hooks in the self.included method" do
177
+ @module.class_eval do
178
+ def self.included(base)
179
+ base.register_instance_hooks :greet
180
+ end
181
+ end
182
+ @class.send(:include, @module)
183
+ @class.instance_hooks[:greet].should_not be_nil
184
+ end
185
+
186
+ it "should be able to register protected methods as hooks" do
187
+ @class.class_eval %{protected; def protected_hookable; end;}
188
+ lambda { @class.register_instance_hooks(:protected_hookable) }.should_not raise_error(ArgumentError)
189
+ end
190
+
191
+ it "should not be able to register private methods as hooks" do
192
+ @class.class_eval %{private; def private_hookable; end;}
193
+ lambda { @class.register_instance_hooks(:private_hookable) }.should raise_error(ArgumentError)
194
+ end
195
+
196
+ it "should allow hooking methods ending in ? or ! with block hooks" do
197
+ @class.class_eval do
198
+ def hookable!; two!; end;
199
+ def hookable?; three!; end;
200
+ register_instance_hooks :hookable!, :hookable?
201
+ end
202
+ @class.before(:hookable!) { one! }
203
+ @class.after(:hookable?) { four! }
204
+
205
+ inst = @class.new
206
+ inst.should_receive(:one!).once.ordered
207
+ inst.should_receive(:two!).once.ordered
208
+ inst.should_receive(:three!).once.ordered
209
+ inst.should_receive(:four!).once.ordered
210
+
211
+ inst.hookable!
212
+ inst.hookable?
213
+ end
214
+
215
+ it "should allow hooking methods ending in ?, ! or = with method hooks" do
216
+ @class.class_eval do
217
+ def before_hookable(val); one!; end;
218
+ def hookable=(val); two!; end;
219
+ def hookable?; three!; end;
220
+ def after_hookable?; four!; end;
221
+ register_instance_hooks :hookable=, :hookable?
222
+ end
223
+ @class.before(:hookable=, :before_hookable)
224
+ @class.after(:hookable?, :after_hookable?)
225
+
226
+ inst = @class.new
227
+ inst.should_receive(:one!).once.ordered
228
+ inst.should_receive(:two!).once.ordered
229
+ inst.should_receive(:three!).once.ordered
230
+ inst.should_receive(:four!).once.ordered
231
+
232
+ inst.hookable = 'hello'
233
+ inst.hookable?
234
+ end
235
+
236
+ it "should allow hooking methods that have single character names" do
237
+ @class.class_eval do
238
+ def a; end;
239
+ def b; end;
240
+ end
241
+
242
+ @class.before(:a) { omg! }
243
+ @class.before(:b) { hi2u! }
244
+
245
+ inst = @class.new
246
+ inst.should_receive(:omg!).once.ordered
247
+ inst.should_receive(:hi2u!).once.ordered
248
+ inst.a
249
+ inst.b
250
+ end
251
+ end
252
+
253
+ end
254
+
255
+ describe "implicit hookable method registration" do
256
+
257
+ describe "for class methods" do
258
+ it "should implicitly register the method as hookable" do
259
+ @class.class_eval %{def self.implicit_hook; end;}
260
+ @class.before_class_method(:implicit_hook) { hello }
261
+
262
+ @class.should_receive(:hello)
263
+ @class.implicit_hook
264
+ end
265
+ end
266
+
267
+ describe "for instance methods" do
268
+ it "should implicitly register the method as hookable" do
269
+ @class.class_eval %{def implicit_hook; end;}
270
+ @class.before(:implicit_hook) { hello }
271
+
272
+ inst = @class.new
273
+ inst.should_receive(:hello)
274
+ inst.implicit_hook
275
+ end
276
+
277
+ it 'should not overwrite methods included by modules after the hook is declared' do
278
+ my_module = Module.new do
279
+ # Just another module
280
+ @another_module = Module.new do
281
+ def some_method; "Hello " + super; end;
282
+ end
283
+
284
+ def some_method; "world"; end;
285
+
286
+ def self.included(base)
287
+ base.before(:some_method, :a_method)
288
+ base.send(:include, @another_module)
289
+ end
290
+ end
291
+
292
+ @class.class_eval { include my_module }
293
+
294
+ inst = @class.new
295
+ inst.should_receive(:a_method)
296
+ inst.some_method.should == "Hello world"
297
+ end
298
+ end
299
+
300
+ end
301
+
302
+ describe "hook method registration" do
303
+
304
+ describe "for class methods" do
305
+ it "should complain when only one argument is passed" do
306
+ lambda { @class.before_class_method(:clakable) }.should raise_error(ArgumentError)
307
+ lambda { @class.after_class_method(:clakable) }.should raise_error(ArgumentError)
308
+ end
309
+
310
+ it "should complain when target_method is not a symbol" do
311
+ lambda { @class.before_class_method("clakable", :ambiguous) }.should raise_error(ArgumentError)
312
+ lambda { @class.after_class_method("clakable", :ambiguous) }.should raise_error(ArgumentError)
313
+ end
314
+
315
+ it "should complain when method_sym is not a symbol" do
316
+ lambda { @class.before_class_method(:clakable, "ambiguous") }.should raise_error(ArgumentError)
317
+ lambda { @class.after_class_method(:clakable, "ambiguous") }.should raise_error(ArgumentError)
318
+ end
319
+
320
+ it "should not allow methods ending in = to be hooks" do
321
+ lambda { @class.before_class_method(:clakable, :annoying=) }.should raise_error(ArgumentError)
322
+ lambda { @class.after_class_method(:clakable, :annoying=) }.should raise_error(ArgumentError)
323
+ end
324
+ end
325
+
326
+ describe "for instance methods" do
327
+ it "should complain when only one argument is passed" do
328
+ lambda { @class.before(:hookable) }.should raise_error(ArgumentError)
329
+ lambda { @class.after(:hookable) }.should raise_error(ArgumentError)
330
+ end
331
+
332
+ it "should complain when target_method is not a symbol" do
333
+ lambda { @class.before("hookable", :ambiguous) }.should raise_error(ArgumentError)
334
+ lambda { @class.after("hookable", :ambiguous) }.should raise_error(ArgumentError)
335
+ end
336
+
337
+ it "should complain when method_sym is not a symbol" do
338
+ lambda { @class.before(:hookable, "ambiguous") }.should raise_error(ArgumentError)
339
+ lambda { @class.after(:hookable, "ambiguous") }.should raise_error(ArgumentError)
340
+ end
341
+
342
+ it "should not allow methods ending in = to be hooks" do
343
+ lambda { @class.before(:hookable, :annoying=) }.should raise_error(ArgumentError)
344
+ lambda { @class.after(:hookable, :annoying=) }.should raise_error(ArgumentError)
345
+ end
346
+ end
347
+
348
+ end
349
+
350
+ #
351
+ # Specs out how hook methods / blocks are invoked when there is no inheritance
352
+ # involved
353
+ #
354
+ describe "hook invocation without inheritance" do
355
+
356
+ describe "for class methods" do
357
+ it 'should run an advice block' do
358
+ @class.before_class_method(:clakable) { hi_mom! }
359
+ @class.should_receive(:hi_mom!)
360
+ @class.clakable
361
+ end
362
+
363
+ it 'should run an advice method' do
364
+ @class.class_eval %{def self.before_method; hi_mom!; end;}
365
+ @class.before_class_method(:clakable, :before_method)
366
+
367
+ @class.should_receive(:hi_mom!)
368
+ @class.clakable
369
+ end
370
+
371
+ it "should not pass any of the hookable method's arguments if the hook block does not accept arguments" do
372
+ @class.class_eval do
373
+ def self.method_with_args(one, two, three); end;
374
+ before_class_method(:method_with_args) { hi_mom! }
375
+ end
376
+
377
+ @class.should_receive(:hi_mom!)
378
+ @class.method_with_args(1, 2, 3)
379
+ end
380
+
381
+ it "should not pass any of the hookable method's arguments if the hook method does not accept arguments" do
382
+ @class.class_eval do
383
+ def self.method_with_args(one, two, three); end;
384
+ def self.before_method_with_args; hi_mom!; end;
385
+ before_class_method(:method_with_args, :before_method_with_args)
386
+ end
387
+
388
+ @class.should_receive(:hi_mom!)
389
+ @class.method_with_args(1, 2, 3)
390
+ end
391
+
392
+ it "should not pass any of the hookable method's arguments if the hook is declared after it is registered and does not accept arguments" do
393
+ @class.class_eval do
394
+ def self.method_with_args(one, two, three); end;
395
+ before_class_method(:method_with_args, :before_method_with_args)
396
+ def self.before_method_with_args; hi_mom!; end;
397
+ end
398
+
399
+ @class.should_receive(:hi_mom!)
400
+ @class.method_with_args(1, 2, 3)
401
+ end
402
+
403
+ it "should not pass any of the hookable method's arguments if the hook has been re-defined not to accept arguments" do
404
+ @class.class_eval do
405
+ def self.method_with_args(one, two, three); end;
406
+ def self.before_method_with_args(one, two, three); hi_mom!; end;
407
+ before_class_method(:method_with_args, :before_method_with_args)
408
+ def self.before_method_with_args; hi_dad!; end;
409
+ end
410
+
411
+ @class.should_not_receive(:hi_mom1)
412
+ @class.should_receive(:hi_dad!)
413
+ @class.method_with_args(1, 2, 3)
414
+ end
415
+
416
+ it 'should pass the hookable method arguments to the hook method if the hook method takes arguments' do
417
+ @class.class_eval do
418
+ def self.hook_this(word, lol); end;
419
+ register_class_hooks(:hook_this)
420
+ def self.before_hook_this(word, lol); hi_mom!(word, lol); end;
421
+ before_class_method(:hook_this, :before_hook_this)
422
+ end
423
+
424
+ @class.should_receive(:hi_mom!).with("omg", "hi2u")
425
+ @class.hook_this("omg", "hi2u")
426
+ end
427
+
428
+ it "should pass the hookable method arguments to the hook block if the hook block takes arguments" do
429
+ @class.class_eval do
430
+ def self.method_with_args(word, lol); end;
431
+ before_class_method(:method_with_args) { |one, two| hi_mom!(one, two) }
432
+ end
433
+
434
+ @class.should_receive(:hi_mom!).with('omg', 'hi2u')
435
+ @class.method_with_args('omg', 'hi2u')
436
+ end
437
+
438
+ it 'should pass the hookable method arguments to the hook method if the hook method was re-defined to accept arguments' do
439
+ @class.class_eval do
440
+ def self.method_with_args(word, lol); end;
441
+ def self.before_method_with_args; hi_mom!; end;
442
+ before_class_method(:method_with_args, :before_method_with_args)
443
+ def self.before_method_with_args(word, lol); hi_dad!(word, lol); end;
444
+ end
445
+
446
+ @class.should_not_receive(:hi_mom!)
447
+ @class.should_receive(:hi_dad!).with("omg", "hi2u")
448
+ @class.method_with_args("omg", "hi2u")
449
+ end
450
+
451
+ it 'should work with glob arguments (or whatever you call em)' do
452
+ @class.class_eval do
453
+ def self.hook_this(*args); end;
454
+ def self.before_hook_this(*args); hi_mom!(*args); end;
455
+ before_class_method(:hook_this, :before_hook_this)
456
+ end
457
+
458
+ @class.should_receive(:hi_mom!).with("omg", "hi2u", "lolercoaster")
459
+ @class.hook_this("omg", "hi2u", "lolercoaster")
460
+ end
461
+
462
+ it 'should allow the use of before and after together' do
463
+ @class.class_eval %{def self.before_hook; first!; end;}
464
+ @class.before_class_method(:clakable, :before_hook)
465
+ @class.after_class_method(:clakable) { last! }
466
+
467
+ @class.should_receive(:first!).once.ordered
468
+ @class.should_receive(:last!).once.ordered
469
+ @class.clakable
470
+ end
471
+
472
+ it 'should be able to use private methods as hooks' do
473
+ @class.class_eval do
474
+ class << self
475
+ private
476
+ def nike; doit!; end;
477
+ end
478
+ before_class_method(:clakable, :nike)
479
+ end
480
+
481
+ @class.should_receive(:doit!)
482
+ @class.clakable
483
+ end
484
+ end
485
+
486
+ describe "for instance methods" do
487
+ it 'should run an advice block' do
488
+ @class.before(:hookable) { hi_mom! }
489
+
490
+ inst = @class.new
491
+ inst.should_receive(:hi_mom!)
492
+ inst.hookable
493
+ end
494
+
495
+ it 'should run an advice method' do
496
+ @class.send(:define_method, :before_method) { hi_mom! }
497
+ @class.before(:hookable, :before_method)
498
+
499
+ inst = @class.new
500
+ inst.should_receive(:hi_mom!)
501
+ inst.hookable
502
+ end
503
+
504
+ it "should not pass any of the hookable method's arguments if the hook block does not accept arguments" do
505
+ @class.class_eval do
506
+ def method_with_args(one, two, three); end;
507
+ before(:method_with_args) { hi_mom! }
508
+ end
509
+
510
+ inst = @class.new
511
+ inst.should_receive(:hi_mom!)
512
+ inst.method_with_args(1, 2, 3)
513
+ end
514
+
515
+ it "should not pass any of the hookable method's arguments if the hook method does not accept arguments" do
516
+ @class.class_eval do
517
+ def method_with_args(one, two, three); end;
518
+ def before_method_with_args; hi_mom!; end;
519
+ before(:method_with_args, :before_method_with_args)
520
+ end
521
+
522
+ inst = @class.new
523
+ inst.should_receive(:hi_mom!)
524
+ inst.method_with_args(1, 2, 3)
525
+ end
526
+
527
+ it "should not pass any of the hookable method's arguments if the hook is declared after it is registered and does not accept arguments" do
528
+ @class.class_eval do
529
+ def method_with_args(one, two, three); end;
530
+ before(:method_with_args, :before_method_with_args)
531
+ def before_method_with_args; hi_mom!; end;
532
+ end
533
+
534
+ inst = @class.new
535
+ inst.should_receive(:hi_mom!)
536
+ inst.method_with_args(1, 2, 3)
537
+ end
538
+
539
+ it "should not pass any of the hookable method's arguments if the hook has been re-defined not to accept arguments" do
540
+ @class.class_eval do
541
+ def method_with_args(one, two, three); end;
542
+ def before_method_with_args(one, two, three); hi_mom!; end;
543
+ before(:method_with_args, :before_method_with_args)
544
+ def before_method_with_args; hi_dad!; end;
545
+ end
546
+
547
+ inst = @class.new
548
+ inst.should_not_receive(:hi_mom1)
549
+ inst.should_receive(:hi_dad!)
550
+ inst.method_with_args(1, 2, 3)
551
+ end
552
+
553
+ it 'should pass the hookable method arguments to the hook method if the hook method takes arguments' do
554
+ @class.class_eval do
555
+ def method_with_args(word, lol); end;
556
+ def before_method_with_args(one, two); hi_mom!(one, two); end;
557
+ before(:method_with_args, :before_method_with_args)
558
+ end
559
+
560
+ inst = @class.new
561
+ inst.should_receive(:hi_mom!).with("omg", "hi2u")
562
+ inst.method_with_args("omg", "hi2u")
563
+ end
564
+
565
+ it "should pass the hookable method arguments to the hook block if the hook block takes arguments" do
566
+ @class.class_eval do
567
+ def method_with_args(word, lol); end;
568
+ before(:method_with_args) { |one, two| hi_mom!(one, two) }
569
+ end
570
+
571
+ inst = @class.new
572
+ inst.should_receive(:hi_mom!).with('omg', 'hi2u')
573
+ inst.method_with_args('omg', 'hi2u')
574
+ end
575
+
576
+ it 'should pass the hookable method arguments to the hook method if the hook method was re-defined to accept arguments' do
577
+ @class.class_eval do
578
+ def method_with_args(word, lol); end;
579
+ def before_method_with_args; hi_mom!; end;
580
+ before(:method_with_args, :before_method_with_args)
581
+ def before_method_with_args(word, lol); hi_dad!(word, lol); end;
582
+ end
583
+
584
+ inst = @class.new
585
+ inst.should_not_receive(:hi_mom!)
586
+ inst.should_receive(:hi_dad!).with("omg", "hi2u")
587
+ inst.method_with_args("omg", "hi2u")
588
+ end
589
+
590
+ it "should not pass the method return value to the after hook if the method does not take arguments" do
591
+ @class.class_eval do
592
+ def method_with_ret_val; 'hello'; end;
593
+ def after_method_with_ret_val; hi_mom!; end;
594
+ after(:method_with_ret_val, :after_method_with_ret_val)
595
+ end
596
+
597
+ inst = @class.new
598
+ inst.should_receive(:hi_mom!)
599
+ inst.method_with_ret_val
600
+ end
601
+
602
+ it 'should work with glob arguments (or whatever you call em)' do
603
+ @class.class_eval do
604
+ def hook_this(*args); end;
605
+ def before_hook_this(*args); hi_mom!(*args) end;
606
+ before(:hook_this, :before_hook_this)
607
+ end
608
+
609
+ inst = @class.new
610
+ inst.should_receive(:hi_mom!).with("omg", "hi2u", "lolercoaster")
611
+ inst.hook_this("omg", "hi2u", "lolercoaster")
612
+ end
613
+
614
+ it 'should allow the use of before and after together' do
615
+ @class.class_eval %{def after_hook; last!; end;}
616
+ @class.before(:hookable) { first! }
617
+ @class.after(:hookable, :after_hook)
618
+
619
+ inst = @class.new
620
+ inst.should_receive(:first!).once.ordered
621
+ inst.should_receive(:last!).once.ordered
622
+ inst.hookable
623
+ end
624
+
625
+ it 'should be able to use private methods as hooks' do
626
+ @class.class_eval %{private; def nike; doit!; end;}
627
+ @class.before(:hookable, :nike)
628
+
629
+ inst = @class.new
630
+ inst.should_receive(:doit!)
631
+ inst.hookable
632
+ end
633
+ end
634
+
635
+ end
636
+
637
+ describe "hook invocation with class inheritance" do
638
+
639
+ describe "for class methods" do
640
+ it 'should run an advice block when the class is inherited' do
641
+ @class.before_class_method(:clakable) { hi_mom! }
642
+ @child = Class.new(@class)
643
+ @child.should_receive(:hi_mom!)
644
+ @child.clakable
645
+ end
646
+
647
+ it 'should run an advice block on child class when hook is registered in parent after inheritance' do
648
+ @child = Class.new(@class)
649
+ @class.before_class_method(:clakable) { hi_mom! }
650
+ @child.should_receive(:hi_mom!)
651
+ @child.clakable
652
+ end
653
+
654
+ it 'should be able to declare advice methods in child classes' do
655
+ @class.class_eval %{def self.before_method; hi_dad!; end;}
656
+ @class.before_class_method(:clakable, :before_method)
657
+
658
+ @child = Class.new(@class) do
659
+ def self.child; hi_mom!; end;
660
+ before_class_method(:clakable, :child)
661
+ end
662
+
663
+ @child.should_receive(:hi_dad!).once.ordered
664
+ @child.should_receive(:hi_mom!).once.ordered
665
+ @child.clakable
666
+ end
667
+
668
+ it "should not execute hooks added in the child classes when in the parent class" do
669
+ @child = Class.new(@class) { def self.child; hi_mom!; end; }
670
+ @child.before_class_method(:clakable, :child)
671
+ @class.should_not_receive(:hi_mom!)
672
+ @class.clakable
673
+ end
674
+
675
+ it 'should not call the hook stack if the hookable method is overwritten and does not call super' do
676
+ @class.before_class_method(:clakable) { hi_mom! }
677
+ @child = Class.new(@class) do
678
+ def self.clakable; end;
679
+ end
680
+
681
+ @child.should_not_receive(:hi_mom!)
682
+ @child.clakable
683
+ end
684
+
685
+ it 'should not call hooks defined in the child class for a hookable method in a parent if the child overwrites the hookable method without calling super' do
686
+ @child = Class.new(@class) do
687
+ before_class_method(:clakable) { hi_mom! }
688
+ def self.clakable; end;
689
+ end
690
+
691
+ @child.should_not_receive(:hi_mom!)
692
+ @child.clakable
693
+ end
694
+
695
+ it 'should not call hooks defined in child class even if hook method exists in parent' do
696
+ @class.class_eval %{def self.hello_world; hello_world!; end;}
697
+ @child = Class.new(@class) do
698
+ before_class_method(:clakable, :hello_world)
699
+ end
700
+
701
+ @class.should_not_receive(:hello_world!)
702
+ @class.clakable
703
+ end
704
+ end
705
+
706
+ describe "for instance methods" do
707
+ it 'should run an advice block when the class is inherited' do
708
+ @inherited_class = Class.new(@class)
709
+ @class.before(:hookable) { hi_dad! }
710
+
711
+ inst = @inherited_class.new
712
+ inst.should_receive(:hi_dad!)
713
+ inst.hookable
714
+ end
715
+
716
+ it 'should run an advice block on child class when hook is registered in parent after inheritance' do
717
+ @child = Class.new(@class)
718
+ @class.before(:hookable) { hi_mom! }
719
+
720
+ inst = @child.new
721
+ inst.should_receive(:hi_mom!)
722
+ inst.hookable
723
+ end
724
+
725
+ it 'should be able to declare advice methods in child classes' do
726
+ @class.send(:define_method, :before_method) { hi_dad! }
727
+ @class.before(:hookable, :before_method)
728
+
729
+ @child = Class.new(@class) do
730
+ def child; hi_mom!; end;
731
+ before :hookable, :child
732
+ end
733
+
734
+ inst = @child.new
735
+ inst.should_receive(:hi_dad!).once.ordered
736
+ inst.should_receive(:hi_mom!).once.ordered
737
+ inst.hookable
738
+ end
739
+
740
+ it "should not execute hooks added in the child classes when in parent class" do
741
+ @child = Class.new(@class)
742
+ @child.send(:define_method, :child) { hi_mom! }
743
+ @child.before(:hookable, :child)
744
+
745
+ inst = @class.new
746
+ inst.should_not_receive(:hi_mom!)
747
+ inst.hookable
748
+ end
749
+
750
+ it 'should not call the hook stack if the hookable method is overwritten and does not call super' do
751
+ @class.before(:hookable) { hi_mom! }
752
+ @child = Class.new(@class) do
753
+ def hookable; end;
754
+ end
755
+
756
+ inst = @child.new
757
+ inst.should_not_receive(:hi_mom!)
758
+ inst.hookable
759
+ end
760
+
761
+ it 'should not call hooks defined in the child class for a hookable method in a parent if the child overwrites the hookable method without calling super' do
762
+ @child = Class.new(@class) do
763
+ before(:hookable) { hi_mom! }
764
+ def hookable; end;
765
+ end
766
+
767
+ inst = @child.new
768
+ inst.should_not_receive(:hi_mom!)
769
+ inst.hookable
770
+ end
771
+
772
+ it 'should not call hooks defined in child class even if hook method exists in parent' do
773
+ @class.send(:define_method, :hello_world) { hello_world! }
774
+ @child = Class.new(@class) do
775
+ before(:hookable, :hello_world)
776
+ end
777
+
778
+ inst = @class.new
779
+ inst.should_not_receive(:hello_world!)
780
+ inst.hookable
781
+ end
782
+ end
783
+
784
+ end
785
+
786
+ describe "hook invocation with module inclusions / extensions" do
787
+
788
+ describe "for class methods" do
789
+ it "should not overwrite methods included by extensions after the hook is declared" do
790
+ @module.class_eval do
791
+ @another_module = Module.new do
792
+ def greet; greetings_from_another_module; super; end;
793
+ end
794
+
795
+ def self.extended(base)
796
+ base.before_class_method(:clakable, :greet)
797
+ base.extend(@another_module)
798
+ end
799
+ end
800
+
801
+ @class.extend(@module)
802
+ @class.should_receive(:greetings_from_another_module).once.ordered
803
+ @class.should_receive(:greetings_from_module).once.ordered
804
+ @class.clakable
805
+ end
806
+ end
807
+
808
+ describe "for instance methods" do
809
+ it 'should not overwrite methods included by modules after the hook is declared' do
810
+ @module.class_eval do
811
+ @another_module = Module.new do
812
+ def greet; greetings_from_another_module; super; end;
813
+ end
814
+
815
+ def self.included(base)
816
+ base.before(:hookable, :greet)
817
+ base.send(:include, @another_module)
818
+ end
819
+ end
820
+
821
+ @class.send(:include, @module)
822
+
823
+ inst = @class.new
824
+ inst.should_receive(:greetings_from_another_module).once.ordered
825
+ inst.should_receive(:greetings_from_module).once.ordered
826
+ inst.hookable
827
+ end
828
+ end
829
+
830
+ end
831
+
832
+ describe "hook invocation with unrelated classes" do
833
+
834
+ describe "for class methods" do
835
+ it "should not execute hooks registered in an unrelated class" do
836
+ @class.before_class_method(:clakable) { hi_mom! }
837
+
838
+ @other.should_not_receive(:hi_mom!)
839
+ @other.clakable
840
+ end
841
+ end
842
+
843
+ describe "for instance methods" do
844
+ it "should not execute hooks registered in an unrelated class" do
845
+ @class.before(:hookable) { hi_mom! }
846
+
847
+ inst = @other.new
848
+ inst.should_not_receive(:hi_mom!)
849
+ inst.hookable
850
+ end
851
+ end
852
+
853
+ end
854
+
855
+ describe "using before hook" do
856
+
857
+ describe "for class methods" do
858
+
859
+ it 'should run the advice before the advised method' do
860
+ @class.class_eval %{def self.hook_me; second!; end;}
861
+ @class.register_class_hooks(:hook_me)
862
+ @class.before_class_method(:hook_me, :first!)
863
+
864
+ @class.should_receive(:first!).ordered
865
+ @class.should_receive(:second!).ordered
866
+ @class.hook_me
867
+ end
868
+
869
+ it 'should execute all advices once in order' do
870
+ @class.before_class_method(:clakable, :hook_1)
871
+ @class.before_class_method(:clakable, :hook_2)
872
+ @class.before_class_method(:clakable, :hook_3)
873
+
874
+ @class.should_receive(:hook_1).once.ordered
875
+ @class.should_receive(:hook_2).once.ordered
876
+ @class.should_receive(:hook_3).once.ordered
877
+ @class.clakable
878
+ end
879
+ end
880
+
881
+ describe "for instance methods" do
882
+
883
+ it 'should run the advice before the advised method' do
884
+ @class.class_eval %{
885
+ def hook_me; second!; end;
886
+ }
887
+ @class.register_instance_hooks(:hook_me)
888
+ @class.before(:hook_me, :first!)
889
+
890
+ inst = @class.new
891
+ inst.should_receive(:first!).ordered
892
+ inst.should_receive(:second!).ordered
893
+ inst.hook_me
894
+ end
895
+
896
+ it 'should execute all advices once in order' do
897
+ @class.before(:hookable, :hook_1)
898
+ @class.before(:hookable, :hook_2)
899
+ @class.before(:hookable, :hook_3)
900
+
901
+ inst = @class.new
902
+ inst.should_receive(:hook_1).once.ordered
903
+ inst.should_receive(:hook_2).once.ordered
904
+ inst.should_receive(:hook_3).once.ordered
905
+ inst.hookable
906
+ end
907
+ end
908
+
909
+ end
910
+
911
+ describe 'using after hook' do
912
+
913
+ describe "for class methods" do
914
+
915
+ it 'should run the advice after the advised method' do
916
+ @class.class_eval %{def self.hook_me; first!; end;}
917
+ @class.register_class_hooks(:hook_me)
918
+ @class.after_class_method(:hook_me, :second!)
919
+
920
+ @class.should_receive(:first!).ordered
921
+ @class.should_receive(:second!).ordered
922
+ @class.hook_me
923
+ end
924
+
925
+ it 'should execute all advices once in order' do
926
+ @class.after_class_method(:clakable, :hook_1)
927
+ @class.after_class_method(:clakable, :hook_2)
928
+ @class.after_class_method(:clakable, :hook_3)
929
+
930
+ @class.should_receive(:hook_1).once.ordered
931
+ @class.should_receive(:hook_2).once.ordered
932
+ @class.should_receive(:hook_3).once.ordered
933
+ @class.clakable
934
+ end
935
+
936
+ it "the advised method should still return its normal value" do
937
+ @class.class_eval %{def self.hello; "hello world"; end;}
938
+ @class.register_class_hooks(:hello)
939
+ @class.after_class_method(:hello) { "BAM" }
940
+
941
+ @class.hello.should == "hello world"
942
+ end
943
+
944
+ it "should pass the return value to a hook method" do
945
+ @class.class_eval do
946
+ def self.with_return_val; 'hello'; end;
947
+ def self.after_with_return_val(retval); retval.should == 'hello'; end;
948
+ after_class_method(:with_return_val, :after_with_return_val)
949
+ end
950
+
951
+ @class.with_return_val
952
+ end
953
+
954
+ it "should pass the return value to a hook block" do
955
+ @class.class_eval do
956
+ def self.with_return_val; 'hello'; end;
957
+ after_class_method(:with_return_val) { |ret| ret.should == 'hello' }
958
+ end
959
+
960
+ @class.with_return_val
961
+ end
962
+
963
+ it "should pass the return value and method arguments to a hook block" do
964
+ @class.class_eval do
965
+ def self.with_args_and_return_val(world); 'hello'; end;
966
+ after_class_method(:with_args_and_return_val) do |hello, world|
967
+ hello.should == "hello"
968
+ world.should == "world"
969
+ end
970
+ end
971
+
972
+ @class.with_args_and_return_val('world')
973
+ end
974
+ end
975
+
976
+ describe "for instance methods" do
977
+
978
+ it 'should run the advice after the advised method' do
979
+ @class.class_eval %{def hook_me; first!; end;}
980
+ @class.register_instance_hooks(:hook_me)
981
+ @class.after(:hook_me, :second!)
982
+
983
+ inst = @class.new
984
+ inst.should_receive(:first!).ordered
985
+ inst.should_receive(:second!).ordered
986
+ inst.hook_me
987
+ end
988
+
989
+ it 'should execute all advices once in order' do
990
+ @class.after(:hookable, :hook_1)
991
+ @class.after(:hookable, :hook_2)
992
+ @class.after(:hookable, :hook_3)
993
+
994
+ inst = @class.new
995
+ inst.should_receive(:hook_1).once.ordered
996
+ inst.should_receive(:hook_2).once.ordered
997
+ inst.should_receive(:hook_3).once.ordered
998
+ inst.hookable
999
+ end
1000
+
1001
+ it "the advised method should still return its normal value" do
1002
+ @class.class_eval %{def hello; "hello world"; end;}
1003
+ @class.register_instance_hooks(:hello)
1004
+ @class.after(:hello) { "BAM" }
1005
+
1006
+ @class.new.hello.should == "hello world"
1007
+ end
1008
+
1009
+ it "should return nil if an after hook throws :halt without a return value" do
1010
+ @class.class_eval %{def with_value; "hello"; end;}
1011
+ @class.register_instance_hooks(:with_value)
1012
+ @class.after(:with_value) { throw :halt }
1013
+
1014
+ @class.new.with_value.should be_nil
1015
+ end
1016
+
1017
+ it "should pass the return value to a hook method" do
1018
+ @class.class_eval do
1019
+ def with_return_val; 'hello'; end;
1020
+ def after_with_return_val(retval); retval.should == 'hello'; end;
1021
+ after(:with_return_val, :after_with_return_val)
1022
+ end
1023
+
1024
+ @class.new.with_return_val
1025
+ end
1026
+
1027
+ it "should pass the return value to a hook block" do
1028
+ @class.class_eval do
1029
+ def with_return_val; 'hello'; end;
1030
+ after(:with_return_val) { |ret| ret.should == 'hello' }
1031
+ end
1032
+
1033
+ @class.new.with_return_val
1034
+ end
1035
+
1036
+ it "should pass the return value and method arguments to a hook block" do
1037
+ @class.class_eval do
1038
+ def with_args_and_return_val(world); 'hello'; end;
1039
+ after(:with_args_and_return_val) do |hello, world|
1040
+ hello.should == "hello"
1041
+ world.should == "world"
1042
+ end
1043
+ end
1044
+
1045
+ @class.new.with_args_and_return_val('world')
1046
+ end
1047
+ end
1048
+
1049
+ end
1050
+
1051
+ describe 'aborting' do
1052
+
1053
+ describe "for class methods" do
1054
+ it "should catch :halt from a before hook and abort the advised method" do
1055
+ @class.class_eval %{def self.no_love; love_me!; end;}
1056
+ @class.register_class_hooks :no_love
1057
+ @class.before_class_method(:no_love) { maybe! }
1058
+ @class.before_class_method(:no_love) { throw :halt }
1059
+ @class.before_class_method(:no_love) { what_about_me? }
1060
+
1061
+ @class.should_receive(:maybe!)
1062
+ @class.should_not_receive(:what_about_me?)
1063
+ @class.should_not_receive(:love_me!)
1064
+ lambda { @class.no_love }.should_not throw_symbol(:halt)
1065
+ end
1066
+
1067
+ it "should not run after hooks if a before hook throws :halt" do
1068
+ @class.before_class_method(:clakable) { throw :halt }
1069
+ @class.after_class_method(:clakable) { bam! }
1070
+
1071
+ @class.should_not_receive(:bam!)
1072
+ lambda { @class.clakable }.should_not throw_symbol(:halt)
1073
+ end
1074
+
1075
+ it "should return nil from the hookable method if a before hook throws :halt" do
1076
+ @class.class_eval %{def self.with_value; "hello"; end;}
1077
+ @class.register_class_hooks(:with_value)
1078
+ @class.before_class_method(:with_value) { throw :halt }
1079
+
1080
+ @class.with_value.should be_nil
1081
+ end
1082
+
1083
+ it "should catch :halt from an after hook and cease the advice" do
1084
+ @class.after_class_method(:clakable) { throw :halt }
1085
+ @class.after_class_method(:clakable) { never_see_me! }
1086
+
1087
+ @class.should_not_receive(:never_see_me!)
1088
+ lambda { @class.clakable }.should_not throw_symbol(:halt)
1089
+ end
1090
+
1091
+ it "should return nil if an after hook throws :halt without a return value" do
1092
+ @class.class_eval %{def self.with_value; "hello"; end;}
1093
+ @class.register_class_hooks(:with_value)
1094
+ @class.after_class_method(:with_value) { throw :halt }
1095
+
1096
+ @class.with_value.should be_nil
1097
+ end
1098
+ end
1099
+
1100
+ describe "for instance methods" do
1101
+ it "should catch :halt from a before hook and abort the advised method" do
1102
+ @class.class_eval %{def no_love; love_me!; end;}
1103
+ @class.register_instance_hooks :no_love
1104
+ @class.before(:no_love) { maybe! }
1105
+ @class.before(:no_love) { throw :halt }
1106
+ @class.before(:no_love) { what_about_me? }
1107
+
1108
+ inst = @class.new
1109
+ inst.should_receive(:maybe!)
1110
+ inst.should_not_receive(:what_about_me?)
1111
+ inst.should_not_receive(:love_me!)
1112
+ lambda { inst.no_love }.should_not throw_symbol(:halt)
1113
+ end
1114
+
1115
+ it "should not run after hooks if a before hook throws :halt" do
1116
+ @class.before(:hookable) { throw :halt }
1117
+ @class.after(:hookable) { bam! }
1118
+
1119
+ inst = @class.new
1120
+ inst.should_not_receive(:bam!)
1121
+ lambda { inst.hookable }.should_not throw_symbol(:halt)
1122
+ end
1123
+
1124
+ it "should return nil from the hookable method if a before hook throws :halt" do
1125
+ @class.class_eval %{def with_value; "hello"; end;}
1126
+ @class.register_instance_hooks(:with_value)
1127
+ @class.before(:with_value) { throw :halt }
1128
+
1129
+ @class.new.with_value.should be_nil
1130
+ end
1131
+
1132
+ it "should catch :halt from an after hook and cease the advice" do
1133
+ @class.after(:hookable) { throw :halt }
1134
+ @class.after(:hookable) { never_see_me! }
1135
+
1136
+ inst = @class.new
1137
+ inst.should_not_receive(:never_see_me!)
1138
+ lambda { inst.hookable }.should_not throw_symbol(:halt)
1139
+ end
1140
+ end
1141
+
1142
+ end
1143
+
1144
+ describe 'aborting with return values' do
1145
+
1146
+ describe "for class methods" do
1147
+
1148
+ it "should be able to abort from a before hook with a return value" do
1149
+ @class.before_class_method(:clakable) { throw :halt, 'omg' }
1150
+ @class.clakable.should == 'omg'
1151
+ end
1152
+
1153
+ it "should be able to abort from an after hook with a return value" do
1154
+ @class.after_class_method(:clakable) { throw :halt, 'omg' }
1155
+ @class.clakable.should == 'omg'
1156
+ end
1157
+
1158
+ end
1159
+
1160
+ describe "for instance methods" do
1161
+
1162
+ it "should be able to abort from a before hook with a return value" do
1163
+ @class.before(:hookable) { throw :halt, 'omg' }
1164
+
1165
+ inst = @class.new
1166
+ inst.hookable.should == 'omg'
1167
+ end
1168
+
1169
+ it "should be able to abort from an after hook with a return value" do
1170
+ @class.after(:hookable) { throw :halt, 'omg' }
1171
+
1172
+ inst = @class.new
1173
+ inst.hookable.should == 'omg'
1174
+ end
1175
+
1176
+ end
1177
+
1178
+ end
1179
+
1180
+ describe "helper methods" do
1181
+ it 'should generate the correct argument signature' do
1182
+ @class.class_eval do
1183
+ def some_method(a, b, c)
1184
+ [a, b, c]
1185
+ end
1186
+
1187
+ def yet_another(a, *heh)
1188
+ [a, *heh]
1189
+ end
1190
+ end
1191
+
1192
+ @class.args_for(@class.instance_method(:hookable)).should == "&block"
1193
+ @class.args_for(@class.instance_method(:some_method)).should == "_1, _2, _3, &block"
1194
+ @class.args_for(@class.instance_method(:yet_another)).should == "_1, *args, &block"
1195
+ end
1196
+ end
1197
+
1198
+ end