Skeptical
Ptarmigan

Hook.io For Dummies, Part 2 - Getting Started

NOTE! Updated 2011-11-27 to reflect hook.io 0.8.2.

Installation

Before thinking of hook.io, you must have node.js and it’s package manager npm installed. With the release of node.js 0.6.3 it’s shockingly easy. Just go to node.js homepage and look under “Downloads”. You’ll know what to do.

Should you prefer to have the latest and greatest, get the source and build it locally. For this, you will need Xcode installed so go get it on the Mac App Store and have a break while it downloads and installs. Then start with node.js, following this [nice instruction][nodeinstall]. You will also need to know how to use the terminal, and some basic unix command. If you don’t, you’re reading the wrong tutorial.

Done? OK, now on to hook.io itself. Let’s install the core/basic/vanilla thing. Open your terminal and:

npm install hook.io -g

The hook.io framework will now be installed. Start the vanilla hook and look at the result:

hookio
Name: the-hook  hook::listening   Type: hook   Data: 5000 ... 
Name: the-hook  hook::started     Type: hook   Data: 5000 ... 
Name: the-hook  hook::ready       Type: hook   Data: {"name":"the-hook", ... 

Open a new terminal window and start a new hook. Notice the difference in what they print out. The first you started became the server and is listening. The second one connected to the already running hook. And it should be rather obvious that this vanilla hook already knows a few tricks, like logging to STDOUT.

Another nice thing is that you can interact with the vanilla hook, the Read-Eval-Print-Loop (REPL) is available in hookio’s core functionality. REPL provides a way to interactively run JavaScript and see the results. It can be used for debugging, testing, or just trying things out. So start yet a new terminal window and start a hook with

hookio --repl

and you can now interact with your little hook system. For example, just type hook and you will see a JSON object describing the current configuration of the hook. Or type hook.emit('foo::bar'); and you have just sent your first event! The server hook will log it and redistribute it to all other client hooks. And they will just ignore it since they are not told to act on such an event.

If you want to know more about the REPL, read more here.

There are a number of ready-made hooks if you want to study something a little more complicated than the vanilla hook. You could try the hello-world hook for example:

npm install hook.io-helloworld -g

This is a very simple hook that emits a helloworld event every second. Try it out by starting it with command hookio-helloworld If all is fine you should get output like this:

prompt: hookio-helloworld
helloworld    hook::listening   5000
helloworld    hook::ready       5000
helloworld    hello             Hello, I am helloworld
helloworld    hello             Hello, I am helloworld
...

By the way, notice the little difference between the package you install, hook.io-helloworld and the executable you run with hookio-helloworld. The latter has no ´.´ in it. This is the naming scheme and you shoud stick with when building your own hooks.

If you start up more helloworld hooks, they will all listen for each others helloworld events and print them in a greeting bonanza, but I find this a rather messy example so let’s move forward with our own coding.

Time To Code!

Let’s start the hook coding with the vanilla hook. It’s very simple and will in this example act as the hub for event handling. If you are familiar with “Integration Patterns”, hook.io is basically using the hub-and-spoke pattern, and our first hook will be the hub, as long as it’s started first. For now I will keep this very simple and ignore praxis on how to structure the code for a more elaborate hook. More on that in the next part.

Create a file named hook.js (for example), looking like this:

#!/usr/bin/env node

var Hook = require('hook.io').Hook;

var hook = new Hook( {
    name: 'vanilla-hook',
    debug: true
});

hook.start();

In the same directory as where you put this file, install hook.io with command npm install hook.io. This will create a sub-folder named node_modules with all required things in it. Sure, it install hook.io again, but this time locally in the folder where you have your first home-cooked hook. This is a praxis with node-modules and not a bad thing so don’t moan, just do it.

Now start your first hook with command node hook.js. If all is well it will print out

Name: vanilla-hook    hook::listening    Type: hook    Data: 5000
Name: vanilla-hook    hook::started      Type: hook    Data: 5000
Name: vanilla-hook    hook::ready        Type: hook    Data: null

Your first hook is up an running! It doesn’t do anything more than the vanilla hook in core, but you have the code base for your first hook established. It’s name is vanilla-hook and it will debug-print on standard out since you told it so in the configuration parameters with which you instantiated the hook. You could also have started it with command node hook, the .js part isn’t needed as node will figure that out itself. Or simply with hook.js, if you first made the file executable with chmod +x hook.js. The shell will see the first line in the file and understand to run it in node.

A Look Into Events

Time to talk about events. An event is a basically a message sent over TCP. All events have a name (a string) and optionally some payload like another string or an object. Event names can be simple like notify, or composed like boxcar::notify. The :: is the separator between the parts in the name. You can, in theory, compose an event name as much as you like, like a::b::c::d::e and so on, but don’t over-engineer it. Less is more.

The current release of hook.io prepends the event name with the emitting hook names and that can be a bit confusing. So if hook MyHook emits event x::y, the actual event name will be MyHook::x::y. To match and catch this event in another hook it should listen for x::y. The emitting hook name part is ignored when matching for listeners.

The composition of event names makes it easy to use wildcards and listen for a whole group of events. Having the events noise::ding, noise::honk, sound::ding, and sound::dong, you can listen for both noise events with *::noise::*, And with *::*::ding you listen both for noise::ding and sound::ding. The wildcard can also be used when emitting events, like *::noise.

The examples below should help you figure out how it work. Assume the emitting hook is named MyHook.

Event As Emitted Actual Event Name How To Catch It
ding MyHook::ding *::ding
noise::ding MyHook::noise::ding *::noise::ding
*::noise::*
*::*::ding
*::ding MyHook::*::ding *::*::ding
*::bar::ding
*::foo::ding

Nota Bene! This is only valid when sending from a “non-root” hook! If you emit the events from a “repl-enabled” root hook, the emitted events will NOT be prepended by the emitting hook name. A bit confusing but that’s the way it is - for now.

Last word on events for this time: if you think it’s a great idea to emit an event ending with wildcard, like foo::*, please re-think. It will crash current version of hook.io badly.

Act on events

So now you know how to format and send events. To receive and act on events you tell your hook to listen to a certain event, and give it callback function to call when that event is received.

self.on("foo::bar", function (data) {
    // Do something
});

This piece of code registers a listener for the “foo::bar” event. The call back function will be called when this event is received, and if there is a payload associated with the event, it will be available in parameter “data”.

Just to get a feel for it, make the vanilla hook a little more beefy. In the code below, two listeners are added. First call with hook.on(…) registers a listener for the hook::ready event. This is sent to every hook when it’s launched and properly connected to the “hook cloud”. The second call, hook.onAny(…) registers a listener for any event. In the callback function supplied, we just printout the current event name, which is always available in the hook’s event property, followed by the payload if there is one.

#!/usr/bin/env node	

var Hook = require('hook.io').Hook;
var hook = new Hook( {
	  name: 'vanilla-hook',
	  debug: false
	});

// Register listener for hook::ready event	
hook.on('hook::ready', function (data) {
	console.log('hook started');
});
	
// register listener for any events
hook.onAny(function(data) {
  console.log(hook.event + ' Data: ' + JSON.stringify(data));
});

hook.start();

So the vanilla hook is now a simple logging hook! This is of course a stupid logger since it adds nothing more than what you get when the hook is in debug mode, but at least you now know how to listen for events.

Conclusion

Stunning, huh. I learned a lot from writing this. In next episode I’ll walk through my first ever hook that actually does something. Let’s go!

blog comments powered by Disqus