dcl.js
2.48 KB
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
91
92
93
94
95
96
97
98
99
(function(factory){
if(typeof define != "undefined"){
define(["./mini"], factory);
}else if(typeof module != "undefined"){
module.exports = factory(require("./mini"));
}else{
dcl = factory(dcl);
}
})(function(dcl){
"use strict";
function nop(){}
var Advice = dcl(dcl.Super, {
//declaredClass: "dcl.Advice",
constructor: function(){
this.before = this.around.before;
this.after = this.around.after;
this.around = this.around.around;
}
});
function advise(advice){ return dcl._makeSuper(advice, Advice); }
function makeAOPStub(before, after, around){
var beforeChain = before || nop,
afterChain = after || nop,
aroundChain = around || nop,
stub = function(){
var r, thrown;
// running the before chain
beforeChain.apply(this, arguments);
// running the around chain
try{
r = aroundChain.apply(this, arguments);
}catch(e){
r = e;
thrown = true;
}
// running the after chain
afterChain.call(this, arguments, r);
if(thrown){
throw r;
}
return r;
};
stub.advices = {before: before, after: after, around: around};
return stub;
}
function chain(id){
return function(ctor, name){
var meta = ctor._meta, rule;
if(meta){
rule = +meta.weaver[name] || 0;
if(rule && rule != id){
dcl._error("set chaining", name, ctor, id, rule);
}
meta.weaver[name] = id;
}
};
}
dcl.mix(dcl, {
// public API
Advice: Advice,
advise: advise,
// expose helper methods
before: function(f){ return dcl.advise({before: f}); },
after: function(f){ return dcl.advise({after: f}); },
around: dcl.superCall,
// chains
chainBefore: chain(1),
chainAfter: chain(2),
isInstanceOf: function(o, ctor){
if(o instanceof ctor){
return true;
}
var t = o.constructor._meta, i;
if(t){
for(t = t.bases, i = t.length - 1; i >= 0; --i){
if(t[i] === ctor){
return true;
}
}
}
return false;
},
// protected API starts with _ (don't use it!)
_stub: /*generic stub*/ function(id, bases, name, chains){
var f = chains[name] = dcl._extractChain(bases, name, "around"),
b = dcl._extractChain(bases, name, "before").reverse(),
a = dcl._extractChain(bases, name, "after");
f = id ? dcl._stubChainSuper(f, id == 1 ? function(f){ return dcl._stubChain(f.reverse()); } : dcl._stubChain, name) : dcl._stubSuper(f, name);
return !b.length && !a.length ? f || function(){} : makeAOPStub(dcl._stubChain(b), dcl._stubChain(a), f);
}
});
return dcl;
});