Friday, March 20, 2009

Dojo Templated, part I

Dojo is a JavaScript toolkit with a number of really nice widgets. One reason it has so many is that it has a very simple way to make new widgets: Templated. This allows you to have a little bit of boilerplate JavaScript, and then you can use HTML with parameters to define your widget.

To illustrate, I will walk through an application I am currently writing that I call Swadesh. It is intended to allow editing of the orthography and IPA phonetic transcription of a limited series of common words know as the Swadesh list. The main interface will have, on the left, the list of cognates for a given word, and on the right a table of TIPA (TeX-IPA) symbols to insert. Let's start with the TIPA table, as it offers a simple example of basic Templated usage. In each bit of example code, I have bolded the part that is interesting.

This is the (slightly trimmed) HTML for the page itself:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html> <head>
<title>Swadesh</title>
<script type="text/javascript" language="javascript" src="/dojoroot/dojo/dojo.js.uncompressed.js" djConfig="isDebug: true, parseOnLoad: true"></script><script type="text/javascript">
dojo.require("swadesh.tipaTable");
</script>
</head>

<body>
<div style="width:50%; float:left">
Cognates go here...
</div>
<div style="width: 50%; float:right">
<div dojoType="swadesh.tipaTable" language="Danish">
Table of TIPA characters will go here
</div>
</div>
</body> </html>

The first bit of JavaScript is what loads Dojo itself. The important part to notice there is the src attribute, which not only defines where Dojo is located, but also where local extensions can live - namely under the /dojoroot directory. The dojoroot directory contains the unpacked Dojo source

The next bit of JavaScript loads our home-grown widget, swadesh.tipaTable. This is analogous to import statements in Java or #include in C. Notice that we do not reference a file here, but an abstract name -- we'll see how that works shortly.

The remaining bit of interest is the div with the dojoType attribute. This is how our widget gets inserted - an HTML tag with a dojoType attribute that names our widget. The tag is then going to be replaced with some lovely home-grown HTML defined by our widget. The language attribute is an argument to the widget - here it specifies which language we want to show TIPA characters for. There's nothing more to using a Dojo Templated widget than this - but many possibilities.

Now let's look at how our widget's HTML is defined. So far, it's a simple thing, as most of its content will later be loaded dynamically from a database, but it shows the first big advantage of Dojo's Templated widget: parameter substitution. Even if this is all you use, you've made writing HTML a lot more dynamic.
<div>
<div>IPA characters for ${language}</div>
<div dojoAttachPoint="tipaDiv"></div>
</div>

This is just a little bit of stand-alone HTML that'll be inserted instead of the tag with the dojoType attribute on it. The noticable part is the ${...} bit - this will be replaced with the argument specified where we used it, in our case "Danish". Seems trivial, but now expand this to a multitude of parameters that can be inserted in numerous places, and you have one sweet little customization engine.

All that's missing now is the glue that actually gets the substitution done. This will look a little cryptic, but most of it is really boilerplate code that can be copied and pasted every time you make a new widget.
dojo.provide("swadesh.tipaTaple");
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");

dojo.declare("swadesh.tipaTable", [ dijit._Widget, dijit._Templated ], {
templatePath: dojo.moduleUrl('swadesh', 'templates/tipaTable.html'),
language: null,
constructor: function(kwArgs) {
dojo.mixin(this, kwArgs);
},
});

I have bolded the only things that need to be changed when making a new widget: The name of the widget, the filename of the template we wrote before, and the list of parameters (of which there is one). The rest can safely be ignored for now.

Now is a good time to look at where we place the files. I mentioned before unpacking the Dojo source in a directory called dojoroot (an arbitrary name, but that's what we specified when we loaded Dojo back at the start, and it's easier to stick with that). Under the dojoroot directory, we make another directory called 'swadesh', which is where we'll keep all our widget-related files. This is the "module" we're defining, which can (and will eventually) consist of several widgets designed to work together. We place the boilerplate code in that directory, in a file called "tipaTable.js". The module name together with the file name is what allow us in the original HTML to use dojo.require('swadesh.tipaTable') to import our code - the module name and the file name (without .js) together form the widget name. This is also the name used in the boilerplate code, both in the dojo.provide statement and the dojo.declare statement.

In the swadesh directory, I create another directory called "templates". This will contain all the bits of HTML that are used by the widgets. In our case, we put the little bit of HTML we defined before in that directory under the name "tipaTable.html". This name is really arbitrary, and could even be dynamically assigned if we so choose, but to keep it simple we use one static piece of HTML with the same name as the widget. The file name is then used in the templatePath statement, which is how our widget knows where to find its defining HTML.

The last thing we need to do is define the parameters that this widget can take. This is done as a series of JavaScript field definitions, so they'll be of the form "parameterName: parameterDefault,". The parameterDefault is a value that will be used if the attribute is not given in the HTML. Frequently, this will be null to indicate a feature not used, but it could as well - and should when possible - be a reasonable default value such that the attribute doesn't need to be specified all the time. Good defaults are a hallmark of useful systems.

This is really all there is to it, and once you've made one widget, the next ones will be easier. You can even use widgets inside your widget by having another tag with the dojoType attribute. Using Dojo Templated, you can change your HTML from huge monsters of cut-and-pasted code with just slight alterations to modular, parameterized pieces -- and these pieces can even be made to do work for you, we'll see how in a later post.

Tuesday, March 3, 2009

A general issue with Ruby

I see why Ruby has the whole "Migrations" idea, to systematize changes to the database. However, there is a little too much magic for my liking. When dealing with database changes, we normally have to do some cryptic work, but we know exactly what happens. When making a Ruby migration, it's easy to make the change, but you also have to set up the reverse change, and if you fsck that up, you cannot roll back correctly. The fact that you (for any non-trivial change) have to make the reverse migration and get it right is a weak spot. Hopefully the automatic generation of migrations for trivial changes can do enough that it doesn't become a big problem. If I were designing Ruby, I'd try very hard to make the reverse migration automatic.