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