aquarium 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Aquarium-IDEA.ipr +252 -0
- data/Aquarium-IDEA.iws +493 -0
- data/Aquarium.ipr +1 -1
- data/Aquarium.iws +133 -138
- data/CHANGES +63 -0
- data/ParseTreePlay.rb +25 -0
- data/README +55 -3
- data/RELEASE-PLAN +9 -1
- data/TODO.rb +175 -15
- data/examples/aspect_design_example.rb +13 -1
- data/examples/aspect_design_example_spec.rb +20 -2
- data/examples/introductions_example.rb +35 -0
- data/examples/introductions_example_spec.rb +37 -0
- data/examples/method_missing_example.rb +2 -1
- data/lib/aquarium/aspects/advice.rb +127 -74
- data/lib/aquarium/aspects/aspect.rb +139 -72
- data/lib/aquarium/aspects/default_objects_handler.rb +6 -4
- data/lib/aquarium/aspects/exclusion_handler.rb +15 -3
- data/lib/aquarium/aspects/join_point.rb +60 -55
- data/lib/aquarium/aspects/pointcut.rb +153 -124
- data/lib/aquarium/aspects/pointcut_composition.rb +1 -1
- data/lib/aquarium/dsl/aspect_dsl.rb +13 -5
- data/lib/aquarium/dsl/object_dsl.rb +4 -2
- data/lib/aquarium/extras/design_by_contract.rb +9 -5
- data/lib/aquarium/finders.rb +1 -0
- data/lib/aquarium/finders/finder_result.rb +13 -5
- data/lib/aquarium/finders/method_finder.rb +75 -70
- data/lib/aquarium/finders/pointcut_finder.rb +166 -0
- data/lib/aquarium/finders/type_finder.rb +104 -62
- data/lib/aquarium/utils/array_utils.rb +1 -1
- data/lib/aquarium/utils/invalid_options.rb +2 -0
- data/lib/aquarium/utils/name_utils.rb +3 -2
- data/lib/aquarium/utils/nil_object.rb +7 -3
- data/lib/aquarium/utils/options_utils.rb +38 -27
- data/lib/aquarium/utils/set_utils.rb +2 -2
- data/lib/aquarium/utils/type_utils.rb +11 -0
- data/lib/aquarium/version.rb +1 -1
- data/spec/aquarium/aspects/advice_spec.rb +147 -32
- data/spec/aquarium/aspects/aspect_invocation_spec.rb +252 -43
- data/spec/aquarium/aspects/aspect_spec.rb +148 -88
- data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +40 -34
- data/spec/aquarium/aspects/aspect_with_subtypes_spec.rb +39 -3
- data/spec/aquarium/aspects/join_point_spec.rb +190 -227
- data/spec/aquarium/aspects/pointcut_spec.rb +24 -1
- data/spec/aquarium/dsl/aspect_dsl_spec.rb +17 -17
- data/spec/aquarium/finders/method_finder_spec.rb +8 -2
- data/spec/aquarium/finders/pointcut_finder_spec.rb +193 -0
- data/spec/aquarium/finders/pointcut_finder_spec_test_classes.rb +90 -0
- data/spec/aquarium/finders/type_finder_spec.rb +17 -0
- data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +4 -4
- data/spec/aquarium/finders/type_finder_with_nested_types.rb +47 -0
- data/spec/aquarium/utils/nil_object_spec.rb +21 -0
- data/spec/aquarium/utils/type_utils_sample_nested_types.rb +51 -0
- data/spec/aquarium/utils/type_utils_spec.rb +18 -1
- 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
|
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
|
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
|
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
|
-
|
6
|
-
|
7
|
-
|
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
|
-
}
|
93
|
+
}
|
94
|
+
pointcut {
|
11
95
|
calls_to /outdex/
|
12
96
|
in_types_and_ancestors_of /^Foo/
|
13
|
-
}
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
in_type TopLevel
|
97
|
+
}
|
98
|
+
}
|
99
|
+
invoke_advice do |join_point, object, *args|
|
100
|
+
p join_point.inspect
|
18
101
|
end
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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}"
|