Jask: Running Javascript Tasks with v8cgi

2009-06-19

Today I’m releasing Jask, a simple task runner written in Javascript, loosely based on Ruby Rake, and runs on v8cgi. I’ve been working on it for the past few hours and after a few tweaks, I decided that I better set it loose on the wild to get some feedback.

For the impatient, you can get the code at the Jask Github Repo. If you have time in your hands, read on.

The Background

While working on one of my client projects yesterday, I opened Futon (the CouchDB manager utility) to run a compaction. Unfortunately, the buttons for “Compact Database” and “Delete Database” were less than 20px apart, and the dialogs that pop-up when either button is clicked are almost identical. That’s how I learned clicking dialog buttons without checking the messages is very bad, especially if you only had 3 hours of sleep. And surprise, surprise: the local copy of my design docs weren’t in sync with the ones on the database.

Frustrated, I decided that there should be a better way to handle this—and by better, I mean via the terminal. I want to be able to write my design docs as separate files, run a command via the terminal and get them updated. So I decided to write a bunch of scripts to handle bootstrapping and updating CouchDB.

My first thought was to use bash scripts, but that wouldn’t be too pretty. So next I considered Rake, but that too was shot down because my Ruby-fu is really rusty. Thankfully, Jabis recently introduced me to the joys of v8cgi, so I said to myself, “Hmm, why not make a Rake clone that runs javascript tasks via v8cgi?”

So I took a few hours to write Jask, and I must say, I’m happy with the results.

Javascript Tasks

Jask is a very simple task runner. Basically, it looks for taskfiles inside the current directory and parses them for possible tasks. Each taskfile is a plain javascript file that exports an object containing tasks.

For instance, this is my basic taskfile for handling CouchDB:

// Export Tasks exports.couch = { 'connect': function(){ var couch = include('./couchdb.js'); this.db = new couch.CouchDB({host: 'http://127.0.0.1', port: '5984', db: 'trace'}); this.docsdir = './design_docs/'; }, 'bootstrap < connect': function(){ var resp = this.db.GET(''); if (resp.error == 'not_found') { resp = this.db.requestDb(dbname, 'PUT'); if (resp.ok) { console.log('Database created!'); } else { console.log('Database creation failed: ' + resp.error); } } else { console.log('Database already exists!'); } return null; }, 'updateDocs < connect': function(){ var dir = new Directory(docsdir); var files = dir.listFiles(); for (var x = 0, y = files.length; x < y; x++) { var file = new File(dir.toString() + files[x]); file.open('rw'); var data = this._updateDoc(file.read()); if (data) file.write(data); file.close(); } }, '_updateDoc': function(data){ data = JSON.parse(data); var resp = this.db.GET(data._id); if (resp.status === 404 || resp._rev === data._rev) { if (resp.status == 404) delete data._rev; resp = this.db.PUT(data._id); if (resp.status == 201) { data._rev = resp._rev; return JSON.stringify(data); } } return null; } };

Like I mentioned before, taskfiles are just simple javascript files, which is pretty evident from the example above. This taksfile is pretty simple: I exported a namespace couch with two tasks, bootstrap which creates my database and updateDocs to update my design docs (_updateDoc, and any function that start with an underscore, is a private function). Yes, it needs some work, but it’s pretty usable.

The great thing about this is that I don’t have to switch brains when working. Since my current project involves a lot of javascript, being able to write tasks in the same language (rather than in ruby or bash) is a welcomed improvement in my workflow.

Start Your JS Engines

You can get Jask at my Github Repo. There’s also a more comprehensive documentation file available there. Jask is released under an MIT-style license and is tested on v8cgi 0.5.2 with v8 1.2.

If you have any questions, leave a comment or get in touch.