Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,18 @@ var predicate = function(data){ return data.From === "Jack" };
mediator.subscribe("channel", function(data){ alert(data.Message); }, { predicate: predicate });
mediator.publish("channel", { Message: "Hey!", From: "Jack" }); //alerts
mediator.publish("channel", { Message: "Hey!", From: "Audrey" }); //doesn't alert

//You can pass "context" argument as third parameter of if "options" doesn't defined
var listener = {
text: 'Event has been published',
onEvent: function() {
alert(this.text);
}
}
mediator.subscribe("channel", listener.onEvent, listener);
mediator.subscribe("channel", listener.onEvent, {calls: 1}, listener);
mediator.publish("channel"); //we'll have two alerts
mediator.publish("channel"); //we'll have an one alert because the second subscriber does have a "calls:1" option
```

You can remove events by passing in a channel, or a channel and the
Expand Down
56 changes: 51 additions & 5 deletions lib/mediator.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@
y = this._subscribers.length,
called = false,
subscriber, l,
subsBefore,subsAfter;
subsBefore,subsAfter,
predicateResult;

// Priority is preserved in the _subscribers index.
for(x, y; x < y; x++) {
Expand All @@ -209,13 +210,27 @@
if(!this.stopped){
subscriber = this._subscribers[x];
if(subscriber.options !== undefined && typeof subscriber.options.predicate === "function"){
if(subscriber.options.predicate.apply(subscriber.context, data)){
subscriber.fn.apply(subscriber.context, data);
try {
predicateResult = subscriber.options.predicate.apply(subscriber.context, data);
} catch (ex) {
predicateResult = false;
this._processException(ex);
}
if(predicateResult){
try {
subscriber.fn.apply(subscriber.context, data);
} catch(ex) {
this._processException(ex);
}
called = true;
}
}else{
subsBefore = this._subscribers.length;
subscriber.fn.apply(subscriber.context, data);
try {
subscriber.fn.apply(subscriber.context, data);
} catch(ex) {
this._processException(ex);
}
subsAfter = this._subscribers.length;
y = subsAfter;
if (subsAfter === subsBefore - 1){
Expand All @@ -241,6 +256,22 @@
}

this.stopped = false;
},

//Throw an exception in another context through setImmediate of setTimeout.
//If neither setImmediate nor setTimeout is defined we log exception's stack to the console (if it is defined)
_processException: function(ex) {
if (typeof setImmediate === 'function') {
setImmediate(function() {
throw ex;
});
} else if (typeof setTimeout === 'function') {
setTimeout(function() {
throw ex;
}, 0);
} else if (typeof console !== "undefined" && console.log) {
console.log(ex.stack);
}
}
};

Expand All @@ -257,6 +288,15 @@

Mediator.prototype = {

// Returns a true if options contains at least one predefined
// field, such as 'predicate', 'priority', 'calls'

_isValidOptions: function(options) {
return options.hasOwnProperty('predicate') ||
options.hasOwnProperty('priority') ||
options.hasOwnProperty('calls');
},

// Returns a channel instance based on namespace, for example
// application:chat:message:received

Expand Down Expand Up @@ -288,11 +328,17 @@
// to call the function in to Subscribe. It will create a channel if one
// does not exist. Options can include a predicate to determine if it
// should be called (based on the data published to it) and a priority
// index.
// index. If options hasn't been defined, context can be passed as third parameter

subscribe: function(channelName, fn, options, context){
var channel = this.getChannel(channelName);

//if context has been passed as third argument
if (!context && options && !this._isValidOptions(options)) {
context = options;
options = null;
}

options = options || {};
context = context || {};

Expand Down
27 changes: 27 additions & 0 deletions test/ChannelSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,5 +299,32 @@ describe("Channel", function() {
expect(this.a).to.equal("0123");
});

it("should call subscribers and continue running, if one of subscribers throws an exception", function(){
var sub1 = function(){
this.a += "1";
},
sub2 = function(){
this.a += "2";
throw new Error('error');
this.a += "444";
},
sub3 = function(){
this.a += "3";
},
data = ["data"];
this.a = "0";

channel.addSubscriber(sub1, {}, this);
channel.addSubscriber(sub2, {}, this);
channel.addSubscriber(sub3, {}, this);
try {
channel.publish(data);
this.a += "4";
} catch(ex) {
this.a += "555";
}
expect(this.a).to.equal("01234");
});

});
});
60 changes: 60 additions & 0 deletions test/MediatorSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,5 +370,65 @@ describe("Mediator", function() {
mediator.publish("test");
expect(spy).not.called;
});

it("should call method of context if context has been passed as third argument", function(){
var context = {
_privateTestMethod: sinon.spy(),
test: function() {
this._privateTestMethod && this._privateTestMethod();
}
},
sub;

sub = mediator.subscribe("test", context.test, null, context);
mediator.publish("test");

expect(context._privateTestMethod).called;
});

it("should call method of context if context has been passed as second argument and options has been missed", function(){
var context = {
_privateTestMethod: sinon.spy(),
test: function() {
this._privateTestMethod && this._privateTestMethod();
}
},
sub;

sub = mediator.subscribe("test", context.test, context);
mediator.publish("test");

expect(context._privateTestMethod).called;
});

it("should call method of context if context has been passed as third argument and options has been passed and defined", function(){
var context = {
_privateTestMethod: sinon.spy(),
test: function() {
this._privateTestMethod && this._privateTestMethod();
}
},
sub;

sub = mediator.subscribe("test", context.test, {calls: 1}, context);
mediator.publish("test");

expect(context._privateTestMethod).called;
});

it("should not call method of context if context has been missed", function(){
var context = {
_privateTestMethod: sinon.spy(),
test: function() {
this._privateTestMethod && this._privateTestMethod();
}
},
sub;

sub = mediator.subscribe("test", context.test, {calls: 1});
mediator.publish("test");

expect(context._privateTestMethod).not.called;
});
});
});