Two Birds, One Hot Code Load

Published Sunday, August 19, 2007 by Bryan

I bet there are a lot of people still questioning the utility of hot code loading. Especially in the web app field, it can seem a little gratuitous. PHP apps don't need any special hot load facility - the script just gets reread from disk every once in a while.

Well, even if we ignore that there are likely parts of web apps that do need to run all the time, and are not just executed at request time, there's still the web server to think about. And, guess what I did last week.

Yaws provides lots of nice utility functions. One in particular is yaws_api:htmlize/1, which takes an IoList as an argument, and returns the "same" list with the four big offenders (ampersand, double-quote, less-than, and greater-than) replaced with their HTML entities (& et al.). This function does exactly what you need when serving HTML directly to a modern web browser.

Unfortuntately, htmlize/1 doesn't work perfectly when sending "FBML" to Facebook. During Facebook's translation, it converts all characters with ASCII values greater than 127 to unrecognizeable characters, which come out as some form of "?" in a browser.

The fix is simple - just HTML-encode all characters over 127 as HTML entities of the form &#X;, where X is the decimal representation of the ASCII value. In Yaws 1.68, just add the following two lines just before yaws_api.erl:590 (the line with the guard for integer(X)):

htmlize_l([X|Tail], Acc) when integer(X), X > 127 ->
    htmlize_l(Tail, [$;, integer_to_list(X), $#, $&|Acc]);

Compile the new code by running make in the base directory of the Yaws source. If you're running Yaws from a directory other than the source directory, copy ebin/yaws_api.beam to that other ebin directory.

Now for the magic. My prefered way to load new code is to open a console to the web server's erlang node. First, run "erl -sname Name", where Name is any name other than that of your webserver. Once erl starts up, type C-g (control-g, for you non-Emacs-ers). You'll be asked for a "User switch command". Typing "h" will get you help here, but what you actually want to do is type "r yaws@host", where "yaws@host" is the node name of the webserver's erlang node. Typing "j" should now show two shell sessions. Connect to the second one with "c 2".

Now that you're connected to your web server's erlang node, just type "l(yaws_api)" to load the new code. Any module calling into the yaws_api module will now automatically use the new code. Meanwhile, any code that was in the middle of a yaws_api module call will finish the call with the old code.

When you're done mucking about (I know you've just spent the last half hour figuring out what other bits of your webserver you can touch from here), type C-g again, then kill the remote console with "k 2". Connect back to your first console with "c 1", then exit it in the normal manner.

Sidenote: David Terrell reached out to say that using -remsh remotenode is a quick way to connect to other nodes without all the C-g stuff. Thanks, dbt!

So, voila, Á now comes out of htmlize/1 as Á. International beer names show up properly in Facebook, and (oh boy!) BeerRiot Local now works in IE6 (which couldn't parse those letters from the XML tag file). Two birds, one stone, I love it.

On a couple of side notes:

Thanks for the versioning system suggestions. I've settled on Mercurial for now, and I'm quite happy with it so far. Bit of a pain upgrading Python versions, but I probably should have done that long ago anyway.

And, I've been doing more than just building a website around beer this summer. I've also been growing my own ingredients! I made my first hop harvest earlier this week. It was only an ounce wet, which turned into about 1/8 oz. dry, but I was proud to have some success anyway. Here's the proof:

Cascade hops growing in my back yard

Categories: Development Erlang