aquarium 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/Aquarium-IDEA.ipr +252 -0
  2. data/Aquarium-IDEA.iws +493 -0
  3. data/Aquarium.ipr +1 -1
  4. data/Aquarium.iws +133 -138
  5. data/CHANGES +63 -0
  6. data/ParseTreePlay.rb +25 -0
  7. data/README +55 -3
  8. data/RELEASE-PLAN +9 -1
  9. data/TODO.rb +175 -15
  10. data/examples/aspect_design_example.rb +13 -1
  11. data/examples/aspect_design_example_spec.rb +20 -2
  12. data/examples/introductions_example.rb +35 -0
  13. data/examples/introductions_example_spec.rb +37 -0
  14. data/examples/method_missing_example.rb +2 -1
  15. data/lib/aquarium/aspects/advice.rb +127 -74
  16. data/lib/aquarium/aspects/aspect.rb +139 -72
  17. data/lib/aquarium/aspects/default_objects_handler.rb +6 -4
  18. data/lib/aquarium/aspects/exclusion_handler.rb +15 -3
  19. data/lib/aquarium/aspects/join_point.rb +60 -55
  20. data/lib/aquarium/aspects/pointcut.rb +153 -124
  21. data/lib/aquarium/aspects/pointcut_composition.rb +1 -1
  22. data/lib/aquarium/dsl/aspect_dsl.rb +13 -5
  23. data/lib/aquarium/dsl/object_dsl.rb +4 -2
  24. data/lib/aquarium/extras/design_by_contract.rb +9 -5
  25. data/lib/aquarium/finders.rb +1 -0
  26. data/lib/aquarium/finders/finder_result.rb +13 -5
  27. data/lib/aquarium/finders/method_finder.rb +75 -70
  28. data/lib/aquarium/finders/pointcut_finder.rb +166 -0
  29. data/lib/aquarium/finders/type_finder.rb +104 -62
  30. data/lib/aquarium/utils/array_utils.rb +1 -1
  31. data/lib/aquarium/utils/invalid_options.rb +2 -0
  32. data/lib/aquarium/utils/name_utils.rb +3 -2
  33. data/lib/aquarium/utils/nil_object.rb +7 -3
  34. data/lib/aquarium/utils/options_utils.rb +38 -27
  35. data/lib/aquarium/utils/set_utils.rb +2 -2
  36. data/lib/aquarium/utils/type_utils.rb +11 -0
  37. data/lib/aquarium/version.rb +1 -1
  38. data/spec/aquarium/aspects/advice_spec.rb +147 -32
  39. data/spec/aquarium/aspects/aspect_invocation_spec.rb +252 -43
  40. data/spec/aquarium/aspects/aspect_spec.rb +148 -88
  41. data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +40 -34
  42. data/spec/aquarium/aspects/aspect_with_subtypes_spec.rb +39 -3
  43. data/spec/aquarium/aspects/join_point_spec.rb +190 -227
  44. data/spec/aquarium/aspects/pointcut_spec.rb +24 -1
  45. data/spec/aquarium/dsl/aspect_dsl_spec.rb +17 -17
  46. data/spec/aquarium/finders/method_finder_spec.rb +8 -2
  47. data/spec/aquarium/finders/pointcut_finder_spec.rb +193 -0
  48. data/spec/aquarium/finders/pointcut_finder_spec_test_classes.rb +90 -0
  49. data/spec/aquarium/finders/type_finder_spec.rb +17 -0
  50. data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +4 -4
  51. data/spec/aquarium/finders/type_finder_with_nested_types.rb +47 -0
  52. data/spec/aquarium/utils/nil_object_spec.rb +21 -0
  53. data/spec/aquarium/utils/type_utils_sample_nested_types.rb +51 -0
  54. data/spec/aquarium/utils/type_utils_spec.rb +18 -1
  55. metadata +13 -3
data/CHANGES CHANGED
@@ -1,3 +1,66 @@
1
+ == Version 0.4.2
2
+
3
+ V0.4.2 adds a few bug fixes and enhancements, greatly improved RDoc output, and internal
4
+ refactorings to improve performance.
5
+
6
+ Bug fixes:
7
+ 15202 Intermittent confusion between classes and objects when invoking advice
8
+ 19262 Just putting join_point argument in advice block causes mysterious method missing error.
9
+ 19321 Advice overhead is too high (ongoing improvements)
10
+
11
+ Enhancements:
12
+ 13403 Support recursion into nested types without requiring "tricky" regular expressions
13
+ 13406 "Sugar" for adding methods and attributes to types
14
+ 18537 Provide a search for all pointcut constants
15
+ 19666 Improve the RDoc output
16
+ 19119 Provide an "after_raising" type of advice that lets you handle the exception.
17
+
18
+ #15202: I never figured out the cause of this problem, but it hasn't been seen since late last
19
+ year, so I suspect it disappeared as a side effect of on-going refactoring and enhancements.
20
+
21
+ #19262: If you just specified "|jp|" for an advice block, you would sometimes get a method missing
22
+ error. I never figured out exactly why, but it was somehow related to passing the usual three
23
+ arguments internally, where the last two would be ignored in this case. Now, the code checks the
24
+ arity and only passes the join point in this case.
25
+
26
+ #19321: I removed some of the wasted object creation and initialization in advice invocation,
27
+ improving the overhead by about 40%. However, it is still at least 10x higher than simple method
28
+ aliasing, so I want to make more improvements. (I did not close this item.)
29
+
30
+ #13403: I added new options :types_and_nested_types and :types_and_nested that are analogous to
31
+ the similar "ancestors" and "descendents" options. The nested option will return the specified
32
+ types and any types they "enclose". There are also corresponding "exclude" options.
33
+
34
+ #13406: I've decided not to do this, as it really isn't the "simplest thing that could possibly
35
+ work." It's easy enough for the user to define a module of new behavior and just use
36
+ "obj.extend(module)". However, when the user needs to do this over a set of types, Aquarium's
37
+ TypeFinder can be helpful, so I added an example to the Examples code and page.
38
+
39
+ #18537: I've provided an example of the design approach where you define pointcuts in a class,
40
+ as a kind of "aspect interface" and write aspects that specify those pointcuts. The problem has
41
+ been that you had to name every single such pointcut explicitly. There was no "finder" option,
42
+ as for types, methods, etc. Now there is a pointcut finder option with a new option
43
+ ":named_pointcuts" for Aspect.new to specify a search for pointcuts in a set of types matching
44
+ a name specification. Either constants or class variables will be matched (or both).
45
+
46
+ #19666: The rdoc for the key classes was cleaned up so it "renders" better. Feedback is welcome.
47
+
48
+ #19119: I finished the previously-incomplete support for allowing advice to change the raised
49
+ exception, in both after and after_raising advice. A common scenario is to wrap the thrown
50
+ exception in another. For example, a low-level, service-specific exception (like a database
51
+ error) in a higher-level, more generic application exception.
52
+
53
+ You still can't rescue the exception completely in after_raising and after advice; the value
54
+ for the exception in
55
+ joinpoint.context.raised_exception
56
+ when the advice returns will be raised by Aquarium. I think that permitting after_raising or
57
+ after advice to "eat" the exception could cause subtle issues with scope and variable binding.
58
+ It would also probably violate the "principle of least surprise"; the advice code that rescues
59
+ the exception would not be as "obvious" to the reader as the familiar idiom of rescue clauses
60
+ that we are accustomed to using. Therefore, if you want to recover completely from an exception,
61
+ use rescue clauses in around advice.
62
+
63
+
1
64
  == Version 0.4.1
2
65
 
3
66
  V0.4.1 adds a few bug fixes, a few more user examples, internal refactoring and some performance
data/ParseTreePlay.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'parse_tree'
3
+
4
+ class Foo
5
+ attr_accessor :name
6
+ def intialize name
7
+ @name = name
8
+ end
9
+ end
10
+
11
+ p ParseTree.new.parse_tree(Foo)
12
+
13
+ class Bar
14
+ attr_accessor :name
15
+ def initialize name
16
+ @name = name
17
+ end
18
+ def attrset x
19
+ p "calling attrset for #{x}"
20
+ super
21
+ end
22
+ end
23
+
24
+ b=Bar.new "name"
25
+ b.name = "name2"
data/README CHANGED
@@ -143,7 +143,7 @@ You can pass in pointcuts defined elsewhere:
143
143
  around :pointcuts => my_pointcut do |jp, obj, *args| ... # Pass in a pointcut
144
144
  around :pointcuts => [my_pointcut, ...] do |jp, obj, *args| ... # Pass in a pointcut array
145
145
 
146
- As a convenience, since a JoinPoint is like a Pointcut with one element, you can pass a JoinPoint object where Pointcut objects are expected:
146
+ As a convenience, since a JoinPoint is a Pointcut with one element, you can pass a JoinPoint object where Pointcut objects are expected:
147
147
 
148
148
  my_join_point1 = JoinPoint.new :type => Foo::Bar, :method => do_this
149
149
  my_join_point2 = JoinPoint.new :type => Foo::Bar, :method => do_that
@@ -161,6 +161,13 @@ You can specify a single type, a type name, a type regular expression, or an arr
161
161
 
162
162
  Everywhere "type" is used, you can substitute "class", "classes", "module", or "modules". Note that they are treated as synonyms; there is currently no enforcement that the values passed with ":class", for example, are actually classes, not modules.
163
163
 
164
+ There are also several prepositional prefixes allowed for any of the synonyms. E.g.,
165
+
166
+ around :for_types = A, ...
167
+ around :on_types = A, ...
168
+ around :in_types = A, ...
169
+ around :within_types = A, ...
170
+
164
171
  Using the plural versions of the synonyms with method specifications sometimes read better:
165
172
 
166
173
  around :calls_to => :all_methods, :on_types => [A, B, ...], ...
@@ -252,6 +259,8 @@ You can specify attributes, which are actually convenience methods for the attri
252
259
  around :attributes = /^foo/, ...
253
260
  around :attributes = [/^foo/, /bar$/], ...
254
261
 
262
+ Again, it's important to remember that actually advising the attribute accesses is not done; it's the public accessor methods that are advised! This may change in a future release.
263
+
255
264
  You can specify a "Pointcut" that encapsulates one or more pre-defined Pointcuts or JoinPoints.
256
265
 
257
266
  around :pointcut = pc, ... # for pre-defined pointcut "pc"
@@ -262,15 +271,57 @@ You can specify a "Pointcut" that encapsulates one or more pre-defined Pointcuts
262
271
 
263
272
  Using the plural versions of the synonyms, with method specifications so they read better:
264
273
 
274
+ around :for_pointcuts => [pc1, pc2, ...], ...
265
275
  around :on_pointcuts => [pc1, pc2, ...], ...
266
276
  around :in_pointcuts => [pc1, pc2, ...], ...
267
277
  around :within_pointcuts => [pc1, pc2, ...], ...
268
278
 
279
+ Since V0.4.2, you can also specify "named" pointcuts, which are searched for just like methods in types (as discussed below).
280
+ For example, if several classes in module "App" define class constant pointcuts named STATE_CHANGE, the following expression
281
+ in an around advice aspect will match all of them:
282
+
283
+ around :named_pointcuts => { :constants_matching => :STATE_CHANGE, :in_types => /App::.*/ } ...
284
+
285
+ For the type specification, which is required, any valid option for the TypeFinder class is allowed.
286
+
287
+ You can also match on class variables, using ":class_variables_matching". To match on either kind of definition, use just
288
+ ":matching". If no :*matching is specified, then any class constant or variable Pointcut found will be matched.
289
+
290
+ Here are the variaus :*matching options and their synonyms:
291
+
292
+ around :named_pointcuts => { :constants_matching => :STATE_CHANGE, ... } ... # class constants only
293
+ around :named_pointcuts => { :constants_named => :STATE_CHANGE, ... } ...
294
+ around :named_pointcuts => { :constants_with_names_matching => :STATE_CHANGE, ... } ...
295
+
296
+ around :named_pointcuts => { :class_variables_matching => :STATE_CHANGE, ... } ... # class variables only
297
+ around :named_pointcuts => { :class_variables_named => :STATE_CHANGE, ... } ...
298
+ around :named_pointcuts => { :class_variables_with_names_matching => :STATE_CHANGE, ... } ...
299
+
300
+ around :named_pointcuts => { :matching => :STATE_CHANGE, ... } ... # class constants and variables
301
+ around :named_pointcuts => { :named => :STATE_CHANGE, ... } ...
302
+ around :named_pointcuts => { :with_names_matching => :STATE_CHANGE, ... } ...
303
+
304
+ The :*matching options take a name, regular expression or array of the same (you can mix names and regular expressions).
305
+
306
+ You can also use the following synonyms for :named_pointcuts:
307
+
308
+ around :named_pointcut => {...}
309
+ around :for_named_pointcut => {...}
310
+ around :on_named_pointcut => {...}
311
+ around :in_named_pointcut => {...}
312
+ around :within_named_pointcut => {...}
313
+ around :for_named_pointcuts => {...}
314
+ around :on_named_pointcuts => {...}
315
+ around :in_named_pointcuts => {...}
316
+ around :within_named_pointcuts => {...}
317
+
269
318
  You can specifically exclude particular pointcuts, join points, types, objects, methods, or attributes. This is useful when you specify a list or regular expression of "items" to match and you want to exclude some of the items.
270
319
  Note that there is an open bug (#15202) that appears to affect advising types, unadvising the types, then advising objects of the same types. (This is not likely to happen a lot in real applications, but it shows up when running Aquarium's specs.)
271
320
 
272
321
  around ..., :exclude_pointcut = pc, ...
273
322
  around ..., :exclude_pointcuts = [pc, ...]
323
+ around ..., :exclude_named_pointcut = {...}
324
+ around ..., :exclude_named_pointcuts = {...}
274
325
  around ..., :exclude_join_point = jp, ...
275
326
  around ..., :exclude_join_points = [jp, ...]
276
327
  around ..., :exclude_type = t, ...
@@ -286,7 +337,7 @@ Note that there is an open bug (#15202) that appears to affect advising types, u
286
337
  around ..., :exclude_attribute = a, ...
287
338
  around ..., :exclude_attributes = [a, ...]
288
339
 
289
- All the same synonyms for :types, :objects, and :methods apply here as well (after the "exclude_" prefix).
340
+ All the same synonyms for :pointcuts, :named_pointcuts, :types, :objects, and :methods apply here as well (after the "exclude_" prefix).
290
341
 
291
342
  You can advice methods before execution:
292
343
 
@@ -345,7 +396,7 @@ Rather than passing a block as the advice, you can pass a previously-created Pro
345
396
  around :type => [...], :methods => :all, :call => advice # synonym for advice.
346
397
  around :type => [...], :methods => :all, :invoke => advice # synonym for advice.
347
398
 
348
- Finally, when running in JRuby, you can advise Java types! See the examples in the separate RSpec suite in the "jruby" directory.
399
+ Finally, when running in JRuby, you can advise Java types! See the examples in the separate RSpec suite in the "jruby" directory and the discussion above concerning known limitations.
349
400
 
350
401
  === Packages
351
402
 
@@ -412,4 +463,5 @@ See http://aquarium.rubyforge.org for further documentation.
412
463
  My colleagues in the AOSD community, in particular those who developed AspectJ, have been a big inspiration.
413
464
  The RSpec team, in particular David Chelimsky, have really inspired my thinking about what's possible in Ruby, especially in the realm of DSLs. I also cribbed parts of the RSpec Rake process ;)
414
465
  My colleagues at Object Mentor are an endless source of insight and inspiration.
466
+ Finally, a number of users have contributed valuable feedback. In particular, thanks to Brendan L., Matthew F., and Mark V.
415
467
 
data/RELEASE-PLAN CHANGED
@@ -46,11 +46,19 @@ advise Java types!
46
46
 
47
47
  === Version 0.5
48
48
 
49
- My goals for this release are performance improvements (#19321) and investigating some ideas
49
+ My goals for this release include more performance improvements (#19321) and investigating some ideas
50
50
  for a real DSL, meaning declarative statements in blocks, rather than method arguments. It will be
51
51
  redundant somewhat with the existing "method-argument form", as it exists today, but it will set
52
52
  the stage for much more complex aspect definitions than would be convenient with the current form.
53
53
 
54
+ Another big change is to make pointcut evaluation and advice application happen continuous at runtime,
55
+ rather than only when the aspect is defined. This would eliminate subtle problems related to the order
56
+ of loading and also makes the declarative nature of aspects more fully realized. For example, if I
57
+ declare an aspect for all types in a module namespace, it should still apply to nested types defined
58
+ or loaded after the aspect is defined. This enhancement will also make it easier to define reusable
59
+ and "abstract" aspects, where they aren't applied immediately, but only when extended by "concrete"
60
+ aspects, for example.
61
+
54
62
  I'll maintain the current form for backwards compatibility and also because it is convenient for
55
63
  simpler aspects.
56
64
 
data/TODO.rb CHANGED
@@ -1,26 +1,186 @@
1
1
 
2
2
  # New DSL ideas.
3
- Aspect.new do
3
+ Aspect.new {
4
+ after
5
+ pointcut {
6
+ within_context_of named_pointcuts {
7
+ matching /^STATE/
8
+ within_types_and_descendents /Foo|Bar/
9
+ } and not_within_context_of pointcuts {
10
+ calls_from /callback$/
11
+ in_types_and_descendents_of /Active/
12
+ } and pointcut {
13
+ pointcut {
14
+ calls_to /index/
15
+ in_types_and_ancestors_of [String, Fixnum]
16
+ } or pointcut {
17
+ calls_to /outdex/
18
+ in_types_and_ancestors_of /^Foo/
19
+ }
20
+ }
21
+ }
22
+ invoke_advice do |join_point, object, *args|
23
+ p join_point.inspect
24
+ end
25
+ }
4
26
 
5
- pointcut do
6
- named_pointcuts :matching => /^STATE/, :within_types_and_descendents => /Foo|Bar/
7
- or {
27
+ Aspect.new {
28
+ after
29
+ pointcut {
30
+ within_context_of {
31
+ pointcuts_named /^STATE/
32
+ within_types_and_descendents /Foo|Bar/
33
+ } and not within_context_of {
34
+ calls_to /_handle$/
35
+ in_types_and_descendents_of /Active/
36
+ } and pointcut {
37
+ pointcut {
38
+ calls_to /index/
39
+ in_types_and_ancestors_of [String, Fixnum]
40
+ } or pointcut {
41
+ calls_to /outdex/
42
+ in_types_and_ancestors_of /^Foo/
43
+ }
44
+ }
45
+ }
46
+ invoke_advice do |join_point, object, *args|
47
+ p join_point.inspect
48
+ end
49
+ }
50
+
51
+ Aspect.new {
52
+ after {
53
+ calls_to /index/
54
+ in_types_and_ancestors_of [String, Fixnum]
55
+ } or after {
56
+ calls_to /outdex/
57
+ in_types_and_ancestors_of /^Foo/
58
+ } within_context_of {
59
+ pointcuts_named /^STATE/
60
+ within_types_and_descendents /Foo|Bar/
61
+ } and not within_context_of {
62
+ calls_to /_handle$/
63
+ in_types_and_descendents_of /Active/
64
+ } and pointcut {
65
+ pointcut {
66
+ calls_to /index/
67
+ in_types_and_ancestors_of [String, Fixnum]
68
+ } or pointcut {
69
+ calls_to /outdex/
70
+ in_types_and_ancestors_of /^Foo/
71
+ }
72
+ }
73
+ }
74
+ invoke_advice do |join_point, object, *args|
75
+ p join_point.inspect
76
+ end
77
+ }
78
+
79
+ Aspect.new {
80
+ after
81
+ pointcut {
82
+ within named_pointcuts {
83
+ matching /^STATE/
84
+ within_types_and_descendents /Foo|Bar/
85
+ }
86
+ not_within pointcuts {
87
+ calls_from /callback$/
88
+ in_types_and_descendents_of /Active/
89
+ }
90
+ pointcut {
8
91
  calls_to /index/
9
92
  in_types_and_ancestors_of [String, Fixnum]
10
- } and {
93
+ }
94
+ pointcut {
11
95
  calls_to /outdex/
12
96
  in_types_and_ancestors_of /^Foo/
13
- }
14
- end
15
- within_pointcuts do
16
- calls_to main
17
- in_type TopLevel
97
+ }
98
+ }
99
+ invoke_advice do |join_point, object, *args|
100
+ p join_point.inspect
18
101
  end
19
- but_not_within pointcuts do
20
- calls_from /callback$/
21
- in_types_and_descendents_of /Active/
102
+ }
103
+
104
+ Aspect.new {
105
+ after
106
+ pointcut {
107
+ within named_pointcuts {
108
+ matching /^STATE/
109
+ within_types_and_descendents /Foo|Bar/
110
+ }.and_not_within pointcuts {
111
+ calls_from /callback$/
112
+ in_types_and_descendents_of /Active/
113
+ } {
114
+ pointcut {
115
+ calls_to /index/
116
+ in_types_and_ancestors_of [String, Fixnum]
117
+ }
118
+ pointcut {
119
+ calls_to /outdex/
120
+ in_types_and_ancestors_of /^Foo/
121
+ }
122
+ }
123
+ }
124
+ invoke_advice do |join_point, object, *args|
125
+ p join_point.inspect
22
126
  end
23
- after do |join_point, object, *args|
127
+ }
128
+
129
+ Aspect.new {
130
+ after
131
+ pointcut {
132
+ within {
133
+ named_pointcuts {
134
+ matching /^STATE/
135
+ within_types_and_descendents /Foo|Bar/
136
+ }
137
+ exclude_pointcuts {
138
+ calls_from /callback$/
139
+ in_types_and_descendents_of /Active/
140
+ }
141
+ }
142
+ pointcut {
143
+ calls_to /index/
144
+ in_types_and_ancestors_of [String, Fixnum]
145
+ }
146
+ pointcut {
147
+ calls_to /outdex/
148
+ in_types_and_ancestors_of /^Foo/
149
+ }
150
+ }
151
+ invoke_advice do |join_point, object, *args|
24
152
  p join_point.inspect
25
153
  end
26
- end
154
+ }
155
+
156
+
157
+
158
+ after :named_pointcuts => { :matching => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
159
+ after :named_pointcuts => { :with_names_matching => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
160
+ after :named_pointcuts => { :named => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
161
+
162
+ after :named_pointcuts => { :constants_matching => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
163
+ after :named_pointcuts => { :constants_with_names_matching => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
164
+ after :named_pointcuts => { :constants_named => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
165
+
166
+ after :named_pointcuts => { :class_variables_matching => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
167
+ after :named_pointcuts => { :class_variables_with_names_matching => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
168
+ after :named_pointcuts => { :class_variables_named => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
169
+
170
+ after :constant_pointcuts => { :matching => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
171
+ after :class_variable_pointcuts => { :matching => /^state/, :within_types_and_descendents => /Foo|Bar/ } do ... end
172
+
173
+ regex= /(constants_|instance_variables_|class_variables_)?(with_)?(named_matching|named|matching)/
174
+ after :pointcuts => { :named => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
175
+ after :pointcuts => { :constants_named => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
176
+ after :pointcuts => { :class_variables_named => /^state/, :within_types_and_descendents => /Foo|Bar/ } do ... end
177
+ after :pointcuts => { :instance_variables_named => /^state/, :within_types_and_descendents => /Foo|Bar/ } do ... end
178
+
179
+ after :named_pointcuts => { :matching => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
180
+ after :named_pointcuts => { :matching => 'STATE_CHANGE', :within_types_and_descendents => /Foo|Bar/ } do ... end
181
+ after :constant_pointcuts => { :matching => /^STATE/, :within_types_and_descendents => /Foo|Bar/ } do ... end
182
+ after :class_variables_pointcuts => { :matching => /^state/, :within_types_and_descendents => /Foo|Bar/ } do ... end
183
+ after :instance_variables_pointcuts => { :matching => /^state/, :within_types_and_descendents => /Foo|Bar/ } do ... end
184
+
185
+ after :pointcuts_matching => { :constants_named => /^STATE/, :within_types_and_ancestors => /Foo|Bar/ }
186
+ after :pointcuts_matching => { :class_variables_named => /^STATE/, :within_types_and_ancestors => /Foo|Bar/ }
@@ -34,8 +34,20 @@ end
34
34
  include Aquarium::Aspects
35
35
 
36
36
  # Observe state changes in the class, using the class-defined pointcut.
37
+ # Two ways of referencing the pointcut are shown. The first assumes you know the particular
38
+ # pointcuts you care about. The second is more general; it uses the recently-introduced
39
+ # :named_pointcut feature to search for all pointcuts matching a name in a set of types.
37
40
 
38
- observer = Aspect.new :after, :pointcut => Aquarium::ClassWithStateAndBehavior::STATE_CHANGE do |jp, obj, *args|
41
+ observer1 = Aspect.new :after,
42
+ :pointcut => Aquarium::ClassWithStateAndBehavior::STATE_CHANGE do |jp, obj, *args|
43
+ p "State has changed. "
44
+ state = obj.state
45
+ p " New state is #{state.nil? ? 'nil' : state.inspect}"
46
+ p " Equivalent to *args: #{args.inspect}"
47
+ end
48
+
49
+ observer2 = Aspect.new :after, :named_pointcuts => {:matching => /CHANGE/,
50
+ :within_types => Aquarium::ClassWithStateAndBehavior} do |jp, obj, *args|
39
51
  p "State has changed. "
40
52
  state = obj.state
41
53
  p " New state is #{state.nil? ? 'nil' : state.inspect}"