Skip to content

Commit 9f6a8e6

Browse files
committed
listenTo equivalents for Commands and Requests.
1 parent c200394 commit 9f6a8e6

File tree

3 files changed

+345
-0
lines changed

3 files changed

+345
-0
lines changed

src/backbone.radio.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,43 @@ Radio.Commands = {
228228
debugLog('Attempted to remove the unregistered command', name, this.channelName);
229229
}
230230

231+
return this;
232+
},
233+
234+
stopComplyingFor: function(obj, name, callback) {
235+
var complyingFor = this._complyingFor;
236+
if (!complyingFor) { return this; }
237+
var remove = !name && !callback;
238+
if (!callback && typeof name === 'object') { callback = this; }
239+
if (obj) { (complyingFor = {})[obj._commandId] = obj; }
240+
for (var id in complyingFor) {
241+
obj = complyingFor[id];
242+
obj.stopComplying(name, callback, this);
243+
if (remove || _.isEmpty(obj._commands)) { delete this._complyingFor[id]; }
244+
}
231245
return this;
232246
}
233247
};
234248

249+
// listenTo equivalent for Commands
250+
var listenMethods = {complyFor: 'comply', complyForOnce: 'complyOnce'};
251+
_.each(listenMethods, function(implementation, method) {
252+
Radio.Commands[method] = function(obj, name, callback) {
253+
var complyingFor = this._complyingFor || (this._complyingFor = {});
254+
var id = obj._commandId || (obj._commandId = _.uniqueId('c'));
255+
complyingFor[id] = obj;
256+
if (!callback && typeof name === 'object') { callback = this; }
257+
if (implementation === 'once') {
258+
callback = _.compose(function(result) {
259+
this.stopListening(_.rest(arguments));
260+
return result;
261+
}, callback);
262+
}
263+
obj[implementation](name, callback, this);
264+
return this;
265+
};
266+
});
267+
235268
//
236269
// Backbone.Radio.Requests
237270
// A messaging system for requesting data.
@@ -317,10 +350,43 @@ Radio.Requests = {
317350
debugLog('Attempted to remove the unregistered request', name, this.channelName);
318351
}
319352

353+
return this;
354+
},
355+
356+
stopReplyingFor: function(obj, name, callback) {
357+
var replyingFor = this._replyingFor;
358+
if (!replyingFor) { return this; }
359+
var remove = !name && !callback;
360+
if (!callback && typeof name === 'object') { callback = this; }
361+
if (obj) { (replyingFor = {})[obj._requestId] = obj; }
362+
for (var id in replyingFor) {
363+
obj = replyingFor[id];
364+
obj.stopReplying(name, callback, this);
365+
if (remove || _.isEmpty(obj._requests)) { delete this._replyingFor[id]; }
366+
}
320367
return this;
321368
}
322369
};
323370

371+
// listenTo equivalent for Requests
372+
var listenMethods = {replyFor: 'reply', replyForOnce: 'replyOnce'};
373+
_.each(listenMethods, function(implementation, method) {
374+
Radio.Requests[method] = function(obj, name, callback) {
375+
var replyingFor = this._replyingFor || (this._replyingFor = {});
376+
var id = obj._requestId || (obj._requestId = _.uniqueId('r'));
377+
replyingFor[id] = obj;
378+
if (!callback && typeof name === 'object') { callback = this; }
379+
if (implementation === 'once') {
380+
callback = _.compose(function(result) {
381+
this.stopListening(_.rest(arguments));
382+
return result;
383+
}, callback);
384+
}
385+
obj[implementation](name, callback, this);
386+
return this;
387+
};
388+
});
389+
324390
//
325391
// Backbone.Radio.channel
326392
// Get a reference to a channel by name.

test/spec/commands.js

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,4 +493,195 @@ describe('Commands:', function() {
493493
.and.calledWith('commandTwo');
494494
});
495495
});
496+
497+
describe('when registering a command with `complyFor`, then executing it', function() {
498+
beforeEach(function() {
499+
this.CommandsTwo = _.clone(Backbone.Radio.Commands);
500+
this.callback = stub();
501+
this.Commands.complyFor(this.CommandsTwo, 'myCommand', this.callback);
502+
this.CommandsTwo.command('myCommand');
503+
});
504+
505+
it('should execute the callback with the correct context', function() {
506+
expect(this.callback)
507+
.to.have.been.calledOnce
508+
.and.to.have.always.been.calledOn(this.Commands);
509+
});
510+
});
511+
512+
describe('when registering a command with `complyFor` and an event map, and then executing one of the commands', function() {
513+
beforeEach(function() {
514+
this.CommandsTwo = _.clone(Backbone.Radio.Commands);
515+
this.callbackOne = stub();
516+
this.callbackTwo = stub();
517+
this.Commands.complyFor(this.CommandsTwo, {
518+
commandOne: this.callbackOne,
519+
commandTwo: this.callbackTwo
520+
});
521+
this.CommandsTwo.command('commandOne');
522+
});
523+
524+
it('should execute the callback with the correct context', function() {
525+
expect(this.callbackOne)
526+
.to.have.been.calledOnce
527+
.and.to.have.always.been.calledOn(this.Commands);
528+
});
529+
530+
it('should not execute the callbacks not specified', function() {
531+
expect(this.callbackTwo).to.not.have.been.called;
532+
});
533+
});
534+
535+
describe('`complyForOnce`', function() {
536+
beforeEach(function() {
537+
this.CommandsTwo = _.clone(Backbone.Radio.Commands);
538+
this.callback = stub();
539+
this.Commands.complyForOnce(this.CommandsTwo, 'myCommand', this.callback);
540+
this.CommandsTwo.command('myCommand');
541+
});
542+
543+
it('should execute the callback with the correct context', function() {
544+
expect(this.callback)
545+
.to.have.been.calledOnce
546+
.and.to.have.always.been.calledOn(this.Commands);
547+
});
548+
549+
it('should remove the reference from the original object', function() {
550+
expect(this.CommandsTwo._commands).to.deep.equal({});
551+
});
552+
});
553+
554+
describe('`complyForOnce` with an event map', function() {
555+
beforeEach(function() {
556+
this.CommandsTwo = _.clone(Backbone.Radio.Commands);
557+
this.callbackOne = stub();
558+
this.callbackTwo = stub();
559+
this.Commands.complyForOnce(this.CommandsTwo, {
560+
commandOne: this.callbackOne,
561+
commandTwo: this.callbackTwo
562+
});
563+
this.CommandsTwo.command('commandOne');
564+
});
565+
566+
it('should execute the callback with the correct context', function() {
567+
expect(this.callbackOne)
568+
.to.have.been.calledOnce
569+
.and.to.have.always.been.calledOn(this.Commands);
570+
});
571+
572+
it('should not execute the callbacks not specified', function() {
573+
expect(this.callbackTwo).to.not.have.been.called;
574+
});
575+
576+
it('should remove the key for a', function() {
577+
expect(this.CommandsTwo._commands).to.not.have.key('commandOne');
578+
});
579+
580+
it('should keep the key for b', function() {
581+
expect(this.CommandsTwo._commands).to.have.key('commandTwo');
582+
});
583+
});
584+
585+
describe('`stopComplyingFor` with the first argument', function() {
586+
beforeEach(function() {
587+
this.CommandsTwo = _.clone(Backbone.Radio.Commands);
588+
this.CommandsThree = _.clone(Backbone.Radio.Commands);
589+
this.callbackOne = stub();
590+
this.callbackTwo = stub();
591+
this.callbackThree = stub();
592+
this.Commands.complyFor(this.CommandsTwo, {
593+
commandOne: this.callbackOne,
594+
commandTwo: this.callbackTwo
595+
});
596+
this.Commands.complyFor(this.CommandsThree, 'commandThree', this.callbackThree);
597+
this.Commands.stopComplyingFor(this.CommandsTwo);
598+
});
599+
600+
it('should only remove the callbacks on CommandsTwo', function() {
601+
expect(this.CommandsTwo._commands).to.deep.equal({});
602+
});
603+
604+
it('should leave the callback on CommandThree', function() {
605+
expect(this.CommandsThree._commands).to.have.key('commandThree');
606+
});
607+
});
608+
609+
describe('`stopComplyingFor` with the first two arguments', function() {
610+
beforeEach(function() {
611+
this.CommandsTwo = _.clone(Backbone.Radio.Commands);
612+
this.CommandsThree = _.clone(Backbone.Radio.Commands);
613+
this.callbackOne = stub();
614+
this.callbackTwo = stub();
615+
this.callbackThree = stub();
616+
this.Commands.complyFor(this.CommandsTwo, {
617+
commandOne: this.callbackOne,
618+
commandTwo: this.callbackTwo
619+
});
620+
this.Commands.complyFor(this.CommandsThree, 'commandThree', this.callbackThree);
621+
this.Commands.stopComplyingFor(this.CommandsTwo, 'commandOne');
622+
});
623+
624+
it('should only remove the proper callback on CommandsTwo', function() {
625+
expect(this.CommandsTwo._commands).to.not.have.key('commandOne');
626+
});
627+
628+
it('should not touch the other callback on CommandsTwo', function() {
629+
expect(this.CommandsTwo._commands).to.have.key('commandTwo');
630+
});
631+
632+
it('should leave the callback on CommandThree', function() {
633+
expect(this.CommandsThree._commands).to.have.key('commandThree');
634+
});
635+
});
636+
637+
describe('`stopComplyingFor` with the all three arguments', function() {
638+
beforeEach(function() {
639+
this.CommandsTwo = _.clone(Backbone.Radio.Commands);
640+
this.CommandsThree = _.clone(Backbone.Radio.Commands);
641+
this.callbackOne = stub();
642+
this.callbackTwo = stub();
643+
this.callbackThree = stub();
644+
this.Commands.complyFor(this.CommandsTwo, {
645+
commandOne: this.callbackOne,
646+
commandTwo: this.callbackTwo
647+
});
648+
this.Commands.complyFor(this.CommandsThree, 'commandThree', this.callbackThree);
649+
this.Commands.stopComplyingFor(this.CommandsTwo, 'commandOne', this.callbackOne);
650+
});
651+
652+
it('should only remove the proper callback on CommandsTwo', function() {
653+
expect(this.CommandsTwo._commands).to.not.have.key('commandOne');
654+
});
655+
656+
it('should not touch the other callback on CommandsTwo', function() {
657+
expect(this.CommandsTwo._commands).to.have.key('commandTwo');
658+
});
659+
660+
it('should leave the callback on CommandThree', function() {
661+
expect(this.CommandsThree._commands).to.have.key('commandThree');
662+
});
663+
});
664+
665+
describe('`stopComplyingFor` with just the third argument', function() {
666+
beforeEach(function() {
667+
this.CommandsTwo = _.clone(Backbone.Radio.Commands);
668+
this.CommandsThree = _.clone(Backbone.Radio.Commands);
669+
this.callbackOne = stub();
670+
this.callbackTwo = stub();
671+
this.Commands.complyFor(this.CommandsTwo, {
672+
commandOne: this.callbackOne,
673+
commandTwo: this.callbackOne
674+
});
675+
this.Commands.complyFor(this.CommandsThree, 'commandThree', this.callbackTwo);
676+
this.Commands.stopComplyingFor(undefined, undefined, this.callbackOne);
677+
});
678+
679+
it('should only remove the proper callback on CommandsTwo', function() {
680+
expect(this.CommandsTwo._commands).to.deep.equal({});
681+
});
682+
683+
it('should not touch the other callback on CommandsTwo', function() {
684+
expect(this.CommandsThree._commands).to.have.key('commandThree');
685+
});
686+
});
496687
});

test/spec/requests.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,4 +512,92 @@ describe('Requests:', function() {
512512
.and.calledWith('requestTwo');
513513
});
514514
});
515+
516+
describe('when registering a request with `replyFor`, then executing it', function() {
517+
beforeEach(function() {
518+
this.RequestsTwo = _.clone(Backbone.Radio.Requests);
519+
this.callback = stub();
520+
this.Requests.replyFor(this.RequestsTwo, 'myRequest', this.callback);
521+
this.RequestsTwo.request('myRequest');
522+
});
523+
524+
it('should execute the callback with the correct context', function() {
525+
expect(this.callback)
526+
.to.have.been.calledOnce
527+
.and.to.have.always.been.calledOn(this.Requests);
528+
});
529+
});
530+
531+
describe('when registering a request with `replyFor` and an event map, and then executing one of the requests', function() {
532+
beforeEach(function() {
533+
this.RequestsTwo = _.clone(Backbone.Radio.Requests);
534+
this.callbackOne = stub();
535+
this.callbackTwo = stub();
536+
this.Requests.replyFor(this.RequestsTwo, {
537+
requestOne: this.callbackOne,
538+
requestTwo: this.callbackTwo
539+
});
540+
this.RequestsTwo.request('requestOne');
541+
});
542+
543+
it('should execute the callback with the correct context', function() {
544+
expect(this.callbackOne)
545+
.to.have.been.calledOnce
546+
.and.to.have.always.been.calledOn(this.Requests);
547+
});
548+
549+
it('should not execute the callbacks not specified', function() {
550+
expect(this.callbackTwo).to.not.have.been.called;
551+
});
552+
});
553+
554+
describe('`replyForOnce` should clean up after itself', function() {
555+
beforeEach(function() {
556+
this.RequestsTwo = _.clone(Backbone.Radio.Requests);
557+
this.callback = stub();
558+
this.Requests.replyForOnce(this.RequestsTwo, 'myRequest', this.callback);
559+
this.RequestsTwo.request('myRequest');
560+
});
561+
562+
it('should execute the callback with the correct context', function() {
563+
expect(this.callback)
564+
.to.have.been.calledOnce
565+
.and.to.have.always.been.calledOn(this.Requests);
566+
});
567+
568+
it('should remove the reference from the original object', function() {
569+
expect(this.RequestsTwo._requests).to.deep.equal({});
570+
});
571+
});
572+
573+
describe('`replyForOnce` with an event map should clean up after itself', function() {
574+
beforeEach(function() {
575+
this.RequestsTwo = _.clone(Backbone.Radio.Requests);
576+
this.callbackOne = stub();
577+
this.callbackTwo = stub();
578+
this.Requests.replyForOnce(this.RequestsTwo, {
579+
requestOne: this.callbackOne,
580+
requestTwo: this.callbackTwo
581+
});
582+
this.RequestsTwo.request('requestOne');
583+
});
584+
585+
it('should execute the callback with the correct context', function() {
586+
expect(this.callbackOne)
587+
.to.have.been.calledOnce
588+
.and.to.have.always.been.calledOn(this.Requests);
589+
});
590+
591+
it('should not execute the callbacks not specified', function() {
592+
expect(this.callbackTwo).to.not.have.been.called;
593+
});
594+
595+
it('should remove the key for a', function() {
596+
expect(this.RequestsTwo._requests).to.not.have.key('requestOne');
597+
});
598+
599+
it('should keep the key for b', function() {
600+
expect(this.RequestsTwo._requests).to.have.key('requestTwo');
601+
});
602+
});
515603
});

0 commit comments

Comments
 (0)