Fullstack Conference - Proxies before proxies: The hidden gems of Javascript AOP

Preview:

DESCRIPTION

Tired of console.logging your way through applications? Want a way to slice through your application without adding complexity? AOP has been the answer to these questions for object oriented languages, such as Java and C#, but is not available in Javascript. ScarletJS(https://github.com/scarletjs/scarlet) is a project that tackles AOP using a clean, fluent, performant interface. The ScarletJS project provides Javascript developers a different way of thinking about traditional javascript problems. The project is still growing and looking into the future of what ES6 proxies will open up to the Javascript community. The talk will highlight the problems that javascript developers face with logging application behavior, security, and more. It will discuss the benefits of identifying a cross cutting concern, and programming using aspects. The talk will highlight how thinking about a project and cross cutting concerns can lead to cleaner more SOLID code. It will also discuss the future of ES6 proxies and the benefits that they will bring.

Citation preview

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