Skip to content

Commit

Permalink
See #108. Implement proxy.advise for base object proxies, port connec…
Browse files Browse the repository at this point in the history
…tion facets to use proxy.advise where appropriate
  • Loading branch information
briancavalier committed Jul 2, 2013
1 parent 4ac04c9 commit 6953041
Show file tree
Hide file tree
Showing 25 changed files with 743 additions and 763 deletions.
137 changes: 64 additions & 73 deletions aop.js
Expand Up @@ -67,11 +67,11 @@ define(function(require) {
// Simple advice
//

function addSingleAdvice(addAdviceFunc, advices, proxy, advice, options, wire) {
function addSingleAdvice(addAdviceFunc, proxy, advice, options, wire, advices) {

function handleAopConnection(srcObject, srcMethod, adviceHandler) {
checkAdvisable(srcObject, srcMethod);
advices.push(addAdviceFunc(srcObject, srcMethod, adviceHandler));
function handleAopConnection(srcProxy, srcMethod, adviceHandler) {
checkAdvisable(srcProxy.target, srcMethod);
advices.push(addAdviceFunc(srcProxy, srcMethod, adviceHandler));
}

return connection.parse(proxy, advice, options, wire, handleAopConnection);
Expand All @@ -84,30 +84,38 @@ define(function(require) {
}

function makeSingleAdviceAdd(adviceType) {
return function (source, sourceMethod, advice) {
return meld[adviceType](source, sourceMethod, advice);
return function (srcProxy, sourceMethod, advice) {
var aspect = {};
aspect[adviceType] = advice;
return srcProxy.advise(sourceMethod, aspect);
};
}

function addAfterFulfillingAdvice(source, sourceMethod, advice) {
return meld.afterReturning(source, sourceMethod, function(promise) {
return when(promise, advice);
function addAfterFulfillingAdvice(srcProxy, sourceMethod, advice) {
return srcProxy.advise(sourceMethod, {
afterReturning: function(promise) {
return when(promise, advice);
}
});
}

function addAfterRejectingAdvice(source, sourceMethod, advice) {
return meld.afterReturning(source, sourceMethod, function(promise) {
return when(promise, null, advice);
function addAfterRejectingAdvice(srcProxy, sourceMethod, advice) {
return srcProxy.advise(sourceMethod, {
afterReturning: function(promise) {
return when(promise, null, advice);
}
});
}

function addAfterPromiseAdvice(source, sourceMethod, advice) {
return meld.after(source, sourceMethod, function(promise) {
return when(promise, advice, advice);
function addAfterPromiseAdvice(srcProxy, sourceMethod, advice) {
return srcProxy.advise(sourceMethod, {
after: function(promise) {
return when(promise, advice, advice);
}
});
}

function makeAdviceFacet(addAdviceFunc, advices) {
function makeAdviceFacet(advices, addAdviceFunc) {
return function(resolver, facet, wire) {
var advice, target, advicesToAdd, promises;

Expand All @@ -116,8 +124,8 @@ define(function(require) {
promises = [];

for(advice in advicesToAdd) {
promises.push(addSingleAdvice(addAdviceFunc, advices,
target, advice, advicesToAdd[advice], wire));
promises.push(addSingleAdvice(addAdviceFunc,
target, advice, advicesToAdd[advice], wire, advices));
}

resolver.resolve(when.all(promises));
Expand All @@ -128,28 +136,28 @@ define(function(require) {
// Aspect Weaving
//

function applyAspectCombined(target, aspect, wire, add) {
function applyAspectCombined(targetProxy, aspect, wire, aspects) {
return when(wire.resolveRef(aspect), function (aspect) {
var pointcut = aspect.pointcut;

if (pointcut) {
add(target, pointcut, aspect);
aspects.push(targetProxy.advise(pointcut, aspect));
}

return target;
return targetProxy;
});
}

function applyAspectSeparate(target, aspect, wire, add) {
function applyAspectSeparate(targetProxy, aspect, wire, aspects) {
var pointcut, advice;

pointcut = aspect.pointcut;
advice = aspect.advice;

function applyAdvice(pointcut) {
return when(wire.resolveRef(advice), function (aspect) {
add(target, pointcut, aspect);
return target;
aspects.push(targetProxy.advise(pointcut, aspect));
return targetProxy;
});
}

Expand All @@ -158,24 +166,23 @@ define(function(require) {
: applyAdvice(pointcut);
}

function weave(resolver, proxy, wire, options, add) {
function weave(proxy, wire, options, wovenAspects) {
// TODO: Refactor weaving to use proxy.invoke

var target, path, aspects, applyAdvice;

aspects = options.aspects;
path = proxy.path;

if (!aspects || path === undef) {
resolver.resolve();
if (path === undef) {
return;
}

target = proxy.target;
applyAdvice = applyAspectCombined;

// Reduce will preserve order of aspects being applied
resolver.resolve(when.reduce(aspects, function(target, aspect) {
return when.reduce(aspects, function(proxy, aspect) {
var aspectPath;

if (aspect.advice) {
Expand All @@ -186,10 +193,10 @@ define(function(require) {
}

return typeof aspectPath === 'string' && aspectPath !== path
? applyAdvice(target, aspect, wire, add)
: target;
? applyAdvice(proxy, aspect, wire, wovenAspects)
: proxy;

}, target));
}, proxy);
}

/**
Expand All @@ -199,67 +206,51 @@ define(function(require) {
*/
return function(options) {

// Track aspects so they can be removed when the context is destroyed
var woven, plugin, i, len, adviceType;

woven = [];

/**
* Function to add an aspect and remember it in the current context
* so that it can be removed when the context is destroyed.
* @param target
* @param pointcut
* @param aspect
*/
function add(target, pointcut, aspect) {
woven.push(meld.add(target, pointcut, aspect));
}

function makeFacet(step, callback) {
var facet = {};
var plugin, aspects, makeAdvice;

facet[step] = function(resolver, proxy, wire) {
callback(resolver, proxy, wire);
};

return facet;
}
aspects = [];
makeAdvice = makeAdviceFacet.bind(null, aspects);

// Plugin
plugin = {
context: {
destroy: function(resolver) {
woven.forEach(function(aspect) {
aspect.remove();
});
connection.removeAll(aspects);
resolver.resolve();
}
},
facets: {
decorate: makeFacet('configure:after', decorateFacet),
afterFulfilling: makeFacet(adviceStep, makeAdviceFacet(addAfterFulfillingAdvice, woven)),
afterRejecting: makeFacet(adviceStep, makeAdviceFacet(addAfterRejectingAdvice, woven)),
after: makeFacet(adviceStep, makeAdviceFacet(addAfterPromiseAdvice, woven))
afterFulfilling: makeFacet(adviceStep, makeAdvice(addAfterFulfillingAdvice)),
afterRejecting: makeFacet(adviceStep, makeAdvice(addAfterRejectingAdvice)),
after: makeFacet(adviceStep, makeAdvice(addAfterPromiseAdvice))
}
};

if(options.aspects) {
plugin.create = function(resolver, proxy, wire) {
weave(resolver, proxy, wire, options, add);
var woven = weave(proxy, wire, options, aspects);
resolver.resolve(woven);
};
}

// Add all regular single advice facets
for(i = 0, len = adviceTypes.length; i<len; i++) {
adviceType = adviceTypes[i];
plugin.facets[adviceType] = makeFacet(adviceStep, makeAdviceFacet(makeSingleAdviceAdd(adviceType), woven));
}
adviceTypes.forEach(function(adviceType) {
plugin.facets[adviceType] = makeFacet(adviceStep,
makeAdvice(makeSingleAdviceAdd(adviceType)));
});

return plugin;
};

function makeFacet(step, callback) {
var facet = {};

facet[step] = function(resolver, proxy, wire) {
callback(resolver, proxy, wire);
};

return facet;
}

};
});
})(typeof define == 'function'
// use define for AMD if available
? define
: function(factory) { module.exports = factory(require); }
);
}(typeof define == 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
43 changes: 19 additions & 24 deletions connect.js
@@ -1,4 +1,4 @@
/** @license MIT License (c) copyright B Cavalier & J Hann */
/** @license MIT License (c) copyright 2011-2013 original author or authors */

/**
* wire/connect plugin
Expand Down Expand Up @@ -38,41 +38,43 @@
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*
* @author Brian Cavalier
* @author John Hann
*/

(function(define) {
define(['when', 'meld', './lib/functional', './lib/connection'],
function(when, meld, functional, connection) {
(function(define) { 'use strict';
define(function(require) {

return function eventsPlugin(/* options */) {
var all, connection;

var connectHandles = [];
all = require('when').all;
connection = require('./lib/connection');

function handleConnection(instance, methodName, handler) {
connectHandles.push(meld.on(instance, methodName, handler));
}
return function connectPlugin(/* options */) {

var connections = [];

function doConnect(proxy, connect, options, wire) {
return connection.parse(proxy, connect, options, wire, handleConnection);
function makeConnection(sourceProxy, methodName, handler) {
connections.push(sourceProxy.advise(methodName, { on: handler }));
}

function connectFacet(wire, facet) {
var promises, connects;

connects = facet.options;
promises = Object.keys(connects).map(function(key) {
return doConnect(facet, key, connects[key], wire);
return connection.parse(
facet, key, connects[key], wire, makeConnection);
});

return when.all(promises);
return all(promises);
}

return {
context: {
destroy: function(resolver) {
connectHandles.forEach(function(handle) {
handle.remove();
});
connection.removeAll(connections);
resolver.resolve();
}
},
Expand All @@ -88,12 +90,5 @@ function(when, meld, functional, connection) {
};
};
});
})(typeof define == 'function'
? define
: function(deps, factory) {
module.exports = factory.apply(this, deps.map(function(x) {
return require(x);
}));
}
);
}(typeof define == 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));

13 changes: 8 additions & 5 deletions dojo/events.js
@@ -1,4 +1,4 @@
/** @license MIT License (c) copyright B Cavalier & J Hann */
/** @license MIT License (c) copyright 2011-2013 original author or authors */

/**
* wire/dojo/events plugin
Expand All @@ -7,22 +7,25 @@
* This implementation uses dojo.connect and dojo.disconnect to do
* the work of connecting and disconnecting event handlers.
*
* wire is part of the cujo.js family of libraries (http://cujojs.com/)
* wire is part of the cujoJS family of libraries (http://cujojs.com/)
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*
* @author Brian Cavalier
* @author John Hann
*/

define(['when', '../lib/connection', 'dojo', 'dojo/_base/event'],
function(when, connection, events) {

return {
wire$plugin: function eventsPlugin(/*, options*/) {
wire$plugin: function dojoEventsPlugin(/*, options*/) {

var connectHandles = [];

function handleConnection(source, eventName, handler) {
connectHandles.push(events.connect(source, eventName, handler));
function handleConnection(sourceProxy, eventName, handler) {
connectHandles.push(events.connect(sourceProxy.target, eventName, handler));
}

function connect(source, connect, options, wire) {
Expand Down

0 comments on commit 6953041

Please sign in to comment.