Initial Tests Passing on Strategies Addition

This commit is contained in:
ifandelse 2014-01-10 01:08:35 -05:00
parent 186c541a37
commit 45cb57e0e5
19 changed files with 712 additions and 333 deletions

View file

@ -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,

File diff suppressed because one or more lines are too long

View file

@ -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,

File diff suppressed because one or more lines are too long

View file

@ -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,

2
lib/postal.min.js vendored

File diff suppressed because one or more lines are too long

View file

@ -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 ) );

View file

@ -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( [] ) );

View file

@ -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()

View file

@ -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" );

View file

@ -15,6 +15,8 @@
<script type="text/javascript" src="../ext/amplify.core.js"></script>
<script type="text/javascript" src="../ext/amplify.store.js"></script>
<script type="text/javascript" src="../ext/underscore.js"></script>
<script type="text/javascript" src="../src/strategy.js"></script>
<script type="text/javascript" src="../src/strategies.js"></script>
<script type="text/javascript" src="../src/DistinctPredicate.js"></script>
<script type="text/javascript" src="../src/ConsecutiveDistinctPredicate.js"></script>
<script type="text/javascript" src="../src/ChannelDefinition.js"></script>

View file

@ -5,7 +5,8 @@ postal = {
bus : localBus,
resolver : bindingsResolver,
DEFAULT_CHANNEL : "/",
SYSTEM_CHANNEL : "postal"
SYSTEM_CHANNEL : "postal",
strategies : strats
},
ChannelDefinition : ChannelDefinition,

View file

@ -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;

View file

@ -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;
};
};

View file

@ -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 );
}
};

View file

@ -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;
}
};

View file

@ -19,6 +19,8 @@
var postal;
//import("strategy.js");
//import("strategies.js");
//import("ConsecutiveDistinctPredicate.js");
//import("DistinctPredicate.js");
//import("ChannelDefinition.js");

66
src/strategies.js Normal file
View file

@ -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);
}
}
};
}
};

68
src/strategy.js Normal file
View file

@ -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;
}
};