Upload
tim-chaplin
View
407
Download
0
Embed Size (px)
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