README Updates

This commit is contained in:
Jim Cowart 2012-03-22 01:34:53 -04:00
parent db785cecdb
commit 1f8bf876e2
21 changed files with 854 additions and 1334 deletions

View file

@ -25,6 +25,14 @@ JavaScript:
// doesn't specify a channel name, so it defaults to "/" (DEFAULT_CHANNEL)
var channel = postal.channel( { topic: "Name.Changed" } );
// this call is identical to the one above
var channel = postal.channel( "Name.Changed" )
// To specify a channel name you can do one of the following
var channel = postal.channel( { channel: "MyChannel", topic: "MyTopic" } );
var channel = postal.channel( "MyChannel","MyTopic" );
// subscribe
var subscription = channel.subscribe( function( data, envelope ) {
$( "#example1" ).html( "Name: " + data.name );
@ -60,13 +68,18 @@ var starChannel = postal.channel( { channel: "Doctor.Who", topic: "DrWho.*.Chang
starSubscription = starChannel.subscribe( function( data ) {
$( '<li>' + data.type + " Changed: " + data.value + '</li>' ).appendTo( "#example3" );
});
// demonstrating how we're re-using the channel delcared above to publish, but overriding the topic in the second argument
starChannel.publish( { type: "Name", value:"Rose" }, { topic: "DrWho.NinthDoctor.Companion.Changed" } );
starChannel.publish( { type: "Name", value:"Martha" }, { topic: "DrWho.TenthDoctor.Companion.Changed" } );
starChannel.publish( { type: "Name", value:"Amy" }, { topic: "DrWho.Eleventh.Companion.Changed" } );
starChannel.publish( { type: "Location", value: "The Library" }, { topic: "DrWho.Location.Changed" } );
starChannel.publish( { type: "DrumBeat", value: "This won't trigger any subscriptions" }, { topic: "TheMaster.DrumBeat.Changed" } );
starChannel.publish( { type: "Useless", value: "This won't trigger any subscriptions either" }, { topic: "Changed" } );
/*
demonstrating how we're re-using the channel delcared above to publish, but overriding the topic in the second argument
note to override the topic, you have to use the "envelope" structure, which means an object like:
{ channel: "myChannel", topic: "myTopic", data: { someProp: "SomeVal, moarData: "MoarValue" } };
The only thing to note is that since we are publishing from a channel definition, you don't need to pass "channel" (in fact, it would be ignored)
*/
starChannel.publish( { topic: "DrWho.NinthDoctor.Companion.Changed", data: { type: "Name", value:"Rose" } } );
starChannel.publish( { topic: "DrWho.TenthDoctor.Companion.Changed", data: { type: "Name", value:"Martha" } } );
starChannel.publish( { topic: "DrWho.Eleventh.Companion.Changed", data: { type: "Name", value:"Amy" } } );
starChannel.publish( { topic: "DrWho.Location.Changed", data: { type: "Location", value: "The Library" } } );
starChannel.publish( { topic: "TheMaster.DrumBeat.Changed", data: { type: "DrumBeat", value: "This won't trigger any subscriptions" } } );
starChannel.publish( { topic: "Changed", data: { type: "Useless", value: "This won't trigger any subscriptions either" } } );
starSubscription.unsubscribe();
```
@ -79,7 +92,7 @@ var dupChannel = postal.channel( { topic: "WeepingAngel.*" } ),
$( '<li>' + data.value + '</li>' ).appendTo( "#example4" );
}).ignoreDuplicates();
// demonstrating multiple channels per topic being used
// You can do it this way if you like, but the example above has nicer syntax (and less overhead)
// You can do it this way if you like, but the example above has nicer syntax (and *much* less overhead)
postal.channel( { topic: "WeepingAngel.DontBlink" } )
.publish( { value:"Don't Blink" } );
postal.channel( { topic: "WeepingAngel.DontBlink" } )

View file

@ -1,6 +1,6 @@
{
"source": "src/diags",
"output": "lib/browser/standard",
"output": "lib/browser",
"lint": {},
"uglify": {},
"gzip": {},

View file

@ -1,6 +1,6 @@
{
"source": "src/main",
"output": "lib/browser/standard",
"output": "lib/browser",
"lint": {},
"uglify": {},
"gzip": {},

View file

@ -1,33 +0,0 @@
(function(root, doc, factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
define(["postal", "underscore"], function(postal, _) {
return factory(postal, _, root, doc);
});
} else {
// Browser globals
factory(root.postal, root._, root, doc);
}
}(this, document, function(postal, _, global, document, undefined) {
// this returns a callback that, if invoked, removes the wireTap
return postal.addWireTap(function(data, envelope) {
var all = _.extend(envelope, { data: data });
if(!JSON) {
throw "This browser or environment does not provide JSON support";
}
try {
console.log(JSON.stringify(all));
}
catch(exception) {
try {
all.data = "ERROR: " + exception.message;
console.log(JSON.stringify(all));
}
catch(ex) {
console.log("Unable to parse data to JSON: " + exception);
}
}
});
}));

View file

@ -1 +0,0 @@
(function(a,b,c){typeof define=="function"&&define.amd?define(["postal","underscore"],function(d,e){return c(d,e,a,b)}):c(a.postal,a._,a,b)})(this,document,function(a,b,c,d,e){return a.addWireTap(function(a,c){var d=b.extend(c,{data:a});if(!JSON)throw"This browser or environment does not provide JSON support";try{console.log(JSON.stringify(d))}catch(e){try{d.data="ERROR: "+e.message,console.log(JSON.stringify(d))}catch(f){console.log("Unable to parse data to JSON: "+e)}}})})

View file

@ -1,414 +0,0 @@
/*
postal.js
Author: Jim Cowart
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
Version 0.6.0
*/
(function(root, doc, factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
define(["underscore"], function(_) {
return factory(_, root, doc);
});
} else {
// Browser globals
factory(root._, root, doc);
}
}(this, document, function(_, global, document, undefined) {
var DEFAULT_CHANNEL = "/",
DEFAULT_PRIORITY = 50,
DEFAULT_DISPOSEAFTER = 0,
SYSTEM_CHANNEL = "postal",
NO_OP = function() { };
var DistinctPredicate = function() {
var previous;
return function(data) {
var eq = false;
if(_.isString(data)) {
eq = data === previous;
previous = data;
}
else {
eq = _.isEqual(data, previous);
previous = _.clone(data);
}
return !eq;
};
};
var ChannelDefinition = function(channelName, defaultTopic) {
this.channel = channelName || DEFAULT_CHANNEL;
this._topic = defaultTopic || "";
};
ChannelDefinition.prototype = {
subscribe: function() {
var len = arguments.length;
if(len === 1) {
return new SubscriptionDefinition(this.channel, this._topic, arguments[0]);
}
else if (len === 2) {
return new SubscriptionDefinition(this.channel, arguments[0], arguments[1]);
}
},
publish: function(obj) {
var envelope = {
channel: this.channel,
topic: this._topic,
data: obj
};
// If this is an envelope....
if( obj.topic && obj.data ) {
envelope = obj;
envelope.channel = envelope.channel || this.channel;
}
envelope.timeStamp = new Date();
postal.configuration.bus.publish(envelope);
},
topic: function(topic) {
if(topic === this._topic) {
return this;
}
return new ChannelDefinition(this.channel, topic);
}
};
var SubscriptionDefinition = function(channel, topic, callback) {
this.channel = channel;
this.topic = topic;
this.callback = callback;
this.priority = DEFAULT_PRIORITY;
this.constraints = new Array(0);
this.maxCalls = DEFAULT_DISPOSEAFTER;
this.onHandled = NO_OP;
this.context = null;
postal.configuration.bus.publish({
channel: SYSTEM_CHANNEL,
topic: "subscription.created",
timeStamp: new Date(),
data: {
event: "subscription.created",
channel: channel,
topic: topic
}
});
postal.configuration.bus.subscribe(this);
};
SubscriptionDefinition.prototype = {
unsubscribe: function() {
postal.configuration.bus.unsubscribe(this);
postal.configuration.bus.publish({
channel: SYSTEM_CHANNEL,
topic: "subscription.removed",
timeStamp: new Date(),
data: {
event: "subscription.removed",
channel: this.channel,
topic: this.topic
}
});
},
defer: function() {
var fn = this.callback;
this.callback = function(data) {
setTimeout(fn,0,data);
};
return this;
},
disposeAfter: function(maxCalls) {
if(_.isNaN(maxCalls) || maxCalls <= 0) {
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
}
var fn = this.onHandled;
var dispose = _.after(maxCalls, _.bind(function() {
this.unsubscribe(this);
}, this));
this.onHandled = function() {
fn.apply(this.context, arguments);
dispose();
};
return this;
},
ignoreDuplicates: function() {
this.withConstraint(new DistinctPredicate());
return this;
},
whenHandledThenExecute: function(callback) {
if(! _.isFunction(callback)) {
throw "Value provided to 'whenHandledThenExecute' must be a function";
}
this.onHandled = callback;
return this;
},
withConstraint: function(predicate) {
if(! _.isFunction(predicate)) {
throw "Predicate constraint must be a function";
}
this.constraints.push(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;
return this;
},
withDebounce: function(milliseconds) {
if(_.isNaN(milliseconds)) {
throw "Milliseconds must be a number";
}
var fn = this.callback;
this.callback = _.debounce(fn, milliseconds);
return this;
},
withDelay: function(milliseconds) {
if(_.isNaN(milliseconds)) {
throw "Milliseconds must be a number";
}
var fn = this.callback;
this.callback = function(data) {
setTimeout(fn, milliseconds, data);
};
return this;
},
withPriority: function(priority) {
if(_.isNaN(priority)) {
throw "Priority must be a number";
}
this.priority = priority;
return this;
},
withThrottle: function(milliseconds) {
if(_.isNaN(milliseconds)) {
throw "Milliseconds must be a number";
}
var fn = this.callback;
this.callback = _.throttle(fn, milliseconds);
return this;
},
subscribe: function(callbacl) {
this.callback = callback;
return this;
}
};
var bindingsResolver = {
cache: { },
compare: function(binding, topic) {
if(this.cache[topic] && this.cache[topic][binding]) {
return true;
}
// binding.replace(/\./g,"\\.") // escape actual periods
// .replace(/\*/g, ".*") // asterisks match any value
// .replace(/#/g, "[A-Z,a-z,0-9]*"); // hash matches any alpha-numeric 'word'
var rgx = new RegExp("^" + binding.replace(/\./g,"\\.").replace(/\*/g, ".*").replace(/#/g, "[A-Z,a-z,0-9]*") + "$"),
result = rgx.test(topic);
if(result) {
if(!this.cache[topic]) {
this.cache[topic] = {};
}
this.cache[topic][binding] = true;
}
return result;
}
};
var localBus = {
subscriptions: {},
wireTaps: new Array(0),
publish: function(envelope) {
_.each(this.wireTaps,function(tap) {
tap(envelope.data, envelope);
});
_.each(this.subscriptions[envelope.channel], function(topic) {
_.each(topic, function(subDef){
if(postal.configuration.resolver.compare(subDef.topic, envelope.topic)) {
if(_.all(subDef.constraints, function(constraint) { return constraint(envelope.data); })) {
if(typeof subDef.callback === 'function') {
subDef.callback.apply(subDef.context, [envelope.data, envelope]);
subDef.onHandled();
}
}
}
});
});
},
subscribe: function(subDef) {
var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
if(!channel) {
channel = this.subscriptions[subDef.channel] = {};
}
subs = this.subscriptions[subDef.channel][subDef.topic];
if(!subs) {
subs = this.subscriptions[subDef.channel][subDef.topic] = new Array(0);
}
idx = subs.length - 1;
for(; idx >= 0; idx--) {
if(subs[idx].priority <= subDef.priority) {
subs.splice(idx + 1, 0, subDef);
found = true;
break;
}
}
if(!found) {
subs.unshift(subDef);
}
return subDef;
},
unsubscribe: function(config) {
if(this.subscriptions[config.channel][config.topic]) {
var len = this.subscriptions[config.channel][config.topic].length,
idx = 0;
for ( ; idx < len; idx++ ) {
if (this.subscriptions[config.channel][config.topic][idx] === config) {
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
break;
}
}
}
},
addWireTap: function(callback) {
var self = this;
self.wireTaps.push(callback);
return function() {
var idx = self.wireTaps.indexOf(callback);
if(idx !== -1) {
self.wireTaps.splice(idx,1);
}
};
}
};
var publishPicker = {
"1" : function(envelope) {
if(!envelope) {
throw new Error("publishing from the 'global' postal.publish call requires a valid envelope.");
}
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
envelope.timeStamp = new Date();
postal.configuration.bus.publish(envelope);
},
"2" : function(topic, data) {
postal.configuration.bus.publish({ channel: DEFAULT_CHANNEL, topic: topic, timeStamp: new Date(), data: data });
},
"3" : function(channel, topic, data) {
postal.configuration.bus.publish({ channel: channel, topic: topic, timeStamp: new Date(), data: data });
}
};
// save some setup time, albeit tiny
localBus.subscriptions[SYSTEM_CHANNEL] = {};
var postal = {
configuration: {
bus: localBus,
resolver: bindingsResolver
},
channel: function() {
var len = arguments.length,
channel = arguments[0],
tpc = arguments[1];
if(len === 1) {
if(Object.prototype.toString.call(channel) === "[object String]") {
channel = DEFAULT_CHANNEL;
tpc = arguments[0];
}
else {
channel = arguments[0].channel || DEFAULT_CHANNEL;
tpc = arguments[0].topic;
}
}
return new ChannelDefinition(channel, tpc);
},
subscribe: function(options) {
var callback = options.callback,
topic = options.topic,
channel = options.channel || DEFAULT_CHANNEL;
return new SubscriptionDefinition(channel, topic, callback);
},
publish: function() {
var len = arguments.length;
if(publishPicker[len]) {
publishPicker[len].apply(this, arguments);
}
},
addWireTap: function(callback) {
return this.configuration.bus.addWireTap(callback);
},
linkChannels: function(sources, destinations) {
var result = [];
if(!_.isArray(sources)) {
sources = [sources];
}
if(!_.isArray(destinations)) {
destinations = [destinations];
}
_.each(sources, function(source){
var sourceTopic = source.topic || "*";
_.each(destinations, function(destination) {
var destChannel = destination.channel || DEFAULT_CHANNEL;
result.push(
postal.subscribe({
channel: source.channel || DEFAULT_CHANNEL,
topic: source.topic || "*",
callback : function(data, env) {
var newEnv = env;
newEnv.topic = _.isFunction(destination.topic) ? destination.topic(env.topic) : destination.topic || env.topic;
newEnv.channel = destChannel;
newEnv.data = data;
postal.publish(newEnv);
}
})
);
});
});
return result;
}
};
global.postal = postal;
return postal;
}));

File diff suppressed because one or more lines are too long

View file

@ -1,90 +1,46 @@
QUnit.specify("postal.js", function(){
describe("bindingsResolver", function(){
/*describe("When calling regexify", function() {
describe("With a topic containing no special escape chars", function() {
var result = bindingsResolver.regexify("CoolTopic");
it("Should equal 'CoolTopic'", function(){
assert(result).equals("CoolTopic");
});
});
describe("With a topic containing periods", function() {
var result = bindingsResolver.regexify("Top.Middle.Bottom");
it("Only the periods should be escaped", function(){
assert(result).equals("Top\\.Middle\\.Bottom");
});
});
describe("With a topic containing a hash", function() {
var result = bindingsResolver.regexify("Top#Bottom");
it("Only the hash should be escaped", function(){
assert(result).equals("Top[A-Z,a-z,0-9]*Bottom");
});
});
describe("With a topic containing a hash and periods", function() {
var result = bindingsResolver.regexify("Top.#.Bottom");
it("The hash should be escaped for alphanumeric regex", function(){
assert(result).equals("Top\\.[A-Z,a-z,0-9]*\\.Bottom");
});
});
describe("With a topic containing a hash and asterisk", function() {
var result = bindingsResolver.regexify("Top#Bottom*");
it("The hash should be escaped for alphanumeric regex", function(){
assert(result).equals("Top[A-Z,a-z,0-9]*Bottom.*");
});
});
describe("With a topic containing a hash, asterisk and periods", function() {
var result = bindingsResolver.regexify("Top.#.Bottom.*");
it("The hash should be escaped for alphanumeric regex", function(){
assert(result).equals("Top\\.[A-Z,a-z,0-9]*\\.Bottom\\..*");
});
});
describe("With a topic containing an asterisk and periods", function() {
var result = bindingsResolver.regexify("Top.*.Bottom");
it("The asterisk should be escaped", function(){
assert(result).equals("Top\\..*\\.Bottom");
});
});
});*/
describe("When calling compare", function(){
describe("With topic Top.Middle.Bottom and binding Top.Middle.Bottom", function(){
var result = bindingsResolver.compare("Top.Middle.Bottom", "Top.Middle.Bottom"),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.Middle.Bottom"];
it("Result should be true", function() {
assert(result).isTrue();
});
it("Should create a resolver cache entry", function(){
assert(cached).isTrue();
});
});
describe("With topic Top.Middle.Bottom and binding Top.#.Bottom", function(){
var result = bindingsResolver.compare("Top.#.Bottom", "Top.Middle.Bottom"),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.#.Bottom"];
it("Result should be true", function() {
assert(result).isTrue();
});
it("Should create a resolver cache entry", function(){
assert(cached).isTrue();
});
});
describe("With topic Top.Middle.Bottom and binding Top.*.Bottom", function(){
var result = bindingsResolver.compare("Top.*.Bottom", "Top.Middle.Bottom"),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.*.Bottom"];
it("Result should be true", function() {
assert(result).isTrue();
});
it("Should create a resolver cache entry", function(){
assert(cached).isTrue();
});
});
describe("With topic Top.Middle.Bottom and binding #.*.Bottom", function(){
var result = bindingsResolver.compare("#.*.Bottom", "Top.Middle.Bottom"),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["#.*.Bottom"];
it("Result should be true", function() {
assert(result).isTrue();
});
it("Should create a resolver cache entry", function(){
assert(cached).isTrue();
});
});
});
});
describe("bindingsResolver", function(){
describe("When calling compare", function(){
describe("With topic Top.Middle.Bottom and binding Top.Middle.Bottom", function(){
var result = bindingsResolver.compare("Top.Middle.Bottom", "Top.Middle.Bottom"),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.Middle.Bottom"];
it("Result should be true", function() {
assert(result).isTrue();
});
it("Should create a resolver cache entry", function(){
assert(cached).isTrue();
});
});
describe("With topic Top.Middle.Bottom and binding Top.#.Bottom", function(){
var result = bindingsResolver.compare("Top.#.Bottom", "Top.Middle.Bottom"),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.#.Bottom"];
it("Result should be true", function() {
assert(result).isTrue();
});
it("Should create a resolver cache entry", function(){
assert(cached).isTrue();
});
});
describe("With topic Top.Middle.Bottom and binding Top.*.Bottom", function(){
var result = bindingsResolver.compare("Top.*.Bottom", "Top.Middle.Bottom"),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.*.Bottom"];
it("Result should be true", function() {
assert(result).isTrue();
});
it("Should create a resolver cache entry", function(){
assert(cached).isTrue();
});
});
describe("With topic Top.Middle.Bottom and binding #.*.Bottom", function(){
var result = bindingsResolver.compare("#.*.Bottom", "Top.Middle.Bottom"),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["#.*.Bottom"];
it("Result should be true", function() {
assert(result).isTrue();
});
it("Should create a resolver cache entry", function(){
assert(cached).isTrue();
});
});
});
});
});

View file

@ -1,20 +1,20 @@
QUnit.specify("postal.js", function(){
describe("ChannelDefinition", function(){
describe("When initializing a channel definition", function() {
var chDef = new ChannelDefinition("TestChannel", "TestTopic");
it("should set channel to TestChannel", function(){
assert(chDef.channel).equals("TestChannel");
});
it("should set topic to TestTopic", function(){
assert(chDef._topic).equals("TestTopic");
});
});
describe("When calling subscribe", function() {
var ch = new ChannelDefinition("TestChannel", "TestTopic"),
sub = ch.subscribe(function(){ });
it("subscription should be instance of SubscriptionDefinition", function(){
assert(sub instanceof SubscriptionDefinition).isTrue();
});
});
});
describe("ChannelDefinition", function(){
describe("When initializing a channel definition", function() {
var chDef = new ChannelDefinition("TestChannel", "TestTopic");
it("should set channel to TestChannel", function(){
assert(chDef.channel).equals("TestChannel");
});
it("should set topic to TestTopic", function(){
assert(chDef._topic).equals("TestTopic");
});
});
describe("When calling subscribe", function() {
var ch = new ChannelDefinition("TestChannel", "TestTopic"),
sub = ch.subscribe(function(){ });
it("subscription should be instance of SubscriptionDefinition", function(){
assert(sub instanceof SubscriptionDefinition).isTrue();
});
});
});
});

View file

@ -1,76 +1,76 @@
QUnit.specify("postal.js", function(){
describe("DistinctPredicate", function(){
describe("When calling the function with the same data multiple times", function() {
var pred = new DistinctPredicate(),
data = { name: "Dr Who" },
results = [];
results.push(pred(data));
results.push(pred(data));
results.push(pred(data));
describe("DistinctPredicate", function(){
describe("When calling the function with the same data multiple times", function() {
var pred = new DistinctPredicate(),
data = { name: "Dr Who" },
results = [];
results.push(pred(data));
results.push(pred(data));
results.push(pred(data));
it("The first result should be true", function(){
assert(results[0]).isTrue();
});
it("The second result should be false", function(){
assert(results[1]).isFalse();
});
it("The third result should be false", function(){
assert(results[2]).isFalse();
});
});
describe("When calling the function with different data every time", function() {
var predA = new DistinctPredicate(),
data = { name: "Amelia" },
res = [];
res.push(predA(data));
data.name = "Rose";
res.push(predA(data));
data.name = "Martha";
res.push(predA(data));
it("The first result should be true", function(){
assert(results[0]).isTrue();
});
it("The second result should be false", function(){
assert(results[1]).isFalse();
});
it("The third result should be false", function(){
assert(results[2]).isFalse();
});
});
describe("When calling the function with different data every time", function() {
var predA = new DistinctPredicate(),
data = { name: "Amelia" },
res = [];
res.push(predA(data));
data.name = "Rose";
res.push(predA(data));
data.name = "Martha";
res.push(predA(data));
it("The first result should be true", function(){
assert(res[0]).isTrue();
});
it("The second result should be true", function(){
assert(res[1]).isTrue();
});
it("The third result should be true", function(){
assert(res[2]).isTrue();
});
});
describe("When calling the function with different data every two calls", function() {
var predA = new DistinctPredicate(),
data = { name: "Amelia" },
res = [];
res.push(predA(data));
res.push(predA(data));
data.name = "Rose";
res.push(predA(data));
res.push(predA(data));
data.name = "Martha";
res.push(predA(data));
res.push(predA(data));
it("The first result should be true", function(){
assert(res[0]).isTrue();
});
it("The second result should be true", function(){
assert(res[1]).isTrue();
});
it("The third result should be true", function(){
assert(res[2]).isTrue();
});
});
describe("When calling the function with different data every two calls", function() {
var predA = new DistinctPredicate(),
data = { name: "Amelia" },
res = [];
res.push(predA(data));
res.push(predA(data));
data.name = "Rose";
res.push(predA(data));
res.push(predA(data));
data.name = "Martha";
res.push(predA(data));
res.push(predA(data));
it("The first result should be true", function(){
assert(res[0]).isTrue();
});
it("The second result should be false", function(){
assert(res[1]).isFalse();
});
it("The first result should be true", function(){
assert(res[0]).isTrue();
});
it("The second result should be false", function(){
assert(res[1]).isFalse();
});
it("The third result should be isTrue", function(){
assert(res[2]).isTrue();
});
it("The fourth result should be false", function(){
assert(res[3]).isFalse();
});
it("The third result should be isTrue", function(){
assert(res[2]).isTrue();
});
it("The fourth result should be false", function(){
assert(res[3]).isFalse();
});
it("The fifth result should be true", function(){
assert(res[4]).isTrue();
});
it("The sixth result should be false", function(){
assert(res[5]).isFalse();
});
});
});
it("The fifth result should be true", function(){
assert(res[4]).isTrue();
});
it("The sixth result should be false", function(){
assert(res[5]).isFalse();
});
});
});
});

File diff suppressed because it is too large Load diff

View file

@ -1,85 +1,85 @@
QUnit.specify("postal.js", function(){
describe("SubscriptionDefinition", function(){
describe("When initializing SubscriptionDefinition", function() {
var sDef = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP);
it("should set the channel to TestChannel", function() {
assert(sDef.channel).equals("TestChannel");
});
it("should set the topic to TestTopic", function() {
assert(sDef.topic).equals("TestTopic");
});
it("should set the callback", function() {
assert(sDef.callback).equals(NO_OP);
});
it("should default the priority", function() {
assert(sDef.priority).equals(50);
});
it("should default the constraints", function() {
assert(sDef.constraints.length).equals(0);
});
it("should default the maxCalls", function() {
assert(sDef.maxCalls).equals(0);
});
it("should default the onHandled", function() {
assert(sDef.onHandled).equals(NO_OP);
});
it("should default the context", function() {
assert(sDef.context).isNull();
});
});
describe("SubscriptionDefinition", function(){
describe("When initializing SubscriptionDefinition", function() {
var sDef = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP);
it("should set the channel to TestChannel", function() {
assert(sDef.channel).equals("TestChannel");
});
it("should set the topic to TestTopic", function() {
assert(sDef.topic).equals("TestTopic");
});
it("should set the callback", function() {
assert(sDef.callback).equals(NO_OP);
});
it("should default the priority", function() {
assert(sDef.priority).equals(50);
});
it("should default the constraints", function() {
assert(sDef.constraints.length).equals(0);
});
it("should default the maxCalls", function() {
assert(sDef.maxCalls).equals(0);
});
it("should default the onHandled", function() {
assert(sDef.onHandled).equals(NO_OP);
});
it("should default the context", function() {
assert(sDef.context).isNull();
});
});
describe("When setting ignoreDuplicates", function(){
var sDefa = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP).ignoreDuplicates();
describe("When setting ignoreDuplicates", function(){
var sDefa = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP).ignoreDuplicates();
it("Should add a DistinctPredicate constraint to the configuration constraints", function() {
assert(sDefa.constraints.length).equals(1);
});
});
it("Should add a DistinctPredicate constraint to the configuration constraints", function() {
assert(sDefa.constraints.length).equals(1);
});
});
describe("When adding a constraint", function(){
var sDefb = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP).withConstraint(function() { });
describe("When adding a constraint", function(){
var sDefb = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP).withConstraint(function() { });
it("Should add a constraint", function() {
assert(sDefb.constraints.length).equals(1);
});
});
it("Should add a constraint", function() {
assert(sDefb.constraints.length).equals(1);
});
});
describe("When adding multiple constraints", function(){
var sDefc = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP).withConstraints([function() { }, function() { }, function() { }]);
describe("When adding multiple constraints", function(){
var sDefc = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP).withConstraints([function() { }, function() { }, function() { }]);
it("Should add a constraint", function() {
assert(sDefc.constraints.length).equals(3);
});
});
it("Should add a constraint", function() {
assert(sDefc.constraints.length).equals(3);
});
});
describe("When setting the context", function(){
var obj = {},
sDefd = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP).withContext(obj);
describe("When setting the context", function(){
var obj = {},
sDefd = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP).withContext(obj);
it("Should set context", function() {
assert(sDefd.context).equals(obj);
});
});
it("Should set context", function() {
assert(sDefd.context).equals(obj);
});
});
describe("When setting priority", function(){
var sDefe = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP).withPriority(10);
describe("When setting priority", function(){
var sDefe = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP).withPriority(10);
it("Should set priority", function() {
assert(sDefe.priority).equals(10);
});
});
it("Should set priority", function() {
assert(sDefe.priority).equals(10);
});
});
describe("When setting whenHandledThenExecute", function(){
var sDeff = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP).whenHandledThenExecute(function() { });
describe("When setting whenHandledThenExecute", function(){
var sDeff = new SubscriptionDefinition("TestChannel", "TestTopic", NO_OP).whenHandledThenExecute(function() { });
it("Should add an onHandled callback", function() {
assert(typeof sDeff.onHandled).equals("function");
});
it("Should not equal NO_OP", function() {
assert(sDeff.onHandled).isNotEqualTo(NO_OP);
});
});
it("Should add an onHandled callback", function() {
assert(typeof sDeff.onHandled).equals("function");
});
it("Should not equal NO_OP", function() {
assert(sDeff.onHandled).isNotEqualTo(NO_OP);
});
});
//TODO: need to determine best way to add tests for defer, debounce, throttle, delay & disposeAfter
});
//TODO: need to determine best way to add tests for defer, debounce, throttle, delay & disposeAfter
});
});

View file

@ -1,28 +1,28 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type="text/javascript" src="../ext/jquery-1.5.2.js"></script>
<script type="text/javascript" src="../ext/qunit.js"></script>
<script type="text/javascript" src="../ext/pavlov.js"></script>
<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/main/Constants.js"></script>
<script type="text/javascript" src="../src/main/DistinctPredicate.js"></script>
<script type="text/javascript" src="../src/main/ChannelDefinition.js"></script>
<script type="text/javascript" src="../src/main/SubscriptionDefinition.js"></script>
<script type="text/javascript" src="../src/main/BindingsResolver.js"></script>
<script type="text/javascript" src="../src/main/LocalBus.js"></script>
<script type="text/javascript" src="../src/main/Api.js"></script>
<script type="text/javascript" src="DistinctPredicate.spec.js"></script>
<script type="text/javascript" src="ChannelDefinition.spec.js"></script>
<script type="text/javascript" src="SubscriptionDefinition.spec.js"></script>
<script type="text/javascript" src="BindingsResolver.spec.js"></script>
<script type="text/javascript" src="Postal.spec.js"></script>
<link rel="stylesheet" href="../ext/qunit.css" type="text/css" media="screen" />
<script type="text/javascript" src="../ext/jquery-1.5.2.js"></script>
<script type="text/javascript" src="../ext/qunit.js"></script>
<script type="text/javascript" src="../ext/pavlov.js"></script>
<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/main/Constants.js"></script>
<script type="text/javascript" src="../src/main/DistinctPredicate.js"></script>
<script type="text/javascript" src="../src/main/ChannelDefinition.js"></script>
<script type="text/javascript" src="../src/main/SubscriptionDefinition.js"></script>
<script type="text/javascript" src="../src/main/BindingsResolver.js"></script>
<script type="text/javascript" src="../src/main/LocalBus.js"></script>
<script type="text/javascript" src="../src/main/Api.js"></script>
<script type="text/javascript" src="DistinctPredicate.spec.js"></script>
<script type="text/javascript" src="ChannelDefinition.spec.js"></script>
<script type="text/javascript" src="SubscriptionDefinition.spec.js"></script>
<script type="text/javascript" src="BindingsResolver.spec.js"></script>
<script type="text/javascript" src="Postal.spec.js"></script>
<link rel="stylesheet" href="../ext/qunit.css" type="text/css" media="screen" />
</head>
<body>
<h1 id="qunit-header"></h1>
<h1 id="qunit-header"></h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>

View file

@ -1,21 +1,21 @@
var bindingsResolver = {
cache: { },
cache: { },
compare: function(binding, topic) {
if(this.cache[topic] && this.cache[topic][binding]) {
return true;
}
// binding.replace(/\./g,"\\.") // escape actual periods
// .replace(/\*/g, ".*") // asterisks match any value
// .replace(/#/g, "[A-Z,a-z,0-9]*"); // hash matches any alpha-numeric 'word'
var rgx = new RegExp("^" + binding.replace(/\./g,"\\.").replace(/\*/g, ".*").replace(/#/g, "[A-Z,a-z,0-9]*") + "$"),
result = rgx.test(topic);
if(result) {
if(!this.cache[topic]) {
this.cache[topic] = {};
}
this.cache[topic][binding] = true;
}
return result;
}
compare: function(binding, topic) {
if(this.cache[topic] && this.cache[topic][binding]) {
return true;
}
// binding.replace(/\./g,"\\.") // escape actual periods
// .replace(/\*/g, ".*") // asterisks match any value
// .replace(/#/g, "[A-Z,a-z,0-9]*"); // hash matches any alpha-numeric 'word'
var rgx = new RegExp("^" + binding.replace(/\./g,"\\.").replace(/\*/g, ".*").replace(/#/g, "[A-Z,a-z,0-9]*") + "$"),
result = rgx.test(topic);
if(result) {
if(!this.cache[topic]) {
this.cache[topic] = {};
}
this.cache[topic][binding] = true;
}
return result;
}
};

View file

@ -1,33 +1,33 @@
var ChannelDefinition = function(channelName, defaultTopic) {
this.channel = channelName || DEFAULT_CHANNEL;
this._topic = defaultTopic || "";
this.channel = channelName || DEFAULT_CHANNEL;
this._topic = defaultTopic || "";
};
ChannelDefinition.prototype = {
subscribe: function() {
var len = arguments.length;
if(len === 1) {
return new SubscriptionDefinition(this.channel, this._topic, arguments[0]);
}
else if (len === 2) {
return new SubscriptionDefinition(this.channel, arguments[0], arguments[1]);
}
},
subscribe: function() {
var len = arguments.length;
if(len === 1) {
return new SubscriptionDefinition(this.channel, this._topic, arguments[0]);
}
else if (len === 2) {
return new SubscriptionDefinition(this.channel, arguments[0], arguments[1]);
}
},
publish: function(obj) {
var envelope = {
channel: this.channel,
topic: this._topic,
data: obj
};
// If this is an envelope....
if( obj.topic && obj.data ) {
envelope = obj;
envelope.channel = envelope.channel || this.channel;
}
envelope.timeStamp = new Date();
publish: function(obj) {
var envelope = {
channel: this.channel,
topic: this._topic,
data: obj
};
// If this is an envelope....
if( obj.topic && obj.data ) {
envelope = obj;
envelope.channel = envelope.channel || this.channel;
}
envelope.timeStamp = new Date();
postal.configuration.bus.publish(envelope);
},
},
topic: function(topic) {
if(topic === this._topic) {

View file

@ -1,5 +1,5 @@
var DEFAULT_CHANNEL = "/",
DEFAULT_PRIORITY = 50,
DEFAULT_DISPOSEAFTER = 0,
SYSTEM_CHANNEL = "postal",
NO_OP = function() { };
DEFAULT_PRIORITY = 50,
DEFAULT_DISPOSEAFTER = 0,
SYSTEM_CHANNEL = "postal",
NO_OP = function() { };

View file

@ -1,15 +1,15 @@
var DistinctPredicate = function() {
var previous;
return function(data) {
var eq = false;
if(_.isString(data)) {
eq = data === previous;
previous = data;
}
else {
eq = _.isEqual(data, previous);
previous = _.clone(data);
}
return !eq;
};
var previous;
return function(data) {
var eq = false;
if(_.isString(data)) {
eq = data === previous;
previous = data;
}
else {
eq = _.isEqual(data, previous);
previous = _.clone(data);
}
return !eq;
};
};

View file

@ -8,14 +8,14 @@ var SubscriptionDefinition = function(channel, topic, callback) {
this.onHandled = NO_OP;
this.context = null;
postal.configuration.bus.publish({
channel: SYSTEM_CHANNEL,
topic: "subscription.created",
timeStamp: new Date(),
data: {
event: "subscription.created",
channel: channel,
topic: topic
}
channel: SYSTEM_CHANNEL,
topic: "subscription.created",
timeStamp: new Date(),
data: {
event: "subscription.created",
channel: channel,
topic: topic
}
});
postal.configuration.bus.subscribe(this);

View file

@ -1,6 +1,6 @@
/*
postal.js
Author: Jim Cowart
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
Version 0.6.0
*/
postal.js
Author: Jim Cowart
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
Version 0.6.0
*/