-
Notifications
You must be signed in to change notification settings - Fork 24
/
aspects.page
90 lines (66 loc) · 5.43 KB
/
aspects.page
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
---
title: Aspects
order_info: 1
in_menu: true
---
## Aspects
<p><span class="keyword">Aspects</span> are modularization constructs for encapsulating <span class="keyword">concerns</span> that cut across the normal system object and module boundaries. Two classic examples including the mapping of a global persistence or security strategy to the objects in the system that must use or be controlled by them.</p>
Think of your persistence strategy as one, well-modularized domain and your business logic as another. Rather than <span class="keyword">tangle</span> the business objects with the code to support persistence and <span class="keyword">scatter</span> that logic all over the application (breaking [DRY]), <span class="keyword">aspects</span> modularize the <i>integration</i> logic between the persistence and domain-logic subsystems.
By modularization, I mean that you can
express the intersection <span class="keyword">join points</span> (defined shortly) succinctly and in one
place, as well as defining the logic that expresses what should happen at those intersection points.
### Terminology
Several terms are used in the AOP community.
<table>
<tr><td><span class="keyword">Join Point</span></td><td>A point of execution in a program where "advice" might be invoked.</td></tr>
<tr><td><span class="keyword">Pointcut</span> (one word...)</td><td>A set of join points of interest, like a query over all join points in the system.</td></tr>
<tr><td><span class="keyword">Advice</span></td><td>The behavior invoked at a join point.</td></tr>
</table>
Potential <span class="keyword">join points</span> include method calls, variable reads and writes, conditionals, <i>etc.</i>. <span class="keyword">Aquarium</span> currently supports only method calls, which is sufficient for most needs.
There are several kinds of <span class="keyword">advice</span>:
<table>
<tr><td><span class="keyword">Before</span></td><td>Advice invoked before the actual join point is invoked.</td></tr>
<tr><td><span class="keyword">After Returning</span></td><td>Advice invoked after the join point executes successfully.</td></tr>
<tr><td><span class="keyword">After Raising</span></td><td>Advice invoked only if the join point raises an exception.</td></tr>
<tr><td><span class="keyword">After</span></td><td>Advice invoked after the join point executes successfully or raises an exception.</td></tr>
<tr><td><span class="keyword">Around</span></td><td>Advice invoked instead of the join point. The around advice must choose whether or not to invoke the join point by calling a special "proceed" method. Otherwise, the join point is NOT executed.</td></tr>
</table>
Only <span class="keyword">around advice</span> can prevent execution of the <span class="keyword">join point</span>, except for the special case where before <span class="keyword">advice</span> raises an exception.
There are two disadvantages of the term <span class="keyword">advice</span>. One disadvantage is that it implies that we're just "tweaking" the system in some sense, which is a limiting perspective. The second disadvantage is that both of the words "advise" and "advice" are used and it's easy to use one when you meant the other!
### Usage in Aquarium
Aspects are implemented as classes and you can either create instances of them or use the convenient methods that are added to <code>Object</code>, by default. For example, the following two sections of code are equivalent:
{coderay:: ruby}aspect1 = Aspect.new :before, :calls_to => :all_methods,
:on_types => /*Service$/ do |join_point, object, *args|
log("calling #{join_point.inspect}")
end
include Aquarium::DSL
aspect2 = before :calls_to => :all_methods,
:on_types => /*Service$/ do |join_point, object, *args|
log("calling #{join_point.inspect}")
end
{coderay}
<p></p>
Both examples add <span class="keyword">before advice</span> (which logs the invocations of the methods) to all the public methods in all classes and modules whose name ends with <code>Service</code>. Note that while the second form is using what is effectively an instance method on the current <code>self</code>, it has no effect on <code>self</code>.
Here is a similar example that advises just a single instance of the <code>MyService</code> class.
{coderay:: ruby}service = MyService.new(...)
aspect = before :calls_to = :all_methods,
:on_object => service do |join_point, object, *args|
log("calling #{join_point.inspect}")
end
{coderay}
<p></p>
Here is effectively the same example that uses a previously-defined <span class="keyword">pointcut</span>.
{coderay:: ruby}service = MyService.new(...)
pointcut = Pointcut.new :calls_to => :all_methods, :on_object => service
aspect = before :pointcut => pointcut do |join_point, object, *args|
log("calling #{join_point.inspect}")
end
{coderay}
<p></p>
Note that many synonyms are available for the keywords in the API. In these examples, we have used <code>:calls_to</code> as a synonym for <code>:methods</code> and <code>:on_types</code> for <code>:types</code> and <code>:on_objects</code> for <code>:objects</code>. The synonyms are designed to promote more intuitive readability.
Finally, <span class="keyword">Aquarium</span> supports removing <span class="keyword">advice</span>:
{coderay:: ruby}aspect.unadvise{coderay}
<p></p>
Other examples can be found in the [RDoc pages].
[DRY]:http://en.wikipedia.org/wiki/Don't_repeat_yourself
[RDoc pages]:http://aquarium.rubyforge.org/rdoc/