JavaScript Templates (redux)

In the spirit of code kata (the origional problem was a kata) here is an alternative solution to the JavaScript templating problem:

var template = (function(){
    var grammar = parsec.grammar([
        parsec.token("placeholder", "[$]\\w+"),
        parsec.token("literal", "[^$]+")
    ]);

    with(grammar){
        root("template", placeholder.or(literal).repeated().then(eof));
    }

    return {
        render: function render(source, context){
            var handlers = {
                literal: function(node){
                    return node.v;
                },

                placeholder: function(node){
                    var key = node.v.substring(1);
                    return context[key] || "";
                },

                template: function(node){
                    return $.map(node.children, evaluate).join("");
                }
            };

            function evaluate(node){
                return handlers[node.type](node);
            }

            var root = grammar.parse(source);
            return evaluate(root);
        }
    };
})(parsec.util);

It's a bunch longer. It's not quite ten time's longer, but it's kind of close... and it passes exactly the same test suite and has the same features, so what, if anything is good about it?

For one thing, it uses a completely different approach. It builds a recursive descent parser using a parsing library that I put together that's based on Parsec. The basic idea of Parsec, and other parser combinator libraries, is that you define simple parsers and then compose them into more complicated parsers using combinators. What this means is that, while it is not necessary here, this approach can handle much more complicated languages than the regex based approach.

Oh, and there was one little nugget of joy in there that deserves special mention. You, might have missed it, so I'll show it again:

with(grammar){
    root("template", placeholder.or(literal).repeated().then(eof));
}

That's right, a with statement. I bet you never though you would see somebody actually try to use one of those. Sure, with statements are widely reviled, they are not one of the good parts, and they're dead in EC5 strict mode... but embedded DSLs are one of the places where you're allowed to cheat like there's not tomorrow. 'sides, $.extend(window, grammar); is your other option for scope manipulation and that's just plain evil.

JavaScript Templates

While playing around with autotest-style QUnit, I needed something to work on that actually needed some tests. Naturally, I settled on the same problem that they did in the video: Given a string with $-placeholders, replace the placeholders with values from a context object. Here's the code I came up with:

var template = (function(){
    return {
        render: function(source, context){
            return source.replace(/\$\w+/g, function(match){
                var key = match.substring(1);
                return context[key] || "";
            }) ;
        }
    };
})();

Dead simple, right? Four actual lines of code and a bunch of setup to put it in a fake module. Here are the tests:

$(function(){
    function replace(source, expected, context, msg){
        equals(template.render(source, context), expected, msg);
    }

    module("template.render");
    test("with no tokens", function() {
        replace("", "", {},
            "returns blank string if the given string is blank"
        );

        replace("hello, world", "hello, world", {},
            "returns a non-blank string unchanged"
        );
    });

    test("with one token", function(){
        replace("$name", "", {},
            "replaces token with empty string if no value in context"
        );

        replace("$name", "Aaron", {name: "Aaron"},
            "replaces token with value from the context"
        );

        replace("Hello $name!", "Hello Aaron!", {name: "Aaron"},
            "leaves the rest of the string unchanged"
        );
    });

    test("with two tokens", function(){
        replace(
            "Hello $name! It is a $attitude day.",
            "Hello Aaron! It is a wonderful day.",
            {name: "Aaron", attitude: "wonderful"},
            "replaces each token with the value from the context"
        );

        replace(
            "Hello $name! It is a $attitude day.",
            "Hello Aaron! It is a  day.",
            {name: "Aaron"},
            "replaces each token without a value in the context with the blank string"
        );

        replace("$me$you", "AaronJohn", {me: "Aaron", you: "John"},
            "replaces adjacent tokens with their respective values"
        );
    });
});

Kind of verbose, but they're actually kind of interesting. They're written in a BDD style, or as much of a BDD style as QUnit will allow. You can easily imagine a tool that extracts:

template.render
    with no tokens
        - returns blank string if the given string is blank
        - returns a non-blank string unchanged

... from tests like these. Indeed, such tools exists for RSpec and there is a BDD JavaScript testing framework, imaginatively named: JSpec, which I expect provides similar tools for it's tests.

It was an interesting experience to work my way through the tests one at a time, implementing that simplest thing that could possibly work. Though, It is kind of discouraging that the test suite is so much longer than the implementation, especially considering this code is so easily tested.

Autotesting QUnit

I was watching a video on performance code kata and I was intrigued by the use of autotest. Basically, it's just a tool that watches the filesystem for changes and re-runs your test-suite when stuff changes. I thought it was a neat idea and wanted to try it out, but I don't really write any Ruby. Instead I turned to the world's most popular dynamic language: JavaScript. I've used QUnit to do some test driven development in JavScript, so tried the simplest thing that could possibly work, a meta-refresh:

<meta http-equiv="refresh" content="5"/>

... and that kind of worked. It was kind of cool, but the constant refreshing gets in the way of of using the JavaScript console to poke at things. Which, when you're writing JavaScript, is kind of an important thing to do. So... meta-refresh with a pause button?

$(function(){
    function startPaused(){
        return location.hash.indexOf("autotest=true") == -1;
    }

    function enable(){
        button.removeAttr("disabled");
        $("body").unbind("mousemove", enable);
    }

    var paused = startPaused();
    var button = $('<input type="button" value="{title}" id="auto-refresh">'.replace("{title}",
        (paused ? "autotest" : "pause")
    ));
    $("body").prepend(button);

    button.click(function(){
        paused = !paused;
        button.attr("value", (paused ? "resume" : "pause"));
        button.attr("disabled", "disabled");

        if(paused){
            location.hash = "";
        }

        $("body").mousemove(enable);
    });


    setInterval(function(){
        if(!paused){
            location.hash = "#autotest=true"
            location.reload(true);
        }
    }, 5000);
});
#auto-refresh {
    float: right;
    background-color: #D2E0E6;
    border: 2px solid #2B81AF;
    color: #2B81AF;
    margin-right: 1em;
    margin-top: 1em;
    border-radius: 5px;
}

#auto-refresh[disabled] {
    opacity: 0.75;
}

There you go. Thirty-odd lines to implement meta-refresh a pause button sigh.

It is actually pretty neat. If I used QUnit more and was in a better position to judge the actually usefulness of such a feature I might try hacking this into core QUnit and seeing if I could get it merged upstream. I mean, there are entire projects built around adding this kind of functionality to other tools, and it's so easy to do in a browser. I'm not planning on it though, because I've only actually used QUnit for fun-zies, and can't really judge how useful this kind of functionality would be on an actual test suite.

May I Bring Your Attention To...

One of the cool things I ended up implementing was Chrome-style disable-enable behaviour for the pause/resume button. Basically, as part of the click handler, it disabled the button and attached a mousemove event handler on the body. The handler then enables the button and unbinds itself when a mousemove event fires. The cool part is that while the button is disabled and your cursor is over it, moving your mouse doesn't fire any mousemove events, but the second your cursor leaves the button, a mousemove event fires and the button gets enabled. You get to safely reuse the pause/resume button without worrying about double clicks doing the wrong thing. Pretty spiffy, huh?

Happy Wintertime Everybody!

Macro photo of a leaf.

I went for a walk in Brooklyn Bridge park and took a few photos and I liked this one, but that's mostly because I like macro photography.

Grayvatar 2.0

In my post about Grayvatar, I warned everybody away from actually using it for anything as it was a complete toy. The major reason I cited was that it did not make any attempt to respect cache headers. Enter Grayvatar 2.0, which is slightly less of a toy as it, at least, makes a half-hearted attempt to respect cache headers.

The key addition was httplib2, which maintains it's own cache, so that we're not hitting Gravatar's servers for every single request we receive. The other big change was to copy cache headers from the client to the server and vice-versa. This means that we'll pass a long the cache-control: max-age=300 that we get from Gravatar or the cache-control: max-age=0 we get from the client. All of this adds about 20 lines on top of the existing implementation. You still shouldn't use it for anything, it's still a toy, but at least it's slightly less naive one.

import StringIO
import tempfile
import urllib, urllib2

import cherrypy
import httplib2
import Image

from paver.easy import path

URL = "http://www.gravatar.com/avatar/%s?%s"
cache_headers = ("cache-control",)


class Grayvatar(object):
    def __init__(self):
        tmp_dir = tempfile.mkdtemp(".cache")
        self.http = http = httplib2.Http(tmp_dir)

    @cherrypy.expose
    def default(self, *bits, **kwargs):
        if bits:
            cherrypy.response.headers["Content-Type"] = "image/png"
            url = URL % (bits[0], urllib.urlencode(kwargs))
            headers, img = self.get_img(url)

            for header in cache_headers:
                if header in headers:
                    cherrypy.response.headers[header] = headers[header]

            return img_as_str(img.convert("L"))

        pwd = path(__file__).abspath().parent
        return (pwd / "how_it_works.html").text()

    def get_img(self, url):
        headers, body = self.http.request(url,
            headers = project(cherrypy.request.headers, cache_headers)
        )
        return headers, Image.open(StringIO.StringIO(body))


def project(d, keys):
    return dict((k, d[k]) for k in keys if k in d)

def img_as_str(img):
    sock = StringIO.StringIO()
    img.save(sock, "png")
    return sock.getvalue()

if __name__ == "__main__":
    cherrypy.quickstart(Grayvatar())