There are often cross cutting concerns in your app, such as logging, security checkpoints and or maybe business validations.
This is a very simple approach of implementing method interceptors in pure javascript.
The crux: This decorates all the methods for the given object with before and after interceptions.
intercept: function(o) {
for (let p in o) {
if (typeof o[p] === 'function') {
let c = o[p];
o[p] = function() {
MIFactory.beforeExecution(o, p);
let r = c.call(o, arguments);
MIFactory.afterExecution(o, p);
return r;
}
}
}
return o;
}
method interceptor factory:
MIFactory = {
interceptors: [],
register: function(interceptor) {
MIFactory.interceptors.push(interceptor);
},
unregister: function(interceptor) {
var index = MIFactory.interceptors.indexOf(interceptor);
if (index > -1) {
MIFactory.interceptors.splice(index, 1);
}
},
beforeExecution: function(o, p) {
if (!o.__interceptors__) o.__interceptors__ = [];
if (!o.__interceptors__[p]) o.__interceptors__[p] = [];
for (var i = 0; i < MIFactory.interceptors.length; i++) {
try {
o.__interceptors__[p].push(new MIFactory.interceptors[i]());
} catch (e) {}
}
for (var i = 0; i < o.__interceptors__[p].length; i++) {
try {
o.__interceptors__[p][i].beforeExecution(o, p);
} catch (e) {}
}
},
afterExecution: function(o, p) {
for (var i = 0; i < o.__interceptors__[p].length; i++) {
try {
o.__interceptors__[p][i].afterExecution(o, p);
} catch (e) {}
}
},
intercept: function(o) {
for (let p in o) {
if (typeof o[p] === 'function') {
let c = o[p];
o[p] = function() {
MIFactory.beforeExecution(o, p);
let r = c.call(o, arguments);
MIFactory.afterExecution(o, p);
return r;
}
}
}
return o;
}
}
This is a simple method interceptor: This just logs before and after execution of the context method.
MyInterceptor = function() {
var time;
this.beforeExecution = function(o, p) {
time = (new Date()).getTime();
Logger.log('before..' + p);
}
this.afterExecution = function(o, p) {
time = (new Date()).getTime() - time;
Logger.log('after..' + p + ' ... time taken: ' + time + ' ticks');
}
}
You may have any number of interceptors, they all need to implement the contract
{
function beforeExecution;
function afterExecution;
}
This is a simple class that i need to add interceptions:
MyTestClass = function() {
this.doMethod1 = function() {
console.log('executing method 1');
for (var i = 0; i < 230000; i++) {
var x = new Date();
var y = x.toString();
}
return 100;
}
this.doMethod2 = function() {
console.log('executing method 2');
for (var i = 0; i < 100000; i++) {
var x = new Date();
var y = x.toString();
}
}
}
Without modifying the class, I will prepare it for interception, by decorating it via MIFactory.
let m = MIFactory.intercept(new MyTestClass());
console.log('_________________________');
console.log('Result ' + m.doMethod1());
console.log('_________________________');
m.doMethod2();
The output is:
_________________________
executing method 1
Result 100
_________________________
executing method 2
I will register my interceptor. [MyInterceptor] and do the same execution.
MIFactory.register(MyInterceptor);
let m = MIFactory.intercept(new MyTestClass());
console.log('_________________________');
console.log('Result ' + m.doMethod1());
console.log('_________________________');
m.doMethod2();
now the results look like this.
_________________________
before..doMethod1
executing method 1
after..doMethod1 ... time taken: 654 ticks
Result 100
_________________________
before..doMethod2
executing method 2
after..doMethod2 ... time taken: 285 ticks