I am very excited about Ember.js, an awesome JavaScript library with some neat features like Bindings, Computed Properties, Templates, … you should go check it out on GitHub.
If you need some help you can simple ask your question at the #emberjs IRC channel. There is just one problem: you cannot read through older conversations because this channel is not logged - yet. So I decided to hack something together to end this.
The result - which is hosted here http://emberjs.iriscouch.com/irc/_design/viewer/index.html - is a Hubot hanging around the IRC channel, storing the messages in a CouchDB. Additionally there is a viewer written in - you guessed it - ember.js which let you browse through the transcripts. This blog post covers the basics how I implemented all this.
CouchDB - Time to relax
CouchDB is an open source, document oriented database with a HTTP REST interface. It lets you store JSON documents and do powerful map/reduce queries on the data. I don’t own a server where I can install CouchDB so I’ve created a free account with the name emberjs
at IrisCouch.
Next I created a database irc
with a simple
1
|
|
The IRC logs should be read only so I setup an administrator account and secured the database as described in this excellent blog post by Liz Conlan. So now there is a secured CouchDB instance hosted at emberjs.iriscouch.com with a database named irc
which can be read by everybody and to store data you have to be an administrator.
Hubot meets Heroku
Hubot is a bot written in Node.js by sexy GitHub. It is originally written for Campfire but an adapter for IRC exists. Heroku is a hosting platform with a git interface and hence very easy to use. Compared to Google App Engine installing of Heroku is a charm by doing a gem install heroku
.
I downloaded the version of Hubot which is designed for Heroku. Inside the scripts
folder are some scripts which are available to Hubot. More scripts can be made available by writing one or by downloading them from the hubot-scripts repository. Luckily there is already a script originally written by Patrik Votoček which stores all messages into a CouchDB. Because the Hubot should not make too much noise inside the IRC channel all pre installed scripts are removed and only the essential ones are left inside the scripts
folder: store-messages-couchdb.coffee
and pugme.coffee
.
The downloaded Hubot instance does not come with the ability to connect to IRC. This is easy fixed via adding the IRC adapter hubot-irc
. The final dependencies
section of package.json
looks like:
1 2 3 4 5 6 7 |
|
Before Hubot is deployed to Heroku I’ve changed the content of Procfile
so Heroku knows to start Hubot with IRC adapter:
1
|
|
The next step is the actual deployment. First a new heroku application on the cedar stack is created and afterwards a simple git push
deploys it on Heroku. It’s that easy.
1 2 |
|
Afterwards Hubot needs to be configured so he knows where to hang out and where to save the messages. This is simple:
1 2 3 4 |
|
So now there is a Hubot instance running on Heroku and it’s configured to hang out in the #emberjs
channel and save all messages into the previously created CouchDB irc
database. Perfect. The next step is to create a front end, which is captured in the following part of this post.
Viewer
Because CouchDB is awesome and has an HTTP interface, there are Couchapps which are basically HTML applications hosted inside a CouchDB database. There is a convenient tool available which simplifies the process of writing a Couchapp which suprisingly is named couchapp
.
To start I created a new app via couchapp generate app viewer
command. This generates a folder viewer
with the following structure:
1 2 3 4 5 6 7 |
|
The hosted application itself will be located inside _attachments
folder. I removed all other folders except views
because those Couchapp related functionalities are not yet needed. A view is used to make data inside the CouchDB available and query-able. The basic structure of an IRC message document inside the irc
database looks like:
1 2 3 4 5 6 7 8 9 10 11 |
|
To get a sorted list of messages for a specific period of time, I created a CouchDB view via executing couchapp generate view messages
. This creates two files named map.js
and reduce.js
inside views/messages
. The messages
view implements functionality to list all messages sorted by date:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
1 2 3 |
|
Deploying the couchapp into the irc
database via a couchapp push
makes the view available and it is now accessible at http://emberjs.iriscouch.com/irc/design/viewer/view/messages?startkey=[2012,1,1]&endkey=[2012,1,7]&reduce=false&include_docs=true
The query on the view returns the results with following structure:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
So now there is a view named messages
which returns the messages sorted by date. The view can be queried to get all messages for a specific period and in combination with the CouchDB reduce
functionality the count of messages for given period. Finally everything is set up for developing the basic HTML application for the IRC log viewer.
BPM
I use BPM for building the application and handling dependencies. BPM stands for Browser Package Manager and is the browser equivalent to NPM, the package manager for Node.js. It seems that it’s no more developed actively, but the current version is stable.
To create a BPM application I executed bpm init irc
inside viewer
and afterwards I renamed the generated folder irc
to _attachaments
(this is where Couchapp looks for static files). bpm init
creates the following structure:
1 2 3 4 5 6 |
|
Next step is to add the needed dependencies via bpm add ember
and bpm add spade
inside the _attachments
folder. I modified the index.html
so spade is used to load the application. Spade is a JavaScript Module Loader similar to RequireJS. To interact with CouchDB I’ve included the jquery.couch.js
file which is hosted inside a Couchapp’s _utils/script/
folder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Test setup
Before I’ve added any functionality to the application I created a basic test setup using qunit
by adding it as a dependency via bpm add qunit --development
. All tests will be located inside _attachments/tests
. To create a separate file irc/bpm_tests.js
just containing the files inside the tests folder, I modified irc.json
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Afterwards I created an empty file tests/tests.js
which will be the entry for the tests, as well as a tests.html
which follows the basic layout as documented in Using Qunit:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Running the tests is done via executing bpm preview
and navigating to http://localhost:4020/tests.html
Create IRC namespace
The core of the IRC viewer application is located in the lib/core.js
file. It should import ember
and define the IRC
application namespace. So I first created a test:
1 2 3 4 5 6 |
|
and modified tests/tests.js
to include the new application test:
1 2 3 4 |
|
Accessing localhost:4020/tests.html throws an error in the Web Inspector: Uncaught Error: Module irc/core not found
. So I’ve created a file lib/core.js
and rerun the tests. Now the module is found but the tests fail. Seems legit since I haven’t created the IRC namespace yet. This is changed by modifying lib/core.js
:
1 2 3 4 |
|
Because the viewer has to deal with dates I use the sproutcore-datetime library. This library is available as bpm dependency, but the version hosted on GetBPM.org is not compatible with the used ember version 0.9.5
. So I downloaded datetime.js into the lib
folder and required it in lib/core.js
via require('./datetime');
:
1 2 3 4 5 6 |
|
I then created two utility methods inside the IRC
application and added corresponding tests:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
Implement controllers
The IRC viewer basically has two controllers: one for the messages and one for all available days. Both controllers are located in lib/controller.js
and the corresponding tests are defined in tests/controller_test.js
. The controllers are basically Ember.ArrayProxy
’s with specific methods addMessage
and addDay
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
Create the templates
The views are described via Handlebars templates which are located inside lib/templates
folder. To format dates and parse possible URL’s inside a IRC message, I wrote two Handlebars helpers parse
and format
which are located in lib/templates.js
. parse
parses a String for URL’s and wraps them in <a>
tags. format
formats a date with a given format (enough use of ‘format’ for now).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
The templates itself are simple text files with the extension .handlebars
located in lib/templates/
:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
The registered action
in the days.handlebars
template invokes the IRC#loadDate
method. It gets the date
of the clicked day
and tells the dataSource
to load the specific date. The dataSource
itself is defined in lib/main.js
, which is discussed in the next section:
1 2 3 4 5 6 7 8 9 10 11 |
|
To load the templates a require
is needed. I extended String
prototype to add a tmpl
function which handles this:
1 2 3 4 5 6 7 8 |
|
So now a template can be loaded simply via 'TEMPLATE_NAME'.tmpl()
.
I also downloaded Twitter’s Bootstrap to _attachments/css/bootstrap.css
to add a basic style. Additionally I modified the index.html
’s <body>
so it looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
DataSource
As mentioned when the templates has been discussed, the IRC
application has a dataSource
. This DataSource is capable of loading messages and days and setting the data on the controllers. The DataSource is defined in lib/couchdb_datasource.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
|
Assembly
Everything is defined now in its own place and its time to assembly everything together. The lib/main.js
- which is loaded in the index.html
- is therefore defined as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
To deploy the app I first executed a bpm rebuild
inside the _attachments
folder so the assets are freshly generated and afterwards pushed the Couchapp via couchapp push http://USERNAME:[email protected]/irc
to the final location and so it is available at http://emberjs.iriscouch.com/irc/_design/viewer/index.html.
Summary
So now there is an IRC viewer build with BPM and using spade. The application itself is written in Ember.js and deployed as a Couchapp to a CouchDB instance which itself hosts all the IRC messages. A Hubot instance hanging around the IRC channel stores the messages into this very CouchDB.
The code for the viewer is hosted at pangratz/irc-log-viewer. Feel free to contribute! Just open an Issue or a Pull Request. I’m planning to blog about how this can be used to create an IRC logger for any IRC channel.
Outlook
During my development I wrote down some notes and future ideas:
- When Hubot is down, no messages are logged: this is a problem. A possible solution would be to deploy a second Hubot which listens on the event when the primary Hubot leaves the room and takes over. The same problem is on the side of CouchDB: when the CouchDB is down, no messages can be saved. A backup CouchDB - maybe on a different hosting platform - would be a good idea I guess.
- Export the transcript as Colloquy logs (using the CouchDB’s
show
/list
functionality). - Searching the IRC logs would be very cool, for example via Apache Solr. I haven’t found any free hosting service. Anyone knows one? There is also the possibility to use couchdb-lucene but as far as I know iriscouch.com does not offer this…
- Add realtime updates of incoming IRC messages by using the
_changes
feed of CouchDB - Show some statistics: when are the most messages? On which weekday? When is a user active the most?
- Parse gist/pastie/… urls and show preview of it