Advice Joining added to Cut-based AOP

After some recent conversion on AOP on the list, I reviewing the
Cut-based AOP proposal and found one area that needed some address,
namely redirectly methods to their advice in a dyanamic and flexible
fashion. So I modified part of the proposal and addeda new feature,
which I think really strengthens the whole concept.

Advice Joining

Since advising multiple methods with a single advice is the common case
of AOP, a convenient means of redirecting target methods to advice is
essential. While it is trivial to define a method like the following
Cut#redirect_advice:

class Cut
def redirect_advice( h )
c = h.collect { |k,v|
“def #{k}(*a,&b) #{v}(this,*a, &b); end”
}
module_eval c.join("\n")
end
end

cut A < C
redirect_advice :f => :bracket, :g => :bracket
def bracket( target )
‘{’ + target.super + ‘}’
end
end

It is not sufficient for dealing with Ruby’s dynamicism. The above only
handles methods defined in the target class at the moment the cut is
defined. Complete AOP support requires the advice always stay in sync
even under dynamic alteration of the targeted class. Ruby already
provides means for this via the Module#method_added hook, but robust
use of this techinque is inconvenient at best. So a proper
advice-oriented call is warrented, namely Cut#join.

cut A < C
join :f => :bracket, :g => :bracket
def bracket( target )
‘{’ + target.super + ‘}’
end
end

The Cut#join method would also accept wildcards.

cut A < C
join ‘*’ => :bracket
def bracket( target )
‘{’ + target.super + ‘}’
end
end

The #join method provides the essential dynamic flexibility required of
a robust Ruby AOP solution.

I would argue that is a bit more problematic than just applying the
pointcuts onto the new methods.

If AOP were only about interception, that would be an all ok approach to
just apply the cuts as soon as a new method appears.

however AOP != interception
your aspect might rely on some mixed in state, and that state might be
the result of a chain of actions done onto the subject.

if a new method is added to the subject, and you re-apply the cuts, you
would also have to put the mixed in state for the aspect in a valid
mode.

a very simple example:

lets pretend we have an aspect that cuts every getter in a class in
order to apply “lazy load”.
in order to do this, we need to mixin a state holder for the subject, so
that we know in which state each property is, eg “not loaded” and
“loaded”

when the subject is fresh, all the properties (attributes in ruby) might
be marked as “not loaded”

and when you access a few of them they change their status to “loaded”

now lets assume that we add a new method(a getter method)

First of all, do we even want the lazy load cut to be applied to this
method?
the cut might have said “Cut all getters in the class” because when the
cut was defined there were only a few properties in the class, and all
of them were mapped to a DB.
its highly unlikely that you would add a getter that is also mapped to
the db at runtime.
So the cut might be valid at one point but invalid once you add new
methods.

but if we pretend that that was the case.
what state should that property have in the LazyLoad mixin?
loaded? , not loaded?

you might say “not loaded”
but what if the getters member var had a value assigned to it to when
the method was added, in that case maybe the getter should be treated as
“loaded”?

and when and where should that state be applied?
should the state mixin have some sort of mechanism to deal with new
methods beeing added?

So the problem of added methods is not just about re-evaluating the
cuts, you must also deal with state of the subject.

//Roger

Roger J. wrote:

if a new method is added to the subject, and you re-apply the cuts, you
would also have to put the mixed in state for the aspect in a valid
mode.

AOP is not just interception, I agree with you. And the whole of the
propsal certainly atests to that. The idea of cuts is not to supply a
“total AOP framework” but just to provide a common and standardized
foundation for doing AOP in Ruby. Going beyond that starts to push
scope of what this proposal is about. One might argue for a more
complete function, say:

cut A < C
pointcut( :someadvice ){ |jp| loaded?( jp.method ) …
end

Sure, that can be done. You can do it in pure Ruby. The proposal
doesn’t need to go into those details.

I also point out that Cut-based AOP is all about the 80/20 rule. There
are things AOP it purposefully dosen’t address. For instance, local
varaible assignments are not available joinpoints. But capabilities
like that fall into that 20% range. Method interception on the other
hand is 80% range. What happens when we shoot for the full 100% is we
end up with a system that is too complex for general use and too
inefficient in execution.

T.