From 97dc41cab06c20992b0ccd6db767bae71dd54c9c Mon Sep 17 00:00:00 2001 From: foxdonut Date: Sun, 19 Apr 2015 15:33:39 +0000 Subject: [PATCH 1/2] Allow connecting to multiple methods using an array. --- docs/connections.md | 39 +++++- lib/connection.js | 37 +++-- test/node/aop-test.js | 311 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 373 insertions(+), 14 deletions(-) diff --git a/docs/connections.md b/docs/connections.md index c187314..80b5fca 100644 --- a/docs/connections.md +++ b/docs/connections.md @@ -267,6 +267,34 @@ define({ }); ``` +Connections can also be made to more than one method. Use an array of Strings instead of a single String to invoke multiple methods: + +```js +define({ + $plugins: [ + { module: 'wire/connect'}, + // other plugins ... + ], + + component1: { + create: // ... + connect: { + // Whenever component2.doSomething is called, + // component1.doSomethingAlso1 and component1.doSomethingAlso2 will also + // be invoked, with the same parameters. + 'component2.doSomething': [ + 'doSomethingAlso1', + 'doSomethingAlso2' + ] + } + }, + + component2: { + create: // ... + } +}); +``` + # Aspect Oriented Programming (AOP) **Plugin:** wire/aop @@ -304,7 +332,16 @@ define({ // component2.doSomething returns (but not if it throws, see // afterThrowing below). The return value of component2.doSomething // will be passed to component1.doSomethingAfterReturning - doSomething: 'component1.doSomethingAfterReturning' + doSomething: 'component1.doSomethingAfterReturning', + + // component1.doSomething1 and component1.doSomething2 will be + // invoked after component2.doSomethingElse returns. The return + // value of component2.doSomething will be passed to both + // component1.doSomething1 and component1.doSomething2. + doSomethingElse: [ + 'component1.doSomething1', + 'component1.doSomething2' + ] }, afterThrowing: { diff --git a/lib/connection.js b/lib/connection.js index 106a735..7f4cebc 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -104,21 +104,24 @@ define(function(require) { * rejects if an error occurs. */ function parseIncoming(source, eventName, targetProxy, connect, options, wire, createConnection) { - var promise, methodName; + var promise, methodName, methodNames; if(eventName) { // 'component.eventName': 'methodName' // 'component.eventName': 'transform | methodName' methodName = options; + methodNames = Array.isArray(options) ? options : [ options ]; - promise = pipeline(targetProxy, methodName, wire).then( - function(func) { - var invoker = proxyInvoker(targetProxy, func); + promise = when.map(methodNames, function(methodName) { + return pipeline(targetProxy, methodName, wire).then( + function(func) { + var invoker = proxyInvoker(targetProxy, func); - return createConnection(source, eventName, invoker); - } - ); + return createConnection(source, eventName, invoker); + } + ); + }); } else { // componentName: { @@ -190,10 +193,6 @@ define(function(require) { promise = connectOneOutgoing(targetProxy, options); } else { - // eventName: { - // componentName: 'methodName' - // componentName: 'transform | methodName' - // } promises = []; resolveAndConnectOneOutgoing = function(targetRef, targetMethodSpec) { @@ -202,8 +201,20 @@ define(function(require) { }); }; - for(name in options) { - promises.push(resolveAndConnectOneOutgoing(name, options[name])); + if (Array.isArray(options)) { + // eventName: [ 'methodName1', 'methodName2' ] + for (var i = 0, t = options.length; i < t; i++) { + promises.push(connectOneOutgoing(targetProxy, options[i])); + } + + } else { + // eventName: { + // componentName: 'methodName' + // componentName: 'transform | methodName' + // } + for(name in options) { + promises.push(resolveAndConnectOneOutgoing(name, options[name])); + } } promise = when.all(promises); diff --git a/test/node/aop-test.js b/test/node/aop-test.js index f36d4ab..a38b22a 100644 --- a/test/node/aop-test.js +++ b/test/node/aop-test.js @@ -66,6 +66,37 @@ buster.testCase('aop', { ); }, + 'should execute multiple functions before method': function() { + var spy1 = this.spy(); + var spy2 = this.spy(); + + return wire({ + plugins: [aopPlugin], + target: { + literal: { + method: function() {} + }, + before: { + method: [ + 'handler.test1', + 'handler.test2' + ] + } + }, + handler: { + test1: spy1, + test2: spy2 + } + }, { require: require }).then( + function(context) { + context.target.method(sentinel); + assert.calledOnceWith(spy1, sentinel); + assert.calledOnceWith(spy2, sentinel); + }, + fail + ); + }, + 'should fail when self advised method is missing': function() { var spy = this.spy(); @@ -180,6 +211,41 @@ buster.testCase('aop', { ); }, + 'should execute multiple functions after method returns': function() { + var spy1 = this.spy(); + var spy2 = this.spy(); + + return wire({ + plugins: [aopPlugin], + target: { + literal: { + method: this.stub().returns(sentinel) + }, + afterReturning: { + method: [ + 'handler.test1', + 'handler.test2' + ] + } + }, + handler: { + test1: spy1, + test2: spy2 + } + }, { require: require }) + .then( + function(context) { + return context.target.method(sentinel); + } + ).then( + function() { + assert.calledOnceWith(spy1, sentinel); + assert.calledOnceWith(spy2, sentinel); + }, + fail + ); + }, + 'should not execute function after method throws': function() { var spy = this.spy(); @@ -242,6 +308,41 @@ buster.testCase('aop', { ); }, + 'should execute multiple functions after method throws': function() { + var spy1 = this.spy(); + var spy2 = this.spy(); + + return wire({ + plugins: [aopPlugin], + target: { + literal: { + method: this.stub().throws(sentinel) + }, + afterThrowing: { + method: [ + 'handler.test1', + 'handler.test2' + ] + } + }, + handler: { + test1: spy1, + test2: spy2 + } + }, { require: require }) + .then( + function(context) { + context.target.method(other); + } + ).then( + fail, + function() { + assert.calledOnceWith(spy1, sentinel); + assert.calledOnceWith(spy2, sentinel); + } + ); + }, + 'should not execute function after method returns': function() { var spy = this.spy(); @@ -299,6 +400,41 @@ buster.testCase('aop', { ); }, + 'should execute multiple functions after method returns': function() { + var spy1 = this.spy(); + var spy2 = this.spy(); + + return wire({ + plugins: [aopPlugin], + target: { + literal: { + method: this.stub().returns(sentinel) + }, + after: { + method: [ + 'handler.test1', + 'handler.test2' + ] + } + }, + handler: { + test1: spy1, + test2: spy2 + } + }, { require: require }) + .then( + function(context) { + return context.target.method(sentinel); + } + ).then( + function() { + assert.calledOnceWith(spy1, sentinel); + assert.calledOnceWith(spy2, sentinel); + }, + fail + ); + }, + 'should execute function after method throws': function() { var spy = this.spy(); @@ -326,6 +462,41 @@ buster.testCase('aop', { assert.calledOnceWith(spy, sentinel); } ); + }, + + 'should execute multiple functions after method throws': function() { + var spy1 = this.spy(); + var spy2 = this.spy(); + + return wire({ + plugins: [aopPlugin], + target: { + literal: { + method: this.stub().throws(sentinel) + }, + after: { + method: [ + 'handler.test1', + 'handler.test2' + ] + } + }, + handler: { + test1: spy1, + test2: spy2 + } + }, { require: require }) + .then( + function(context) { + context.target.method(other); + } + ).then( + fail, + function() { + assert.calledOnceWith(spy1, sentinel); + assert.calledOnceWith(spy2, sentinel); + } + ); } }, @@ -361,6 +532,41 @@ buster.testCase('aop', { ); }, + 'should execute multiple functions after returned promise is fulfilled': function() { + var spy1 = this.spy(); + var spy2 = this.spy(); + + return wire({ + plugins: [aopPlugin], + target: { + literal: { + method: this.stub().returns(when(sentinel)) + }, + afterFulfilling: { + method: [ + 'handler.test1', + 'handler.test2' + ] + } + }, + handler: { + test1: spy1, + test2: spy2 + } + }, { require: require }) + .then( + function(context) { + return context.target.method(other); + } + ).then( + function() { + assert.calledOnceWith(spy1, sentinel); + assert.calledOnceWith(spy2, sentinel); + }, + fail + ); + }, + 'should not execute function after returned promise is rejected': function() { var spy = this.spy(); @@ -421,6 +627,41 @@ buster.testCase('aop', { ); }, + 'should execute multiples function after returned promise is rejected': function() { + var spy1 = this.spy(); + var spy2 = this.spy(); + + return wire({ + plugins: [aopPlugin], + target: { + literal: { + method: this.stub().returns(when.reject(sentinel)) + }, + afterRejecting: { + method: [ + 'handler.test1', + 'handler.test2' + ] + } + }, + handler: { + test1: spy1, + test2: spy2 + } + }, { require: require }) + .then( + function(context) { + return context.target.method(other); + } + ).then( + fail, + function() { + assert.calledOnceWith(spy1, sentinel); + assert.calledOnceWith(spy2, sentinel); + } + ); + }, + 'should not execute function after returned promise is fulfilled': function() { var spy = this.spy(); @@ -481,6 +722,41 @@ buster.testCase('aop', { ); }, + 'should execute multiple functions after returned promise is fulfilled': function() { + var spy1 = this.spy(); + var spy2 = this.spy(); + + return wire({ + plugins: [aopPlugin], + target: { + literal: { + method: this.stub().returns(when(sentinel)) + }, + after: { + method: [ + 'handler.test1', + 'handler.test2' + ] + } + }, + handler: { + test1: spy1, + test2: spy2 + } + }, { require: require }) + .then( + function(context) { + return context.target.method(other); + } + ).then( + function() { + assert.calledOnceWith(spy1, sentinel); + assert.calledOnceWith(spy2, sentinel); + }, + fail + ); + }, + 'should execute function after returned promise is rejected': function() { var spy = this.spy(); @@ -508,6 +784,41 @@ buster.testCase('aop', { assert.calledOnceWith(spy, sentinel); } ); + }, + + 'should execute multiple functions after returned promise is rejected': function() { + var spy1 = this.spy(); + var spy2 = this.spy(); + + return wire({ + plugins: [aopPlugin], + target: { + literal: { + method: this.stub().returns(when.reject(sentinel)) + }, + after: { + method: [ + 'handler.test1', + 'handler.test2' + ] + } + }, + handler: { + test1: spy1, + test2: spy2 + } + }, { require: require }) + .then( + function(context) { + return context.target.method(other); + } + ).then( + fail, + function() { + assert.calledOnceWith(spy1, sentinel); + assert.calledOnceWith(spy2, sentinel); + } + ); } } } From fe79d2b25671b06f9edabdfbec6ee0d0cae73880 Mon Sep 17 00:00:00 2001 From: foxdonut Date: Mon, 20 Apr 2015 23:37:37 +0000 Subject: [PATCH 2/2] Remove unused variable. --- lib/connection.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/connection.js b/lib/connection.js index 7f4cebc..e4cf336 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -104,13 +104,12 @@ define(function(require) { * rejects if an error occurs. */ function parseIncoming(source, eventName, targetProxy, connect, options, wire, createConnection) { - var promise, methodName, methodNames; + var promise, methodNames; if(eventName) { // 'component.eventName': 'methodName' // 'component.eventName': 'transform | methodName' - methodName = options; methodNames = Array.isArray(options) ? options : [ options ]; promise = when.map(methodNames, function(methodName) { @@ -129,7 +128,6 @@ define(function(require) { // eventName: 'transform | methodName' // } - source = methodName; promise = wire.getProxy(connect).then(function(srcProxy) { var name, promises;