Automatic JS compiling in Django via Ruby

There are a few Ruby scripts I use in development of Django sites that I’ve found invaluable. Yeah, some of them are written by friends of mine, but did I mention they’re really smart friends?

Don’t be afraid of the mix!

Most people love to choose one camp over the other – either Django and Python, or Rails and Ruby. But to discount one in loyalty to the other is just silliness. The project I’m currently working on is actually a mix of two web applications – one in Django, and one in Rails. I’m writing the Django part (along with Objective-C, it’s my area of expertise) and a good friend is handling the Rails app. (To be fair, the Rails app existed before Hire an Esquire, but that’s another story.) The experience for the end user will be seamless.

What that’s led to, however, is my introduction to a few really helpful Ruby tools that I’ve integrated in to my development environment.

Sprockets

Despite the somewhat agnostic name, Sam Stephenson, one of the bright programmers over at 37 Signals released Sprockets, a little “gem” (pun not intended but totally acceptable) that allows you to do all kinds of cool things with packing JavaScript for production. I’ve been taught to use it by my friend Thomas Fuchs, who was both a contributor to Sprockets as well as one half of the team behind the Rails part of Hire an Esquire.

I first heard of Sprockets during Slash7’s JavasScript Master Class (which I strongly recommend), which I took in order to write better JS code for Hire an Esquire. I didn’t realize I’d come out of the class writing all kinds of file-separated, namespaced, dependency-configured JavaScript code that relies on Sprockets.

I use Sprockets for two purposes – it helps me keep my files smaller and properly namespaced and my code small and easy to read and edit, and it generates a single JavaScript file for production purposes that includes all of my code plus the 3rd party libraries (Prototype, Scripty2) I rely on. Basically, I have it create a sprockets.js file which I put in my web page, and I include no other JS files on my site.

Sprockets is amazing with the way it works with Rails, but we’re in a Django environment, so I decided to get it working in my environment. (NOTE: Ruby will have to be installed on your system.) I installed Sprockets gem which also installs the “sprocketize” command-line utility, which is perfect for us. This is the command line I use:

It’s pretty straight forward:

  • -C puts me in the appropriate javascript directory
  • -I includes subdirectories which hold my dependencies
  • hae.js is the “starting point” for compiling my JS code
  • > outputs the file to the specified “compiled” JS file

My main JS file, hae.js looks like this:

At the top I require 3rd party libraries (which I put in a “vendor” subfolder). In this case, it’s vendor/prototype.s2.js, which Scripty2 provides as a nice combination Prototype/Scripty2 pre-compiled library. Then I declare my default namespace, require the includes of both the appropriately namespaced hae/attorney.js and hae/hiringagency.js files. Each of those files can have their own requirements for further embedding.

The nice thing is that the requirements are parsed in place, which means that my compiled file will first have Prototype and Scripty2 code, then my currently empty base HAE namespace code, then the contents of the hae/attorney.js and hae/hiringagency.js files and their dependencies, and finally a document.observe function that executes dead-last, adding a quick “highlight” function to DOM elements. (Here, Prototype’s document.observe('dom:loaded', function) is much like JQuery’s document.ready() function.)

Next I needed to get this to execute dynamically via Python. No problem.

This script sat in my main Django app directory, and uses os.getcwd() to get the current directory. The path to my JS files uses some basic os.path to get an absolute path to my JavaScript files, which is then formatted in to a command-line string, and executed on the shell using the Python subprocess library.

Great, now make it happen automatically

We know that Django’s runserver as well as the much better runserver_plus utilities have an auto-reloader which reloads the server when any Python code file is saved. I basically wanted the same thing to happen when I saved a JS file.

Kicker

Thomas pointed me to Kicker, which is a Ruby utility that listens for changes on the OS X filesystem using native FSEvents calls. Point it at a directory, give it a command to execute, and whenever something changes in that directory, run the command.

Perfect.

All I needed was Kicker to listen to my JS directory, and whenever I made a change to one of the JavaScript files, kick off the sprocketize command-line. I installed the Kicker gem as instructed, and tried it out:

I basically took our sprocketize call above, passed that in to Kicker as the command to be called whenever anything in the JavaScript directory (second argument) changed. (Kicker is awesome enough to integrate with Growl too!) It worked great.

Putting it together

In order to really integrate this in to my Django environment, I wanted Kicker to start up whenever I executed the development server runserver or runserver_plus and die off when I closed the server.

This does the following things in order:

  1. Starts Kicker in a subprocess using subprocess.Popen, returning the hander to the main function. It gets the JavaScript path, creates the sprocketize command-line argument, passes that to a string format option to format our Kicker command-line string, and executes it.
  2. Calls start_runserver() which uses the subprocess.call(...) to block until the subprocess exits.
  3. Once start_runserver() returns (the subprocess exists) we kill the Kicker subprocess and exit

Now I have a single Python script that will augment runserver by launching Kicker to listen for edits to my JavaScript files in order to re-create my compiled sprockets.js, making it immediately available to my development environment and development server.

Improvements?

Oh yeah, this could be improved, but for less than an hour’s worth of work, I’ve been able to get JavaScript compilation, dependency management, single-JS file serving, and an auto-compile listener running with my dev server, mostly because I wasn’t afraid to use good tools – even if that meant Ruby in a Python environment.

I’m now re-using the sprocketize Python code above in my Fabric Python deploy scripts. I like Fabric’s Pythonic way of deploying builds better than Capistrano – that’s a personal preference – but Sprockets and Kicker didn’t have Python equivalents, so I used Ruby instead – and have a much better dev environment for it.