diff --git a/example/amd/js/libs/postal/postal.js b/example/amd/js/libs/postal/postal.js
index 00dd3e8..dad99dd 100755
--- a/example/amd/js/libs/postal/postal.js
+++ b/example/amd/js/libs/postal/postal.js
@@ -25,10 +25,145 @@
var postal;
+ var Strategy = function( options ) {
+ var _target = options.owner[options.prop];
+ if ( typeof _target !== "function" ) {
+ throw new Error( "Strategies can only target methods." );
+ }
+ var _strategies = [];
+ var _context = options.context || options.owner;
+ var strategy = function() {
+ var idx = 0;
+ var next = function next() {
+ var args = Array.prototype.slice.call( arguments, 0 );
+ var thisIdx = idx;
+ var strategy;
+ idx += 1;
+ if ( thisIdx < _strategies.length ) {
+ strategy = _strategies[thisIdx];
+ strategy.fn.apply( strategy.context || _context, [next].concat( args ) );
+ } else {
+ _target.apply( _context, args );
+ }
+ };
+ next.apply( this, arguments );
+ };
+ strategy.target = function() {
+ return _target;
+ };
+ strategy.context = function( ctx ) {
+ if ( arguments.length === 0 ) {
+ return _context;
+ } else {
+ _context = ctx;
+ }
+ };
+ strategy.strategies = function() {
+ return _strategies;
+ };
+ strategy.useStrategy = function( strategy ) {
+ var idx = 0,
+ exists = false;
+ while ( idx < _strategies.length ) {
+ if ( _strategies[idx].name === strategy.name ) {
+ _strategies[idx] = strategy;
+ exists = true;
+ break;
+ }
+ idx += 1;
+ }
+ if ( !exists ) {
+ _strategies.push( strategy );
+ }
+ };
+ strategy.reset = function() {
+ _strategies = [];
+ };
+ if ( options.lazyInit ) {
+ _target.useStrategy = function() {
+ options.owner[options.prop] = strategy;
+ strategy.useStrategy.apply( strategy, arguments );
+ };
+ _target.context = function() {
+ options.owner[options.prop] = strategy;
+ return strategy.context.apply( strategy, arguments );
+ };
+ return _target;
+ } else {
+ return strategy;
+ }
+ };
+ /* global DistinctPredicate,ConsecutiveDistinctPredicate */
+ var strats = {
+ setTimeout: function(ms) {
+ return {
+ name: "setTimeout",
+ fn: function (next, data, envelope) {
+ setTimeout(function () {
+ next(data, envelope);
+ }, ms);
+ }
+ };
+ },
+ after: function(maxCalls, callback) {
+ var dispose = _.after(maxCalls, callback);
+ return {
+ name: "after",
+ fn: function (next, data, envelope) {
+ dispose();
+ next(data, envelope);
+ }
+ };
+ },
+ throttle : function(ms) {
+ return {
+ name: "throttle",
+ fn: _.throttle(function(next, data, envelope) {
+ next(data, envelope);
+ }, ms)
+ };
+ },
+ debounce: function(ms, immediate) {
+ return {
+ name: "debounce",
+ fn: _.debounce(function(next, data, envelope) {
+ next(data, envelope);
+ }, ms, !!immediate)
+ };
+ },
+ predicate: function(pred) {
+ return {
+ name: "predicate",
+ fn: function(next, data, envelope) {
+ if(pred.call(this, data, envelope)) {
+ next.call(this, data, envelope);
+ }
+ }
+ };
+ },
+ distinct : function(options) {
+ options = options || {};
+ var accessor = function(args) {
+ return args[0];
+ };
+ var check = options.all ?
+ new DistinctPredicate(accessor) :
+ new ConsecutiveDistinctPredicate(accessor);
+ return {
+ name : "distinct",
+ fn : function(next, data, envelope) {
+ if(check(data)) {
+ next(data, envelope);
+ }
+ }
+ };
+ }
+ };
/*jshint -W098 */
- var ConsecutiveDistinctPredicate = function () {
+ var ConsecutiveDistinctPredicate = function (argsAccessor) {
var previous;
- return function ( data ) {
+ return function () {
+ var data = argsAccessor(arguments);
var eq = false;
if ( _.isString( data ) ) {
eq = data === previous;
@@ -42,10 +177,11 @@
};
};
/*jshint -W098 */
- var DistinctPredicate = function () {
+ var DistinctPredicate = function (argsAccessor) {
var previous = [];
- return function ( data ) {
+ return function () {
+ var data = argsAccessor(arguments);
var isDistinct = !_.any( previous, function ( p ) {
if ( _.isObject( data ) || _.isArray( data ) ) {
return _.isEqual( data, p );
@@ -55,7 +191,7 @@
if ( isDistinct ) {
previous.push( data );
}
- return isDistinct;
+ return isDistinct;
};
};
/* global postal, SubscriptionDefinition */
@@ -83,9 +219,7 @@
var SubscriptionDefinition = function ( channel, topic, callback ) {
this.channel = channel;
this.topic = topic;
- this.callback = callback;
- this.constraints = [];
- this.context = null;
+ this.subscribe(callback);
postal.configuration.bus.publish( {
channel : postal.configuration.SYSTEM_CHANNEL,
topic : "subscription.created",
@@ -116,13 +250,7 @@
},
defer : function () {
- var self = this;
- var fn = this.callback;
- this.callback = function ( data, env ) {
- setTimeout( function () {
- fn.call( self.context, data, env );
- }, 0 );
- };
+ this.callback.useStrategy(postal.configuration.strategies.setTimeout(0));
return this;
},
@@ -130,26 +258,20 @@
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
}
- var self = this;
- var fn = this.callback;
- var dispose = _.after( maxCalls, _.bind( function () {
- this.unsubscribe();
- }, this ) );
-
- this.callback = function () {
- fn.apply( self.context, arguments );
- dispose();
- };
- return this;
+ var self = this;
+ self.callback.useStrategy(postal.configuration.strategies.after(maxCalls, function() {
+ self.unsubscribe.call(self);
+ }));
+ return self;
},
distinctUntilChanged : function () {
- this.withConstraint( new ConsecutiveDistinctPredicate() );
+ this.callback.useStrategy(postal.configuration.strategies.distinct());
return this;
},
distinct : function () {
- this.withConstraint( new DistinctPredicate() );
+ this.callback.useStrategy(postal.configuration.strategies.distinct({ all: true }));
return this;
},
@@ -162,22 +284,12 @@
if ( !_.isFunction( predicate ) ) {
throw "Predicate constraint must be a function";
}
- this.constraints.push( predicate );
+ this.callback.useStrategy(postal.configuration.strategies.predicate(predicate));
return this;
},
- withConstraints : function ( predicates ) {
- var self = this;
- if ( _.isArray( predicates ) ) {
- _.each( predicates, function ( predicate ) {
- self.withConstraint( predicate );
- } );
- }
- return self;
- },
-
withContext : function ( context ) {
- this.context = context;
+ this.callback.context(context);
return this;
},
@@ -194,13 +306,7 @@
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
- var self = this;
- var fn = this.callback;
- this.callback = function ( data, env ) {
- setTimeout( function () {
- fn.call( self.context, data, env );
- }, milliseconds );
- };
+ this.callback.useStrategy(postal.configuration.strategies.setTimeout(milliseconds));
return this;
},
@@ -208,13 +314,18 @@
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
- var fn = this.callback;
- this.callback = _.throttle( fn, milliseconds );
+ this.callback.useStrategy(postal.configuration.strategies.throttle(milliseconds));
return this;
},
subscribe : function ( callback ) {
this.callback = callback;
+ this.callback = new Strategy({
+ owner : this,
+ prop : "callback",
+ context : this, // TODO: is this the best option?
+ lazyInit : true
+ });
return this;
}
};
@@ -259,13 +370,7 @@
/* global postal */
var fireSub = function ( subDef, envelope ) {
if ( !subDef.inactive && postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
- if ( _.all( subDef.constraints, function ( constraint ) {
- return constraint.call( subDef.context, envelope.data, envelope );
- } ) ) {
- if ( typeof subDef.callback === "function" ) {
- subDef.callback.call( subDef.context, envelope.data, envelope );
- }
- }
+ subDef.callback.call( subDef.callback.context ? subDef.callback.context() : this, envelope.data, envelope );
}
};
@@ -366,7 +471,8 @@
bus : localBus,
resolver : bindingsResolver,
DEFAULT_CHANNEL : "/",
- SYSTEM_CHANNEL : "postal"
+ SYSTEM_CHANNEL : "postal",
+ strategies : strats
},
ChannelDefinition : ChannelDefinition,
diff --git a/example/amd/js/libs/postal/postal.min.js b/example/amd/js/libs/postal/postal.min.js
index 1d18aba..7b20459 100755
--- a/example/amd/js/libs/postal/postal.min.js
+++ b/example/amd/js/libs/postal/postal.min.js
@@ -4,4 +4,4 @@
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
Version 0.8.9
*/
-(function(t,n){"object"==typeof module&&module.exports?module.exports=function(t){return t=t||require("underscore"),n(t)}:"function"==typeof define&&define.amd?define(["underscore"],function(i){return n(i,t)}):t.postal=n(t._,t)})(this,function(t,n){var i,s=function(){var n;return function(i){var s=!1;return t.isString(i)?(s=i===n,n=i):(s=t.isEqual(i,n),n=t.clone(i)),!s}},c=function(){var n=[];return function(i){var s=!t.any(n,function(n){return t.isObject(i)||t.isArray(i)?t.isEqual(i,n):i===n});return s&&n.push(i),s}},e=function(t){this.channel=t||i.configuration.DEFAULT_CHANNEL};e.prototype.subscribe=function(){return 1===arguments.length?new r(this.channel,arguments[0].topic,arguments[0].callback):new r(this.channel,arguments[0],arguments[1])},e.prototype.publish=function(){var t=1===arguments.length?"[object String]"===Object.prototype.toString.call(arguments[0])?{topic:arguments[0]}:arguments[0]:{topic:arguments[0],data:arguments[1]};return t.channel=this.channel,i.configuration.bus.publish(t)};var r=function(t,n,s){this.channel=t,this.topic=n,this.callback=s,this.constraints=[],this.context=null,i.configuration.bus.publish({channel:i.configuration.SYSTEM_CHANNEL,topic:"subscription.created",data:{event:"subscription.created",channel:t,topic:n}}),i.configuration.bus.subscribe(this)};r.prototype={unsubscribe:function(){this.inactive||(this.inactive=!0,i.configuration.bus.unsubscribe(this),i.configuration.bus.publish({channel:i.configuration.SYSTEM_CHANNEL,topic:"subscription.removed",data:{event:"subscription.removed",channel:this.channel,topic:this.topic}}))},defer:function(){var t=this,n=this.callback;return this.callback=function(i,s){setTimeout(function(){n.call(t.context,i,s)},0)},this},disposeAfter:function(n){if(t.isNaN(n)||0>=n)throw"The value provided to disposeAfter (maxCalls) must be a number greater than zero.";var i=this,s=this.callback,c=t.after(n,t.bind(function(){this.unsubscribe()},this));return this.callback=function(){s.apply(i.context,arguments),c()},this},distinctUntilChanged:function(){return this.withConstraint(new s),this},distinct:function(){return this.withConstraint(new c),this},once:function(){return this.disposeAfter(1),this},withConstraint:function(n){if(!t.isFunction(n))throw"Predicate constraint must be a function";return this.constraints.push(n),this},withConstraints:function(n){var i=this;return t.isArray(n)&&t.each(n,function(t){i.withConstraint(t)}),i},withContext:function(t){return this.context=t,this},withDebounce:function(n,i){if(t.isNaN(n))throw"Milliseconds must be a number";var s=this.callback;return this.callback=t.debounce(s,n,!!i),this},withDelay:function(n){if(t.isNaN(n))throw"Milliseconds must be a number";var i=this,s=this.callback;return this.callback=function(t,c){setTimeout(function(){s.call(i.context,t,c)},n)},this},withThrottle:function(n){if(t.isNaN(n))throw"Milliseconds must be a number";var i=this.callback;return this.callback=t.throttle(i,n),this},subscribe:function(t){return this.callback=t,this}};var o={cache:{},regex:{},compare:function(n,i){var s,c,e,r=this.cache[i]&&this.cache[i][n];return r!==undefined?r:((c=this.regex[n])||(s="^"+t.map(n.split("."),function(t){var n="";return e&&(n="#"!==e?"\\.\\b":"\\b"),n+="#"===t?"[\\s\\S]*":"*"===t?"[^.]+":t,e=t,n}).join("")+"$",c=this.regex[n]=RegExp(s)),this.cache[i]=this.cache[i]||{},this.cache[i][n]=r=c.test(i),r)},reset:function(){this.cache={},this.regex={}}},a=function(n,s){!n.inactive&&i.configuration.resolver.compare(n.topic,s.topic)&&t.all(n.constraints,function(t){return t.call(n.context,s.data,s)})&&"function"==typeof n.callback&&n.callback.call(n.context,s.data,s)},u=0,h=[],l=function(){for(;h.length;)h.shift().unsubscribe()},f={addWireTap:function(t){var n=this;return n.wireTaps.push(t),function(){var i=n.wireTaps.indexOf(t);-1!==i&&n.wireTaps.splice(i,1)}},publish:function(n){return++u,n.timeStamp=new Date,t.each(this.wireTaps,function(t){t(n.data,n)}),this.subscriptions[n.channel]&&t.each(this.subscriptions[n.channel],function(t){for(var i,s=0,c=t.length;c>s;)(i=t[s++])&&a(i,n)}),0===--u&&l(),n},reset:function(){this.subscriptions&&(t.each(this.subscriptions,function(n){t.each(n,function(t){for(;t.length;)t.pop().unsubscribe()})}),this.subscriptions={})},subscribe:function(t){var n,i=this.subscriptions[t.channel];return i||(i=this.subscriptions[t.channel]={}),n=this.subscriptions[t.channel][t.topic],n||(n=this.subscriptions[t.channel][t.topic]=[]),n.push(t),t},subscriptions:{},wireTaps:[],unsubscribe:function(t){if(u)return h.push(t),undefined;if(this.subscriptions[t.channel][t.topic])for(var n=this.subscriptions[t.channel][t.topic].length,i=0;n>i;){if(this.subscriptions[t.channel][t.topic][i]===t){this.subscriptions[t.channel][t.topic].splice(i,1);break}i+=1}}};if(i={configuration:{bus:f,resolver:o,DEFAULT_CHANNEL:"/",SYSTEM_CHANNEL:"postal"},ChannelDefinition:e,SubscriptionDefinition:r,channel:function(t){return new e(t)},subscribe:function(t){return new r(t.channel||i.configuration.DEFAULT_CHANNEL,t.topic,t.callback)},publish:function(t){return t.channel=t.channel||i.configuration.DEFAULT_CHANNEL,i.configuration.bus.publish(t)},addWireTap:function(t){return this.configuration.bus.addWireTap(t)},linkChannels:function(n,s){var c=[];return n=t.isArray(n)?n:[n],s=t.isArray(s)?s:[s],t.each(n,function(n){var e=n.topic||"#";t.each(s,function(s){var r=s.channel||i.configuration.DEFAULT_CHANNEL;c.push(i.subscribe({channel:n.channel||i.configuration.DEFAULT_CHANNEL,topic:e,callback:function(n,c){var e=t.clone(c);e.topic=t.isFunction(s.topic)?s.topic(c.topic):s.topic||c.topic,e.channel=r,e.data=n,i.publish(e)}}))})}),c},utils:{getSubscribersFor:function(){var t=arguments[0],n=arguments[1];return 1===arguments.length&&(t=arguments[0].channel||i.configuration.DEFAULT_CHANNEL,n=arguments[0].topic),i.configuration.bus.subscriptions[t]&&Object.prototype.hasOwnProperty.call(i.configuration.bus.subscriptions[t],n)?i.configuration.bus.subscriptions[t][n]:[]},reset:function(){i.configuration.bus.reset(),i.configuration.resolver.reset()}}},f.subscriptions[i.configuration.SYSTEM_CHANNEL]={},n&&n.hasOwnProperty("__postalReady__")&&t.isArray(n.__postalReady__))for(;n.__postalReady__.length;)n.__postalReady__.shift().onReady(i);return i});
\ No newline at end of file
+(function(t,n){"object"==typeof module&&module.exports?module.exports=function(t){return t=t||require("underscore"),n(t)}:"function"==typeof define&&define.amd?define(["underscore"],function(i){return n(i,t)}):t.postal=n(t._,t)})(this,function(t,n){var i,e=function(t){var n=t.owner[t.prop];if("function"!=typeof n)throw Error("Strategies can only target methods.");var i=[],e=t.context||t.owner,r=function(){var t=0,r=function r(){var c,s=Array.prototype.slice.call(arguments,0),o=t;t+=1,i.length>o?(c=i[o],c.fn.apply(c.context||e,[r].concat(s))):n.apply(e,s)};r.apply(this,arguments)};return r.target=function(){return n},r.context=function(t){return 0===arguments.length?e:(e=t,undefined)},r.strategies=function(){return i},r.useStrategy=function(t){for(var n=0,e=!1;i.length>n;){if(i[n].name===t.name){i[n]=t,e=!0;break}n+=1}e||i.push(t)},r.reset=function(){i=[]},t.lazyInit?(n.useStrategy=function(){t.owner[t.prop]=r,r.useStrategy.apply(r,arguments)},n.context=function(){return t.owner[t.prop]=r,r.context.apply(r,arguments)},n):r},r={setTimeout:function(t){return{name:"setTimeout",fn:function(n,i,e){setTimeout(function(){n(i,e)},t)}}},after:function(n,i){var e=t.after(n,i);return{name:"after",fn:function(t,n,i){e(),t(n,i)}}},throttle:function(n){return{name:"throttle",fn:t.throttle(function(t,n,i){t(n,i)},n)}},debounce:function(n,i){return{name:"debounce",fn:t.debounce(function(t,n,i){t(n,i)},n,!!i)}},predicate:function(t){return{name:"predicate",fn:function(n,i,e){t.call(this,i,e)&&n.call(this,i,e)}}},distinct:function(t){t=t||{};var n=function(t){return t[0]},i=t.all?new s(n):new c(n);return{name:"distinct",fn:function(t,n,e){i(n)&&t(n,e)}}}},c=function(n){var i;return function(){var e=n(arguments),r=!1;return t.isString(e)?(r=e===i,i=e):(r=t.isEqual(e,i),i=t.clone(e)),!r}},s=function(n){var i=[];return function(){var e=n(arguments),r=!t.any(i,function(n){return t.isObject(e)||t.isArray(e)?t.isEqual(e,n):e===n});return r&&i.push(e),r}},o=function(t){this.channel=t||i.configuration.DEFAULT_CHANNEL};o.prototype.subscribe=function(){return 1===arguments.length?new a(this.channel,arguments[0].topic,arguments[0].callback):new a(this.channel,arguments[0],arguments[1])},o.prototype.publish=function(){var t=1===arguments.length?"[object String]"===Object.prototype.toString.call(arguments[0])?{topic:arguments[0]}:arguments[0]:{topic:arguments[0],data:arguments[1]};return t.channel=this.channel,i.configuration.bus.publish(t)};var a=function(t,n,e){this.channel=t,this.topic=n,this.subscribe(e),i.configuration.bus.publish({channel:i.configuration.SYSTEM_CHANNEL,topic:"subscription.created",data:{event:"subscription.created",channel:t,topic:n}}),i.configuration.bus.subscribe(this)};a.prototype={unsubscribe:function(){this.inactive||(this.inactive=!0,i.configuration.bus.unsubscribe(this),i.configuration.bus.publish({channel:i.configuration.SYSTEM_CHANNEL,topic:"subscription.removed",data:{event:"subscription.removed",channel:this.channel,topic:this.topic}}))},defer:function(){return this.callback.useStrategy(i.configuration.strategies.setTimeout(0)),this},disposeAfter:function(n){if(t.isNaN(n)||0>=n)throw"The value provided to disposeAfter (maxCalls) must be a number greater than zero.";var e=this;return e.callback.useStrategy(i.configuration.strategies.after(n,function(){e.unsubscribe.call(e)})),e},distinctUntilChanged:function(){return this.callback.useStrategy(i.configuration.strategies.distinct()),this},distinct:function(){return this.callback.useStrategy(i.configuration.strategies.distinct({all:!0})),this},once:function(){return this.disposeAfter(1),this},withConstraint:function(n){if(!t.isFunction(n))throw"Predicate constraint must be a function";return this.callback.useStrategy(i.configuration.strategies.predicate(n)),this},withContext:function(t){return this.callback.context(t),this},withDebounce:function(n,i){if(t.isNaN(n))throw"Milliseconds must be a number";var e=this.callback;return this.callback=t.debounce(e,n,!!i),this},withDelay:function(n){if(t.isNaN(n))throw"Milliseconds must be a number";return this.callback.useStrategy(i.configuration.strategies.setTimeout(n)),this},withThrottle:function(n){if(t.isNaN(n))throw"Milliseconds must be a number";return this.callback.useStrategy(i.configuration.strategies.throttle(n)),this},subscribe:function(t){return this.callback=t,this.callback=new e({owner:this,prop:"callback",context:this,lazyInit:!0}),this}};var u={cache:{},regex:{},compare:function(n,i){var e,r,c,s=this.cache[i]&&this.cache[i][n];return s!==undefined?s:((r=this.regex[n])||(e="^"+t.map(n.split("."),function(t){var n="";return c&&(n="#"!==c?"\\.\\b":"\\b"),n+="#"===t?"[\\s\\S]*":"*"===t?"[^.]+":t,c=t,n}).join("")+"$",r=this.regex[n]=RegExp(e)),this.cache[i]=this.cache[i]||{},this.cache[i][n]=s=r.test(i),s)},reset:function(){this.cache={},this.regex={}}},h=function(t,n){!t.inactive&&i.configuration.resolver.compare(t.topic,n.topic)&&t.callback.call(t.callback.context?t.callback.context():this,n.data,n)},f=0,l=[],p=function(){for(;l.length;)l.shift().unsubscribe()},b={addWireTap:function(t){var n=this;return n.wireTaps.push(t),function(){var i=n.wireTaps.indexOf(t);-1!==i&&n.wireTaps.splice(i,1)}},publish:function(n){return++f,n.timeStamp=new Date,t.each(this.wireTaps,function(t){t(n.data,n)}),this.subscriptions[n.channel]&&t.each(this.subscriptions[n.channel],function(t){for(var i,e=0,r=t.length;r>e;)(i=t[e++])&&h(i,n)}),0===--f&&p(),n},reset:function(){this.subscriptions&&(t.each(this.subscriptions,function(n){t.each(n,function(t){for(;t.length;)t.pop().unsubscribe()})}),this.subscriptions={})},subscribe:function(t){var n,i=this.subscriptions[t.channel];return i||(i=this.subscriptions[t.channel]={}),n=this.subscriptions[t.channel][t.topic],n||(n=this.subscriptions[t.channel][t.topic]=[]),n.push(t),t},subscriptions:{},wireTaps:[],unsubscribe:function(t){if(f)return l.push(t),undefined;if(this.subscriptions[t.channel][t.topic])for(var n=this.subscriptions[t.channel][t.topic].length,i=0;n>i;){if(this.subscriptions[t.channel][t.topic][i]===t){this.subscriptions[t.channel][t.topic].splice(i,1);break}i+=1}}};if(i={configuration:{bus:b,resolver:u,DEFAULT_CHANNEL:"/",SYSTEM_CHANNEL:"postal",strategies:r},ChannelDefinition:o,SubscriptionDefinition:a,channel:function(t){return new o(t)},subscribe:function(t){return new a(t.channel||i.configuration.DEFAULT_CHANNEL,t.topic,t.callback)},publish:function(t){return t.channel=t.channel||i.configuration.DEFAULT_CHANNEL,i.configuration.bus.publish(t)},addWireTap:function(t){return this.configuration.bus.addWireTap(t)},linkChannels:function(n,e){var r=[];return n=t.isArray(n)?n:[n],e=t.isArray(e)?e:[e],t.each(n,function(n){var c=n.topic||"#";t.each(e,function(e){var s=e.channel||i.configuration.DEFAULT_CHANNEL;r.push(i.subscribe({channel:n.channel||i.configuration.DEFAULT_CHANNEL,topic:c,callback:function(n,r){var c=t.clone(r);c.topic=t.isFunction(e.topic)?e.topic(r.topic):e.topic||r.topic,c.channel=s,c.data=n,i.publish(c)}}))})}),r},utils:{getSubscribersFor:function(){var t=arguments[0],n=arguments[1];return 1===arguments.length&&(t=arguments[0].channel||i.configuration.DEFAULT_CHANNEL,n=arguments[0].topic),i.configuration.bus.subscriptions[t]&&Object.prototype.hasOwnProperty.call(i.configuration.bus.subscriptions[t],n)?i.configuration.bus.subscriptions[t][n]:[]},reset:function(){i.configuration.bus.reset(),i.configuration.resolver.reset()}}},b.subscriptions[i.configuration.SYSTEM_CHANNEL]={},n&&n.hasOwnProperty("__postalReady__")&&t.isArray(n.__postalReady__))for(;n.__postalReady__.length;)n.__postalReady__.shift().onReady(i);return i});
\ No newline at end of file
diff --git a/example/standard/js/postal.js b/example/standard/js/postal.js
index 00dd3e8..dad99dd 100755
--- a/example/standard/js/postal.js
+++ b/example/standard/js/postal.js
@@ -25,10 +25,145 @@
var postal;
+ var Strategy = function( options ) {
+ var _target = options.owner[options.prop];
+ if ( typeof _target !== "function" ) {
+ throw new Error( "Strategies can only target methods." );
+ }
+ var _strategies = [];
+ var _context = options.context || options.owner;
+ var strategy = function() {
+ var idx = 0;
+ var next = function next() {
+ var args = Array.prototype.slice.call( arguments, 0 );
+ var thisIdx = idx;
+ var strategy;
+ idx += 1;
+ if ( thisIdx < _strategies.length ) {
+ strategy = _strategies[thisIdx];
+ strategy.fn.apply( strategy.context || _context, [next].concat( args ) );
+ } else {
+ _target.apply( _context, args );
+ }
+ };
+ next.apply( this, arguments );
+ };
+ strategy.target = function() {
+ return _target;
+ };
+ strategy.context = function( ctx ) {
+ if ( arguments.length === 0 ) {
+ return _context;
+ } else {
+ _context = ctx;
+ }
+ };
+ strategy.strategies = function() {
+ return _strategies;
+ };
+ strategy.useStrategy = function( strategy ) {
+ var idx = 0,
+ exists = false;
+ while ( idx < _strategies.length ) {
+ if ( _strategies[idx].name === strategy.name ) {
+ _strategies[idx] = strategy;
+ exists = true;
+ break;
+ }
+ idx += 1;
+ }
+ if ( !exists ) {
+ _strategies.push( strategy );
+ }
+ };
+ strategy.reset = function() {
+ _strategies = [];
+ };
+ if ( options.lazyInit ) {
+ _target.useStrategy = function() {
+ options.owner[options.prop] = strategy;
+ strategy.useStrategy.apply( strategy, arguments );
+ };
+ _target.context = function() {
+ options.owner[options.prop] = strategy;
+ return strategy.context.apply( strategy, arguments );
+ };
+ return _target;
+ } else {
+ return strategy;
+ }
+ };
+ /* global DistinctPredicate,ConsecutiveDistinctPredicate */
+ var strats = {
+ setTimeout: function(ms) {
+ return {
+ name: "setTimeout",
+ fn: function (next, data, envelope) {
+ setTimeout(function () {
+ next(data, envelope);
+ }, ms);
+ }
+ };
+ },
+ after: function(maxCalls, callback) {
+ var dispose = _.after(maxCalls, callback);
+ return {
+ name: "after",
+ fn: function (next, data, envelope) {
+ dispose();
+ next(data, envelope);
+ }
+ };
+ },
+ throttle : function(ms) {
+ return {
+ name: "throttle",
+ fn: _.throttle(function(next, data, envelope) {
+ next(data, envelope);
+ }, ms)
+ };
+ },
+ debounce: function(ms, immediate) {
+ return {
+ name: "debounce",
+ fn: _.debounce(function(next, data, envelope) {
+ next(data, envelope);
+ }, ms, !!immediate)
+ };
+ },
+ predicate: function(pred) {
+ return {
+ name: "predicate",
+ fn: function(next, data, envelope) {
+ if(pred.call(this, data, envelope)) {
+ next.call(this, data, envelope);
+ }
+ }
+ };
+ },
+ distinct : function(options) {
+ options = options || {};
+ var accessor = function(args) {
+ return args[0];
+ };
+ var check = options.all ?
+ new DistinctPredicate(accessor) :
+ new ConsecutiveDistinctPredicate(accessor);
+ return {
+ name : "distinct",
+ fn : function(next, data, envelope) {
+ if(check(data)) {
+ next(data, envelope);
+ }
+ }
+ };
+ }
+ };
/*jshint -W098 */
- var ConsecutiveDistinctPredicate = function () {
+ var ConsecutiveDistinctPredicate = function (argsAccessor) {
var previous;
- return function ( data ) {
+ return function () {
+ var data = argsAccessor(arguments);
var eq = false;
if ( _.isString( data ) ) {
eq = data === previous;
@@ -42,10 +177,11 @@
};
};
/*jshint -W098 */
- var DistinctPredicate = function () {
+ var DistinctPredicate = function (argsAccessor) {
var previous = [];
- return function ( data ) {
+ return function () {
+ var data = argsAccessor(arguments);
var isDistinct = !_.any( previous, function ( p ) {
if ( _.isObject( data ) || _.isArray( data ) ) {
return _.isEqual( data, p );
@@ -55,7 +191,7 @@
if ( isDistinct ) {
previous.push( data );
}
- return isDistinct;
+ return isDistinct;
};
};
/* global postal, SubscriptionDefinition */
@@ -83,9 +219,7 @@
var SubscriptionDefinition = function ( channel, topic, callback ) {
this.channel = channel;
this.topic = topic;
- this.callback = callback;
- this.constraints = [];
- this.context = null;
+ this.subscribe(callback);
postal.configuration.bus.publish( {
channel : postal.configuration.SYSTEM_CHANNEL,
topic : "subscription.created",
@@ -116,13 +250,7 @@
},
defer : function () {
- var self = this;
- var fn = this.callback;
- this.callback = function ( data, env ) {
- setTimeout( function () {
- fn.call( self.context, data, env );
- }, 0 );
- };
+ this.callback.useStrategy(postal.configuration.strategies.setTimeout(0));
return this;
},
@@ -130,26 +258,20 @@
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
}
- var self = this;
- var fn = this.callback;
- var dispose = _.after( maxCalls, _.bind( function () {
- this.unsubscribe();
- }, this ) );
-
- this.callback = function () {
- fn.apply( self.context, arguments );
- dispose();
- };
- return this;
+ var self = this;
+ self.callback.useStrategy(postal.configuration.strategies.after(maxCalls, function() {
+ self.unsubscribe.call(self);
+ }));
+ return self;
},
distinctUntilChanged : function () {
- this.withConstraint( new ConsecutiveDistinctPredicate() );
+ this.callback.useStrategy(postal.configuration.strategies.distinct());
return this;
},
distinct : function () {
- this.withConstraint( new DistinctPredicate() );
+ this.callback.useStrategy(postal.configuration.strategies.distinct({ all: true }));
return this;
},
@@ -162,22 +284,12 @@
if ( !_.isFunction( predicate ) ) {
throw "Predicate constraint must be a function";
}
- this.constraints.push( predicate );
+ this.callback.useStrategy(postal.configuration.strategies.predicate(predicate));
return this;
},
- withConstraints : function ( predicates ) {
- var self = this;
- if ( _.isArray( predicates ) ) {
- _.each( predicates, function ( predicate ) {
- self.withConstraint( predicate );
- } );
- }
- return self;
- },
-
withContext : function ( context ) {
- this.context = context;
+ this.callback.context(context);
return this;
},
@@ -194,13 +306,7 @@
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
- var self = this;
- var fn = this.callback;
- this.callback = function ( data, env ) {
- setTimeout( function () {
- fn.call( self.context, data, env );
- }, milliseconds );
- };
+ this.callback.useStrategy(postal.configuration.strategies.setTimeout(milliseconds));
return this;
},
@@ -208,13 +314,18 @@
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
- var fn = this.callback;
- this.callback = _.throttle( fn, milliseconds );
+ this.callback.useStrategy(postal.configuration.strategies.throttle(milliseconds));
return this;
},
subscribe : function ( callback ) {
this.callback = callback;
+ this.callback = new Strategy({
+ owner : this,
+ prop : "callback",
+ context : this, // TODO: is this the best option?
+ lazyInit : true
+ });
return this;
}
};
@@ -259,13 +370,7 @@
/* global postal */
var fireSub = function ( subDef, envelope ) {
if ( !subDef.inactive && postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
- if ( _.all( subDef.constraints, function ( constraint ) {
- return constraint.call( subDef.context, envelope.data, envelope );
- } ) ) {
- if ( typeof subDef.callback === "function" ) {
- subDef.callback.call( subDef.context, envelope.data, envelope );
- }
- }
+ subDef.callback.call( subDef.callback.context ? subDef.callback.context() : this, envelope.data, envelope );
}
};
@@ -366,7 +471,8 @@
bus : localBus,
resolver : bindingsResolver,
DEFAULT_CHANNEL : "/",
- SYSTEM_CHANNEL : "postal"
+ SYSTEM_CHANNEL : "postal",
+ strategies : strats
},
ChannelDefinition : ChannelDefinition,
diff --git a/example/standard/js/postal.min.js b/example/standard/js/postal.min.js
index 1d18aba..7b20459 100755
--- a/example/standard/js/postal.min.js
+++ b/example/standard/js/postal.min.js
@@ -4,4 +4,4 @@
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
Version 0.8.9
*/
-(function(t,n){"object"==typeof module&&module.exports?module.exports=function(t){return t=t||require("underscore"),n(t)}:"function"==typeof define&&define.amd?define(["underscore"],function(i){return n(i,t)}):t.postal=n(t._,t)})(this,function(t,n){var i,s=function(){var n;return function(i){var s=!1;return t.isString(i)?(s=i===n,n=i):(s=t.isEqual(i,n),n=t.clone(i)),!s}},c=function(){var n=[];return function(i){var s=!t.any(n,function(n){return t.isObject(i)||t.isArray(i)?t.isEqual(i,n):i===n});return s&&n.push(i),s}},e=function(t){this.channel=t||i.configuration.DEFAULT_CHANNEL};e.prototype.subscribe=function(){return 1===arguments.length?new r(this.channel,arguments[0].topic,arguments[0].callback):new r(this.channel,arguments[0],arguments[1])},e.prototype.publish=function(){var t=1===arguments.length?"[object String]"===Object.prototype.toString.call(arguments[0])?{topic:arguments[0]}:arguments[0]:{topic:arguments[0],data:arguments[1]};return t.channel=this.channel,i.configuration.bus.publish(t)};var r=function(t,n,s){this.channel=t,this.topic=n,this.callback=s,this.constraints=[],this.context=null,i.configuration.bus.publish({channel:i.configuration.SYSTEM_CHANNEL,topic:"subscription.created",data:{event:"subscription.created",channel:t,topic:n}}),i.configuration.bus.subscribe(this)};r.prototype={unsubscribe:function(){this.inactive||(this.inactive=!0,i.configuration.bus.unsubscribe(this),i.configuration.bus.publish({channel:i.configuration.SYSTEM_CHANNEL,topic:"subscription.removed",data:{event:"subscription.removed",channel:this.channel,topic:this.topic}}))},defer:function(){var t=this,n=this.callback;return this.callback=function(i,s){setTimeout(function(){n.call(t.context,i,s)},0)},this},disposeAfter:function(n){if(t.isNaN(n)||0>=n)throw"The value provided to disposeAfter (maxCalls) must be a number greater than zero.";var i=this,s=this.callback,c=t.after(n,t.bind(function(){this.unsubscribe()},this));return this.callback=function(){s.apply(i.context,arguments),c()},this},distinctUntilChanged:function(){return this.withConstraint(new s),this},distinct:function(){return this.withConstraint(new c),this},once:function(){return this.disposeAfter(1),this},withConstraint:function(n){if(!t.isFunction(n))throw"Predicate constraint must be a function";return this.constraints.push(n),this},withConstraints:function(n){var i=this;return t.isArray(n)&&t.each(n,function(t){i.withConstraint(t)}),i},withContext:function(t){return this.context=t,this},withDebounce:function(n,i){if(t.isNaN(n))throw"Milliseconds must be a number";var s=this.callback;return this.callback=t.debounce(s,n,!!i),this},withDelay:function(n){if(t.isNaN(n))throw"Milliseconds must be a number";var i=this,s=this.callback;return this.callback=function(t,c){setTimeout(function(){s.call(i.context,t,c)},n)},this},withThrottle:function(n){if(t.isNaN(n))throw"Milliseconds must be a number";var i=this.callback;return this.callback=t.throttle(i,n),this},subscribe:function(t){return this.callback=t,this}};var o={cache:{},regex:{},compare:function(n,i){var s,c,e,r=this.cache[i]&&this.cache[i][n];return r!==undefined?r:((c=this.regex[n])||(s="^"+t.map(n.split("."),function(t){var n="";return e&&(n="#"!==e?"\\.\\b":"\\b"),n+="#"===t?"[\\s\\S]*":"*"===t?"[^.]+":t,e=t,n}).join("")+"$",c=this.regex[n]=RegExp(s)),this.cache[i]=this.cache[i]||{},this.cache[i][n]=r=c.test(i),r)},reset:function(){this.cache={},this.regex={}}},a=function(n,s){!n.inactive&&i.configuration.resolver.compare(n.topic,s.topic)&&t.all(n.constraints,function(t){return t.call(n.context,s.data,s)})&&"function"==typeof n.callback&&n.callback.call(n.context,s.data,s)},u=0,h=[],l=function(){for(;h.length;)h.shift().unsubscribe()},f={addWireTap:function(t){var n=this;return n.wireTaps.push(t),function(){var i=n.wireTaps.indexOf(t);-1!==i&&n.wireTaps.splice(i,1)}},publish:function(n){return++u,n.timeStamp=new Date,t.each(this.wireTaps,function(t){t(n.data,n)}),this.subscriptions[n.channel]&&t.each(this.subscriptions[n.channel],function(t){for(var i,s=0,c=t.length;c>s;)(i=t[s++])&&a(i,n)}),0===--u&&l(),n},reset:function(){this.subscriptions&&(t.each(this.subscriptions,function(n){t.each(n,function(t){for(;t.length;)t.pop().unsubscribe()})}),this.subscriptions={})},subscribe:function(t){var n,i=this.subscriptions[t.channel];return i||(i=this.subscriptions[t.channel]={}),n=this.subscriptions[t.channel][t.topic],n||(n=this.subscriptions[t.channel][t.topic]=[]),n.push(t),t},subscriptions:{},wireTaps:[],unsubscribe:function(t){if(u)return h.push(t),undefined;if(this.subscriptions[t.channel][t.topic])for(var n=this.subscriptions[t.channel][t.topic].length,i=0;n>i;){if(this.subscriptions[t.channel][t.topic][i]===t){this.subscriptions[t.channel][t.topic].splice(i,1);break}i+=1}}};if(i={configuration:{bus:f,resolver:o,DEFAULT_CHANNEL:"/",SYSTEM_CHANNEL:"postal"},ChannelDefinition:e,SubscriptionDefinition:r,channel:function(t){return new e(t)},subscribe:function(t){return new r(t.channel||i.configuration.DEFAULT_CHANNEL,t.topic,t.callback)},publish:function(t){return t.channel=t.channel||i.configuration.DEFAULT_CHANNEL,i.configuration.bus.publish(t)},addWireTap:function(t){return this.configuration.bus.addWireTap(t)},linkChannels:function(n,s){var c=[];return n=t.isArray(n)?n:[n],s=t.isArray(s)?s:[s],t.each(n,function(n){var e=n.topic||"#";t.each(s,function(s){var r=s.channel||i.configuration.DEFAULT_CHANNEL;c.push(i.subscribe({channel:n.channel||i.configuration.DEFAULT_CHANNEL,topic:e,callback:function(n,c){var e=t.clone(c);e.topic=t.isFunction(s.topic)?s.topic(c.topic):s.topic||c.topic,e.channel=r,e.data=n,i.publish(e)}}))})}),c},utils:{getSubscribersFor:function(){var t=arguments[0],n=arguments[1];return 1===arguments.length&&(t=arguments[0].channel||i.configuration.DEFAULT_CHANNEL,n=arguments[0].topic),i.configuration.bus.subscriptions[t]&&Object.prototype.hasOwnProperty.call(i.configuration.bus.subscriptions[t],n)?i.configuration.bus.subscriptions[t][n]:[]},reset:function(){i.configuration.bus.reset(),i.configuration.resolver.reset()}}},f.subscriptions[i.configuration.SYSTEM_CHANNEL]={},n&&n.hasOwnProperty("__postalReady__")&&t.isArray(n.__postalReady__))for(;n.__postalReady__.length;)n.__postalReady__.shift().onReady(i);return i});
\ No newline at end of file
+(function(t,n){"object"==typeof module&&module.exports?module.exports=function(t){return t=t||require("underscore"),n(t)}:"function"==typeof define&&define.amd?define(["underscore"],function(i){return n(i,t)}):t.postal=n(t._,t)})(this,function(t,n){var i,e=function(t){var n=t.owner[t.prop];if("function"!=typeof n)throw Error("Strategies can only target methods.");var i=[],e=t.context||t.owner,r=function(){var t=0,r=function r(){var c,s=Array.prototype.slice.call(arguments,0),o=t;t+=1,i.length>o?(c=i[o],c.fn.apply(c.context||e,[r].concat(s))):n.apply(e,s)};r.apply(this,arguments)};return r.target=function(){return n},r.context=function(t){return 0===arguments.length?e:(e=t,undefined)},r.strategies=function(){return i},r.useStrategy=function(t){for(var n=0,e=!1;i.length>n;){if(i[n].name===t.name){i[n]=t,e=!0;break}n+=1}e||i.push(t)},r.reset=function(){i=[]},t.lazyInit?(n.useStrategy=function(){t.owner[t.prop]=r,r.useStrategy.apply(r,arguments)},n.context=function(){return t.owner[t.prop]=r,r.context.apply(r,arguments)},n):r},r={setTimeout:function(t){return{name:"setTimeout",fn:function(n,i,e){setTimeout(function(){n(i,e)},t)}}},after:function(n,i){var e=t.after(n,i);return{name:"after",fn:function(t,n,i){e(),t(n,i)}}},throttle:function(n){return{name:"throttle",fn:t.throttle(function(t,n,i){t(n,i)},n)}},debounce:function(n,i){return{name:"debounce",fn:t.debounce(function(t,n,i){t(n,i)},n,!!i)}},predicate:function(t){return{name:"predicate",fn:function(n,i,e){t.call(this,i,e)&&n.call(this,i,e)}}},distinct:function(t){t=t||{};var n=function(t){return t[0]},i=t.all?new s(n):new c(n);return{name:"distinct",fn:function(t,n,e){i(n)&&t(n,e)}}}},c=function(n){var i;return function(){var e=n(arguments),r=!1;return t.isString(e)?(r=e===i,i=e):(r=t.isEqual(e,i),i=t.clone(e)),!r}},s=function(n){var i=[];return function(){var e=n(arguments),r=!t.any(i,function(n){return t.isObject(e)||t.isArray(e)?t.isEqual(e,n):e===n});return r&&i.push(e),r}},o=function(t){this.channel=t||i.configuration.DEFAULT_CHANNEL};o.prototype.subscribe=function(){return 1===arguments.length?new a(this.channel,arguments[0].topic,arguments[0].callback):new a(this.channel,arguments[0],arguments[1])},o.prototype.publish=function(){var t=1===arguments.length?"[object String]"===Object.prototype.toString.call(arguments[0])?{topic:arguments[0]}:arguments[0]:{topic:arguments[0],data:arguments[1]};return t.channel=this.channel,i.configuration.bus.publish(t)};var a=function(t,n,e){this.channel=t,this.topic=n,this.subscribe(e),i.configuration.bus.publish({channel:i.configuration.SYSTEM_CHANNEL,topic:"subscription.created",data:{event:"subscription.created",channel:t,topic:n}}),i.configuration.bus.subscribe(this)};a.prototype={unsubscribe:function(){this.inactive||(this.inactive=!0,i.configuration.bus.unsubscribe(this),i.configuration.bus.publish({channel:i.configuration.SYSTEM_CHANNEL,topic:"subscription.removed",data:{event:"subscription.removed",channel:this.channel,topic:this.topic}}))},defer:function(){return this.callback.useStrategy(i.configuration.strategies.setTimeout(0)),this},disposeAfter:function(n){if(t.isNaN(n)||0>=n)throw"The value provided to disposeAfter (maxCalls) must be a number greater than zero.";var e=this;return e.callback.useStrategy(i.configuration.strategies.after(n,function(){e.unsubscribe.call(e)})),e},distinctUntilChanged:function(){return this.callback.useStrategy(i.configuration.strategies.distinct()),this},distinct:function(){return this.callback.useStrategy(i.configuration.strategies.distinct({all:!0})),this},once:function(){return this.disposeAfter(1),this},withConstraint:function(n){if(!t.isFunction(n))throw"Predicate constraint must be a function";return this.callback.useStrategy(i.configuration.strategies.predicate(n)),this},withContext:function(t){return this.callback.context(t),this},withDebounce:function(n,i){if(t.isNaN(n))throw"Milliseconds must be a number";var e=this.callback;return this.callback=t.debounce(e,n,!!i),this},withDelay:function(n){if(t.isNaN(n))throw"Milliseconds must be a number";return this.callback.useStrategy(i.configuration.strategies.setTimeout(n)),this},withThrottle:function(n){if(t.isNaN(n))throw"Milliseconds must be a number";return this.callback.useStrategy(i.configuration.strategies.throttle(n)),this},subscribe:function(t){return this.callback=t,this.callback=new e({owner:this,prop:"callback",context:this,lazyInit:!0}),this}};var u={cache:{},regex:{},compare:function(n,i){var e,r,c,s=this.cache[i]&&this.cache[i][n];return s!==undefined?s:((r=this.regex[n])||(e="^"+t.map(n.split("."),function(t){var n="";return c&&(n="#"!==c?"\\.\\b":"\\b"),n+="#"===t?"[\\s\\S]*":"*"===t?"[^.]+":t,c=t,n}).join("")+"$",r=this.regex[n]=RegExp(e)),this.cache[i]=this.cache[i]||{},this.cache[i][n]=s=r.test(i),s)},reset:function(){this.cache={},this.regex={}}},h=function(t,n){!t.inactive&&i.configuration.resolver.compare(t.topic,n.topic)&&t.callback.call(t.callback.context?t.callback.context():this,n.data,n)},f=0,l=[],p=function(){for(;l.length;)l.shift().unsubscribe()},b={addWireTap:function(t){var n=this;return n.wireTaps.push(t),function(){var i=n.wireTaps.indexOf(t);-1!==i&&n.wireTaps.splice(i,1)}},publish:function(n){return++f,n.timeStamp=new Date,t.each(this.wireTaps,function(t){t(n.data,n)}),this.subscriptions[n.channel]&&t.each(this.subscriptions[n.channel],function(t){for(var i,e=0,r=t.length;r>e;)(i=t[e++])&&h(i,n)}),0===--f&&p(),n},reset:function(){this.subscriptions&&(t.each(this.subscriptions,function(n){t.each(n,function(t){for(;t.length;)t.pop().unsubscribe()})}),this.subscriptions={})},subscribe:function(t){var n,i=this.subscriptions[t.channel];return i||(i=this.subscriptions[t.channel]={}),n=this.subscriptions[t.channel][t.topic],n||(n=this.subscriptions[t.channel][t.topic]=[]),n.push(t),t},subscriptions:{},wireTaps:[],unsubscribe:function(t){if(f)return l.push(t),undefined;if(this.subscriptions[t.channel][t.topic])for(var n=this.subscriptions[t.channel][t.topic].length,i=0;n>i;){if(this.subscriptions[t.channel][t.topic][i]===t){this.subscriptions[t.channel][t.topic].splice(i,1);break}i+=1}}};if(i={configuration:{bus:b,resolver:u,DEFAULT_CHANNEL:"/",SYSTEM_CHANNEL:"postal",strategies:r},ChannelDefinition:o,SubscriptionDefinition:a,channel:function(t){return new o(t)},subscribe:function(t){return new a(t.channel||i.configuration.DEFAULT_CHANNEL,t.topic,t.callback)},publish:function(t){return t.channel=t.channel||i.configuration.DEFAULT_CHANNEL,i.configuration.bus.publish(t)},addWireTap:function(t){return this.configuration.bus.addWireTap(t)},linkChannels:function(n,e){var r=[];return n=t.isArray(n)?n:[n],e=t.isArray(e)?e:[e],t.each(n,function(n){var c=n.topic||"#";t.each(e,function(e){var s=e.channel||i.configuration.DEFAULT_CHANNEL;r.push(i.subscribe({channel:n.channel||i.configuration.DEFAULT_CHANNEL,topic:c,callback:function(n,r){var c=t.clone(r);c.topic=t.isFunction(e.topic)?e.topic(r.topic):e.topic||r.topic,c.channel=s,c.data=n,i.publish(c)}}))})}),r},utils:{getSubscribersFor:function(){var t=arguments[0],n=arguments[1];return 1===arguments.length&&(t=arguments[0].channel||i.configuration.DEFAULT_CHANNEL,n=arguments[0].topic),i.configuration.bus.subscriptions[t]&&Object.prototype.hasOwnProperty.call(i.configuration.bus.subscriptions[t],n)?i.configuration.bus.subscriptions[t][n]:[]},reset:function(){i.configuration.bus.reset(),i.configuration.resolver.reset()}}},b.subscriptions[i.configuration.SYSTEM_CHANNEL]={},n&&n.hasOwnProperty("__postalReady__")&&t.isArray(n.__postalReady__))for(;n.__postalReady__.length;)n.__postalReady__.shift().onReady(i);return i});
\ No newline at end of file
diff --git a/lib/postal.js b/lib/postal.js
index 00dd3e8..dad99dd 100755
--- a/lib/postal.js
+++ b/lib/postal.js
@@ -25,10 +25,145 @@
var postal;
+ var Strategy = function( options ) {
+ var _target = options.owner[options.prop];
+ if ( typeof _target !== "function" ) {
+ throw new Error( "Strategies can only target methods." );
+ }
+ var _strategies = [];
+ var _context = options.context || options.owner;
+ var strategy = function() {
+ var idx = 0;
+ var next = function next() {
+ var args = Array.prototype.slice.call( arguments, 0 );
+ var thisIdx = idx;
+ var strategy;
+ idx += 1;
+ if ( thisIdx < _strategies.length ) {
+ strategy = _strategies[thisIdx];
+ strategy.fn.apply( strategy.context || _context, [next].concat( args ) );
+ } else {
+ _target.apply( _context, args );
+ }
+ };
+ next.apply( this, arguments );
+ };
+ strategy.target = function() {
+ return _target;
+ };
+ strategy.context = function( ctx ) {
+ if ( arguments.length === 0 ) {
+ return _context;
+ } else {
+ _context = ctx;
+ }
+ };
+ strategy.strategies = function() {
+ return _strategies;
+ };
+ strategy.useStrategy = function( strategy ) {
+ var idx = 0,
+ exists = false;
+ while ( idx < _strategies.length ) {
+ if ( _strategies[idx].name === strategy.name ) {
+ _strategies[idx] = strategy;
+ exists = true;
+ break;
+ }
+ idx += 1;
+ }
+ if ( !exists ) {
+ _strategies.push( strategy );
+ }
+ };
+ strategy.reset = function() {
+ _strategies = [];
+ };
+ if ( options.lazyInit ) {
+ _target.useStrategy = function() {
+ options.owner[options.prop] = strategy;
+ strategy.useStrategy.apply( strategy, arguments );
+ };
+ _target.context = function() {
+ options.owner[options.prop] = strategy;
+ return strategy.context.apply( strategy, arguments );
+ };
+ return _target;
+ } else {
+ return strategy;
+ }
+ };
+ /* global DistinctPredicate,ConsecutiveDistinctPredicate */
+ var strats = {
+ setTimeout: function(ms) {
+ return {
+ name: "setTimeout",
+ fn: function (next, data, envelope) {
+ setTimeout(function () {
+ next(data, envelope);
+ }, ms);
+ }
+ };
+ },
+ after: function(maxCalls, callback) {
+ var dispose = _.after(maxCalls, callback);
+ return {
+ name: "after",
+ fn: function (next, data, envelope) {
+ dispose();
+ next(data, envelope);
+ }
+ };
+ },
+ throttle : function(ms) {
+ return {
+ name: "throttle",
+ fn: _.throttle(function(next, data, envelope) {
+ next(data, envelope);
+ }, ms)
+ };
+ },
+ debounce: function(ms, immediate) {
+ return {
+ name: "debounce",
+ fn: _.debounce(function(next, data, envelope) {
+ next(data, envelope);
+ }, ms, !!immediate)
+ };
+ },
+ predicate: function(pred) {
+ return {
+ name: "predicate",
+ fn: function(next, data, envelope) {
+ if(pred.call(this, data, envelope)) {
+ next.call(this, data, envelope);
+ }
+ }
+ };
+ },
+ distinct : function(options) {
+ options = options || {};
+ var accessor = function(args) {
+ return args[0];
+ };
+ var check = options.all ?
+ new DistinctPredicate(accessor) :
+ new ConsecutiveDistinctPredicate(accessor);
+ return {
+ name : "distinct",
+ fn : function(next, data, envelope) {
+ if(check(data)) {
+ next(data, envelope);
+ }
+ }
+ };
+ }
+ };
/*jshint -W098 */
- var ConsecutiveDistinctPredicate = function () {
+ var ConsecutiveDistinctPredicate = function (argsAccessor) {
var previous;
- return function ( data ) {
+ return function () {
+ var data = argsAccessor(arguments);
var eq = false;
if ( _.isString( data ) ) {
eq = data === previous;
@@ -42,10 +177,11 @@
};
};
/*jshint -W098 */
- var DistinctPredicate = function () {
+ var DistinctPredicate = function (argsAccessor) {
var previous = [];
- return function ( data ) {
+ return function () {
+ var data = argsAccessor(arguments);
var isDistinct = !_.any( previous, function ( p ) {
if ( _.isObject( data ) || _.isArray( data ) ) {
return _.isEqual( data, p );
@@ -55,7 +191,7 @@
if ( isDistinct ) {
previous.push( data );
}
- return isDistinct;
+ return isDistinct;
};
};
/* global postal, SubscriptionDefinition */
@@ -83,9 +219,7 @@
var SubscriptionDefinition = function ( channel, topic, callback ) {
this.channel = channel;
this.topic = topic;
- this.callback = callback;
- this.constraints = [];
- this.context = null;
+ this.subscribe(callback);
postal.configuration.bus.publish( {
channel : postal.configuration.SYSTEM_CHANNEL,
topic : "subscription.created",
@@ -116,13 +250,7 @@
},
defer : function () {
- var self = this;
- var fn = this.callback;
- this.callback = function ( data, env ) {
- setTimeout( function () {
- fn.call( self.context, data, env );
- }, 0 );
- };
+ this.callback.useStrategy(postal.configuration.strategies.setTimeout(0));
return this;
},
@@ -130,26 +258,20 @@
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
}
- var self = this;
- var fn = this.callback;
- var dispose = _.after( maxCalls, _.bind( function () {
- this.unsubscribe();
- }, this ) );
-
- this.callback = function () {
- fn.apply( self.context, arguments );
- dispose();
- };
- return this;
+ var self = this;
+ self.callback.useStrategy(postal.configuration.strategies.after(maxCalls, function() {
+ self.unsubscribe.call(self);
+ }));
+ return self;
},
distinctUntilChanged : function () {
- this.withConstraint( new ConsecutiveDistinctPredicate() );
+ this.callback.useStrategy(postal.configuration.strategies.distinct());
return this;
},
distinct : function () {
- this.withConstraint( new DistinctPredicate() );
+ this.callback.useStrategy(postal.configuration.strategies.distinct({ all: true }));
return this;
},
@@ -162,22 +284,12 @@
if ( !_.isFunction( predicate ) ) {
throw "Predicate constraint must be a function";
}
- this.constraints.push( predicate );
+ this.callback.useStrategy(postal.configuration.strategies.predicate(predicate));
return this;
},
- withConstraints : function ( predicates ) {
- var self = this;
- if ( _.isArray( predicates ) ) {
- _.each( predicates, function ( predicate ) {
- self.withConstraint( predicate );
- } );
- }
- return self;
- },
-
withContext : function ( context ) {
- this.context = context;
+ this.callback.context(context);
return this;
},
@@ -194,13 +306,7 @@
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
- var self = this;
- var fn = this.callback;
- this.callback = function ( data, env ) {
- setTimeout( function () {
- fn.call( self.context, data, env );
- }, milliseconds );
- };
+ this.callback.useStrategy(postal.configuration.strategies.setTimeout(milliseconds));
return this;
},
@@ -208,13 +314,18 @@
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
- var fn = this.callback;
- this.callback = _.throttle( fn, milliseconds );
+ this.callback.useStrategy(postal.configuration.strategies.throttle(milliseconds));
return this;
},
subscribe : function ( callback ) {
this.callback = callback;
+ this.callback = new Strategy({
+ owner : this,
+ prop : "callback",
+ context : this, // TODO: is this the best option?
+ lazyInit : true
+ });
return this;
}
};
@@ -259,13 +370,7 @@
/* global postal */
var fireSub = function ( subDef, envelope ) {
if ( !subDef.inactive && postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
- if ( _.all( subDef.constraints, function ( constraint ) {
- return constraint.call( subDef.context, envelope.data, envelope );
- } ) ) {
- if ( typeof subDef.callback === "function" ) {
- subDef.callback.call( subDef.context, envelope.data, envelope );
- }
- }
+ subDef.callback.call( subDef.callback.context ? subDef.callback.context() : this, envelope.data, envelope );
}
};
@@ -366,7 +471,8 @@
bus : localBus,
resolver : bindingsResolver,
DEFAULT_CHANNEL : "/",
- SYSTEM_CHANNEL : "postal"
+ SYSTEM_CHANNEL : "postal",
+ strategies : strats
},
ChannelDefinition : ChannelDefinition,
diff --git a/lib/postal.min.js b/lib/postal.min.js
index 1d18aba..7b20459 100755
--- a/lib/postal.min.js
+++ b/lib/postal.min.js
@@ -4,4 +4,4 @@
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
Version 0.8.9
*/
-(function(t,n){"object"==typeof module&&module.exports?module.exports=function(t){return t=t||require("underscore"),n(t)}:"function"==typeof define&&define.amd?define(["underscore"],function(i){return n(i,t)}):t.postal=n(t._,t)})(this,function(t,n){var i,s=function(){var n;return function(i){var s=!1;return t.isString(i)?(s=i===n,n=i):(s=t.isEqual(i,n),n=t.clone(i)),!s}},c=function(){var n=[];return function(i){var s=!t.any(n,function(n){return t.isObject(i)||t.isArray(i)?t.isEqual(i,n):i===n});return s&&n.push(i),s}},e=function(t){this.channel=t||i.configuration.DEFAULT_CHANNEL};e.prototype.subscribe=function(){return 1===arguments.length?new r(this.channel,arguments[0].topic,arguments[0].callback):new r(this.channel,arguments[0],arguments[1])},e.prototype.publish=function(){var t=1===arguments.length?"[object String]"===Object.prototype.toString.call(arguments[0])?{topic:arguments[0]}:arguments[0]:{topic:arguments[0],data:arguments[1]};return t.channel=this.channel,i.configuration.bus.publish(t)};var r=function(t,n,s){this.channel=t,this.topic=n,this.callback=s,this.constraints=[],this.context=null,i.configuration.bus.publish({channel:i.configuration.SYSTEM_CHANNEL,topic:"subscription.created",data:{event:"subscription.created",channel:t,topic:n}}),i.configuration.bus.subscribe(this)};r.prototype={unsubscribe:function(){this.inactive||(this.inactive=!0,i.configuration.bus.unsubscribe(this),i.configuration.bus.publish({channel:i.configuration.SYSTEM_CHANNEL,topic:"subscription.removed",data:{event:"subscription.removed",channel:this.channel,topic:this.topic}}))},defer:function(){var t=this,n=this.callback;return this.callback=function(i,s){setTimeout(function(){n.call(t.context,i,s)},0)},this},disposeAfter:function(n){if(t.isNaN(n)||0>=n)throw"The value provided to disposeAfter (maxCalls) must be a number greater than zero.";var i=this,s=this.callback,c=t.after(n,t.bind(function(){this.unsubscribe()},this));return this.callback=function(){s.apply(i.context,arguments),c()},this},distinctUntilChanged:function(){return this.withConstraint(new s),this},distinct:function(){return this.withConstraint(new c),this},once:function(){return this.disposeAfter(1),this},withConstraint:function(n){if(!t.isFunction(n))throw"Predicate constraint must be a function";return this.constraints.push(n),this},withConstraints:function(n){var i=this;return t.isArray(n)&&t.each(n,function(t){i.withConstraint(t)}),i},withContext:function(t){return this.context=t,this},withDebounce:function(n,i){if(t.isNaN(n))throw"Milliseconds must be a number";var s=this.callback;return this.callback=t.debounce(s,n,!!i),this},withDelay:function(n){if(t.isNaN(n))throw"Milliseconds must be a number";var i=this,s=this.callback;return this.callback=function(t,c){setTimeout(function(){s.call(i.context,t,c)},n)},this},withThrottle:function(n){if(t.isNaN(n))throw"Milliseconds must be a number";var i=this.callback;return this.callback=t.throttle(i,n),this},subscribe:function(t){return this.callback=t,this}};var o={cache:{},regex:{},compare:function(n,i){var s,c,e,r=this.cache[i]&&this.cache[i][n];return r!==undefined?r:((c=this.regex[n])||(s="^"+t.map(n.split("."),function(t){var n="";return e&&(n="#"!==e?"\\.\\b":"\\b"),n+="#"===t?"[\\s\\S]*":"*"===t?"[^.]+":t,e=t,n}).join("")+"$",c=this.regex[n]=RegExp(s)),this.cache[i]=this.cache[i]||{},this.cache[i][n]=r=c.test(i),r)},reset:function(){this.cache={},this.regex={}}},a=function(n,s){!n.inactive&&i.configuration.resolver.compare(n.topic,s.topic)&&t.all(n.constraints,function(t){return t.call(n.context,s.data,s)})&&"function"==typeof n.callback&&n.callback.call(n.context,s.data,s)},u=0,h=[],l=function(){for(;h.length;)h.shift().unsubscribe()},f={addWireTap:function(t){var n=this;return n.wireTaps.push(t),function(){var i=n.wireTaps.indexOf(t);-1!==i&&n.wireTaps.splice(i,1)}},publish:function(n){return++u,n.timeStamp=new Date,t.each(this.wireTaps,function(t){t(n.data,n)}),this.subscriptions[n.channel]&&t.each(this.subscriptions[n.channel],function(t){for(var i,s=0,c=t.length;c>s;)(i=t[s++])&&a(i,n)}),0===--u&&l(),n},reset:function(){this.subscriptions&&(t.each(this.subscriptions,function(n){t.each(n,function(t){for(;t.length;)t.pop().unsubscribe()})}),this.subscriptions={})},subscribe:function(t){var n,i=this.subscriptions[t.channel];return i||(i=this.subscriptions[t.channel]={}),n=this.subscriptions[t.channel][t.topic],n||(n=this.subscriptions[t.channel][t.topic]=[]),n.push(t),t},subscriptions:{},wireTaps:[],unsubscribe:function(t){if(u)return h.push(t),undefined;if(this.subscriptions[t.channel][t.topic])for(var n=this.subscriptions[t.channel][t.topic].length,i=0;n>i;){if(this.subscriptions[t.channel][t.topic][i]===t){this.subscriptions[t.channel][t.topic].splice(i,1);break}i+=1}}};if(i={configuration:{bus:f,resolver:o,DEFAULT_CHANNEL:"/",SYSTEM_CHANNEL:"postal"},ChannelDefinition:e,SubscriptionDefinition:r,channel:function(t){return new e(t)},subscribe:function(t){return new r(t.channel||i.configuration.DEFAULT_CHANNEL,t.topic,t.callback)},publish:function(t){return t.channel=t.channel||i.configuration.DEFAULT_CHANNEL,i.configuration.bus.publish(t)},addWireTap:function(t){return this.configuration.bus.addWireTap(t)},linkChannels:function(n,s){var c=[];return n=t.isArray(n)?n:[n],s=t.isArray(s)?s:[s],t.each(n,function(n){var e=n.topic||"#";t.each(s,function(s){var r=s.channel||i.configuration.DEFAULT_CHANNEL;c.push(i.subscribe({channel:n.channel||i.configuration.DEFAULT_CHANNEL,topic:e,callback:function(n,c){var e=t.clone(c);e.topic=t.isFunction(s.topic)?s.topic(c.topic):s.topic||c.topic,e.channel=r,e.data=n,i.publish(e)}}))})}),c},utils:{getSubscribersFor:function(){var t=arguments[0],n=arguments[1];return 1===arguments.length&&(t=arguments[0].channel||i.configuration.DEFAULT_CHANNEL,n=arguments[0].topic),i.configuration.bus.subscriptions[t]&&Object.prototype.hasOwnProperty.call(i.configuration.bus.subscriptions[t],n)?i.configuration.bus.subscriptions[t][n]:[]},reset:function(){i.configuration.bus.reset(),i.configuration.resolver.reset()}}},f.subscriptions[i.configuration.SYSTEM_CHANNEL]={},n&&n.hasOwnProperty("__postalReady__")&&t.isArray(n.__postalReady__))for(;n.__postalReady__.length;)n.__postalReady__.shift().onReady(i);return i});
\ No newline at end of file
+(function(t,n){"object"==typeof module&&module.exports?module.exports=function(t){return t=t||require("underscore"),n(t)}:"function"==typeof define&&define.amd?define(["underscore"],function(i){return n(i,t)}):t.postal=n(t._,t)})(this,function(t,n){var i,e=function(t){var n=t.owner[t.prop];if("function"!=typeof n)throw Error("Strategies can only target methods.");var i=[],e=t.context||t.owner,r=function(){var t=0,r=function r(){var c,s=Array.prototype.slice.call(arguments,0),o=t;t+=1,i.length>o?(c=i[o],c.fn.apply(c.context||e,[r].concat(s))):n.apply(e,s)};r.apply(this,arguments)};return r.target=function(){return n},r.context=function(t){return 0===arguments.length?e:(e=t,undefined)},r.strategies=function(){return i},r.useStrategy=function(t){for(var n=0,e=!1;i.length>n;){if(i[n].name===t.name){i[n]=t,e=!0;break}n+=1}e||i.push(t)},r.reset=function(){i=[]},t.lazyInit?(n.useStrategy=function(){t.owner[t.prop]=r,r.useStrategy.apply(r,arguments)},n.context=function(){return t.owner[t.prop]=r,r.context.apply(r,arguments)},n):r},r={setTimeout:function(t){return{name:"setTimeout",fn:function(n,i,e){setTimeout(function(){n(i,e)},t)}}},after:function(n,i){var e=t.after(n,i);return{name:"after",fn:function(t,n,i){e(),t(n,i)}}},throttle:function(n){return{name:"throttle",fn:t.throttle(function(t,n,i){t(n,i)},n)}},debounce:function(n,i){return{name:"debounce",fn:t.debounce(function(t,n,i){t(n,i)},n,!!i)}},predicate:function(t){return{name:"predicate",fn:function(n,i,e){t.call(this,i,e)&&n.call(this,i,e)}}},distinct:function(t){t=t||{};var n=function(t){return t[0]},i=t.all?new s(n):new c(n);return{name:"distinct",fn:function(t,n,e){i(n)&&t(n,e)}}}},c=function(n){var i;return function(){var e=n(arguments),r=!1;return t.isString(e)?(r=e===i,i=e):(r=t.isEqual(e,i),i=t.clone(e)),!r}},s=function(n){var i=[];return function(){var e=n(arguments),r=!t.any(i,function(n){return t.isObject(e)||t.isArray(e)?t.isEqual(e,n):e===n});return r&&i.push(e),r}},o=function(t){this.channel=t||i.configuration.DEFAULT_CHANNEL};o.prototype.subscribe=function(){return 1===arguments.length?new a(this.channel,arguments[0].topic,arguments[0].callback):new a(this.channel,arguments[0],arguments[1])},o.prototype.publish=function(){var t=1===arguments.length?"[object String]"===Object.prototype.toString.call(arguments[0])?{topic:arguments[0]}:arguments[0]:{topic:arguments[0],data:arguments[1]};return t.channel=this.channel,i.configuration.bus.publish(t)};var a=function(t,n,e){this.channel=t,this.topic=n,this.subscribe(e),i.configuration.bus.publish({channel:i.configuration.SYSTEM_CHANNEL,topic:"subscription.created",data:{event:"subscription.created",channel:t,topic:n}}),i.configuration.bus.subscribe(this)};a.prototype={unsubscribe:function(){this.inactive||(this.inactive=!0,i.configuration.bus.unsubscribe(this),i.configuration.bus.publish({channel:i.configuration.SYSTEM_CHANNEL,topic:"subscription.removed",data:{event:"subscription.removed",channel:this.channel,topic:this.topic}}))},defer:function(){return this.callback.useStrategy(i.configuration.strategies.setTimeout(0)),this},disposeAfter:function(n){if(t.isNaN(n)||0>=n)throw"The value provided to disposeAfter (maxCalls) must be a number greater than zero.";var e=this;return e.callback.useStrategy(i.configuration.strategies.after(n,function(){e.unsubscribe.call(e)})),e},distinctUntilChanged:function(){return this.callback.useStrategy(i.configuration.strategies.distinct()),this},distinct:function(){return this.callback.useStrategy(i.configuration.strategies.distinct({all:!0})),this},once:function(){return this.disposeAfter(1),this},withConstraint:function(n){if(!t.isFunction(n))throw"Predicate constraint must be a function";return this.callback.useStrategy(i.configuration.strategies.predicate(n)),this},withContext:function(t){return this.callback.context(t),this},withDebounce:function(n,i){if(t.isNaN(n))throw"Milliseconds must be a number";var e=this.callback;return this.callback=t.debounce(e,n,!!i),this},withDelay:function(n){if(t.isNaN(n))throw"Milliseconds must be a number";return this.callback.useStrategy(i.configuration.strategies.setTimeout(n)),this},withThrottle:function(n){if(t.isNaN(n))throw"Milliseconds must be a number";return this.callback.useStrategy(i.configuration.strategies.throttle(n)),this},subscribe:function(t){return this.callback=t,this.callback=new e({owner:this,prop:"callback",context:this,lazyInit:!0}),this}};var u={cache:{},regex:{},compare:function(n,i){var e,r,c,s=this.cache[i]&&this.cache[i][n];return s!==undefined?s:((r=this.regex[n])||(e="^"+t.map(n.split("."),function(t){var n="";return c&&(n="#"!==c?"\\.\\b":"\\b"),n+="#"===t?"[\\s\\S]*":"*"===t?"[^.]+":t,c=t,n}).join("")+"$",r=this.regex[n]=RegExp(e)),this.cache[i]=this.cache[i]||{},this.cache[i][n]=s=r.test(i),s)},reset:function(){this.cache={},this.regex={}}},h=function(t,n){!t.inactive&&i.configuration.resolver.compare(t.topic,n.topic)&&t.callback.call(t.callback.context?t.callback.context():this,n.data,n)},f=0,l=[],p=function(){for(;l.length;)l.shift().unsubscribe()},b={addWireTap:function(t){var n=this;return n.wireTaps.push(t),function(){var i=n.wireTaps.indexOf(t);-1!==i&&n.wireTaps.splice(i,1)}},publish:function(n){return++f,n.timeStamp=new Date,t.each(this.wireTaps,function(t){t(n.data,n)}),this.subscriptions[n.channel]&&t.each(this.subscriptions[n.channel],function(t){for(var i,e=0,r=t.length;r>e;)(i=t[e++])&&h(i,n)}),0===--f&&p(),n},reset:function(){this.subscriptions&&(t.each(this.subscriptions,function(n){t.each(n,function(t){for(;t.length;)t.pop().unsubscribe()})}),this.subscriptions={})},subscribe:function(t){var n,i=this.subscriptions[t.channel];return i||(i=this.subscriptions[t.channel]={}),n=this.subscriptions[t.channel][t.topic],n||(n=this.subscriptions[t.channel][t.topic]=[]),n.push(t),t},subscriptions:{},wireTaps:[],unsubscribe:function(t){if(f)return l.push(t),undefined;if(this.subscriptions[t.channel][t.topic])for(var n=this.subscriptions[t.channel][t.topic].length,i=0;n>i;){if(this.subscriptions[t.channel][t.topic][i]===t){this.subscriptions[t.channel][t.topic].splice(i,1);break}i+=1}}};if(i={configuration:{bus:b,resolver:u,DEFAULT_CHANNEL:"/",SYSTEM_CHANNEL:"postal",strategies:r},ChannelDefinition:o,SubscriptionDefinition:a,channel:function(t){return new o(t)},subscribe:function(t){return new a(t.channel||i.configuration.DEFAULT_CHANNEL,t.topic,t.callback)},publish:function(t){return t.channel=t.channel||i.configuration.DEFAULT_CHANNEL,i.configuration.bus.publish(t)},addWireTap:function(t){return this.configuration.bus.addWireTap(t)},linkChannels:function(n,e){var r=[];return n=t.isArray(n)?n:[n],e=t.isArray(e)?e:[e],t.each(n,function(n){var c=n.topic||"#";t.each(e,function(e){var s=e.channel||i.configuration.DEFAULT_CHANNEL;r.push(i.subscribe({channel:n.channel||i.configuration.DEFAULT_CHANNEL,topic:c,callback:function(n,r){var c=t.clone(r);c.topic=t.isFunction(e.topic)?e.topic(r.topic):e.topic||r.topic,c.channel=s,c.data=n,i.publish(c)}}))})}),r},utils:{getSubscribersFor:function(){var t=arguments[0],n=arguments[1];return 1===arguments.length&&(t=arguments[0].channel||i.configuration.DEFAULT_CHANNEL,n=arguments[0].topic),i.configuration.bus.subscriptions[t]&&Object.prototype.hasOwnProperty.call(i.configuration.bus.subscriptions[t],n)?i.configuration.bus.subscriptions[t][n]:[]},reset:function(){i.configuration.bus.reset(),i.configuration.resolver.reset()}}},b.subscriptions[i.configuration.SYSTEM_CHANNEL]={},n&&n.hasOwnProperty("__postalReady__")&&t.isArray(n.__postalReady__))for(;n.__postalReady__.length;)n.__postalReady__.shift().onReady(i);return i});
\ No newline at end of file
diff --git a/spec/ConsecutiveDistinctPredicate.spec.js b/spec/ConsecutiveDistinctPredicate.spec.js
index dc51d41..cf22cb3 100644
--- a/spec/ConsecutiveDistinctPredicate.spec.js
+++ b/spec/ConsecutiveDistinctPredicate.spec.js
@@ -1,7 +1,10 @@
/* global describe, postal, it, after, before, expect, ConsecutiveDistinctPredicate */
describe( "ConsecutiveDistinctPredicate", function () {
+ var accessor = function(args) {
+ return args[0];
+ };
describe( "When calling the function with the same data multiple times", function () {
- var pred = new ConsecutiveDistinctPredicate(),
+ var pred = new ConsecutiveDistinctPredicate(accessor),
data = { name : "Dr Who" },
results = [];
results.push( pred( data ) );
@@ -19,7 +22,7 @@ describe( "ConsecutiveDistinctPredicate", function () {
} );
} );
describe( "When calling the function with different data every time", function () {
- var predA = new ConsecutiveDistinctPredicate(),
+ var predA = new ConsecutiveDistinctPredicate(accessor),
data = { name : "Amelia" },
res = [];
res.push( predA( data ) );
@@ -39,7 +42,7 @@ describe( "ConsecutiveDistinctPredicate", function () {
} );
} );
describe( "When calling the function with different data every two calls", function () {
- var predA = new ConsecutiveDistinctPredicate(),
+ var predA = new ConsecutiveDistinctPredicate(accessor),
data = { name : "Amelia" },
res = [];
res.push( predA( data ) );
diff --git a/spec/DistinctPredicate.spec.js b/spec/DistinctPredicate.spec.js
index a840595..43515c9 100644
--- a/spec/DistinctPredicate.spec.js
+++ b/spec/DistinctPredicate.spec.js
@@ -1,8 +1,11 @@
/* global describe, postal, it, after, before, expect, DistinctPredicate */
describe( 'DistinctPredicate', function () {
+ var accessor = function(args) {
+ return args[0];
+ };
describe( 'When calling the function with the same data object multiple times', function () {
- var pred = new DistinctPredicate(),
+ var pred = new DistinctPredicate(accessor),
results = [];
results.push( pred( {career : 'ninja'} ) );
@@ -21,7 +24,7 @@ describe( 'DistinctPredicate', function () {
} );
describe( 'When calling the function with the same primitive multiple times', function () {
- var pred = new DistinctPredicate(),
+ var pred = new DistinctPredicate(accessor),
results = [];
results.push( pred( 'ninja' ) );
@@ -40,7 +43,7 @@ describe( 'DistinctPredicate', function () {
} );
describe( 'When calling the function with the same array multiple times', function () {
- var pred = new DistinctPredicate(),
+ var pred = new DistinctPredicate(accessor),
results = [];
results.push( pred( ['Jack Black', 'Kyle Gass'] ) );
@@ -61,7 +64,7 @@ describe( 'DistinctPredicate', function () {
// ------------------------------------------
describe( 'When calling the function with different data object every time', function () {
- var pred = new DistinctPredicate(),
+ var pred = new DistinctPredicate(accessor),
results = [];
results.push( pred( {codename : 'tinker'} ) );
@@ -78,7 +81,7 @@ describe( 'DistinctPredicate', function () {
} );
describe( 'When calling the function with different primitive every time', function () {
- var pred = new DistinctPredicate(),
+ var pred = new DistinctPredicate(accessor),
results = [];
results.push( pred( 100.5 ) );
@@ -95,7 +98,7 @@ describe( 'DistinctPredicate', function () {
} );
describe( 'When calling the function with different array every time', function () {
- var pred = new DistinctPredicate(),
+ var pred = new DistinctPredicate(accessor),
results = [];
results.push( pred( [] ) );
@@ -114,7 +117,7 @@ describe( 'DistinctPredicate', function () {
// ------------------------------------------
describe( 'When calling the function with different data object between duplicates', function () {
- var pred = new DistinctPredicate(),
+ var pred = new DistinctPredicate(accessor),
results = [];
results.push( pred( {game : 'Diablo 3'} ) );
@@ -141,7 +144,7 @@ describe( 'DistinctPredicate', function () {
} );
describe( 'When calling the function with different primitive between duplicates', function () {
- var pred = new DistinctPredicate(),
+ var pred = new DistinctPredicate(accessor),
results = [];
results.push( pred( 'Stan Marsh' ) );
@@ -168,7 +171,7 @@ describe( 'DistinctPredicate', function () {
} );
describe( 'When calling the function with different array between duplicates', function () {
- var pred = new DistinctPredicate(),
+ var pred = new DistinctPredicate(accessor),
results = [];
results.push( pred( [] ) );
diff --git a/spec/Postal.spec.js b/spec/Postal.spec.js
index ca81e30..2cbf972 100644
--- a/spec/Postal.spec.js
+++ b/spec/Postal.spec.js
@@ -40,11 +40,8 @@ describe( "Postal", function () {
it( "should have set subscription topic value", function () {
expect( sub.topic ).to.be( "MyTopic" );
} );
- it( "should have defaulted the subscription constraints array", function () {
- expect( sub.constraints.length ).to.be( 0 );
- } );
it( "should have defaulted the subscription context value", function () {
- expect( sub.context ).to.be( null );
+ expect( sub.callback.context() ).to.be( sub );
} );
it( "should have captured subscription creation event", function () {
expect( caughtSubscribeEvent ).to.be.ok();
@@ -143,9 +140,9 @@ describe( "Postal", function () {
} );
it( "should produce expected messages", function () {
expect( results.length ).to.be( 3 );
- expect( results[0] ).to.be( "1 received message" );
- expect( results[1] ).to.be( "2 received message" );
- expect( results[2] ).to.be( "unsubscribed" );
+ expect( results[0] ).to.be( "unsubscribed" );
+ expect( results[1] ).to.be( "1 received message" );
+ expect( results[2] ).to.be( "2 received message" );
} );
} );
} );
@@ -221,8 +218,7 @@ describe( "Postal", function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
subInvokedCnt++;
- } )
- .distinctUntilChanged();
+ }).distinctUntilChanged();
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
@@ -234,9 +230,9 @@ describe( "Postal", function () {
postal.utils.reset();
subInvokedCnt = 0;
} );
- it( "should have a constraint on the subscription", function () {
- expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 1 );
- } );
+ it( "callback should be a strategy", function () {
+ expect( typeof postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].callback.context ).to.be( "function" );
+ } );
it( "subscription callback should be invoked once", function () {
expect( subInvokedCnt ).to.be( 1 );
} );
@@ -247,10 +243,9 @@ describe( "Postal", function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
recvd = true;
- } )
- .withConstraint( function () {
- return true;
- } );
+ }).withConstraint( function () {
+ return true;
+ });
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
@@ -258,7 +253,7 @@ describe( "Postal", function () {
recvd = false;
} );
it( "should have a constraint on the subscription", function () {
- expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 1 );
+ expect( subscription.callback.strategies()[0].name ).to.be( "predicate" );
} );
it( "should have invoked the subscription callback", function () {
expect( recvd ).to.be.ok();
@@ -281,70 +276,41 @@ describe( "Postal", function () {
recvd = false;
} );
it( "should have a constraint on the subscription", function () {
- expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 1 );
+ expect( subscription.callback.strategies()[0].name ).to.be( "predicate" );
} );
it( "should not have invoked the subscription callback", function () {
expect( recvd ).to.not.be.ok();
} );
} );
- describe( "When subscribing with multiple constraints returning true", function () {
+ describe( "When subscribing with multiple constraints", function () {
var recvd = false;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
recvd = true;
} )
- .withConstraints( [function () {
- return true;
- },
- function () {
- return true;
- },
- function () {
- return true;
- }] );
+ .withConstraint(function () {
+ return false;
+ })
+ .withConstraint(function () {
+ return false;
+ })
+ .withConstraint(function () {
+ return true;
+ });
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
recvd = false;
} );
- it( "should have a constraint on the subscription", function () {
- expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 3 );
+ it( "should overwrite constraint with last one passed in", function () {
+ expect( subscription.callback.strategies().length ).to.be( 1 );
} );
it( "should have invoked the callback", function () {
expect( recvd ).to.be.ok();
} );
} );
- describe( "When subscribing with multiple constraints and one returning false", function () {
- var recvd = false;
- before( function () {
- channel = postal.channel( "MyChannel" );
- subscription = channel.subscribe( "MyTopic", function ( data ) {
- recvd = true;
- } )
- .withConstraints( [function () {
- return true;
- },
- function () {
- return false;
- },
- function () {
- return true;
- }] );
- channel.publish( "MyTopic", "Testing123" );
- } );
- after( function () {
- postal.utils.reset();
- recvd = false;
- } );
- it( "should have a constraint on the subscription", function () {
- expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 3 );
- } );
- it( "should not have invoked the callback", function () {
- expect( recvd ).to.not.be.ok()
- } );
- } );
describe( "When subscribing with the context being set", function () {
var count = 0,
obj = {
@@ -610,11 +576,8 @@ describe( "Postal", function () {
it( "should have set subscription topic value", function () {
expect( sub.topic ).to.be( "MyTopic" );
} );
- it( "should have defaulted the subscription constraints array", function () {
- expect( sub.constraints.length ).to.be( 0 );
- } );
- it( "should have defaulted the subscription context value", function () {
- expect( sub.context ).to.be( null );
+ it( "should have context method", function () {
+ expect( typeof sub.callback.context ).to.be( "function" );
} );
} );
describe( "When using global channel api", function () {
@@ -683,8 +646,7 @@ describe( "Postal", function () {
it( "should have created a subscription definition", function () {
expect( sub.channel ).to.be( "MyChannel" );
expect( sub.topic ).to.be( "MyTopic" );
- expect( sub.constraints.length ).to.be( 0 );
- expect( sub.context ).to.be( null );
+ expect( sub.context ).to.be( undefined );
} );
it( "should have created a resolver cache entry", function () {
expect( _.isEmpty( resolver ) ).to.not.be.ok()
diff --git a/spec/SubscriptionDefinition.spec.js b/spec/SubscriptionDefinition.spec.js
index 8891079..0e40dd5 100644
--- a/spec/SubscriptionDefinition.spec.js
+++ b/spec/SubscriptionDefinition.spec.js
@@ -34,12 +34,6 @@ describe( "SubscriptionDefinition", function () {
it( "should set the callback", function () {
expect( sDef.callback ).to.be( NO_OP );
} );
- it( "should default the constraints", function () {
- expect( sDef.constraints.length ).to.be( 0 );
- } );
- it( "should default the context", function () {
- expect( sDef.context ).to.be( null );
- } );
it( "should fire the subscription.created message", function () {
expect( caughtSubscribeEvent ).to.be( true );
} );
@@ -48,29 +42,18 @@ describe( "SubscriptionDefinition", function () {
describe( "When setting distinctUntilChanged", function () {
var sDefa = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP ).distinctUntilChanged();
- it( "Should add a DistinctPredicate constraint to the configuration constraints", function () {
- expect( sDefa.constraints.length ).to.be( 1 );
- } );
+ it( "callback should be a strategy", function () {
+ expect( typeof sDefa.callback.context ).to.be( "function" );
+ } );
} );
describe( "When adding a constraint", function () {
var sDefb = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP ).withConstraint( function () {
} );
- it( "Should add a constraint", function () {
- expect( sDefb.constraints.length ).to.be( 1 );
- } );
- } );
-
- describe( "When adding multiple constraints", function () {
- var sDefc = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP ).withConstraints( [function () {
- }, function () {
- }, function () {
- }] );
-
- it( "Should add a constraint", function () {
- expect( sDefc.constraints.length ).to.be( 3 );
- } );
+ it( "callback should be a strategy", function () {
+ expect( typeof sDefb.callback.context ).to.be( "function" );
+ } );
} );
describe( "When setting the context", function () {
@@ -86,7 +69,7 @@ describe( "SubscriptionDefinition", function () {
postal.publish( { channel : "TestChannel", topic : "TestTopic", data : "Oh, hai"} )
it( "Should set context", function () {
- expect( sDefd.context ).to.be( obj );
+ expect( sDefd.callback.context() ).to.be( obj );
} );
it( "Should apply context to predicate/constraint", function () {
expect( name ).to.be( "Rose" );
diff --git a/spec/index.html b/spec/index.html
index fd516ba..d6e618c 100644
--- a/spec/index.html
+++ b/spec/index.html
@@ -15,6 +15,8 @@
+
+
diff --git a/src/Api.js b/src/Api.js
index dfdc4b6..4f1c447 100644
--- a/src/Api.js
+++ b/src/Api.js
@@ -5,7 +5,8 @@ postal = {
bus : localBus,
resolver : bindingsResolver,
DEFAULT_CHANNEL : "/",
- SYSTEM_CHANNEL : "postal"
+ SYSTEM_CHANNEL : "postal",
+ strategies : strats
},
ChannelDefinition : ChannelDefinition,
diff --git a/src/ConsecutiveDistinctPredicate.js b/src/ConsecutiveDistinctPredicate.js
index e94ed48..fc6aab0 100644
--- a/src/ConsecutiveDistinctPredicate.js
+++ b/src/ConsecutiveDistinctPredicate.js
@@ -1,7 +1,8 @@
/*jshint -W098 */
-var ConsecutiveDistinctPredicate = function () {
+var ConsecutiveDistinctPredicate = function (argsAccessor) {
var previous;
- return function ( data ) {
+ return function () {
+ var data = argsAccessor(arguments);
var eq = false;
if ( _.isString( data ) ) {
eq = data === previous;
diff --git a/src/DistinctPredicate.js b/src/DistinctPredicate.js
index 16ad115..0303067 100644
--- a/src/DistinctPredicate.js
+++ b/src/DistinctPredicate.js
@@ -1,8 +1,9 @@
/*jshint -W098 */
-var DistinctPredicate = function () {
+var DistinctPredicate = function (argsAccessor) {
var previous = [];
- return function ( data ) {
+ return function () {
+ var data = argsAccessor(arguments);
var isDistinct = !_.any( previous, function ( p ) {
if ( _.isObject( data ) || _.isArray( data ) ) {
return _.isEqual( data, p );
@@ -12,6 +13,6 @@ var DistinctPredicate = function () {
if ( isDistinct ) {
previous.push( data );
}
- return isDistinct;
+ return isDistinct;
};
};
\ No newline at end of file
diff --git a/src/LocalBus.js b/src/LocalBus.js
index fa5e770..3a9927b 100644
--- a/src/LocalBus.js
+++ b/src/LocalBus.js
@@ -1,13 +1,7 @@
/* global postal */
var fireSub = function ( subDef, envelope ) {
if ( !subDef.inactive && postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
- if ( _.all( subDef.constraints, function ( constraint ) {
- return constraint.call( subDef.context, envelope.data, envelope );
- } ) ) {
- if ( typeof subDef.callback === "function" ) {
- subDef.callback.call( subDef.context, envelope.data, envelope );
- }
- }
+ subDef.callback.call( subDef.callback.context ? subDef.callback.context() : this, envelope.data, envelope );
}
};
diff --git a/src/SubscriptionDefinition.js b/src/SubscriptionDefinition.js
index b6874a5..531ba43 100644
--- a/src/SubscriptionDefinition.js
+++ b/src/SubscriptionDefinition.js
@@ -3,9 +3,7 @@
var SubscriptionDefinition = function ( channel, topic, callback ) {
this.channel = channel;
this.topic = topic;
- this.callback = callback;
- this.constraints = [];
- this.context = null;
+ this.subscribe(callback);
postal.configuration.bus.publish( {
channel : postal.configuration.SYSTEM_CHANNEL,
topic : "subscription.created",
@@ -36,13 +34,7 @@ SubscriptionDefinition.prototype = {
},
defer : function () {
- var self = this;
- var fn = this.callback;
- this.callback = function ( data, env ) {
- setTimeout( function () {
- fn.call( self.context, data, env );
- }, 0 );
- };
+ this.callback.useStrategy(postal.configuration.strategies.setTimeout(0));
return this;
},
@@ -50,26 +42,20 @@ SubscriptionDefinition.prototype = {
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
}
- var self = this;
- var fn = this.callback;
- var dispose = _.after( maxCalls, _.bind( function () {
- this.unsubscribe();
- }, this ) );
-
- this.callback = function () {
- fn.apply( self.context, arguments );
- dispose();
- };
- return this;
+ var self = this;
+ self.callback.useStrategy(postal.configuration.strategies.after(maxCalls, function() {
+ self.unsubscribe.call(self);
+ }));
+ return self;
},
distinctUntilChanged : function () {
- this.withConstraint( new ConsecutiveDistinctPredicate() );
+ this.callback.useStrategy(postal.configuration.strategies.distinct());
return this;
},
distinct : function () {
- this.withConstraint( new DistinctPredicate() );
+ this.callback.useStrategy(postal.configuration.strategies.distinct({ all: true }));
return this;
},
@@ -82,22 +68,12 @@ SubscriptionDefinition.prototype = {
if ( !_.isFunction( predicate ) ) {
throw "Predicate constraint must be a function";
}
- this.constraints.push( predicate );
+ this.callback.useStrategy(postal.configuration.strategies.predicate(predicate));
return this;
},
- withConstraints : function ( predicates ) {
- var self = this;
- if ( _.isArray( predicates ) ) {
- _.each( predicates, function ( predicate ) {
- self.withConstraint( predicate );
- } );
- }
- return self;
- },
-
withContext : function ( context ) {
- this.context = context;
+ this.callback.context(context);
return this;
},
@@ -114,13 +90,7 @@ SubscriptionDefinition.prototype = {
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
- var self = this;
- var fn = this.callback;
- this.callback = function ( data, env ) {
- setTimeout( function () {
- fn.call( self.context, data, env );
- }, milliseconds );
- };
+ this.callback.useStrategy(postal.configuration.strategies.setTimeout(milliseconds));
return this;
},
@@ -128,13 +98,18 @@ SubscriptionDefinition.prototype = {
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
- var fn = this.callback;
- this.callback = _.throttle( fn, milliseconds );
+ this.callback.useStrategy(postal.configuration.strategies.throttle(milliseconds));
return this;
},
subscribe : function ( callback ) {
this.callback = callback;
+ this.callback = new Strategy({
+ owner : this,
+ prop : "callback",
+ context : this, // TODO: is this the best option?
+ lazyInit : true
+ });
return this;
}
};
\ No newline at end of file
diff --git a/src/postal.js b/src/postal.js
index 9bba05d..be44617 100644
--- a/src/postal.js
+++ b/src/postal.js
@@ -19,6 +19,8 @@
var postal;
+ //import("strategy.js");
+ //import("strategies.js");
//import("ConsecutiveDistinctPredicate.js");
//import("DistinctPredicate.js");
//import("ChannelDefinition.js");
diff --git a/src/strategies.js b/src/strategies.js
new file mode 100644
index 0000000..d2cc60f
--- /dev/null
+++ b/src/strategies.js
@@ -0,0 +1,66 @@
+/* global DistinctPredicate,ConsecutiveDistinctPredicate */
+var strats = {
+ setTimeout: function(ms) {
+ return {
+ name: "setTimeout",
+ fn: function (next, data, envelope) {
+ setTimeout(function () {
+ next(data, envelope);
+ }, ms);
+ }
+ };
+ },
+ after: function(maxCalls, callback) {
+ var dispose = _.after(maxCalls, callback);
+ return {
+ name: "after",
+ fn: function (next, data, envelope) {
+ dispose();
+ next(data, envelope);
+ }
+ };
+ },
+ throttle : function(ms) {
+ return {
+ name: "throttle",
+ fn: _.throttle(function(next, data, envelope) {
+ next(data, envelope);
+ }, ms)
+ };
+ },
+ debounce: function(ms, immediate) {
+ return {
+ name: "debounce",
+ fn: _.debounce(function(next, data, envelope) {
+ next(data, envelope);
+ }, ms, !!immediate)
+ };
+ },
+ predicate: function(pred) {
+ return {
+ name: "predicate",
+ fn: function(next, data, envelope) {
+ if(pred.call(this, data, envelope)) {
+ next.call(this, data, envelope);
+ }
+ }
+ };
+ },
+ distinct : function(options) {
+ options = options || {};
+ var accessor = function(args) {
+ return args[0];
+ };
+ var check = options.all ?
+ new DistinctPredicate(accessor) :
+ new ConsecutiveDistinctPredicate(accessor);
+ return {
+ name : "distinct",
+ fn : function(next, data, envelope) {
+ if(check(data)) {
+ next(data, envelope);
+ }
+ }
+ };
+ }
+};
\ No newline at end of file
diff --git a/src/strategy.js b/src/strategy.js
new file mode 100644
index 0000000..b50bd26
--- /dev/null
+++ b/src/strategy.js
@@ -0,0 +1,68 @@
+var Strategy = function( options ) {
+ var _target = options.owner[options.prop];
+ if ( typeof _target !== "function" ) {
+ throw new Error( "Strategies can only target methods." );
+ }
+ var _strategies = [];
+ var _context = options.context || options.owner;
+ var strategy = function() {
+ var idx = 0;
+ var next = function next() {
+ var args = Array.prototype.slice.call( arguments, 0 );
+ var thisIdx = idx;
+ var strategy;
+ idx += 1;
+ if ( thisIdx < _strategies.length ) {
+ strategy = _strategies[thisIdx];
+ strategy.fn.apply( strategy.context || _context, [next].concat( args ) );
+ } else {
+ _target.apply( _context, args );
+ }
+ };
+ next.apply( this, arguments );
+ };
+ strategy.target = function() {
+ return _target;
+ };
+ strategy.context = function( ctx ) {
+ if ( arguments.length === 0 ) {
+ return _context;
+ } else {
+ _context = ctx;
+ }
+ };
+ strategy.strategies = function() {
+ return _strategies;
+ };
+ strategy.useStrategy = function( strategy ) {
+ var idx = 0,
+ exists = false;
+ while ( idx < _strategies.length ) {
+ if ( _strategies[idx].name === strategy.name ) {
+ _strategies[idx] = strategy;
+ exists = true;
+ break;
+ }
+ idx += 1;
+ }
+ if ( !exists ) {
+ _strategies.push( strategy );
+ }
+ };
+ strategy.reset = function() {
+ _strategies = [];
+ };
+ if ( options.lazyInit ) {
+ _target.useStrategy = function() {
+ options.owner[options.prop] = strategy;
+ strategy.useStrategy.apply( strategy, arguments );
+ };
+ _target.context = function() {
+ options.owner[options.prop] = strategy;
+ return strategy.context.apply( strategy, arguments );
+ };
+ return _target;
+ } else {
+ return strategy;
+ }
+};
\ No newline at end of file