Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save petsel/5690543 to your computer and use it in GitHub Desktop.
Save petsel/5690543 to your computer and use it in GitHub Desktop.
1) thoughts about how to adopt the principles of aspect oriented programming to JavaScripts dynamic and functional nature.2) point (1) now gets accompanied by a working implementation - "modification.ao.js" - of an aspect oriented system as proof of concept.3) there is a working "logging" example now too as always if one needs to justify the exi…

roughly sketched

  • runtime based only and not using any kind of JavaScript "transpilers" or JavaScript build tools for "code weaving" as in e.g. AspectJ.
  • thus being forced focusing on what ES3 language core does provide.
  • implementation of prototypal method modifiers e.g. Function.prototype.before, Function.prototype.after, Function.prototype.around as minimal set of a kind of an AOP base that already supports library / framework agnostic modification of function based control flow by just wrapping additional behaviors / advice handlers around existing methods / functions.
  • clarify role of Joinpoint, Pointcut, Advice, Aspect; especially from this point of view of what makes them distinct from existing approaches in compiled and/or non dynamic and/or non functional programming languages.

  • no need of a pointcut specific language but methods as filters in order to define pointcuts that access/collect joinpoints.
  • maybe support for accumulating joinpoints in a way similar to a pointcut's definition by just providing a filter method.
  • maybe even consider dropping the AOP paradigm of supporting an "oblivious" system thus allowing to mark/flag methods as joinpoints from within any JavaScript source code - think in ways of e.g. Function.prototype.(un)markJoinpoint.

Joinpoint

  • A Joinpoint in JavaScript always needs to feature both a method that is bound to an object and this very object itself (regardless of either this couple is locally scoped or not). One might even think about a label that optionally gets assigned to a joinpoint.
  • Thus a joinpoint will be constructed at least by a method and this method's target object.

Pointcut

  • A Pointcut in JavaScript always should be able to return a collection of joinpoints that are filtered according to certain criteria.
  • Thus a pointcut explicitly will be constructed by its filter method.

Advice

  • An Advice in JavaScript always should feature both a method that defines behavior (or could be seen as advice handler) and a named qualifier or type e.g. before, after, around(, afterReturning, afterThrowing).
  • Thus an advice will be constructed by a type and a method that gets associated with that type.

Aspect

  • An Aspect in JavaScript needs to feature just a sole function that folds advices and pointcuts within it's function body.

additional notes

  • In order to take advantage of JavaScripts dynamic nature it should be allowed to alter the whole system's control flow at any time from any point e.g.
    • advices do alter the system's control flow just by calling one of every advices two methods either confirm or reject / deny.
    • add or remove joinpoints, pointcuts, advices regardless of how many aspects are currently confirmed or rejected.
(function(r,j){var b,c=this;b=c.Object;var e=b.prototype,k=c.Array.prototype,d=c.RegExp.prototype,s=c.String.prototype,t=e.toString,u=e.valueOf,v=d.compile,g=/(?:)/;""+g.compile("(?:)","")!==""+g&&(d.compile=function(){v.apply(this,arguments);return this});var e=function(a){return["^\\[object\\s+",a,"\\]$"].join("")},m=function(a){return t.call(a)},w=e("String"),n=function(a){return void 0===a||null===a?a:u.call(a).valueOf()},f=function(a){return"function"==typeof a&&"function"==typeof a.call&&"function"==typeof a.apply},l=function(a){return g.compile(w).test(m(a))},d=function(a,b){return new c.ReferenceError(["A valid implementation of ",a," is missing.\n\nPlease provide the basic shims of ES5. Have a look at e.g.\nhttps://github.com/kriskowal/es5-shim/blob/master/es5-shim.js",b].join(""))};if(!f(b.keys))throw d("[Object.keys]","#L542");if(!f(s.trim))throw d("[String.prototype.trim]","#L899");if(!f(k.indexOf))throw d("[Array.prototype.indexOf]","#L479");if(!f(k.forEach))throw d("[Array.prototype.forEach]","#L238");if(!f(k.reduce))throw d("[Array.prototype.reduce]","#L382");var p=b.keys,x=c.parseFloat,y={global:c,objects:{regX:g},methods:{noop:function(){}},helpers:{compareTypes:function(a,b,c){c=f(c)&&c||n;var d,e;return(d=c(a))>(e=c(b))&&1||d<e&&-1||(d===e?0:void 0)},createClassSignaturePattern:e,protectBehaviorFromInstantiation:function(a,c){if(c instanceof a)throw new TypeError("Traits and Mixins always need to be applied onto objects but never get instantiated.");}},introspective:{isFunction:f,isString:l,baseValueOf:n,getClassSignature:m}},h={},q=function(a){return h[l(a)&&a.trim()||""]};j=j||c;b=function(a,b){a=l(a)&&a.trim()||"";var d=f(b)&&b(q,c,y);if(d&&a)return h[a]=d};b.all=function(){return p(h)};b.all.size=function(){return p(h).length};b.require=q;(function(){this.first=function(){return this()[0]};this.last=function(){var a;return(a=this())[a.length-1]};this.item=function(a){return this()[x(a,10)]}}).call(b.all);return j[r]=b}).call(null,"composable");
composable("components.Introspective_isFunction_isCallable",function(b,e,f){var g=e.Function.prototype,h=function(a){return"function"==typeof a&&"function"==typeof a.call&&"function"==typeof a.apply},j=function(a){var c;if(c=h(a)){var b;try{g.toString.call(a),b=!0}catch(e){b=!1}c=b}if(!c){var d;try{a(),d=!0}catch(f){try{g.call.call(a),d=!0}catch(j){d=!1}}c=d}return c};b=function(){this.isFunction=h;this.isCallable=j};b.apply(f.introspective);return b});
composable("components.Introspective_isArray_isArguments",function(d,b,c){d("components.Introspective_isFunction_isCallable");d=c.introspective;var e=b.Array,j,a=b.Object.prototype.propertyIsEnumerable;try{a.call(null,"length");var l=a,a=function(h,a){return l.call(h,a)}}catch(t){var m=a,a=function(h,a){var b;try{b=m.call(h,a)}catch(c){b=!0}return b}}j=a;var a=c.helpers.createClassSignaturePattern,f=c.objects.regX,n=a("Object"),p=a("Array"),k=a("Arguments"),g=d.getClassSignature;c=d.isFunction;var q=b.isFinite,r=c(e.isArray)&&e.isArray||function(a){return f.compile(p).test(g(a))};if(!(b=c(e.isArguments)&&e.isArguments))b=function(){return f.compile(k).test(g(arguments))}()?function(a){return f.compile(k).test(g(a))}:function(a){return!!a&&f.compile(n).test(g(a))&&"number"==typeof a.length&&q(a.length)&&!j(a,"length")};var s=b;b=function(){this.isArray=r;this.isArguments=s};b.apply(d);return b});
composable("components.Enumerable_toArray",function(a,d,h){a("components.Introspective_isArray_isArguments");a=h.introspective;var e=d.Array.prototype.slice,j=a.isFunction,k=a.isString,n=a.isArguments,p=a.isArray,l=d.isFinite,f=d.document,q=function(){var a,b,g,d=f&&f.forms||[],m=f&&j(f.getElementsByTagName)&&f.getElementsByTagName("")||d;try{b=e.call(d);b=e.call(m);b=e.call(arguments);g=b.join("");if(3!=b.length||"Array.make"!=g)throw Error();b=e.call(g);if(10!==b.length||"."!=b[5])throw Error();a=function(){var a,c=(this||k(this))&&this.length;"number"==typeof c&&l(c)&&(a=[],a.length=c,a=e.call(this));return a}}catch(h){a=function(){var a,c,b=(p(this)||n(this))&&e.call(this)||k(this)&&this.split("")||a;if(!b&&(c=0!==this&&this&&this.length,"number"==typeof c&&l(c)))if(b=[],b.length=c,j(this.item))for(;c--;)a=this.item(c),c in this&&(b[c]=a);else for(;c--;)a=this[c],c in this&&(b[c]=a);return b}}b=g=d=m=null;return a}("Array",".","make");return function(){this.toArray=q}});
composable("composites.Array_make",function(b,c,d){var a={},e=b("components.Enumerable_toArray").call(a)||a.toArray;c.Array.make=d.helpers.makeArray=function(a){return e.call(a)}});
composable("components.Introspective_typeDetection_core",function(c,l,j){c("components.Introspective_isFunction_isCallable");c("components.Introspective_isArray_isArguments");c=j.introspective;var x=c.isFunction,y=c.isCallable,z=c.isArray,A=c.isArguments,m=c.isString,B=c.baseValueOf,h=j.helpers.createClassSignaturePattern,f=function(a){return["(?:^",a,":)|(?:^",a,"$)|(?:\\[",a,":\\s*name\\:\\s*",a,")"].join("")},b=j.objects.regX,C=h("Boolean"),D=h("Number"),E=h("Object"),F=h("RegExp"),G=h("Date"),H=h("Error"),I=f("Error"),J=f("EvalError"),K=f("RangeError"),L=f("ReferenceError"),M=f("SyntaxError"),N=f("TypeError"),O=f("URIError"),g=c.getClassSignature,d,P=l.Error.prototype.toString;d=function(a){return P.call(a)};var n=l.isFinite,Q=function(a){return void 0===a},R=function(a){return void 0!==a},S=function(a){return null===a},T=function(a){return null!==a},p=function(a){return!a&&(void 0===a||null===a)},k=function(a){return void 0!==a&&null!==a},q=function(a){return"string"==typeof a||"number"==typeof a||"boolean"==typeof a},U=function(a){return q(a)||p(a)},r=function(a){return a&&("object"==typeof a||"function"==typeof a)},s=function(a){return k(a)&&"function"==typeof a.constructor},V=function(a){return r(a)&&"function"!=typeof a.constructor},t=function(a){return b.compile(C).test(g(a))},u=function(a){return"boolean"==typeof a},W=function(a){return t(a)&&!u(a)},v=function(a){return b.compile(D).test(g(a))&&"number"!=typeof a},X=function(a){return"number"==typeof a},Y=function(a){return("number"==typeof a||v(a))&&n(a)},w=function(a){return"string"==typeof a},Z=function(a){return m(a)&&!w(a)},$=function(a){return k(a)&&"number"==typeof a.length&&n(a.length)&&0<=a.length},aa=function(a){return s(a)&&b.compile(E).test(g(a))},ba=function(a){return b.compile(F).test(g(a))},ca=function(a){return b.compile(G).test(g(a))},e=function(a){return b.compile(H).test(g(a))},da=function(a){return e(a)&&(b.compile(I).test(d(a))||"Error"===a.name)},ea=function(a){return e(a)&&(b.compile(J).test(d(a))||"EvalError"===a.name)},fa=function(a){return e(a)&&(b.compile(K).test(d(a))||"RangeError"===a.name)},ga=function(a){return e(a)&&(b.compile(L).test(d(a))||"ReferenceError"===a.name)},ha=function(a){return e(a)&&(b.compile(M).test(d(a))||"SyntaxError"===a.name)},ia=function(a){return e(a)&&(b.compile(N).test(d(a))||"TypeError"===a.name)},ja=function(a){return e(a)&&(b.compile(O).test(d(a))||"URIError"===a.name)};return function(){this.isUndefined=Q;this.isDefined=R;this.isNull=S;this.isNotNull=T;this.isUndefinedOrNull=p;this.isNeitherUndefinedNorNull=k;this.isPrimitive=q;this.isValue=U;this.isObject=r;this.isNative=s;this.isAlien=V;this.isBoolean=t;this.isBooleanValue=u;this.isBooleanObject=W;this.isNumber=Y;this.isNumberValue=X;this.isNumberObject=v;this.isString=m;this.isStringValue=w;this.isStringObject=Z;this.isArray=z;this.isArguments=A;this.isListLike=$;this.isObjectObject=aa;this.isFunction=x;this.isCallable=y;this.isRegExp=ba;this.isDate=ca;this.isError=e;this.isGenericError=da;this.isEvalError=ea;this.isRangeError=fa;this.isReferenceError=ga;this.isSyntaxError=ha;this.isTypeError=ia;this.isURIError=ja;this.baseValueOf=B;this.getClassSignature=g}});
composable("environment",function(a,c,b){a=function(){};a.prototype=b;return new a});
composable("environment_extended_introspective_core",function(a){var b=a("environment");a("components.Introspective_typeDetection_core").call(b.introspective);return b});
composable("components.Enumerable_first_last",function(){return function(){this.first=function(){return this[0]};this.last=function(){return this[this.length-1]}}});
composable("components.Enumerable_first_last_item",function(c,a){var b=a.parseFloat;return function(){this.first=function(){return this[0]};this.last=function(){return this[this.length-1]};this.item=function(a){return this[b(a,10)]}}});
composable("components.Enumerable_first_last_item_listWrapper",function(d,b){var c=b.parseFloat;return function(a){this.first=function(){return a[0]};this.last=function(){return a[a.length-1]};this.item=function(b){return a[c(b,10)]}}});
composable("components.Enumerable_first_last_item_listGetterShorthands",function(d,b){var c=b.parseFloat;return function(){this.first=function(){return this()[0]};this.last=function(){var a;return(a=this())[a.length-1]};this.item=function(a){return this()[c(a,10)]}}});
composable("components.Allocable",function(a,b,c){a("composites.Array_make");var d=a("components.Enumerable_first_last_item_listWrapper");a=b.Array;b=c.introspective.isFunction;var e=b(a.make)&&a.make||c.helpers.makeArray;return function(a){this.valueOf=this.toArray=function(){return e(a)};this.toString=function(){return""+a};this.size=function(){return a.length};d.call(this,a)}});
composable("components.Allocable_all",function(a,b,c){a("composites.Array_make");var d=a("components.Enumerable_first_last_item_listGetterShorthands");a=b.Array;b=c.introspective.isFunction;var e=b(a.make)&&a.make||c.helpers.makeArray;return function(a){this.all=function(){return e(a)};this.all.size=function(){return a.length};d.call(this.all)}});
composable("components.Controllable_Advice_before_after_around",function(k,n,l){k("components.Introspective_isFunction_isCallable");k=l.introspective;var e=k.isFunction,m=k.isCallable,c=function(b){return!b&&(void 0===b||null===b)?null:b};return function(){this.before=function(b,f,g){var a;if(a=e(b))if(a=e(this)){var h=this,d=c(f),j=c(g);a=function(){var a=arguments;b.call(d,a,j);return h.apply(d,a)}}return a||this};this.after=function(b,f,g){var a;if(a=e(this))if(a=e(b)){var h=this,d=c(f),j=c(g);a=function(){var a,c=arguments;a=h.apply(d,c);b.call(d,c,a,j);return a}}return a||this};this.around=function(b,f,g){var a;if(a=m(this))if(a=e(b)){var h=this,d=c(f),j=c(g);a=function(){return b.call(d,h,b,arguments,d,j)}}return a||this}}});
//deprecated//composable("components.Controllable_Advice_before_after_around",function(e,k,h){e("components.Introspective_isFunction_isCallable");e=h.introspective;var b=e.isFunction,j=e.isCallable,f=function(a,c,d){return function(){var b=arguments;a.apply(d,b);return c.apply(d,b)}},g=function(a){return!a&&(void 0===a||null===a)?null:a};return function(){this.before=function(a,c){return b(a)&&b(this)&&f(a,this,g(c))||this};this.after=function(a,c){return b(this)&&b(a)&&f(this,a,g(c))||this};this.around=function(a,c){var d;if(d=j(this))if(d=b(a)){var e=this,f=g(c);d=function(){return a.call(f,e,a,arguments,f)}}return d||this}}});
composable("composites.Function_isFunction_isCallable",function(a,b){a("components.Introspective_isFunction_isCallable").call(b.Function)});
composable("",function(a,b){a("components.Introspective_isArray_isArguments").call(b.Array)});
composable("",function(a,b){a("components.Enumerable_first_last").call(b.Array.prototype)});
composable("composites.Function_modifiers_Advice_before_after_around",function(a,b){a("components.Controllable_Advice_before_after_around").call(b.Function.prototype)});
composable("components.Observable_SignalsAndSlots",function(h,c,p){h("composites.Array_make");h=c.Array;var r=c.Date;c=p.introspective;var s=c.isFunction,n=c.isCallable,f=c.isString,t=s(h.make)&&h.make||p.helpers.makeArray,q=function(a,g){this.constructor=q;this.target=a;this.type=g;this.timeStamp=new r},m=function(a,g,f){var d=new q(a,g);this.constructor=m;this.handleEvent=function(a){a&&"object"==typeof a?(a.target=d.target,a.type=d.type,a.timeStamp=d.timeStamp):a={target:d.target,type:d.type,timeStamp:d.timeStamp};f(a)};this.getType=function(){return g};this.getHandler=function(){return f}};return function(a){a="object"==typeof a&&a||{};var g={},c="addEventListener",d="removeEventListener",k="hasEventListener",l="dispatchEvent",h=function(j,a){var b=g[j],e=!1;if(b){var c=b.handlers,b=b.listeners,f=c.indexOf(a);0<=f&&(c.splice(f,1),b.splice(f,1),e=!0)}return e},c=f(a[c])&&a[c]||c,d=f(a[d])&&a[d]||d,k=f(a[k])&&a[k]||k,l=f(a[l])&&a[l]||l;this[c]=function(j,a){var b;if(j&&f(j)&&n(a)){var e=g[j];b=new m(this,j,a);if(e){var c=e.handlers,e=e.listeners,d=c.indexOf(a);-1==d?(c.push(b.getHandler()),e.push(b)):b=e[d]}else e=g[j]={},e.handlers=[b.getHandler()],e.listeners=[b]}return b};this[d]=function(a,c){return f(a)&&n(c)&&h(a,c)||a instanceof m&&h(a.getType(),a.getHandler())||!1};this[k]=function(a,c){var b;if(!(b=f(a)&&n(c)&&(g[a]||!1)&&0<=g[a].handlers.indexOf(c))){if(b=a instanceof m){b=a.getType();var e=a.getHandler();b=(g[b]||!1)&&0<=g[b].handlers.indexOf(e)}b=b||!1}return b};this[l]=function(a){var c=!1,b=a&&"object"==typeof a&&f(a.type)&&a.type||f(a)&&a;if(b=b&&g[b]){var e=(b=b.listeners&&t(b.listeners))&&b.length||0,d=-1;if(1<=e){for(;++d<e;)b[d].handleEvent(a);c=!0}}return c}}});
composable("modification.ao",function(k,f,h){k("composites.Function_modifiers_Advice_before_after_around");h=k("environment");var E=k("components.Allocable_all");k=k("components.Observable_SignalsAndSlots");var l=h.introspective.isUndefined,c=h.introspective.isFunction,ka=h.introspective.isObject,m=h.introspective.isStringValue,la=f.Math.random,ma=h.helpers.makeArray,F=function(c,a){for(var b=-1,j=c.length;++b<j;)a(c[b],b,c)&&(c.splice(b,1),--b,--j)},N=function(c,a){for(var b,j=-1,d=c.length;++j<d;)if(a(c[j],j,c)){b=c[j];break}return b},g=function(c){return m(c)&&c.trim()||""},O=f.uuid&&c(f.uuid.v4)&&f.uuid.v4||function a(b){return b?(b^16*la()>>b/4).toString(16):([1E7]+-1E3+-4E3+-8E3+-1E11).replace(/[018]/g,a)},W,G,H="on",X=function(){return"off"==H},P,Q;f=function(){P();Q()};var e={};k.call(e,{addEventListener:"on",removeEventListener:"off",dispatchEvent:"emit"});var t,u=[],R=function(a){this.getLabel=function(){return a.label};this.getTarget=function(){return a.target};this.getMethodName=function(){return a.methodName};this.getBaseMethod=function(){return a.baseMethod}};R.prototype.equals=function(a){return v(this)&&v(a)&&this===a||w(this)&&w(a)&&this.getBaseMethod()===a.getBaseMethod()&&this.getMethodName()===a.getMethodName()&&this.getTarget()===a.getTarget()&&this.getLabel()===a.getLabel()};var v=function(a){return a instanceof R},w=function(a){return a&&"object"==typeof a&&c(a.getLabel)&&c(a.getTarget)&&c(a.getMethodName)&&c(a.getBaseMethod)},Y=function(a){return a&&"object"==typeof a&&ka(a.target)&&m(a.methodName)&&c(a.target[a.methodName])},Z=function(a){var b=a.getBaseMethod(),j=a.getMethodName(),d=a.getTarget(),c=a.getLabel();return function(a){return a.getBaseMethod()===b&&a.getMethodName()===j&&a.getTarget()===d&&a.getLabel()===c}},$=function(a){a.baseMethod=a.target[a.methodName];a.label=g(a.label);return new R(a)};t={add:function(a){var b;if(Y(a))b=$(a);else if(v(a)||w(a))b=a;b&&!u.some(Z(b))&&(u.push(b),e.emit({type:"joinpoint:add",joinpoint:b}));return b},remove:function(a){var b;if(Y(a))b=$(a);else if(v(a)||w(a))b=a;b&&(a=Z(b),u.some(a)?(F(u,a),e.emit({type:"joinpoint:remove",joinpoint:b})):b=!1);return b},isJoinpoint:v,isJoinpointLike:w};E.call(t,u);var x,n=[],S={},y=function(a){this.getFilter=function(){return a.filter};this.getId=function(){return a.id}};y.prototype.equals=function(a){return p(this)&&p(a)&&this===a||q(this)&&q(a)&&this.getFilter()===a.getFilter()&&this.getId()===a.getId()};y.prototype.getJoinpoints=function(){return(p(this)||q(this))&&t.all().filter(this.getFilter())||[]};var p=function(a){return a instanceof y},q=function(a){return a&&"object"==typeof a&&c(a.getFilter)&&c(a.getId)},aa=function(a){var b=a.getFilter(),c=a.getId();return function(a){return a.getFilter()===b&&a.getId()===c}},ba=function(a){var b;m(a)&&(b=S[a]);return b},I=function(a){var b,c=g(a.id),d=ba(c);if(d)b=d.getFilter()===a.filter?d:!1;else{var e=a.filter;(d=N(n,function(a){return a.getFilter()===e}))&&(b=d.getId()===c?d:!1)}return b};x={getById:ba,add:function(a){var b;a&&"object"==typeof a&&c(a.filter)?(b=I(a),l(b)&&(a.id=g(a.id)||O(),b=new y(a))):p(a)?b=a:q(a)&&(b=I({filter:a.getFilter(),id:a.getId()}),l(b)&&(a={filter:a.getFilter(),id:a.getId()},b=new y(a)));b&&!n.some(aa(b))&&(n.push(b),S[b.getId()]=b,e.emit({type:"pointcut:add",pointcut:b}));return b},remove:function(a){var b;if(a&&"object"==typeof a&&c(a.filter))b=I(a);else if(p(a)||q(a))b=I({filter:a.getFilter(),id:a.getId()});b&&(a=aa(b),n.some(a)?(F(n,a),delete S[b.getId()],e.emit({type:"pointcut:remove",pointcut:b})):b=!1);return b},isPointcut:p,isPointcutLike:q};E.call(x,n);var z,r=[],T={},J={AROUND:"around",BEFORE:"before",AFTER:"after",AFTERTHROWING:"afterThrowing",AFTERRETURNING:"afterReturning"},K=function(a){this.getHandler=function(){return a.handler};this.getType=function(){return a.type};this.getId=function(){return a.id}};K.prototype.equals=function(a){return A(this)&&A(a)&&this===a||B(this)&&B(a)&&this.getHandler()===a.getHandler()&&this.getType()===a.getType()&&this.getId()===a.getId()};var A=function(a){return a instanceof K},B=function(a){var b;if(b=a)if(b="object"==typeof a)if(b=c(a.getHandler))if(b=c(a.getType))b=a.getType(),b=m(b)&&J[b.toUpperCase()]===b&&c(a.getId);return b},ca=function(a){return a&&"object"==typeof a&&c(a.handler)&&(J[g(a.type).toUpperCase()]||"")},da=function(a){var b=a.getHandler(),c=a.getType(),d=a.getId();return function(a){return a.getHandler()===b&&a.getType()===c&&a.getId()===d}},ea=function(a){var b;m(a)&&(b=T[a]);return b},U=function(a){var b,c=g(a.id),d=ea(c);a.type=J[g(a.type).toUpperCase()]||"";if(d)b=d.getHandler()===a.handler&&d.getType()===a.type?d:!1;else{var e=a.handler,f=a.type;(d=N(r,function(a){return a.getHandler()===e&&a.getType()===f}))&&(b=d.getId()===c?d:!1)}return b},fa=function(a){return U({handler:a.getHandler(),type:a.getType(),id:a.getId()})};z={getById:ea,add:function(a){var b;ca(a)?(b=U(a),l(b)&&(a.type=J[g(a.type).toUpperCase()]||"",a.id=g(a.id)||O(),b=new K(a))):A(a)?b=a:B(a)&&(b=fa(a),l(b)&&(a={handler:a.getHandler(),type:a.getType(),id:a.getId()},b=new K(a)));b&&!r.some(da(b))&&(r.push(b),T[b.getId()]=b,e.emit({type:"advice:add",advice:b}));return b},remove:function(a){var b;if(ca(a))b=U(a);else if(A(a)||B(a))b=fa(a);b&&(a=da(b),r.some(a)?(F(r,a),delete T[b.getId()],e.emit({type:"advice:remove",advice:b})):b=!1);return b},isAdvice:A,isAdviceLike:B};E.call(z,r);var s=[],V={},L=function(a){var b=this,c="deny",d="deny",f=function(){c=""},g=[],h=a.handler;h(function(a,b){var c=z.add(a),d=x.add(b);na(c)&&(oa(d)&&!g.some(pa(c,d)))&&g.push({advice:c,pointcut:d})},W);b.getLinkList=function(){return ma(g)};b.getHandler=function(){return h};b.getId=function(){return a.id};b.deny=function(){this===b&&!b.isDenied()&&(e.off("system:restore",f),d=c="deny",ga(),G.all().forEach(ha))};b.confirm=function(){this===b&&!b.isConfirmed()&&(X()?(c="",d="confirm"):(b.getLinkList().forEach(function(a){var b=a.advice;a=a.pointcut;var c=b.getType(),d=b.getHandler();a.getJoinpoints().forEach(function(a){var b=a.getTarget(),e=a.getMethodName();b[e]=b[e][c](d,b,a)})}),d=c="confirm",e.on("system:restore",f)))};b.isReconfirm=function(){return this===b&&"confirm"!=c&&"confirm"==d};b.isConfirmed=function(){return this===b&&"confirm"==c&&"confirm"==d};b.isDenied=function(){return this===b&&"deny"==c&&"deny"==d}};L.prototype.equals=function(a){return C(this)&&C(a)&&this===a||D(this)&&D(a)&&this.getHandler()===a.getHandler()&&this.getId()===a.getId()};var oa=x.isPointcut,na=z.isAdvice,C=function(a){return a instanceof L},D=function(a){return a&&"object"==typeof a&&c(a.getLinkList)&&c(a.getHandler)&&c(a.getId)&&c(a.deny)&&c(a.confirm)&&c(a.isConfirmed)},ia=function(a){var b=a.getHandler(),c=a.getId();return function(a){return a.getHandler()===b&&a.getId()===c}},pa=function(a,b){return function(c){return c.advice===a&&c.pointcut===b}},ha=function(a){a.isReconfirm()&&a.confirm()},ga=function(){t.all().forEach(function(a){var b=a.getTarget(),c=a.getMethodName();a=a.getBaseMethod();b[c]=a});e.emit("system:restore")},ja=function(a){var b;m(a)&&(b=V[a]);return b},M=function(a){var b,c=g(a.id),d=ja(c);if(d)b=d.getHandler()===a.handler?d:!1;else{var e=a.handler;(d=N(s,function(a){return a.getHandler()===e}))&&(b=d.getId()===c?d:!1)}return b};P=function(){H="off";ga()};Q=function(){H="on";G.all().forEach(ha)};h={getById:ja,add:function(a){var b;a&&"object"==typeof a&&c(a.handler)?(b=M(a),l(b)&&(a.id=g(a.id)||O(),b=new L(a))):C(a)?b=a:D(a)&&(b=M({handler:a.getHandler(),id:a.getId()}),l(b)&&(a={handler:a.getHandler(),id:a.getId()},b=new L(a)));b&&!s.some(ia(b))&&(s.push(b),V[b.getId()]=b,e.emit({type:"aspect:add",aspect:b}));return b},remove:function(a){var b;if(a&&"object"==typeof a&&c(a.handler))b=M(a);else if(C(a)||D(a))b=M({handler:a.getHandler(),id:a.getId()});b&&(a=ia(b),s.some(a)?(F(s,a),delete V[b.getId()],e.emit({type:"aspect:remove",aspect:b})):b=!1);return b},isAspect:C,isAspectLike:D};E.call(h,s);G=h;e.on("joinpoint:add",f);e.on("joinpoint:remove",f);e.on("pointcut:add",f);e.on("pointcut:remove",f);e.on("advice:add",f);e.on("advice:remove",f);e.on("aspect:add",f);e.on("aspect:remove",f);return W={Joinpoint:t,Pointcut:x,Advice:z,Aspect:G,isOff:X,isOn:function(){return"on"==H},off:P,on:Q,reboot:f}});
composable("modification.ao", function (require, global, environment) {
"use strict";
require("composites.Function_modifiers_Advice_before_after_around");
environment = require("environment");
var
Allocable_all = require("components.Allocable_all"),
Observable = require("components.Observable_SignalsAndSlots"),
isUndefined = environment.introspective.isUndefined,
isFunction = environment.introspective.isFunction,
isObject = environment.introspective.isObject,
isStringValue = environment.introspective.isStringValue,
math_random = global.Math.random,
makeArray = environment.helpers.makeArray,
mutateArray_removeItemsByFilter = function (arr, filter) {
var
idx = -1,
len = arr.length
;
while (++idx < len) {
if (filter(arr[idx], idx, arr)) {
arr.splice(idx, 1);
--idx;
--len;
}
}
},
array_getItemByFilter = function(arr, filter) {
var
item,
idx = -1,
len = arr.length
;
while (++idx < len) {
if (filter(arr[idx], idx, arr)) {
item = arr[idx];
break;
}
}
return item;
},
getSanitizedIdentifier = function (identifier) {
return (isStringValue(identifier) && identifier.trim()) || "";
},
createId = (
/* [https://github.com/broofa/node-uuid] - Robert Kieffer */
(global.uuid && isFunction(global.uuid.v4) && global.uuid.v4)
/* [https://gist.github.com/jed/982883] - Jed Schmidt */
|| function b(a){return a?(a^math_random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)}
),
AspectOrientedModule, // control flow modifying module of an Aspect Oriented DSL
Joinpoint,
Pointcut,
Advice,
Aspect,
systemState = "on", // "off"
isSystemOff = function () {return (systemState == "off");},
isSystemOn = function () {return (systemState == "on");},
systemOff, //:Function(scoped)
systemOn, //:Function(scoped)
rebootSystem = function () {
systemOff();
systemOn();
},
EventProxy = {}
;
Observable.call(EventProxy, { // ++ OBSERVABLE API CONFIGURATION ++
addEventListener : "on", // "addListener" - "addObserver"
removeEventListener : "off", // "removeListener" - "removeObserver"
//hasEventListener : "hasListener", // "hasListener" - "hasObserver"
dispatchEvent : "emit" // "dispatch" - "triggerEvent"
});
Joinpoint = (function () {
var
module,
joinpointList = [],
Constructor = function (config) {
var joinpoint = this;
joinpoint.getLabel = function () {
return config.label;
};
joinpoint.getTarget = function () {
return config.target;
};
joinpoint.getMethodName = function () {
return config.methodName;
};
joinpoint.getBaseMethod = function () {
return config.baseMethod;
};
}
;
Constructor.prototype.equals = function (type) {
var joinpoint = this;
return (
(isJoinpoint(joinpoint) && isJoinpoint(type) && (joinpoint === type))
|| (
isJoinpointLike(joinpoint) && isJoinpointLike(type)
&& (joinpoint.getBaseMethod() === type.getBaseMethod())
&& (joinpoint.getMethodName() === type.getMethodName())
&& (joinpoint.getTarget() === type.getTarget())
&& (joinpoint.getLabel() === type.getLabel())
)
);
};
var
isJoinpoint = function (type) {
return (type instanceof Constructor);
},
isJoinpointLike = function (type) {
return (
type && (typeof type == "object")
&& isFunction(type.getLabel) && isFunction(type.getTarget)
&& isFunction(type.getMethodName) && isFunction(type.getBaseMethod)
);
},
isJoinpointConfig = function (type) {
return (
type && (typeof type == "object")
&& isObject(type.target) && isStringValue(type.methodName)
&& isFunction(type.target[type.methodName])
);
},
makeFilter_equalsJoinpoint = function (joinpoint) {
var
baseMethod = joinpoint.getBaseMethod(), // generated
methodName = joinpoint.getMethodName(), // required
target = joinpoint.getTarget(), // required
label = joinpoint.getLabel() // fallback
;
return function/* equalsJoinpoint */(item/*, idx, list*/) {
return (
(item.getBaseMethod() === baseMethod) && (item.getMethodName() === methodName)
&& (item.getTarget() === target) && (item.getLabel() === label)
);
};
},
createJoinpoint = function (config) {
config.baseMethod = config.target[config.methodName];
config.label = getSanitizedIdentifier(config.label);
return (new Constructor(config));
},
addJoinpoint = function (configOrJoinpoint) {
var joinpoint;
if (isJoinpointConfig(configOrJoinpoint)) {
joinpoint = createJoinpoint(configOrJoinpoint);
} else if (isJoinpoint(configOrJoinpoint) || isJoinpointLike(configOrJoinpoint)) {
joinpoint = configOrJoinpoint;
}
if (joinpoint && !joinpointList.some(makeFilter_equalsJoinpoint(joinpoint))) {
// addJoinpoint
joinpointList.push(joinpoint);
EventProxy.emit({
type : "joinpoint:add",
joinpoint : joinpoint
});
}
return joinpoint;
},
removeJoinpoint = function (configOrJoinpoint) {
var joinpoint;
if (isJoinpointConfig(configOrJoinpoint)) {
joinpoint = createJoinpoint(configOrJoinpoint);
} else if (isJoinpoint(configOrJoinpoint) || isJoinpointLike(configOrJoinpoint)) {
joinpoint = configOrJoinpoint;
}
if (joinpoint) {
var equalsJoinpoint = makeFilter_equalsJoinpoint(joinpoint);
if (joinpointList.some(equalsJoinpoint)) {
// this approach keeps the list reference that was passed into the privileged "Allocable_all" Trait Module.
mutateArray_removeItemsByFilter(joinpointList, equalsJoinpoint);
EventProxy.emit({
type : "joinpoint:remove",
joinpoint : joinpoint
});
} else {
joinpoint = false;
}
equalsJoinpoint = null;
}
return joinpoint;
}
;
module = {
add : addJoinpoint,
remove : removeJoinpoint,
isJoinpoint : isJoinpoint,
isJoinpointLike : isJoinpointLike
};
Allocable_all.call(module, joinpointList);
return module;
}());
Pointcut = (function () {
var
module,
pointcutList = [],
pointcutMap = {},
Constructor = function (config) {
var pointcut = this;
pointcut.getFilter = function () {
return config.filter;
};
pointcut.getId = function () {
return config.id;
};
}
;
Constructor.prototype.equals = function (type) {
var pointcut = this;
return (
(isPointcut(pointcut) && isPointcut(type) && (pointcut === type))
|| (
isPointcutLike(pointcut) && isPointcutLike(type)
&& (pointcut.getFilter() === type.getFilter())
&& (pointcut.getId() === type.getId())
)
);
};
Constructor.prototype.getJoinpoints = function () {
var pointcut = this;
return ((isPointcut(pointcut) || isPointcutLike(pointcut)) && Joinpoint.all().filter(pointcut.getFilter())) || [];
};
var
isPointcut = function (type) {
return (type instanceof Constructor);
},
isPointcutLike = function (type) {
return (type && (typeof type == "object") && isFunction(type.getFilter) && isFunction(type.getId));
},
isPointcutConfig = function (type) {
return (type && (typeof type == "object") && isFunction(type.filter));
},
makeFilter_isPointcutFilter = function (filter) {
return function/* isPointcutFilter */(item/*, idx, list*/) {
return (item.getFilter() === filter);
};
},
makeFilter_equalsPointcut = function (pointcut) {
var
filter = pointcut.getFilter(), // required
id = pointcut.getId() // required
;
return function/* equalsPointcut */(item/*, idx, list*/) {
return ((item.getFilter() === filter) && (item.getId() === id));
};
},
getPointcutById = function (id) {
var pointcut;
if (isStringValue(id)) {
pointcut = pointcutMap[id];
}
return pointcut;
},
getPointcutByConfig = function (config) {
var
pointcut,
id = getSanitizedIdentifier(config.id),
pc = getPointcutById(id) // pointcutMap[id]
;
if (pc) {
pointcut = (pc.getFilter() === config.filter) ? pc : false;
} else {
var isPointcutFilter = makeFilter_isPointcutFilter(config.filter);
pc = array_getItemByFilter(pointcutList, isPointcutFilter);
if (pc) {
pointcut = (pc.getId() === id) ? pc : false;
}
isPointcutFilter = null;
}
return pointcut; // :Pointcut|false|undefined
},
getPointcutByReference = function (pointcut) {
return getPointcutByConfig({
filter : pointcut.getFilter(),
id : pointcut.getId()
}); // :Pointcut|false|undefined
},
createPointcut = function (config) {
return (new Constructor(config));
},
addPointcut = function (configOrPointcut) {
var config, pointcut;
if (isPointcutConfig(configOrPointcut)) {
config = configOrPointcut;
pointcut = getPointcutByConfig(config); // :Pointcut|false|undefined
if (isUndefined(pointcut)) {
config.id = (getSanitizedIdentifier(config.id) || createId());
pointcut = createPointcut(config); // :Pointcut
}
} else if (isPointcut(configOrPointcut)) {
pointcut = configOrPointcut; // :Pointcut
} else if (isPointcutLike(configOrPointcut)) {
pointcut = getPointcutByReference(configOrPointcut); // :Pointcut|false|undefined
if (isUndefined(pointcut)) {
pointcut = createPointcut({
filter : configOrPointcut.getFilter(),
id : configOrPointcut.getId()
}); // :Pointcut
}
}
if (pointcut && !pointcutList.some(makeFilter_equalsPointcut(pointcut))) {
// addPointcut
pointcutList.push(pointcut);
pointcutMap[pointcut.getId()] = pointcut;
EventProxy.emit({
type : "pointcut:add",
pointcut : pointcut
});
}
return pointcut; // :Pointcut|false|undefined
},
removePointcut = function (configOrPointcut) {
var pointcut;
if (isPointcutConfig(configOrPointcut)) {
pointcut = getPointcutByConfig(configOrPointcut); // :Pointcut|false|undefined
} else if (isPointcut(configOrPointcut) || isPointcutLike(configOrPointcut)) {
pointcut = getPointcutByReference(configOrPointcut); // :Pointcut|false|undefined
}
if (pointcut) {
var equalsPointcut = makeFilter_equalsPointcut(pointcut);
if (pointcutList.some(equalsPointcut)) {
// this approach keeps the list reference that was passed into the privileged "Allocable_all" Trait Module.
mutateArray_removeItemsByFilter(pointcutList, equalsPointcut);
delete pointcutMap[pointcut.getId()];
EventProxy.emit({
type : "pointcut:remove",
pointcut : pointcut
});
} else {
pointcut = false;
}
equalsPointcut = null;
}
return pointcut;
}
;
module = {
getById : getPointcutById,
add : addPointcut,
remove : removePointcut,
isPointcut : isPointcut,
isPointcutLike : isPointcutLike
};
Allocable_all.call(module, pointcutList);
return module;
}());
Advice = (function () {
var
module,
adviceList = [],
adviceMap = {},
adviceTypeCheckList = {
"AROUND" : "around",
"BEFORE" : "before",
"AFTER" : "after",
"AFTERTHROWING" : "afterThrowing",
"AFTERRETURNING" : "afterReturning"
},
Constructor = function (config) {
var advice = this;
advice.getHandler = function () {
return config.handler;
};
advice.getType = function () {
return config.type;
};
advice.getId = function () {
return config.id;
};
}
;
Constructor.prototype.equals = function (type) {
var advice = this;
return (
(isAdvice(advice) && isAdvice(type) && (advice === type))
|| (
isAdviceLike(advice) && isAdviceLike(type)
&& (advice.getHandler() === type.getHandler())
&& (advice.getType() === type.getType())
&& (advice.getId() === type.getId())
)
);
};
var
getSanitizedAdviceType = function (adviceType) {
return adviceTypeCheckList[getSanitizedIdentifier(adviceType).toUpperCase()] || "";
},
isValidAdviceType = function (adviceType) {
return (isStringValue(adviceType) && (adviceTypeCheckList[adviceType.toUpperCase()] === adviceType));
},
isAdvice = function (type) {
return (type instanceof Constructor);
},
isAdviceLike = function (type) {
return (
type && (typeof type == "object")
&& isFunction(type.getHandler) && isFunction(type.getType)
&& isValidAdviceType(type.getType()) && isFunction(type.getId)
);
},
isAdviceConfig = function (type) {
return (
type && (typeof type == "object")
&& isFunction(type.handler) && getSanitizedAdviceType(type.type)
);
},
makeFilter_equalsBehavior = function (handler, type) {
return function/* equalsBehavior */(item/*, idx, list*/) {
return ((item.getHandler() === handler) && (item.getType() === type));
};
},
makeFilter_equalsAdvice = function (advice) {
var
handler = advice.getHandler(), // required
type = advice.getType(), // required
id = advice.getId() // required
;
return function/* equalsAdvice */(item/*, idx, list*/) {
return ((item.getHandler() === handler) && (item.getType() === type) && (item.getId() === id));
};
},
getAdviceById = function (id) {
var advice;
if (isStringValue(id)) {
advice = adviceMap[id];
}
return advice;
},
getAdviceByConfig = function (config) {
var
advice,
id = getSanitizedIdentifier(config.id),
av = getAdviceById(id) // adviceMap[id]
;
config.type = getSanitizedAdviceType(config.type);
if (av) {
advice = ((av.getHandler() === config.handler) && (av.getType() === config.type)) ? av : false;
} else {
var equalsBehavior = makeFilter_equalsBehavior(config.handler, config.type);
av = array_getItemByFilter(adviceList, equalsBehavior);
if (av) {
advice = (av.getId() === id) ? av : false;
}
equalsBehavior = null;
}
return advice; // :Advice|false|undefined
},
getAdviceByReference = function (advice) {
return getAdviceByConfig({
handler : advice.getHandler(),
type : advice.getType(),
id : advice.getId()
}); // :Advice|false|undefined
},
createAdvice = function (config) {
return (new Constructor(config));
},
addAdvice = function (configOrAdvice) {
var config, advice;
if (isAdviceConfig(configOrAdvice)) {
config = configOrAdvice;
advice = getAdviceByConfig(config); // :Advice|false|undefined
if (isUndefined(advice)) {
config.type = getSanitizedAdviceType(config.type);
config.id = (getSanitizedIdentifier(config.id) || createId());
advice = createAdvice(config); // :Advice
}
} else if (isAdvice(configOrAdvice)) {
advice = configOrAdvice; // :Advice
} else if (isAdviceLike(configOrAdvice)) {
advice = getAdviceByReference(configOrAdvice); // :Advice|false|undefined
if (isUndefined(advice)) {
advice = createAdvice({
handler : configOrAdvice.getHandler(),
type : configOrAdvice.getType(),
id : configOrAdvice.getId()
}); // :Advice
}
}
if (advice && !adviceList.some(makeFilter_equalsAdvice(advice))) {
// addAdvice
adviceList.push(advice);
adviceMap[advice.getId()] = advice;
EventProxy.emit({
type : "advice:add",
advice : advice
});
}
return advice; // :Advice|false|undefined
},
removeAdvice = function (configOrAdvice) {
var advice;
if (isAdviceConfig(configOrAdvice)) {
advice = getAdviceByConfig(configOrAdvice); // :Advice|false|undefined
} else if (isAdvice(configOrAdvice) || isAdviceLike(configOrAdvice)) {
advice = getAdviceByReference(configOrAdvice); // :Advice|false|undefined
}
if (advice) {
var equalsAdvice = makeFilter_equalsAdvice(advice);
if (adviceList.some(equalsAdvice)) {
// this approach keeps the list reference that was passed into the privileged "Allocable_all" Trait Module.
mutateArray_removeItemsByFilter(adviceList, equalsAdvice);
delete adviceMap[advice.getId()];
EventProxy.emit({
type : "advice:remove",
advice : advice
});
} else {
advice = false;
}
equalsAdvice = null;
}
return advice;
}
;
module = {
getById : getAdviceById,
add : addAdvice,
remove : removeAdvice,
isAdvice : isAdvice,
isAdviceLike : isAdviceLike
};
Allocable_all.call(module, adviceList);
return module;
}());
Aspect = (function () {
var
module,
aspectList = [],
aspectMap = {},
Constructor = function (config) {
var
aspect = this,
confirmationState = {
current: "deny",
nominal: "deny"
},
markForReconfirmation = function () {
confirmationState.current = "";
},
linkList = [],
linkAdviceToPointcut = function (configOrAdvice, configOrPointcut) {
injectLinkIntoAspect(linkList, configOrAdvice, configOrPointcut);
},
handler = config.handler
;
handler(linkAdviceToPointcut, AspectOrientedModule); // Aspect Handler Arguments (API)
aspect.getLinkList = function () {
return makeArray(linkList);
};
aspect.getHandler = function () {
return handler;
};
aspect.getId = function () {
return config.id;
};
aspect.deny = function () {
if ((this === aspect) && !aspect.isDenied()) {
denyAspect(/*aspect, */confirmationState, markForReconfirmation);
}
};
aspect.confirm = function () {
if ((this === aspect) && !aspect.isConfirmed()) {
confirmAspect(aspect, confirmationState, markForReconfirmation);
}
};
aspect.isReconfirm = function () {
return ((this === aspect) && (confirmationState.current != "confirm") && (confirmationState.nominal == "confirm"));
};
aspect.isConfirmed = function () {
return ((this === aspect) && (confirmationState.current == "confirm") && (confirmationState.nominal == "confirm"));
};
aspect.isDenied = function () {
return ((this === aspect) && (confirmationState.current == "deny") && (confirmationState.nominal == "deny"));
};
}
;
Constructor.prototype.equals = function (type) {
var aspect = this;
return (
(isAspect(aspect) && isAspect(type) && (aspect === type))
|| (
isAspectLike(aspect) && isAspectLike(type)
&& (aspect.getHandler() === type.getHandler())
&& (aspect.getId() === type.getId())
)
);
};
var
isPointcut = Pointcut.isPointcut,
isAdvice = Advice.isAdvice,
isAspect = function (type) {
return (type instanceof Constructor);
},
isAspectLike = function (type) {
return (
type && (typeof type == "object")
&& isFunction(type.getLinkList) && isFunction(type.getHandler) && isFunction(type.getId)
&& isFunction(type.deny) && isFunction(type.confirm) && isFunction(type.isConfirmed)
);
},
isAspectConfig = function (type) {
return (type && (typeof type == "object") && isFunction(type.handler));
},
makeFilter_isAspectHandler = function (handler) {
return function/* isAspectHandler */(item/*, idx, list*/) {
return (item.getHandler() === handler);
};
},
makeFilter_equalsAspect = function (aspect) {
var
handler = aspect.getHandler(), // required
id = aspect.getId() // required
;
return function/* equalsAspect */(item/*, idx, list*/) {
return ((item.getHandler() === handler) && (item.getId() === id));
};
},
makeFilter_equalsAspectLink = function (advice, pointcut) {
return function/* equalsAspectLink */(item/*, idx, list*/) {
return ((item.advice === advice) && (item.pointcut === pointcut));
};
},
injectLinkIntoAspect = function (linkList, configOrAdvice, configOrPointcut) {
var
advice = Advice.add(configOrAdvice),
pointcut = Pointcut.add(configOrPointcut)
;
if (isAdvice(advice) && isPointcut(pointcut) && !linkList.some(makeFilter_equalsAspectLink(advice, pointcut))) {
linkList.push({
advice : advice,
pointcut : pointcut
});
}
},
isConfirmedAspect = function (aspect/*, idx, list*/) {
return aspect.isConfirmed();
},
reconfirmAspect = function (aspect/*, idx, list*/) {
aspect.isReconfirm() && aspect.confirm();
},
restoreInitialSystemState = function () {
//if (Aspect.all().some(isConfirmedAspect)) { // only in case of finding at least one "confirmed" aspect.
Joinpoint.all().forEach(function (joinpoint/*, idx, list*/) {
restoreJoinpoint(joinpoint.getTarget(), joinpoint.getMethodName(), joinpoint.getBaseMethod());
});
EventProxy.emit("system:restore"/*initialstate*/); // triggers the internal current confirmation state of each
//} // aspect to be set to anything different from "confirm" -
}, // thus switching each aspect into its "isReconfirm" mode.
reinitializeSystemState = function () {
Aspect.all().forEach(reconfirmAspect);
},
restoreJoinpoint = function (methodTarget, methodName, baseMethod) {
methodTarget[methodName] = baseMethod;
},
modifyJoinpoint = function (methodTarget, methodName, adviceType, adviceHandler, joinpoint) {
methodTarget[methodName] = methodTarget[methodName][adviceType](adviceHandler, methodTarget, joinpoint);
//console.warn = console.warn.before(function () {console.log("before warn", arguments);}, console);
},
joinAdviceAndPointcut = function (advice, pointcut) {
var
adviceType = advice.getType(),
adviceHandler = advice.getHandler()
;
pointcut.getJoinpoints().forEach(function (joinpoint/*, idx, list*/) {
modifyJoinpoint(joinpoint.getTarget(), joinpoint.getMethodName(), adviceType, adviceHandler, joinpoint);
});
},
confirmAspect = function (aspect, confirmationState, eventHandler) {
if (isSystemOff()) {
confirmationState.current = "";
confirmationState.nominal = "confirm";
} else {
aspect.getLinkList().forEach(function (link/*, idx, list*/) {
joinAdviceAndPointcut(link.advice, link.pointcut);
});
confirmationState.current = "confirm";
confirmationState.nominal = "confirm";
EventProxy.on("system:restore"/*initialstate*/, eventHandler);
}
},
denyAspect = function (/*aspect, */confirmationState, eventHandler) {
EventProxy.off("system:restore"/*initialstate*/, eventHandler);
confirmationState.current = "deny";
confirmationState.nominal = "deny";
restoreInitialSystemState();
reinitializeSystemState();
},
getAspectById = function (id) {
var aspect;
if (isStringValue(id)) {
aspect = aspectMap[id];
}
return aspect;
},
getAspectByConfig = function (config) {
var
aspect,
id = getSanitizedIdentifier(config.id),
as = getAspectById(id) // aspectMap[id]
;
if (as) {
aspect = (as.getHandler() === config.handler) ? as : false;
} else {
var isAspectHandler = makeFilter_isAspectHandler(config.handler);
as = array_getItemByFilter(aspectList, isAspectHandler);
if (as) {
aspect = (as.getId() === id) ? as : false;
}
isAspectHandler = null;
}
return aspect; // :Aspect|false|undefined
},
getAspectByReference = function (aspect) {
return getAspectByConfig({
handler : aspect.getHandler(),
id : aspect.getId()
}); // :Aspect|false|undefined
},
createAspect = function (config) {
return (new Constructor(config));
},
addAspect = function (configOrAspect) {
var config, aspect;
if (isAspectConfig(configOrAspect)) {
config = configOrAspect;
aspect = getAspectByConfig(config); // :Aspect|false|undefined
if (isUndefined(aspect)) {
config.id = (getSanitizedIdentifier(config.id) || createId());
aspect = createAspect(config); // :Aspect
}
} else if (isAspect(configOrAspect)) {
aspect = configOrAspect; // :Aspect
} else if (isAspectLike(configOrAspect)) {
aspect = getAspectByReference(configOrAspect); // :Aspect|false|undefined
if (isUndefined(aspect)) {
aspect = createAspect({
handler : configOrAspect.getHandler(),
id : configOrAspect.getId()
}); // :Aspect
}
}
if (aspect && !aspectList.some(makeFilter_equalsAspect(aspect))) {
// addAspect
aspectList.push(aspect);
aspectMap[aspect.getId()] = aspect;
EventProxy.emit({
type : "aspect:add",
aspect : aspect
});
}
return aspect; // :Aspect|false|undefined
},
removeAspect = function (configOrAspect) {
var aspect;
if (isAspectConfig(configOrAspect)) {
aspect = getAspectByConfig(configOrAspect); // :Aspect|false|undefined
} else if (isAspect(configOrAspect) || isAspectLike(configOrAspect)) {
aspect = getAspectByReference(configOrAspect); // :Aspect|false|undefined
}
if (aspect) {
var equalsAspect = makeFilter_equalsAspect(aspect);
if (aspectList.some(equalsAspect)) {
// this approach keeps the list reference that was passed into the privileged "Allocable_all" Trait Module.
mutateArray_removeItemsByFilter(aspectList, equalsAspect);
delete aspectMap[aspect.getId()];
EventProxy.emit({
type : "aspect:remove",
aspect : aspect
});
} else {
aspect = false;
}
equalsAspect = null;
}
return aspect;
}
;
systemOff = function () {
systemState = "off";
restoreInitialSystemState();
};
systemOn = function () {
systemState = "on";
reinitializeSystemState();
};
module = {
getById : getAspectById,
add : addAspect,
remove : removeAspect,
isAspect : isAspect,
isAspectLike : isAspectLike
};
Allocable_all.call(module, aspectList);
return module;
}());
EventProxy.on("joinpoint:add", rebootSystem);
EventProxy.on("joinpoint:remove", rebootSystem);
EventProxy.on("pointcut:add", rebootSystem);
EventProxy.on("pointcut:remove", rebootSystem);
EventProxy.on("advice:add", rebootSystem);
EventProxy.on("advice:remove", rebootSystem);
EventProxy.on("aspect:add", rebootSystem);
EventProxy.on("aspect:remove", rebootSystem);
AspectOrientedModule = {
Joinpoint : Joinpoint,
Pointcut : Pointcut,
Advice : Advice,
Aspect : Aspect,
isOff : isSystemOff,
isOn : isSystemOn,
off : systemOff,
on : systemOn,
reboot : rebootSystem
};
return AspectOrientedModule;
});
/**
* as for use with [twitter.com],
*
* if one tries scanning and logging the global namespace/object
* twitter.com prevents logging by overwriting [global.console] features
*
* the approach beneath kind of recovers [global.console.log] and [global.console.warn]
*
* just open a console window for the not closed minified "proxy" window as well
* and place it right to the console window of the browser that runs twitter.com
* (you don't need though, but see what happens / not happens).
*/
var win = window.open("","","");
win.resizeTo(0,0);
win.moveTo(0,0);
window.console.log = (function (loacal_console, remote_console) {
return function () {
remote_console.log.apply(loacal_console, arguments);
remote_console.log.apply(remote_console, arguments);
};
}(window.console, win.console));
window.console.warn = (function (loacal_console, remote_console) {
return function () {
remote_console.warn.apply(loacal_console, arguments);
remote_console.warn.apply(remote_console, arguments);
};
}(window.console, win.console));
//win.close();
//delete window.win;
var
ao = composable.require("modification.ao"),
JP = ao.Joinpoint,
PC = ao.Pointcut,
AV = ao.Advice,
AS = ao.Aspect
;
(function (require, global, rootName) {
var
scanTarget = global[rootName],
Object = global.Object,
env = require("environment"),
//env_helpers = env.helpers,
env_introspective = env.introspective,
isObjectObject = env_introspective.isObjectObject,
//isObject = env_introspective.isObject,
//isArray = env_introspective.isArray,
isFunction = env_introspective.isFunction,
//isCallable = env_introspective.isCallable,
ao = require("modification.ao"),
JP = ao.Joinpoint,
PC = ao.Pointcut,
AV = ao.Advice,
AS = ao.Aspect
;
var globallyAccessibleMethodsMap = Object.keys(scanTarget).reduce(function (scopedCollector, key) {
var
collect = arguments.callee,
target = scopedCollector.target,
list = scopedCollector.list,
path = scopedCollector.path,
methods = scopedCollector.scanned.methods,
objects = scopedCollector.scanned.objects,
type
//type = target[key]
;
try {
type = target[key];
} catch (exc) {
console.warn([exc.message, key, target]);
type = null;
}
scopedCollector.path = [path, ".", key].join("");
if (isFunction(type)) { // if (typeof type == "function") {
if (methods.indexOf(type) == -1) {
methods.push(type);
list.push({
"label" : scopedCollector.path,
"methodName" : key,
"target" : target
});
scopedCollector.target = type;
scopedCollector = Object.keys(type).reduce(collect, scopedCollector);
scopedCollector.target = target;
}
} else if (isObjectObject(type)) { // else if (type && (typeof type == "object")) {
if (objects.indexOf(type) == -1) {
objects.push(type);
scopedCollector.target = type;
scopedCollector = Object.keys(type).reduce(collect, scopedCollector);
scopedCollector.target = target;
}
}
scopedCollector.path = path;
return scopedCollector;
}, {
"target" : scanTarget,
"list" : [],
"path" : rootName,
"scanned" : {
"methods" : [],
"objects" : []
}
});
globallyAccessibleMethodsMap.list.forEach(function (protoJoinpoint) {
// JP.add({
// label : protoJoinpoint.label,
// methodName : protoJoinpoint.methodName,
// target : protoJoinpoint.target
// });
JP.add(protoJoinpoint);
});
return globallyAccessibleMethodsMap;
}(window.composable.require, window, "window"));
JP.all().sort(function (a, b) {
return (a.getLabel() < b.getLabel()) ? -1 : ((a.getLabel() > b.getLabel()) ? 1 : 0);
}).forEach(function (jp/*, idx, list*/) {
console.log(jp.getLabel());
});
console.log(["JP.all.size()", JP.all.size()]);
/**
* working AO system example that needs to be run
* within the console of a google chrome browser
* that runs [twitter.com]
*/
var av__log_before = AV.add({
id : "log_before",
type : "before",
handler : function (argsArray, pointcut) {
if (pointcut) {
var
label = pointcut.getLabel(),
target = pointcut.getTarget(),
methodName = pointcut.getMethodName()
;
console.log([label, methodName, (target === this), argsArray, this]);
} else {
console.log([this.name, argsArray, this]);
}
}
});
var pc__app_data = PC.add({
id : "app_data",
filter : function (jp) {
return (jp.getLabel().indexOf("window.loadrunner.Module.exports.app/data/") >= 0);
}
});
var pc__all_apps = PC.add({
id : "all_apps",
filter : function (jp) {
return (jp.getLabel().indexOf("window.loadrunner.Module.exports.app") >= 0);
}
});
var as__log__app_data = AS.add({
id : "log__app_data",
handler : function (linkAdviceToPointcut, aoSystem) {
linkAdviceToPointcut(av__log_before, pc__app_data);
}
});
var as__log__all_apps = AS.add({
id : "log__all_apps",
handler : function (linkAdviceToPointcut, aoSystem) {
linkAdviceToPointcut(av__log_before, pc__all_apps);
}
});
as__log__app_data.confirm();
as__log__all_apps.confirm();
/**
* play around - stepwise do:
*
* > as__log__all_apps.deny();
* > // ... see what happens ...
* > as__log__app_data.deny();
* > // ... ...
* > as__log__app_data.confirm();
* > // ... ...
* > ao.off()
* > // ... ...
* > as__log__all_apps.confirm();
* > // ... ...
* > ao.on()
* > // ... ...
* > ...
* > ...
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment