aquarium 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/CHANGES +26 -5
  2. data/README +8 -8
  3. data/RELEASE-PLAN +20 -2
  4. data/TODO.rb +26 -0
  5. data/UPGRADE +5 -5
  6. data/examples/aspect_design_example.rb +1 -1
  7. data/examples/aspect_design_example_spec.rb +1 -1
  8. data/examples/design_by_contract_example.rb +4 -9
  9. data/examples/design_by_contract_example_spec.rb +7 -9
  10. data/examples/exception_wrapping_example.rb +48 -0
  11. data/examples/exception_wrapping_example_spec.rb +49 -0
  12. data/examples/reusable_aspect_hack_example.rb +56 -0
  13. data/examples/reusable_aspect_hack_example_spec.rb +80 -0
  14. data/lib/aquarium.rb +1 -0
  15. data/lib/aquarium/aspects.rb +1 -1
  16. data/lib/aquarium/aspects/advice.rb +16 -13
  17. data/lib/aquarium/aspects/aspect.rb +81 -56
  18. data/lib/aquarium/aspects/join_point.rb +4 -4
  19. data/lib/aquarium/aspects/pointcut.rb +49 -73
  20. data/lib/aquarium/dsl.rb +2 -0
  21. data/lib/aquarium/dsl/aspect_dsl.rb +77 -0
  22. data/lib/aquarium/{aspects/dsl → dsl}/object_dsl.rb +2 -2
  23. data/lib/aquarium/extras/design_by_contract.rb +1 -1
  24. data/lib/aquarium/finders.rb +1 -1
  25. data/lib/aquarium/finders/method_finder.rb +26 -26
  26. data/lib/aquarium/finders/type_finder.rb +45 -39
  27. data/lib/aquarium/utils/array_utils.rb +6 -5
  28. data/lib/aquarium/utils/default_logger.rb +2 -1
  29. data/lib/aquarium/utils/options_utils.rb +178 -67
  30. data/lib/aquarium/utils/set_utils.rb +8 -3
  31. data/lib/aquarium/version.rb +1 -1
  32. data/spec/aquarium/aspects/aspect_invocation_spec.rb +111 -14
  33. data/spec/aquarium/aspects/aspect_spec.rb +91 -7
  34. data/spec/aquarium/aspects/pointcut_spec.rb +61 -0
  35. data/spec/aquarium/{aspects/dsl → dsl}/aspect_dsl_spec.rb +76 -32
  36. data/spec/aquarium/finders/method_finder_spec.rb +80 -80
  37. data/spec/aquarium/finders/type_finder_spec.rb +57 -52
  38. data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +12 -12
  39. data/spec/aquarium/spec_example_types.rb +4 -3
  40. data/spec/aquarium/utils/array_utils_spec.rb +9 -7
  41. data/spec/aquarium/utils/options_utils_spec.rb +106 -5
  42. data/spec/aquarium/utils/set_utils_spec.rb +14 -0
  43. metadata +12 -7
  44. data/lib/aquarium/aspects/dsl.rb +0 -2
  45. data/lib/aquarium/aspects/dsl/aspect_dsl.rb +0 -64
@@ -6,14 +6,19 @@ module Aquarium
6
6
 
7
7
  # Return a set containing the input item or list of items. If the input
8
8
  # is a set or an array, it is returned. In all cases, the constructed set is a
9
- # flattened version of the input and any nil elements are removed by #strip_nils.
9
+ # flattened version of the input and any nil elements are removed by #strip_set_nils.
10
10
  # Note that this behavior effectively converts +nil+ to +[]+.
11
11
  def make_set *value_or_set_or_array
12
- strip_nils(convert_to_set(*value_or_set_or_array))
12
+ strip_set_nils(convert_to_set(*value_or_set_or_array))
13
13
  end
14
14
 
15
15
  # Return a new set that is a copy of the input set with all nils removed.
16
- def strip_nils set
16
+ def strip_set_nils set
17
+ set.delete_if {|x| x.nil?}
18
+ end
19
+
20
+ # Return a new set that is a copy of the input set with all nils removed.
21
+ def self.strip_set_nils set
17
22
  set.delete_if {|x| x.nil?}
18
23
  end
19
24
 
@@ -9,7 +9,7 @@ module Aquarium
9
9
  unless defined? MAJOR
10
10
  MAJOR = 0
11
11
  MINOR = 4
12
- TINY = 0
12
+ TINY = 1
13
13
  RELEASE_CANDIDATE = nil
14
14
 
15
15
  # RANDOM_TOKEN: 0.598704893979657
@@ -31,7 +31,7 @@ end
31
31
 
32
32
  module Aquarium
33
33
  class AspectInvocationTestClass
34
- include Aquarium::Aspects::DSL::AspectDSL
34
+ include Aquarium::DSL
35
35
  attr_accessor :public_test_method_args
36
36
  def public_test_method *args; @args=args; end
37
37
  protected
@@ -42,6 +42,16 @@ module Aquarium
42
42
  def self.private_class_test_method *args; end
43
43
  private_class_method :private_class_test_method
44
44
  end
45
+ class AspectInvocationTestClass2
46
+ include Aquarium::DSL
47
+ attr_accessor :public_test_method_args
48
+ def public_test_method *args; @args=args; end
49
+ end
50
+ class AspectInvocationTestClass3
51
+ attr_accessor :public_test_method_args
52
+ attr_accessor :public_test_method_args2
53
+ def public_test_method *args; @args=args; end
54
+ end
45
55
  end
46
56
 
47
57
  describe Aspect, "methods" do
@@ -60,15 +70,15 @@ describe Aspect, "methods" do
60
70
  end
61
71
 
62
72
  it "should warn about no join point matches if the :ignore_no_matching_join_points is not specified." do
63
- lambda {Aspect.new(:after, :logger_stream => @log_stream) {|jp, obj, *args| true}}.should raise_error(Aquarium::Utils::InvalidOptions)
73
+ lambda {Aspect.new(:after, :logger_stream => @log_stream) {true}}.should raise_error(Aquarium::Utils::InvalidOptions)
64
74
  @log_stream.string.should_not be_empty
65
75
  end
66
76
  it "should warn about no join point matches if :ignore_no_matching_join_points => false is specified." do
67
- lambda {Aspect.new(:after, :logger_stream => @log_stream, :ignore_no_matching_join_points => false) {|jp, obj, *args| true}}.should raise_error(Aquarium::Utils::InvalidOptions)
77
+ lambda {Aspect.new(:after, :logger_stream => @log_stream, :ignore_no_matching_join_points => false) {true}}.should raise_error(Aquarium::Utils::InvalidOptions)
68
78
  @log_stream.string.should_not be_empty
69
79
  end
70
80
  it "should not warn about no join point matches if :ignore_no_matching_join_points => true is specified." do
71
- lambda {Aspect.new(:after, :logger_stream => @log_stream, :ignore_no_matching_join_points => true) {|jp, obj, *args| true}}.should raise_error(Aquarium::Utils::InvalidOptions)
81
+ lambda {Aspect.new(:after, :logger_stream => @log_stream, :ignore_no_matching_join_points => true) {true}}.should raise_error(Aquarium::Utils::InvalidOptions)
72
82
  @log_stream.string.should be_empty
73
83
  end
74
84
  end
@@ -111,10 +121,18 @@ describe Aspect, "methods" do
111
121
  lambda { Aspect.new :before, :after_raising => Exception, :pointcut => @pointcut_opts, :noop => true }.should_not raise_error(Aquarium::Utils::InvalidOptions)
112
122
  end
113
123
 
114
- it "should accept a a list of exceptions specified with :after_raising." do
124
+ it "should accept a list of exceptions specified with :after_raising." do
115
125
  lambda { Aspect.new :before, :after_raising => [Exception, String], :pointcut => @pointcut_opts, :noop => true }.should_not raise_error(Aquarium::Utils::InvalidOptions)
116
126
  end
117
- end
127
+
128
+ it "should accept a separate :exceptions => list of exceptions specified with :after_raising." do
129
+ lambda { Aspect.new :before, :after_raising, :exceptions => [Exception, String], :pointcut => @pointcut_opts, :noop => true }.should_not raise_error(Aquarium::Utils::InvalidOptions)
130
+ end
131
+
132
+ it "should reject the :exceptions argument unless specified with :after_raising." do
133
+ lambda { Aspect.new :before, :after, :exceptions => [Exception, String], :pointcut => @pointcut_opts, :noop => true }.should raise_error(Aquarium::Utils::InvalidOptions)
134
+ end
135
+ end
118
136
 
119
137
  describe Aspect, ".new (parameters that specify pointcuts)" do
120
138
  before :all do
@@ -122,22 +140,83 @@ describe Aspect, "methods" do
122
140
  end
123
141
 
124
142
  it "should contain at least one of :method(s), :pointcut(s), :type(s), or :object(s)." do
125
- lambda {Aspect.new(:after, :ignore_no_matching_join_points => true) {|jp, obj, *args| true}}.should raise_error(Aquarium::Utils::InvalidOptions)
143
+ lambda {Aspect.new(:after, :ignore_no_matching_join_points => true) {true}}.should raise_error(Aquarium::Utils::InvalidOptions)
126
144
  end
127
145
 
128
146
  it "should contain at least one of :pointcut(s), :type(s), or :object(s) unless :default_objects => object is given." do
129
- aspect = Aspect.new(:after, :default_objects => Aquarium::AspectInvocationTestClass.new, :methods => :public_test_method, :noop => true) {|jp, obj, *args| true}
147
+ aspect = Aspect.new(:after, :default_objects => Aquarium::AspectInvocationTestClass.new, :method => :public_test_method, :noop => true) {true}
148
+ end
149
+
150
+ it "should ignore the :default_objects if at least one other :object is given and the :default_objects are objects." do
151
+ object1 = Aquarium::AspectInvocationTestClass.new
152
+ object2 = Aquarium::AspectInvocationTestClass2.new
153
+ aspect = Aspect.new(:after, :default_objects => object1, :object => object2, :method => :public_test_method) {true}
154
+ aspect.join_points_matched.size.should == 1
155
+ aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == object1}
156
+ end
157
+
158
+ it "should ignore the :default_objects if at least one other :object is given and the :default_objects are types." do
159
+ object = Aquarium::AspectInvocationTestClass2.new
160
+ aspect = Aspect.new(:after, :default_objects => Aquarium::AspectInvocationTestClass,
161
+ :object => object, :method => :public_test_method) {true}
162
+ aspect.join_points_matched.size.should == 1
163
+ aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == Aquarium::AspectInvocationTestClass}
164
+ end
165
+
166
+ it "should ignore the :default_objects if at least one :pointcut is given and the :default_objects are objects." do
167
+ object = Aquarium::AspectInvocationTestClass.new
168
+ aspect = Aspect.new(:after, :default_objects => object,
169
+ :pointcut => {:type => Aquarium::AspectInvocationTestClass2, :method => :public_test_method}, :method => :public_test_method) {true}
170
+ aspect.join_points_matched.size.should == 1
171
+ aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == object}
172
+ end
173
+
174
+ it "should ignore the :default_objects if at least one :pointcut is given and the :default_objects are types." do
175
+ aspect = Aspect.new(:after, :default_objects => Aquarium::AspectInvocationTestClass,
176
+ :pointcut => {:type => Aquarium::AspectInvocationTestClass2, :method => :public_test_method}, :method => :public_test_method) {true}
177
+ aspect.join_points_matched.size.should == 1
178
+ aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == Aquarium::AspectInvocationTestClass}
179
+ end
180
+
181
+ it "should ignore the :default_objects if at least one :join_point is given and the :default_objects are objects." do
182
+ join_point = JoinPoint.new :type => Aquarium::AspectInvocationTestClass2, :method => :public_test_method
183
+ object = Aquarium::AspectInvocationTestClass.new
184
+ aspect = Aspect.new(:after, :default_objects => object, :join_point => join_point, :method => :public_test_method) {true}
185
+ aspect.join_points_matched.size.should == 1
186
+ aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == object}
187
+ end
188
+
189
+ it "should ignore the :default_objects if at least one :join_point is given and the :default_objects are types." do
190
+ join_point = JoinPoint.new :type => Aquarium::AspectInvocationTestClass2, :method => :public_test_method
191
+ aspect = Aspect.new(:after, :default_objects => Aquarium::AspectInvocationTestClass, :join_point => join_point, :method => :public_test_method) {true}
192
+ aspect.join_points_matched.size.should == 1
193
+ aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == Aquarium::AspectInvocationTestClass}
194
+ end
195
+
196
+ [:type, :type_and_descendents, :type_and_ancestors].each do |type_key|
197
+ it "should ignore the :default_objects if at least one :#{type_key} is given and the :default_objects are objects." do
198
+ object = Aquarium::AspectInvocationTestClass.new
199
+ aspect = Aspect.new(:after, :default_objects => object, type_key => Aquarium::AspectInvocationTestClass2, :method => :public_test_method, :method => :public_test_method) {true}
200
+ aspect.join_points_matched.size.should == 1
201
+ aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == object}
202
+ end
203
+
204
+ it "should ignore the :default_objects if at least one :#{type_key} is given and the :default_objects are types." do
205
+ aspect = Aspect.new(:after, :default_objects => Aquarium::AspectInvocationTestClass, type_key => Aquarium::AspectInvocationTestClass2, :method => :public_test_method, :method => :public_test_method) {true}
206
+ aspect.join_points_matched.size.should == 1
207
+ aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == Aquarium::AspectInvocationTestClass}
208
+ end
130
209
  end
131
210
 
132
211
  Aspect::CANONICAL_OPTIONS["default_objects"].each do |key|
133
212
  it "should accept :#{key} as a synonym for :default_objects." do
134
- aspect = Aspect.new(:after, key.intern => Aquarium::AspectInvocationTestClass.new, :methods => :public_test_method, :noop => true) {|jp, obj, *args| true}
213
+ aspect = Aspect.new(:after, key.intern => Aquarium::AspectInvocationTestClass.new, :method => :public_test_method, :noop => true) {true}
135
214
  end
136
215
  end
137
216
 
138
217
  it "should not contain :pointcut(s) and either :type(s) or :object(s)." do
139
- lambda {Aspect.new(:after, :pointcuts => @pointcut_opts, :type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method) {|jp, obj, *args| true}}.should raise_error(Aquarium::Utils::InvalidOptions)
140
- lambda {Aspect.new(:after, :pointcuts => @pointcut_opts, :object => Aquarium::AspectInvocationTestClass.new, :methods => :public_test_method) {|jp, obj, *args| true}}.should raise_error(Aquarium::Utils::InvalidOptions)
218
+ lambda {Aspect.new(:after, :pointcuts => @pointcut_opts, :type => Aquarium::AspectInvocationTestClass, :method => :public_test_method) {true}}.should raise_error(Aquarium::Utils::InvalidOptions)
219
+ lambda {Aspect.new(:after, :pointcuts => @pointcut_opts, :object => Aquarium::AspectInvocationTestClass.new, :method => :public_test_method) {true}}.should raise_error(Aquarium::Utils::InvalidOptions)
141
220
  end
142
221
  end
143
222
 
@@ -218,6 +297,18 @@ describe Aspect, "methods" do
218
297
  end
219
298
  end
220
299
 
300
+ it "should require the values for :reading => ... and :writing => ... to be equal if both are specified." do
301
+ @advice = Proc.new {}
302
+ lambda {Aspect.new :before, :type => Aquarium::AspectInvocationTestClass3,
303
+ :reading => :public_test_method_args, :writing => :public_test_method_args2, :advice => @advice}.should raise_error(Aquarium::Utils::InvalidOptions)
304
+ end
305
+
306
+ it "should require the values for :reading => ... and :changing => ... to be equal if both are specified." do
307
+ @advice = Proc.new {}
308
+ lambda {Aspect.new :before, :type => Aquarium::AspectInvocationTestClass3,
309
+ :reading => :public_test_method_args, :changing => :public_test_method_args2, :advice => @advice}.should raise_error(Aquarium::Utils::InvalidOptions)
310
+ end
311
+
221
312
  it "should accept :reading => ... as a synonym for :attributes => ..., :attribute_options => [:readers]." do
222
313
  @advice = Proc.new {}
223
314
  @expected_methods = [:public_test_method_args]
@@ -1283,12 +1374,11 @@ describe Aspect, "methods" do
1283
1374
  advice_called.should be_true
1284
1375
  aspect.unadvise
1285
1376
  end
1286
-
1287
1377
  end
1288
1378
 
1289
1379
  describe Aspect, ".new (advice block or proc parameter list)" do
1290
1380
  it "should raise unless an advice block or :advice => advice parameter is specified." do
1291
- lambda {Aspect.new(:after, :type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method)}.should raise_error(Aquarium::Utils::InvalidOptions)
1381
+ lambda { Aspect.new(:after, :type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method)}.should raise_error(Aquarium::Utils::InvalidOptions)
1292
1382
  end
1293
1383
 
1294
1384
  it "should raise if obsolete |jp, *args| list is used." do
@@ -1315,7 +1405,14 @@ describe Aspect, "methods" do
1315
1405
  lambda { Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method, :noop => true do; end }.should_not raise_error(Exception)
1316
1406
  end
1317
1407
  end
1318
-
1408
+
1409
+ describe Aspect, ".new (advice block to around advice with just the join_point parameter - Bug #19262)" do
1410
+ it "should work not raise an error" do
1411
+ aspect = Aspect.new :around, :type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method do |jp|; jp.proceed; end
1412
+ Aquarium::AspectInvocationTestClass.new.public_test_method
1413
+ aspect.unadvise
1414
+ end
1415
+ end
1319
1416
 
1320
1417
  class ExcludeBase
1321
1418
  def doit; end
@@ -220,6 +220,10 @@ describe Aspect, " with :after_returning advice" do
220
220
  end
221
221
  end
222
222
 
223
+ class MyError1 < StandardError; end
224
+ class MyError2 < StandardError; end
225
+ class MyError3 < StandardError; end
226
+
223
227
  describe Aspect, " with :after_raising advice" do
224
228
  after(:each) do
225
229
  @aspect.unadvise if @aspect
@@ -248,10 +252,39 @@ describe Aspect, " with :after_raising advice" do
248
252
  do_watchful_public_protected_private true
249
253
  end
250
254
 
251
- it "should not advise rescue clauses for raised exceptions of types that don't match the specified exception" do
252
- class MyError < StandardError; end
255
+ it "should invoke advice when exceptions of the specified type are raised" do
256
+ aspect_advice_invoked = false
257
+ @aspect = Aspect.new(:after_raising => Watchful::WatchfulError, :pointcut => {:type => Watchful, :methods => /public_watchful_method/}) {|jp, obj, *args| aspect_advice_invoked = true}
258
+ block_invoked = false
259
+ watchful = Watchful.new
260
+ lambda {watchful.public_watchful_method_that_raises(:a1, :a2, :a3) {|*args| block_invoked = true}}.should raise_error(Watchful::WatchfulError)
261
+ aspect_advice_invoked.should be_true
262
+ block_invoked.should be_true
263
+ end
264
+
265
+ it "should invoke advice when exceptions of the specified type are raised, which were specified with :exceptions => ..." do
266
+ aspect_advice_invoked = false
267
+ @aspect = Aspect.new(:after_raising, :exceptions => Watchful::WatchfulError, :pointcut => {:type => Watchful, :methods => /public_watchful_method/}) {|jp, obj, *args| aspect_advice_invoked = true}
268
+ block_invoked = false
269
+ watchful = Watchful.new
270
+ lambda {watchful.public_watchful_method_that_raises(:a1, :a2, :a3) {|*args| block_invoked = true}}.should raise_error(Watchful::WatchfulError)
271
+ aspect_advice_invoked.should be_true
272
+ block_invoked.should be_true
273
+ end
274
+
275
+ it "should not invoke advice when exceptions of types that don't match the specified exception type are raised" do
276
+ aspect_advice_invoked = false
277
+ @aspect = Aspect.new(:after_raising => MyError1, :pointcut => {:type => Watchful, :methods => /public_watchful_method/}) {|jp, obj, *args| aspect_advice_invoked = true}
278
+ block_invoked = false
279
+ watchful = Watchful.new
280
+ lambda {watchful.public_watchful_method_that_raises(:a1, :a2, :a3) {|*args| block_invoked = true}}.should raise_error(Watchful::WatchfulError)
281
+ aspect_advice_invoked.should be_false
282
+ block_invoked.should be_true
283
+ end
284
+
285
+ it "should not invoke advice when exceptions of types that don't match the specified exception type are raised, which were specified with :exceptions => ..." do
253
286
  aspect_advice_invoked = false
254
- @aspect = Aspect.new(:after_raising => MyError, :pointcut => {:type => Watchful, :methods => /public_watchful_method/}) {|jp, obj, *args| aspect_advice_invoked = true}
287
+ @aspect = Aspect.new(:after_raising, :exceptions => MyError1, :pointcut => {:type => Watchful, :methods => /public_watchful_method/}) {|jp, obj, *args| aspect_advice_invoked = true}
255
288
  block_invoked = false
256
289
  watchful = Watchful.new
257
290
  lambda {watchful.public_watchful_method_that_raises(:a1, :a2, :a3) {|*args| block_invoked = true}}.should raise_error(Watchful::WatchfulError)
@@ -259,9 +292,17 @@ describe Aspect, " with :after_raising advice" do
259
292
  block_invoked.should be_true
260
293
  end
261
294
 
262
- it "should not advise rescue clauses for raised exceptions of types that don't match the list of specified exceptions" do
263
- class MyError1 < StandardError; end
264
- class MyError2 < StandardError; end
295
+ it "should invoke advice when one exception in the list of the specified types is raised" do
296
+ aspect_advice_invoked = false
297
+ @aspect = Aspect.new(:after_raising => [Watchful::WatchfulError, MyError1], :pointcut => {:type => Watchful, :methods => /public_watchful_method/}) {|jp, obj, *args| aspect_advice_invoked = true}
298
+ block_invoked = false
299
+ watchful = Watchful.new
300
+ lambda {watchful.public_watchful_method_that_raises(:a1, :a2, :a3) {|*args| block_invoked = true}}.should raise_error(Watchful::WatchfulError)
301
+ aspect_advice_invoked.should be_true
302
+ block_invoked.should be_true
303
+ end
304
+
305
+ it "should not invoke advice when exceptions of types that don't match the specified list of exception types are raised" do
265
306
  aspect_advice_invoked = false
266
307
  @aspect = Aspect.new(:after_raising => [MyError1, MyError2], :pointcut => {:type => Watchful, :methods => /public_watchful_method/}) {|jp, obj, *args| aspect_advice_invoked = true}
267
308
  block_invoked = false
@@ -271,7 +312,33 @@ describe Aspect, " with :after_raising advice" do
271
312
  block_invoked.should be_true
272
313
  end
273
314
 
274
- it "should advise all rescue clauses in the matched methods, if no specific exceptions are specified" do
315
+ it "should not invoke advice when exceptions of types that don't match the specified list of exception types are raised, which were specified with :exceptions => ..." do
316
+ aspect_advice_invoked = false
317
+ @aspect = Aspect.new(:after_raising, :exceptions => [MyError1, MyError2], :pointcut => {:type => Watchful, :methods => /public_watchful_method/}) {|jp, obj, *args| aspect_advice_invoked = true}
318
+ block_invoked = false
319
+ watchful = Watchful.new
320
+ lambda {watchful.public_watchful_method_that_raises(:a1, :a2, :a3) {|*args| block_invoked = true}}.should raise_error(Watchful::WatchfulError)
321
+ aspect_advice_invoked.should be_false
322
+ block_invoked.should be_true
323
+ end
324
+
325
+ it "should treat :exception as a synonym for :exceptions" do
326
+ aspect_advice_invoked = false
327
+ @aspect = Aspect.new(:after_raising, :exception => [MyError1, MyError2], :pointcut => {:type => Watchful, :methods => /public_watchful_method/}) {|jp, obj, *args| aspect_advice_invoked = true}
328
+ block_invoked = false
329
+ watchful = Watchful.new
330
+ lambda {watchful.public_watchful_method_that_raises(:a1, :a2, :a3) {|*args| block_invoked = true}}.should raise_error(Watchful::WatchfulError)
331
+ aspect_advice_invoked.should be_false
332
+ block_invoked.should be_true
333
+ end
334
+
335
+ it "should merge exceptions specified with :exception(s) and :after_raising" do
336
+ aspect_advice_invoked = false
337
+ @aspect = Aspect.new(:after_raising => MyError1, :exception => [MyError2, MyError3], :pointcut => {:type => Watchful, :methods => /public_watchful_method/}) {|jp, obj, *args| aspect_advice_invoked = true}
338
+ @aspect.specification[:after_raising].should eql(Set.new([MyError1, MyError2, MyError3]))
339
+ end
340
+
341
+ it "should advise all methods that raise exceptions when no specific exceptions are specified" do
275
342
  class ClassThatRaises
276
343
  class CTRException < Exception; end
277
344
  def raises
@@ -287,6 +354,23 @@ describe Aspect, " with :after_raising advice" do
287
354
  lambda {ctr.raises}.should raise_error(ClassThatRaises::CTRException)
288
355
  aspect_advice_invoked.should be_true
289
356
  end
357
+
358
+ it "should advise all methods that raise strings (which are converted to RuntimeError) when no specific exceptions are specified" do
359
+ class ClassThatRaisesString
360
+ class CTRException < Exception; end
361
+ def raises
362
+ raise "A string exception."
363
+ end
364
+ end
365
+ aspect_advice_invoked = false
366
+ @aspect = Aspect.new :after_raising, :pointcut => {:type => ClassThatRaisesString, :methods => :raises} do |jp, obj, *args|
367
+ aspect_advice_invoked = true
368
+ end
369
+ aspect_advice_invoked.should be_false
370
+ ctr = ClassThatRaisesString.new
371
+ lambda {ctr.raises}.should raise_error(RuntimeError)
372
+ aspect_advice_invoked.should be_true
373
+ end
290
374
  end
291
375
 
292
376
  describe Aspect, " with :before and :after advice" do
@@ -365,6 +365,67 @@ describe Pointcut, "methods" do
365
365
  end
366
366
  end
367
367
 
368
+ describe Pointcut, ".new (default_objects specified)" do
369
+ it "should use the :default_objects if specified and no other :join_point, :type, or :object is given." do
370
+ object1 = ClassWithPublicInstanceMethod.new
371
+ pc = Pointcut.new :default_objects => object1, :method => :public_instance_test_method
372
+ pc.join_points_matched.size.should == 1
373
+ pc.join_points_matched.each {|jp| jp.type_or_object.should == object1}
374
+ end
375
+
376
+ it "should ignore the :default_objects if at least one other :object is given and the :default_objects are objects." do
377
+ object1 = ClassWithPublicInstanceMethod.new
378
+ object2 = ClassWithPublicInstanceMethod.new
379
+ pc = Pointcut.new :default_objects => object1, :object => object2, :method => :public_instance_test_method
380
+ pc.join_points_matched.size.should == 1
381
+ pc.join_points_matched.each {|jp| jp.type_or_object.should == object2}
382
+ end
383
+
384
+ it "should ignore the :default_objects if at least one other :object is given and the :default_objects are types." do
385
+ object = ClassWithProtectedInstanceMethod.new
386
+ pc = Pointcut.new :default_objects => ClassWithPublicInstanceMethod, :object => object, :method => /_instance_test_method/, :method_options => [:public, :protected, :exclude_ancestor_methods]
387
+ pc.join_points_matched.size.should == 1
388
+ pc.join_points_matched.each {|jp| jp.type_or_object.should_not == ClassWithPublicInstanceMethod}
389
+ end
390
+
391
+ it "should ignore the :default_objects if at least one :join_point is given and the :default_objects are objects." do
392
+ join_point = JoinPoint.new :type => ClassWithProtectedInstanceMethod, :method => :protected_instance_test_method
393
+ object = ClassWithProtectedInstanceMethod.new
394
+ pc = Pointcut.new :default_objects => object, :join_point => join_point, :method => /_instance_test_method/, :method_options => [:public, :protected, :exclude_ancestor_methods]
395
+ pc.join_points_matched.size.should == 1
396
+ pc.join_points_matched.each {|jp| jp.type_or_object.should_not == ClassWithPublicInstanceMethod}
397
+ end
398
+
399
+ it "should ignore the :default_objects if at least one :pointcut is given and the :default_objects are types." do
400
+ join_point = JoinPoint.new :type => ClassWithProtectedInstanceMethod, :method => :protected_instance_test_method
401
+ object = ClassWithProtectedInstanceMethod.new
402
+ pc = Pointcut.new :default_objects => ClassWithPublicInstanceMethod, :join_point => join_point, :method => /_instance_test_method/, :method_options => [:public, :protected, :exclude_ancestor_methods]
403
+ pc.join_points_matched.size.should == 1
404
+ pc.join_points_matched.each {|jp| jp.type_or_object.should_not == ClassWithPublicInstanceMethod}
405
+ end
406
+
407
+ [:type, :type_and_descendents, :type_and_ancestors].each do |type_key|
408
+ it "should ignore the :default_objects if at least one :#{type_key} is given and the :default_objects are objects." do
409
+ object = ClassWithPublicInstanceMethod.new
410
+ pc = Pointcut.new :default_objects => object, type_key => ClassWithProtectedInstanceMethod, :method => /_instance_test_method/, :method_options => [:public, :protected, :exclude_ancestor_methods]
411
+ pc.join_points_matched.size.should == 1
412
+ pc.join_points_matched.each {|jp| jp.type_or_object.should_not == ClassWithPublicInstanceMethod}
413
+ end
414
+
415
+ it "should ignore the :default_objects if at least one :#{type_key} is given and the :default_objects are types." do
416
+ pc = Pointcut.new :default_objects => ClassWithPublicInstanceMethod, type_key => ClassWithProtectedInstanceMethod, :method => /_instance_test_method/, :method_options => [:public, :protected, :exclude_ancestor_methods]
417
+ pc.join_points_matched.size.should == 1
418
+ pc.join_points_matched.each {|jp| jp.type_or_object.should_not == ClassWithPublicInstanceMethod}
419
+ end
420
+ end
421
+
422
+ Aspect::CANONICAL_OPTIONS["default_objects"].each do |key|
423
+ it "should accept :#{key} as a synonym for :default_objects." do
424
+ pc = Pointcut.new key.intern => ClassWithPublicInstanceMethod.new, :method => :public_instance_test_method
425
+ end
426
+ end
427
+ end
428
+
368
429
  describe Pointcut, ".new (:exclude_types => types specified)" do
369
430
  before(:each) do
370
431
  before_exclude_spec
@@ -1,12 +1,12 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
2
- require File.dirname(__FILE__) + '/../../spec_example_types'
3
- require 'aquarium/aspects/dsl/aspect_dsl'
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/../spec_example_types'
3
+ require 'aquarium/dsl/aspect_dsl'
4
4
 
5
5
  class DSLClass
6
- include Aquarium::Aspects::DSL::AspectDSL
6
+ include Aquarium::DSL
7
7
  end
8
8
 
9
- describe "Aquarium::Aspects::DSL::AspectDSL" do
9
+ describe "Aquarium::DSL" do
10
10
  before :all do
11
11
  @advice = proc {|jp, obj, *args| "advice"}
12
12
  @pointcut_opts = {:calls_to => :public_watchful_method, :in_type => Watchful}
@@ -20,7 +20,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
20
20
  @aspects.each {|a| a.unadvise}
21
21
  end
22
22
 
23
- it "should be equivalent to advise :before." do
23
+ it "should be equivalent to advice kind :before." do
24
24
  @aspects << DSLClass.advise(:before, :noop => true, :pointcut => @pointcut_opts, &@advice)
25
25
  @aspects << DSLClass.before( :noop => true, :pointcut => @pointcut_opts, &@advice)
26
26
  @aspects[1].should == @aspects[0]
@@ -35,13 +35,36 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
35
35
  @aspects.each {|a| a.unadvise}
36
36
  end
37
37
 
38
- it "should be equivalent to advise :after." do
38
+ it "should be equivalent to advice kind :after." do
39
39
  @aspects << DSLClass.advise(:after, :noop => true, :pointcut => @pointcut_opts, &@advice)
40
40
  @aspects << DSLClass.after( :noop => true, :pointcut => @pointcut_opts, &@advice)
41
41
  @aspects[1].should == @aspects[0]
42
42
  end
43
43
  end
44
44
 
45
+ describe "DSL method #after_raising" do
46
+ before :each do
47
+ @aspects = []
48
+ end
49
+ after :each do
50
+ @aspects.each {|a| a.unadvise}
51
+ end
52
+
53
+ it "should be equivalent to advice kind :after_raising." do
54
+ @aspects << DSLClass.advise(:after_raising, :noop => true, :pointcut => @pointcut_opts, &@advice)
55
+ @aspects << DSLClass.after_raising( :noop => true, :pointcut => @pointcut_opts, &@advice)
56
+ @aspects[1].should == @aspects[0]
57
+ end
58
+
59
+ it "should be equivalent to advice kind :after_raising => exceptions, when used with the :exceptions argument." do
60
+ @aspects << DSLClass.advise(:after_raising, :exceptions => [StandardError], :noop => true, :pointcut => @pointcut_opts, &@advice)
61
+ @aspects << DSLClass.after_raising( :exceptions => [StandardError], :noop => true, :pointcut => @pointcut_opts, &@advice)
62
+ @aspects << Aquarium::Aspects::Aspect.new(:after_raising => [StandardError], :noop => true, :pointcut => @pointcut_opts, &@advice)
63
+ @aspects[1].should == @aspects[0]
64
+ @aspects[2].specification[:after_raising].should == @aspects[0].specification[:after_raising]
65
+ end
66
+ end
67
+
45
68
  describe "DSL method #after_raising_within_or_returning_from" do
46
69
  before :each do
47
70
  @dsl = DSLClass.new
@@ -52,7 +75,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
52
75
  @aspects.each {|a| a.unadvise}
53
76
  end
54
77
 
55
- it "should be equivalent to advise :after." do
78
+ it "should be equivalent to advice kind :after." do
56
79
  @aspects << DSLClass.after( :noop => true, :pointcut => @pointcut_opts, &@advice)
57
80
  @aspects << DSLClass.after_raising_within_or_returning_from(:noop => true, :pointcut => @pointcut_opts, &@advice)
58
81
  @aspects[1].should == @aspects[0]
@@ -69,7 +92,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
69
92
  @aspects.each {|a| a.unadvise}
70
93
  end
71
94
 
72
- it "should be equivalent to advise :after_returning." do
95
+ it "should be equivalent to advice kind :after_returning." do
73
96
  @aspects << DSLClass.advise(:after_returning, :noop => true, :pointcut => @pointcut_opts, &@advice)
74
97
  @aspects << DSLClass.after_returning( :noop => true, :pointcut => @pointcut_opts, &@advice)
75
98
  @aspects[1].should == @aspects[0]
@@ -86,7 +109,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
86
109
  @aspects.each {|a| a.unadvise}
87
110
  end
88
111
 
89
- it "should be equivalent to advise :after_returning." do
112
+ it "should be equivalent to advice kind :after_returning." do
90
113
  @aspects << DSLClass.advise(:after_returning, :noop => true, :pointcut => @pointcut_opts, &@advice)
91
114
  @aspects << DSLClass.after_returning_from( :noop => true, :pointcut => @pointcut_opts, &@advice)
92
115
  @aspects[1].should == @aspects[0]
@@ -103,7 +126,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
103
126
  @aspects.each {|a| a.unadvise}
104
127
  end
105
128
 
106
- it "should be equivalent to advise :after_raising." do
129
+ it "should be equivalent to advice kind :after_raising." do
107
130
  class ThrowsUp
108
131
  def tosses_cookies *args; raise Exception.new(args.inspect); end
109
132
  end
@@ -123,7 +146,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
123
146
  @aspects.each {|a| a.unadvise}
124
147
  end
125
148
 
126
- it "should be equivalent to advise :after_raising." do
149
+ it "should be equivalent to advice kind :after_raising." do
127
150
  class ThrowsUp
128
151
  def tosses_cookies *args; raise Exception.new(args.inspect); end
129
152
  end
@@ -143,7 +166,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
143
166
  @aspects.each {|a| a.unadvise}
144
167
  end
145
168
 
146
- it "should be equivalent to advise :before, :after." do
169
+ it "should be equivalent to advice kind :before, :after." do
147
170
  @aspects << DSLClass.advise(:before, :after, :noop => true, :pointcut => @pointcut_opts, &@advice)
148
171
  @aspects << DSLClass.before_and_after( :noop => true, :pointcut => @pointcut_opts, &@advice)
149
172
  @aspects[1].should == @aspects[0]
@@ -160,7 +183,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
160
183
  @aspects.each {|a| a.unadvise}
161
184
  end
162
185
 
163
- it "should be equivalent to advise :before and advise :after." do
186
+ it "should be equivalent to advice kind :before and advice :after." do
164
187
  @aspects << DSLClass.advise(:before, :after, :noop => true, :pointcut => @pointcut_opts, &@advice)
165
188
  @aspects << DSLClass.before_and_after_raising_within_or_returning_from(:noop => true, :pointcut => @pointcut_opts, &@advice)
166
189
  @aspects[1].should == @aspects[0]
@@ -177,7 +200,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
177
200
  @aspects.each {|a| a.unadvise}
178
201
  end
179
202
 
180
- it "should be equivalent to advise :before and advise :after_returning." do
203
+ it "should be equivalent to advice kind :before and advice :after_returning." do
181
204
  @aspects << DSLClass.advise(:before, :after_returning, :noop => true, :pointcut => @pointcut_opts, &@advice)
182
205
  @aspects << DSLClass.before_and_after_returning( :noop => true, :pointcut => @pointcut_opts, &@advice)
183
206
  @aspects[1].should == @aspects[0]
@@ -194,7 +217,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
194
217
  @aspects.each {|a| a.unadvise}
195
218
  end
196
219
 
197
- it "should be equivalent to advise :before and advise :after_returning." do
220
+ it "should be equivalent to advice kind :before and advice :after_returning." do
198
221
  @aspects << DSLClass.advise(:before, :after_returning, :noop => true, :pointcut => @pointcut_opts, &@advice)
199
222
  @aspects << DSLClass.before_and_after_returning_from( :noop => true, :pointcut => @pointcut_opts, &@advice)
200
223
  @aspects[1].should == @aspects[0]
@@ -211,7 +234,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
211
234
  @aspects.each {|a| a.unadvise}
212
235
  end
213
236
 
214
- it "should be equivalent to advise :before and advise :after_raising." do
237
+ it "should be equivalent to advice kind :before and advice :after_raising." do
215
238
  @aspects << DSLClass.advise(:before, :after_raising, :noop => true, :pointcut => @pointcut_opts, &@advice)
216
239
  @aspects << DSLClass.before_and_after_raising( :noop => true, :pointcut => @pointcut_opts, &@advice)
217
240
  @aspects[1].should == @aspects[0]
@@ -228,7 +251,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
228
251
  @aspects.each {|a| a.unadvise}
229
252
  end
230
253
 
231
- it "should be equivalent to advise :around." do
254
+ it "should be equivalent to advice kind :around." do
232
255
  @aspects << DSLClass.advise(:around, :noop => true, :pointcut => @pointcut_opts, &@advice)
233
256
  @aspects << DSLClass.around( :noop => true, :pointcut => @pointcut_opts, &@advice)
234
257
  @aspects[1].should == @aspects[0]
@@ -245,7 +268,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
245
268
 
246
269
  it "should ignore the default object \"self\" when an :object is specified." do
247
270
  class Watchful1
248
- include Aquarium::Aspects::DSL::AspectDSL
271
+ include Aquarium::DSL
249
272
  def public_watchful_method; end
250
273
  @@watchful = Watchful1.new
251
274
  @@aspect = after(:invoking => :public_watchful_method, :on_object => @@watchful) {|jp, obj, *args|}
@@ -261,7 +284,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
261
284
 
262
285
  it "should ignore the default object \"self\" when a :type is specified." do
263
286
  class Watchful2
264
- include Aquarium::Aspects::DSL::AspectDSL
287
+ include Aquarium::DSL
265
288
  def public_watchful_method; end
266
289
  @@aspect = after(:calls_to => :public_watchful_method, :in_type => Watchful2) {|jp, obj, *args|}
267
290
  def self.aspect; @@aspect; end
@@ -282,7 +305,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
282
305
  end
283
306
 
284
307
  class WatchfulSelf
285
- include Aquarium::Aspects::DSL::AspectDSL
308
+ include Aquarium::DSL
286
309
  @@aspect = nil
287
310
  def self.aspect; @@aspect; end
288
311
  def public_watchful_method; "public_watchful_method"; end
@@ -300,7 +323,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
300
323
 
301
324
  it "should infer the object as \"self\" when no :object, :type, or :pointcut is specified." do
302
325
  watchful_self = WatchfulSelf.new
303
- watchful_self.extend Aquarium::Aspects::DSL::AspectDSL
326
+ watchful_self.extend Aquarium::DSL
304
327
  @aspects << WatchfulSelf.advise(:after, :pointcut => {:invoking => :public_watchful_method, :on_object => watchful_self}) {|jp, obj, *args|}
305
328
  @aspects << watchful_self.after(:method => :public_watchful_method) {|jp, obj, *args|}
306
329
  @aspects[1].join_points_matched.should == @aspects[0].join_points_matched
@@ -314,7 +337,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
314
337
 
315
338
  describe "DSL method #advise, when parsing the parameter list," do
316
339
  class Watchful3
317
- include Aquarium::Aspects::DSL::AspectDSL
340
+ include Aquarium::DSL
318
341
  def public_watchful_method; "public_watchful_method"; end
319
342
  end
320
343
 
@@ -344,7 +367,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
344
367
 
345
368
  it "should treat \"ClassName.advise\" as advising instance methods, by default." do
346
369
  class WatchfulExampleWithSeparateAdviseCall
347
- include Aquarium::Aspects::DSL::AspectDSL
370
+ include Aquarium::DSL
348
371
  def public_watchful_method *args; end
349
372
  end
350
373
  advice_called = 0
@@ -358,7 +381,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
358
381
 
359
382
  it "should treat \"ClassName.advise\" as advising instance methods when the :instance method option is specified." do
360
383
  class WatchfulExampleWithSeparateAdviseCall2
361
- include Aquarium::Aspects::DSL::AspectDSL
384
+ include Aquarium::DSL
362
385
  def self.class_public_watchful_method *args; end
363
386
  def public_watchful_method *args; end
364
387
  end
@@ -376,7 +399,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
376
399
 
377
400
  it "should treat \"ClassName.advise\" as advising class methods when the :class method option is specified." do
378
401
  class WatchfulExampleWithSeparateAdviseCall3
379
- include Aquarium::Aspects::DSL::AspectDSL
402
+ include Aquarium::DSL
380
403
  def self.class_public_watchful_method *args; end
381
404
  def public_watchful_method *args; end
382
405
  end
@@ -394,7 +417,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
394
417
 
395
418
  it "should invoke the type-based advise for all objects when the aspect is defined by calling #advise within the class definition." do
396
419
  class WatchfulExampleWithBeforeAdvice
397
- include Aquarium::Aspects::DSL::AspectDSL
420
+ include Aquarium::DSL
398
421
  @@advice_called = 0
399
422
  def public_watchful_method *args; end
400
423
  before :public_watchful_method do |jp, obj, *args|
@@ -410,7 +433,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
410
433
 
411
434
  describe "DSL methods for the advice kind, when determining instance or class methods to advise," do
412
435
  class Watchful4
413
- include Aquarium::Aspects::DSL::AspectDSL
436
+ include Aquarium::DSL
414
437
  def public_watchful_method; "public_watchful_method"; end
415
438
  end
416
439
 
@@ -490,7 +513,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
490
513
  describe "DSL method #advise (or synonyms) called within a type body" do
491
514
  it "will not advise a method whose definition hasn't been seen yet in the type body." do
492
515
  class WatchfulWithMethodAlreadyDefined
493
- include Aquarium::Aspects::DSL::AspectDSL
516
+ include Aquarium::DSL
494
517
  @@advice_called = 0
495
518
  def public_watchful_method *args; end
496
519
  before :public_watchful_method do |jp, obj, *args|
@@ -502,7 +525,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
502
525
  WatchfulWithMethodAlreadyDefined.new.public_watchful_method :a3, :a4
503
526
  WatchfulWithMethodAlreadyDefined.advice_called.should == 2
504
527
  class WatchfulWithMethodNotYetDefined
505
- include Aquarium::Aspects::DSL::AspectDSL
528
+ include Aquarium::DSL
506
529
  @@advice_called = 0
507
530
  before(:public_watchful_method, :ignore_no_matching_join_points=>true) {|jp, obj, *args| @@advice_called += 1}
508
531
  def public_watchful_method *args; end
@@ -528,7 +551,7 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
528
551
 
529
552
  it "should use self as the object if no object or type is specified." do
530
553
  class PC2
531
- include Aquarium::Aspects::DSL::AspectDSL
554
+ include Aquarium::DSL
532
555
  POINTCUT = pointcut :method => :doit
533
556
  end
534
557
  pointcut2 = Aquarium::Aspects::Pointcut.new :type => PC2, :method => :doit
@@ -536,4 +559,25 @@ describe "Aquarium::Aspects::DSL::AspectDSL" do
536
559
  PC2::POINTCUT.join_points_not_matched.should == pointcut2.join_points_not_matched
537
560
  end
538
561
  end
539
- end
562
+
563
+ class OldDSLClass
564
+ include Aquarium::Aspects::DSL::AspectDSL
565
+ end
566
+ describe "DSL methods available through the old package Aquarium::Aspects::DSL::AspectDSL" do
567
+ before :each do
568
+ @dsl = OldDSLClass.new
569
+ @advice = proc {|jp, obj, *args| "advice"}
570
+ @aspects = []
571
+ end
572
+ after :each do
573
+ @aspects.each {|a| a.unadvise}
574
+ end
575
+
576
+ it "should be equivalent to advice kind :around." do
577
+ @aspects << OldDSLClass.advise(:around, :noop => true, :pointcut => @pointcut_opts, &@advice)
578
+ @aspects << OldDSLClass.around( :noop => true, :pointcut => @pointcut_opts, &@advice)
579
+ @aspects[1].should == @aspects[0]
580
+ end
581
+ end
582
+ end
583
+