-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add first class support for Backbone.Radio in Mn.Object #2431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| (function(Marionette, Radio) { | ||
|
|
||
| //Proxy Radio message handling to enable declarative interactions with radio channels | ||
| var radioAPI = { | ||
| 'radioEvents' : { | ||
| startMethod: 'on', | ||
| stopMethod: 'off' | ||
| }, | ||
| 'radioCommands' : { | ||
| startMethod: 'comply', | ||
| stopMethod: 'stopComplying' | ||
| }, | ||
| 'radioRequests' : { | ||
| startMethod: 'reply', | ||
| stopMethod: 'stopReplying' | ||
| } | ||
| }; | ||
|
|
||
| function proxyRadioHandlers() { | ||
| unproxyRadioHandlers.apply(this); | ||
| _.each(radioAPI, function(commands, radioType) { | ||
| var hash = _.result(this, radioType); | ||
| if (!hash) { | ||
| return; | ||
| } | ||
| _.each(hash, function(handler, radioMessage) { | ||
| handler = normalizeHandler.call(this, handler); | ||
| if (!handler) { | ||
| return; | ||
| } | ||
| var messageComponents = radioMessage.split(' '), | ||
| channel = messageComponents[0], | ||
| messageName = messageComponents[1]; | ||
| proxyRadioHandler.call(this,channel, radioType, messageName, handler); | ||
| }, this); | ||
| }, this); | ||
| } | ||
|
|
||
| function proxyRadioHandler(channel, radioType, messageName, handler) { | ||
| var method = radioAPI[radioType].startMethod; | ||
| this._radioChannels = this._radioChannels || []; | ||
| if(!_.contains(this._radioChannels, channel)) { | ||
| this._radioChannels.push(channel); | ||
| } | ||
|
|
||
| Radio[method](channel, messageName, handler, this); | ||
| } | ||
|
|
||
| function unproxyRadioHandlers() { | ||
| _.each(this._radioChannels, function(channel) { | ||
| _.each(radioAPI,function(commands) { | ||
| Radio[commands.stopMethod](channel, null, null, this); | ||
| }, this); | ||
| }, this); | ||
| } | ||
|
|
||
| function normalizeHandler(handler) { | ||
| if (!_.isFunction(handler)) { | ||
| handler = this[handler]; | ||
| } | ||
| return handler; | ||
| } | ||
|
|
||
| Marionette.proxyRadioHandlers = proxyRadioHandlers; | ||
| Marionette.unproxyRadioHandlers = unproxyRadioHandlers; | ||
|
|
||
| })(Marionette,Backbone.Radio); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| describe('Marionette radio helpers', function() { | ||
|
|
||
| describe('when creating a Marionette.Object', function() { | ||
|
|
||
| beforeEach(function() { | ||
| this.clickStub1 = this.sinon.stub(); | ||
| this.clickStub2 = this.sinon.stub(); | ||
| this.clickStub3 = this.sinon.stub(); | ||
| this.Object = Marionette.Object.extend({ | ||
| radioEvents: { | ||
| 'foo bar' : this.clickStub1 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is weird because you run into the The easier thing is to add the stub to the object directly. O = extend({
radioEvents: {'foo bar': 'barHandler'}
barHandler: function() {}
});
o = new O();
o.barHandler = this.barHandler;
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, I can switch that up.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jasonLaster So after looking at this again, I don't see the problem here. I pass 2 functions directly and one by reference, to make sure that we handle both of those cases. Yes object2 shares the stub on the prototype, but I think the testing is still pretty unambiguous. I just wrote it out the other way, and it makes the code a lot more verbose and (in my mind) less clear. So I'm going to leave it unless you strongly object.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm cool with this |
||
| }, | ||
| radioCommands: { | ||
| 'bar foo' : this.clickStub2 | ||
| }, | ||
| radioRequests: { | ||
| 'foo bar' : 'baz' | ||
| }, | ||
|
|
||
| baz: this.clickStub3, | ||
|
|
||
| }); | ||
| this.object = new this.Object(); | ||
| Backbone.Radio.channel('foo').trigger('bar'); | ||
| Backbone.Radio.channel('foo').request('bar'); | ||
| Backbone.Radio.channel('bar').command('foo'); | ||
|
|
||
| }); | ||
|
|
||
| it('should support listening to radio events declaratively', function() { | ||
| expect(this.clickStub1).to.have.been.calledOnce; | ||
| }); | ||
|
|
||
| it('should support complying to radio commands declaratively', function() { | ||
| expect(this.clickStub2).to.have.been.calledOnce; | ||
| }); | ||
|
|
||
| it('should support replying to radio requests declaratively', function() { | ||
| expect(this.clickStub3).to.have.been.calledOnce; | ||
| }); | ||
|
|
||
|
|
||
| it('should unsubscribe events when the object is destroyed', function() { | ||
| this.object.destroy(); | ||
| Backbone.Radio.channel('foo').trigger('bar'); | ||
| expect(this.clickStub1).to.have.been.calledOnce; | ||
| }); | ||
|
|
||
| it('should unsubscribe commands when the object is destroyed', function() { | ||
| this.object.destroy(); | ||
| Backbone.Radio.channel('bar').command('foo'); | ||
| expect(this.clickStub2).to.have.been.calledOnce; | ||
| }); | ||
|
|
||
| it('should unsubscribe requests when the object is destroyed', function() { | ||
| this.object.destroy(); | ||
| Backbone.Radio.channel('foo').request('bar'); | ||
| expect(this.clickStub3).to.have.been.calledOnce; | ||
| }); | ||
|
|
||
| it('shouldn\'t overunsubscribe events when the object is destroyed', function() { | ||
| this.object2 = new this.Object(); | ||
| this.object.destroy(); | ||
| Backbone.Radio.channel('foo').trigger('bar'); | ||
| expect(this.clickStub1).to.have.been.calledTwice; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm. why do we need these?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mostly that was to assuage the concerns from my original version. I'm ok with not having the tests, but there was a concern raised that my code might over-unsubscribe and we should wait for Radio to add listenTo equivalents. |
||
| }); | ||
|
|
||
| it('shouldn\'t overunsubscribe commands when the object is destroyed', function() { | ||
| this.object2 = new this.Object(); | ||
| this.object.destroy(); | ||
| Backbone.Radio.channel('bar').command('foo'); | ||
| expect(this.clickStub2).to.have.been.calledTwice; | ||
| }); | ||
|
|
||
| it('shouldn\'t overunsubscribe requests when the object is destroyed', function() { | ||
| this.object2 = new this.Object(); | ||
| this.object.destroy(); | ||
| Backbone.Radio.channel('foo').request('bar'); | ||
| expect(this.clickStub3).to.have.been.calledTwice; | ||
| }); | ||
|
|
||
| }); | ||
|
|
||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we use a
normalizeXmethod?