Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added new text to the README

  • Loading branch information...
commit 56218d335401d25745328d0110fb06f706a48d90 1 parent 8295849
John Newman authored
Showing with 196 additions and 139 deletions.
  1. +185 −135 v2/README.md
  2. +1 −1  v2/shotgun2-min.js
  3. +10 −3 v2/shotgun2.js
View
320 v2/README.md
@@ -2,249 +2,299 @@
> Smarter than your average pubsub library.
-Shotgun.js is a custom event manager for JavaScript. Built on the pubsub concept, you can tell functions to listen
-for events that you define and manually fire those events at will. Unlike using jQuery events, you can easily
-unsubscribe any function from any event, even if your function isn't named. Shotgun.js also publishes trappable,
-internal events as you use it.
-
-Shotgun passes JSLint (except for one optimization meant for Google Closure Compiler ADVANCED_MODE), can be installed anywhere, and is also cross-browser compatible. By default, it attaches a
-namespace called SHOTGUN to the global object. You can also use SG as a shorcut for this. Here's how it works:
+Shotgun.js is a custom event manager for JavaScript. Built on the observer pattern,
+you can tell functions to listen for events that you define and manually fire those events at will. As
+an added benefit, you can easily unsubscribe any function from any event, even if your function isn't
+named. Shotgun.js also publishes trappable, internal events as you use it.
+
+Shotgun passes JSLint (except for one optimization meant for Google Closure Compiler ADVANCED_MODE),
+can be installed anywhere, and is also cross-browser compatible. By default, it attaches a
+namespace called SHOTGUN to the global object. You can also use SG as a shorcut for this.
+
+> As of v2.0, Shotgun has a completely revamped events syntax built to work and feel like a directory
+> system. You can now group events together under event directories and sub-directories. As such,
+> you will no longer pass in arrays of keys when you want to publish a specific group of events since
+> you can simply store those events under sub-directories.
+
+Here's how it works:
## Creating Subscriptions
-When you create an event subscription with Shotgun, you are, in essence, telling a specific function to listen for
-the firing of some event and then invoke itself when it hears that event fired. The event will publish an array
-of arguments that the function will take into itself upon invocation. Let's look at an example:
+When you create an event subscription with Shotgun, you are, in essence, telling a specific
+function to listen for the firing of some event and then invoke itself when it hears that event
+fired. The event will publish an array of arguments that the function will take into itself
+upon invocation. Let's look at an example:
+
+```javascript
+
+var eventKey = SHOTGUN.listen('myEvent', 'myKeyName', function (x, y) {
+ return x + y;
+});
+
+// returns => 'myKeyName'
+
+```
+
+In the above example we passed 3 arguments to **SHOTGUN.listen**. The first argument contains
+the name of the event your function will subscribe to. The last argument contains the function
+that should be invoked when the event is fired. But the real secret to Shotgun.js is the
+middle argument: the subscription key.
+
+With a subscription key, you can uniquely name each of your individual subscriptions. If you don't
+pass a key name to **SHOTGUN.listen**, a random 24 character key will be generated for you. Doing
+it that way would look like this:
```javascript
-var eventKey = SHOTGUN.listen({
- "event" : "myCustomEvent",
- "key" : "someKeyName",
- "action" : function (x, y) {
- return x + y;
- }
+var eventKey = SHOTGUN.listen('myEvent', function (x, y) {
+ return x + y;
});
-// returns => 'someKeyName'
+// returns something like => 'eejHGuiDswqttrLPPOlikrgu'
```
+
+**SHOTGUN.listen** returns your subscription key so that you will have it available for use in
+the future. All random keys are guaranteed to be unique.
-In the above example we passed an object with 3 properties to **SHOTGUN.listen**. The **event** property contains
-the name of the event your function will subscribe to. The **action** property contains the function that should be
-invoked when the event is fired. But the real secret to Shotgun.js is the **key** property.
+You can subscribe as many functions to an event name as you want but you can not subscribe more
+than one function using the same key. If you try to give two subscriptions the same key, the
+first subscription will disappear, being overridden by the the second. Uniquely naming your
+keys will allow you to delete any subscription, whether the function is named or not.
-With the **key** property you can uniquely name each of your individual subscriptions. If you do not pass a **key**
-property to **SHOTGUN.listen**, a random 24 character key will be generated for you. **SHOTGUN.listen** returns your
-subscription key so that you will have it available for use in the future. All random keys are guaranteed to be unique.
+Note: You don't have to do anything special to create an event for the first time. When you make
+a subscription, Shotgun.js will check to see if that event already exists. If not, it will be
+created for you.
-You can subscribe as many functions to an event name as you want but you can not subscribe more than one function using
-the same key. If you try to give two subscriptions the same **key**, the first subscription will disappear, being
-overridden by the the second. Uniquely naming your keys will allow you to delete any subscription, whether the
-**action** function is named or not.
+Shotgun also gives you the ability to nest your events into directories. For example, you might
+do something like this:
-Note: You don't have to do anything special to create an event for the first time. When you make a subscription,
-Shotgun.js will check to see if that event already exists. If not, it will be created for you.
+```javascript
+
+SHOTGUN.listen('myEvents/subEvent1', function (x, y) {
+ return x + y;
+});
+
+SHOTGUN.listen('myEvents/subEvent2', function (x, y) {
+ return x - y;
+});
+
+```
+
+In the above example, we have created two sub-directories under "myEvents". This technique will
+allow you to organize your subscriptions into invocable groups.
Now let's fire an event.
## Firing (Publishing) Events
-In order to demonstrate how cool Shotgun.js is, we will first need to make two more subscriptions to the same event.
+In order to demonstrate how firing events works, we will first need to make a few subscriptions:
```javascript
-var eventKey2 = SHOTGUN.listen({
- "event" : "myCustomEvent",
- "key" : "anotherKeyName",
- "action" : function (x, y) {
- return x - y;
- }
+SHOTGUN.listen('myEvent', 'keyName', function (x, y) {
+ return x + y;
});
+// returns => 'keyName'
-// returns => 'anotherKeyName'
-
-var eventKey3 = SHOTGUN.listen({
- "event" : "myCustomEvent",
- "action" : function (x, y) {
- return x * y;
- }
+SHOTGUN.listen('myEvent', 'keyName2', function (x, y) {
+ return x - y;
});
+// returns => 'keyName2'
+SHOTGUN.listen('myEvent/subEvent1', function (x, y) {
+ return x * y;
+});
// returns something like => 'rJkB3oeekYLdQDvgG4LaRB9Y'
+SHOTGUN.listen('myEvent/subEvent2', function (x, y) {
+ return x / y;
+});
+// returns another random key
+
```
-We now have 3 functions subscribed to **myCustomEvent**. If we simply fire **myCustomEvent** with an array of
-arguments, all 3 functions will be invoked and take those arguments into themselves. Like so:
+We now have 2 function subscribed directly to **myEvent**. This event also has two sub-events
+underneath it, each with its own function subscription. When we fire an event, we publish an
+array of arguments to an event directory. Each function subscribed to that event directory will
+then be invoked, taking the arguments into itself. In the simplest case, we might do this:
```javascript
-SHOTGUN.fire({
- "event" : "myCustomEvent",
- "args" : [4, 2]
-});
+SHOTGUN.fire('myEvent', [2, 2]);
-// Each function subscribed to 'myCustomEvent' runs.
-// They return 6, 2, and 8, respectively.
+// Two functions run...
+// The first returns 4.
+// The second returns 0.
```
-In the above example we fired an event with an array of arguments thus invoking every function subscribed to the event.
-However, we have subscription keys we can use! So let's be more specific. We'll invoke only 2 of our three functions
-subscripted to **myCustomEvent**.
+In the above example, only the functions directly subscribed to **myEvent** run. And if we only
+wanted to run one of them, we could pass in the subscription key as well:
```javascript
-SHOTGUN.fire({
- "event" : "myCustomEvent",
- "key" : [eventKey, eventKey2],
- "args" : [4, 2]
-});
+SHOTGUN.fire('myEvent', 'keyName', [2, 2]);
+
+// One function runs. It returns 4.
+
+```
+
+As you can see above, the subscription key uniquely identifies a single function to which you
+would like to publish your arguments.
+
+Of course, firing a sub event is as easy as firing a parent event:
+
+```javascript
+
+SHOTGUN.fire('myEvent/subEvent1', [2, 4]);
+
+// One function runs. It returns 8.
+
+```
+
+That said, you may also want to fire an umbrella event and have it invoke not only the functions
+directly subscribed to that event, but also all functions subscribed to sub-events underneath it.
+In that case you will do this:
+
+```javascript
+
+SHOTGUN.fire('myEvent/*', [2, 4]);
-// Only 2 of the three functions are invoked. They return 6 and 2 respectively.
+// Four functions run.
+// The first returns 6.
+// The second returns -2.
+// The third returns 8.
+// The fourth returns .5.
```
-By passing **a single key or array of keys** to **SHOTGUN.fire**, you can specify which specific subscriptions you
-want to receive your event publication.
+Using an asterisk tells Shotgun "I want you to invoke EVERYTHING underneath this event." You'll
+want to be congniscent of what you're doing when you use this technique however, because it does
+work recursively. Of course, you won't risk blowing out the call stack unless you have created
+hundreds of levels of nested sub-events. But you should know to avoid creating hundreds of levels
+of nested sub-events and then using an asterisk at the top level.
+
## Unsubscribing
-Once you make a subscription, it exists until you either remove it manually or override it with another subscription.
-That's OK because subscription keys give us a way to easily do that. We just call **SHOTGUN.remove**.
+Once you make a subscription, it exists until you either remove it manually or override it
+with another subscription. That's OK because subscription keys give us a way to easily do
+that. We just call **SHOTGUN.remove**.
```javascript
-SHOTGUN.remove({
- "event" : "myCustomEvent",
- "key" : eventKey
-});
+SHOTGUN.remove('event/subEvent/subsubEvent', 'uniqueKey');
// A function is unsubscribed from 'myCustomEvent'
```
-In the above example we removed a single subscription from an event. However, we can also remove all subscriptions
-from an event simply by not passing in a **key** property to **SHOTGUN.remove**.
+In the above example we removed a single subscription from an event. However, we can also
+remove all subscriptions from an event simply by not passing in a key to **SHOTGUN.remove**.
```javascript
-SHOTGUN.remove({"event" : "myCustomEvent"});
+SHOTGUN.remove('event/subEvent/subsubEvent');
-// The entire event and all subscriptions to it are removed.
+// The entire "subsubEvent" and all subscriptions to it are removed.
```
## Better Error Handling
-As of v1.5 Shotgun makes it so you never have to write another ugly try/catch block ever again. Take a look at the
-following code:
+Shotgun makes it so you never have to write another ugly try/catch block ever again.
+Take a look at the following code:
```javascript
function parseJSON(obj) {
- SHOTGUN.try({
- "event" : "parseJSON",
- "action" : function () {
- JSON.parse(obj);
- }
+ SHOTGUN.try('errors/parseJSON', function() {
+ JSON.parse(obj);
});
}
-SHOTGUN.listen({
- "event" : "parseJSON".
- "action" : function (err) {
- doSomethingWith(err);
- }
+SHOTGUN.listen('errors/parseJSON', function (err) {
+ doSomethingWith(err);
});
parseJSON('asdfasdfasdfadefasdf');
-// Error caught. Launches doSomethingWith(err);
+// The error is caught by SHOTGUN.listen.
+// Launches doSomethingWith(err);
```
-In the above code we define a function that calls **SHOTGUN.try** rather than setting up a traditional try block.
-**SHOTGUN.try** runs a try under the hood and publishes the error to an events channel for you within the catch block
-if an error occurs. This way, you can catch the error with **SHOTGUN.listen** and completely decouple your error
-handling from your normal work flow. In fact, you can even catch multiple errors with a single **.listen** and,
-if you wanted to, recursively call your try block again using the events channel.
+In the above code we define a function that calls **SHOTGUN.try** rather than setting up a
+traditional try block. **SHOTGUN.try** runs a try under the hood and publishes the error
+to an events channel for you within the catch block if an error occurs.
+
+This way, you can
+catch the error with **SHOTGUN.listen** and completely decouple your error handling from
+your normal work flow. In fact, you can even catch multiple errors with a single **.listen** and,
+if you want to, recursively call your try block again using the events channel.
## Internal Events
-You might be interested to know that Shotgun publishes events for you to trap whenever you make a subscription or
-remove a subscription. There is no trappable event when a publish occurs because that would suck you into an infinite
-loop. Nobody wants that. Anyway, here are your internal events:
+You might be interested to know that Shotgun publishes events for you to trap whenever you
+make a subscription or remove a subscription. There is no trappable event when a publish
+occurs because that would suck you into an infinite loop. Nobody wants that.
+Anyway, here are your internal events:
```javascript
// newListener -> Fired any time a subscription is made
-SHOTGUN.listen({
- "event" : "newListener",
- "action" : function (obj) {
- // obj.event === the event name (in this case 'newListener')
- // obj.key === the subscirption key
- // obj.action === the function that subscribed to the event
- }
+SHOTGUN.listen('newListener', function (ev, key, fn) {
+ // ev === the event name (in this case 'newListener')
+ // key === the subscirption key
+ // fn === the function that subscribed to the event
});
// rmListener -> Fired any time a specific subscription is removed
-SHOTGUN.listen({
- "event" : "rmListener",
- "action" : function (e, k) {
- // e === the event name that the function was subscribed to
- // k === the subscirption key
- }
+SHOTGUN.listen('rmListener', function (ev, key) {
+ // ev === the event name (in this case 'rmListener')
+ // key === the subscirption key
});
// rmEvent -> Fired any time you delete an entire event and all its subscriptions
-SHOTGUN.listen({
- "event" : "rmEvent",
- "action" : function (e) {
- // e === the event name that was removed
- }
+SHOTGUN.listen('rmEvent', function (ev) {
+ // ev === the event name (in this case 'rmEvent')
});
// tryError -> Fired any time SHOTGUN.try publishes an error
-SHOTGUN.listen({
- "event" : "tryError",
- "action" : function (err) {
+SHOTGUN.listen('tryError', function (err) {
// err === the error object
- }
});
```
## Keeping Track
-Lastly, you can always get a good look at the current state of Shotgun.js subscriptions by running
-**SHOTGUN.events**.
+Lastly, you can always get a look at the current state of Shotgun.js subscriptions by calling
+**SHOTGUN.getEvents**.
```javascript
-SHOTGUN.events()
+SHOTGUN.getEvents()
/*
returns an object like this =>
{
- "eventName" : {
- "subscriptionKey" : {
- "action" : function () {}
- },
- "subscriptionKey2" : {
- "action" : function () {}
- }
- },
- "eventName2" : {
- "subscriptionKey" : {
- "action" : function () {}
- },
- "subscriptionKey2" : {
- "action" : function () {}
- }
- }
+ "_::name" : 'myEvent',
+ "_:subEvent" : [object],
+ "rJkB3oeekYLdQDvgG4LaRB9Y" : [function],
+ "eejHGuiDswqttrLPPOlikrgu" : [function]
}
*/
-```
+```
+
+Notice in the example object returned that each directory and sub-directory is really just
+an object. Each one has **_::name** property containing the name you've given it. All event
+sub-directories also begin with **_:**. For this reason among others, you should only use
+letters and numbers in your event names and subscription keys.
+
+Since **getEvents** gives you such an honest look at the state of your subscriptions and their
+names, you will need to keep that in mind when drilling down to look into sub-levels. This
+tool is really meant for debugging as you will probably not need to really use it for any
+reason in your code.
View
2  v2/shotgun2-min.js
@@ -3,4 +3,4 @@ Name: shotgun.js
License: MIT
Version: 2.0
*/
-(function(gpa){"use strict";var va,vb={},vc='2.0',vd;function EvDir(pa){this['_::name']=pa;}va=new EvDir('eventsObj');function fa(pa,pb){var i,l=pa.length;for(i=0;i<l;i+=1){pb(pa[i],i);}}function fb(pa,pb){var i;for(i in pa){if(Object.prototype.hasOwnProperty.call(pa,i)){pb(pa[i],i);}}}function fc(pa){return pa.slice(1);}function fd(pa){return pa.slice(0,pa.length-1);}function fe(pa){return pa[pa.length-1];}function ff(pa) {if(pa[0]==='/'){pa=fc(pa);}if(fe(pa)==='/'){pa=fd(pa);}return pa;}function fg(){var i,ve='',vf='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghiklmnopqrstuvwxyz';for(i=0;i<24;i+=1){ve+=vf[Math.floor(Math.random()*vf.length)];}if(!vb[ve]||ve.slice(0,2)!=='_:'){vb[ve]=true;return ve;}else{return fg();}}vd={"fire":function(pa,pb,pc){var ve=[],vf=this,vg,vh,vi,vj;pa=ff(pa);vh=pa.split('/');if(!pc&&Object.prototype.toString.call(pb)==='[object Array]'){pc=pb;}else{vg=pb;}if(fe(vh)==='*'){vg=null;vj=true;vh.length=vh.length-1;}if(vh.length>1){fa(vh,function(e,i){var vk=fe(ve)||va,vl='_:'+e;ve.push(vk[vl]);});vi=fe(ve);}else{vi=va['_:'+vh[0]];}if(vi){if(vg){vi[vg]&&vi[vg].apply(null,pc);}else{if(vj){fb(vi,function(v,k){if(k.slice(0,2)==='_:'){(k!=='_::name')&&vf.fire(vh.join('/')+'/'+k.slice(2)+'/*',pc);}else{v.apply(null,pc);}});}else{fb(vi,function(v,k){if(k.slice(0, 2)!=='_:'){v.apply(null,pc);}});}}}},"listen":function(pa,pb,pc){var ve,vf,vg=[],vh,vi;pa=ff(pa);vf=pa.split('/');if(fe(vf)==='*'){vf.length=vf.length-1;}if(!pc&&typeof pb==='function'){pc=pb;ve=fg();}else{ve=pb;}if(vf.length>1){fa(vf,function(e,i){var vj=fe(vg)||va,vk='_:'+e;vj[vk]=vj[vk]||new EvDir(e);vg.push(vj[vk]);});vh=fe(vg);vh[ve]=pc;}else{vi='_:'+vf[0];va[vi]=va[vi]||new EvDir(vf[0]);va[vi][ve]=pc;}this.fire('newListener',[pa,ve,pc]);return ve;},"remove":function(pa,pb){var ve,vf=[],vg,vh;pa=ff(pa);ve=pa.split('/');if(fe(ve)==='*'){ve.length=ve.length-1;}if(ve.length>1){fa(ve,function(e,i){var vi=fe(vf)||va,vj='_:'+e;vf.push(vi[vj]);});vg=fe(vf);vh=fe(fd(vf));}else{vg=va[ve[0]];vh=va;}if(pb&&vg[pb]){delete vg[pb];delete vb[pb];this.fire('rmListener',[pa, pb]);}else if(!pb){fb(vg,function(v,k){delete vb[k];});delete vh['_:'+((vg)?vg['_::name']:ve[0])];this.fire('rmEvent',[pa]);}},"try":function(pa,pb,pc){var ve;if(!pc&&typeof pb==='function'){pc=pb;ve=fg();}else{ve=pb;}try{pc();}catch(e){this.fire(pa,pb,[e]);if(pa!=='tryError'){this.fire('tryError',pb,[e]);}}},"getEvents":function(){return va;},"version":vc};if(gpa.define&&typeof gpa.define==='function'&&gpa.define.amd){gpa.define('SHOTGUN',[],vd);gpa.define('SG',[],vd);}else if(gpa.module&&gpa.module.exports){gpa.module.exports=vd;}else{gpa['SHOTGUN']=gpa['SG']=vd;}}(this));
+(function(gpa){"use strict";var va,vb={},vc='2.0',vd;function EvDir(pa){this['_::name']=pa;}va=new EvDir('eventsObj');function fa(pa,pb){var i,l=pa.length;for(i=0;i<l;i+=1){pb(pa[i],i);}}function fb(pa,pb){var i;for(i in pa){if(Object.prototype.hasOwnProperty.call(pa,i)){pb(pa[i],i);}}}function fc(pa){return pa.slice(1);}function fd(pa){return pa.slice(0,pa.length-1);}function fe(pa){return pa[pa.length-1];}function ff(pa) {if(pa[0]==='/'){pa=fc(pa);}if(fe(pa)==='/'){pa=fd(pa);}return pa;}function fg(){var i,ve='',vf='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghiklmnopqrstuvwxyz';for(i=0;i<24;i+=1){ve+=vf[Math.floor(Math.random()*vf.length)];}if(!vb[ve]||ve.slice(0,2)!=='_:'){vb[ve]=true;return ve;}else{return fg();}}vd={"fire":function(pa,pb,pc){var ve=[],vf=this,vg,vh,vi,vj;pa=ff(pa);vh=pa.split('/');if(!pc&&Object.prototype.toString.call(pb)==='[object Array]'){pc=pb;}else{vg=pb;}if(fe(vh)==='*'){vg=null;vj=true;vh.length=vh.length-1;}if(vh.length>1){fa(vh,function(e,i){var vk=fe(ve)||va,vl='_:'+e;ve.push(vk[vl]);});vi=fe(ve);}else{vi=va['_:'+vh[0]];}if(vi){if(vg){vi[vg]&&vi[vg].apply(null,pc);}else{if(vj){fb(vi,function(v,k){if(k.slice(0,2)==='_:'){(k!=='_::name')&&vf.fire(vh.join('/')+'/'+k.slice(2)+'/*',pc);}else{v.apply(null,pc);}});}else{fb(vi,function(v,k){if(k.slice(0, 2)!=='_:'){v.apply(null,pc);}});}}}},"listen":function(pa,pb,pc){var ve,vf,vg=[],vh,vi;pa=ff(pa);vf=pa.split('/');if(fe(vf)==='*'){vf.length=vf.length-1;}if(!pc&&typeof pb==='function'){pc=pb;ve=fg();}else{ve=pb;}if(vf.length>1){fa(vf,function(e,i){var vj=fe(vg)||va,vk='_:'+e;vj[vk]=vj[vk]||new EvDir(e);vg.push(vj[vk]);});vh=fe(vg);vh[ve]=pc;}else{vi='_:'+vf[0];va[vi]=va[vi]||new EvDir(vf[0]);va[vi][ve]=pc;}this.fire('newListener',[pa,ve,pc]);return ve;},"remove":function(pa,pb){var ve,vf=[],vg,vh;pa=ff(pa);ve=pa.split('/');if(fe(ve)==='*'){ve.length=ve.length-1;}if(ve.length>1){fa(ve,function(e,i){var vi=fe(vf)||va,vj='_:'+e;vf.push(vi[vj]);});vg=fe(vf);vh=fe(fd(vf));}else{vg=va[ve[0]];vh=va;}if(pb&&vg[pb]){delete vg[pb];delete vb[pb];this.fire('rmListener',[pa, pb]);}else if(!pb){fb(vg,function(v,k){delete vb[k];});delete vh['_:'+((vg)?vg['_::name']:ve[0])];this.fire('rmEvent',[pa]);}},"try":function(pa,pb,pc){var ve;if(!pc&&typeof pb==='function'){pc=pb;}else{ve=pb;}try{pc();}catch(e){(ve)?this.fire(pa,ve,[e]):this.fire(pa,[e]);if(pa!=='tryError'){(ve)?this.fire('tryError',ve,[e]):this.fire('tryError',[e]);}}},"getEvents":function(){return va;},"version":vc};if(gpa.define&&typeof gpa.define==='function'&&gpa.define.amd){gpa.define('SHOTGUN',[],vd);gpa.define('SG',[],vd);}else if(gpa.module&&gpa.module.exports){gpa.module.exports=vd;}else{gpa['SHOTGUN']=gpa['SG']=vd;}}(this));
View
13 v2/shotgun2.js
@@ -225,16 +225,23 @@ Internal events you can trap:
var keyfix;
if (!fn && typeof key === 'function') {
fn = key;
- keyfix = genUnique();
} else {
keyfix = key;
}
try {
fn();
} catch (err) {
- this.fire(ev, key, [err]);
+ if (keyfix) {
+ this.fire(ev, keyfix, [err]);
+ } else {
+ this.fire(ev, [err]);
+ }
if (ev !== 'tryError') {
- this.fire('tryError', key, [err]);
+ if (keyfix) {
+ this.fire('tryError', keyfix, [err]);
+ } else {
+ this.fire('tryError', [err]);
+ }
}
}
},
Please sign in to comment.
Something went wrong with that request. Please try again.