Skip to content
Browse files

See #108. Implement proxy.advise for base object proxies, port connec…

…tion facets to use proxy.advise where appropriate
  • Loading branch information...
1 parent 4ac04c9 commit 6953041def03c41792ed3b64dc027f5a745c0bbc @briancavalier briancavalier committed Jul 2, 2013
View
137 aop.js
@@ -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);
@@ -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;
@@ -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));
@@ -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;
});
}
@@ -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) {
@@ -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);
}
/**
@@ -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); }));
View
43 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
@@ -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();
}
},
@@ -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); }));
View
13 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
@@ -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) {
View
17 lib/ComponentFactory.js
@@ -11,11 +11,12 @@
(function(define) { 'use strict';
define(function(require) {
- var when, object, WireProxy, undef;
+ var when, object, WireProxy, ObjectProxy, undef;
when = require('when');
object = require('./object');
WireProxy = require('./WireProxy');
+ ObjectProxy = require('./ObjectProxy');
function ComponentFactory(lifecycle, plugins, pluginApi) {
this.plugins = plugins;
@@ -60,7 +61,7 @@ define(function(require) {
function(proxy) {
return self.startupInstance(proxy);
}
- ).then(WireProxy.getTarget);
+ );
},
initInstance: function(proxy) {
@@ -78,15 +79,20 @@ define(function(require) {
proxy = instance;
instance = WireProxy.getTarget(proxy);
} else {
- proxy = WireProxy.create(instance);
+ proxy = new ObjectProxy(instance);
}
+ proxy = this.initProxy(proxy);
+
if(component) {
+ component.proxy = proxy;
proxy.id = component.id;
proxy.metadata = component;
}
- return this.initProxy(proxy);
+ this._registerProxy(proxy);
+
+ return proxy;
},
initProxy: function(proxy) {
@@ -102,10 +108,7 @@ define(function(require) {
proxy
);
- this._registerProxy(proxy);
-
return proxy;
-
},
destroy: function() {
View
57 lib/Container.js
@@ -8,11 +8,13 @@
(function(define){ 'use strict';
define(function(require) {
- var when, advice, WireContext, Scope, PluginRegistry, defaultPlugins,
+ var when, advice, object, WireContext, Scope,
+ PluginRegistry, defaultPlugins,
DirectedGraph, trackInflightRefs, slice, scopeProto, undef;
when = require('when');
advice = require('./advice');
+ object = require('./object');
WireContext = require('./WireContext');
Scope = require('./scope');
PluginRegistry = require('./plugin/registry');
@@ -31,40 +33,40 @@ define(function(require) {
* Container inherits from Scope, adding plugin support and
* context level events.
*/
- Container.prototype = Object.create(scopeProto, {
- _inheritInstances: { value: function(parent) {
+ Container.prototype = object.extend(scopeProto, {
+ _inheritInstances: function(parent) {
var publicApi = {
wire: this._createChild.bind(this),
destroy: this.destroy.bind(this),
resolve: this._resolveRef.bind(this)
};
return WireContext.inherit(parent.instances, publicApi);
- }},
+ },
- _init: { value: advice.after(
+ _init: advice.after(
scopeProto._init,
function() {
this.plugins = new PluginRegistry();
return this._installDefaultPlugins();
}
- )},
+ ),
- _startup: { value: advice.after(
+ _startup: advice.after(
scopeProto._startup,
function(started) {
var self = this;
return when(started).otherwise(function(e) {
return self._contextEvent('error', e).yield(started);
});
}
- )},
+ ),
- _installDefaultPlugins: { value: function() {
+ _installDefaultPlugins: function() {
return this._installPlugins(defaultPlugins);
- }},
+ },
- _installPlugins: { value: function(plugins) {
+ _installPlugins: function(plugins) {
if(!plugins) {
return when.resolve();
}
@@ -103,17 +105,17 @@ define(function(require) {
return registry.scanModule(plugin, pluginSpec, namespace);
});
}
- }},
+ },
- _createResolver: { value: advice.after(
+ _createResolver: advice.after(
scopeProto._createResolver,
function(resolver) {
return trackInflightRefs(
new DirectedGraph(), resolver, this.refCycleTimeout);
}
- )},
+ ),
- _contextEvent: { value: function (type, data) {
+ _contextEvent: function (type, data) {
var api, listeners;
if(!this.contextEventApi) {
@@ -134,38 +136,39 @@ define(function(require) {
return undef;
}, undef);
- }},
+ },
- _createComponents: { value: advice.beforeAsync(
+ _createComponents: advice.beforeAsync(
scopeProto._createComponents,
function(parsed) {
var self = this;
- return this._installPlugins(parsed.plugins).then(function() {
- return self._contextEvent('initialize');
- });
+ return this._installPlugins(parsed.plugins)
+ .then(function() {
+ return self._contextEvent('initialize');
+ });
}
- )},
+ ),
- _awaitInstances: { value: advice.afterAsync(
+ _awaitInstances: advice.afterAsync(
scopeProto._awaitInstances,
function() {
return this._contextEvent('ready');
}
- )},
+ ),
- _destroyComponents: { value: advice.beforeAsync(
+ _destroyComponents: advice.beforeAsync(
scopeProto._destroyComponents,
function() {
return this._contextEvent('shutdown');
}
- )},
+ ),
- _releaseResources: { value: advice.beforeAsync(
+ _releaseResources: advice.beforeAsync(
scopeProto._releaseResources,
function() {
return this._contextEvent('destroy');
}
- )}
+ )
});
return Container;
View
51 lib/ObjectProxy.js
@@ -0,0 +1,51 @@
+/** @license MIT License (c) copyright 2010-2013 original author or authors */
+
+/**
+ * Licensed under the MIT License at:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * @author: Brian Cavalier
+ * @author: John Hann
+ */
+
+(function(define) { 'use strict';
+define(function(require) {
+
+ var WireProxy, extend, before, meld, advise, superDestroy;
+
+ WireProxy = require('./WireProxy');
+ extend = require('./object').extend;
+ before = require('./advice').before;
+ meld = require('meld');
+
+ // FIXME: Remove support for meld.add after deprecation period
+ advise = typeof meld === 'function' ? meld : meld.add;
+
+ superDestroy = WireProxy.prototype.destroy;
+
+ function ObjectProxy(target) {
+ WireProxy.apply(this, arguments);
+ }
+
+ ObjectProxy.prototype = extend(WireProxy.prototype, {
+ /**
+ * Add an aspect to the proxy's target. Sub-types should
+ * override to add aspects in whatever specialized way is
+ * necessary.
+ * @param {String|Array|RegExp|Function} pointcut
+ * expression matching methods to be advised
+ * @param {Object} aspect aspect to add
+ * @returns {{remove:function}} object with remove() that
+ * will remove the aspect.
+ */
+ advise: function(pointcut, aspect) {
+ return advise(this.target, pointcut, aspect);
+ }
+
+
+ });
+
+ return ObjectProxy;
+
+});
+}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
View
26 lib/WireProxy.js
@@ -72,6 +72,20 @@ define(function(require) {
},
/**
+ * Add an aspect to the proxy's target. Sub-types should
+ * override to add aspects in whatever specialized way is
+ * necessary.
+ * @param {String|Array|RegExp|Function} pointcut
+ * expression matching methods to be advised
+ * @param {Object} aspect aspect to add
+ * @returns {{remove:function}} object with remove() that
+ * will remove the aspect.
+ */
+ advise: function(pointcut, aspect) {
+ throw new TypeError('Advice not supported on component type: ' + this.target);
+ },
+
+ /**
* Destroy the proxy's target. Sub-types should override
* to destroy their targets in whatever specialized way is
* necessary.
@@ -103,23 +117,13 @@ define(function(require) {
}
};
- WireProxy.create = createProxy;
WireProxy.isProxy = isProxy;
WireProxy.getTarget = getTarget;
WireProxy.extend = extendProxy;
return WireProxy;
/**
- * Creates a new WireProxy for the supplied target. See WireProxy
- * @param {*} target value to be proxied
- * @returns {WireProxy}
- */
- function createProxy(target) {
- return new WireProxy(target);
- }
-
- /**
* Returns a new WireProxy, whose prototype is proxy, with extensions
* as own properties. This is the "official" way to extend the functionality
* of an existing WireProxy.
@@ -132,7 +136,7 @@ define(function(require) {
throw new Error('Cannot extend non-WireProxy');
}
- return object.mixin(Object.create(proxy), extensions);
+ return object.extend(proxy, extensions);
}
/**
View
15 lib/advice.js
@@ -19,12 +19,27 @@ define(function(require) {
// This is NOT a replacement for meld. These advices stack
// differently and will not be as efficient.
return {
+ before: before,
after: after,
beforeAsync: beforeAsync,
afterAsync: afterAsync
};
/**
+ * Execute advice before f, passing same arguments to both, and
+ * discarding advice's return value.
+ * @param {function} f function to advise
+ * @param {function} advice function to execute before f
+ * @returns {function} advised function
+ */
+ function before(f, advice) {
+ return function() {
+ advice.apply(this, arguments);
+ return f.apply(this, arguments);
+ }
+ }
+
+ /**
* Execute advice after f, passing f's return value to advice
* @param {function} f function to advise
* @param {function} advice function to execute after f
View
62 lib/connection.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 */
/**
* Licensed under the MIT License at:
@@ -27,23 +27,32 @@
* ...
* }
*
+ * @author Brian Cavalier
+ * @author John Hann
*/
(function(define){ 'use strict';
define(function(require) {
- var when, array, functional;
+ var when, array, pipeline;
when = require('when');
array = require('./array');
- functional = require('./functional');
+ pipeline = require('./pipeline');
return {
parse: parse,
parseIncoming: parseIncoming,
- parseOutgoing: parseOutgoing
+ parseOutgoing: parseOutgoing,
+ removeAll: removeAll
};
+ function removeAll(connections) {
+ connections.forEach(function(c) {
+ c.remove();
+ });
+ }
+
/**
* Determines if the connections are incoming or outgoing, and invokes parseIncoming
* or parseOutgoing accordingly.
@@ -69,9 +78,9 @@ define(function(require) {
eventName = source[1];
source = source[0];
- return when(wire.resolveRef(source),
- function(source) {
- return parseIncoming(source, eventName, proxy, connect, options, wire, createConnection);
+ return when(wire.getProxy(source),
+ function(srcProxy) {
+ return parseIncoming(srcProxy, eventName, proxy, connect, options, wire, createConnection);
},
function() {
return parseOutgoing(proxy, connect, options, wire, createConnection);
@@ -103,9 +112,11 @@ define(function(require) {
methodName = options;
- promise = when(functional.compose.parse(targetProxy, methodName, wire),
+ promise = pipeline(targetProxy, methodName, wire).then(
function(func) {
- return createConnection(source, eventName, proxyInvoker(targetProxy, func));
+ var invoker = proxyInvoker(targetProxy, func);
+
+ return createConnection(source, eventName, invoker);
}
);
@@ -116,20 +127,25 @@ define(function(require) {
// }
source = methodName;
- promise = when(wire.resolveRef(connect), function(source) {
+ promise = wire.getProxy(connect).then(function(srcProxy) {
var name, promises;
- function createConnectionFactory(source, name, targetProxy) {
+ function createConnectionFactory(srcProxy, name, targetProxy) {
return function(func) {
- return createConnection(source, name, proxyInvoker(targetProxy, func));
+ var invoker = proxyInvoker(targetProxy, func);
+
+ return createConnection(srcProxy, name, invoker);
};
}
promises = [];
for(name in options) {
- promises.push(when(functional.compose.parse(targetProxy, options[name], wire),
- createConnectionFactory(source, name, targetProxy)
- ));
+ var connectionFactory, composed;
+
+ connectionFactory = createConnectionFactory(srcProxy, name, targetProxy);
+ composed = pipeline(targetProxy, options[name], wire);
+
+ promises.push(composed.then(connectionFactory));
}
return when.all(promises);
@@ -154,16 +170,17 @@ define(function(require) {
* rejects if an error occurs.
*/
function parseOutgoing(proxy, connect, options, wire, createConnection) {
- return createOutgoing(proxy.target, connect, proxy, connect, options, wire, createConnection);
+ return createOutgoing(proxy, connect, proxy, options, wire, createConnection);
}
- function createOutgoing(source, eventName, targetProxy, connect, options, wire, createConnection) {
+ function createOutgoing(sourceProxy, eventName, targetProxy, options, wire, createConnection) {
var promise, promises, resolveAndConnectOneOutgoing, name;
function connectOneOutgoing(targetProxy, targetMethodSpec) {
- return when(functional.compose.parse(targetProxy, targetMethodSpec, wire),
+ return when(pipeline(targetProxy, targetMethodSpec, wire),
function(func) {
- return createConnection(source, eventName, proxyInvoker(targetProxy, func));
+ var invoker = proxyInvoker(targetProxy, func);
+ return createConnection(sourceProxy, eventName, invoker);
});
}
@@ -202,9 +219,4 @@ define(function(require) {
}
});
-})(typeof define == 'function'
- // AMD
- ? define
- // CommonJS
- : function(factory) { module.exports = factory(require); }
-);
+}(typeof define == 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
View
26 lib/context.js
@@ -8,8 +8,10 @@
(function(define){ 'use strict';
define(function(require) {
- var loader, Container;
+ var when, mixin, loader, Container;
+ when = require('when');
+ mixin = require('./object').mixin;
loader = require('./loader');
Container = require('./Container');
@@ -37,15 +39,31 @@ define(function(require) {
var moduleLoader = loader(options.require, parent.moduleLoader);
- return moduleLoader.merge(specs).then(function(spec) {
+ return mergeSpecs(moduleLoader, specs).then(function(spec) {
var container = new Container(parent, options);
// Expose only the component instances and controlled API
- return container.init(spec).then(function(self) {
- return self.instances;
+ return container.init(spec).then(function(context) {
+ return context.instances;
});
});
};
+ function mergeSpecs(moduleLoader, specs) {
+ return when(specs, function(specs) {
+ return when.resolve(Array.isArray(specs)
+ ? mergeAll(moduleLoader, specs)
+ : (typeof specs === 'string' ? moduleLoader(specs) : specs));
+ });
+ }
+
+ function mergeAll(moduleLoader, specs) {
+ return when.reduce(specs, function(merged, module) {
+ return typeof module == 'string'
+ ? when(moduleLoader(module), function(spec) { return mixin(merged, spec); })
+ : mixin(merged, module);
+ }, {});
+ }
+
});
}(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
View
82 lib/functional.js
@@ -45,7 +45,9 @@ define(function (require) {
}
/**
- * Compose functions
+ * Promise-aware function composition. If any function in
+ * the composition returns a promise, the entire composition
+ * will be lifted to return a promise.
* @param funcs {Array} array of functions to compose
* @return {Function} composed function
*/
@@ -65,84 +67,6 @@ define(function (require) {
};
}
- /**
- * Parses the function composition string, resolving references as needed, and
- * composes a function from the resolved refs.
- * @param proxy {Object} wire proxy on which to invoke the final method of the composition
- * @param composeString {String} function composition string
- * of the form: 'transform1 | transform2 | ... | methodOnProxyTarget"
- * @param {function} wire
- * @param {function} wire.resolveRef function to use is resolving references, returns a promise
- * @param {function} wire.getProxy function used to obtain a proxy for a component
- * @return {Promise} a promise for the composed function
- */
- compose.parse = function parseCompose(proxy, composeString, wire) {
-
- var bindSpecs, resolveRef, getProxy;
-
- if(typeof composeString != 'string') {
- return wire(composeString).then(function(func) {
- return createProxyInvoker(proxy, func);
- });
- }
-
- bindSpecs = composeString.split(/\s*\|\s*/);
- resolveRef = wire.resolveRef;
- getProxy = wire.getProxy;
-
- function createProxyInvoker(proxy, method) {
- return function() {
- return proxy.invoke(method, arguments);
- };
- }
-
- function createBound(proxy, bindSpec) {
- var target, method;
-
- target = bindSpec.split('.');
-
- if(target.length > 2) {
- throw new Error('Only 1 "." is allowed in refs: ' + bindSpec);
- }
-
- if(target.length > 1) {
- method = target[1];
- target = target[0];
- if(!target) {
- return function(target) {
- return target[method].apply(target, slice.call(arguments, 1));
- };
- }
- return when(getProxy(target), function(proxy) {
- return createProxyInvoker(proxy, method);
- });
- } else {
- if(proxy && typeof proxy.get(bindSpec) == 'function') {
- return createProxyInvoker(proxy, bindSpec);
- } else {
- return resolveRef(bindSpec);
- }
- }
-
- }
-
- // First, resolve each transform function, stuffing it into an array
- // The result of this reduce will an array of concrete functions
- // Then add the final context[method] to the array of funcs and
- // return the composition.
- return when.reduce(bindSpecs, function(funcs, bindSpec) {
- return when(createBound(proxy, bindSpec), function(func) {
- funcs.push(func);
- return funcs;
- });
- }, []).then(
- function(funcs) {
- var context = proxy && proxy.target;
- return (funcs.length == 1 ? funcs[0] : compose(funcs)).bind(context);
- }
- );
- };
-
function conditionalWhen(promiseOrValue, onFulfill, onReject) {
return when.isPromise(promiseOrValue)
? when(promiseOrValue, onFulfill, onReject)
View
19 lib/loader.js
@@ -49,24 +49,7 @@ define(function(require) {
? wrapPlatformLoader(platformLoader)
: parentLoader || wrapPlatformLoader(require);
- return {
- load: loadModule,
- merge: function(specs) {
- return when(specs, function(specs) {
- return when.resolve(Array.isArray(specs)
- ? mergeAll(specs, loadModule)
- : (typeof specs === 'string' ? loadModule(specs) : specs));
- });
- }
- };
- }
-
- function mergeAll(specs, loadModule) {
- return when.reduce(specs, function(merged, module) {
- return typeof module == 'string'
- ? when(loadModule(module), function(spec) { return mixin(merged, spec); })
- : mixin(merged, module);
- }, {});
+ return loadModule;
}
});
View
14 lib/object.js
@@ -16,7 +16,8 @@ define(function() {
hasOwn: hasOwn,
isObject: isObject,
inherit: inherit,
- mixin: mixin
+ mixin: mixin,
+ extend: extend
};
function isObject(it) {
@@ -48,6 +49,17 @@ define(function() {
}, to);
}
+ /**
+ * Beget a new object from base and then mixin own properties from
+ * extensions. Equivalent to mixin(inherit(base), extensions)
+ * @param {object} base
+ * @param {object} extensions
+ * @returns {object}
+ */
+ function extend(base, extensions) {
+ return mixin(inherit(base), extensions);
+ }
+
});
})(typeof define == 'function'
// AMD
View
88 lib/pipeline.js
@@ -0,0 +1,88 @@
+/** @license MIT License (c) copyright 2010-2013 original author or authors */
+
+/**
+ * Licensed under the MIT License at:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * @author: Brian Cavalier
+ * @author: John Hann
+ */
+
+(function(define) { 'use strict';
+define(function(require) {
+
+ var when, compose, pipelineSplitRx;
+
+ when = require('when');
+ compose = require('./functional').compose;
+ pipelineSplitRx = /\s*\|\s*/;
+
+ return function pipeline(proxy, composeString, wire) {
+
+ var bindSpecs, resolveRef, getProxy;
+
+ if(typeof composeString != 'string') {
+ return wire(composeString).then(function(func) {
+ return createProxyInvoker(proxy, func);
+ });
+ }
+
+ bindSpecs = composeString.split(pipelineSplitRx);
+ resolveRef = wire.resolveRef;
+ getProxy = wire.getProxy;
+
+ function createProxyInvoker(proxy, method) {
+ return function() {
+ return proxy.invoke(method, arguments);
+ };
+ }
+
+ function createBound(proxy, bindSpec) {
+ var target, method;
+
+ target = bindSpec.split('.');
+
+ if(target.length > 2) {
+ throw new Error('Only 1 "." is allowed in refs: ' + bindSpec);
+ }
+
+ if(target.length > 1) {
+ method = target[1];
+ target = target[0];
+ if(!target) {
+ return function(target) {
+ return target[method].apply(target, slice.call(arguments, 1));
+ };
+ }
+ return when(getProxy(target), function(proxy) {
+ return createProxyInvoker(proxy, method);
+ });
+ } else {
+ if(proxy && typeof proxy.get(bindSpec) == 'function') {
+ return createProxyInvoker(proxy, bindSpec);
+ } else {
+ return resolveRef(bindSpec);
+ }
+ }
+
+ }
+
+ // First, resolve each transform function, stuffing it into an array
+ // The result of this reduce will an array of concrete functions
+ // Then add the final context[method] to the array of funcs and
+ // return the composition.
+ return when.reduce(bindSpecs, function(funcs, bindSpec) {
+ return when(createBound(proxy, bindSpec), function(func) {
+ funcs.push(func);
+ return funcs;
+ });
+ }, []).then(
+ function(funcs) {
+ var context = proxy && proxy.target;
+ return (funcs.length == 1 ? funcs[0] : compose(funcs)).bind(context);
+ }
+ );
+ };
+
+});
+}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
View
45 lib/plugin-base/on.js
@@ -9,11 +9,16 @@
* http://www.opensource.org/licenses/mit-license.php
*/
(function (define) {
-define(['when', 'when/apply', '../functional', '../connection'],
-function (when, apply, functional, connection) {
-"use strict";
+define(function (require) {
+'use strict';
- var theseAreNotEvents, thisLooksLikeCssRx, eventSplitterRx, undef;
+ var when, apply, functional, connection,
+ theseAreNotEvents, thisLooksLikeCssRx, eventSplitterRx, undef;
+
+ when = require('when');
+ apply = require('when/apply');
+ functional = require('../functional');
+ connection = require('../connection');
theseAreNotEvents = {
selector: 1,
@@ -38,19 +43,20 @@ function (when, apply, functional, connection) {
options = {};
}
- function createConnection(source, eventsString, handler) {
- var events, prevent, stop;
+ function createConnection(nodeProxy, eventsString, handler) {
+ var events, node, prevent, stop;
events = splitEventSelectorString(eventsString);
+ node = nodeProxy.target;
prevent = options.preventDefault;
stop = options.stopPropagation;
removers = removers.concat(
- registerHandlers(events, source, handler, prevent, stop)
+ registerHandlers(events, node, handler, prevent, stop)
);
}
- function parseIncomingOn(source, targetProxy, connections, wire) {
+ function parseIncomingOn(srcProxy, targetProxy, connections, wire) {
// NOTE: Custom parsing for incoming connections
@@ -84,9 +90,14 @@ function (when, apply, functional, connection) {
*/
function createTransformedConnection(events, targetMethod, transformPromise) {
return when(transformPromise, function(transform) {
- var composed = functional.compose([transform, targetMethod]).bind(targetProxy.target);
+ var composed, node;
+
+ node = srcProxy.target;
+ composed = functional.compose([transform, targetMethod]);
+ //.bind(targetProxy.target);
+
removers = removers.concat(
- registerHandlers(events, source, function() {
+ registerHandlers(events, node, function() {
return targetProxy.invoke(composed, arguments);
}, prevent, stop)
);
@@ -104,7 +115,7 @@ function (when, apply, functional, connection) {
method = connections[event];
promises.push(createTransformedConnection(events, target[method], wire(transform)));
} else {
- promises.push(connection.parseIncoming(source, event, targetProxy, options, connections[event], wire, createConnection));
+ promises.push(connection.parseIncoming(srcProxy, event, targetProxy, options, connections[event], wire, createConnection));
}
}
}
@@ -115,10 +126,10 @@ function (when, apply, functional, connection) {
function parseOn (proxy, refName, connections, wire) {
// First, figure out if the left-hand-side is a ref to
// another component, or an event/delegation string
- return when(wire.resolveRef(refName),
- function (source) {
+ return when(wire.getProxy(refName),
+ function (srcProxy) {
// It's an incoming connection, parse it as such
- return parseIncomingOn(source, proxy, connections, wire);
+ return parseIncomingOn(srcProxy, proxy, connections, wire);
},
function () {
// Failed to resolve refName as a reference, assume it
@@ -303,8 +314,4 @@ function (when, apply, functional, connection) {
}
});
-}(
- typeof define == 'function' && define.amd
- ? define
- : function (deps, factory) { module.exports = factory.apply(this, deps.map(require)); }
-));
+}(typeof define == 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
View
5 lib/plugin/basePlugin.js
@@ -13,12 +13,13 @@
(function(define) { 'use strict';
define(function(require) {
- var when, object, functional, instantiate, createInvoker,
+ var when, object, functional, pipeline, instantiate, createInvoker,
whenAll, obj, pluginInstance, undef;
when = require('when');
object = require('../object');
functional = require('../functional');
+ pipeline = require('../pipeline');
instantiate = require('../instantiate');
createInvoker = require('../invoker');
@@ -260,7 +261,7 @@ define(function(require) {
options = componentDef.options;
if(typeof options == 'string') {
- promise = functional.compose.parse(undef, options, wire);
+ promise = pipeline(undef, options, wire);
} else {
// Assume it's an array of things that will wire to functions
promise = when(wire(options), function(funcArray) {
View
39 lib/scope.js
@@ -55,7 +55,7 @@ define(function(require) {
this.initializers = array.delegate(this.initializers);
this.destroyers = array.delegate(this.destroyers);
- this.moduleLoader = loader(this.require, parent.moduleLoader).load;
+ this.moduleLoader = loader(this.require, parent.moduleLoader);
},
_inheritInstances: function(parent) {
@@ -90,7 +90,7 @@ define(function(require) {
pluginApi.contextualize = function(name) {
function contextualApi(spec, id) {
- return self._resolveItem(self._createComponentDef(id, spec));
+ return self._resolveInstance(self._createComponentDef(id, spec));
}
contextualApi.createChild = self._createChild.bind(self);
@@ -168,7 +168,7 @@ define(function(require) {
var instances = this.instances;
return this.componentFactory.destroy().then(function() {
- for (var p in instances) {
+ for (var p in instances) {
delete instances[p];
}
});
@@ -189,12 +189,23 @@ define(function(require) {
},
getProxy: function(nameOrComponent, onBehalfOf) {
- var componentFactory = this.componentFactory;
- return typeof nameOrComponent == 'string'
- ? when(this._resolveRefName(nameOrComponent, {}, onBehalfOf), function (component) {
- return componentFactory.createProxy(component);
- })
- : componentFactory.createProxy(nameOrComponent);
+ var componentFactory, component;
+
+ componentFactory = this.componentFactory;
+
+ if(typeof nameOrComponent === 'string') {
+ component = this.components[nameOrComponent];
+ return this._resolveRefName(nameOrComponent, {}, onBehalfOf)
+ .then(function (instance) {
+ var proxy = component && component.proxy;
+ if(!proxy) {
+ proxy = componentFactory.createProxy(instance);
+ }
+ return proxy;
+ });
+ } else {
+ return componentFactory.createProxy(nameOrComponent);
+ }
},
_createResolver: function(plugins, pluginApi) {
@@ -231,7 +242,7 @@ define(function(require) {
var instances, components, plugins, id, d;
instances = this.instances;
- components = {};
+ components = this.components;
// Setup a promise for each item in this scope
for (id in spec) {
@@ -293,7 +304,7 @@ define(function(require) {
self = this;
item = this._resolveItem(component).then(function (resolved) {
self._makeResolvable(component, resolved);
- return resolved;
+ return WireProxy.getTarget(resolved);
});
component.resolver.resolve(item);
@@ -309,6 +320,10 @@ define(function(require) {
return instance;
},
+ _resolveInstance: function(component) {
+ return this._resolveItem(component).then(WireProxy.getTarget);
+ },
+
_resolveItem: function(component) {
var item, spec;
@@ -356,7 +371,7 @@ define(function(require) {
// Minor optimization, if it's an empty array spec, just return an empty array.
return when.map(component.spec, function(item) {
var componentDef = self._createComponentDef(id + '[' + (i++) + ']', item);
- return self._resolveItem(componentDef);
+ return self._resolveInstance(componentDef);
});
},
View
5 test/all.js
@@ -21,14 +21,11 @@
// wire/on
doh.registerUrl('wire/on', '../../on.html' + hash);
- // wire/connect
- doh.registerUrl('wire/connect', '../../connect.html' + hash);
-
// wire/sizzle
doh.registerUrl('sizzle', '../../sizzle.html' + hash);
// Dojo
-// doh.registerUrl('wire/dojo/dom', '../../dojo/dom.html' + hash);
+ doh.registerUrl('wire/dojo/dom', '../../dojo/dom.html' + hash);
// doh.registerUrl('wire/dojo/dom-insert', '../../dojo/dom-insert.html' + hash);
doh.registerUrl('wire/dojo/on', '../../dojo/on.html' + hash);
// doh.registerUrl('wire/dojo/pubsub', '../../dojo/pubsub1.html' + hash);
View
303 test/connect.html
@@ -1,303 +0,0 @@
-<!DOCTYPE HTML>
-<html lang="en-US">
-<head>
- <meta charset="UTF-8">
- <title>wire/connect test</title>
-
- <script src="test-config.js"></script>
-
- <script type="text/javascript">
- define('Sender', function() {
- function Sender() {}
-
- Sender.prototype = {
- onEvent: function(a) {
- console.log()
- }
- };
-
- return Sender;
- });
-
- define('Receiver', function() {
-
- function Receiver(deferred, expected) {
- this.deferred = deferred;
- this.expected = expected;
- }
-
- Receiver.prototype = {
- handleEvent: function(arg) {
- this.deferred.callback(this.expected === arg);
- }
- };
-
- return Receiver;
- });
-
- function transform(x) {
- return x+1;
- }
-
- define('transform', function() {
- return transform;
- });
-
- define('promised-transform', function() {
- return function(x) {
- return when(x, transform);
- }
- })
-
- require(['wire'], function(wire) {
-
- var expected = 1;
-
- doh.register('wire/connect', [
- function connectFromReceiverToSenderShort(doh) {
- var dohd = new doh.Deferred();
-
- wire({
- plugins: [ { module: 'wire/connect' } ],
- sender: { create: 'Sender' },
- receiver: {
- create: {
- module: 'Receiver',
- args: [{ literal: dohd }, expected]
- },
- // Test connect
- connect: {
- 'sender.onEvent': 'handleEvent'
- }
- }
- }).then(
- function(context) {
- context.sender.onEvent(expected);
- },
- function(e) {
- dohd.errback(e);
- }
- );
-
- return dohd;
- },
- function connectFromReceiverToSenderTransformedShort(doh) {
- var dohd = new doh.Deferred();
-
- wire({
- plugins: [ { module: 'wire/connect' } ],
- tx: { module: 'transform' },
- sender: { create: 'Sender' },
- receiver: {
- create: {
- module: 'Receiver',
- args: [{ literal: dohd }, expected+1]
- },
- // Test connect
- connect: {
- 'sender.onEvent': 'tx | handleEvent'
- }
- }
- }).then(
- function(context) {
- context.sender.onEvent(expected);
- },
- function(e) {
- dohd.errback(e);
- }
- );
-
- return dohd;
- },
- function connectFromReceiverToSenderLong(doh) {
- var dohd = new doh.Deferred();
-
- wire({
- plugins: [ { module: 'wire/connect' } ],
- sender: { create: 'Sender' },
- receiver: {
- create: {
- module: 'Receiver',
- args: [{ literal: dohd }, expected]
- },
- // Test connect
- connect: {
- sender: {
- onEvent: 'handleEvent'
- }
- }
- }
- }).then(
- function(context) {
- context.sender.onEvent(expected);
- },
- function(e) {
- dohd.errback(e);
- }
- );
-
- return dohd;
- },
- function connectFromReceiverToSenderTransformedLong(doh) {
- var dohd = new doh.Deferred();
-
- wire({
- plugins: [ { module: 'wire/connect' } ],
- tx: { module: 'transform' },
- sender: { create: 'Sender' },
- receiver: {
- create: {
- module: 'Receiver',
- args: [{ literal: dohd }, expected+1]
- },
- // Test connect
- connect: {
- sender: {
- onEvent: 'tx | handleEvent'
- }
- }
- }
- }).then(
- function(context) {
- context.sender.onEvent(expected);
- },
- function(e) {
- dohd.errback(e);
- }
- );
-
- return dohd;
- },
- function connectFromSenderToReceiverShort(doh) {
- var dohd = new doh.Deferred();
-
- wire({
- plugins: [ { module: 'wire/connect' } ],
- sender: {
- create: 'Sender',
- // Test connect
- connect: {
- onEvent: 'receiver.handleEvent'
- }
- },
- receiver: {
- create: {
- module: 'Receiver',
- args: [{ literal: dohd }, expected]
- }
- }
- }).then(
- function(context) {
- context.sender.onEvent(expected);
- },
- function(e) {
- dohd.errback(e);
- }
- );
-
- return dohd;
- },
- function connectFromSenderToReceiverTransformedShort(doh) {
- var dohd = new doh.Deferred();
-
- wire({
- plugins: [ { module: 'wire/connect' } ],
- tx: { module: 'transform' },
- sender: {
- create: 'Sender',
- // Test connect
- connect: {
- onEvent: 'tx | receiver.handleEvent'
- }
- },
- receiver: {
- create: {
- module: 'Receiver',
- args: [{ literal: dohd }, expected+1]
- }
- }
- }).then(
- function(context) {
- context.sender.onEvent(expected);
- },
- function(e) {
- dohd.errback(e);
- }
- );
-
- return dohd;
- },
- function connectFromSenderToReceiverLong(doh) {
- var dohd = new doh.Deferred();
-
- wire({
- plugins: [ { module: 'wire/connect' } ],
- sender: {
- create: 'Sender',
- // Test connect
- connect: {
- onEvent: {
- receiver: 'handleEvent'
- }
- }
- },
- receiver: {
- create: {
- module: 'Receiver',
- args: [{ literal: dohd }, expected]
- }
- }
- }).then(
- function(context) {
- context.sender.onEvent(expected);
- },
- function(e) {
- dohd.errback(e);
- }
- );
-
- return dohd;
- },
- function connectFromSenderToReceiverTransformedLong(doh) {
- var dohd = new doh.Deferred();
-
- wire({
- plugins: [ { module: 'wire/connect' } ],
- tx: { module: 'transform' },
- sender: {
- create: 'Sender',
- // Test connect
- connect: {
- onEvent: {
- receiver: 'tx | handleEvent'
- }
- }
- },
- receiver: {
- create: {
- module: 'Receiver',
- args: [{ literal: dohd }, expected+1]
- }
- }
- }).then(
- function(context) {
- context.sender.onEvent(expected);
- },
- function(e) {
- dohd.errback(e);
- }
- );
-
- return dohd;
- }
-
- ]);
-
- doh.run();
-
- });
- </script>
-</head>
-<body>
-</body>
-</html>
View
186 test/node/connect-test.js
@@ -0,0 +1,186 @@
+(function(buster, context, connectPlugin, when) {
+'use strict';
+
+var assert, refute, fail, sentinel, other, obj1, obj2;
+
+assert = buster.assert;
+refute = buster.refute;
+fail = buster.assertions.fail;
+
+sentinel = { value: 'sentinel' };
+other = { value: 'other' };
+
+obj1 = { method: function() {} };
+obj2 = { method: function() {} };
+
+function createContext(spec) {
+ return context(spec, { require: require });
+}
+
+buster.testCase('connect', {
+ 'short form': {
+ 'should connect outgoing': function() {
+ var receiver = this.stub({ method: function() {} });
+
+ return createContext({
+ plugins: [connectPlugin],
+ sender: {
+ literal: { method: function() {} },
+ connect: { method: 'receiver.method' }
+ },
+ receiver: {
+ literal: receiver
+ }
+ }).then(function(context) {
+ context.sender.method(sentinel);
+ assert.calledOnceWith(receiver.method, sentinel);
+ });
+ },
+
+ 'should connect incoming': function() {
+ var receiver = this.stub({ method: function() {} });
+
+ return createContext({
+ plugins: [connectPlugin],
+ sender: {
+ literal: { method: function() {} }
+ },
+ receiver: {
+ literal: receiver,
+ connect: { 'sender.method': 'method' }
+ },
+ }).then(function(context) {
+ context.sender.method(sentinel);
+ assert.calledOnceWith(receiver.method, sentinel);
+ });
+ },
+
+ 'should transform outgoing': function() {
+ var receiver = this.stub({ method: function() {} });
+
+ return createContext({
+ plugins: [connectPlugin],
+ sender: {
+ literal: { method: function() {} },
+ connect: { method: 'tx | receiver.method' }
+ },
+ tx: {
+ literal: function() { return sentinel; }
+ },
+ receiver: {
+ literal: receiver
+ }
+ }).then(function(context) {
+ context.sender.method(other);
+ assert.calledOnceWith(receiver.method, sentinel);
+ });
+ },
+
+ 'should transform incoming': function() {
+ var receiver = this.stub({ method: function() {} });
+
+ return createContext({
+ plugins: [connectPlugin],
+ sender: {
+ literal: { method: function() {} }
+ },
+ tx: {
+ literal: function() { return sentinel; }
+ },
+ receiver: {
+ literal: receiver,
+ connect: { 'sender.method': 'tx | method' }
+ }
+ }).then(function(context) {
+ context.sender.method(other);
+ assert.calledOnceWith(receiver.method, sentinel);
+ });
+ }
+ },
+
+ 'long form': {
+ 'should connect outgoing': function() {
+ var receiver = this.stub({ method: function() {} });
+
+ return createContext({
+ plugins: [connectPlugin],
+ sender: {
+ literal: { method: function() {} },
+ connect: { method: { receiver: 'method' } }
+ },
+ receiver: {
+ literal: receiver
+ }
+ }).then(function(context) {
+ context.sender.method(sentinel);
+ assert.calledOnceWith(receiver.method, sentinel);
+ });
+ },
+
+ 'should connect incoming': function() {
+ var receiver = this.stub({ method: function() {} });
+
+ return createContext({
+ plugins: [connectPlugin],
+ sender: {
+ literal: { method: function() {} }
+ },
+ receiver: {
+ literal: receiver,
+ connect: { sender: { method: 'method' } }
+ }
+ }).then(function(context) {
+ context.sender.method(sentinel);
+ assert.calledOnceWith(receiver.method, sentinel);
+ });
+ },
+
+ 'should transform outgoing': function() {
+ var receiver = this.stub({ method: function() {} });
+
+ return createContext({
+ plugins: [connectPlugin],
+ sender: {
+ literal: { method: function() {} },
+ connect: { method: { receiver: 'tx | method' } }
+ },
+ tx: {
+ literal: function() { return sentinel; }
+ },
+ receiver: {
+ literal: receiver
+ }
+ }).then(function(context) {
+ context.sender.method(other);
+ assert.calledOnceWith(receiver.method, sentinel);
+ });
+ },
+
+ 'should transform incoming': function() {
+ var receiver = this.stub({ method: function() {} });
+
+ return createContext({
+ plugins: [connectPlugin],
+ sender: {
+ literal: { method: function() {} }
+ },
+ tx: {
+ literal: function() { return sentinel; }
+ },
+ receiver: {
+ literal: receiver,
+ connect: { sender: { method: 'tx | method' } }
+ }
+ }).then(function(context) {
+ context.sender.method(other);
+ assert.calledOnceWith(receiver.method, sentinel);
+ });
+ }
+ }
+});
+})(
+ require('buster'),
+ require('../../lib/context'),
+ require('../../connect'),
+ require('when')
+);
View
109 test/node/lib/ObjectProxy-test.js
@@ -0,0 +1,109 @@
+(function(buster, delay, ObjectProxy, WireProxy) {
+"use strict";
+
+var assert, refute, fail;
+
+assert = buster.assert;
+refute = buster.refute;
+fail = buster.assertions.fail;
+
+buster.testCase('lib/ObjectProxy', {
+ 'should create a proxy for the supplied target': function() {
+ var p, target;
+
+ target = {};
+ p = new ObjectProxy(target);
+
+ assert.same(p.target, target);
+ assert(WireProxy.isProxy(p));
+ },
+
+ advise: {
+ 'should advise named methods': function() {
+ var p, method, before, after;
+
+ method = this.spy(function() { return 3; });
+ before = this.spy();
+ after = this.spy();
+
+ p = new ObjectProxy({ method: method });
+ p.advise('method', { before: before, after: after });
+
+ p.target.method(1, 2);
+
+ assert.calledOnceWith(before, 1, 2);
+ assert.calledOnceWith(method, 1, 2);
+ assert.calledOnceWith(after, 3);
+ assert.callOrder(before, method, after);
+ },
+
+ 'should work with proxy.invoke': function() {
+ var p, method, before;
+
+ method = this.spy();
+ before = this.spy();
+
+ p = new ObjectProxy({ method: method });
+ p.advise('method', { before: before });
+
+ p.invoke('method', [1, 2]);
+
+ assert.calledOnceWith(before, 1, 2);
+ assert.calledOnceWith(method, 1, 2);
+ assert.callOrder(before, method);
+ },
+
+ 'should return an aspect remover': function() {
+ var p, method, before, aspect;
+
+ method = this.spy();
+ before = this.spy();
+
+ p = new ObjectProxy({ method: method });
+ aspect = p.advise('method', { before: before });
+
+ p.target.method(1, 2);
+
+ assert.calledOnceWith(before, 1, 2);
+ assert.calledOnceWith(method, 1, 2);
+ assert.callOrder(before, method);
+
+ aspect.remove();
+
+ p.target.method(3, 4);
+
+ refute.calledTwice(before);
+ assert.calledTwice(method);
+ },
+
+ 'should remove aspects when remove is called': function() {
+ var p, method, before, aspect;
+
+ method = this.spy();
+ before = this.spy();
+
+ p = new ObjectProxy({ method: method });
+ aspect = p.advise('method', { before: before });
+
+ p.target.method(1, 2);
+
+ assert.calledOnceWith(before, 1, 2);
+ assert.calledOnceWith(method, 1, 2);
+ assert.callOrder(before, method);
+
+ aspect.remove();
+
+ p.target.method(3, 4);
+
+ refute.calledTwice(before);
+ assert.calledTwice(method);
+ }
+ }
+
+});
+})(
+ require('buster'),
+ require('when/delay'),
+ require('../../../lib/ObjectProxy'),
+ require('../../../lib/WireProxy')
+);
View
28 test/node/lib/WireProxy-test.js
@@ -7,12 +7,12 @@ assert = buster.assert;
refute = buster.refute;
fail = buster.assertions.fail;
-buster.testCase('proxy', {
+buster.testCase('lib/WireProxy', {
'should create a base proxy for the supplied target': function() {
var p, target;
target = {};
- p = WireProxy.create(target);
+ p = new WireProxy(target);
assert.same(p.target, target);
},
@@ -22,7 +22,7 @@ buster.testCase('proxy', {
target = {};
- p = WireProxy.create(target);
+ p = new WireProxy(target);
p.set('test', 1);
assert.equals(target.test, 1);
@@ -32,7 +32,7 @@ buster.testCase('proxy', {
var p, target;
target = { test: 1 };
- p = WireProxy.create(target);
+ p = new WireProxy(target);
assert.equals(p.get('test'), 1);
},
@@ -42,7 +42,7 @@ buster.testCase('proxy', {
var p, target;
target = { method: this.spy() };
- p = WireProxy.create(target);
+ p = new WireProxy(target);
p.invoke('method', [1]);
assert.calledOnceWith(target.method, 1);
@@ -58,7 +58,7 @@ buster.testCase('proxy', {
expectedVal = 1;
target = {};
- p = WireProxy.create(target);
+ p = new WireProxy(target);
p.invoke(method, [expectedVal]);
}
@@ -67,7 +67,7 @@ buster.testCase('proxy', {
clone: {
'should return primitives': function() {
- assert.equals(1, WireProxy.create(1).clone());
+ assert.equals(1, new WireProxy(1).clone());
},
'should clone Function': function() {
@@ -78,7 +78,7 @@ buster.testCase('proxy', {
}
expected = {};
- clone = WireProxy.create(original).clone();
+ clone = new WireProxy(original).clone();
refute.same(original, clone);
assert.same(clone(), expected);
@@ -89,7 +89,7 @@ buster.testCase('proxy', {
original = new Date();
- clone = WireProxy.create(original).clone();
+ clone = new WireProxy(original).clone();
refute.same(original, clone);
assert.equals(original.getTime(), clone.getTime());
@@ -100,7 +100,7 @@ buster.testCase('proxy', {
original = /123/;
- clone = WireProxy.create(original).clone();
+ clone = new WireProxy(original).clone();
refute.same(original, clone);
assert.equals(original.toString(), clone.toString());
@@ -112,7 +112,7 @@ buster.testCase('proxy', {
original = { a: 1 };
- p = WireProxy.create(original);
+ p = new WireProxy(original);
clone = p.clone();
refute.same(original, clone);
@@ -126,7 +126,7 @@ buster.testCase('proxy', {
deepArray = [3];
original = { a: deepObject, b: deepArray };
- p = WireProxy.create(original);
+ p = new WireProxy(original);
clone = p.clone({ deep: true });
refute.same(deepObject, clone.a);
@@ -144,7 +144,7 @@ buster.testCase('proxy', {
original = [1, 2, 3];
- p = WireProxy.create(original);
+ p = new WireProxy(original);
clone = p.clone();