<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>OnRails.org: Introducing Hashdown</title>
    <link>http://www.onrails.org/articles/2009/08/04/introducing-hashdown</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Ruby On Rails and related matters.</description>
    <item>
      <title>Introducing Hashdown</title>
      <description>&lt;p&gt;If your database is normalized, you will almost always end up with small tables (often referred to as reference data or lookup tables) which provide a set of possible values for a particular attribute.  (e.g. Currency, Category, etc.)  A common pattern that emerges in many applications is accessing these records by a symbolic name (as opposed to by id) for purposes of clarity when reading the code.  In C (and friends), the database ids can be mapped to the positions of an enum datatype.  I recently released the &lt;a href="https://github.com/rubysolo/hashdown"&gt;hashdown&lt;/a&gt; plugin that provides hash-like access for reference data records, and also adds some dropdown option list generation support, since this data is often used to populate select list in forms.&lt;/p&gt;


	&lt;p&gt;As an example of what hashdown does, suppose we have the following model:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;CardType&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;with the following data:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;+----+-------+------------------+
| id | code  | name             |
+====+=======+==================+
| 1  | visa  | Visa             |
| 2  | mc    | MasterCard       |
| 3  | disc  | Discover         |
| 4  | amex  | American Express |
+----+-------+------------------+&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;By adding the following line to the model:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;CardType&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="ident"&gt;finder&lt;/span&gt; &lt;span class="symbol"&gt;:code&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;You get the functionality of a hash-like square-bracket accessor for the model that will let you do something like:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="attribute"&gt;@order&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;card_type&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;CardType&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:visa&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;The underlying implementation is similar to:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;CardType&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.[]&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;find_by_code&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;...except it adds a caching layer to boost performance by preventing repeated database access.&lt;/p&gt;


	&lt;p&gt;Adding the following directive:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;CardType&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="ident"&gt;selectable&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;to the model gives you a class method called &lt;code&gt;select_options&lt;/code&gt; that can be used to populate a select list like this:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; form.select :card_type_id, CardType.select_options %&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;produces:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_html "&gt;&amp;lt;input type=&amp;quot;select&amp;quot; name=&amp;quot;order[card_type_id]&amp;quot; id=&amp;quot;order_card_type_id&amp;quot;&amp;gt;
  &amp;lt;option value=&amp;quot;1&amp;quot;&amp;gt;Visa&amp;lt;/option&amp;gt;
  &amp;lt;option value=&amp;quot;2&amp;quot;&amp;gt;MasterCard&amp;lt;/option&amp;gt;
  &amp;lt;option value=&amp;quot;3&amp;quot;&amp;gt;Discover&amp;lt;/option&amp;gt;
  &amp;lt;option value=&amp;quot;4&amp;quot;&amp;gt;American Express&amp;lt;/option&amp;gt;
&amp;lt;/input&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;By default, this will use the &lt;code&gt;id&lt;/code&gt; attribute as the submitted value of the option and call a &lt;code&gt;display_name&lt;/code&gt; method (if it exists) for the displayed value of the option, falling back to the &lt;code&gt;name&lt;/code&gt; method/attribute.  Each of these can be overridden by passing a symbol attribute / method name, or a lambda that will be executed to generate the value.  For (a contrived) example:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; form.select :card_type_id, CardType.select_options(:key &lt;/span&gt;&lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:code&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:value&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;lambda&lt;/span&gt;&lt;span class="punct"&gt;{|&lt;/span&gt;&lt;span class="ident"&gt;card_type&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;card_type&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;reverse&lt;/span&gt; &lt;span class="punct"&gt;})&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;produces:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_html "&gt;&amp;lt;input type=&amp;quot;select&amp;quot; name=&amp;quot;order[card_type_id]&amp;quot; id=&amp;quot;order_card_type_id&amp;quot;&amp;gt;
  &amp;lt;option value=&amp;quot;visa&amp;quot;&amp;gt;asiV&amp;lt;/option&amp;gt;
  &amp;lt;option value=&amp;quot;mc&amp;quot;&amp;gt;draCretsaM&amp;lt;/option&amp;gt;
  &amp;lt;option value=&amp;quot;disc&amp;quot;&amp;gt;revocsiD&amp;lt;/option&amp;gt;
  &amp;lt;option value=&amp;quot;amex&amp;quot;&amp;gt;sserpxE naciremA&amp;lt;/option&amp;gt;
&amp;lt;/input&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Again, the &lt;code&gt;select_option&lt;/code&gt; results are cached for better performance.&lt;/p&gt;


	&lt;p&gt;This is a pretty small plugin that I&amp;#8217;m using to &lt;span class="caps"&gt;DRY&lt;/span&gt; up some code in a current project I&amp;#8217;m working on.  Let me know if you have feature requests (or fork and patch it on &lt;a href="https://github.com/rubysolo/hashdown"&gt;GitHub&lt;/a&gt;!)&lt;/p&gt;</description>
      <pubDate>Tue, 04 Aug 2009 23:51:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:29e2df29-fe41-438e-84c9-87fe7e5bbded</guid>
      <author>Solomon White</author>
      <link>http://www.onrails.org/articles/2009/08/04/introducing-hashdown</link>
      <category>Ruby On Rails</category>
    </item>
    <item>
      <title>"Introducing Hashdown" by sksizer</title>
      <description>&lt;p&gt;Very nice &amp;#8211; I&amp;#8217;ve just been using static/class functions to achieve the same thing &amp;#8211; cached values &amp;#8211; but that&amp;#8217;s obviously not so DRY.  This is great!&lt;/p&gt;</description>
      <pubDate>Thu, 20 Aug 2009 04:24:42 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:f40e7761-79b6-4dc8-902a-dd82f5d15486</guid>
      <link>http://www.onrails.org/articles/2009/08/04/introducing-hashdown#comment-4768</link>
    </item>
    <item>
      <title>"Introducing Hashdown" by sksizer</title>
      <description>&lt;p&gt;Very nice &amp;#8211; I&amp;#8217;ve just been using static/class functions to achieve the same thing &amp;#8211; cached values &amp;#8211; but that&amp;#8217;s obviously not so DRY.  This is great!&lt;/p&gt;</description>
      <pubDate>Thu, 20 Aug 2009 04:24:29 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:e539ecee-321c-481c-9e78-a66bcf61dff8</guid>
      <link>http://www.onrails.org/articles/2009/08/04/introducing-hashdown#comment-4767</link>
    </item>
    <item>
      <title>"Introducing Hashdown" by Solomon White</title>
      <description>&lt;p&gt;@Cássio&amp;#8212;&lt;/p&gt;


	&lt;p&gt;Thanks&amp;#8212;I&amp;#8217;m glad you like it!&lt;/p&gt;


	&lt;p&gt;I&amp;#8217;m using &lt;a href="http://github.com/mbleigh/seed-fu/tree/master"&gt;seed-fu&lt;/a&gt; to populate required data for my app, which I&amp;#8217;ve liked pretty well so far.&lt;/p&gt;</description>
      <pubDate>Sun, 09 Aug 2009 05:11:09 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:f068f3be-8f72-49df-8bbb-5a4c978fc8af</guid>
      <link>http://www.onrails.org/articles/2009/08/04/introducing-hashdown#comment-4741</link>
    </item>
    <item>
      <title>"Introducing Hashdown" by Cássio Marques</title>
      <description>&lt;p&gt;Cool!&lt;/p&gt;


	&lt;p&gt;What method are you using to populate those tables? Are you using migrations for that? Or the populate plugin?&lt;/p&gt;


	&lt;p&gt;Thanks and good job!&lt;/p&gt;</description>
      <pubDate>Sat, 08 Aug 2009 23:54:24 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:3fd4f642-232b-4a23-8fb2-94244c25ad55</guid>
      <link>http://www.onrails.org/articles/2009/08/04/introducing-hashdown#comment-4740</link>
    </item>
  </channel>
</rss>
