cujojs / meld

AOP for JS with before, around, on, afterReturning, afterThrowing, after advice, and pointcuts
Other
644 stars 65 forks source link

is there a way to have a before() cancel the method it was applied upon? #14

Closed 0xgeert closed 11 years ago

0xgeert commented 11 years ago

I'd like to have before-aspect that does some validation and just entirely skips the execution of the method it was applied to (as well as the entire optional before, after-chain succeeding it) .

Is this possible?

unscriptable commented 11 years ago

Hey @gbrits,

It sounds like you're looking for "around" advice. You have a reference to the joinpoint so you can decide to run the original function or not. Around advice is a bit more work to use, but is fairly straightforward.

https://github.com/cujojs/meld/blob/master/docs/api.md#meldaround

-- John

0xgeert commented 11 years ago

Great, I'll have a look thanks.

2013/3/2 John Hann notifications@github.com

Hey @gbrits https://github.com/gbrits,

It sounds like you're looking for "around" advice. You have a reference to the joinpoint so you can decide to run the original function or not. Around advice is a bit more work to use, but is fairly straightforward.

https://github.com/cujojs/meld/blob/master/docs/api.md#meldaround

-- John

— Reply to this email directly or view it on GitHubhttps://github.com/cujojs/meld/issues/14#issuecomment-14329157 .

unscriptable commented 11 years ago

Hey Brian, it's not clear to me how to ensure the after advice will be skipped. Does the advice have to throw? -- John

briancavalier commented 11 years ago

Hey @gbrits, yeah, the only advice type that is allowed to cancel a method invocation safely is around. It's the most powerful. However, it cannot prevent other advices, such as after from executing. This is pretty standard behavior for AOP implementations.

While you could use before advice and throw an exception, the exception will propagate, which is probably not what you want.

One approach that may work for skipping certain advices may be to use a shared flag. An early advice type, such as before or around could set the flag when necessary, and later advices could simply look at the flag and do nothing if it is set. It's not a great solution--this is definitely a tricky use case!

0xgeert commented 11 years ago

Just so I understand fully: not calling joinpoint.proceed() in my around() would skip the original method invocation, but it wouldn' t stop other advices from running, correct?

briancavalier commented 11 years ago

Yep, that's correct (although there is a bit more to it, see below). Here is a simple example that only logs "after", even tho the around advice never calls joinpoint.proceed:

var meld = require('meld');
var o = {
    f: function() { console.log('o.f'); }
}

meld.add(o, 'f', {
    around: function() {},
    after: function() { console.log('after'); }
});

o.f();

You can think of around advice like "layers" or wrappers around the original method. As you apply more around advices, it adds more layers. So technically speaking, joinpoint.proceed proceeds to the next "layer" of around advice, or, if there are no layers remaining, to the original method. For example:

var o = {
    f: function() {
        console.log('o.f');
    }
}

meld.around(o, 'f', function(joinpoint) {
    console.log('inner around');
});

meld.around(o, 'f', function(joinpoint) {
    console.log('outer around');
    return joinpoint.proceed();
});

This example will log "outer around" then "inner around", but will never log "o.f". The outer around calls jointpoint.proceed(), which proceeds to the next layer, in this case, the inner around, which simply logs without calling joinpoint.proceed, so the original method is never called.

Hope that helps!

0xgeert commented 11 years ago

Thanks that clarifies things! Cheers

2013/3/3 Brian Cavalier notifications@github.com

Yep, that's correct (although there is a bit more to it, see below). Here is a simple example that only logs "after", even tho the around advice never calls joinpoint.proceed:

var meld = require('meld');var o = { f: function() { console.log('o.f'); }} meld.add(o, 'f', { around: function() {}, after: function() { console.log('after'); }}); o.f();

You can think of around advice like "layers" or wrappers around the original method. As you apply more around advices, it adds more layers. So technically speaking, joinpoint.proceed proceeds to the next "layer" of around advice, or, if there are no layers remaining, to the original method. For example:

var o = { f: function() { console.log('o.f'); }} meld.around(o, 'f', function(joinpoint) { console.log('inner around');}); meld.around(o, 'f', function(joinpoint) { console.log('outer around'); return joinpoint.proceed();});

This example will log "outer around" then "inner around", but will never log "o.f". The outer around calls jointpoint.proceed(), which proceeds to the next layer, in this case, the inner around, which simply logs without calling joinpoint.proceed, so the original method is never called.

Hope that helps!

— Reply to this email directly or view it on GitHubhttps://github.com/cujojs/meld/issues/14#issuecomment-14352339 .

briancavalier commented 11 years ago

Great, glad to help. Is there something else we need to do with this issue? If not, please close it. If there is, though, please let us know and we can discuss further. Thanks!