Aspects
See also the Aquarium Rdocs.
Aspects are modularization constructs for encapsulating concerns 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.
Think of your persistence strategy as one, well-modularized domain and your business logic as another. Rather than tangle the business objects with the code to support persistence and scatter that logic all over the application (breaking DRY), aspects modularize the integration logic between the persistence and domain-logic subsystems.
By modularization, I mean that you can express the intersection join points (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.
Join Point | A point of execution in a program where "advice" might be invoked. |
Pointcut (one word...) | A set of join points of interest, like a query over all join points in the system. |
Advice | The behavior invoked at a join point. |
Potential join points include method calls, variable reads and writes, conditionals, etc.. Aquarium currently supports only method calls, which is sufficient for most needs.
There are several kinds of advice:
Before | Advice invoked before the actual join point is invoked. |
After Returning | Advice invoked after the join point executes successfully. |
After Raising | Advice invoked only if the join point raises an exception. |
After | Advice invoked after the join point executes successfully or raises an exception. |
Around | 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. |
Only around advice can prevent execution of the join point, except for the special case where before advice raises an exception.
There are two disadvantages of the term advice. 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 Object
, by default. For example, the following two sections of code are equivalent:
1aspect1 = Aspect.new :before, :calls_to => :all_methods, 2 :on_types => /*Service$/ do |join_point, object, *args| 3 log("calling #{join_point.inspect}") 4end 5 6include Aquarium::DSL 7aspect2 = before :calls_to => :all_methods, 8 :on_types => /*Service$/ do |join_point, object, *args| 9 log("calling #{join_point.inspect}") 10end
Both examples add before advice (which logs the invocations of the methods) to all the public methods in all classes and modules whose name ends with Service
. Note that while the second form is using what is effectively an instance method on the current self
, it has no effect on self
.
Here is a similar example that advises just a single instance of the MyService
class.
Here is effectively the same example that uses a previously-defined pointcut.
Note that many synonyms are available for the keywords in the API. In these examples, we have used :calls_to
as a synonym for :methods
and :on_types
for :types
and :on_objects
for :objects
. The synonyms are designed to promote more intuitive readability.
Finally, Aquarium supports removing advice:
1aspect.unadvise