Announcing Ojay, the nice way to use YUI

I’ve been wanting to talk about this project for weeks if not months, and now I finally can. the OTHER media (the web shop I work for) is open-sourcing Ojay, a project I’ve been developing on-and-off since I started at the company back in October. It’s a wrapper for the core DOM, event, animation and Ajax modules in YUI, providing a very concise, expressive API to what is, truth be told, a finely crafted library with a verbose, repetitive API.

The project started out as a replacement for DED|Chain, which we’d been using for a while but has not kept pace with the changes in YUI. When I started at this job, I’d been using Prototype for a year, and jQuery a little before that and was somewhat surprised at the amount of work I had to do to build anything with YUI. It does a sterling job of smoothing over browser inconsistencies and has some really nice features, they’re just not very nice to use. Ojay evolved out of my desire to get back the expressiveness I’d enjoyed with other libraries, while keeping access to YUI’s feature set.

To me, expressiveness is not just ‘syntactic sugar’, a tool to help you type fewer lines of code. It’s a core factor in how maintainable and well-designed your codebase is. We have a team made up of Java devs, a few Ruby enthusiasts and a couple of JavaScripters, and it’s crucially important to us that anyone can look at a piece of code and figure out what it does. Code should tell a story. Allow me to quote Reg Braithwaite:

One problem with the for loop is that it can only handle one loop at a time. We have to nest loops to work with two lists at once. This is patently wrong: there’s nothing inherently nested about what we’re trying to do. We can demonstrate this easily: try calling a colleague on the telephone and explaining what we want as succinctly as possible. Do you say “We want a loop inside a loop and inside of that an if, and…”?

No, we say, “We want to count the number of employees that have been with the company longer than their departments have existed.” There’s no discussion of nesting.

… The difference is more than just semantics, or counting characters, or the alleged pleasure of fooling around with closures. …

We have an example of the “Telephone Test:” when code very closely resembles how you would explain your solution over the telephone, we often say it is “very high level.” The usual case is that such code expresses a lot more what and a lot less how. The concern of what has been very clearly separated from the concern of how: you can’t even see the how if you don’t go looking for it. In general, we think this is a good thing.

And so it is with Ojay. Take this example from the documentation site:

$('a#run-this-code').on('click', $.stopEvent)
    .setContent('Running...')
    ._('#example').animate({
        height:     {to: 0},
        opacity:    {from: 1, to: 0}
    }, 0.5)
    ._($.HTTP).GET('/service/hello.html')
    .insertInto('#example')
    ._('#example').animate({
        height:     {to: 80},
        opacity:    {from: 0, to: 1}
    }, 0.7);

It says what you want to happen, with very little how. There are at least three pieces of asynchronous behaviour in this code: we want the code to fire when a certain link is clicked, then when the first animation is over we want to request some data from the server, then when the data arrives we want to insert it into the document and perform another animation. The usual way to handle asynchronous behaviour is using callback functions, but I’ve not used the word function but once in the above example. That would involve nesting things, and — hey, you guessed — there’s nothing inherently nested about what we’re trying to do. What we’re trying to do is carry out a list of instructions: wait for an event, then animate something, then make an Ajax request. And that’s exactly what the code looks like. I’m willing to bet that, with no knowledge of Ojay itself, most programmers with a passing familiarity with client-side scripting could guess what the above code does.

Update, in response to some comments on Ajaxian… The alternative way to write the above would be:

$('a#run-this-code').on('click', function(link, e) {
    e.stopEvent();
    link.setContent('Running...');
    var example = $('#example');
    example.animate({
        height:     {to: 0},
        opacity:    {from: 1, to: 0}
    }, 0.5, {
        after: function() {
            $.HTTP.GET('/service/hello.html', {}, {
                onSuccess: function(transport) {
                    example.setContent(transport.responseText);
                    example.animate({
                        height:     {to: 80},
                        opacity:    {from: 0, to: 1}
                    }, 0.7);
                }
            })
        }
    });
});

The point being that Ojay’s chaining mechanism handles asynchronous behaviour for you, rather than synchronously calling methods on the same base object. This approach means that, as well as being shorter and easier to follow, it’s much easier to change and debug: if you want to remove one of the asynchronous steps in the sequence, you only need to modify code in one place, rather than two or more as you would in the second example. (End update)

This is exactly the sort of stuff I’ve been alluding to when talking about ChainCollector. (It’s now called MethodChain, and will be part of the next release of JS.Class, which Ojay itself is based on.) Ojay uses this technique extensively to let you handle asynchronous coding as if it were synchronous, so that your code more clearly expresses what you’re trying to do. One particularly expressive part of the library is its HTML module:

Ojay.HTML.ul({id: 'list'}, function(HTML) {
    HTML.li('Item one');
    HTML.li('Item two');
    ['Cats', 'Dogs', 'Pets'].forEach(HTML.method('li'));
})

// -> <ul id="list">
          <li>Item one</li>
          <li>Item two</li>
          <li>Cats</li>
          <li>Dogs</li>
          <li>Pets</li>
      </ul>

Yes, people have done this sort of thing before, but I knew it could be made even cleaner. Most of Ojay is essentially an attempt to push JavaScript as far as I can, to make it get out of the way and let me say what I mean.

One other major area of frustration we’ve paved over with Ojay is YAHOO.util.History. It’s a great tool, but it encourages bad design, or at least makes good design fairly tricky. Ojay includes a wrapper for YAHOO.util.History that distills it down to one function call:

$.History.manage(myObject, 'itsName');

myObject has to implement a couple of special methods for this to work, but it’s not that hard and it will improve the design of your code: the concern of having an object know its state has been separated from the concern of getting the browser to remember that state. Your object will work with or without history management — it can just be bolted on as and when you feel like it.

That’s really all I wanted to say about it for now. Chances are I’ll be demonstrating some example code here, and digging into Ojay’s innards a little along the way. For now, I’d be thrilled if you’d download Ojay and give it a whirl. Our code is hosted on Google Code, and we have a mailing list should you want to get in touch.

If you've enjoyed this article, you might enjoy my recently published book JavaScript Testing Recipes. It's full of simple techniques for writing modular, maintainable JavaScript apps in the browser and on the server.