Using this private object it is then possible to add private properties and methods which are accessible only to methods decorated with 'private'.
As noted by coda in the comment section, pattern mutators is now included in mootools 2.0.
PatternMutator is also the base of my kenta.AOP Class for Mootools that I'm going to introduce, and as a bonus I will show how to use kenta.AOP to create a simple Mootools Class Profiler.
What kenta.AOP is?
kenta.AOP is simple way to handle AOP in MooTools for debugging purpose.
In particular kenta.AOP handles method invocation by rewriting all Mootools class, allowing you to intercept these methods before and after the execution.
You can then use the event parameter to cancel the method's execution or hijack the method's return value.
kenta.AOP uses Mootools Events in a Publish-Subscribe pattern to let you write any modules you want.
In this post I will show you kenta.AOP.Profile to better demonstrate what kenta.AOP can do.
I wrote kenta.AOP as a little project to better understand patternmutator and AOP myself, but since it might be useful for other people I will share this snippet. piece of code.
What kenta.AOP is not? It is not a complete AOP Framework. In particular kenta.AOP can't handle property access and it doesn't perform exception handling by design; it also overwrites all MooTools class methods, so I advise against using it for production-code :)
the complete code of kenta.AOP is here:
/* | |
--- | |
name: kenta.AOP | |
description: AOP for MooTools 1.3 | |
version: 1.1 | |
license: MIT-style license. | |
copyright: Carlesso Cristian | |
requires: Events, Class.PatternMutators | |
provides: [AOP] | |
... | |
*/ | |
AOP = new (new Class({ | |
Implements:[Events], | |
_call : 0, | |
getCallNumber : function() { | |
var _call = 0 + this._call++; | |
return _call; | |
} | |
})); | |
Class.defineMutator(new RegExp("^((?!(" +(function(){ | |
var current = []; | |
for(var m in Class.Mutators){ | |
var desc = ''+m; | |
if(desc.split('')[0] != '$'){ | |
current.push(desc); | |
}else{ | |
var extract = desc.replace("$mutator:/",""); | |
current.push(extract.substring(0,extract.length-1)); | |
} | |
} | |
return current.join('|'); | |
})()+ ")).*)$"), function(fn, name) { | |
var wrapFN = function(fn){ | |
return function() { | |
var parameter = { | |
description: this.Aspect, | |
uniqueID:$uid(this), | |
name : name, | |
params: arguments, | |
callno: AOP.getCallNumber() | |
}; | |
if ('Aspect' in this) { | |
//PRE | |
AOP.fireEvent('pre', parameter); | |
if (parameter.cancel) { | |
return; | |
} | |
if (parameter.returnthis) { | |
return parameter.returnthis; | |
} | |
} | |
parameter.result = fn.apply(this, arguments); | |
if ('Aspect' in this) { | |
//POST | |
AOP.fireEvent('post', parameter); | |
if (parameter.cancel) { | |
return; | |
} | |
if (parameter.returnthis) { | |
return parameter.returnthis; | |
} | |
} | |
return parameter.result; | |
}; | |
}; | |
if (name == "implement") { | |
var imp = this.implement; | |
var me = this; | |
this.implement = function(key, value) { | |
if (typeOf(key) == 'string' && typeOf(value) == 'function') { | |
imp.apply(me, [ key, wrapFN(value) ]); | |
return me; | |
} | |
return imp.apply(me, arguments); | |
} | |
} else { | |
if (typeOf(name) == 'string' && typeOf(fn) == 'function') { | |
this.prototype[name] = wrapFN(fn); | |
} else { | |
this.prototype[name] = fn; | |
} | |
} | |
}) |
Even if kenta.AOP listens all Mootools class method, it is designed to fire only if the Class has a 'Aspect' property
I guess you are wondering what kenta.AOP can be useful for, so here's a little example of how this code can be useful for debugging:
/* | |
--- | |
name: kenta.AOP.Profile.js | |
description: Profiling for MooTools 1.3 | |
version: 1.0 | |
license: MIT-style license. | |
copyright: Carlesso Cristian | |
requires: Events, Class.PatternMutators, kenta.AOP | |
provides: [Profile] | |
... | |
*/ | |
var Profile = new (new Class({ | |
Implements:[Events], | |
_profilation:{}, | |
initialize:function(){ | |
var me = this; | |
if(typeof(AOP)=='undefined'){ | |
alert('Profile requires kenta.AOP'); | |
return; | |
} | |
AOP.addEvent('pre',function(params){ | |
var Profilation = me._profilation; | |
var callID = params.uniqueID + '_' + params.name; | |
var data = Profilation[callID]||(Profilation[callID] = []); | |
data.push($time()); | |
}); | |
AOP.addEvent('post', function(params){ | |
var Profilation = me._profilation; | |
var stop_time = $time(); | |
var callID=params.uniqueID + '_' + params.name; | |
var data = Profilation[callID]; | |
var start_time = data.pop(); | |
if(data.length == 0){ | |
delete Profilation[callID]; | |
} | |
params.executionTime = stop_time - start_time; | |
me.fireEvent('trace', params); | |
}); | |
} | |
}))(); |
Since Profiling don't require to overwrite the return value I will show you another example:
and another one in which we cancel the method execution: