aquarium 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/CHANGES +4 -0
  2. data/EXAMPLES.rd +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README +250 -0
  5. data/RELEASE-PLAN +1 -0
  6. data/Rakefile +236 -0
  7. data/UPGRADE +3 -0
  8. data/examples/aspect_design_example.rb +36 -0
  9. data/examples/design_by_contract_example.rb +88 -0
  10. data/examples/method_missing_example.rb +44 -0
  11. data/examples/method_tracing_example.rb +64 -0
  12. data/lib/aquarium.rb +7 -0
  13. data/lib/aquarium/aspects.rb +6 -0
  14. data/lib/aquarium/aspects/advice.rb +189 -0
  15. data/lib/aquarium/aspects/aspect.rb +577 -0
  16. data/lib/aquarium/aspects/default_object_handler.rb +27 -0
  17. data/lib/aquarium/aspects/dsl.rb +1 -0
  18. data/lib/aquarium/aspects/dsl/aspect_dsl.rb +61 -0
  19. data/lib/aquarium/aspects/join_point.rb +158 -0
  20. data/lib/aquarium/aspects/pointcut.rb +254 -0
  21. data/lib/aquarium/aspects/pointcut_composition.rb +36 -0
  22. data/lib/aquarium/extensions.rb +5 -0
  23. data/lib/aquarium/extensions/hash.rb +85 -0
  24. data/lib/aquarium/extensions/regexp.rb +20 -0
  25. data/lib/aquarium/extensions/set.rb +49 -0
  26. data/lib/aquarium/extensions/string.rb +13 -0
  27. data/lib/aquarium/extensions/symbol.rb +22 -0
  28. data/lib/aquarium/extras.rb +4 -0
  29. data/lib/aquarium/extras/design_by_contract.rb +64 -0
  30. data/lib/aquarium/finders.rb +4 -0
  31. data/lib/aquarium/finders/finder_result.rb +121 -0
  32. data/lib/aquarium/finders/method_finder.rb +228 -0
  33. data/lib/aquarium/finders/object_finder.rb +74 -0
  34. data/lib/aquarium/finders/type_finder.rb +127 -0
  35. data/lib/aquarium/utils.rb +9 -0
  36. data/lib/aquarium/utils/array_utils.rb +29 -0
  37. data/lib/aquarium/utils/hash_utils.rb +28 -0
  38. data/lib/aquarium/utils/html_escaper.rb +17 -0
  39. data/lib/aquarium/utils/invalid_options.rb +9 -0
  40. data/lib/aquarium/utils/method_utils.rb +18 -0
  41. data/lib/aquarium/utils/nil_object.rb +13 -0
  42. data/lib/aquarium/utils/set_utils.rb +32 -0
  43. data/lib/aquarium/version.rb +30 -0
  44. data/rake_tasks/examples.rake +7 -0
  45. data/rake_tasks/examples_specdoc.rake +8 -0
  46. data/rake_tasks/examples_with_rcov.rake +8 -0
  47. data/rake_tasks/verify_rcov.rake +7 -0
  48. data/spec/aquarium/aspects/advice_chain_node_spec.rb +34 -0
  49. data/spec/aquarium/aspects/advice_spec.rb +103 -0
  50. data/spec/aquarium/aspects/aspect_invocation_spec.rb +111 -0
  51. data/spec/aquarium/aspects/aspect_spec.rb +978 -0
  52. data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +129 -0
  53. data/spec/aquarium/aspects/concurrent_aspects_spec.rb +423 -0
  54. data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +103 -0
  55. data/spec/aquarium/aspects/concurrently_accessed.rb +21 -0
  56. data/spec/aquarium/aspects/dsl/aspect_dsl_spec.rb +514 -0
  57. data/spec/aquarium/aspects/join_point_spec.rb +302 -0
  58. data/spec/aquarium/aspects/pointcut_and_composition_spec.rb +131 -0
  59. data/spec/aquarium/aspects/pointcut_or_composition_spec.rb +111 -0
  60. data/spec/aquarium/aspects/pointcut_spec.rb +800 -0
  61. data/spec/aquarium/extensions/hash_spec.rb +187 -0
  62. data/spec/aquarium/extensions/regex_spec.rb +40 -0
  63. data/spec/aquarium/extensions/set_spec.rb +105 -0
  64. data/spec/aquarium/extensions/string_spec.rb +25 -0
  65. data/spec/aquarium/extensions/symbol_spec.rb +37 -0
  66. data/spec/aquarium/extras/design_by_contract_spec.rb +68 -0
  67. data/spec/aquarium/finders/finder_result_spec.rb +359 -0
  68. data/spec/aquarium/finders/method_finder_spec.rb +878 -0
  69. data/spec/aquarium/finders/method_sorting_spec.rb +16 -0
  70. data/spec/aquarium/finders/object_finder_spec.rb +230 -0
  71. data/spec/aquarium/finders/type_finder_spec.rb +210 -0
  72. data/spec/aquarium/spec_example_classes.rb +117 -0
  73. data/spec/aquarium/spec_helper.rb +3 -0
  74. data/spec/aquarium/utils/array_utils_spec.rb +47 -0
  75. data/spec/aquarium/utils/hash_utils_spec.rb +48 -0
  76. data/spec/aquarium/utils/html_escaper_spec.rb +18 -0
  77. data/spec/aquarium/utils/method_utils_spec.rb +50 -0
  78. data/spec/aquarium/utils/nil_object_spec.rb +19 -0
  79. data/spec/aquarium/utils/set_utils_spec.rb +60 -0
  80. metadata +132 -0
@@ -0,0 +1,103 @@
1
+ # Specifically tests behavior when two or more advices apply to the same join points,
2
+ # where one advice is for the type and the other is for an object of the type.
3
+
4
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
5
+ require File.dirname(__FILE__) + '/../spec_example_classes'
6
+ require File.dirname(__FILE__) + '/concurrently_accessed'
7
+ require 'aquarium/aspects'
8
+
9
+ def make_aspect method, advice_kind, type_or_object_key, type_or_object
10
+ advise(advice_kind, :pointcut => {type_or_object_key => type_or_object, :method => method}) do |jp, *args|
11
+ @invoked << type_or_object_key
12
+ jp.proceed if advice_kind == :around
13
+ end
14
+ end
15
+
16
+ describe "Advising an object join point and then the corresponding type join point" do
17
+ before :each do
18
+ @aspects = []
19
+ @invoked = []
20
+ @accessed = ConcurrentlyAccessed.new
21
+ end
22
+ after :each do
23
+ @aspects.each {|a| a.unadvise}
24
+ end
25
+
26
+ Aquarium::Aspects::Advice.kinds.each do |advice_kind|
27
+ it "should invoke only the advice on the object join point for :#{advice_kind} advice" do
28
+ method = advice_kind == :after_raising ? :invoke_raises : :invoke
29
+ @aspects << make_aspect(method, advice_kind, :object, @accessed)
30
+ @aspects << make_aspect(method, advice_kind, :type, ConcurrentlyAccessed)
31
+ begin
32
+ @accessed.method(method).call :a1, :a2
33
+ fail if advice_kind == :after_raising
34
+ rescue ConcurrentlyAccessed::Error
35
+ fail unless advice_kind == :after_raising
36
+ end
37
+ @invoked.should == [:object]
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "Advising a type join point and then the corresponding join point on an object of the type" do
43
+ before :each do
44
+ @aspects = []
45
+ @invoked = []
46
+ @accessed = ConcurrentlyAccessed.new
47
+ end
48
+ after :each do
49
+ @aspects.each {|a| a.unadvise}
50
+ end
51
+
52
+ [:around, :before].each do |advice_kind|
53
+ it "should invoke first the advice on the object join point and then invoke the advice on the type join point for :#{advice_kind} advice" do
54
+ method = advice_kind == :after_raising ? :invoke_raises : :invoke
55
+ @aspects << make_aspect(:invoke, advice_kind, :type, ConcurrentlyAccessed)
56
+ @aspects << make_aspect(:invoke, advice_kind, :object, @accessed)
57
+ @accessed.invoke :a1, :a2
58
+ @invoked.should == [:object, :type]
59
+ end
60
+ end
61
+
62
+ [:after, :after_returning, :after_raising].each do |advice_kind|
63
+ it "should invoke first the advice on the type join point and then invoke the advice on the object join point for :#{advice_kind} advice" do
64
+ method = advice_kind == :after_raising ? :invoke_raises : :invoke
65
+ @aspects << make_aspect(method, advice_kind, :type, ConcurrentlyAccessed)
66
+ @aspects << make_aspect(method, advice_kind, :object, @accessed)
67
+ begin
68
+ @accessed.method(method).call :a1, :a2
69
+ fail if advice_kind == :after_raising
70
+ rescue ConcurrentlyAccessed::Error
71
+ fail unless advice_kind == :after_raising
72
+ end
73
+ @invoked.should == [:type, :object]
74
+ end
75
+ end
76
+ end
77
+
78
+ describe "Removing two advices, one from an object join point and one from the corresponding type join point" do
79
+ before :each do
80
+ @aspects = []
81
+ @invoked = []
82
+ @accessed = ConcurrentlyAccessed.new
83
+ end
84
+
85
+ Aquarium::Aspects::Advice.kinds.each do |advice_kind|
86
+ it "should be removable for :#{advice_kind} advice only when the object join point was advised first" do
87
+ method = advice_kind == :after_raising ? :invoke_raises : :invoke
88
+ @aspects << make_aspect(method, advice_kind, :object, @accessed)
89
+ @aspects << make_aspect(method, advice_kind, :type, ConcurrentlyAccessed)
90
+ @aspects.each {|a| a.unadvise}
91
+ begin
92
+ @accessed.method(method).call :a1, :a2
93
+ fail if advice_kind == :after_raising
94
+ rescue ConcurrentlyAccessed::Error
95
+ fail unless advice_kind == :after_raising
96
+ end
97
+ @invoked.should == []
98
+ end
99
+ end
100
+ end
101
+
102
+
103
+
@@ -0,0 +1,21 @@
1
+ class ConcurrentlyAccessed
2
+ class Error < Exception; end
3
+
4
+ def invoke *args
5
+ @invoked_count += 1
6
+ @invoked_args = args
7
+ end
8
+
9
+ def invoke_raises *args
10
+ @invoked_count += 1
11
+ @invoked_args = args
12
+ raise Error.new(args.inspect)
13
+ end
14
+
15
+ def initialize
16
+ @invoked_count = 0
17
+ @invoked_args = nil
18
+ end
19
+
20
+ attr_reader :invoked_count, :invoked_args
21
+ end
@@ -0,0 +1,514 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
+ require File.dirname(__FILE__) + '/../../spec_example_classes'
3
+ require 'aquarium/aspects/dsl/aspect_dsl'
4
+
5
+ describe Object, "#before" do
6
+ before :each do
7
+ @advice = proc {|jp,*args| "advice"}
8
+ @aspects = []
9
+ end
10
+ after :each do
11
+ @aspects.each {|a| a.unadvise}
12
+ end
13
+
14
+ it "should be equivalent to advise :before." do
15
+ @aspects << advise(:before, :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
16
+ @aspects << before( :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
17
+ @aspects[1].should == @aspects[0]
18
+ end
19
+ end
20
+
21
+ describe Object, "#after" do
22
+ before :each do
23
+ @advice = proc {|jp,*args| "advice"}
24
+ @aspects = []
25
+ end
26
+ after :each do
27
+ @aspects.each {|a| a.unadvise}
28
+ end
29
+
30
+ it "should be equivalent to advise :after." do
31
+ @aspects << advise(:after, :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
32
+ @aspects << after( :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
33
+ @aspects[1].should == @aspects[0]
34
+ end
35
+ end
36
+
37
+ describe Object, "#after_raising_within_or_returning_from" do
38
+ before :each do
39
+ @advice = proc {|jp,*args| "advice"}
40
+ @aspects = []
41
+ end
42
+ after :each do
43
+ @aspects.each {|a| a.unadvise}
44
+ end
45
+
46
+ it "should be equivalent to advise :after." do
47
+ @aspects << after( :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
48
+ @aspects << after_raising_within_or_returning_from(:noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
49
+ @aspects[1].should == @aspects[0]
50
+ end
51
+ end
52
+
53
+ describe Object, "#after_returning" do
54
+ before :each do
55
+ @advice = proc {|jp,*args| "advice"}
56
+ @aspects = []
57
+ end
58
+ after :each do
59
+ @aspects.each {|a| a.unadvise}
60
+ end
61
+
62
+ it "should be equivalent to advise :after_returning." do
63
+ @aspects << advise(:after_returning, :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
64
+ @aspects << after_returning( :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
65
+ @aspects[1].should == @aspects[0]
66
+ end
67
+ end
68
+
69
+ describe Object, "#after_returning_from" do
70
+ before :each do
71
+ @advice = proc {|jp,*args| "advice"}
72
+ @aspects = []
73
+ end
74
+ after :each do
75
+ @aspects.each {|a| a.unadvise}
76
+ end
77
+
78
+ it "should be equivalent to advise :after_returning." do
79
+ @aspects << advise(:after_returning, :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
80
+ @aspects << after_returning_from( :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
81
+ @aspects[1].should == @aspects[0]
82
+ end
83
+ end
84
+
85
+ describe Object, "#after_raising" do
86
+ before :each do
87
+ @advice = proc {|jp,*args| "advice"}
88
+ @aspects = []
89
+ end
90
+ after :each do
91
+ @aspects.each {|a| a.unadvise}
92
+ end
93
+
94
+ it "should be equivalent to advise :after_raising." do
95
+ class ThrowsUp
96
+ def tosses_cookies *args; raise Exception.new(args.inspect); end
97
+ end
98
+ @aspects << advise(:after_raising, :noop, :pointcut => {:type => ThrowsUp, :methods => :tosses_cookies}, &@advice)
99
+ @aspects << after_raising( :noop, :pointcut => {:type => ThrowsUp, :methods => :tosses_cookies}, &@advice)
100
+ @aspects[1].should == @aspects[0]
101
+ end
102
+ end
103
+
104
+ describe Object, "#after_raising_within" do
105
+ before :each do
106
+ @advice = proc {|jp,*args| "advice"}
107
+ @aspects = []
108
+ end
109
+ after :each do
110
+ @aspects.each {|a| a.unadvise}
111
+ end
112
+
113
+ it "should be equivalent to advise :after_raising." do
114
+ class ThrowsUp
115
+ def tosses_cookies *args; raise Exception.new(args.inspect); end
116
+ end
117
+ @aspects << advise(:after_raising, :noop, :pointcut => {:type => ThrowsUp, :methods => :tosses_cookies}, &@advice)
118
+ @aspects << after_raising_within( :noop, :pointcut => {:type => ThrowsUp, :methods => :tosses_cookies}, &@advice)
119
+ @aspects[1].should == @aspects[0]
120
+ end
121
+ end
122
+
123
+ describe Object, "#before_and_after" do
124
+ before :each do
125
+ @advice = proc {|jp,*args| "advice"}
126
+ @aspects = []
127
+ end
128
+ after :each do
129
+ @aspects.each {|a| a.unadvise}
130
+ end
131
+
132
+ it "should be equivalent to advise :before, :after." do
133
+ @aspects << advise(:before, :after, :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
134
+ @aspects << before_and_after(:noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
135
+ @aspects[1].should == @aspects[0]
136
+ end
137
+ end
138
+
139
+ describe Object, "#before_and_after_raising_within_or_returning_from" do
140
+ before :each do
141
+ @advice = proc {|jp,*args| "advice"}
142
+ @aspects = []
143
+ end
144
+ after :each do
145
+ @aspects.each {|a| a.unadvise}
146
+ end
147
+
148
+ it "should be equivalent to advise :before and advise :after." do
149
+ @aspects << advise(:before, :after, :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
150
+ @aspects << before_and_after_raising_within_or_returning_from(:noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
151
+ @aspects[1].should == @aspects[0]
152
+ end
153
+ end
154
+
155
+ describe Object, "#before_and_after_returning" do
156
+ before :each do
157
+ @advice = proc {|jp,*args| "advice"}
158
+ @aspects = []
159
+ end
160
+ after :each do
161
+ @aspects.each {|a| a.unadvise}
162
+ end
163
+
164
+ it "should be equivalent to advise :before and advise :after_returning." do
165
+ @aspects << advise(:before, :after_returning, :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
166
+ @aspects << before_and_after_returning( :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
167
+ @aspects[1].should == @aspects[0]
168
+ end
169
+ end
170
+
171
+ describe Object, "#before_and_after_returning_from" do
172
+ before :each do
173
+ @advice = proc {|jp,*args| "advice"}
174
+ @aspects = []
175
+ end
176
+ after :each do
177
+ @aspects.each {|a| a.unadvise}
178
+ end
179
+
180
+ it "should be equivalent to advise :before and advise :after_returning." do
181
+ @aspects << advise(:before, :after_returning, :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
182
+ @aspects << before_and_after_returning_from(:noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
183
+ @aspects[1].should == @aspects[0]
184
+ end
185
+ end
186
+
187
+ describe Object, "#before_and_after_raising" do
188
+ before :each do
189
+ @advice = proc {|jp,*args| "advice"}
190
+ @aspects = []
191
+ end
192
+ after :each do
193
+ @aspects.each {|a| a.unadvise}
194
+ end
195
+
196
+ it "should be equivalent to advise :before and advise :after_raising." do
197
+ @aspects << advise(:before, :after_raising, :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
198
+ @aspects << before_and_after_raising(:noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
199
+ @aspects[1].should == @aspects[0]
200
+ end
201
+ end
202
+
203
+ describe Object, "#around" do
204
+ before :each do
205
+ @advice = proc {|jp,*args| "advice"}
206
+ @aspects = []
207
+ end
208
+ after :each do
209
+ @aspects.each {|a| a.unadvise}
210
+ end
211
+
212
+ it "should be equivalent to advise :around." do
213
+ @aspects << advise(:around, :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
214
+ @aspects << around( :noop, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, &@advice)
215
+ @aspects[1].should == @aspects[0]
216
+ end
217
+ end
218
+
219
+ describe Object, "#advise (inferred arguments)" do
220
+ before :each do
221
+ @watchful = Watchful.new
222
+ @aspects = []
223
+ end
224
+ after :each do
225
+ @aspects.each {|a| a.unadvise}
226
+ end
227
+
228
+ it "should ignore the default object \"self\" when an :object is specified." do
229
+ class Watchful
230
+ @@watchful = Watchful.new
231
+ @@aspect = after(:object => @@watchful, :method => :public_watchful_method) {|jp,*args|}
232
+ def self.watchful; @@watchful; end
233
+ def self.aspect; @@aspect; end
234
+ end
235
+ @aspects << after(:object => Watchful.watchful, :method => :public_watchful_method) {|jp,*args|}
236
+ @aspects << Watchful.aspect
237
+ @aspects[1].join_points_matched.should == @aspects[0].join_points_matched
238
+ @aspects[1].pointcuts.should == @aspects[0].pointcuts
239
+ end
240
+
241
+ it "should ignore the default object \"self\" when a :type is specified." do
242
+ class Watchful
243
+ @@aspect = after(:type => Watchful, :method => :public_watchful_method) {|jp,*args|}
244
+ def self.aspect; @@aspect; end
245
+ end
246
+ @aspects << after(:type => Watchful, :method => :public_watchful_method) {|jp,*args|}
247
+ @aspects << Watchful.aspect
248
+ @aspects[1].join_points_matched.should == @aspects[0].join_points_matched
249
+ @aspects[1].pointcuts.should == @aspects[0].pointcuts
250
+ end
251
+
252
+ it "should infer the type to advise as \"self\" when no :object, :type, or :pointcut is specified." do
253
+ @aspects << after(:type => Watchful, :method => :public_watchful_method) {|jp,*args|}
254
+ class Watchful
255
+ @@aspect = after(:method => :public_watchful_method) {|jp,*args|}
256
+ def self.aspect; @@aspect; end
257
+ end
258
+ @aspects << Watchful.aspect
259
+ @aspects[1].join_points_matched.should == @aspects[0].join_points_matched
260
+ @aspects[1].pointcuts.should == @aspects[0].pointcuts
261
+ end
262
+
263
+ it "should treat \"ClassName.advise\" as advising instance methods, by default." do
264
+ class WatchfulExampleWithSeparateAdviseCall
265
+ def public_watchful_method *args; end
266
+ end
267
+ advice_called = 0
268
+ WatchfulExampleWithSeparateAdviseCall.before :public_watchful_method do |jp, *args|
269
+ advice_called += 1
270
+ end
271
+ WatchfulExampleWithSeparateAdviseCall.new.public_watchful_method :a1, :a2
272
+ WatchfulExampleWithSeparateAdviseCall.new.public_watchful_method :a3, :a4
273
+ advice_called.should == 2
274
+ end
275
+
276
+ it "should treat \"ClassName.advise\" as advising instance methods when the :instance method option is specified." do
277
+ class WatchfulExampleWithSeparateAdviseCall2
278
+ def self.class_public_watchful_method *args; end
279
+ def public_watchful_method *args; end
280
+ end
281
+ advice_called = 0
282
+ Aquarium::Aspects::Aspect.new :before, :type => WatchfulExampleWithSeparateAdviseCall2, :methods => /public_watchful_method/, :method_options =>[:instance] do |jp, *args|
283
+ advice_called += 1
284
+ end
285
+ WatchfulExampleWithSeparateAdviseCall2.class_public_watchful_method :a1, :a2
286
+ WatchfulExampleWithSeparateAdviseCall2.class_public_watchful_method :a3, :a4
287
+ advice_called.should == 0
288
+ WatchfulExampleWithSeparateAdviseCall2.new.public_watchful_method :a1, :a2
289
+ WatchfulExampleWithSeparateAdviseCall2.new.public_watchful_method :a3, :a4
290
+ advice_called.should == 2
291
+ end
292
+
293
+ it "should treat \"ClassName.advise\" as advising class methods when the :class method option is specified." do
294
+ class WatchfulExampleWithSeparateAdviseCall
295
+ def self.class_public_watchful_method *args; end
296
+ def public_watchful_method *args; end
297
+ end
298
+ advice_called = 0
299
+ WatchfulExampleWithSeparateAdviseCall.before :methods => /public_watchful_method/, :method_options =>[:class] do |jp, *args|
300
+ advice_called += 1
301
+ end
302
+ WatchfulExampleWithSeparateAdviseCall.class_public_watchful_method :a1, :a2
303
+ WatchfulExampleWithSeparateAdviseCall.class_public_watchful_method :a3, :a4
304
+ advice_called.should == 2
305
+ WatchfulExampleWithSeparateAdviseCall.new.public_watchful_method :a1, :a2
306
+ WatchfulExampleWithSeparateAdviseCall.new.public_watchful_method :a3, :a4
307
+ advice_called.should == 2
308
+ end
309
+
310
+ it "should invoke the type-based advise for all objects when the aspect is defined by calling #advise within the class definition." do
311
+ class WatchfulExampleWithBeforeAdvice
312
+ @@advice_called = 0
313
+ def public_watchful_method *args; end
314
+ before :public_watchful_method do |jp, *args|
315
+ @@advice_called += 1
316
+ end
317
+ def self.advice_called; @@advice_called; end
318
+ end
319
+ WatchfulExampleWithBeforeAdvice.new.public_watchful_method :a1, :a2
320
+ WatchfulExampleWithBeforeAdvice.new.public_watchful_method :a3, :a4
321
+ WatchfulExampleWithBeforeAdvice.advice_called.should == 2
322
+ end
323
+
324
+ it "should infer the object to advise as \"self\" when no :object, :type, or :pointcut is specified." do
325
+ @aspects << @watchful.after(:method => :public_watchful_method) {|jp,*args|}
326
+ @aspects << advise( :after, :pointcut => {:object => @watchful, :method => :public_watchful_method}) {|jp,*args|}
327
+ @aspects[1].join_points_matched.should == @aspects[0].join_points_matched
328
+ @aspects[1].pointcuts.should == @aspects[0].pointcuts
329
+ end
330
+
331
+ it "should infer no types or objects if a :pointcut => {...} parameter is used and it does not specify a type or object." do
332
+ @aspects << after(:pointcut => {:method => /method/}) {|jp,*args|}
333
+ @aspects[0].join_points_matched.size.should == 0
334
+ end
335
+
336
+ it "should infer the first symbol parameter after the advice kind parameter is the method name to advise if no other :method => ... parameter is used." do
337
+ @aspects << @watchful.after( :public_watchful_method) {|jp,*args|}
338
+ @aspects.each do |aspect|
339
+ aspect.join_points_matched.size.should == 1
340
+ aspect.specification[:methods].should == Set.new([:public_watchful_method])
341
+ end
342
+ end
343
+ end
344
+
345
+ describe Object, "advice kind convenience methods (inferred arguments)" do
346
+ before :each do
347
+ @advice = proc {|jp,*args| "advice"}
348
+ @watchful = Watchful.new
349
+ @aspects = []
350
+ end
351
+ after :each do
352
+ @aspects.each {|a| a.unadvise}
353
+ end
354
+
355
+ (Aquarium::Aspects::Advice.kinds + [:after_raising_within_or_returning_from]).each do |advice_kind|
356
+ it "##{advice_kind} method should infer the first symbol parameter as the method name to advise if no other :method => ... parameter is used." do
357
+ @aspects << @watchful.method(advice_kind).call(:public_watchful_method, &@advice)
358
+ @aspects.each do |aspect|
359
+ aspect.join_points_matched.size.should == 1
360
+ aspect.specification[:methods].should == Set.new([:public_watchful_method])
361
+ end
362
+ end
363
+ end
364
+ end
365
+
366
+ describe "Synonyms for :types" do
367
+ before :each do
368
+ @advice = proc {|jp,*args| "advice"}
369
+ @aspects = [after(:noop, :types => Watchful, :methods => :public_watchful_method, &@advice)]
370
+ end
371
+ after :each do
372
+ @aspects.each {|a| a.unadvise}
373
+ end
374
+
375
+ it ":type is a synonym for :types" do
376
+ @aspects << after(:noop, :type => Watchful, :methods => :public_watchful_method, &@advice)
377
+ @aspects[1].should == @aspects[0]
378
+ end
379
+
380
+ it ":within_types is a synonym for :types" do
381
+ @aspects << after(:noop, :within_type => Watchful, :methods => :public_watchful_method, &@advice)
382
+ @aspects[1].should == @aspects[0]
383
+ end
384
+
385
+ it ":within_types is a synonym for :types" do
386
+ @aspects << after(:noop, :within_types => Watchful, :methods => :public_watchful_method, &@advice)
387
+ @aspects[1].should == @aspects[0]
388
+ end
389
+ end
390
+
391
+ describe "Synonyms for :objects" do
392
+ before :each do
393
+ @advice = proc {|jp,*args| "advice"}
394
+ @watchful = Watchful.new
395
+ @aspects = [after(:noop, :objects => @watchful, :methods => :public_watchful_method, &@advice)]
396
+ end
397
+ after :each do
398
+ @aspects.each {|a| a.unadvise}
399
+ end
400
+
401
+ it ":object is a synonym for :objects" do
402
+ @aspects << after(:noop, :object => @watchful, :methods => :public_watchful_method, &@advice)
403
+ @aspects[1].should == @aspects[0]
404
+ end
405
+
406
+ it ":within_objects is a synonym for :objects" do
407
+ @aspects << after(:noop, :within_object => @watchful, :methods => :public_watchful_method, &@advice)
408
+ @aspects[1].should == @aspects[0]
409
+ end
410
+
411
+ it ":within_objects is a synonym for :objects" do
412
+ @aspects << after(:noop, :within_objects => @watchful, :methods => :public_watchful_method, &@advice)
413
+ @aspects[1].should == @aspects[0]
414
+ end
415
+ end
416
+
417
+ describe "Synonyms for :methods" do
418
+ before :each do
419
+ @advice = proc {|jp,*args| "advice"}
420
+ @watchful = Watchful.new
421
+ @aspects = [after(:noop, :objects => @watchful, :methods => :public_watchful_method, &@advice)]
422
+ end
423
+ after :each do
424
+ @aspects.each {|a| a.unadvise}
425
+ end
426
+
427
+ it ":method is a synonym for :methods" do
428
+ @aspects << after(:noop, :object => @watchful, :method => :public_watchful_method, &@advice)
429
+ @aspects[1].should == @aspects[0]
430
+ end
431
+
432
+ it ":within_methods is a synonym for :methods" do
433
+ @aspects << after(:noop, :within_object => @watchful, :within_methods => :public_watchful_method, &@advice)
434
+ @aspects[1].should == @aspects[0]
435
+ end
436
+
437
+ it ":within_methods is a synonym for :methods" do
438
+ @aspects << after(:noop, :within_objects => @watchful, :within_method => :public_watchful_method, &@advice)
439
+ @aspects[1].should == @aspects[0]
440
+ end
441
+ end
442
+
443
+ describe "Synonyms for :pointcut" do
444
+ before :each do
445
+ @advice = proc {|jp,*args| "advice"}
446
+ @watchful = Watchful.new
447
+ @aspects = [after(:noop, :pointcut => {:objects => @watchful, :methods => :public_watchful_method}, &@advice)]
448
+ end
449
+ after :each do
450
+ @aspects.each {|a| a.unadvise}
451
+ end
452
+
453
+ it ":pointcuts is a synonym for :pointcut" do
454
+ @aspects << after(:noop, :pointcuts => {:objects => @watchful, :methods => :public_watchful_method}, &@advice)
455
+ @aspects[1].should == @aspects[0]
456
+ end
457
+
458
+ it "should accept :within_pointcuts as a synonym for :pointcut." do
459
+ @aspects << after(:noop, :within_pointcuts => {:objects => @watchful, :methods => :public_watchful_method}, &@advice)
460
+ @aspects[1].should == @aspects[0]
461
+ end
462
+
463
+ it "should accept :within_pointcut as a synonym for :pointcut." do
464
+ @aspects << after(:noop, :within_pointcut => {:objects => @watchful, :methods => :public_watchful_method}, &@advice)
465
+ @aspects[1].should == @aspects[0]
466
+ end
467
+ end
468
+
469
+ describe Object, "#advise (or synonyms) called within a type body" do
470
+ it "will not advise a method whose definition hasn't been seen yet in the type body." do
471
+ class WatchfulWithMethodAlreadyDefined
472
+ @@advice_called = 0
473
+ def public_watchful_method *args; end
474
+ before :public_watchful_method do |jp, *args|
475
+ @@advice_called += 1
476
+ end
477
+ def self.advice_called; @@advice_called; end
478
+ end
479
+ WatchfulWithMethodAlreadyDefined.new.public_watchful_method :a1, :a2
480
+ WatchfulWithMethodAlreadyDefined.new.public_watchful_method :a3, :a4
481
+ WatchfulWithMethodAlreadyDefined.advice_called.should == 2
482
+ class WatchfulWithMethodNotYetDefined
483
+ @@advice_called = 0
484
+ before(:public_watchful_method) {|jp, *args| @@advice_called += 1}
485
+ def public_watchful_method *args; end
486
+ def self.advice_called; @@advice_called; end
487
+ end
488
+ WatchfulWithMethodNotYetDefined.new.public_watchful_method :a1, :a2
489
+ WatchfulWithMethodNotYetDefined.new.public_watchful_method :a3, :a4
490
+ WatchfulWithMethodNotYetDefined.advice_called.should == 0
491
+ end
492
+ end
493
+
494
+ describe Object, "#pointcut" do
495
+ class PC1;
496
+ def doit; end
497
+ end
498
+
499
+ it "should match equivalent join points as Pointcut.new" do
500
+ pointcut1 = pointcut :type => PC1, :method => :doit
501
+ pointcut2 = Aquarium::Aspects::Pointcut.new :type => PC1, :method => :doit
502
+ pointcut1.join_points_matched.should == pointcut2.join_points_matched
503
+ pointcut1.join_points_not_matched.should == pointcut2.join_points_not_matched
504
+ end
505
+
506
+ it "should use self as the object if no object or type is specified." do
507
+ class PC1
508
+ POINTCUT = pointcut :method => :doit
509
+ end
510
+ pointcut2 = Aquarium::Aspects::Pointcut.new :type => PC1, :method => :doit
511
+ PC1::POINTCUT.join_points_matched.should == pointcut2.join_points_matched
512
+ PC1::POINTCUT.join_points_not_matched.should == pointcut2.join_points_not_matched
513
+ end
514
+ end