backbone.radio
Advanced tools
Comparing version 0.9.1 to 1.0.0
{ | ||
"name": "backbone.radio", | ||
"version": "0.9.1", | ||
"version": "1.0.0", | ||
"homepage": "https://github.com/marionettejs/backbone.radio", | ||
@@ -5,0 +5,0 @@ "authors": [ |
@@ -1,2 +0,2 @@ | ||
// Backbone.Radio v0.9.1 | ||
// Backbone.Radio v1.0.0 | ||
(function (global, factory) { | ||
@@ -11,3 +11,3 @@ typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory(require("underscore"), require("backbone")) : typeof define === "function" && define.amd ? define(["underscore", "backbone"], factory) : global.Backbone.Radio = factory(global._, global.Backbone); | ||
Radio.VERSION = "0.9.1"; | ||
Radio.VERSION = "1.0.0"; | ||
@@ -44,4 +44,4 @@ // This allows you to run multiple instances of Radio on the same | ||
// An internal method used to handle Radio's method overloading for Requests and | ||
// Commands. It's borrowed from Backbone.Events. It differs from Backbone's overload | ||
// An internal method used to handle Radio's method overloading for Requests. | ||
// It's borrowed from Backbone.Events. It differs from Backbone's overload | ||
// API (which is used in Backbone.Events) in that it doesn't support space-separated | ||
@@ -171,88 +171,2 @@ // event names. | ||
/* | ||
* Backbone.Radio.Commands | ||
* ----------------------- | ||
* A messaging system for sending orders. | ||
* | ||
*/ | ||
Radio.Commands = { | ||
// Issue a command | ||
command: function command(name) { | ||
var args = _.rest(arguments); | ||
if (Radio._eventsApi(this, "command", name, args)) { | ||
return this; | ||
} | ||
var channelName = this.channelName; | ||
var commands = this._commands; | ||
// Check if we should log the command, and if so, do it | ||
if (channelName && this._tunedIn) { | ||
Radio.log.apply(this, [channelName, name].concat(args)); | ||
} | ||
// If the command isn't handled, log it in DEBUG mode and exit | ||
if (commands && (commands[name] || commands["default"])) { | ||
var handler = commands[name] || commands["default"]; | ||
args = commands[name] ? args : arguments; | ||
Radio._callHandler(handler.callback, handler.context, args); | ||
} else { | ||
Radio.debugLog("An unhandled command was fired", name, channelName); | ||
} | ||
return this; | ||
}, | ||
// Register a handler for a command. | ||
comply: function comply(name, callback, context) { | ||
if (Radio._eventsApi(this, "comply", name, [callback, context])) { | ||
return this; | ||
} | ||
this._commands || (this._commands = {}); | ||
if (this._commands[name]) { | ||
Radio.debugLog("A command was overwritten", name, this.channelName); | ||
} | ||
this._commands[name] = { | ||
callback: callback, | ||
context: context || this | ||
}; | ||
return this; | ||
}, | ||
// Register a handler for a command that happens just once. | ||
complyOnce: function complyOnce(name, callback, context) { | ||
if (Radio._eventsApi(this, "complyOnce", name, [callback, context])) { | ||
return this; | ||
} | ||
var self = this; | ||
var once = _.once(function () { | ||
self.stopComplying(name); | ||
return callback.apply(this, arguments); | ||
}); | ||
return this.comply(name, once, context); | ||
}, | ||
// Remove handler(s) | ||
stopComplying: function stopComplying(name, callback, context) { | ||
if (Radio._eventsApi(this, "stopComplying", name)) { | ||
return this; | ||
} | ||
// Remove everything if there are no arguments passed | ||
if (!name && !callback && !context) { | ||
delete this._commands; | ||
} else if (!removeHandlers(this._commands, name, callback, context)) { | ||
Radio.debugLog("Attempted to remove the unregistered command", name, this.channelName); | ||
} | ||
return this; | ||
} | ||
}; | ||
/* | ||
* Backbone.Radio.Requests | ||
@@ -375,3 +289,3 @@ * ----------------------- | ||
* A Channel is an object that extends from Backbone.Events, | ||
* Radio.Commands, and Radio.Requests. | ||
* and Radio.Requests. | ||
* | ||
@@ -384,3 +298,3 @@ */ | ||
_.extend(Radio.Channel.prototype, Backbone.Events, Radio.Commands, Radio.Requests, { | ||
_.extend(Radio.Channel.prototype, Backbone.Events, Radio.Requests, { | ||
@@ -391,3 +305,2 @@ // Remove all handlers from the messaging systems of this channel | ||
this.stopListening(); | ||
this.stopComplying(); | ||
this.stopReplying(); | ||
@@ -394,0 +307,0 @@ return this; |
@@ -1,3 +0,3 @@ | ||
// Backbone.Radio v0.9.1 | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("underscore"),require("backbone")):"function"==typeof define&&define.amd?define(["underscore","backbone"],t):e.Backbone.Radio=t(e._,e.Backbone)}(this,function(e,t){"use strict";function n(e,t,n,s){var r=e[t];return n&&n!==r.callback&&n!==r.callback._callback||s&&s!==r.context?void 0:(delete e[t],!0)}function s(t,s,r,i){t||(t={});for(var a=s?[s]:e.keys(t),o=!1,c=0,u=a.length;u>c;c++)s=a[c],t[s]&&n(t,s,r,i)&&(o=!0);return o}function r(t){return u[t]||(u[t]=e.partial(o.log,t))}function i(t){return e.isFunction(t)?t:function(){return t}}var a=t.Radio,o=t.Radio={};o.VERSION="0.9.1",o.noConflict=function(){return t.Radio=a,this},o.DEBUG=!1,o._debugText=function(e,t,n){return e+(n?" on the "+n+" channel":"")+': "'+t+'"'},o.debugLog=function(e,t,n){o.DEBUG&&console&&console.warn&&console.warn(o._debugText(e,t,n))};var c=/\s+/;o._eventsApi=function(t,n,s,r){if(!s)return!1;var i={};if("object"==typeof s){for(var a in s){var o=t[n].apply(t,[a,s[a]].concat(r));c.test(a)?e.extend(i,o):i[a]=o}return i}if(c.test(s)){for(var u=s.split(c),l=0,h=u.length;h>l;l++)i[u[l]]=t[n].apply(t,[u[l]].concat(r));return i}return!1},o._callHandler=function(e,t,n){var s=n[0],r=n[1],i=n[2];switch(n.length){case 0:return e.call(t);case 1:return e.call(t,s);case 2:return e.call(t,s,r);case 3:return e.call(t,s,r,i);default:return e.apply(t,n)}};var u={};e.extend(o,{log:function(t,n){var s=e.rest(arguments,2);console.log("["+t+'] "'+n+'"',s)},tuneIn:function(e){var t=o.channel(e);return t._tunedIn=!0,t.on("all",r(e)),this},tuneOut:function(e){var t=o.channel(e);return t._tunedIn=!1,t.off("all",r(e)),delete u[e],this}}),o.Commands={command:function(t){var n=e.rest(arguments);if(o._eventsApi(this,"command",t,n))return this;var s=this.channelName,r=this._commands;if(s&&this._tunedIn&&o.log.apply(this,[s,t].concat(n)),r&&(r[t]||r["default"])){var i=r[t]||r["default"];n=r[t]?n:arguments,o._callHandler(i.callback,i.context,n)}else o.debugLog("An unhandled command was fired",t,s);return this},comply:function(e,t,n){return o._eventsApi(this,"comply",e,[t,n])?this:(this._commands||(this._commands={}),this._commands[e]&&o.debugLog("A command was overwritten",e,this.channelName),this._commands[e]={callback:t,context:n||this},this)},complyOnce:function(t,n,s){if(o._eventsApi(this,"complyOnce",t,[n,s]))return this;var r=this,i=e.once(function(){return r.stopComplying(t),n.apply(this,arguments)});return this.comply(t,i,s)},stopComplying:function(e,t,n){return o._eventsApi(this,"stopComplying",e)?this:(e||t||n?s(this._commands,e,t,n)||o.debugLog("Attempted to remove the unregistered command",e,this.channelName):delete this._commands,this)}},o.Requests={request:function(t){var n=e.rest(arguments),s=o._eventsApi(this,"request",t,n);if(s)return s;var r=this.channelName,i=this._requests;if(r&&this._tunedIn&&o.log.apply(this,[r,t].concat(n)),i&&(i[t]||i["default"])){var a=i[t]||i["default"];return n=i[t]?n:arguments,o._callHandler(a.callback,a.context,n)}o.debugLog("An unhandled request was fired",t,r)},reply:function(e,t,n){return o._eventsApi(this,"reply",e,[t,n])?this:(this._requests||(this._requests={}),this._requests[e]&&o.debugLog("A request was overwritten",e,this.channelName),this._requests[e]={callback:i(t),context:n||this},this)},replyOnce:function(t,n,s){if(o._eventsApi(this,"replyOnce",t,[n,s]))return this;var r=this,a=e.once(function(){return r.stopReplying(t),i(n).apply(this,arguments)});return this.reply(t,a,s)},stopReplying:function(e,t,n){return o._eventsApi(this,"stopReplying",e)?this:(e||t||n?s(this._requests,e,t,n)||o.debugLog("Attempted to remove the unregistered request",e,this.channelName):delete this._requests,this)}},o._channels={},o.channel=function(e){if(!e)throw new Error("You must provide a name for the channel.");return o._channels[e]?o._channels[e]:o._channels[e]=new o.Channel(e)},o.Channel=function(e){this.channelName=e},e.extend(o.Channel.prototype,t.Events,o.Commands,o.Requests,{reset:function(){return this.off(),this.stopListening(),this.stopComplying(),this.stopReplying(),this}});var l,h,d=[t.Events,o.Commands,o.Requests];e.each(d,function(t){e.each(t,function(t,n){o[n]=function(t){return h=e.rest(arguments),l=this.channel(t),l[n].apply(l,h)}})}),o.reset=function(t){var n=t?[this._channels[t]]:this._channels;e.invoke(n,"reset")};var f=o;return f}); | ||
// Backbone.Radio v1.0.0 | ||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n(require("underscore"),require("backbone")):"function"==typeof define&&define.amd?define(["underscore","backbone"],n):e.Backbone.Radio=n(e._,e.Backbone)}(this,function(e,n){"use strict";function t(e,n,t,r){var s=e[n];return t&&t!==s.callback&&t!==s.callback._callback||r&&r!==s.context?void 0:(delete e[n],!0)}function r(n,r,s,i){n||(n={});for(var a=r?[r]:e.keys(n),u=!1,o=0,c=a.length;c>o;o++)r=a[o],n[r]&&t(n,r,s,i)&&(u=!0);return u}function s(n){return c[n]||(c[n]=e.partial(u.log,n))}function i(n){return e.isFunction(n)?n:function(){return n}}var a=n.Radio,u=n.Radio={};u.VERSION="1.0.0",u.noConflict=function(){return n.Radio=a,this},u.DEBUG=!1,u._debugText=function(e,n,t){return e+(t?" on the "+t+" channel":"")+': "'+n+'"'},u.debugLog=function(e,n,t){u.DEBUG&&console&&console.warn&&console.warn(u._debugText(e,n,t))};var o=/\s+/;u._eventsApi=function(n,t,r,s){if(!r)return!1;var i={};if("object"==typeof r){for(var a in r){var u=n[t].apply(n,[a,r[a]].concat(s));o.test(a)?e.extend(i,u):i[a]=u}return i}if(o.test(r)){for(var c=r.split(o),l=0,h=c.length;h>l;l++)i[c[l]]=n[t].apply(n,[c[l]].concat(s));return i}return!1},u._callHandler=function(e,n,t){var r=t[0],s=t[1],i=t[2];switch(t.length){case 0:return e.call(n);case 1:return e.call(n,r);case 2:return e.call(n,r,s);case 3:return e.call(n,r,s,i);default:return e.apply(n,t)}};var c={};e.extend(u,{log:function(n,t){var r=e.rest(arguments,2);console.log("["+n+'] "'+t+'"',r)},tuneIn:function(e){var n=u.channel(e);return n._tunedIn=!0,n.on("all",s(e)),this},tuneOut:function(e){var n=u.channel(e);return n._tunedIn=!1,n.off("all",s(e)),delete c[e],this}}),u.Requests={request:function(n){var t=e.rest(arguments),r=u._eventsApi(this,"request",n,t);if(r)return r;var s=this.channelName,i=this._requests;if(s&&this._tunedIn&&u.log.apply(this,[s,n].concat(t)),i&&(i[n]||i["default"])){var a=i[n]||i["default"];return t=i[n]?t:arguments,u._callHandler(a.callback,a.context,t)}u.debugLog("An unhandled request was fired",n,s)},reply:function(e,n,t){return u._eventsApi(this,"reply",e,[n,t])?this:(this._requests||(this._requests={}),this._requests[e]&&u.debugLog("A request was overwritten",e,this.channelName),this._requests[e]={callback:i(n),context:t||this},this)},replyOnce:function(n,t,r){if(u._eventsApi(this,"replyOnce",n,[t,r]))return this;var s=this,a=e.once(function(){return s.stopReplying(n),i(t).apply(this,arguments)});return this.reply(n,a,r)},stopReplying:function(e,n,t){return u._eventsApi(this,"stopReplying",e)?this:(e||n||t?r(this._requests,e,n,t)||u.debugLog("Attempted to remove the unregistered request",e,this.channelName):delete this._requests,this)}},u._channels={},u.channel=function(e){if(!e)throw new Error("You must provide a name for the channel.");return u._channels[e]?u._channels[e]:u._channels[e]=new u.Channel(e)},u.Channel=function(e){this.channelName=e},e.extend(u.Channel.prototype,n.Events,u.Requests,{reset:function(){return this.off(),this.stopListening(),this.stopReplying(),this}});var l,h,f=[n.Events,u.Commands,u.Requests];e.each(f,function(n){e.each(n,function(n,t){u[t]=function(n){return h=e.rest(arguments),l=this.channel(n),l[t].apply(l,h)}})}),u.reset=function(n){var t=n?[this._channels[n]]:this._channels;e.invoke(t,"reset")};var p=u;return p}); | ||
//# sourceMappingURL=backbone.radio.min.js.map |
@@ -5,3 +5,3 @@ { | ||
"homepage": "https://github.com/marionettejs/backbone.radio", | ||
"version": "0.9.1", | ||
"version": "1.0.0", | ||
"main": "build/backbone.radio.js", | ||
@@ -8,0 +8,0 @@ "keywords": [ |
199
README.md
@@ -9,9 +9,13 @@ # Backbone.Radio | ||
Use Backbone.Radio to build large, maintainable, and decoupled applications. | ||
Backbone.Radio provides additional messaging patterns for Backbone applications. | ||
Backbone.Radio is a collection of messaging patterns for Backbone applications. It uses Backbone.Events as a | ||
pub-sub message bus, then adds semantics to your communications through the addition of two new messaging | ||
patterns, Commands and Requests. The three systems are bound together as Channels, which provide explicit | ||
namespacing to your communications. | ||
Backbone includes an event system, Backbone.Events, which is an implementation of the publish-subscribe pattern. Pub-sub is by far the most | ||
common event pattern in client-side applications, and for good reason: it is incredibly useful. It should also be familiar to web developers | ||
in particular, because the DOM relies heavily on pub-sub. Consider, for instance, registering a handler on an element's `click` event. This isn't | ||
so much different than listening to a Model's `change` event, as both of these situations are using pub-sub. | ||
Backbone.Radio adds two additional messaging-related features. The first is Requests, an implementation of the request-reply pattern. Request-reply | ||
should also be familiar to web developers, as it's the messaging pattern that backs HTTP communications. The other feature are Channels: explicit | ||
namespaces to your communications. | ||
## Installation | ||
@@ -30,3 +34,2 @@ | ||
- [Backbone.Events](#backboneevents) | ||
- [Radio.Commands](#backboneradiocommands) | ||
- [Radio.Requests](#backboneradiorequests) | ||
@@ -36,3 +39,2 @@ - [Channels](#channels) | ||
- [API](#api) | ||
- [Radio.Commands](#commands) | ||
- [Radio.Requests](#requests) | ||
@@ -79,61 +81,20 @@ - [Channel](#channel) | ||
This is the first principle of Backbone.Radio: building a message bus out of Backbone.Events is useful. But before we go more | ||
into that, let's look at the two other messaging systems of Backbone.Radio. | ||
As long as there was an easy way to access this message bus throughout your entire application, then you would have a central | ||
place to store a collection of events. This is the idea behind Channels. But before we go more into that, let's take a look at Requests. | ||
### Backbone.Radio.Commands | ||
### Backbone.Radio.Requests | ||
Commands is similar to Backbone.Events in many ways. You can mix it into your objects, or use it as a standalone message | ||
bus. | ||
Requests is similar to Events in that it's another event system. And it has a similar API, too. For this reason, you *could* mix | ||
it into an object. | ||
```js | ||
// You should be familiar with attaching Backbone.Events to an object... | ||
_.extend(myObj, Backbone.Events); | ||
// Well, attaching Commands is identical | ||
_.extend(myObj, Backbone.Radio.Commands); | ||
_.extend(myView, Backbone.Radio.Requests); | ||
``` | ||
Once you've attached Commands to your object your object will now have access to the Commands API. | ||
Although this works, I wouldn't recommend it. Requests are most useful, I think, when they're used with a Channel. | ||
The next question, then, is what *are* Commands? Commands are a semantic implementation of Backbone.Events. One of the primary | ||
differences between Backbone.Events and Commands is that Commands have **intent**, whereas Events do not. For example, when a model triggers its | ||
change event, it has no goal in mind. Instead, the listeners of that event decide what to do with that information. Commands are different. | ||
You fire a Command when you do have a goal in mind. And to be even more specific, you fire a Command when you want another object to perform a | ||
particular task. | ||
Perhaps the biggest difference between Events and Requests is that Requests have *intention*. Unlike Events, which notify | ||
nothing in particular about an occurrence, Requests are asking for a very specific thing to occur. As a consequence of this, | ||
requests are 'one-to-one,' which means that you cannot have multiple 'listeners' to a single request. | ||
```js | ||
// Set up a view to comply with a command | ||
myView.comply('render', myView.render); | ||
// Causes the view to render | ||
myView.command('render'); | ||
``` | ||
Commands have a few other things that make it distinct from Backbone.Events. First, you can only register one 'listener' at a time, unlike | ||
Backbone.Events where there can be many listeners for each trigger. Instead, Commands is a one-to-one relationship. Another difference is that | ||
no information is returned from the executed callback. This is also unlike Events, where information can travel from the triggerer to its listeners. | ||
The following diagram illustrates the Commands pattern: | ||
<p align='center'> | ||
<img src='https://i.cloudup.com/7e9M5rKFOr.svg' alt='Backbone.Commands diagram'> | ||
</p> | ||
You might ask yourself, 'Now why in the world would I fire the command when I can | ||
just call the method directly?' The answer is that you wouldn't. I only meant for the above example to be used as a means to familiarize yourself | ||
with the way Commands works. The real utility of Commands comes when it is used in an independent message bus. But more on that later – let's | ||
first look at Requests. | ||
### Backbone.Radio.Requests | ||
Requests is the last piece of Backbone.Radio. You use it just like Events and Commands: mix it into an object. | ||
```js | ||
_.extend(myObj, Backbone.Radio.Requests); | ||
``` | ||
Requests share more similarities to Commands than they do Events. They are semantic, by which I mean that there is an intention when making a | ||
request. Of course, the intent here is that you are asking for information to be returned. Just like Commands, requests have a one-to-one system; | ||
you can't have multiple 'listeners' to the triggerer. | ||
Let's look at a basic example. | ||
@@ -143,6 +104,6 @@ | ||
// Set up an object to reply to a request. In this case, whether or not its visible. | ||
myView.reply('visible', this.isVisible); | ||
myObject.reply('visible', this.isVisible); | ||
// Get whether it's visible or not. | ||
var isViewVisible = myView.request('visible'); | ||
var isViewVisible = myObject.request('visible'); | ||
``` | ||
@@ -159,6 +120,25 @@ | ||
Although the name is 'Requests,' you can just as easily request information as you can request that an action be completed. Just like HTTP, | ||
where you can both make GET requests for information, or DELETE requests to order than a resource be deleted, Requests can be used for a variety | ||
of purposes. | ||
One thing to note is that this pattern is **identical** to a simple method call. One can just as easily rewrite the above example as: | ||
```js | ||
// Set up a method... | ||
myObject.isVisible = function() { | ||
return this.viewIsVisible; | ||
} | ||
// Call that method | ||
var isViewVisible = myObject.isVisible(); | ||
``` | ||
This is why mixing Requests into something like a View or Model does not make much sense. If you have access to the View or Model, then | ||
you might as well just use methods. | ||
### Channels | ||
The real draw of Backbone.Radio are channels. A Channel is simply an object that has Backbone.Events, Radio.Commands, and Radio.Requests mixed into it; | ||
it's a standalone message bus comprised of all three systems. | ||
The real draw of Backbone.Radio are Channels. A Channel is simply an object that has Backbone.Events and Radio.Requests mixed into it: | ||
it's a standalone message bus comprised of both systems. | ||
@@ -179,6 +159,2 @@ Getting a handle of a Channel is easy. | ||
userChannel.comply('some:action', function() { | ||
console.log('I was told to execute some action'); | ||
}); | ||
userChannel.reply('some:request', 'food is good'); | ||
@@ -192,4 +168,2 @@ ``` | ||
userChannel.command('some:command'); | ||
userChannel.request('some:request'); | ||
@@ -208,11 +182,5 @@ ``` | ||
The whole point of Channels is that they provide a way to explicitly namespace events in your application. It gives you greater | ||
control over which objects are able to talk to one another. | ||
The whole point of Channels is that they provide a way to explicitly namespace events in your application, and a means to easily access | ||
any of those namespaces. | ||
If you're having difficulty remembering the API of Channels here's a useful mnemonic for you. | ||
Events is the API that you know; `on`, `off`, `stopListening` and so on. Commands, which starts with a C, only | ||
uses verbs that start with C: `command`, `comply`, `stopComplying`. And lastly, Requests, which starts with an R, | ||
only uses verbs that start with R: `request`, `reply`, and so on. | ||
### Using With Marionette | ||
@@ -227,65 +195,2 @@ | ||
### Commands | ||
#### `command( commandName [, args...] )` | ||
Order a command to be completed. Optionally pass arguments to send along to the callback. Like Backbone.Event's `trigger` method, | ||
this method returns the instance of Commands. | ||
You can order multiple commands at once by using the space-separated syntax. | ||
```js | ||
myChannel.command('commandOne commandTwo'); | ||
``` | ||
This method always returns `undefined`. | ||
#### `comply( commandName, callback [, context] )` | ||
Register a handler for `commandName` on this object. `callback` will be executed whenever the command is run. Optionally | ||
pass a `context` for the callback, defaulting to `this`. | ||
To register a default handler for Commands use the `default` commandName. The unhandled `commandName` will be passed as the first argument. | ||
```js | ||
myChannel.comply('default', function(commandName) { | ||
console.log('No handler was found for this command: ' + commandName); | ||
}); | ||
// This will be handled by the default handler | ||
myChannel.command('someUnhandledCommand'); | ||
``` | ||
To register multiple commands at once you may also pass in a hash. | ||
```js | ||
// Connect all of the commands at once | ||
myChannel.comply({ | ||
'some:command': myCallback, | ||
'some:other:command': someOtherCallback | ||
}, context); | ||
``` | ||
Returns the instance of Commands. | ||
#### `complyOnce( commandName, callback [, context] )` | ||
Register a handler for `commandName` that only executes a single time. | ||
Like `comply`, you may also pass a hash of commands to register many at once. Refer to the `comply` documentation above | ||
for an example. | ||
Returns the instance of Commands. | ||
#### `stopComplying( [commandName] [, callback] [, context] )` | ||
If `context` is passed, then all handlers with that context will be removed from the object. If `callback` is | ||
passed then all handlers with that callback will be removed. If `commandName` is passed then this method will | ||
remove that handler. If no arguments are passed then all handlers are removed from the object. | ||
You may also pass a hash of commands or space-separated list to remove many commands at once. Refer to the `comply` documentation above | ||
for an example. | ||
Returns the instance of Commands. | ||
### Requests | ||
@@ -301,3 +206,3 @@ | ||
```js | ||
myChannel.request('commandOne commandTwo'); | ||
myChannel.request('requestOne requestTwo'); | ||
``` | ||
@@ -363,3 +268,3 @@ | ||
Destroy all handlers from Backbone.Events, Radio.Commands, and Radio.Requests from the channel. Returns the channel. | ||
Destroy all handlers from Backbone.Events and Radio.Requests from the channel. Returns the channel. | ||
@@ -379,3 +284,3 @@ ### Radio | ||
This is a Boolean property. Setting it to `true` will cause console warnings to be issued | ||
whenever you interact with a `request` or `command` that isn't registered. This is useful in development when you want to | ||
whenever you interact with a `request` that isn't registered. This is useful in development when you want to | ||
ensure that you've got your event names in order. | ||
@@ -388,3 +293,3 @@ | ||
// This will log a warning to the console if it goes unhandled | ||
myChannel.command('show:view'); | ||
myChannel.request('show:view'); | ||
@@ -397,3 +302,3 @@ // Likewise, this will too, helping to prevent memory leaks | ||
A function executed whenever an unregistered command or request is interacted with on a Channel. Only | ||
A function executed whenever an unregistered request is interacted with on a Channel. Only | ||
called when `DEBUG` is set to `true`. By overriding this you could, for instance, make unhandled | ||
@@ -404,3 +309,3 @@ events throw Errors. | ||
> Attempted to remove the unregistered command | ||
> Attempted to remove the unregistered request | ||
@@ -412,3 +317,3 @@ while the `eventName` and `channelName` are what you would expect. | ||
Tuning into a Channel is another useful tool for debugging. It passes all | ||
triggers, commands, and requests made on the channel to | ||
triggers and requests made on the channel to | ||
@@ -452,3 +357,3 @@ [`Radio.log`](https://github.com/jmeas/backbone.radio#log-channelname-eventname--args-). | ||
All of the methods for all three messaging systems are available from the top-level API. | ||
All of the methods for both messaging systems are available from the top-level API. | ||
@@ -455,0 +360,0 @@ #### `reset( [channelName] )` |
@@ -41,4 +41,4 @@ import _ from 'underscore'; | ||
// An internal method used to handle Radio's method overloading for Requests and | ||
// Commands. It's borrowed from Backbone.Events. It differs from Backbone's overload | ||
// An internal method used to handle Radio's method overloading for Requests. | ||
// It's borrowed from Backbone.Events. It differs from Backbone's overload | ||
// API (which is used in Backbone.Events) in that it doesn't support space-separated | ||
@@ -163,88 +163,3 @@ // event names. | ||
/* | ||
* Backbone.Radio.Commands | ||
* ----------------------- | ||
* A messaging system for sending orders. | ||
* | ||
*/ | ||
Radio.Commands = { | ||
// Issue a command | ||
command: function(name) { | ||
var args = _.rest(arguments); | ||
if (Radio._eventsApi(this, 'command', name, args)) { | ||
return this; | ||
} | ||
var channelName = this.channelName; | ||
var commands = this._commands; | ||
// Check if we should log the command, and if so, do it | ||
if (channelName && this._tunedIn) { | ||
Radio.log.apply(this, [channelName, name].concat(args)); | ||
} | ||
// If the command isn't handled, log it in DEBUG mode and exit | ||
if (commands && (commands[name] || commands['default'])) { | ||
var handler = commands[name] || commands['default']; | ||
args = commands[name] ? args : arguments; | ||
Radio._callHandler(handler.callback, handler.context, args); | ||
} else { | ||
Radio.debugLog('An unhandled command was fired', name, channelName); | ||
} | ||
return this; | ||
}, | ||
// Register a handler for a command. | ||
comply: function(name, callback, context) { | ||
if (Radio._eventsApi(this, 'comply', name, [callback, context])) { | ||
return this; | ||
} | ||
this._commands || (this._commands = {}); | ||
if (this._commands[name]) { | ||
Radio.debugLog('A command was overwritten', name, this.channelName); | ||
} | ||
this._commands[name] = { | ||
callback: callback, | ||
context: context || this | ||
}; | ||
return this; | ||
}, | ||
// Register a handler for a command that happens just once. | ||
complyOnce: function(name, callback, context) { | ||
if (Radio._eventsApi(this, 'complyOnce', name, [callback, context])) { | ||
return this; | ||
} | ||
var self = this; | ||
var once = _.once(function() { | ||
self.stopComplying(name); | ||
return callback.apply(this, arguments); | ||
}); | ||
return this.comply(name, once, context); | ||
}, | ||
// Remove handler(s) | ||
stopComplying: function(name, callback, context) { | ||
if (Radio._eventsApi(this, 'stopComplying', name)) { | ||
return this; | ||
} | ||
// Remove everything if there are no arguments passed | ||
if (!name && !callback && !context) { | ||
delete this._commands; | ||
} else if (!removeHandlers(this._commands, name, callback, context)) { | ||
Radio.debugLog('Attempted to remove the unregistered command', name, this.channelName); | ||
} | ||
return this; | ||
} | ||
}; | ||
/* | ||
@@ -366,3 +281,3 @@ * Backbone.Radio.Requests | ||
* A Channel is an object that extends from Backbone.Events, | ||
* Radio.Commands, and Radio.Requests. | ||
* and Radio.Requests. | ||
* | ||
@@ -375,3 +290,3 @@ */ | ||
_.extend(Radio.Channel.prototype, Backbone.Events, Radio.Commands, Radio.Requests, { | ||
_.extend(Radio.Channel.prototype, Backbone.Events, Radio.Requests, { | ||
@@ -382,3 +297,2 @@ // Remove all handlers from the messaging systems of this channel | ||
this.stopListening(); | ||
this.stopComplying(); | ||
this.stopReplying(); | ||
@@ -385,0 +299,0 @@ return this; |
@@ -26,6 +26,2 @@ describe('Channel:', function () { | ||
it('should have all of the Radio.Commands methods', function() { | ||
expect(this.channel).to.contain(Backbone.Radio.Commands); | ||
}); | ||
it('should have all of the Radio.Requests methods', function() { | ||
@@ -50,3 +46,2 @@ expect(this.channel).to.contain(Backbone.Radio.Requests); | ||
stub(this.channel, 'stopListening'); | ||
stub(this.channel, 'stopComplying'); | ||
stub(this.channel, 'stopReplying'); | ||
@@ -63,6 +58,2 @@ spy(this.channel, 'reset'); | ||
it('should call the reset functions of Backbone.Radio.Commands', function() { | ||
expect(this.channel.stopComplying).to.have.been.calledOnce; | ||
}); | ||
it('should call the reset functions of Backbone.Radio.Requests', function() { | ||
@@ -97,16 +88,2 @@ expect(this.channel.stopReplying).to.have.been.calledOnce; | ||
describe('on Commands', function() { | ||
beforeEach(function() { | ||
this.channel.comply('some:command', this.callbackOne); | ||
this.channelTwo.comply('some:command', this.callbackOne); | ||
this.channel.command('some:command'); | ||
}); | ||
it('should only trigger the callback on the channel specified', function() { | ||
expect(this.callbackOne).to.have.been.calledOnce; | ||
expect(this.callbackTwo).to.not.have.been.called; | ||
}); | ||
}); | ||
describe('on Requests', function() { | ||
@@ -113,0 +90,0 @@ beforeEach(function() { |
describe('DEBUG mode:', function() { | ||
beforeEach(function() { | ||
this.channel = Backbone.Radio.channel('myChannel'); | ||
this.Commands = _.clone(Backbone.Radio.Commands); | ||
this.Requests = _.clone(Backbone.Radio.Requests); | ||
@@ -16,3 +15,3 @@ | ||
Backbone.Radio.DEBUG = true; | ||
this.channel.command('some:event'); | ||
this.channel.request('some:event'); | ||
}); | ||
@@ -38,8 +37,2 @@ | ||
it('should log a console warning when firing a command on a channel without a handler', function() { | ||
this.channel.command('some:event'); | ||
this.warning = 'An unhandled command was fired on the myChannel channel: "some:event"'; | ||
expect(console.warn).to.have.been.calledOnce.and.calledWithExactly(this.warning); | ||
}); | ||
it('should log a console warning when firing a request on a channel without a handler', function() { | ||
@@ -51,8 +44,2 @@ this.channel.request('some:event'); | ||
it('should log a console warning when firing a command on an object without a handler', function() { | ||
this.Commands.command('some:event'); | ||
this.warning = 'An unhandled command was fired: "some:event"'; | ||
expect(console.warn).to.have.been.calledOnce.and.calledWithExactly(this.warning); | ||
}); | ||
it('should log a console warning when firing a request on an object without a handler', function() { | ||
@@ -64,8 +51,2 @@ this.Requests.request('some:event'); | ||
it('should log a console warning when unregistering a command that was never registered on a channel', function() { | ||
this.channel.stopComplying('some:event'); | ||
this.warning = 'Attempted to remove the unregistered command on the myChannel channel: "some:event"'; | ||
expect(console.warn).to.have.been.calledOnce.and.calledWithExactly(this.warning); | ||
}); | ||
it('should log a console warning when unregistering a request that was never registered on a channel', function() { | ||
@@ -77,8 +58,2 @@ this.channel.stopReplying('some:event'); | ||
it('should log a console warning when unregistering a command that was never registered on an object', function() { | ||
this.Commands.stopComplying('some:event'); | ||
this.warning = 'Attempted to remove the unregistered command: "some:event"'; | ||
expect(console.warn).to.have.been.calledOnce.and.calledWithExactly(this.warning); | ||
}); | ||
it('should log a console warning when unregistering a request that was never registered on an object', function() { | ||
@@ -90,8 +65,2 @@ this.Requests.stopReplying('some:event'); | ||
it('should log a console warning when unregistering a command that was never registered on an object', function() { | ||
this.Commands.comply('some:event'); | ||
this.Commands.stopComplying('some:event'); | ||
expect(console.warn).to.not.have.been.called; | ||
}); | ||
it('should log a console warning when unregistering a request that was never registered on an object', function() { | ||
@@ -104,8 +73,2 @@ this.Requests.reply('some:event'); | ||
it('should log a console warning when unregistering a callback that was never registered on an object', function() { | ||
this.Commands.stopComplying(undefined, function() {}); | ||
this.warning = 'Attempted to remove the unregistered command: "undefined"'; | ||
expect(console.warn).to.have.been.calledOnce.and.calledWithExactly(this.warning); | ||
}); | ||
it('should log a console warning when unregistering a callback that was never registered on an object', function() { | ||
this.Requests.stopReplying(undefined, function() {}); | ||
@@ -118,9 +81,2 @@ this.warning = 'Attempted to remove the unregistered request: "undefined"'; | ||
this.callback = function() {}; | ||
this.Commands.comply('some:event', this.callback); | ||
this.Commands.stopComplying(undefined, this.callback); | ||
expect(console.warn).to.not.have.been.called; | ||
}); | ||
it('should not log a console warning when unregistering a callback that was registered on an object', function() { | ||
this.callback = function() {}; | ||
this.Requests.reply('some:event', this.callback); | ||
@@ -132,8 +88,2 @@ this.Requests.stopReplying(undefined, this.callback); | ||
it('should log a console warning when unregistering a context that was never registered on an object', function() { | ||
this.Commands.stopComplying(undefined, undefined, {}); | ||
this.warning = 'Attempted to remove the unregistered command: "undefined"'; | ||
expect(console.warn).to.have.been.calledOnce.and.calledWithExactly(this.warning); | ||
}); | ||
it('should log a console warning when unregistering a context that was never registered on an object', function() { | ||
this.Requests.stopReplying(undefined, undefined, {}); | ||
@@ -146,9 +96,2 @@ this.warning = 'Attempted to remove the unregistered request: "undefined"'; | ||
this.context = {}; | ||
this.Commands.comply('some:event', function() {}, this.context); | ||
this.Commands.stopComplying(undefined, undefined, this.context); | ||
expect(console.warn).to.not.have.been.called; | ||
}); | ||
it('should not log a console warning when unregistering a context that was registered on an object', function() { | ||
this.context = {}; | ||
this.Requests.reply('some:event', function() {}, this.context); | ||
@@ -159,7 +102,2 @@ this.Requests.stopReplying(undefined, undefined, this.context); | ||
it('should not log a console warning when unregistering all commands when none were registered', function() { | ||
this.Commands.stopComplying(); | ||
expect(console.warn).to.not.have.been.called; | ||
}); | ||
it('should not log a console warning when unregistering all requests when none were registered', function() { | ||
@@ -170,9 +108,2 @@ this.Requests.stopReplying(); | ||
it('should log a console warning when registering a command that already was registered', function() { | ||
this.Commands.comply('some:event', function() {}); | ||
this.Commands.comply('some:event', function() {}); | ||
this.warning = 'A command was overwritten: "some:event"'; | ||
expect(console.warn).to.have.been.calledOnce.and.calledWithExactly(this.warning); | ||
}); | ||
it('should log a console warning when registering a request that already was registered', function() { | ||
@@ -187,7 +118,2 @@ this.Requests.reply('some:event', function() {}); | ||
describe('when turned off', function() { | ||
it('should not log a console warning when firing a command on a channel without a handler', function() { | ||
this.channel.command('some:event'); | ||
expect(console.warn).to.not.have.been.called; | ||
}); | ||
it('should not log a console warning when firing a request on a channel without a handler', function() { | ||
@@ -198,7 +124,2 @@ this.channel.request('some:event'); | ||
it('should not log a console warning when firing a command on an object without a handler', function() { | ||
this.Commands.command('some:event'); | ||
expect(console.warn).to.not.have.been.called; | ||
}); | ||
it('should not log a console warning when firing a request on an object without a handler', function() { | ||
@@ -209,7 +130,2 @@ this.Requests.request('some:event'); | ||
it('should not log a console warning when unregistering a command that was never registered on a channel', function() { | ||
this.channel.stopComplying('some:event'); | ||
expect(console.warn).to.not.have.been.called; | ||
}); | ||
it('should not log a console warning when unregistering a request that was never registered on a channel', function() { | ||
@@ -220,7 +136,2 @@ this.channel.stopReplying('some:event'); | ||
it('should not log a console warning when unregistering a command that was never registered on an object', function() { | ||
this.Commands.stopComplying('some:event'); | ||
expect(console.warn).to.not.have.been.called; | ||
}); | ||
it('should not log a console warning when unregistering a request that was never registered on an object', function() { | ||
@@ -231,8 +142,2 @@ this.Requests.stopReplying('some:event'); | ||
it('should not log a console warning when registering a command that already was registered', function() { | ||
this.Commands.comply('some:event', function() {}); | ||
this.Commands.comply('some:event', function() {}); | ||
expect(console.warn).to.not.have.been.called; | ||
}); | ||
it('should not log a console warning when registering a request that already was registered', function() { | ||
@@ -239,0 +144,0 @@ this.Requests.reply('some:event', function() {}); |
@@ -22,6 +22,2 @@ describe('When Radio is attached to your application', function() { | ||
it('should have the Commands Class attached to it', function() { | ||
expect(Backbone.Radio.Commands).to.exist; | ||
}); | ||
it('should have the Requests Class attached to it', function() { | ||
@@ -28,0 +24,0 @@ expect(Backbone.Radio.Requests).to.exist; |
@@ -44,33 +44,2 @@ describe('Top-level API:', function() { | ||
describe('when executing Commands methods', function() { | ||
beforeEach(function() { | ||
Backbone.Radio.comply('myChannel', 'some:command', 'firstArg1', 'secondArg1'); | ||
Backbone.Radio.complyOnce('myChannel', 'some:command', 'firstArg2', 'secondArg2'); | ||
Backbone.Radio.stopComplying('myChannel', 'some:command', 'firstArg3', 'secondArg3'); | ||
Backbone.Radio.command('myChannel', 'some:command', 'firstArg4', 'secondArg4'); | ||
}); | ||
it('should execute each method on the proper channel with the arguments.', function() { | ||
expect(this.channel.comply) | ||
.to.have.been.calledOnce | ||
.and.to.have.been.calledOn(this.channel) | ||
.and.calledWithExactly('some:command', 'firstArg1', 'secondArg1'); | ||
expect(this.channel.complyOnce) | ||
.to.have.been.calledOnce | ||
.and.to.have.been.calledOn(this.channel) | ||
.and.calledWithExactly('some:command', 'firstArg2', 'secondArg2'); | ||
expect(this.channel.stopComplying) | ||
.to.have.been.calledOnce | ||
.and.to.have.been.calledOn(this.channel) | ||
.and.calledWithExactly('some:command', 'firstArg3', 'secondArg3'); | ||
expect(this.channel.command) | ||
.to.have.been.calledOnce | ||
.and.to.have.been.calledOn(this.channel) | ||
.and.calledWithExactly('some:command', 'firstArg4', 'secondArg4'); | ||
}); | ||
}); | ||
describe('when executing Requests methods', function() { | ||
@@ -77,0 +46,0 @@ beforeEach(function() { |
@@ -74,28 +74,6 @@ describe('Tune-in:', function() { | ||
describe('when tuned into a channel and ordering a command', function() { | ||
beforeEach(function() { | ||
this.channel.command('some:event', 'argOne', 'argTwo'); | ||
this.warning = '[myChannel] "some:event"'; | ||
}); | ||
it('should log that activity', function() { | ||
expect(console.log).to.have.been.calledOnce.and.calledWithExactly(this.warning, ['argOne', 'argTwo']); | ||
}); | ||
}); | ||
describe('when tuning in, then out, and ordering a command', function() { | ||
beforeEach(function() { | ||
Backbone.Radio.tuneOut('myChannel'); | ||
this.channel.command('some:event'); | ||
}); | ||
it('should not log that activity', function() { | ||
expect(console.log).to.not.have.been.called; | ||
}); | ||
}); | ||
describe('When providing a custom logging function and tuning it', function() { | ||
beforeEach(function() { | ||
stub(Backbone.Radio, 'log'); | ||
this.channel.command('some:event', 'argOne', 'argTwo'); | ||
this.channel.request('some:event', 'argOne', 'argTwo'); | ||
}); | ||
@@ -102,0 +80,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1
0
145982
1677
352