P ROXIES BEFORE PROXIES: THE HIDDEN GEMS OF JAVASCRIPT AOP
Jul 03, 2015
PROXIES BEFORE
PROXIES: THE HIDDEN
GEMS OF JAVASCRIPT AOP
1 - SHOES
2 - GLOVES
3 - JUMPER
4 – COAT TAILS
SCARLETJS
The simple fast JavaScript interceptor.
SCARLETJS
• API fluent interface
• Readability
• Performance
• Use of Core features
• Lack of ability to use common proxies
• Challenge
• And to have a little fun
WHAT IS A PROXY?
var awesmeoThing = function(){
…
}
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
called awesomeoThing <Any Date> with [X,Y, Z] params executed in .00000001 ms
var awesomeoThing = function(x,y,z){
var startTime = new Date();
…
var endTime = new Date();
console.log(‘called awesomeoThing‘ + new Date()
+ ’ with params:’
+ [x,y,z]
+ ’ executed in:’
+ endTime-startTime);
…
}
Public class AnyClass{
Public void awesmeoThing(int x,int y,int z){
…
}
}
@Component
@Aspect
public class MyLogger{
@Around("execution(* com.my.messaging..*.*(..))")
public Object logAfterMethod(ProceedingJoinPoint joinPoint) throws Throwable {
Logger log = Logger.getLogger(joinPoint.getTarget().getClass());
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object retVal = joinPoint.proceed();
stopWatch.stop();
….
System.out.println(“called: “
+ joinPoint.getTarget().getClass().getName()
+ “ with args: “
+ args
+ “executed in: “
+ stopWatch.getTotalTimeMillis());
}
}
OK…SO WHAT ARE
PROXIES?
And what happened to the JavaScript?
Var wrapper = function(){
var startTime = new Date();
var result = awesmeoThing();
var endTime = new Date();
console.log(‘called awesomeoThing‘
+ new Date()
+ ’ with params:’
+ [x,y,z]
+ ’ executed in:’
+ endTime-startTime);
return result;
}
var wrapper = function(){
var startTime = new Date();
var result = awesmeoThing();
var endTime = new Date();
console.log(‘called awesomeoThing‘
+ new Date()
+ ’ with params:’
+ x+y+z
+ ’ executed in:’
+ endTime-startTime);
return result;
}
var awesomeoThing = function(x,y,z){
var startTime = new Date();
…
var endTime = new Date();
console.log(‘called awesomeoThing‘
+ new Date()
+ ’ with params:’
+ x+y+z
+ ’ executed in:’
+ endTime-startTime);
…
}
WHAT’S OUT THERE
TODAY?
• Hooker by Ben Alman (Cowboy) (Author and
main contributor of grunt)
• “Monkey-Patch(hook) functions for debugging
and stuff” – Github Readme
• Hooks by Brian Noguchi(bnoguchi)
• “Add pre and post middleware hooks to your
JavaScript methods.”
HOOKER
hooker.hook(Math, "max", function() {
console.log(arguments.length + " arguments passed");
});
// logs: "3 arguments passed"
Math.max(5, 6, 7)
HOOKJS
• create an instance of the thing
var instance = new awesemoThing();
• assign each member of the hooks implementation into the object
for(var k in hooks){
Instance[k] = hooks[k];
}
• Implement a method to implement before underlying method is called:
instance.pre(‘method’,function(next){
})
• Implement a post method to get called after the underlying method is called:
Instance.post(‘method’,function(next){
})
EXISTING
IMPLEMENTATIONS
• Imbedded in popular projects
• We want to do it
• API and Module design
API FLUENT
INTERFACE
myFunction.do(<SOMETHING>)
.do(<SOMETHING_ELSE>)
.andDoMore();
READABILITY
PERFORMANCE
• Lo-Dash - John-David Dalton
• Best of breed - https://github.com/beastiejs
USE OF CORE
FEATURES
• No pre and post methods. Use real events
LACK OF ABILITY TO USE
COMMON PROXIES
• Easily be able to pull down and
use a logger, security module, etc
CHALLENGE
AND TO HAVE A LITTLE
FUN…TO MAKE MY CODE
PURR
var purr = scarlet.plugins.purr;
purr.when(Math,'min').play();
Math.min(1,2,3);
SCARLETJS -
THE PROJECT
TESTING
• Tools Used
• Mocha
• Builders
ScarletBuilder.forInstance(<AnyInstance>)
ScarletBuilder.forProperty(<AnyProperty>)
.withInterceptors(<ArrayOfInterceptors>);
ScarletBuilder.forMethod(<AnyMethod>)
.withParameters(<AnyParams>)
.withEvents(<AnyEvents>)
.Assert();
MAKING IT EASY
• Intercept a function, object, or property
• Define event listeners for before and after the intercepted function had been called
• Define multiple functions to do the interception
• Being able to chain method calls together and have a fluent interface
Scarlet.intercept(<ANYTHING>)
Scarlet.intercept(<ANYTHING>)
.on(‘before’,<BEFORE_FUNC>)
.on(‘after’,<AFTER_FUNC>)
.on(‘done’,<DONE_FUNC>)
.on(‘error’,<ERROR_FUNC>);
Scarlet.intercept(<ANYFUNCTION>)
.using(<INTERCEPTOR_FUNC)
Scarlet.intercept(<ANYFUNCTION>)
.using(function(proceed){
//…BEFORE
proceed();
//…AFTER
});
Scarlet.intercept(<ANYFUNCTION>)
.using(function(info, proceed){
//…BEFORE
proceed();
//…AFTER
});
var someProxy = Scarlet.intercept(<ANYFUNCTION>)
.using(<INTERCEPTOR_FUNCTION>)
.proxy();
Var awesemeoThing = function(){}
var someProxy = Scarlet.intercept(awesemeoThing)
.using(function(info, proceed){
var startTime = new Date();
var result = proceed();
var endTime = new Date();
console.log(‘called ‘ +info.methodName
+ new Date()
+ ’ with params:’
+ [x,y,z]
+ ’ executed in:’
+ endTime-startTime);
})
.proxy();
PERFORMANCE
PLUGINS
• Scarlet Passport
• Scarlet-winston
• Scarlet-ioc
• Scarlet-init
var requestListener = scarletPassport
.scarletPassport
.authenticate('local',
{},
requestListener)
scarletWinston.bindTo(Math,'min');
Math.min(1,2,3);
//->info: [Mon Sep 02 2013 00:49:58 GMT+0100 (BST)] calling -
Object::min(1,2,3)
//->info: [Mon Sep 02 2013 00:49:58 GMT+0100 (BST)]
Object::min(1,2,3) - returned:1 - execution time(0:0:0.0)
function MyObjectB(myObjectA){
};
scarlet.plugins.ioc
.register("myObjectA", MyObjectA)
.register("myObjectB", MyObjectB);
var myObjectB = scarlet
.plugins
.ioc
.resolve("myObjectB");
myObjectB.anyMethod();
ES6 - PROXIES
”A proxy object is an exotic object whose essential
internal methods are partially implemented using ECMAScript
code” – ES6 Draft October 2014
var awesemeoFunction = function(){…};
var proxiedObject = new Proxy(awesemeoFunction,{
apply: function(target, receiver, args){
var startTime = new Date();
var result = target(args);
var endTime = new Date();
console.log(‘called awesomeThing‘ + new Date()
+ ’ with params:’
+ args
+ ’ executed in:’
+ endTime-startTime);
return result;
}
});
var awesemeoThing= {};
var proxiedThing = new Proxy(awesemeoThing,{
get: function(target, name){
console.log("In Get Proxy");
return target[name];
}
});
var x = proxiedThing.any;
var awesemeoThing= {};
var proxiedThing = new Proxy(awesemeoThing,{
set: function(target, name, value){
console.log("In Set Proxy");
target[name] = value;
}
});
proxiedThing.any = 'thing';
NODE.JS PROXIES
• Proxies can be enabled using the following commands:
• $ node –harmony-proxies
• $ node --harmony
HARMONY-REFLECT
• Shim so that the current draft implementation of proxies can be used https://github.com/tvcutsem/harmony-reflect
1. Install the module:
• npm install harmony-reflect
2. Require at the top of your js file:
• require(‘harmony-reflect);
3. Run node in harmony mode:
• $ node --harmony
FIREFOX
• Available in versions 18+
PROXIES IN USE –
NEGATIVE ARRAY
var negativeArray = require('negative-array');
var unicorn = negativeArray(['pony',
'cake',
'rainbow']);
console.log(unicorn[-1]);
module.exports = function (arr) {
…
return new Proxy(arr, {
get: function (target, name) {
var i = +name;
return target[i < 0 ?
target.length + i :
i];
},
}
ANNOTATIONS
• Traceur – Converts ES6 to ES5 runnable code
• https://github.com/google/traceur-compiler
• Install through npm
• Npm install traceur
• Allows for annotations to be used with the following flags:
• traceur <ANY_FILE> --annotations true
@AnyAnnotation
function simpleFunction () {
return 'any';
}
…
var AnyAnnotation = function() {};
function simpleFunction() {
return 'any';
}
Object.defineProperty(simpleFunction,
"annotations",
{
get: function() {
return [new AnyAnnotation];
}
});
Console.log(simpleFunction.annotations)
//-> [function AnyAnnotation(){}]
LOGGER
ANNOTATIONS
@Logger
function simpleFunction () {
return 'any';
}
var Logger = function(){
return function( target, receiver, args) {
var startTime = new Date();
var result = target(args);
var endTime = new Date();
console.log(‘called ‘ + target.name + new Date()
+ ’ with params:’
+ args
+ ’ executed in:’
+ endTime-startTime);
return result;
};
};
var createProxy = function(anyFunction){
return new Proxy(anyFunction,
{
apply : anyFunction. annotations[0]
});
};
simpleFunction = createProxy(simpleFunction);
simpleFunction();
called simpleFunction Thu Oct 23 2014
23:33:02 GMT+0100 (GMT Daylight Time)
with params: executed in:0
THE FUTURE FOR
SCARLETJS
THANK YOU
• @timchaplin
• https://github.com/tjchaplin
REFERENCES
• http://soft.vub.ac.be/~tvcutsem/invokedynamic/proxies_tutorial
• https://brendaneich.com/2012/10/harmony-of-dreams-come-true/
• http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts
• http://dailyjs.com/2013/11/15/negative-array/
• https://github.com/sindresorhus/negative-array/blob/master/package.json
• https://github.com/tvcutsem/proxy-handlers
• https://github.com/JustinDrake/node-es6-examples#proxies