Metaprogramming JavaScript - Part 1
Wednesday, March 28, 2007
Last week, I gave a presentation at the Columbus Ruby Brigade meeting about metaprogramming JavaScript. Although I posted that presentation here, the slides were short on text, so I thought it would be more helpful if I wrote a series of tutorials to discuss these concepts. Also, I want to take my example a bit further with more extensions and refactoring.
But before we dive into what metaprogramming is, why it is important, and how we can bring it to life in JavaScript, let’s start with an example that we can build on throughout the tutorial: Say we’ve got a really basic form with select boxes for country and state, and we have a requirement to only show the state when the selected country is “United States”. [example]
Event.observe(window, "load", function() {
Event.observe($("country"), "change", function() {
if ($F("country") == "United States")
$("state-field").show();
else
$("state-field").hide();
});
});
The code I have used to implement this behavior is pretty basic, but it does use a bit of Prototype that I should explain. If you’re a Prototype guru, you can safely skip this paragraph. 1) Event.observe is Prototype’s version of the addEvent method every JS developer has in their toolbox. 2) The dollar-sign method is a shortcut for document.getElementById, which is handy since I seem to do this *everywhere*. 3) $F is a similar shortcut which grabs the current value of any form element. 4) One of the many things that Prototype provides is a set of extension methods available on all HTML elements - show() and hide() are two examples of this which simply modify the “display” style property of an element.
This code works just fine, and there is certainly nothing wrong with it for such a simple requirement. Unfortunately, things get hairy as requirements change. Let’s see how our code scales when we want to do the following:
add province field
show province when country is “Canada”
show state when country is “United States”
show Brutus when state is “Ohio” or “Michigan”
Check it out in action: [example].
Event.observe(window, "load", function() {
Event.observe($("country"), "change", function() {
var country = $F("country");
if (country == "United States") {
$("us-state-field").show();
$("province-field").hide();
} else if (country == "Canada") {
$("province-field").show();
$("us-state-field").hide();
} else {
$("us-state-field").hide();
$("province-field").hide();
}
});
Event.observe($("us-state"), "change", function() {
var state = $F("us-state");
if (state == "Ohio" || state == "Michigan")
$("brutus").show();
else
$("brutus").hide();
});
});
Using the same style of event handling and field checking, I have attempted to meet the new requirements. Unfortunately, we have several problems. First, the code has two bugs: everything is shown when the page initially loads, and hiding the state field doesn’t automatically hide Brutus. I refer to the latter as the “domino” bug, since fields that are dynamically shown and hidden will create a domino effect if other elements are dependent on them. Knowing that these bugs exist, let’s put them off for a just a bit.
Our second problem in the code is readability. I’d like to be able to glance at the code and instantly know what it’s trying to accomplish. I’d like to be sure that I haven’t left out or misunderstood any requirements. When a requirement changes, I’d like to know exactly where my code needs changed. And when I make those changes, I’d like to have confidence that I’m not breaking something else. This is where the concepts of metaprogramming come into play.

The Pragmatic Programmer describes metaprogramming as pulling the details out of your code and into metadata. Often this metadata comes in the form of a configuration file or some other data source, but there is no reason it can’t be executable code, as long as you enforce a clean separation of the “what” and the “how”. The idea with this approach is to write your code with a vocabulary that mirrors your problem domain (the “what” you’re trying to describe). This kind of mini-language is where we get the term “domain specific language”, or DSL.
Now, in our case, the details - or the “what” - are the actual rules for when to show and hide form fields. Right now we have the rules intertwined with the implementation of those rules, and we have felt the pain from it - pain that will only continue to worsen as requirements change and features are added. What would be great is if we could pull those rules out so we can modify them independently of the implementation. What would be great is if those rules were written in a language that looks more like our problem domain than JavaScript. What would be great is if we built a DSL for dynamic form behavior.
That’s exactly what we’ll do… in part 2.
Adam McCrea 28-Mar-07 at 10:52 am
I know this is painful to read. A new design and code highlighting are coming soon, I promise.
surfacedamage 28-Mar-07 at 10:52 am
Charles Dickens has nothing on you. This is a serial novel at its best. Except for the fact that Charles Dickens knows how to use keyword hilighting when he posts his code. He’s smart like that. Oh, and dead. Very dead. I think.
adamlogic » Metaprogramming JavaScript Presentation 28-Mar-07 at 10:56 am
[...] Update: Part 1 of the tutorial is now posted. [...]
Arjan Eising » Metaprogramming JavaScript 28-Mar-07 at 12:53 pm
[...] haven’t had time to read everything, but Metaprogramming JavaScript looks great. I will definitely have a close look soon. The tutorial is more readable than his [...]
Ted Young 28-Mar-07 at 2:10 pm
Interesting…I’m not quite sure that this is metaprogramming, but it’s cool. It definitely is a FluentInterface (see http://martinfowler.com/bliki/FluentInterface.html). Looking forward to more.
Afshin 28-Mar-07 at 4:59 pm
Hi,
Just tried to check the example, and I get a javascript error regarding the ‘Event’ being ‘undefined’.
By the way great slides and very nice tutorial.
Looking forward to see the rest of the tutorial.
Adam McCrea 28-Mar-07 at 5:34 pm
Doh! Forgot to upload Prototype. Fixed now. Thanks for letting me know.
phipster 28-Mar-07 at 6:50 pm
Adam, great stuff! Your slideshow made this esoteric topic very understandable, even as I paged through it VERY late last night. I’m still trying to wrap my head around it, and it’s very apropos to a project I’m working on now… so keep going with the tutorial! Can a book deal for you be far off now? Say, “Professional Javascript Metaprogramming”!?
p.s. Mr. Dickens was a two-finger typist ;P
Ryan Johnson 02-Apr-07 at 3:29 am
Adam, wanted to send this to you via email, but your email isn’t listed anywhere and my guesses bounced.
http://livepipe.net/projects/event_behavior/
Your presentation really inspired me at precisely the right moment, I hit a wall on an app I am building, and so I wrote this library based on your slides. The implementation is completely different, but let me know what you think.
adamlogic » Metaprogramming JavaScript, Part 2 02-May-07 at 12:42 pm
[...] part 1 last month, I introduced a common UI scenario - dynamically showing and hiding form fields. The [...]