<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
  <channel>
    <title>Adam Hooper's Engineering Tips</title>
    <desription>Articles by Adam Hooper about Software Engineering</desription>
    <link>http://adamhooper.com/blog/posts</link>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/AdamHoopersEngineeringTips" /><feedburner:info uri="adamhoopersengineeringtips" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
      <title>refinery: a fast RAW decoding library</title>
      <description>&lt;p&gt;Don&amp;#8217;t you hate how long it takes for your computer to import &lt;span class="caps"&gt;RAW&lt;/span&gt; photos?&lt;/p&gt;
&lt;p&gt;The de-facto program to convert &lt;span class="caps"&gt;RAW&lt;/span&gt; files to other formats, &lt;a href="http://www.cybercom.net/~dcoffin/dcraw/"&gt;dcraw&lt;/a&gt;, by Dave Coffin, gives great-quality images &amp;#8230; slowly.&lt;/p&gt;
&lt;p&gt;Dcraw&amp;#8217;s author refuses to turn dcraw into a library, saying: &amp;#8220;Library code is ugly because it cannot use global variables. Libraries are more difficult to modify, build, install, and test than standalone programs.&amp;#8221; Those are bold statements, and I disagree with all of them.&lt;/p&gt;
&lt;p&gt;A couple of alternatives have come up. &lt;a href="http://www.libraw.org/"&gt;libraw&lt;/a&gt; is a library built by running a Perl script on dcraw code. It presents a couple of speed-ups and allows multi-threading, but the final output&amp;#8217;s interface is icky because dcraw&amp;#8217;s is. &lt;a href="http://libopenraw.freedesktop.org"&gt;libopenraw&lt;/a&gt; is very well documented library and has a much nicer interface, but it&amp;#8217;s slow and all it does for now is thumbnails.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s where my new project, &lt;a href="http://adamh.github.com/refinery/"&gt;refinery&lt;/a&gt;, fits in. It&amp;#8217;s brand-new, so it doesn&amp;#8217;t do much. Here&amp;#8217;s what sets it apart:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Refinery is twice as fast as dcraw and can produce the exact same output. On a 2.4Ghz Intel Core 2 Duo, dcraw can take 36 seconds to process 10 photographs. Libraw takes 25 using threads. Refinery takes 18 using  threads.&lt;/li&gt;
&lt;li&gt;Refinery doesn&amp;#8217;t handle metadata. Use &lt;a href="http://www.exiv2.org/"&gt;Exiv2&lt;/a&gt;, which refinery depends on, for that. (As it turns out, most popular &lt;span class="caps"&gt;RAW&lt;/span&gt;-refining software that depends on dcraw or libraw depends on Exiv2 anyway.)&lt;/li&gt;
&lt;li&gt;Refinery doesn&amp;#8217;t extract thumbnails. Cameras store thumbnails as metadata, so you should use Exiv2 to extract thumbnails.&lt;/li&gt;
&lt;li&gt;Refinery grants access to image data from any step in the image-processing pipeline. For instance, users can access the camera&amp;#8217;s raw sensor data.&lt;/li&gt;
&lt;li&gt;Refinery is unit-tested. Unit tests are small, fast and precise, so developers don&amp;#8217;t need to process dozens of test &lt;span class="caps"&gt;RAW&lt;/span&gt; files every time they tweak the code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At the time I write this, refinery can only read Nikon D5000 .&lt;span class="caps"&gt;NEF&lt;/span&gt; files and &lt;span class="caps"&gt;PPM&lt;/span&gt; files. It&amp;#8217;s not difficult to support every camera dcraw supports: it just takes some time and understanding to adopt dcraw&amp;#8217;s code. The intricacies of each camera are buried in uncommented if-statements within dcraw. Refinery uses an object-oriented approach to put each camera&amp;#8217;s traits into understandable classes.&lt;/p&gt;
&lt;p&gt;The one thing going against refinery is that I don&amp;#8217;t want to maintain it. I prefer photojournalism: I just want to import my photographs more quickly.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m making refinery as open and accessible as possible, so others will take it up. Check out the code and submit issues at &lt;a href="http://github.com/adamh/refinery"&gt;refinery&amp;#8217;s GitHub repository&lt;/a&gt; and read more at &lt;a href="http://adamh.github.com/refinery"&gt;the refinery project page&lt;/a&gt;.</description>
      <pubDate>Wed, 15 Dec 2010 15:57:25 -0500</pubDate>
      <link>http://feedproxy.google.com/~r/AdamHoopersEngineeringTips/~3/-Haa-xk_cGA/10-refinery-a-fast-raw-decoding-library</link>
      <guid isPermaLink="false">http://adamhooper.com/eng/articles/10-refinery-a-fast-raw-decoding-library</guid>
    <feedburner:origLink>http://adamhooper.com/eng/articles/10-refinery-a-fast-raw-decoding-library</feedburner:origLink></item>
    <item>
      <title>SVG/JavaScript mapping</title>
      <description>&lt;p&gt;Mapping is all the rage on the Web these days, but it often involves complicated mapping servers, complicated features like zooming and tiling, and complicated coordinate systems.&lt;/p&gt;
&lt;p&gt;Try answering this: how can I display a map of a country which shows a certain economic indicator (for instance, proportion of people with education) by administrative region?&lt;/p&gt;
&lt;p&gt;Yes, you can fire up Mapnik, worry about OpenStreetMap layers and install server after server to preprocess and prepare the data. But I&amp;#8217;m looking for a one-sentence answer.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s mine: I display the map as &lt;span class="caps"&gt;SVG&lt;/span&gt; and color it with JavaScript.&lt;/p&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;ll start with the fun stuff. Here&amp;#8217;s an interactive map, entirely public-domain. Play all you want, and &amp;#8220;View Source&amp;#8221; to play even more. Below, I&amp;#8217;ll describe how this came to be.&lt;/p&gt;
&lt;p&gt;&lt;iframe id="svg-map-iframe" src="/images/eng/tanzania-districts.svg"&gt;&lt;br /&gt;
  If you see this message, the map isn&amp;#8217;t loaded and all of a sudden my post looks a lot less awesome. Your loss.&lt;br /&gt;
&lt;/iframe&gt;&lt;/p&gt;
&lt;style type="text/css"&gt;
#svg-map-iframe {
float: left;
width: 600px;
height: 581px;
border: 2px solid black;
padding: 0;
margin: 0 0 1em;
}
.svg-map-legend {
float: left;
margin-left: 1em;
}
.svg-map-legend ul {
border: 2px solid black;
margin: 0;
padding: 1em;
}
.svg-map-legend li {
margin: 0;
padding: 0;
list-style: none;
line-height: 1.8;
vertical-align: middle;
}
.svg-map-legend div {
width: 1em;
height: 1em;
margin-right: .25em;
border: 1px solid black;
display: inline-block;
}
#svg-map-data table {
border-collapse: collapse;
}
#svg-map-data th {
border-bottom: 2px solid black;
}
#svg-map-data td {
border-bottom: 1px dotted gray;
}
&lt;/style&gt;
&lt;script type="text/javascript"&gt;//&lt;!-- &lt;![CDATA[
/*
 * This big block defines window.SvgMap.
 *
 * It requires jQuery.
 */
(function($) {
  var VERBOSE = false;

  function SvgMapDataAnalysis(data, options) {
    this.data = data;
    this.options = options;

    this.data_as_list = [];
    for (var key in this.data) {
      this.data_as_list.push(this.data[key]);
    }
    this.data_as_list.sort(function(a, b) { return a - b; });

    this.min = 0;
    this.max = this.data_as_list[this.data_as_list.length - 1];

    if (this.options.partitions) {
      this._calculatePartitions();
    }
  }

  $.extend(SvgMapDataAnalysis.prototype, {
    _calculatePartitions: function() {
      /*
       * We don't know anything about the semantics of the data, so let's just
       * divide it into categories: if there's a 0 we'll put 0 into its own
       * category, otherwise we'll divide the data into this.options.partitions
       * equally-populated partitions.
       */
      this.partitions = [];

      var start = 0;
      var values = this.data_as_list;

      if (values[0] == 0) {
        this.partitions.push(0);
        for (start = 0; values[start] == 0 &amp;&amp; start &lt; values.length; start++) {}
      }

      var nValues = values.length - start;
      var nBuckets = this.options.partitions - this.partitions.length;

      var interval = nValues / nBuckets;

      for (var i = 1; i &lt;= nBuckets; i++) {
        var x = i * interval;
        var index = Math.round(x) - 1;
        this.partitions.push(values[index]);
      }
    },

    /* Returns an HTML color for 0 &lt;= x &lt;= 1 */
    _lookupColorForFraction: function(x) {
      if (x &lt; 0) x = 0;
      if (x &gt; 1) x = 1;

      var letters = Math.floor(255 - (255 * x)).toString(16);
      if (letters.length == 1) letters = '0' + letters;

      return "#ff" + letters + letters; // shades of red
    },

    /* Returns an HTML color for one of the points in our data */
    lookupColor: function(value) {
      if (this.partitions) {
        var partition;

        for (partition = 0; partition &lt; this.partitions.length; partition++) {
          if (this.partitions[partition] &gt;= value) {
            break;
          }
        }
        if (partition &gt; this.partitions.length) {
          partition = this.partitions.length - 1;
        }

        var x = partition / (this.partitions.length - 1);
        return this._lookupColorForFraction(x);
      } else {
        if (this.max &gt; 0) {
          return this._lookupColorForFraction(value / this.max);
        } else {
          return '#ffffff';
        }
      }
    }
  });

  function SvgMapWalker(svg, data, analysis, pathClassToKey, options) {
    this.svg = svg;
    this.data = data;
    this.analysis = analysis;
    this.pathClassToKey = pathClassToKey;

    this.options = $.extend({
      style: 'fill:#{color};stroke-width:.015;stroke:black;'
    }, options);
  }

  $.extend(SvgMapWalker.prototype, {
    _getKeyFromPath: function(path) {
      var class_name = path.getAttribute('class');
      return this.pathClassToKey(class_name);
    },

    _logUnusedKeys: function(path_keys, data_keys) {
      if (!VERBOSE) return;

      console.log("These keys were in the SVG file but not in the dataset:\n" + path_keys.join("\n"));
      console.log("These keys were in the dataset but not in the SVG:\n" + data_keys.join("\n"));
    },

    refresh: function() {
      var unused_path_keys = [];
      var unused_data_keys = [];
      var used_keys = {};

      var _this = this;
      $(this.svg).find('path').each(function() {
        var path = this;
        var key = _this._getKeyFromPath(path);

        var color;
        if (/^#[\da-f]{6}$/.test(key)) {
          // the key is a color; use that instead
          color = key;
        } else {
          if (_this.data[key] === undefined) {
            unused_path_keys.push(key);
          }
          used_keys[key] = true; // we color it even if it's invalid

          var value = _this.data[key] || 0;
          color = _this.analysis.lookupColor(value);
        }

        var style = _this.options.style.replace('#{color}', color);
        path.setAttribute('style', style);
      });

      for (var data_key in this.data) {
        if (!used_keys[data_key]) {
          unused_data_keys.push(data_key);
        }
      }

      this._logUnusedKeys(unused_path_keys, unused_data_keys);
    }
  });

  /*
   * Shows data on an SVG map.
   *
   * Parameters:
   * - svg: an &lt;svg&gt; element
   * - data: an Object mapping (String) "key" to (Number) value &gt;= 0
   * - pathClassToKey: a function which accepts an SVG path element's "class"
   *                   attribute and returns a (String) "key"
   * - options: options...
   *   - "partitions": Integer greater than 1, saying how many different colors
   *                   will be drawn. If unset, will use 256 colors.
   *   - "style": style to set on path elements. "#{color}" will be replaced
   *              with the color SvgMap chooses. Default is
   *              "fill:#{color};stroke-width:.015;stroke:black;"
   */
  function SvgMap(svg, data, pathClassToKey, options) {
    this.svg = svg;
    this.pathClassToKey = pathClassToKey;
    this.options = options || {};

    this.setData(data);
  }

  $.extend(SvgMap.prototype, {
    setData: function(data) {
      this.data = data;
      this.analysis = new SvgMapDataAnalysis(data, this.options);
      this.walker = new SvgMapWalker(this.svg, this.data, this.analysis, this.pathClassToKey, this.options);

      this.walker.refresh();
    },

    _createLegendLi: function(value, color) {
      var $li = $('&lt;li&gt;&lt;div class="svg-map-swatch" style="background-color:' + color + ';"&gt;&lt;/div&gt; up to &lt;input type="text" value="' + value + '" /&gt;&lt;/li&gt;');
      return $li;
    },

    createLegendDiv: function() {
      if (!this.options.partitions) return undefined;
      var _this = this;

      var $legend = $('&lt;form class="svg-map-legend" action=""&gt;&lt;/form&gt;');
      var $ul = $('&lt;ul&gt;&lt;/ul&gt;');

      $.each(this.analysis.partitions, function() {
        var value = this;
        var color = _this.analysis.lookupColor(value);
        $ul.append(_this._createLegendLi(value, color));
      });

      $legend.append($ul);

      $legend.submit(function(e) { e.preventDefault(); });
      $legend.find('input').change(function() {
        var $input = $(this);
        var $li = $input.parent();
        var index = $li.prevAll().length;

        _this.analysis.partitions[index] = parseFloat($input.val());
        _this.walker.refresh();
      });

      return $legend[0];
    }
  });

  window.SvgMap = SvgMap;

})(jQuery);

/*
 * Here's the stuff specific to this dataset and map...
 */
function strip(s) {
  return s.replace(/^\s*|\s*$/g, '');
}

function parse_number(s) {
  var stripped = strip(s).replace(/[^\d\.]/g, '');
  var value = parseFloat(stripped);
  if (value == Infinity || !!value == false || value &lt; 0) {
    return 0; // We don't know what to do with NaN, Infinity or negatives
  } else {
    return value;
  }
}

function parse_string(s) {
  return strip(s).replace(/ /g, '-').toLowerCase();
}

function parse_region(text) {
  return parse_string(text);
}

function parse_district(text) {
  return parse_string(text);
}

function parse_num(text) {
  return s_to_float(text);
}

var MAP_KEY_TO_COMMON_KEY = {
  'kaskazini-pemba': 'zanzibar',
  'kaskazini-unguja': 'zanzibar',
  'kusini': 'zanzibar',
  'kusini-pemba': 'zanzibar',
  'mjini-magharibi': 'zanzibar',
  'manyara': 'arusha'
};

/*
 * Transforms a (String) region into a (String) key. (Usually a no-op.)
 */
function make_key(region, is_dataset) {
  var key = region;
  if (is_dataset) {
    return key;
  } else {
    return MAP_KEY_TO_COMMON_KEY[key] || key;
  }
}

/*
 * Returns the index of the column of data we want. For instance, "1" is the
 * first column of data. (Column "0" is the "Region" column.)
 */
function get_selected_column($table) {
  var $selected = $table.find('input:checked');
  var $th = $selected.closest('th');
  return $th.prevAll().length;
}

/*
 * Returns Object of { common_key: Number } by parsing #svg-map-data
 */
function get_table_data() {
  var $table = $('#svg-map-data');
  var data_column = get_selected_column($table);
  var ret = {};

  $table.find('tbody tr').each(function() {
    var $tr = $(this);
    var region = parse_region($tr.children('td:eq(0)').text());
    var value = parse_number($tr.children('td:eq(' + data_column + ')').text());

    var key = make_key(region, true);

    ret[key] = value;
  });

  return ret;
}

/*
 * Returns a string title
 */
function get_table_title() {
  var $table = $('#svg-map-data');
  var data_column = get_selected_column($table);

  var $th = $table.find('th:eq(' + data_column + ')');
  return $th.text();
}

/*
 * Returns a key given a &lt;path&gt;'s "class" attribute.
 */
function path_class_to_key(class_name) {
  if (!/\bmi-region\b/.test(class_name)) return undefined;
  var region_match = class_name.match(/ADM1-([-\w]*)/);
  /* district must match "ADM2-(Moshi Rural) ..." (a bug in the map data) */
  var district_match = class_name.match(/ADM2-([- \w]*?) mi-TAN/);

  if (!region_match || !district_match) return undefined;

  var region = parse_region(region_match[1]);
  var district = parse_district(district_match[1]);

  if (/-Lake$/.test(district)) return '#0000ff'; // it's a lake

  var key = make_key(region, false);

  return key;
}

/*
 * Returns an &lt;h3&gt; element.
 */
function make_title_heading() {
  var title = get_table_title();
  var $h3 = $('&lt;h3&gt;&lt;/h3&gt;');
  $h3.text(title);
  return $h3[0];
}

$(document).ready(function() {
  //$('#svg-map-iframe').load(function() {
  //  var $iframe = $(this);
    var $iframe = $('#svg-map-iframe');
    var svg = $iframe.contents()[0];
    var data = get_table_data();
  
    map = new SvgMap(svg, data, path_class_to_key, {
      partitions: 5,
      style: "fill:#{color};stroke-width:.015;stroke:black;"
    });
  
    var legend_div = map.createLegendDiv();
    if (legend_div) {
      $(legend_div).prepend(make_title_heading());
      $iframe.after(legend_div);
    }
  
    $('#svg-map-data').find('input').change(function() {
      if (legend_div) {
        $(legend_div).remove();
      }
  
      data = get_table_data();
      map.setData(data);
  
      legend_div = map.createLegendDiv();
      if (legend_div) {
        $(legend_div).prepend(make_title_heading());
        $iframe.after(legend_div);
      }

      $iframe.load(function() {
        map.setData(data);
      });
    //});
  });
});
//--&gt; ]]&gt;&lt;/script&gt;&lt;p style="clear:both;"&gt;Here is the &lt;em&gt;actual&lt;/em&gt; data used for the map. What I mean is, the JavaScript reads the data from this table and colours the map with it. Click to change the indicator shown on the map.&lt;/p&gt;
&lt;form action=""&gt;
&lt;table id="svg-map-data"&gt;
&lt;thead&gt;
&lt;tr&gt;
        &lt;th&gt;Region&lt;/th&gt;
        &lt;th&gt;&lt;label&gt;&lt;input type="radio" name="indicator" checked="checked" /&gt;% Adults without education&lt;/label&gt;&lt;/th&gt;
        &lt;th&gt;&lt;label&gt;&lt;input type="radio" name="indicator" /&gt;% Non-enrollment in primary school&lt;/label&gt;&lt;/th&gt;
        &lt;th&gt;&lt;label&gt;&lt;input type="radio" name="indicator" /&gt;% Individuals ill in 4 weeks before the survey&lt;/label&gt;&lt;/th&gt;
        &lt;th&gt;&lt;label&gt;&lt;input type="radio" name="indicator" /&gt;% Adults in agriculture&lt;/label&gt;&lt;/th&gt;
        &lt;th&gt;&lt;label&gt;&lt;input type="radio" name="indicator" /&gt;% Children(5-14) working&lt;/label&gt;&lt;/th&gt;
        &lt;th&gt;&lt;label&gt;&lt;input type="radio" name="indicator" /&gt;% Households with modern walls&lt;/label&gt;&lt;/th&gt;
        &lt;th&gt;&lt;label&gt;&lt;input type="radio" name="indicator"/&gt;% Households with modern roofs&lt;/label&gt;&lt;/th&gt;
        &lt;th&gt;&lt;label&gt;&lt;input type="radio" name="indicator"/&gt;% Households connected to electricity grid&lt;/label&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
      &lt;tr&gt;&lt;td&gt;Dodoma&lt;/td&gt;&lt;td&gt;31&lt;/td&gt;&lt;td&gt;58&lt;/td&gt;&lt;td&gt;34&lt;/td&gt;&lt;td&gt;67&lt;/td&gt;&lt;td&gt;67&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;33&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Arusha&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;td&gt;53&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;42&lt;/td&gt;&lt;td&gt;73&lt;/td&gt;&lt;td&gt;18&lt;/td&gt;&lt;td&gt;53&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kilimanjaro&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;81&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;56&lt;/td&gt;&lt;td&gt;64&lt;/td&gt;&lt;td&gt;39&lt;/td&gt;&lt;td&gt;85&lt;/td&gt;&lt;td&gt;18&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Tanga&lt;/td&gt;&lt;td&gt;31&lt;/td&gt;&lt;td&gt;50&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;67&lt;/td&gt;&lt;td&gt;80&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;41&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Morogoro&lt;/td&gt;&lt;td&gt;26&lt;/td&gt;&lt;td&gt;61&lt;/td&gt;&lt;td&gt;32&lt;/td&gt;&lt;td&gt;63&lt;/td&gt;&lt;td&gt;55&lt;/td&gt;&lt;td&gt;28&lt;/td&gt;&lt;td&gt;45&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Pwani&lt;/td&gt;&lt;td&gt;39&lt;/td&gt;&lt;td&gt;56&lt;/td&gt;&lt;td&gt;34&lt;/td&gt;&lt;td&gt;62&lt;/td&gt;&lt;td&gt;57&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;33&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Dar es Salaam&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;71&lt;/td&gt;&lt;td&gt;19&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;28&lt;/td&gt;&lt;td&gt;88&lt;/td&gt;&lt;td&gt;98&lt;/td&gt;&lt;td&gt;59&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Lindi&lt;/td&gt;&lt;td&gt;44&lt;/td&gt;&lt;td&gt;44&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;td&gt;72&lt;/td&gt;&lt;td&gt;40&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Mtwara&lt;/td&gt;&lt;td&gt;28&lt;/td&gt;&lt;td&gt;59&lt;/td&gt;&lt;td&gt;28&lt;/td&gt;&lt;td&gt;69&lt;/td&gt;&lt;td&gt;46&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;28&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ruvuma&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;63&lt;/td&gt;&lt;td&gt;22&lt;/td&gt;&lt;td&gt;77&lt;/td&gt;&lt;td&gt;73&lt;/td&gt;&lt;td&gt;67&lt;/td&gt;&lt;td&gt;42&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Iringa&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;76&lt;/td&gt;&lt;td&gt;25&lt;/td&gt;&lt;td&gt;67&lt;/td&gt;&lt;td&gt;60&lt;/td&gt;&lt;td&gt;28&lt;/td&gt;&lt;td&gt;48&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Mbeya&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;69&lt;/td&gt;&lt;td&gt;24&lt;/td&gt;&lt;td&gt;55&lt;/td&gt;&lt;td&gt;53&lt;/td&gt;&lt;td&gt;32&lt;/td&gt;&lt;td&gt;53&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Singida&lt;/td&gt;&lt;td&gt;27&lt;/td&gt;&lt;td&gt;61&lt;/td&gt;&lt;td&gt;29&lt;/td&gt;&lt;td&gt;60&lt;/td&gt;&lt;td&gt;52&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;21&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Tabora&lt;/td&gt;&lt;td&gt;31&lt;/td&gt;&lt;td&gt;55&lt;/td&gt;&lt;td&gt;27&lt;/td&gt;&lt;td&gt;69&lt;/td&gt;&lt;td&gt;39&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;24&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Rukwa&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt;61&lt;/td&gt;&lt;td&gt;24&lt;/td&gt;&lt;td&gt;76&lt;/td&gt;&lt;td&gt;51&lt;/td&gt;&lt;td&gt;32&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kigoma&lt;/td&gt;&lt;td&gt;28&lt;/td&gt;&lt;td&gt;48&lt;/td&gt;&lt;td&gt;21&lt;/td&gt;&lt;td&gt;76&lt;/td&gt;&lt;td&gt;60&lt;/td&gt;&lt;td&gt;39&lt;/td&gt;&lt;td&gt;21&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Shinyanga&lt;/td&gt;&lt;td&gt;40&lt;/td&gt;&lt;td&gt;46&lt;/td&gt;&lt;td&gt;32&lt;/td&gt;&lt;td&gt;68&lt;/td&gt;&lt;td&gt;69&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;24&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kagera&lt;/td&gt;&lt;td&gt;25&lt;/td&gt;&lt;td&gt;59&lt;/td&gt;&lt;td&gt;34&lt;/td&gt;&lt;td&gt;81&lt;/td&gt;&lt;td&gt;68&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;53&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Mwanza&lt;/td&gt;&lt;td&gt;27&lt;/td&gt;&lt;td&gt;52&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt;71&lt;/td&gt;&lt;td&gt;84&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;42&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Mara&lt;/td&gt;&lt;td&gt;24&lt;/td&gt;&lt;td&gt;62&lt;/td&gt;&lt;td&gt;29&lt;/td&gt;&lt;td&gt;70&lt;/td&gt;&lt;td&gt;55&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;43&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/form&gt;
&lt;p&gt;&lt;em&gt;Source: &lt;a href="http://www.nbs.go.tz/index.php?option=com_content&amp;view=article&amp;id=103&amp;Itemid=114"&gt;2001 Household Budget Survey&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;How it works&lt;/h3&gt;
&lt;p&gt;This guide is written for somebody familiar enough with &lt;span class="caps"&gt;HTML&lt;/span&gt; and JavaScript, and inspired enough by the above map, to want to play around for a few hours.&lt;/p&gt;
&lt;p&gt;In broad strokes, you must:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Create an &lt;span class="caps"&gt;SVG&lt;/span&gt; map of the area in question, entirely from closed &lt;tt&gt;path&lt;/tt&gt; elements, with &lt;tt&gt;class&lt;/tt&gt; attributes indicating the Region and District.&lt;/li&gt;
  &lt;li&gt;Output all the data to map, as in the above table.&lt;/li&gt;
  &lt;li&gt;Write JavaScript to parse the data and update &lt;tt&gt;style&lt;/tt&gt; attributes in the map.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I suggest you &amp;#8220;View Source&amp;#8221; on this page if you want to follow along.&lt;/p&gt;
&lt;h4&gt;1. Create an &lt;span class="caps"&gt;SVG&lt;/span&gt; map&lt;/h4&gt;
&lt;p&gt;This will take some hunting, and I&amp;#8217;m the wrong person to ask for advice. I created this map of Tanzania by running the &lt;tt&gt;mi2svg&lt;/tt&gt; tool on a public-domain MapInfo (&lt;tt&gt;.mif&lt;/tt&gt;) file from &lt;a href="http://www.maplibrary.org/stacks/Africa/index.php"&gt;maplibrary.org&lt;/a&gt;, which I found by starting at &lt;a href="http://wiki.openstreetmap.org/wiki/WikiProject_Tanzania"&gt;OpenStreetMap&amp;#8217;s Tanzania wiki page&lt;/a&gt;. I tidied it up in Inkscape and removed Lake Victoria. It&amp;#8217;s a bit disappointing because most lakes and national parks aren&amp;#8217;t shown, and some of its administrative regions are missing or misspelled, but it&amp;#8217;s better than nothing.&lt;/p&gt;
&lt;p&gt;Insert the &lt;span class="caps"&gt;SVG&lt;/span&gt; image into an &lt;span class="caps"&gt;XHTML&lt;/span&gt; document using an &lt;tt&gt;iframe&lt;/tt&gt; tag.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m afraid I&amp;#8217;m no expert on either &lt;span class="caps"&gt;SVG&lt;/span&gt; or mapping. I don&amp;#8217;t know any good sources for maps and I don&amp;#8217;t even know whether &lt;span class="caps"&gt;SVG&lt;/span&gt; can be embedded in &lt;span class="caps"&gt;HTML&lt;/span&gt; documents in the same way. I stopped when I got something that works for me.&lt;/p&gt;
&lt;h4&gt;2. Output the data&lt;/h4&gt;
&lt;p&gt;There&amp;#8217;s a big fad out there called &lt;a href="http://en.wikipedia.org/wiki/Unobtrusive_JavaScript"&gt;unobtrusive JavaScript&lt;/a&gt;. Rather than go into it in detail, I&amp;#8217;ll merely suggest that instead of outputting a JavaScript array programmatically, you output an actual &lt;tt&gt;table&lt;/tt&gt; element and parse it out in JavaScript. You won&amp;#8217;t regret it.&lt;/p&gt;
&lt;p&gt;I can&amp;#8217;t help you find the data, either. Census data is always nice, and your organization&amp;#8217;s in-house data may be great.&lt;/p&gt;
&lt;h4&gt;3. Write the JavaScript&lt;/h4&gt;
&lt;p&gt;On document load, my JavaScript (which, incidentally, is public-domain&amp;#8212;you are welcome to use it) does this:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Parses the data from the &lt;span class="caps"&gt;HTML&lt;/span&gt; table (into an Object mapping &amp;#8220;keys&amp;#8221; to numeric values)&lt;/li&gt;
  &lt;li&gt;Analyzes that data to find the maximum value (it assumes 0 is the semantic minimum) and does any necessary preprocessing so we get a quick function to map values to colors&lt;/li&gt;
  &lt;li&gt;Walks through every &lt;tt&gt;path&lt;/tt&gt; in the &lt;span class="caps"&gt;SVG&lt;/span&gt; document, looks up our value (using the &lt;tt&gt;path&lt;/tt&gt;&amp;#8216;s &amp;#8220;key&amp;#8221;, like the key in step 1), finds the color (using our analysis from step 2), and sets the &lt;tt&gt;fill&lt;/tt&gt; color appropriately in the &lt;tt&gt;path&lt;/tt&gt;&amp;#8217;s &lt;tt&gt;style&lt;/tt&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The crux is the &amp;#8220;key&amp;#8221;: once &lt;span class="caps"&gt;SVG&lt;/span&gt; &lt;tt&gt;path&lt;/tt&gt;s and &lt;span class="caps"&gt;HTML&lt;/span&gt; &lt;tt&gt;td&lt;/tt&gt;s can be translated into the same String key, everything falls into place. If your &lt;span class="caps"&gt;SVG&lt;/span&gt; map and dataset agree, everything will go smoothly. In the case of this particular map, there are some issues which can be resolved by tweaking the &lt;span class="caps"&gt;SVG&lt;/span&gt; map, the dataset, and/or the JavaScript. (I tweaked all three.)&lt;/p&gt;
&lt;p&gt;Adding the interactive legend is a simple JavaScript (and styling) exercise.&lt;/p&gt;
&lt;h3&gt;Pleasures&lt;/h3&gt;
&lt;p&gt;It&amp;#8217;s interactive. Just play with the values above and watch it work. Isn&amp;#8217;t it fun?&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s simple.&lt;/p&gt;
&lt;p&gt;Advantages of &lt;span class="caps"&gt;SVG&lt;/span&gt; come for free: for instance, maps scale very nicely to large sizes and high resolutions. To render a &lt;span class="caps"&gt;PNG&lt;/span&gt; version, just use &amp;#8220;Print Screen&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Every step is fast. The processing time scales linearly with the size of the dataset and the complexity of the &lt;span class="caps"&gt;SVG&lt;/span&gt; file. (Okay, the &lt;tt&gt;sort()&lt;/tt&gt; I put in the analysis step doesn&amp;#8217;t technically scale linearly, but it&amp;#8217;s darned fast anyway.)&lt;/p&gt;
&lt;p&gt;The data is open. You can just as easily set it up so that the data is a set of editable text-boxes or is one big &lt;span class="caps"&gt;CSV&lt;/span&gt; textarea. Imagine copy/pasting random spreadsheets into a page to produce maps. (The code, while simple, is not included here. Yet.)&lt;/p&gt;
&lt;p&gt;The complex task of generating maps from data can be left in the hands of the slightly-computer literate. There&amp;#8217;s nothing more satisfying to a programmer than programming himself out of a task forevermore.&lt;/p&gt;
&lt;h3&gt;Limitations&lt;/h3&gt;
&lt;p&gt;Great system, right? There are some flaws.&lt;/p&gt;
&lt;p&gt;For one thing, Internet Explorer (up to version 8) doesn&amp;#8217;t support &lt;span class="caps"&gt;SVG&lt;/span&gt;. You&amp;#8217;ll have to find a workaround (complicated &lt;tt&gt;canvas&lt;/tt&gt; magic? server-side &lt;span class="caps"&gt;SVG&lt;/span&gt; rendering?) which might remove interactivity. Or you can just ignore Internet Explorer or wait for people to take up Internet Explorer 9. (I used this particular map in an internal website at an office where nobody uses Internet Explorer.)&lt;/p&gt;
&lt;p&gt;The &lt;span class="caps"&gt;SVG&lt;/span&gt; file is hard to get right: you&amp;#8217;ll find problems in any map data, and if you&amp;#8217;re trying to merge multiple maps (for instance, merging lakes and rivers with administrative regions) you might run into inconsistencies. I&amp;#8217;m by no means an expert at generating an &lt;span class="caps"&gt;SVG&lt;/span&gt; map.&lt;/p&gt;
&lt;p&gt;Your dataset and your &lt;span class="caps"&gt;SVG&lt;/span&gt; must share the exact same definitions. This can be finicky. In this example map of Tanzania, the &lt;span class="caps"&gt;SVG&lt;/span&gt; borders are at the district level while the dataset operates in terms of regions. The dataset isn&amp;#8217;t perfect, either: it&amp;#8217;s missing Manyara and the five regions of Zanzibar. Try not to think in terms of &amp;#8220;correct&amp;#8221; and &amp;#8220;incorrect&amp;#8221;: just get things to match up. Set &lt;tt&gt;&lt;span class="caps"&gt;VERBOSE&lt;/span&gt;=true&lt;/tt&gt; in the JavaScript to display those inconsistencies.&lt;/p&gt;
&lt;p&gt;The more time you spend, the more ideal your &lt;span class="caps"&gt;SVG&lt;/span&gt; and dataset can become.&lt;/p&gt;
&lt;p&gt;Finally, if you&amp;#8217;re trying to generate a map while keeping the data behind it private, this whole system will not work. If you &amp;#8220;Print Screen&amp;#8221; to make a &lt;span class="caps"&gt;PNG&lt;/span&gt; file, though, the &lt;span class="caps"&gt;PNG&lt;/span&gt; version won&amp;#8217;t carry your data table.&lt;/p&gt;
&lt;p&gt;Because each &lt;span class="caps"&gt;SVG&lt;/span&gt; file is unique and each dataset is, too, lots of the JavaScript code here isn&amp;#8217;t portable to another project. So the last limitation of this mapping technique is its learning curve.&lt;/p&gt;
&lt;p&gt;Still, it&amp;#8217;s easier than setting up a map server.&lt;/p&gt;
&lt;h3&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;I can&amp;#8217;t think of another way to map data in such a quick and satisfying manner, either on web pages or in standalone applications. The concept and the code are simple and intuitive.&lt;/p&gt;
&lt;p&gt;Internet Explorer may prevent this kind of map from adorning many websites, but intranets everywhere can use maps like this to bring data from complicated databases into simple graphics.&lt;/p&gt;
&lt;h3&gt;Legal&lt;/h3&gt;
&lt;p&gt;I declare this code to belong to the public domain in every way possible and in every country. Use it however you want without fear of reprisals from me (though if you seek to use these ideas or any others to plan raids and bombings, I urge you, on a more personal level, to reconsider your ambitions).&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;d like me to share the code with you under a more restrictive license, send me the license file and I&amp;#8217;ll probably agree.&lt;/p&gt;</description>
      <pubDate>Tue, 27 Apr 2010 05:24:24 -0400</pubDate>
      <link>http://feedproxy.google.com/~r/AdamHoopersEngineeringTips/~3/gTK71Y4z8BI/9-svg-javascript-mapping</link>
      <guid isPermaLink="false">http://adamhooper.com/eng/articles/9-svg-javascript-mapping</guid>
    <feedburner:origLink>http://adamhooper.com/eng/articles/9-svg-javascript-mapping</feedburner:origLink></item>
    <item>
      <title>Bodacity</title>
      <description>&lt;p&gt;I&amp;#8217;ve just released some handy JavaScript toys as open-source: &lt;a href="http://adamhooper.com/bodacity"&gt;Bodacity JavaScript Utilities&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Tue, 01 Sep 2009 15:07:42 -0400</pubDate>
      <link>http://feedproxy.google.com/~r/AdamHoopersEngineeringTips/~3/m4Wd4jP2cCg/8-bodacity</link>
      <guid isPermaLink="false">http://adamhooper.com/eng/articles/8-bodacity</guid>
    <feedburner:origLink>http://adamhooper.com/eng/articles/8-bodacity</feedburner:origLink></item>
    <item>
      <title>Ruby's Object#respond_to?: String or Symbol?</title>
      <description>&lt;p&gt;When calling &lt;a href="http://ruby-doc.org/core/classes/Object.html#M000331"&gt;&lt;code&gt;Object#respond_to?&lt;/code&gt;&lt;/a&gt;, use a Symbol when you can. Ruby&amp;#8217;s internal method lookup uses &amp;#8220;IDs,&amp;#8221; which correspond to programmer-visible Symbols.&lt;/p&gt;
&lt;p&gt;To get an ID from a Symbol, the Ruby interpreter uses a bit-shift, &lt;code&gt;SYM2ID&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define SYM2ID(x) RSHIFT((unsigned long)x,8)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To get an ID from a String, on the other hand, it does this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
static ID
str_to_id(str)
    VALUE str;
{
    VALUE sym = rb_str_intern(str);&lt;/code&gt;
    
&lt;code&gt;    return SYM2ID(sym);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;#8230;and since that calls &lt;code&gt;SYM2ID&lt;/code&gt; anyway, and we assume the shortest path between two points is a straight line, your code will be more efficient if it uses a Symbol.&lt;/p&gt;
&lt;p&gt;Be wary of &lt;a href="http://www.c2.com/cgi/wiki?PrematureOptimization"&gt;premature optimization&lt;/a&gt;, too: this tip is really to justify a convention, not to encourage you to rewrite all your code.&lt;/p&gt;</description>
      <pubDate>Tue, 28 Jul 2009 15:27:41 -0400</pubDate>
      <link>http://feedproxy.google.com/~r/AdamHoopersEngineeringTips/~3/AkzT_1qnwEM/7-ruby-s-object-respond_to-string-or-symbol</link>
      <guid isPermaLink="false">http://adamhooper.com/eng/articles/7-ruby-s-object-respond_to-string-or-symbol</guid>
    <feedburner:origLink>http://adamhooper.com/eng/articles/7-ruby-s-object-respond_to-string-or-symbol</feedburner:origLink></item>
    <item>
      <title>HTML Namespacing in Rails</title>
      <description>&lt;p&gt;&lt;span class="caps"&gt;CSS&lt;/span&gt; is a nightmare. In Rails, &lt;a href="http://sass-lang.com"&gt;Sass&lt;/a&gt; makes it more bearable, but class naming is always frustrating: you must pick a convention before your project starts and follow it religiously, or your &lt;span class="caps"&gt;CSS&lt;/span&gt; rules will soon find themselves being applied way too liberally. Even with strict conventions, reality soon outpaces theory and your &lt;span class="caps"&gt;CSS&lt;/span&gt; directory becomes a mess.&lt;/p&gt;
&lt;p&gt;In Rails, the truly annoying bit is the contrast with partials: &lt;span class="caps"&gt;CSS&lt;/span&gt; is a mess, but partials are organized so nicely. Why not organize your &lt;span class="caps"&gt;CSS&lt;/span&gt; to line up with your partials?&lt;/p&gt;
&lt;p&gt;No more excuses. Here is a solution, fully functional and documented: &lt;a href="http://github.com/adamh/html_namespacing"&gt;&lt;span class="caps"&gt;HTML&lt;/span&gt; Namespacing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;HTML&lt;/span&gt; namespacing automatically annotates your &lt;span class="caps"&gt;HTML&lt;/span&gt; partials with special &lt;tt&gt;class&lt;/tt&gt; attributes, and it lets you write Sass files with rules automatically scoped to those classes. Should you feel the need, you can even scope JavaScript files to those classes.&lt;/p&gt;
&lt;p&gt;How? By using parallel directory structures. In your Rails framework, you can create a convention to tie the following files together automatically:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code&gt;app/views/foos/_foo.html.haml&lt;/code&gt;: specifies the &lt;span class="caps"&gt;HTML&lt;/span&gt;&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;app/stylesheets/views/foos/_foo.sass&lt;/code&gt;: specifies the &lt;span class="caps"&gt;CSS&lt;/span&gt;&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;app/javascripts/views/foos/_foo.js&lt;/code&gt;: specifies the JavaScript&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(I will probably be releasing a Rails plugin to ease JavaScript inclusion within the next month. And though &lt;span class="caps"&gt;HTML&lt;/span&gt; namespacing could apply to any web framework, my particular implementation is Rails-only.)&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;HTML&lt;/span&gt; namespacing is especially helpful when working in a team: nomenclature decisions are mostly handled automatically, and every developer knows where to put each &lt;span class="caps"&gt;CSS&lt;/span&gt; rule and line of JavaScript.&lt;/p&gt;
&lt;p&gt;Give &lt;a href="http://github.com/adamh/html_namespacing"&gt;&lt;span class="caps"&gt;HTML&lt;/span&gt; namespacing&lt;/a&gt; a try. It is easy to integrate into existing projects, and it can save you headaches.&lt;/p&gt;</description>
      <pubDate>Fri, 10 Jul 2009 17:53:00 -0400</pubDate>
      <link>http://feedproxy.google.com/~r/AdamHoopersEngineeringTips/~3/suoUjQkmD4g/6-html-namespacing-in-rails</link>
      <guid isPermaLink="false">http://adamhooper.com/eng/articles/6-html-namespacing-in-rails</guid>
    <feedburner:origLink>http://adamhooper.com/eng/articles/6-html-namespacing-in-rails</feedburner:origLink></item>
    <item>
      <title>Full Sentences in Rails Validation Messages</title>
      <description>&lt;p&gt;This article assumes a rudimentary understanding of &lt;a href="http://guides.rubyonrails.org/i18n.html"&gt;Rails Internationalization&lt;/a&gt; (or at least a working knowledge of Rails and a vague awareness that strings meant to be displayed to users can be stored outside of models, controllers, and views).&lt;/p&gt;
&lt;p&gt;I will explain why Rails&amp;#8217;s validation messages are incorrect, and I will provide a simple monkey-patch which can let you write less restricted validation messages. In the process, you should become more comfortable with translations and less comfortable with several Rails methods.&lt;/p&gt;
&lt;h3&gt;Rationale&lt;/h3&gt;
&lt;p&gt;There is a golden rule in i18n (&amp;#8220;internationalization&amp;#8221;): &lt;a href="http://www.gnu.org/software/gettext/manual/gettext.html#Preparing-Strings"&gt;do not use string concatenation&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Why String Concatenation Breaks&amp;#8230;&lt;/h4&gt;
&lt;p&gt;An example: I once worked on a website which lists organizations in both English and Swahili. In English, one could write, &amp;#8220;found 16 companies&amp;#8221;; in Swahili, this might translate to, &amp;#8220;masharika 16 yamekutanwa&amp;#8221; (if I studied my grammar correctly).&lt;/p&gt;
&lt;p&gt;A programmer might be tempted to write:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;s = I18n.t('Found') + ' ' + number + ' ' + I18n.t('company', :count =&amp;gt; number)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with a translations file containing&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;en:
  company:
    one: company
    other: companies
  found: found&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But in Swahili, the best this strategy could produce is, &amp;#8220;Tumeyakutana 16 masharika,&amp;#8221; which is completely out of order.&lt;/p&gt;
&lt;p&gt;Instead, our intrepid programmer should write:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;s = I18n.t('found n companies', :count =&amp;gt; number)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with a translations file containing&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;en:
  found n companies:
    zero: found no companies
    one: found one company
    other: 'found {{count}} companies'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In English, the output is identical. At first glance, one might find the translation file repetitive (and clearer to read). But the real gain is that our program can now be translated to any language (depending on the I18n backend). We can now write a Swahili version of the string (excuse my grammar if I get these wrong):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sw:
  found n companies:
    zero: masharika sifuri yamekutanwa
    one: sharika moja limekutanwa
    other: 'masharika {{count}} yamekutanwa'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Problem solved! And we learned a valuable moral: do not use string concatenation to build sentences.&lt;/p&gt;
&lt;h4&gt;&amp;#8230; Even in a Single Language&lt;/h4&gt;
&lt;p&gt;Another example: suppose an ActiveRecord validation produces an error. Rails will normally return a string such as &amp;#8220;title: can&amp;#8217;t be blank&amp;#8221;. You may want to adjust that message to, &amp;#8220;Please enter a title.&amp;#8221;&lt;/p&gt;
&lt;p&gt;ActiveRecord does not support this use case, despite the fact that ActiveRecord validations are advertised as i18n-aware. Why? Because ActiveRecord, at least in Rails 2.3, has a nasty tendency to build sentences through string concatenation: an absolute no-no.&lt;/p&gt;
&lt;p&gt;I can help you fix those validation messages.&lt;/p&gt;
&lt;h3&gt;fix_active_record_validations_full_messages.rb&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ActiveRecord::Errors&lt;/code&gt; has a method called &lt;code&gt;full_messages&lt;/code&gt; which returns a list of all error messages on an object. In Rails 2.3, &lt;code&gt;full_messages&lt;/code&gt; is the unlikely arbiter of error messages. Let us examine the original:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;...
    def full_messages(options = {})
      full_messages = [] &lt;/code&gt;

&lt;code&gt;      @errors.each_key do |attr|
        @errors[attr].each do |message|
          next unless message&lt;/code&gt;

&lt;code&gt;          if attr == "base"
            full_messages &amp;lt;&amp;lt; message
          else 
            attr_name = @base.class.human_attribute_name(attr)
            full_messages &amp;lt;&amp;lt; attr_name + I18n.t('activerecord.errors.format.separator', :default =&amp;gt; ' ') + message 
          end  
        end  
      end  
      full_messages
    end &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See the &lt;code&gt;+&lt;/code&gt; signs next to &lt;code&gt;I18n.t&lt;/code&gt;? Those are string concatenations: definitely off-limits. The funny thing is, by the time &lt;code&gt;full_messages&lt;/code&gt; is called, all those messages have already been translated (in &lt;code&gt;ActiveRecord::Errors#add&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Getting confused? That is not your fault. Suffice it to say, validations in Rails 2.3 are crufty, confusing, and ultimately incorrect.&lt;/p&gt;
&lt;p&gt;The fix is simple: just remove the fancy logic from &lt;code&gt;ActiveRecord::Errors#full_messages&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In your Rails project, put this in &lt;code&gt;config/initializers/fix_active_record_validations_full_messages.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Ensures that when we pass a :message parameter to our validations, that
# message is a sentence (and not something to be prefixed by the column
# name). Rationale: ActiveSupport::Inflector is in over its head on this
# one.
#
# So instead of:
#   validates_presence_of :name, :message =&amp;gt; 'should not be blank'
# Use:
#   validates_presence_of :name, :message =&amp;gt; 'Name should not be blank'
#
# If, however, you just use:
#   validates_presence_of :name
# The behavior will remain unchanged.
if RAILS_GEM_VERSION =~ /^2\.3/
  ActiveRecord::Errors.class_eval do
    # Remove complicated logic
    def full_messages
      returning full_messages = [] do
        @errors.each_key do |attr|
          @errors[attr].each do |msg|
            full_messages &amp;lt;&amp;lt; msg if msg 
          end 
        end 
      end 
    end 
  end 
end&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;config/locales/en.yml&lt;/h3&gt;
&lt;p&gt;The default Rails 2.3 error messages do not interpolate the attribute name: as we saw above, that is done in Rails 2.3&amp;#8217;s flawed &lt;code&gt;ActiveRecord::Errors#full_messages&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The sensible thing to do is correct the messages which, I think everyone should agree, are incorrect (because&amp;#8212;you guessed it!&amp;#8212;they are not full sentences).&lt;/p&gt;
&lt;p&gt;Write this in &lt;code&gt;config/locales/en.yml&lt;/code&gt; (or wherever your translations take place):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;en:
  activerecord:
    errors:
      messages:
        # Default messages: complete sentences
        inclusion: "{{attribute}}: is not included in the list"
        exclusion: "{{attribute}}: is reserved"
        invalid: "{{attribute}}: is invalid"
        confirmation: "{{attribute}}: doesn't match confirmation"
        accepted: "{{attribute}}: must be accepted"
        empty: "{{attribute}}: can't be empty"
        blank: "{{attribute}}: can't be blank"
        too_long: "{{attribute}}: is too long (maximum is {{count}} characters)"
        too_short: "{{attribute}} is too short (minimum is {{count}} characters)"
        wrong_length: "{{attribute}}: is the wrong length (should be {{count}} characters)" 
        taken: "{{attribute}}: has already been taken"
        not_a_number: "{{attribute}}: is not a number"
        greater_than: "{{attribute}}: must be greater than {{count}}"
        greater_than_or_equal_to: "{{attribute}}: must be greater than or equal to {{count}}"
        equal_to: "{{attribute}}: must be equal to {{count}}"
        less_than: "{{attribute}}: must be less than {{count}}"
        less_than_or_equal_to: "{{attribute}}: must be less than or equal to {{count}}"     
        odd: "{{attribute}}: must be odd"
        even: "{{attribute}}: must be even"&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are the default Rails 2.3 validation messages with the &lt;code&gt;attribute&lt;/code&gt; thrown in. With this &lt;code&gt;en.yml&lt;/code&gt; and the above-mentioned monkey-patch to &lt;code&gt;ActiveRecord::Errors#full_messages&lt;/code&gt;, Rails 2.3 will superficially behave identically to a non-monkey-patched version.&lt;/p&gt;
&lt;p&gt;(Personally, I take this opportunity to make Rails&amp;#8217;s default messages more human-friendly, by removing colons, adding periods, and rephrasing some sentences.)&lt;/p&gt;
&lt;h3&gt;Why&lt;/h3&gt;
&lt;p&gt;With our groundwork in place, Rails gets out of the way and lets us write what we want.&lt;/p&gt;
&lt;p&gt;For instance, suppose we have an &lt;code&gt;Article&lt;/code&gt; with a &lt;code&gt;:body&lt;/code&gt; and a &lt;code&gt;:title&lt;/code&gt;, both validated using &lt;code&gt;validates_presence_of&lt;/code&gt;, with an extra &lt;code&gt;validates_length_of&lt;/code&gt; on the &lt;code&gt;:title&lt;/code&gt;. We can add the following in &lt;code&gt;config/locales/en.yml&lt;/code&gt;, within the &lt;code&gt;en: { active_record: { errors: ... } }&lt;/code&gt; section (alongside &lt;code&gt;messages&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;...
      models:
        article:
          attributes:
            body:
              blank: Please enter body text for your article.
            title:
              blank: Your article is desperately seeking a title.
              too_long: "Your article's title cannot exceed {{count}} characters. Give it a trim."&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And there you have it: full sentences in validation messages.&lt;/p&gt;
&lt;h3&gt;The Last Word&lt;/h3&gt;
&lt;p&gt;I started this article under the guise of internationalization, but my actual code and use case are purely English. This is because &lt;em&gt;you should not build sentences using string concatenation&lt;/em&gt;, no matter which language you speak (or, for that matter, in which language you program).&lt;/p&gt;
&lt;p&gt;This article should help you avoid string concatenation in one part of Rails 2.3; there are many other errors in Rails 2.3 and perhaps even in your own code, and I hope you feel better-equipped to remedy those problems as well.&lt;/p&gt;
&lt;h3&gt;Appendix: Other Confusing Rails Methods&lt;/h3&gt;
&lt;p&gt;Here is a list of Rails methods which you should think before using:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code&gt;ActiveSupport::Inflector#titleize&lt;/code&gt; (Never use this: it does not translate, and even in English it will fail you every time. Even the string used as an example in the method&amp;#8217;s documentation, &amp;#8220;Man From The Boondocks,&amp;#8221; is titleized incorrectly by this method.)&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;ActiveSupport::Inflector#humanize&lt;/code&gt; (Never use this unless you are rewriting Rails&amp;#8217;s internals. Maybe you want &lt;code&gt;ActiveRecord::Base#human_attribute_name&lt;/code&gt; and &lt;code&gt;ActiveRecord::Base#human_name&lt;/code&gt;?)&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;ActiveSupport::Inflector#ordinalize&lt;/code&gt; (If you mean to translate your application to non-English and you use &lt;code&gt;ordinalize&lt;/code&gt;, you will need to override this method or define a replacement.)&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;ActiveSupport::CoreExtensions::String::Inflector#singularize&lt;/code&gt; and &lt;code&gt;ActiveSupport::Inflector#singularize&lt;/code&gt; (Never &lt;code&gt;singularize&lt;/code&gt; user-input strings, but feel free to &lt;code&gt;singularize&lt;/code&gt; hard-coded strings such as class names and method names.)&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;ActiveSupport::CoreExtensions::String::Inflector#pluralize&lt;/code&gt; and &lt;code&gt;ActiveSupport::Inflector#pluralize&lt;/code&gt; (Never &lt;code&gt;pluralize&lt;/code&gt; user-input strings, but feel free to &lt;code&gt;pluralize&lt;/code&gt; hard-coded strings such as class names and method names.)&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;I18n::Backend::Simple#pluralize&lt;/code&gt; (The &lt;code&gt;Simple&lt;/code&gt; I18n backend is only correct for English and a few other languages (by happenstance)&amp;#8212;pluralization rules are different in different languages. If you later translate to a language with more complex pluralization rules than &amp;#8220;zero/one/many&amp;#8221;, you will need to replace the I18n backend for a more complete &lt;code&gt;pluralize&lt;/code&gt; method, but you will not need to change the files which &lt;em&gt;called&lt;/em&gt; &lt;code&gt;pluralize&lt;/code&gt;.)&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;ActionView::Helpers::TextHelper#pluralize&lt;/code&gt; (This is the helper you can call from your views. Never use it: it does not translate to other languages, and even in English you should be writing special strings instead of displaying &amp;#8220;0&amp;#8221; and &amp;#8220;1&amp;#8221;, as a matter of style.)&lt;/li&gt;
&lt;/ul&gt;</description>
      <pubDate>Thu, 09 Jul 2009 11:35:21 -0400</pubDate>
      <link>http://feedproxy.google.com/~r/AdamHoopersEngineeringTips/~3/wur6sDpsAKU/5-full-sentences-in-rails-validation-messages</link>
      <guid isPermaLink="false">http://adamhooper.com/eng/articles/5-full-sentences-in-rails-validation-messages</guid>
    <feedburner:origLink>http://adamhooper.com/eng/articles/5-full-sentences-in-rails-validation-messages</feedburner:origLink></item>
    <item>
      <title>HTML Rendering Tests with Rails</title>
      <description>&lt;h3&gt;The Problem&lt;/h3&gt;
&lt;p&gt;Picture the situation: 3pm on deploy day, after fully testing that your Rails application outputs the proper &lt;span class="caps"&gt;HTML&lt;/span&gt;, you run Capistrano and bring up the newest version of your beautiful website. An uneventful half-hour passes, after which you are told by your &lt;span class="caps"&gt;CEO&lt;/span&gt; that in Internet Explorer 7, a certain component of your web page is misaligned by 20px. While investigating further, you find your supposedly-vertical lists of checkboxes are being rendered in a diagonal line by Internet Explorer 6. Your deploy has transformed your once-perfect website into an unprofessional disaster.&lt;/p&gt;
&lt;p&gt;You blow off dinner plans, fix bugs, validate all other pages on all major browsers, and finally, around 8pm, relax. At this point, the question is not so much, &amp;#8220;why?&amp;#8221; as it is, &amp;#8220;how can we make sure this never &lt;em&gt;ever&lt;/em&gt; happens again?&amp;#8221;&lt;/p&gt;
&lt;p&gt;Testing manually is simply too time-consuming (especially if you support many browsers). You want to go The Rails Way and automate your tests. How can you automatically render your &lt;span class="caps"&gt;HTML&lt;/span&gt; on various browsers and ensure the output looks right?&lt;/p&gt;
&lt;h3&gt;Assumptions&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;You are using Ruby on Rails.&lt;/li&gt;
	&lt;li&gt;You want to verify every pixel on every browser/platform in your tests.&lt;/li&gt;
	&lt;li&gt;You want your test to fail if any pixel on any browser/platform changes from what you verified.&lt;/li&gt;
	&lt;li&gt;Different browsers/platforms will very rarely produce exact, pixel-by-pixel matches, and the differences should be validated manually.&lt;/li&gt;
	&lt;li&gt;You do not mind if the &lt;span class="caps"&gt;HTML&lt;/span&gt; structure changes, so long as the rendered output does not change.&lt;/li&gt;
	&lt;li&gt;Tests should be easy to write and edit.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Overview&lt;/h3&gt;
&lt;p&gt;We need a test framework which can render any Rails partial or template using any browser/platform. Though we only intend to test our own templates, we are actually implicitly forced to test our models and the browsers we support, as shown in this diagram:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://adamhooper.com/images/eng/html-rendering-tests-request-lifecycle.png" title="HTML Request Lifecycle" alt="HTML Request Lifecycle" /&gt;&lt;/p&gt;
&lt;h3&gt;Implementation&lt;/h3&gt;
&lt;p&gt;The trickiest part of implementation is finding a way to automatically gather web browser output. This is especially difficult because:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Web browsers were historically not designed to render off-screen, so &amp;#8220;render a &lt;span class="caps"&gt;PNG&lt;/span&gt; of a web page&amp;#8221; is generally a difficult task involving very different code on each browser.&lt;/li&gt;
	&lt;li&gt;Rendering is a very slow operation, compared to, say, parsing an &lt;span class="caps"&gt;HTML&lt;/span&gt; document&amp;#8217;s &lt;span class="caps"&gt;DOM&lt;/span&gt; structure.&lt;/li&gt;
	&lt;li&gt;Some web browsers are mutually exclusive (for instance, Internet Explorer 7 and Internet Explorer 8) and cannot usually appear on the same computer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A distributed testing environment is necessary, either through physical or virtual machines. It will look something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://adamhooper.com/images/eng/html-rendering-tests-distributed-layout.png" title="HTML Rendering Distributed Computing Layout" alt="HTML Rendering Distributed Computing Layout" /&gt;&lt;/p&gt;
&lt;p&gt;This leaves plenty of programs for us to write:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;The &amp;#8220;Test Runner&amp;#8221;&lt;/li&gt;
	&lt;li&gt;The &amp;#8220;Render Server&amp;#8221;&lt;/li&gt;
	&lt;li&gt;The &amp;#8220;Thumbnailing Program&amp;#8221; (which is different for each browser/platform)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Render Server, Thumbnailing Programs&lt;/h4&gt;
&lt;p&gt;I have decided to bundle (platform-specific) thumbnailing programs with the (cross-platform) render server. The project is hosted on GitHub: &lt;a href="http://github.com/adamh/html_to_png_server"&gt;html_to_png_server&lt;/a&gt;, which includes compiled versions but currently only handles IE6/7 on Windows and Firefox 3 on &lt;span class="caps"&gt;GNOME&lt;/span&gt;. (New thumbnailing programs, while trivial to plug into the render server code, are difficult to implement.)&lt;/p&gt;
&lt;p&gt;The server accepts a &lt;span class="caps"&gt;POST&lt;/span&gt; request with &lt;span class="caps"&gt;HTML&lt;/span&gt; data, and it returns a &lt;span class="caps"&gt;PNG&lt;/span&gt; response. The &lt;span class="caps"&gt;URL&lt;/span&gt; determines which thumbnailing program (i.e., browser) to use for rendering. What a wondrous black box!&lt;/p&gt;
&lt;p&gt;Visit &lt;a href="http://github.com/adamh/html_to_png_server"&gt;http://github.com/adamh/html_to_png_server&lt;/a&gt; for details.&lt;/p&gt;
&lt;h4&gt;Test Runner&lt;/h4&gt;
&lt;p&gt;The test runner is written in Ruby and hosted on GitHub: &lt;a href="http://github.com/adamh/html_render"&gt;html_render&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It contains a Rails-agnostic component, which is simply a client to the Render Server, and a Rails-specific testing component.&lt;/p&gt;
&lt;p&gt;Visit &lt;a href="http://github.com/adamh/html_render"&gt;http://github.com/adamh/html_render&lt;/a&gt; for details.&lt;/p&gt;
&lt;h3&gt;Your Render Test Framework&lt;/h3&gt;
&lt;p&gt;The idea is for Rails rendering tests to be as simple as possible to write, while incorporating your own application&amp;#8217;s specific needs. You, the test automation engineer, will have two tasks:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Application-specific (or, if you feel creative, sub-application-specific) &lt;span class="caps"&gt;HTML&lt;/span&gt;-preparation and render server configuration: your project-specific framework.&lt;/li&gt;
	&lt;li&gt;Actual tests which use your framework.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Your Project-Specific Framework&lt;/h4&gt;
&lt;p&gt;Here is a sample framework, in which we have decided the following:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Tests are stored in &lt;code&gt;RAILS_ROOT/test/render/&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;Tests are invoked by running &lt;code&gt;ruby test/render_test.rb&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;You will render on &lt;code&gt;winxp-ie6.local&lt;/code&gt;, &lt;code&gt;winxp-ie7.local&lt;/code&gt;, and &lt;code&gt;ubuntu-ff3.local&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Create the file &lt;code&gt;test/render_test.rb&lt;/code&gt; with the following contents:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;require File.dirname(__FILE__) + '/test_helper'&lt;/code&gt;

&lt;code&gt;require 'html_render/render_test/rails'&lt;/code&gt;

&lt;code&gt;class RenderTest &amp;lt; ActiveSupport::TestCase
  extend HTMLRender::RenderTest::Rails::TestCaseDefinitions&lt;/code&gt;

&lt;code&gt;  PREFIX = File.join(File.dirname(__FILE__), 'render')
  SERVERS = {
    :winxp_ie6 =&amp;gt; 'http://winxp-ie6.local:20558/ie',
    :winxp_ie7 =&amp;gt; 'http://winxp-ie7.local:20558/ie',
    :ubuntu_ff3 =&amp;gt; 'http://linux-ff3.local:20558/ff3_linux'
  }&lt;/code&gt;

&lt;code&gt;  class ApplicationRenderTest &amp;lt; HTMLRender::RenderTest::Rails::RenderTest
    def css
      @css ||= File.open(File.join(File.dirname(__FILE__), '..', 'public', 'stylesheets', 'application.css')) { |f| f.read }
    end&lt;/code&gt;

&lt;code&gt;    def javascript
      '' # We're not ambitious enough to test our JavaScript just yet
    end
  end&lt;/code&gt;

&lt;code&gt;  define_tests(PREFIX, :servers =&amp;gt; SERVERS, :base_class =&amp;gt; ApplicationRenderTest)
end&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Your Tests&lt;/h4&gt;
&lt;p&gt;To test the partial &lt;code&gt;/foos/_foo.html.erb&lt;/code&gt; (or &lt;code&gt;.haml&lt;/code&gt;), create the file &lt;code&gt;test/render/foos/_foo/basic/setup.rb&lt;/code&gt; with the following contents:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def locals
  { foo: Foo.new }
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Yes, that is the entire test.)&lt;/p&gt;
&lt;p&gt;Here, we name the test &lt;code&gt;basic&lt;/code&gt;: we may want to create several tests of the same partial, and for those we would replace &amp;#8220;basic&amp;#8221; with something else. The path, &lt;code&gt;foos/_foo&lt;/code&gt;, implicitly specifies that we mean to test &lt;code&gt;app/views/foos/_foo.html.erb&lt;/code&gt; (or &lt;code&gt;.haml&lt;/code&gt;, as the case may be). Tests may define &lt;code&gt;locals&lt;/code&gt;, &lt;code&gt;assigns&lt;/code&gt;, &lt;code&gt;css&lt;/code&gt;, &lt;code&gt;javascript&lt;/code&gt;, or any helper methods deemed necessary. Behind the scenes, the contents of &lt;code&gt;setup.rb&lt;/code&gt; will be appended to a subclass of &lt;code&gt;ApplicationRenderTest&lt;/code&gt;. Any methods, up to and including &lt;code&gt;html&lt;/code&gt; (the only method the testing framework actually uses) may be overridden.&lt;/p&gt;
&lt;h3&gt;Running Your Test Suite&lt;/h3&gt;
&lt;p&gt;First, download &lt;a href="http://github.com/adamh/html_to_png_server"&gt;html_to_png_server&lt;/a&gt; (specifically, &lt;code&gt;html_to_png_server.jar&lt;/code&gt;) to each rendering machine and start the server on each machine:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;java -jar html_to_png_server.jar&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, on the test runner computer (which holds your Rails project code), install the &lt;a href="http://github.com/adamh/html_render"&gt;html_render&lt;/a&gt; gem:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo rubygems install adamh-html_render --source http://gems.github.com&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can run the test suite from your Rails project directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ruby test/render_test.rb&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(This command could just as easily be made into a Rake task, of course, and included as part of &lt;code&gt;rake test&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;All your render tests will fail: the expected images have not been defined yet! But each render has actually taken place, and the results are now viewable.&lt;/p&gt;
&lt;h4&gt;Making Tests Pass&lt;/h4&gt;
&lt;p&gt;Look in &lt;code&gt;test/render/foos/_foo/basic&lt;/code&gt;. Where before you only had &lt;code&gt;setup.rb&lt;/code&gt;, you will now have a &lt;code&gt;run/&lt;/code&gt; subdirectory with &lt;code&gt;html.html&lt;/code&gt;, &lt;code&gt;winxp_ie6.png&lt;/code&gt;, &lt;code&gt;winxp_ie7.png&lt;/code&gt;, and &lt;code&gt;ubuntu_ff3.png&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here is the manual validation part: make sure each of those images looks the way you want it. If it does not, you will need to fix the partial (or, if you made a mistake settings up the framework, you will need to fix the stylesheets you are supplying in &lt;code&gt;ApplicationRenderTest&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Assuming the images look correct, you can set the expected value. Choose the best-looking image and copy it to &lt;code&gt;canonical.png&lt;/code&gt; (in the same directory as &lt;code&gt;setup.rb&lt;/code&gt;). Now, should you add any new servers to your testing framework, their output will be compared pixel-by-pixel with the image in &lt;code&gt;canonical.png&lt;/code&gt; (and the test will fail if the pixels do not match). Typically, &lt;code&gt;canonical.png&lt;/code&gt; will be chosen from a Firefox server. (Not that I&amp;#8217;m opinionated against Internet Explorer or anything.)&lt;/p&gt;
&lt;p&gt;Now, if you run your test again, you will probably find that it still fails. While your &lt;code&gt;ubuntu_ff3.png&lt;/code&gt; matches &lt;code&gt;canonical.png&lt;/code&gt; as expected, the Internet Explorer images are not identical to &lt;code&gt;canonical.png&lt;/code&gt; and so they trigger the failure. This failure is expected and healthy. To mark the images as valid, create a &lt;code&gt;valid/&lt;/code&gt; subdirectory in the same directory as &lt;code&gt;setup.rb&lt;/code&gt; and copy &lt;code&gt;winxp_ie6.png&lt;/code&gt; and &lt;code&gt;winxp_ie7.png&lt;/code&gt; into it.&lt;/p&gt;
&lt;p&gt;In the end, your single test&amp;#8217;s folder will look like this:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code&gt;test/render/foos/_foo/basic/setup.rb&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;test/render/foos/_foo/basic/run/html.html&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;test/render/foos/_foo/basic/run/ubuntu_ff3.png&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;test/render/foos/_foo/basic/run/winxp_ie6.png&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;test/render/foos/_foo/basic/run/winxp_ie7.png&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;test/render/foos/_foo/basic/canonical.png&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;test/render/foos/_foo/basic/valid/winxp_ie6.png&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;test/render/foos/_foo/basic/valid/winxp_ie7.png&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;#8230;and your test will pass. (Until you change the partial, that is: try editing it and watch the test fail!)&lt;/p&gt;
&lt;h4&gt;Test Policy Considerations&lt;/h4&gt;
&lt;p&gt;When adding your tests to version control, ignore the &lt;code&gt;run/&lt;/code&gt; subdirectories: the test only really comprises &lt;code&gt;setup.rb&lt;/code&gt;, &lt;code&gt;canonical.png&lt;/code&gt;, and the images within &lt;code&gt;valid/&lt;/code&gt;. (The &lt;code&gt;run/&lt;/code&gt; directory can be considered a temporary directory, though it is useful enough that the &lt;code&gt;html_render&lt;/code&gt; test framework leaves it behind.)&lt;/p&gt;
&lt;p&gt;You may also choose to never set a &lt;code&gt;canonical.png&lt;/code&gt;. I chose to include it because it seems idealogically appropriate and it will help when developing a nice user interface for the test runner.&lt;/p&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;You now have a test framework which validates, with pixel-perfection, the way your partial/template is output from major web browsers. It can work at any detail level, from a single input box to your whole home page. You can also write tests of your &lt;span class="caps"&gt;HTML&lt;/span&gt;-producing helpers, independently of any actual templates (by overriding &lt;code&gt;html&lt;/code&gt; in the test&amp;#8217;s &lt;code&gt;setup.rb&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The system is somewhat fragile: all render servers must be running html_to_png_server.&lt;/p&gt;
&lt;p&gt;The system is somewhat slow: your render test suite will take approximately as long to run as it takes your slowest render server to render each partial.&lt;/p&gt;
&lt;p&gt;But despite the system&amp;#8217;s faults, you now confidently catch those irksome 20px Internet Explorer hasLayout issues before your boss does.&lt;/p&gt;
&lt;h3&gt;Looking Forward&lt;/h3&gt;
&lt;p&gt;This system is currently prototypical. Here, I discuss some problems I intend to solve.&lt;/p&gt;
&lt;p&gt;The test suite is difficult to manage, with its masses of images in several directories. A graphical front-end to this large directory structure is a huge priority and should make the process of fixing tests much easier.&lt;/p&gt;
&lt;p&gt;More challenging, the process of writing a test is not as seamless as it could be. I envision a point-and-click interface running on top of the actual Rails application, though of course this is easier said than done.&lt;/p&gt;</description>
      <pubDate>Thu, 16 Apr 2009 15:06:22 -0400</pubDate>
      <link>http://feedproxy.google.com/~r/AdamHoopersEngineeringTips/~3/0CrN1kL6o3U/4-html-rendering-tests-with-rails</link>
      <guid isPermaLink="false">http://adamhooper.com/eng/articles/4-html-rendering-tests-with-rails</guid>
    <feedburner:origLink>http://adamhooper.com/eng/articles/4-html-rendering-tests-with-rails</feedburner:origLink></item>
    <item>
      <title>Optimizing jQuery-based JavaScript</title>
      <description>&lt;p&gt;I write this article as much for internal use as for others. Its target audience is competent jQuery users who need their code to run faster.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jquery.com"&gt;jQuery&lt;/a&gt; is fantastic, but its documentation does little to distinguish best practices from worst. In particular, jQuery supports very-slow and very-fast ways to do the same thing. What follows is a collection of my notes during two days dedicated to optimizing a large, JavaScript-intensive website.&lt;/p&gt;
&lt;h3&gt;Before You Begin&lt;/h3&gt;
&lt;p&gt;Before you begin to optimize, &lt;em&gt;do not optimize&lt;/em&gt;. Only optimize:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;When somebody complains;&lt;/li&gt;
	&lt;li&gt;When you think somebody is only not-complaining out of courtesy;&lt;/li&gt;
	&lt;li&gt;When you suspect somebody is about to complain; or&lt;/li&gt;
	&lt;li&gt;When you have implemented all functionality and you want additional bragging rights&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Why? Because optimizing early is a waste of time: you are not able to guess where the bottlenecks are. Trust me.&lt;/p&gt;
&lt;p&gt;Also, bottlenecks may be slightly different on different web browsers.&lt;/p&gt;
&lt;p&gt;Okay, disclaimer out of the way, here is where your bottlenecks may be.&lt;/p&gt;
&lt;h3&gt;Step 1: Define Metrics and Targets&lt;/h3&gt;
&lt;p&gt;Do not optimize until you have metrics and targets. A fair metric is, for instance, &amp;#8220;how long it takes &lt;code&gt;$(document).ready()&lt;/code&gt; to run, in milliseconds.&amp;#8221; A fair target depends on your worldview; 50ms might sound good for some pages. Different projects or optimization sessions may call for different metrics. Every metric needs a unit (usually &amp;#8220;milliseconds&amp;#8221;).&lt;/p&gt;
&lt;p&gt;In our particular project, we invoke &lt;code&gt;$(document).ready()&lt;/code&gt; in two places: either from an &amp;#8220;initializer JavaScript&amp;#8221; file or in &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags. Any code we run is within &lt;code&gt;$(document).ready()&lt;/code&gt;. Since each file implements a different feature, we decided to measure each file&amp;#8217;s delay separately so we could determine, feature-by-feature, whether to optimize or scrap our functionality.&lt;/p&gt;
&lt;h3&gt;Step 2: Measure&lt;/h3&gt;
&lt;p&gt;Design some way to quickly measure all your metrics.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://getfirebug.com"&gt;Firebug&lt;/a&gt; has a very useful &amp;#8220;Profile&amp;#8221; mode which is simple enough to use. We wrapped our code like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
if (window.console) window.console.profile("path/to/file.js");
[ ... code to profile ... ]
if (window.console) window.console.profileEnd();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For non-Firefox browsers, we added some pseudo-profiling code which will &lt;code&gt;alert()&lt;/code&gt; a list of metrics after the page has loaded. This lets us at least verify our work, though it does not help with the actual work like Firebug&amp;#8217;s profiling data does.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
if (!window.console || !window.console.profile) {
  window.PROFILES = [];
  window.console = {
    profile: function(name) {
      window.PROFILE_NAME = name;
      window.PROFILE_TIME = new Date();
    },
    profileEnd: function() {
      window.PROFILES.push(window.PROFILE_NAME + ': ' + (new Date() - window.PROFILE_TIME) + 'ms');
    }
  };
  $(window).load(function() {
    // Ensure we finish $(document).ready() first....
    window.setTimeout(function() {
      if (window.PROFILES.length) {
        alert("Profiled:\n\n" + window.PROFILES.join("\n"));
      }
    }, 100);
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 3: Profile&lt;/h3&gt;
&lt;p&gt;For our particular website, because we use Firebug for our metrics anyway, profiling in Firefox amounts to loading our web page. We can see which functions are slow, which functions are called too many times, and which functions are not worth investigating.&lt;/p&gt;
&lt;p&gt;The slowdowns generally fall into some basic categories, and the solutions are often transferrable site-wide.&lt;/p&gt;
&lt;h3&gt;Step 4: Optimize&lt;/h3&gt;
&lt;h4&gt;Speed Up jQuery Selectors&lt;/h4&gt;
&lt;p&gt;Selectors come in varying flavours:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;ID-based selectors, such as &lt;code&gt;$("#foo")&lt;/code&gt;. These are blindingly fast.&lt;/li&gt;
	&lt;li&gt;Element-based selectors, such as &lt;code&gt;$("input")&lt;/code&gt;. These are fast, but slower depending on what element you use. &lt;code&gt;$("label")&lt;/code&gt; will probably be faster than $&lt;code&gt;("div")&lt;/code&gt; if you use more @div@s than @label@s.&lt;/li&gt;
	&lt;li&gt;Class-based selectors, such as &lt;code&gt;$(".foo")&lt;/code&gt;. These are less fast but more versatile.&lt;/li&gt;
	&lt;li&gt;Attribute-based selectors, such as &lt;code&gt;$("[name=bar]")&lt;/code&gt;, and virtual selectors, such as &lt;code&gt;$(':input')&lt;/code&gt;. These are painfully slow.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If &lt;code&gt;jQuery.find()&lt;/code&gt; is dragging you down, your first order of business is to move your selectors up the above list. If you are using &lt;code&gt;$(":input")&lt;/code&gt; on your entire site during page load, the browser needs to walk the entire &lt;span class="caps"&gt;DOM&lt;/span&gt; tree; change that to an element-based selector such as &lt;code&gt;$("button, input, select, textarea")&lt;/code&gt; and the search will be an order of magnitude faster. If you are dealing with a unique element on the page, consider giving it an ID and selecting with that.&lt;/p&gt;
&lt;p&gt;It is worth explicitly mentioning: avoid writing selectors without either element names or IDs. In other words, never use &lt;code&gt;$(".foo")&lt;/code&gt; when you can write &lt;code&gt;$("div.foo")&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;These rules apply for compound selectors, too. For instance, rewrite &lt;code&gt;$(".foo .bar")&lt;/code&gt; as &lt;code&gt;$("div.foo div.bar")&lt;/code&gt;.&lt;/p&gt;
&lt;h5&gt;Remove Selectors&lt;/h5&gt;
&lt;p&gt;If your selectors are still too slow, you may be able to remove them altogether. For instance, if we start with code like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$('div.geo').each(function() {
  var $geo = $(this);
  var lat = parseFloat($geo.children('div.latitude').text());
  var lng = parseFloat($geo.children('div.longitude').text());
  var image_path = $geo.children('div.image_path').text();
  // ...
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because there were so many calls to &lt;code&gt;$geo.children()&lt;/code&gt; in our website (with maybe 75 &lt;code&gt;$geo&lt;/code&gt; elements on a page), these selectors were excessively slow. The rewrite was not too painful:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$('div.geo').each(function() {
  var $geo = $(this);
  var lat, lng, image_path;
  $geo.children().each(function() {
    var c = this.className;
    if (/\blatitude\b/.test(c)) {
      lat = parseFloat($(this).text());
    } else if (/\blongitude\n/.test(c)) {
      lng = parseFloat($(this).text());
    } else if (/\bimage_path\b/.test(c)) {
      image_path = $(this).text();
    }
  });
  // ...
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By eliminating all the excess selectors, we sped up our code by 40%.&lt;/p&gt;
&lt;p&gt;Notice, by the way, an interesting tidbit from the above code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$element.hasClass('foo'); // slow
/\bfoo\b/.test($element[0].className); // fast&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This optimization works when hasClass() is being called very frequently on a jQuery object which we are sure holds exactly one &lt;span class="caps"&gt;DOM&lt;/span&gt; element.&lt;/p&gt;
&lt;h5&gt;Cache Selectors&lt;/h5&gt;
&lt;p&gt;If you have code like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$('div.foo div.bar div.baz1').something();
$('div.foo div.bar div.baz2').somethingElse();
$('div.foo div.bar div.baz3').somethingElseEntirely();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Consider rewriting to this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
var $bar = $('div.foo div.bar');
$bar.find('div.baz1').something();
$bar.find('div.baz2').somethingElse();
$bar.find('div.baz3').somethingElseEntirely();&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Add Redundant Selector Elements&lt;/h5&gt;
&lt;p&gt;Imagine your page is divided into &lt;code&gt;#header&lt;/code&gt;, &lt;code&gt;#page&lt;/code&gt;, and &lt;code&gt;#footer&lt;/code&gt;. Also, imagine &lt;code&gt;#header&lt;/code&gt; contains a significant portion of the website&amp;#8212;maybe 1/5 of it. Then, changing &lt;code&gt;$("div.foo")&lt;/code&gt; to &lt;code&gt;$("#page div.foo")&lt;/code&gt; may, in some cases, give you a 20% speed boost. Check this with your profiler. (Hopefully you&amp;#8217;re scraping the bottom of the barrel at this point, as we are talking of 1ms-2ms differences here&amp;#8230;.)&lt;/p&gt;
&lt;h5&gt;Upgrade to jQuery 1.3&lt;/h5&gt;
&lt;p&gt;While jQuery 1.3 has some regressions compared with 1.2.6, it boasts a rewritten selector engine with a noticeable performance improvement. The above rules almost certainly apply anyway.&lt;/p&gt;
&lt;h4&gt;Avoid &lt;span class="caps"&gt;DOM&lt;/span&gt; Manipulation&lt;/h4&gt;
&lt;p&gt;&lt;span class="caps"&gt;DOM&lt;/span&gt; manipulation is slow. For instance, imagine a calendar written like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
var $calendar = $('&amp;lt;table&amp;gt;&amp;lt;/table&amp;gt;');
for (var i = 0; i &amp;lt; 6; i++) {
  var $tr = $('&amp;lt;tr&amp;gt;&amp;lt;/tr&amp;gt;');
  for (var j = 0; j &amp;lt; 7; j++) {
    $tr.append('&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;');
  }
  $calendar.append($tr);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, it&amp;#8217;s pretty, but it is impractical. You will have to rewrite it to something more pragmatic, such as:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
var $calendar = $('&amp;lt;table&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;td&amp;gt;...&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;...&amp;lt;/tr&amp;gt;...&amp;lt;/table&amp;gt;');&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;(Except When Escaping User Input)&lt;/h5&gt;
&lt;p&gt;In general, a good portion of &lt;span class="caps"&gt;DOM&lt;/span&gt; manipulation can be circumvented. The speedups are enormous, but make sure you escape user-supplied &lt;span class="caps"&gt;HTML&lt;/span&gt; portions through jQuery&amp;#8217;s interfaces.&lt;/p&gt;
&lt;p&gt;That is, do not do this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
var $calendar = $('&amp;lt;table class="' + $('input.calendar_name').text() + '"&amp;gt;&amp;lt;tr&amp;gt;...&amp;lt;/table&amp;gt;');&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;#8230;because a quotation mark in the &lt;code&gt;$('input.calendar_name').text()&lt;/code&gt; would lead to unpredictable results. Use jQuery&amp;#8217;s &lt;code&gt;attr()&lt;/code&gt;, &lt;code&gt;text()&lt;/code&gt;, and &lt;code&gt;html()&lt;/code&gt; when dealing with strings which may be partially or fully constructed by your end user&amp;#8217;s input.&lt;/p&gt;
&lt;h5&gt;Push Static Stuff to the Server Side&lt;/h5&gt;
&lt;p&gt;We use &lt;a href="http://en.wikipedia.org/wiki/HTML#Semantic_HTML"&gt;semantic &lt;span class="caps"&gt;HTML&lt;/span&gt;&lt;/a&gt; on our particular website, so it seems horrendous to us to write multi-column lists on the server side. Idealists as we were, we served our navigation menus in pristene &lt;span class="caps"&gt;HTML&lt;/span&gt; &lt;code&gt;ul&lt;/code&gt; elements, relying upon a jQuery plugin to split the menus into smaller sub-lists as they were received by the client. Our profiler reported this splitting code as taking 130ms (&lt;span class="caps"&gt;DOM&lt;/span&gt; manipulations are expensive). We caved and split the menus on the server side, removing all that JavaScript altogether.&lt;/p&gt;
&lt;h4&gt;Manipulate the &lt;span class="caps"&gt;DOM&lt;/span&gt; Directly&lt;/h4&gt;
&lt;p&gt;jQuery&amp;#8217;s &lt;span class="caps"&gt;DOM&lt;/span&gt; manipulation methods are brilliant, but they present some overhead. For instance, imagine an unoptimized method to create an &amp;#8220;excerpt&amp;#8221; of text which fits within a certain bounds, working something like this (untested) jQuery plugin:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
jQuery.fn.excerpt = function(max_height) {
  return $(this).each(function() {
    var $e = $(this);
    var words = $e.text().split('\s*');
    var longest_fitting_string = '';
    var cur_string = '';
    for (var i = 0; i &amp;lt; words.length; i++) {
      cur_string = cur_string + ' ' + words[i];
      $e.text(cur_string);
      if ($e.height() &amp;gt; max_height) {
        break;
      } else {
        longest_fitting_string = cur_string;
      }
    }
    $e.text(longest_fitting_string);
  });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method is terribly slow: in particular, &lt;code&gt;$e.text()&lt;/code&gt; and &lt;code&gt;$e.height()&lt;/code&gt; eat up too much time, as they are called dozens or hundreds of times in the loop.&lt;/p&gt;
&lt;p&gt;Because we know there is exactly one element in our inner loop and we assume the element only contains text, we can replace &lt;code&gt;$e.text(cur_string)&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$e[0].firstChild.nodeValue = cur_string;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Likewise, we can use similar assumptions to transform &lt;code&gt;$e.height() &amp;gt; max_height&lt;/code&gt; into the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$e[0].offsetHeight &amp;gt; max_height&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These will make the &lt;code&gt;excerpt()&lt;/code&gt; function many times faster than before (but, frankly, still not enough to use in a production website).&lt;/p&gt;
&lt;p&gt;Many aspects of jQuery are surprisingly slow. &lt;code&gt;css()&lt;/code&gt;, &lt;code&gt;show()&lt;/code&gt;, &lt;code&gt;hide()&lt;/code&gt;, and &lt;code&gt;hasClass()&lt;/code&gt; spring to mind immediately, as they are so easy to replicate with similar, yet much faster, behaviour. If you are calling jQuery methods in a loop your profiler highlights, consider using direct &lt;span class="caps"&gt;DOM&lt;/span&gt; manipulation, as long as you are certain you are not relying upon some of the wonderful guarantees jQuery gives you.&lt;/p&gt;
&lt;h4&gt;Reimplement Algorithms&lt;/h4&gt;
&lt;p&gt;Computer science geeks excel here. Using the above &lt;code&gt;excerpt()&lt;/code&gt; code as a typical example, we can generally say that many algorithms are slower than they need to be: not because of their tiny details (such as using &lt;code&gt;$e.text()&lt;/code&gt; instead of &lt;code&gt;$e[0].firstChild.nodeValue&lt;/code&gt;), but because they go about things the wrong way.&lt;/p&gt;
&lt;p&gt;I left this optimization for the end for two reasons: it is often time-consuming; and my readership is likely divided between the camp who have done this already and the camp who do not know how. Basically, this is the &amp;#8220;computer science&amp;#8221; aspect of JavaScript coding.&lt;/p&gt;
&lt;p&gt;Improving the &lt;a href="http://en.wikipedia.org/wiki/Running_time"&gt;running time&lt;/a&gt; of algorithms can be fun, and the profiler will drop hints when it is necessary. For instance, the profiler will show us that &lt;code&gt;excerpt()&lt;/code&gt; is still too slow. A savvy computer scientist may then rewrite it to use a more clever heuristic, possibly based on a binary search, to eliminate 90% of the calls to &lt;code&gt;$e.text()&lt;/code&gt; in typical calls to &lt;code&gt;excerpt()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Optimizing in this manner is often (usually?) more effective than circumventing jQuery, and it leaves legible code. It works particularly well if you coded using &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development"&gt;Test-driven development&lt;/a&gt; since rewriting algorithms will often break them.&lt;/p&gt;
&lt;h3&gt;Step 5: Test&lt;/h3&gt;
&lt;p&gt;Ensure you did not break anything. There are many, many things you could have broken. Hopefully you unit-test your code&amp;#8230;.&lt;/p&gt;
&lt;h3&gt;Step 6: Repeat&lt;/h3&gt;
&lt;p&gt;Engineering is all about steps. Depending on your allocated time, shifting priorities, and mood, after optimizing you should return to one of the following steps:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Return to Step 5 (Test)&lt;/li&gt;
	&lt;li&gt;Return to Step 4 (Optimize)&lt;/li&gt;
	&lt;li&gt;Return to Step 3 (Profile)&lt;/li&gt;
	&lt;li&gt;Return to Step 2 (Measure)&lt;/li&gt;
	&lt;li&gt;Return to Step 1 (Define Metrics and Targets)&lt;/li&gt;
	&lt;li&gt;Return to Step 0 (Do Not Optimize)&lt;/li&gt;
&lt;/ul&gt;</description>
      <pubDate>Tue, 10 Feb 2009 18:48:10 -0500</pubDate>
      <link>http://feedproxy.google.com/~r/AdamHoopersEngineeringTips/~3/U2tPwuvfpfE/3-optimizing-jquery-based-javascript</link>
      <guid isPermaLink="false">http://adamhooper.com/eng/articles/3-optimizing-jquery-based-javascript</guid>
    <feedburner:origLink>http://adamhooper.com/eng/articles/3-optimizing-jquery-based-javascript</feedburner:origLink></item>
    <item>
      <title>Working With and Around FormBuilder</title>
      <description>&lt;p&gt;My current work is entirely focused on &lt;a href="http://rubyonrails.org"&gt;Ruby on Rails&lt;/a&gt;; and once you put Rails into focus, a lot of well-intended, badly-implemented designs peer back at you. Today&amp;#8217;s topic: &lt;code&gt;FormBuilder&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;FormBuilder&lt;/code&gt; is a class most Rails users use transparently and most Rails users do not realize exists. It is the class of object returned by the &lt;code&gt;form_for()&lt;/code&gt; and &lt;code&gt;fields_for()&lt;/code&gt; helper methods.&lt;/p&gt;
&lt;p&gt;Rails&amp;#8217;s &lt;code&gt;FormBuilder&lt;/code&gt; code looks like it was abandoned before being completed, and it suffers from abysmal documentation. Best practices, aside from being undocumented, do not cover all use cases&amp;#8212;or even common use cases. In fact, best practices are downright nonexistent. Fortunately, all that can change with about 20 lines of well-placed code.&lt;/p&gt;
&lt;h3&gt;What &lt;code&gt;FormBuilder&lt;/code&gt; does well&lt;/h3&gt;
&lt;p&gt;In your templates (depicted here in &lt;a href="http://haml.hamptoncatlin.com"&gt;Haml&lt;/a&gt; instead of Rails&amp;#8217;s default, ERb, as Haml is less sucky), &lt;code&gt;FormBuilder&lt;/code&gt; shines with simple fields:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
- form_for(@thing) do |f|
  %p
    = f.label(:name, 'Name:')
    = f.text_field(:name)
  %p
    = f.label(:description, 'Description:')
    %br/
    = f.text_area(:description)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, &lt;code&gt;f&lt;/code&gt; is the &lt;code&gt;FormBuilder&lt;/code&gt; instance. It provides the &lt;code&gt;label()&lt;/code&gt;, &lt;code&gt;text_field()&lt;/code&gt; and &lt;code&gt;text_area()&lt;/code&gt; methods.&lt;/p&gt;
&lt;h3&gt;What &lt;code&gt;FormBuilder&lt;/code&gt; does badly&lt;/h3&gt;
&lt;p&gt;Inevitably, in the course of a project, you will want to use more than text fields and date fields. And here, &lt;code&gt;FormBuilder&lt;/code&gt; falls flat. For instance, a select field might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
  %p
    = f.label(:thing_type_id, 'Type:')
    = f.select(:thing_type_id, ThingType.all(:order =&amp;gt; :name).collect{ |t| [ t.name, t.id ] }, :include_blank =&amp;gt; 'None')&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This verbose amalgamation of complicated code and simple markup is even, with no apparent embarassment, suggested in the Rails &lt;span class="caps"&gt;API&lt;/span&gt; documentation.&lt;/p&gt;
&lt;p&gt;But even &lt;code&gt;select()&lt;/code&gt; is elegant compared to other common solutions &lt;code&gt;FormBuilder&lt;/code&gt; supposedly provides. Writing a functional &lt;code&gt;date_field()&lt;/code&gt;, needed for virtually any serious Rails project, is a nightmare; compound fields are nonexistent; and, most common and worst of all, there is no way to specify, for instance, &amp;#8220;title fields should look like &lt;em&gt;this&lt;/em&gt;&amp;#8221;: the very Don&amp;#8217;t Repeat Yourself principle Rails fans so avidly embrace.&lt;/p&gt;
&lt;h3&gt;Mediocre Solutions&lt;/h3&gt;
&lt;p&gt;Rails plugins may ease the pain, but they almost never do exactly what you want. Even if you find a perfect &lt;code&gt;date_field()&lt;/code&gt;, you are only postponing an inevitable need for a more general solution.&lt;/p&gt;
&lt;p&gt;Helper methods can work around some of &lt;code&gt;FormBuilder&lt;/code&gt;, but if you resort to your own system of helper methods, you end up essentially engineering your own mini-framework of form helpers. The problem here is the unintuitive divide (what is &lt;code&gt;FormBuilder&lt;/code&gt; and what is custom?) and a very inconsistent method signature (that is, the types and orders of parameters). And if you do not plan things out ahead of time, your helper code which began as a helper or two will end as a confusing mess.&lt;/p&gt;
&lt;p&gt;Developers also get tempted to use Rails&amp;#8217;s alternate tagging library methods, such as &lt;code&gt;text_field_tag()&lt;/code&gt;, &lt;code&gt;select_tag()&lt;/code&gt;, and other non-incumbered helpers. The problem with these is that they do not do the wonderful things &lt;code&gt;FormBuilder&lt;/code&gt; does: automatically figure out element names and IDs, for instance, magically calculate fields&amp;#8217; initial values, or (prior to Rails 2.2) output valid &lt;span class="caps"&gt;HTML&lt;/span&gt;. These methods should be avoided when building a form for a model.&lt;/p&gt;
&lt;p&gt;The funny thing is, &lt;code&gt;FormBuilder&lt;/code&gt; actually &lt;em&gt;does&lt;/em&gt; solve all these problems, &lt;em&gt;almost&lt;/em&gt;. It really does get 95% of the way there. This article presents the last 5%.&lt;/p&gt;
&lt;h3&gt;How &lt;code&gt;FormBuilder&lt;/code&gt; works&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;FormBuilder&lt;/code&gt;, implemented mostly in &lt;code&gt;action-pack/lib/action_view/helpers/form_helper.rb&lt;/code&gt; (but spread into &lt;code&gt;date_helper.rb&lt;/code&gt; and &lt;code&gt;form_options_helper.rb&lt;/code&gt; in a textbook example of &amp;#8220;when &lt;em&gt;not&lt;/em&gt; to use Ruby&amp;#8217;s class-reopening feature&amp;#8221;), is implemented with three main pieces:&lt;/p&gt;
&lt;p&gt;First, the helper methods: &lt;code&gt;ActionView::Helpers::FormHelper&lt;/code&gt; defines several methods which are available to every view: &lt;code&gt;text_field()&lt;/code&gt;, &lt;code&gt;text_area()&lt;/code&gt;, &lt;code&gt;select()&lt;/code&gt;, and so on. Most of them follow a simple calling convention:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
  def something_field(object_name, method, options = {})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here&amp;#8217;s the magical part: if &lt;code&gt;options[:object]&lt;/code&gt; is set, the helper will call &lt;code&gt;options[:object].send(method)&lt;/code&gt;: it will use the actual object to determine the initial value of the field.&lt;/p&gt;
&lt;p&gt;Second, &lt;code&gt;ActionView::Helpers::FormBuilder&lt;/code&gt; (which is not a helper at all): essentially, &lt;code&gt;FormBuilder&lt;/code&gt; holds the object you are building a form for and serves as a proxy to the helper methods. It redefines every helper method with its own version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
  def something_field(method, options = {})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;FormBuilder&lt;/code&gt; uses its own &lt;code&gt;object_name&lt;/code&gt; and &lt;code&gt;object&lt;/code&gt; methods, so that when your template calls:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
= f.text_field(:name, :size =&amp;gt; 15)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;FormBuilder&lt;/code&gt; (in the above example, &lt;code&gt;f&lt;/code&gt;) passes the call through as:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
= text_field(f.object_name, :name, :size =&amp;gt; 15, :object =&amp;gt; f.object)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lastly, &lt;code&gt;InstanceTag&lt;/code&gt;: an implementation detail which may strike you at first as over-engineered, but upon further inspection rubs off as sideways-engineered at best and upside-down-engineered the rest of the time. Try to avoid calling its methods or modifying it whenever possible.&lt;/p&gt;
&lt;h3&gt;What We Want&lt;/h3&gt;
&lt;p&gt;We want a simple way to add new types of fields to &lt;code&gt;FormBuilder&lt;/code&gt;: perhaps, extending the above examples, a &lt;code&gt;thing_type_field()&lt;/code&gt; or a &lt;code&gt;date_field()&lt;/code&gt;. I will showcase both below.&lt;/p&gt;
&lt;h3&gt;Implementation&lt;/h3&gt;
&lt;p&gt;I shy away from plugins and I encourage others to act the same way. If you do not understand this code, you should not use it: that would be applying my solutions to your problems, which only works if you have the exact same problems as I do, which is unlikely. Continue on your merry way until you reach the aforementioned impasses, and use this guide as an &lt;em&gt;aid&lt;/em&gt; in solving your problems.&lt;/p&gt;
&lt;p&gt;That said, here is my solution:&lt;/p&gt;
&lt;p&gt;Create the directory &lt;code&gt;lib/forms/&lt;/code&gt; and add a file, &lt;code&gt;lib/forms/application_helper.rb&lt;/code&gt;, and leave it nearly empty for now:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
module Forms::ApplicationHelper
  # Helper methods go here
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the top of &lt;code&gt;app/helpers/application_helper.rb&lt;/code&gt;, include it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
module ApplicationHelper
  include Forms::ApplicationHelper
  #... leave whatever was here before ...
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a custom &lt;code&gt;FormBuilder&lt;/code&gt;, in &lt;code&gt;lib/forms&lt;/code&gt;, in a file called &lt;code&gt;lib/forms/application_form_builder.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
class Forms::ApplicationFormBuilder &amp;lt; ActionView::Helpers::FormBuilder
  # Copied from FormBuilder. FormBuilder looks like it has some bright
  # engineering ideas but never finished implementing them. This *should*
  # be automated by defining "self.field_helpers", but it's used before
  # this class is loaded.
  Forms::ApplicationHelper.instance_methods.each do |selector|
    src = &amp;lt;&amp;lt;-end_src
      def #{selector}(method, options = {})
        @template.send(#{selector.inspect}, @object_name, method, objectify_options(options))
      end
    end_src
    class_eval src, __FILE__, __LINE__
  end&lt;/code&gt;
 
&lt;code&gt;  private&lt;/code&gt;
 
&lt;code&gt;  def objectify_options(options)
    @default_options.merge(options.merge(:object =&amp;gt; @object))
  end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above code is copied, almost word-for-word, out of Rails&amp;#8217;s original &lt;code&gt;FormBuilder&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, we want to use our own &lt;code&gt;ApplicationFormBuilder&lt;/code&gt; instead of Rails&amp;#8217;s default, so add the file &lt;code&gt;config/initializers/application_form_builder.rb&lt;/code&gt; with the following contents:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
ActionView::Base.class_eval do
  def self.default_form_builder
    Forms::ApplicationFormBuilder
  end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restart your Rails application (this is the last time&amp;#8212;a subtle bonus) and we are now using our new-and-improved &lt;code&gt;ApplicationFormBuilder&lt;/code&gt;. At the moment, it behaves exactly like Rails&amp;#8217;s regular &lt;code&gt;FormBuilder&lt;/code&gt;: it maintains all the great features, adding no overhead. But now we can extend it!&lt;/p&gt;
&lt;h3&gt;A better &lt;code&gt;date_field&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;When engineering, always determine what the customer wants (which should be easy here, because you are the customer). In my case, I want to write this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
%p
  = f.label(:start_date, 'Start Date:')
  = f.date_field(:start_date)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let&amp;#8217;s make this work!&lt;/p&gt;
&lt;p&gt;I use &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; for all my JavaScript needs, which is fantastic because it lets me work without &lt;span class="caps"&gt;HTML&lt;/span&gt; IDs and it lets me remove 100% of my JavaScript code from my templates, which is good because code does not belong in templates. There are a couple of jQuery date-select plugins which border on acceptable; let us use the one which comes as part of the jQuery-UI download, &lt;code&gt;ui.datepicker.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Our first hurdle: all these JavaScript date-picker fields submit text (for example, &lt;code&gt;"Nov 10, 2008"&lt;/code&gt;) which the model does not expect. We will need to edit our model to allow reading and writing a text representation. So let&amp;#8217;s take our model, for instance, &lt;code&gt;app/models/thing.rb&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
class Thing
  # ... typical model stuff ...&lt;/code&gt;

&lt;code&gt;  def start_date_string
    d = self.start_date
    d.blank? ? nil : d.strftime('%b %d %Y')
  end&lt;/code&gt;

&lt;code&gt;  def start_date_string=(value)
    d = nil
    begin
      d = Date.parse(value)
    rescue ArgumentError
      # d = nil
    end&lt;/code&gt;

&lt;code&gt;    self.start_date = d
  end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(In a real project, this behaviour should be extracted into a mix-in.)&lt;/p&gt;
&lt;p&gt;Now we have &lt;code&gt;Thing::start_date_string()&lt;/code&gt; and &lt;code&gt;Thing::start_date_string=()&lt;/code&gt;, which behave like &lt;code&gt;Thing::start_date()&lt;/code&gt; and &lt;code&gt;Thing::start_date=()&lt;/code&gt; except they allow human-readable strings that can be submitted via &lt;span class="caps"&gt;HTML&lt;/span&gt; forms.&lt;/p&gt;
&lt;p&gt;We should add the necessary JavaScript includes, as well, in &lt;code&gt;app/views/layouts/application.html.haml&lt;/code&gt; (or, as the case may be, &lt;code&gt;app/views/layouts/application.html.erb&lt;/code&gt;&amp;#8212;or whatever layout you happen to be using), in the header:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
= javascript_include_tag(['vendor/jquery.js', 'vendor/jquery.ui.all.js', 'date_field.js'])&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To tie our &lt;span class="caps"&gt;HTML&lt;/span&gt; into jQuery (since, as mentioned above, we write no JavaScript in our &lt;span class="caps"&gt;HTML&lt;/span&gt;), our templates will need to output text fields with a special class attached. Let us arbitrarily choose the &lt;span class="caps"&gt;HTML&lt;/span&gt; class, &lt;code&gt;date_field&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;public/javascripts/date_field.js&lt;/code&gt; with the following contents:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$(document).ready(function() {
  $('input.date_field').datepicker({
    // Add options here...
    dateFormat: 'M d yy'
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This JavaScript, when loaded, will turn any &lt;span class="caps"&gt;HTML&lt;/span&gt; &lt;code&gt;input&lt;/code&gt; with class &lt;code&gt;date_field&lt;/code&gt; into an interactive date selector.&lt;/p&gt;
&lt;p&gt;Now, let us use our magical &lt;code&gt;ApplicationFormBuilder&lt;/code&gt; to create the missing piece of the puzzle, by adding the following to &lt;code&gt;lib/forms/application_helper.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
  def date_field(object_name, method, options = {})
    options = options.reverse_merge(:size =&amp;gt; 12)
    if options[:class].blank?
      options[:class] = 'date_field'
    else
      options[:class] += ' date_field'
    end
    method = "#{method}_string".to_sym
    text_field(object_name, method, options)
  end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Presto! Your template should work now. If you have forgotten, your template looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
- form_for(@thing) do |f|
  %p
    = f.label(:start_date, 'Start Date:')
    = f.date_field(:start_date)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And internally, that behaves as follows:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;The template calls &lt;code&gt;form_for(&lt;/code&gt;&lt;code&gt;@thing)&lt;/code&gt; to create a new &lt;code&gt;ApplicationFormBuilder&lt;/code&gt;, &lt;code&gt;f&lt;/code&gt;. (The initializer we wrote specifies &lt;code&gt;ApplicationFormBuilder&lt;/code&gt; instead of the regular &lt;code&gt;FormBuilder&lt;/code&gt;.)&lt;/li&gt;
	&lt;li&gt;The template calls &lt;code&gt;f.date_field(:start_date)&lt;/code&gt; (and maybe passes optional parameters, such as an &lt;span class="caps"&gt;HTML&lt;/span&gt; class or style). &lt;code&gt;ApplicationFormBuilder::date_field()&lt;/code&gt; is not the helper you wrote: it is a new method, generated automatically because you wrote the helper method.&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;ApplicationFormBuilder::date_field()&lt;/code&gt; calls the helper you wrote, &lt;code&gt;date_field(:thing, :start_date)&lt;/code&gt;.&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;Forms::ApplicationHelper::date_field()&lt;/code&gt; calls &lt;code&gt;text_field(:thing, :start_date_string, :object =&amp;gt; thing, :size =&amp;gt; 12, :class =&amp;gt; 'date_field')&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;FormHelper&lt;/code&gt; (the Rails helper) creates the text field, with the &lt;code&gt;date_field&lt;/code&gt; &lt;span class="caps"&gt;HTML&lt;/span&gt; class.&lt;/li&gt;
	&lt;li&gt;When the page loads, jQuery selects all text fields with the &lt;code&gt;date_field&lt;/code&gt; &lt;span class="caps"&gt;HTML&lt;/span&gt; class and calls &lt;code&gt;datepicker()&lt;/code&gt; on them, turning them into date pickers.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Whew, a lot of work, right? yes. But here are the advantages:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;We can pick another jQuery-style date-picker implementation, and all we need to change is &lt;code&gt;public/javascripts/date_field.js&lt;/code&gt;.&lt;/li&gt;
	&lt;li&gt;We can change the &lt;span class="caps"&gt;HTML&lt;/span&gt; completely, for instance reverting to Rails&amp;#8217;s three-dropdown-box behaviour, by simply changing &lt;code&gt;lib/forms/application_helper.rb&lt;/code&gt;.&lt;/li&gt;
	&lt;li&gt;It is easy to unit-test the helper, the template, and the JavaScript, since each has an isolated and distinct role.&lt;/li&gt;
	&lt;li&gt;Views receive the real benefits:
	&lt;ul&gt;
		&lt;li&gt;There is no bloat in a simple call to &lt;code&gt;f.date_field(:method)&lt;/code&gt;: we remain readable and concise.&lt;/li&gt;
		&lt;li&gt;Site-wide changes to date field behaviour can transpire without a need to edit templates.&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As for drawbacks? This procedure is certainly complicated, so some people may prefer to just install a Rails plugin and be done with it. I have tried a few date selector plugins and found them all lacking in one way or another compared with this implementation, but your mileage may vary.&lt;/p&gt;
&lt;h3&gt;A better &lt;code&gt;select()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;While ranting above about &lt;code&gt;FormBuilder&lt;/code&gt; shortcomings, I produced this piece of code, which the Rails &lt;span class="caps"&gt;API&lt;/span&gt; documentation shockingly presents as exemplary.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
  %p
    = f.label(:thing_type_id, 'Type:')
    = f.select(:thing_type_id, ThingType.all(:order =&amp;gt; :name).collect{ |t| [ t.name, t.id ] }, :include_blank =&amp;gt; 'None')&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What do we, the customer (template writer), &lt;em&gt;want&lt;/em&gt; to type? Certainly not all that, since we may have &lt;code&gt;ThingType&lt;/code&gt; selectors elsewhere in our application and we are almost guaranteed to need to change the code within as our application evolves. Helper methods are a common but unsatisfactory compromise. What we &lt;em&gt;really&lt;/em&gt; want is this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
  %p
    = f.label(:thing_type_id, 'Type:')
    = f.thing_type_field(:thing_type_id)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the implementation? Easy: Just add this to &lt;code&gt;lib/forms/application_helper.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
  def thing_type_field(object_name, method, options = {})
    select(object_name, method, ThingType.all(:order =&amp;gt; :name).collect{ |t| [ t.name, t.id ] }, options.reverse_merge({:include_blank =&amp;gt; 'None'}))
  end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Advantages:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Our template is simple.&lt;/li&gt;
	&lt;li&gt;We reuse code and ensure consistency across the site by only writing our model query once.&lt;/li&gt;
	&lt;li&gt;Because the model query code is now in a helper (where it belongs) and not in the template (where it does not), it can (and should) be easily split into multiple lines and have more logic added.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Disadvantages:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;We lose the nice &lt;code&gt;html_options&lt;/code&gt; parameter in the &lt;code&gt;select()&lt;/code&gt; method&amp;#8217;s signature, which is inconsistent across the Rails &lt;span class="caps"&gt;API&lt;/span&gt; but useful nonetheless. It is straightforward to pass a &lt;code&gt;:html_options&lt;/code&gt; item within our &lt;code&gt;options&lt;/code&gt; hash to work around this problem.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;A Nested Object Field&lt;/h3&gt;
&lt;p&gt;Rails is unhelpful when you want to include a nested object in your form. The best you can do is render a partial, but I find that annoying because it melds interface with implementation. How about this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
  def foo_field(object_name, method, options = {})
    foo = options[:object] &amp;amp;&amp;amp; options[:object].send(method) || Foo.new
    fields_for("#{object_name}[#{method}]", foo) do |f|
      render(:partial =&amp;gt; '/foo/form', :object =&amp;gt; f)
    end
  end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And because at this point all your form partials use &lt;code&gt;FormBuilder&lt;/code&gt; exclusively and never rely upon an &lt;code&gt;@object&lt;/code&gt; or &lt;code&gt;@foo&lt;/code&gt; or anything like that, this works without a hitch.&lt;/p&gt;
&lt;h3&gt;Parting Thoughts&lt;/h3&gt;
&lt;p&gt;This guide is the result of many of hours of hard work with &lt;code&gt;FormBuilder&lt;/code&gt;, and I hope it helps save others that time. If you have any questions, comments, or even completely different and possibly better solutions, I would love to hear them.&lt;/p&gt;</description>
      <pubDate>Tue, 11 Nov 2008 01:19:05 -0500</pubDate>
      <link>http://feedproxy.google.com/~r/AdamHoopersEngineeringTips/~3/ZjoVBkciVFw/2-working-with-and-around-formbuilder</link>
      <guid isPermaLink="false">http://adamhooper.com/eng/articles/2-working-with-and-around-formbuilder</guid>
    <feedburner:origLink>http://adamhooper.com/eng/articles/2-working-with-and-around-formbuilder</feedburner:origLink></item>
    <item>
      <title>Suckiness Engineering</title>
      <description>&lt;p&gt;Everything sucks.&lt;/p&gt;
&lt;p&gt;This is a website dedicated to engineering and programming: to learning why computers do what they do, to understanding how to trick them into doing what you want, and to making the world (or at least, the computer programs therein) better.&lt;/p&gt;
&lt;p&gt;Yet nobody seems to realize everything sucks, so I cannot resist a diatribe.&lt;/p&gt;
&lt;h3&gt;Computers Suck&lt;/h3&gt;
&lt;p&gt;Before computers were even invented, scientists already knew there are some problems computers can never solve. (They all stem from the halting problem: writing Program A to run Program B and determine whether Program B has gotten into an infinite loop: Program A, an idiot like all computer programs, is doomed to sit idly by and twiddle its thumbs while Program B goes crazy and explodes.)&lt;/p&gt;
&lt;p&gt;Computers are very bad at certain tasks, too, like organizing an airplane-confined salesperson&amp;#8217;s itinerary. They know what to do, yet they insist on taking millions of years in which to do it: a rather inconvenient delay, since they are bound to break before they finish.&lt;/p&gt;
&lt;p&gt;But it might be a good thing computers are so inept. If computers could organize efficient itineraries, they would also break all our encryption algorithms (since if you knew how to organize efficient itineraries you could use that knowledge to decipher encrypted text).&lt;/p&gt;
&lt;h3&gt;Computer Programs Suck&lt;/h3&gt;
&lt;p&gt;A current computer can run billions of calculations every second, and yet it still takes a minute from when I turn it on to when I have Google open. I implicitly run this same test on every computer I use, with different suites of programs (written by Apple, Microsoft, or freedom fighters), and the story is always the same. Wait, wait, wait. Except for the times when something breaks and I never get my Google prompt after all.&lt;/p&gt;
&lt;p&gt;A lot of things can break, too: viruses can latch onto your computer, physical defects can infest it, or, more enraging, you can click on that one pixel the developers thought you would never click on, drag your mouse over three pixels to some other pixel they assumed you would miss, and let go. I do house calls to fix computer problems from time to time, and the most common annoyance I see is the Microsoft Windows taskbar floating to the left of the screen or toolbars in Microsoft Word floating nowhere in particular.&lt;/p&gt;
&lt;p&gt;And then&amp;#8230; the crashes. Oh, Lord, the crashes.&lt;/p&gt;
&lt;p&gt;Then, not to be forgotten, the inevitable &amp;#8220;security bug&amp;#8221; dilemma, which can render your wonderful web server into a spam-sending turd in the span of five minutes. Most of these bugs are extremely stupid; yet because of the halting problem, computers cannot help us find them all.&lt;/p&gt;
&lt;h3&gt;User Interaction Sucks&lt;/h3&gt;
&lt;p&gt;&amp;#8220;But wait!&amp;#8221; you say. &amp;#8220;What about Google? Google Search doesn&amp;#8217;t suck, does it?&amp;#8221;&lt;/p&gt;
&lt;p&gt;Yes, it does. If Google Search did not suck there would be no need for the &amp;#8220;Search&amp;#8221; button because the &amp;#8220;I&amp;#8217;m Feeling Lucky&amp;#8221; button would always take you exactly where you want. In fact, there would be no button at all, because there would be no text field, because Google would already &lt;em&gt;know&lt;/em&gt; what you want. Even better, you would never need to open the Google website at all, as Google would simply infuse the knowledge into your mind a millisecond before you even realize you want it.&lt;/p&gt;
&lt;p&gt;Keyboards and mice and user interfaces and web pages are the first signs of imperfection in our computer programs. They make the user &lt;em&gt;do&lt;/em&gt; something, which sucks because the user wants the &lt;em&gt;computer&lt;/em&gt; to do it.&lt;/p&gt;
&lt;p&gt;I hate using computers, and you should, too. But assuming you are a programmer programming programs for non-programmers, never forget that your users hate using computers even more.&lt;/p&gt;
&lt;h3&gt;Where Engineering Comes In&lt;/h3&gt;
&lt;p&gt;Everything about computers sucks. But some aspects suck &lt;em&gt;less&lt;/em&gt;. And therein lies my approach to software engineering. This is a rehash of textbook processes, yet I have never seen it written down with this wording: probably because textbook writers consider the word &amp;#8220;suckiness&amp;#8221; taboo.&lt;/p&gt;
&lt;p&gt;To write a program:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Define perfection. Because computers suck, perfection does not necessarily involve computers. Perfection involves users getting what they want with a minimum of hassle.&lt;/li&gt;
	&lt;li&gt;Define and measure how far your program is from perfection: your &amp;#8220;suckiness&amp;#8221;, if you will.&lt;/li&gt;
	&lt;li&gt;Remove as much suckiness as you can as quickly as you can.&lt;/li&gt;
	&lt;li&gt;Repeat steps 1-3 until you have overspent your resources or your program&amp;#8217;s suckiness level is below your acceptance threshold.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Good engineering is all about cycles. The cycles can have any length (1-2 week cycles seem to work in several contexts) and you can repeat as many times as you like. You can alter requirements (Step 1) and their priorities (Step 2) as often as you like, and you can run as many or as few cycles as you like.&lt;/p&gt;
&lt;p&gt;In the spirit of negativity, here is what happens if you try to skip the above steps:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;If you neglect to define what you are trying to program, then you have implicitly decided to program &amp;#8220;something&amp;#8221;. And your &amp;#8220;something&amp;#8221; does exactly that, which is annoying because you realize soon afterwards that you meant for it to do what you &lt;em&gt;want&lt;/em&gt; it to do, and meanwhile it is off &amp;#8220;something&amp;#8221;-ing. Then you need to change it.&lt;/li&gt;
	&lt;li&gt;If you neglect to prioritize, you spend ten days working on Internet Explorer 4 compatibility or something equally unimportant (for your purposes), leaving the important work unfinished and untested.&lt;/li&gt;
	&lt;li&gt;If you neglect to code, your code does not get written. More subtly (because more people seem to ignore it), if you neglect to &lt;em&gt;test&lt;/em&gt; your code, your program&amp;#8217;s suckiness does not change because you have no proof that the suckiness indeed did decrease as outlined in Steps 1 and 2. (&lt;em&gt;How&lt;/em&gt; you test is up to you, but rest assured, you &lt;em&gt;are&lt;/em&gt; testing.)&lt;/li&gt;
	&lt;li&gt;If you never repeat your cycle, you never incorporate new insights into your process. If a piece of code turns out to be difficult (Step 3), you may want to reprioritize it (Step 2) or even drop it completely (Step 1). Conversely, if you have hit your goals, you can &lt;em&gt;stop&lt;/em&gt; repeating and go outside and play, confident that your program does not suck as much as, say, remaining indoors.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It may seem cynical, but this loop is very effective at suckiness-removal. And that is what software engineering is all about.&lt;/p&gt;
&lt;p&gt;Perfect programs belong to computer programmers. Proofs that they do cannot exist belong to computer scientists. Real programs for real people are, whether you like it or not, whether you &lt;em&gt;realize&lt;/em&gt; it or not, generated through software engineering.&lt;/p&gt;
&lt;h3&gt;&amp;#8220;Why are you telling me what to do?&amp;#8221;&lt;/h3&gt;
&lt;p&gt;I am not telling you what to do. I am showing you what you do &lt;em&gt;already&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Everything from basement-tinkered tools to bank security software is built in this same manner. Not just with computers: with everything we create. In fact, it can be carried to things we do &lt;em&gt;not&lt;/em&gt; create. Evolution, for instance, is a constant striving towards the perfection of remaining alive, with priority number one being having lots of babies.&lt;/p&gt;
&lt;p&gt;Don&amp;#8217;t be a wooly mammoth. Engineer your software.&lt;/p&gt;</description>
      <pubDate>Sun, 02 Nov 2008 01:24:23 -0500</pubDate>
      <link>http://feedproxy.google.com/~r/AdamHoopersEngineeringTips/~3/DHMq1VLGk9o/1-suckiness-engineering</link>
      <guid isPermaLink="false">http://adamhooper.com/eng/articles/1-suckiness-engineering</guid>
    <feedburner:origLink>http://adamhooper.com/eng/articles/1-suckiness-engineering</feedburner:origLink></item>
  </channel>
</rss>

