Nunt is a light-weight event eco system for javascript. It allow your app to communicate trough custom events.
It can be used on the browser, with node or to communicate between both.
Nunt exists solely to simplify event driven programming.
You basically use it like this.
// listen
nunt.on("foo", function(event){
console.log("hello " + event.name);
});
// send
nunt.send("foo", {name: "bar"});
Everything else nunt offers is extra sugar to make your solutions more flexible than ever.
Nunt offerts some helper functions to initialize a full functional event driven eco system. It even has methods for testing and a chrome extension to inspect everything that is sent and received.
Nunt consists of two base functions and a couple of helper functions. The base functions are the ones that are used most of the time. The helper functions are used in specific cases or when testing your code.
These are two base functions for nunt:
Example for listening:
// listen
nunt.on("foo", function(event){
console.log("hello " + event.name);
});
Example for sending:
// send
nunt.send("foo", {message: "bar"});
The circles get their width from the amount of current tweets with the hashtags #nodejs, #javascript or #unicorn
An event is basically a message with a name. In nunt its usually sent as an object with at least one property called _name which identifies the event.
Whatever you choose to send as data will be passed forward. However, if you plan to use nunt on both client and server, the data has to be serializable.
// send an event called "foo.bar" with an object {id: 1}
nunt.send("foo", {id: 1});
// will just trigger the event without custom data
nunt.send("foo");
// will send an event with a functions as its data
nunt.send("foo", function(){console.log('foo')});
Your solution will typically consist of something more than one small function.
As your solution grows, you will end up with many classes and functions that have to be initialized and synchronized.
To make sure everything is up and running, this is a common approach with nunt:
Lets assume you have one class/function in your solution that is called notify, that handles messages shown to the user:
(function(){
nunt.on("notification.show", show);
function show(e){
alert("Notify: " , e.message);
}
})();
You might have another function somewhere that checks if the user has a cookie that signlalizes that he/she is logged in:
(function(){
if (userHasLoggedIn()){
nunt.send("nofity.show", {message: "Welcome"});
}
function userHasLoggedIn()
{
// cool user check code here
}
})();
The problem that arrises here, is that the user function doenst know if notify.view.js has been loaded and run yet. And if it hasnt been loaded, the "notify.show" listener hasnt been registred yet. And if the event hasnt the listener, no message will be shown.
The solution is simple and work pretty much like the ready event in jQuery. When everything is loaded nunt will send a special event called nunt.READY. The event singalizes that all documents have been loaded and them DOM is ready.
With that in place, we can solve the above situation like this instead:
(function(){
nunt.on(nunt.READY, init);
function init()
{
if (userHasLoggedIn()) {
nunt.send("nofity.show", {message: "Welcome"});
}
}
function userHasLoggedIn()
{
// cool user check code here
}
})();
To help developers write their apps more efficient, nunt has some helper functions.
nunt.addGlobalListeners(callback)
Adds a callback to be called for any event sent to nunt.
This can be used when your application wants to react to all events. Inside the callback you might have condition to react to only one type of events.
Example:
(function(){
nunt.addGlobalListeners(function(e){
if (e._name.indexOf("user.") == 0)
{
//do something
}
}
})();
This example would set a listener for all events and run a callback. The callbacks checks if the event name begins with the string "user." and does something.
nunt.removeListener(event, [callback])
Removes an event listener.
If you create a massive app and you want to make sure unwanted listeners don't take resources, then you might want to unregister them.
This method takes the event name and the callback as parameters. If you omit the callack, it will remove all callbacks connected to the event.
Example:
(function(){
nunt.removeListener("foo.bar", mySpecialCallback);
})();
This will remove the listener for the event "foo.bar" connected to the callback "mySpecialCallback".
nunt.getExposedInstance(classRef)
Used for testing
When you call this function with a class reference as the parameter, you get a new class that exposes the private functions.
nunt.getDispatchedEvents()
Used for testing mostly, get a list of all sent events.
nunt.getListeners(event)
Get a list of all callback for one event
When you include nunt in your application by loading it, nunt will prepare itself by doing adding a callback to check when them DOM is ready. It will then go trough a couple of steps to make sure you can develop as easy as possible.
In other words, it you put this into your html document
<script type="text/javascript" src="/js/lib/nunt.js" charset="utf-8"><script>
Then the start process of nunt follows this pattern:
By following this process, all our functions are initiated and ready when we get the nunt.READY event. This works in a similar way as jQuerys $(document).ready method.
Nunt in nodejs will work exaclty as on the browser. But it's when you use it on both on the browser and on the server that you really are creating magic.
It lets you send an event from the server and listen to it on the client and vice versa.
The benefits are many...
Nunt uses socket.io to build transparent bridge between node and your client. The result is simple, your app communicates with the server without need to develop an extra layer. Just send events.
Lets describe it with an example. Assume you have an app that has a save button:
$(".saveButton").click(function(){
nunt.send("save", {myData: "lorem"}
});
The server will listen to the event "save" in the same way your client could listen to it. So on you node server you might have something like this:
nunt.on("save", function(event){
// save your data in some way
console.log(event.myData);
nunt.send("save.done", {ok: true});
});
It's almost magic because you are using one kind of event system, one way of communicating, one language and one mindset for your whole app. It makes the two worlds grow a little closer together and makes developing easier.
In the example above, when the "save" events is sent, the server will send the "save.done" event so that the client could show some kind of notification.
If you have events that aren't supposed to be sent from the client to the server (or vice versa), you can just add a property called "local" to your event data, and it will not be send to the other side (like this: nunt.send("myLocalEvent", {local: true})).
When your app is running nunt on both server and client trough socket.io and events are sent back and forward, you have to manually make sure an event is either sent to all connected clients or just to the one that sent an initial event.
In the case demonstrated above, you might don't want to send the "save.done" event to all clients, but just to the one that clicked the save button.
In that case, you have to add a property to the data called sessionId. Just do it like this:
nunt.on("save", function(event){
// save your data in some way
console.log(event.myData);
nunt.send("save.done", {ok: true, sessionId: event.sessionId});
});
As you can see, we grab the event.sessionId from the incoming event, and set it to the outgoing event. Simple. If we where to omit the sessionId property, the event will be sent to all connected clients.
nunt has some testing helper methods built in. You can set nunt to test mode by setting a property called nunt.unitTestMode = true;
Just put this code before loading nunt:
<script type="text/javascript" charset="utf-8">var nunt = window.nunt || {}; nunt.unitTestMode = true;</script>
This will start nunt in test mode, meaning that it won't initialize all methods registered with the helper methods. It will then allow you to get instances of your classes and test them separately.
So you could test a class called cart by getting an instance of it like this:
var instance = nunt.views.cart.getInstance();
When you are in test mode and you get an instance like this, you are automatically able to access all private methods defined in the class as well.
So a class that is defined the following way:
nunt.views.cart = function()
{
function hello()
{
…
}
}
And you get the instances as above:
var instance = nunt.views.cart.getInstance();
Will expose its private methods like this:
instance._privates.hello()
...making it even easier to test.
You can also access all triggered events with the methods:
// which gets the last event nunt.getLatestDispatchedEvents() // which gives you a list of all sent events nunt.getDispatchedEvents();And you can test if an event has a specific listener
nunt.hasListenerWithCallback("shop.cart.updated", foo);
To download and install under node:
npm install nunt
To use it in the browser:
<script type="text/javascript" src="/js/lib/nunt.js" charset="utf-8"><script>
Or just get it from github at https://github.com/camme/nunt
(The MIT License)
Copyright (c) 2011 Camilo Tapia <camilo.tapia@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.