aquarium 0.1.0

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