Welcome back to the Tower.js tutorial. Using BDD, a style of test-driven development, we are building an app with Tower.js that will log messages in an irc channel and publish those as Github Gists. By the time we are through, you should be quite comfortable with the basics of working with tower, and ready to take on apps of your own. If you are just now joining us, please start out with part 1 so that you will be ready for these steps. Don’t worry, we’ll wait right here for you to catch up!
In the last part of this tutorial, we generated some scaffolds (pre-made sets of views/controllers/models with default routing built in), and then fixed up the tests that came with those. Despite being a bit of work to get those running green, we now have tests covering much of the basic functionality of our models and controllers. That is a good thing. It means as we work, we can have confidence that changes we make aren’t messing something fundamental up.
Today, we’re going to fill in the functionality of those scaffolds, so that the logs our ircbot makes will get recorded as “messages” and then when there are a few of those (ten actually), we’ll use the messages to create our gists and post them to github. Remember to restart both our cake task and mocha tests with:
Now let’s get started.
As before, testing the ircbot’s response to messages sent in an irc chatroom is a bit more complex than we really want to get into here. Since the point of the tutorial is about making apps with Tower.js, and not about building irc servers, we’re going to gloss over those a little bit. Modify the file app/models/ircBot.coffee to look like this:
This adds the message listener to the init method. If you look at what it’s doing, it is pretty simple. We add a listener to the bot for any messages passed in the channel. When the channel emits one of these events, the bot makes a new “message” object, sets the properties on the object to be the user and their message, and then saves it. We could also have used “build” here, and done the object construction slightly differently. You can remove the logging if you want, but you might want to leave it in until after we’ve determined it to be working, in case anything isn’t working.
While I was working on this bit of code, I briefly got a failing test, which immediately ran again and went back to passing. If this happens to you, it’s nothing to be alarmed about (as long as tests begin passing again), it just means that while putting in the code, we accidentally disrupted the flow of the tests.
Checking the Count
Now, back to writing some tests of our own. Open up the file test/models/messageTest.coffee and add this code down beneath where it says ‘describe “relations”‘:
You’ll immediately have a failing test or two again. If you remember from before, sinon is a library we use to make testing easier. It wraps a method for us, in a spy object, which can then provide our tests with information about how that method gets used. In this case, we are checking that upon saving a new message, it calls the checkCount method, which we’ll now write. In app/models/message.coffee, add the additional code you see here.
First, we tell it that after an instance is created, it needs to call the checkCount method. Then in the checkCount method, we literally check the message count. If its over ten, we call the “gistify” method, which we haven’t written yet.
Tests are passing, let’s make them fail again. Back in messageTest.coffee, beneath our last test, add:
We could have written this test in a number of ways, but this seemed the most straightforward. At least it did, until I had to add the setTimeout which I’ll explain momentarily. What we do here is just set a standard loop up to quickly create 11 messages. Then we use a spy to see if the “gistify” method gets called. The problem was that, due to the asynchronous nature of coffeescript, the spy was getting checked while loop was still running, so I simply set a timeOut on that check, to give the loop a few milliseconds to complete. A more correct way would be to somehow use “process.nextTick,” or some kind of callback, but again, mostly owing to my naivety as a noob, I wasn’t able to get the test working properly using either of those. I definitely welcome your suggestions in the comments, as this feels really hacky and ugly to me. If you get a cleaner version of this test working, please let me know so I can refactor my code, and the tutorial.
Now, let’s get this test passing. Go back to message.coffee and alter it to look like so:
This code finds all the messages, loops through them, gets their different properties and does some string concatenation to create one large string which it then turns into a gist and saves. The messages are destroyed once their information is safely saved into a gist, as we don’t need two copies of each message in our database, and this will make it easier, later, to always display the most recent messages from the chat, while allowing us to keep messages bundled together with others in a gist. If we really wanted to make this powerful, we’d also set up a worker that would call this gistify method at certain times and if gistify had any messages it would go ahead and create the gist, so that messages would be more likely to be stored in the context of the conversations in which they occurred. I’ll leave this as “extra credit” for my readers.
Anyone who comes up with a good way to do this, please leave us a note in the comments.
Once again, we should have pretty, green tests.
TowerBot: A Published Author?
Now, this creates “gists” in our database, but we want to actually publish these gists on Github. To do that, we’ll need to access Github’s Gist API. Fortunately, there are libraries for node which make this easy. We’ll be using my own fork of node-gist by Max Ogden which was in turn forked from Emerson Macedo, whose original version is the one currently stored in npm. Because of this, we can’t get our copy directly from npm’s storage and need to instead get a copy from Github. We could download it, unzip it, and then copy the unzipped library into the right place. Or, more easily, we could change into our node_modules directory, and then use the “git clone” command to copy and unpack it. Fortunately, we don’t have to do either one. Instead, npm makes this easy for us, and by using npm, we can ensure anyone who installs our package after us will be able to download the correct library. So:
npm install git://github.com/edubkendo/node-gist --save
When the install finishes, check your package.json and in your dependency list, you should see:
Now, in the folder app/config/server/initializers create the file gist.coffee with this:
Now there should be two files in this folder. Along with the earlier file we created, “irc.coffee”, this will require the new dependency and make it available throughout your app. By the way, any guesses as to why we called the variable “App.gisted” instead of “App.gist”? It’s because App.gist is what our model is called, so we altered it slightly to avoid a name collision.
We’ll need to restart both cake and our tests now, something which you should have down at this point. I want to note something now that I was just thinking about. On my machine, these tests normally take around two to two-and-a-half seconds to run. Coming from another programming language, that may seem fast, but for mocha tests on node, it’s actually fairly slow. I’m sure things like that time-out hack, as well as waiting for the various server and ircbot connections to be made and checked is part of the problem, but I imagine there is plenty of room for these tests to be optimized and made much faster. As always, I’d love to see your suggestions and constructive criticism in the comments below.
Now, let’s write a test. We’re going to follow the same pattern we’ve been following, and ensure that after an instance of our model is created, that something we want to happen, does. In the file test/models/gistTest.coffee, beneath ‘describe “relations”‘, add the following code:
This should be self-explanatory at this point. Now to get to green we just need to add the method “makeGist” to app/models/gist.coffee, then tell it when to call this function. Alter your file to look like this:
In the makeGist method we are doing several things. We are creating an object, newGist, which conforms to the API that our ‘gist’ library (AKA: App.gisted) expects. Then we call on this library to actually publish the gist, log the json object this returns, extract the url at github that the gist was published to, and save that to our model instance so that later we can find this information in our database. We will use this information later, when we begin creating the client side of our app.
If your tests don’t go green right away, just stop them and re-start them. That should do the trick. Else check that everything is copied exactly.
Alright, that’s plenty for today. Let’s commit our changes and merge them back into master.
First check things out with:
Don’t be surprised if there are quite a few changes and untracked files listed. We did a lot of work. Still, we always run “git status” first, just to get the lay of the land. Next add the new files and commit the changes:
git add .
git commit -am "Adds messages and gist publishing functionality to TowerBot"
Next, checkout the master branch and merge things back in:
git checkout master
git merge messages-and-gists
Finally, if your keeping a repo on github, push up your changes with :
Awesome. We accomplished a lot today.
Please leave your questions, comments and polite suggestions below and I’ll see you in part 6. Very soon now, we’ll get to see exactly what it is that makes tower so cool, with self-updating models on the browser bound to the server side controllers, so that our views will update (without repainting the entire UI) every time a new message is logged, and of course, we’ll have to take advantage of tower’s all knowing, all seeing cursor. See you soon!