extlib 0.9.8 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of extlib might be problematic. Click here for more details.

Files changed (54) hide show
  1. data/History.txt +22 -0
  2. data/LICENSE +1 -1
  3. data/README +0 -0
  4. data/Rakefile +17 -12
  5. data/lib/extlib.rb +1 -1
  6. data/lib/extlib/blank.rb +51 -21
  7. data/lib/extlib/boolean.rb +1 -1
  8. data/lib/extlib/class.rb +12 -12
  9. data/lib/extlib/datetime.rb +20 -2
  10. data/lib/extlib/dictionary.rb +2 -2
  11. data/lib/extlib/hash.rb +57 -34
  12. data/lib/extlib/inflection.rb +0 -1
  13. data/lib/extlib/lazy_array.rb +327 -36
  14. data/lib/extlib/logger.rb +8 -8
  15. data/lib/extlib/mash.rb +45 -45
  16. data/lib/extlib/module.rb +1 -1
  17. data/lib/extlib/nil.rb +1 -1
  18. data/lib/extlib/numeric.rb +1 -1
  19. data/lib/extlib/object.rb +8 -21
  20. data/lib/extlib/object_space.rb +3 -3
  21. data/lib/extlib/pathname.rb +10 -0
  22. data/lib/extlib/pooling.rb +9 -17
  23. data/lib/extlib/rubygems.rb +7 -7
  24. data/lib/extlib/simple_set.rb +35 -8
  25. data/lib/extlib/string.rb +85 -42
  26. data/lib/extlib/struct.rb +10 -1
  27. data/lib/extlib/symbol.rb +11 -7
  28. data/lib/extlib/time.rb +31 -9
  29. data/lib/extlib/version.rb +1 -1
  30. data/lib/extlib/virtual_file.rb +1 -1
  31. data/spec/blank_spec.rb +85 -0
  32. data/spec/class_spec.rb +141 -0
  33. data/spec/datetime_spec.rb +22 -0
  34. data/spec/hash_spec.rb +537 -0
  35. data/spec/hook_spec.rb +1198 -0
  36. data/spec/inflection/plural_spec.rb +564 -0
  37. data/spec/inflection/singular_spec.rb +497 -0
  38. data/spec/inflection_extras_spec.rb +93 -0
  39. data/spec/lazy_array_spec.rb +1869 -0
  40. data/spec/mash_spec.rb +286 -0
  41. data/spec/module_spec.rb +58 -0
  42. data/spec/object_space_spec.rb +9 -0
  43. data/spec/object_spec.rb +114 -0
  44. data/spec/pooling_spec.rb +499 -0
  45. data/spec/simple_set_spec.rb +57 -0
  46. data/spec/spec_helper.rb +7 -0
  47. data/spec/string_spec.rb +220 -0
  48. data/spec/struct_spec.rb +12 -0
  49. data/spec/symbol_spec.rb +8 -0
  50. data/spec/time_spec.rb +22 -0
  51. data/spec/try_dup_spec.rb +45 -0
  52. data/spec/virtual_file_spec.rb +21 -0
  53. metadata +51 -26
  54. data/README.txt +0 -3
@@ -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