<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Rob Nelson : Writings from The Killswitch Collective</title>
    <link>http://www.killswitchcollective.com/blog/index.xml</link>
    <description>These articles were written by Rob Nelson for Perspectives, the blog of The Killswitch Collective, a Chicago-based web development, design and communication firm.</description>
    <language>en-us</language>
    <item>
      <title>Exercises in Pruning Code, Episode Two</title>
      <category>Development</category>
      <description>&lt;h3&gt;A story about building an application&lt;/h3&gt;
 
&lt;p&gt;
 I was recently working on a Ruby on Rails application that had a section for 
 sending messages. This sounds pretty easy, right?  I started with a 
 &lt;code&gt;User&lt;/code&gt; model and a &lt;code&gt;Message&lt;/code&gt; model and some basic
 associations:
&lt;/p&gt;


  
  &lt;pre&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:messages&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;  
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.1 - initial &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Message&lt;/code&gt; models&lt;/p&gt;
&lt;p&gt;
 But when it came time to actually start building the application, I found this simple model code was not
 enough.  The devil is in the details, as they say. There was a lot of functionality
 I needed to add beyond just a list of messages connected to a &lt;code&gt;User&lt;/code&gt;.
&lt;/p&gt;

&lt;h4&gt;1) Filtered views&lt;/h4&gt;
&lt;p&gt;I needed different views of the messages such as sent messages, drafts, and deleted messages.&lt;/p&gt;

&lt;p&gt;How do I determine 'draft' status?  Well one way is to fill in a &lt;code&gt;delivered_at&lt;/code&gt; date
whenever a message is sent. Then a draft is just a &lt;code&gt;Message&lt;/code&gt;
with no &lt;code&gt;delivered_at&lt;/code&gt; date.  
&lt;/p&gt;

&lt;p&gt;So after adding that field (and a &lt;code&gt;sender_id&lt;/code&gt; and &lt;code&gt;receiver_id&lt;/code&gt;) to the database 
I went to my &lt;code&gt;messages_controller.rb&lt;/code&gt; file and added a few methods that looked sort of like this:
&lt;/p&gt;


  
  &lt;pre&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
   &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
     &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delivered_at is not NULL and recipient_id = ?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
 
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sent_mail&lt;/span&gt;
   &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
     &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delivered_at is not NULL and sender_id = ?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
 
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drafts&lt;/span&gt; 
   &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
     &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delivered_at is NULL and sender_id = ?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.2 - initial fragment from &lt;code&gt;messages_controller.rb&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;2) Pagination&lt;/h4&gt;

&lt;p&gt;Nobody wants to load a page of 1000 messages at a time, so I needed to be able to break up that list into limited
 sized chunks.
 I used the excellent plugin &lt;code&gt;&lt;a href="http://github.com/mislav/will_paginate/tree/master"&gt;will_paginate&lt;/a&gt;&lt;/code&gt; 
 for that purpose.  Then my controllers methods got a little more verbose:
&lt;/p&gt;


  
  &lt;pre&gt; 
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
   &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
     &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delivered_at is not NULL and recipient_id = ?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sent_mail&lt;/span&gt;
   &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
     &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delivered_at is not NULL and sender_id = ?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
 
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drafts&lt;/span&gt; 
   &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
     &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delivered_at is NULL and sender_id = ?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.3 - fragment from &lt;code&gt;messages_controller.rb&lt;/code&gt; with pagination&lt;/p&gt;

&lt;h4&gt;3) The ability to flag content (i.e. spam, objectionable content etc...)&lt;/h4&gt;

&lt;p&gt;What if someone gets spam in the message system - or something objectionable in some other way.  Well I need
  to filter that stuff out.  I added a &lt;code&gt;Flag&lt;/code&gt; model and connected that to messages like so:
&lt;/p&gt;


  
  &lt;pre&gt;  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:flags&lt;/span&gt;
    &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.4 - &lt;code&gt;Message&lt;/code&gt; model with flags added&lt;/p&gt;


&lt;p&gt;However, at this point my controller methods are starting to look like this:&lt;/p&gt;


  
  &lt;pre&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sent_mail&lt;/span&gt;
   &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
     &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delivered_at is not NULL and &lt;/span&gt;
&lt;span class="s1"&gt;         flags.flagged_item_id is NULL and &lt;/span&gt;
&lt;span class="s1"&gt;         recipient_id = ?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="ss"&gt;:include&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:flags&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sent_mail&lt;/span&gt;
   &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
     &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delivered_at is not NULL and &lt;/span&gt;
&lt;span class="s1"&gt;         flags.flagged_item_id is NULL and &lt;/span&gt;
&lt;span class="s1"&gt;         sender_id = ?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="ss"&gt;:include&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:flags&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
 
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drafts&lt;/span&gt; 
   &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
     &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delivered_at is NULL and&lt;/span&gt;
&lt;span class="s1"&gt;         flags.flagged_item_id is NULL and &lt;/span&gt;
&lt;span class="s1"&gt;         sender_id = ?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="ss"&gt;:include&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:flags&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.5 - fragment from &lt;code&gt;messages_controller.rb&lt;/code&gt; with flags&lt;/p&gt;

&lt;p&gt;I'm looking at some ugly code - with a lot of repetition.  How do I pare this down?&lt;/p&gt;

&lt;h4&gt;Begin Pruning&lt;/h4&gt;

&lt;p&gt;My first thought is that if anything in my application can be flagged, I should
 be able to do a little meta-programming to create a find method that will give me only
 un-flagged items.  Ideally I could even send in all the rest of the find arguments exactly the same.
&lt;/p&gt;

&lt;p&gt;
 There is the 
&lt;code&gt;&lt;a href="http://ryandaigle.com/articles/2008/3/24/what-s-new-in-edge-rails-has-finder-functionality"&gt;named_scope&lt;/a&gt;&lt;/code&gt; 
 addition to Rails 2.x that does just that - but I also want
 something I can add to any class as a &lt;code&gt;Mixin&lt;/code&gt;.  That way I can write code like this:
&lt;/p&gt;


  
  &lt;pre&gt;  &lt;span class="no"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unflagged_items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delivered_at is not NULL&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;SomeOtherThing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unflagged_items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.6 - call to imagined method &lt;code&gt;unflagged_items&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The method 
&lt;code&gt;&lt;a href="http://railscasts.com/episodes/5"&gt;with_scope&lt;/a&gt;&lt;/code&gt; is a good candidate for sending in 
some pre-determined find conditions - but leaving it open to add more later.  I'm  wanting to 
add the following method to all my classes that need to be flagged:
&lt;/p&gt;


  
  &lt;pre&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;unflagged_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;with_scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:find&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;flags.flagged_item_id is NULL&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="ss"&gt;:include&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:flags&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;  
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;  
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.7 - code for imaginary &lt;code&gt;unflagged_items&lt;/code&gt; method&lt;/p&gt;

&lt;p&gt;How do I do that? Well, I can turn that code into a Module and add it to any class 
automatically using a little metaprogramming:
&lt;/p&gt;


  
  &lt;pre&gt;  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Flaggable&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;included&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:as&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:flagged_item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:dependent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ClassMethods&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ClassMethods&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;unflagged_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;with_scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:find&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;flags.flagged_item_id is NULL&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="ss"&gt;:include&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:flags&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;  
          &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;  
      &lt;span class="k"&gt;end&lt;/span&gt;  
    &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.8 - &lt;code&gt;Flaggable&lt;/code&gt; module&lt;/p&gt;

&lt;p&gt;Any model I put the line &lt;code&gt;include Flaggable&lt;/code&gt; in will have that method available.  
So if I include it in the &lt;code&gt;User&lt;/code&gt; class I've added a method
&lt;code&gt;user.messages.unflagged_items&lt;/code&gt; which returns a sort of incomplete version of the find function - with all
the necessary logic to limit the list to unflagged items already filled in.  I still have to fill in the &lt;code&gt;:all&lt;/code&gt;
or &lt;code&gt;:first&lt;/code&gt; or any other &lt;code&gt;:conditions&lt;/code&gt; I want.  But the function is sort of half-called.
This is a useful thing - getting half-called functions.  In functional programming it's called currying.  I'll come
back to that in a moment.
&lt;/p&gt;

&lt;p&gt;
Anyway, So now my controller methods now look like this:
&lt;/p&gt;


  
  &lt;pre&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unflagged_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delivered_at is not NULL and recipient_id = ?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sent_mail&lt;/span&gt;
    &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unflagged_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delivered_at is not NULL and sender_id = ?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
 
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drafts&lt;/span&gt; 
   &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unflagged_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
     &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delivered_at is NULL and sender_id = ?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.9 - fragment from new &lt;code&gt;messages_controller.rb&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;Continue Pruning&lt;/h4&gt;
&lt;p&gt;
 It's getting better, but isn't there some way I can pare it down even more?  Now I'll go to the 
&lt;code&gt;User&lt;/code&gt; model. Instead of simply using &lt;code&gt;has_many :messages&lt;/code&gt; - since 
&lt;code&gt;has_many&lt;/code&gt; 
supports blocks - I can add some more convenience methods to the &lt;code&gt;User&lt;/code&gt; class:
&lt;/p&gt;


  
  &lt;pre&gt;  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;

    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:received_messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:foreign_key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;recipient_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
          &lt;span class="ss"&gt;:class_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Message&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delivered_and_unflagged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;unflagged_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;delivered_at IS NOT NULL&amp;#39;&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:per_page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vi"&gt;@messages_per_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:sent_messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:foreign_key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sender_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
          &lt;span class="ss"&gt;:class_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Message&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delivered_and_unflagged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;unflagged_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;delivered_at IS NOT NULL&amp;#39;&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:per_page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vi"&gt;@messages_per_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:draft_messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:foreign_key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sender_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
          &lt;span class="ss"&gt;:class_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
          &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;delivered_at IS NULL&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;paginated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:per_page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vi"&gt;@messages_per_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;received_messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delivered_and_unflagged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sent_mail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sent_messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delivered_and_unflagged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drafts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;draft_messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.10 - more developed &lt;code&gt;User&lt;/code&gt; model&lt;/p&gt;

&lt;p&gt;I'm doing pretty well with reduction of code in my controller now.  The only ugly bit of code leftover
 is the &lt;code&gt;params[:page]...&lt;/code&gt; bit - but I can make that slightly better too by factoring it out.  
 I would like to use &lt;code&gt;params[:page] || 1&lt;/code&gt; but &lt;code&gt;params[:page]&lt;/code&gt; returns an empty
 string if there is no matching parameter and &lt;code&gt;will_paginate&lt;/code&gt; interprets an empty string
 as a request for page 0 and returns an error.  So I have to use the longer statement with the ternary operator.
 Now my controller code looks like this:
&lt;/p&gt;


  
  &lt;pre&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
   &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
 
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sent_mail&lt;/span&gt;
   &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sent_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
 
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drafts&lt;/span&gt; 
   &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drafts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;figure_page&lt;/span&gt;
   &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.11 - pruned fragment from &lt;code&gt;messages_controller.rb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I'm happy enough with that.  I've made different lists of messages
for the currently logged on &lt;code&gt;User&lt;/code&gt; that automatically paginate and filter out flagged items 
with just one line of code per method.
&lt;/p&gt;

&lt;h4&gt;3) Next and Previous Message&lt;/h4&gt;

&lt;p&gt;I'm not done yet though - because the view page of a message needs a &lt;code&gt;next&lt;/code&gt; and &lt;code&gt;previous&lt;/code&gt; link.
So if the user is looking at a draft - &lt;code&gt;next&lt;/code&gt; should be the next draft - not the next sent message - and previous
should be the previous draft - not the previous sent message. Make sense?
&lt;/p&gt;

&lt;p&gt;One way I could do this
is to have a &lt;code&gt;show_draft&lt;/code&gt; method, a &lt;code&gt;show_sent_item&lt;/code&gt; method etc... and just call the correct 
link from the correct listing page (i.e. the list of all drafts page has links to &lt;code&gt;show_draft&lt;/code&gt;, 
the sent items page has links to &lt;code&gt;show_sent_item&lt;/code&gt; etc...).
&lt;/p&gt;

&lt;p&gt;There are 2 problems with this though.  1) That is creating several methods for basically one 'show' action.  So they
will all be virtually the same code over and over again. 2) I'm using a partial to render the list of messages - so 
I'd have to send in some way to create a different link based on the type of filter ('drafts', 'sent mail' etc...)
but I'd rather just call &lt;code&gt;render :partial =&gt; "message", :collection =&gt; @messages&lt;/code&gt;.  
I don't want the partial to have to worry about what particular filtered list of messages it happens to
be rendering.
&lt;/p&gt;

&lt;p&gt;I'm sure there are a lot of ways to solve this. What I came up with was to add a 'from' value as a 
parameter for each &lt;code&gt;link_to :action =&gt; 'show'&lt;/code&gt;
in the partial. That way I could just append 
&lt;code&gt;params[:action]&lt;/code&gt; to every url and by the time the controller gets the request, it knows where the 
request is coming from.  This gives me the information I need to respond differently to the &lt;code&gt;show&lt;/code&gt;
action depending on that parameter.  And leaves that logic out of the view.
&lt;/p&gt;

&lt;p&gt;In order to get the next and previous messages though, 
I needed to be able to identify and generate a list of messages based on the value of 
a string (i.e. value of &lt;code&gt;params[:from]&lt;/code&gt;).
&lt;/p&gt; 

&lt;p&gt;The code I wrote at first looked something like this and was in the controller:&lt;/p&gt;


  
  &lt;pre&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="vi"&gt;@message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# need @messages for previous, next&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:from&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sent_mail&amp;#39;&lt;/span&gt;
      &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sent_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;drafts&amp;#39;&lt;/span&gt;
      &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drafts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;#...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bulk_action&lt;/span&gt;
    &lt;span class="c1"&gt;# ... do bulk action&lt;/span&gt;

    &lt;span class="c1"&gt;# need @messages for previous, next&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:from&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sent_mail&amp;#39;&lt;/span&gt;
      &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sent_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;drafts&amp;#39;&lt;/span&gt;
      &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drafts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;#...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.12 - fragment of &lt;code&gt;messages_controller.rb&lt;/code&gt; with new code&lt;/p&gt;

&lt;p&gt;So I've lost some of my simplicity, I'm repeating myself again and my code is in need of pruning.&lt;/p&gt;

&lt;p&gt;What I need is a function that returns a function waiting to receive arguments.  This is similar to the &lt;code&gt;with_scope&lt;/code&gt;
method I mentioned earlier, and the idea of function currying. I need a function that's partially filled out - but not called yet - waiting for 
some parameters.  This is a good place
to use the the fact that a &lt;code&gt;Method&lt;/code&gt; is just another object in Ruby - and create a method to 
return whichever &lt;code&gt;User&lt;/code&gt; method I want.
&lt;/p&gt;

&lt;h4&gt;A method that returns a method&lt;/h4&gt;


  
  &lt;pre&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_messages_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# special case of &amp;#39;index&amp;#39; action&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;index&amp;#39;&lt;/span&gt;
       &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;inbox&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
       &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.13 - fragment from &lt;code&gt;User&lt;/code&gt; model&lt;/p&gt;

&lt;p&gt;returns a method as an object waiting for arguments. So I can put that
code in my &lt;code&gt;User&lt;/code&gt; class and I can call it like this in my controller:
&lt;/p&gt;


  
  &lt;pre&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
  &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_messages_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:action&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sent_mail&lt;/span&gt;
  &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_messages_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:action&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
 
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drafts&lt;/span&gt; 
  &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_messages_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:action&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
  &lt;span class="vi"&gt;@message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_messages_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:from&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.14 - fragment from new &lt;code&gt;messages_controller.rb&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;One last trick&lt;/h4&gt;

&lt;p&gt;I'm almost done.  But I can go one step further in minimization of code.  Taking
advantages of the fact that a &lt;code&gt;method&lt;/code&gt; can be converted to a block by putting an &lt;code&gt;&amp;amp;&lt;/code&gt;
in front of it. In the controller, since all the returned methods are taking that same &lt;code&gt;figure_page&lt;/code&gt;
parameter - I can factor that out as a method accepting a block and do something like this:&lt;/p&gt;



  
  &lt;pre&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_messages_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:inbox&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sent_mail&lt;/span&gt;
    &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_messages_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:sent_mail&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="vi"&gt;@message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_messages_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:from&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;figure_page&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p class="pre_label"&gt;Fig.15 - fragment from another revision to &lt;code&gt;messages_controller.rb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It's odd looking, I admit. I've lost a little readability for the sake of density.  
But I've left myself very little code in the controller
and nothing specific about controllers in the model.  That much I like.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;So if you ever writing a Ruby on Rails application that has messages that need to be filtered, paginated and 
include a detail view with a 
previous, next link - you might be able to glean some code from the article to help get 
started.  Also, today's lesson is that it's sometimes handy to pass around functions as objects. 
&lt;/p&gt;

&lt;p&gt;&lt;b&gt;NOTE:&lt;/b&gt; I've included a &lt;a href="http://www2.killswitchcollective.com/articles/blog_ksc_ep2.zip"&gt;zip&lt;/a&gt; 
file of various items related to this article.  It includes
some Ruby code as a demonstration which requires a sqlite3 installation.
Also, I used Python to generate this document with all the color-coded sections. 
I've included that in case it is of interest
to anyone.  It requires the &lt;code&gt;Mako&lt;/code&gt; and &lt;code&gt;Pygments&lt;/code&gt; packages. 
&lt;/p&gt;



</description>
      <pubDate>Wed, 09 Jul 2008 07:00:37 -0700</pubDate>
      <link>http://www.killswitchcollective.com/articles/27</link>
      <guid>http://www.killswitchcollective.com/articles/27</guid>
    </item>
    <item>
      <title>Poor Man's Multi-Table Inheritance</title>
      <category>Development</category>
      <description>&lt;p&gt;A while ago, I was given the task of converting a working Java web application to Ruby on Rails. The word on the street was that Ruby on Rails was great for a 'green' project, lays i.e. starting from scratch - but not so good for a legacy application.&lt;/p&gt;
&lt;p&gt;However, since the database I was using already had an auto-increment primary-key column 'id' for every table, I didn't run into that many problems. There was, however, one bit of functionality I puzzled over for a while.&lt;/p&gt;
&lt;p&gt;The application was a project tracking application: projects with attached assignments, and each assigment was associated with a document of some sort. The document types weren't really related to each other all that much, except for a few common attributes and the fact that they could be assigned. To protect the names of the innocent, I will pretend the types of items were articles, books and blog posts. If you consider those, they don't have a lot in common. No article or blog post needs an isbn, and no book needs a date posted field. They are all pretty distinct conceptually; the commonality can be found in that each are something an editor might need to work on - i.e. they are all coherent bodies of text.&lt;/p&gt;
&lt;p&gt;I looked at the existing Java code. The application had classes &lt;code&gt;Article&lt;/code&gt;, &lt;code&gt;Book&lt;/code&gt; and &lt;code&gt;Post&lt;/code&gt; all derived from &lt;code&gt;Document&lt;/code&gt;. But there was no 'documents' table - only articles, books and posts.&lt;/p&gt;
&lt;p&gt;Technically this is Multi-table inheritance since each type of object gets its own table. This is not something supported by Rails. Rails supports the idea of Single Table Inheritance (STI): put every record in one table. This often works out very well because it is efficient and simple.&lt;/p&gt;
&lt;p&gt;However, I didn't want to force all these records into one table for a variety of reasons. One such reason is I wanted to make sure no Book was created without an isbn - but if I put them all in one table, the isbn field would have to allow for NULL (for articles for instance). Some would argue this an application level concern, but I think if you can guarantee data validity at the database level - and bolster that reliability on the application side - you are much better off than just merely relying on the application.&lt;/p&gt;
&lt;p&gt;Another reason was because I've found, when rewriting an application in Rails, the longer I can push forward keeping the original data structure intact the better. That way I can run both applications at the same time as I figure out the functionality of the first. In the ideal world, the functionality of the original app would be fully detailed in a spec somewhere. This project, unfortunately, was deeply rooted in the actual world.&lt;/p&gt;
&lt;p&gt;The currently working Java application was using an ORM, just like Rails does. The ORM for this project was something from the Apache group called the &lt;a href="http://db.apache.org/ojb/"&gt;&lt;code&gt;ObjectRelationalBridge&lt;/code&gt;&lt;/a&gt; or OJB for short. I looked into the code to see how it was achieving this multi-table inheritance. Basically it was using a simple table to keep track of what the next unique id should be for a given 'type'. Some databases support sequences that do this, but not MySQL 3.&lt;/p&gt;
&lt;p&gt;The name of the table was &lt;code&gt;sequences&lt;/code&gt; and it looked roughly like this:&lt;/p&gt;
&lt;style&gt;
  .table-def { border-collapse: collapse; width: 500px; margin-bottom: 12px;}
  .table-def td,th { text-align: left; border: 1px solid #CCC; width: 100px; }
&lt;/style&gt;

&lt;table class="table-def"&gt;
  &lt;caption&gt;sequences&lt;/caption&gt;
  &lt;tr&gt;
    &lt;th&gt;id&lt;/th&gt;
    &lt;th&gt;type_name&lt;/th&gt;
    &lt;th&gt;value&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;1&lt;/td&gt;
    &lt;td&gt;document&lt;/td&gt;
    &lt;td&gt;5825&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;Whenever it was time to create a new Article, Book or Post, the database went to this table looking for the 'value' in the row with the 'type_name' = 'document', used that value to assign an id in a table corresponding to the object, and incremented the value. So the next &lt;code&gt;Article&lt;/code&gt; would get an id of 5826 which would be stored in the id column of &lt;code&gt;articles&lt;/code&gt;, the next &lt;code&gt;Book&lt;/code&gt; would get an id of 5827 in the id column of &lt;code&gt;books&lt;/code&gt; and so on. Pretty simple.&lt;/p&gt;

&lt;p&gt;The other tables were structured something along these lines (this is a simplification):&lt;/p&gt;

&lt;p&gt;projects&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;id&lt;/li&gt;
  &lt;li&gt;subject&lt;/li&gt;
  &lt;li&gt;start_date&lt;/li&gt;
  &lt;li&gt;end_date&lt;/li&gt;
  &lt;li&gt;status&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;assignments&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;id&lt;/li&gt;
  &lt;li&gt;document_id&lt;/li&gt;
  &lt;li&gt;document_type&lt;/li&gt;
  &lt;li&gt;start_date&lt;/li&gt;
  &lt;li&gt;end_date&lt;/li&gt;
  &lt;li&gt;status&lt;/li&gt;
  &lt;li&gt;project_id&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;articles&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;id&lt;/li&gt;
  &lt;li&gt;title&lt;/li&gt;
  &lt;li&gt;author_id&lt;/li&gt;
  &lt;li&gt;published&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;books&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;id&lt;/li&gt;
  &lt;li&gt;title&lt;/li&gt;
  &lt;li&gt;author_id&lt;/li&gt;
  &lt;li&gt;isbn&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;posts&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;id&lt;/li&gt;
  &lt;li&gt;title&lt;/li&gt;
  &lt;li&gt;author_id&lt;/li&gt;
  &lt;li&gt;slug&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So how do I go about duplicating the functionality in Rails? Ideally, I'd like not to have to normalize all data into one big table, and ideally, I'd like to do something that doesn't mess with the internals of Rails, simply using the conventions that are already there. Over the years, I've found meddling with the internals of a framework can come back to bite you. Call me paranoid.&lt;/p&gt;
&lt;h3&gt;First Try&lt;/h3&gt;
&lt;p&gt;So, working in the most basic Rails idioms, I start off with something like this:&lt;/p&gt;
&lt;style type="text/css"&gt;
  .code-block span {
    font-family: 'Lucida Console', 'Courier', sans-serif;
    color: #000000;
    font-size: 9pt;
  }
  .code-block .S0 {
    color: #808080;
  }
  .code-block .S4 {
    color: #007F7F;
  }
  .code-block .S5 {
    font-weight: bold;
    color: #00007F;
  }
  .code-block .S6 {
    font-family: 'Lucida Console';
    color: #7F007F;
    font-size: 9pt;
  }
  .code-block .S8 {
    font-weight: bold;
    color: #0000FF;
  }
  .code-block .S9 {
    font-weight: bold;
    color: #007F7F;
  }
  .code-block .S10 {
    font-weight: bold;
    color: #000000;
  }
  .code-block .S14 {
    color: #C0A030;
  }
  .code-block .S15 {
    font-weight: bold;
    color: #A000A0;
  }
  .code-block .S20 {
    color: #000000;
    background: #DDD0DD;
    text-decoration: inherit;
  }
  .code-block .S21 {
    color: #7F007F;
    background: #DDD0DD;
    text-decoration: inherit;
  }

&lt;/style&gt;


&lt;pre class="code-block"&gt;
  &lt;span&gt;&lt;span class="S5"&gt;class&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S8"&gt;Project&lt;/span&gt;&lt;span class="S0"&gt; &amp;nbsp;&lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;ActiveRecord&lt;span class="S10"&gt;::&lt;/span&gt;Base&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;has_many&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:assignments&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S5"&gt;class&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S8"&gt;Sequence&lt;/span&gt;&lt;span class="S0"&gt; &amp;nbsp;&lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;ActiveRecord&lt;span class="S10"&gt;::&lt;/span&gt;Base&lt;br /&gt;
    &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S5"&gt;class&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S8"&gt;Article&lt;/span&gt;&lt;span class="S0"&gt; &amp;nbsp;&lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;ActiveRecord&lt;span class="S10"&gt;::&lt;/span&gt;Base&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;before_create&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:generate_key&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;def&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S9"&gt;generate_key&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;type_name&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S6"&gt;"document"&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;key&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;Sequence&lt;span class="S10"&gt;.&lt;/span&gt;find&lt;span class="S10"&gt;(&lt;/span&gt;&lt;span class="S14"&gt;:first&lt;/span&gt;&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:conditions&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;[&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S6"&gt;"type_name = ?"&lt;/span&gt;&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;type_name&lt;span class="S10"&gt;])&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;new_id&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;key&lt;span class="S10"&gt;.&lt;/span&gt;value&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;+&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S4"&gt;1&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;key&lt;span class="S10"&gt;.&lt;/span&gt;value&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;new_id&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;key&lt;span class="S10"&gt;.&lt;/span&gt;save&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;self&lt;/span&gt;&lt;span class="S10"&gt;.&lt;/span&gt;id&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;new_id&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S5"&gt;class&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S8"&gt;Book&lt;/span&gt;&lt;span class="S0"&gt; &amp;nbsp;&lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;ActiveRecord&lt;span class="S10"&gt;::&lt;/span&gt;Base&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;before_create&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:generate_key&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;def&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S9"&gt;generate_key&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;type_name&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S6"&gt;"document"&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;key&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;Sequence&lt;span class="S10"&gt;.&lt;/span&gt;find&lt;span class="S10"&gt;(&lt;/span&gt;&lt;span class="S14"&gt;:first&lt;/span&gt;&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:conditions&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;[&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S6"&gt;"type_name = ?"&lt;/span&gt;&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;type_name&lt;span class="S10"&gt;])&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;new_id&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;key&lt;span class="S10"&gt;.&lt;/span&gt;value&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;+&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S4"&gt;1&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;key&lt;span class="S10"&gt;.&lt;/span&gt;value&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;new_id&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;key&lt;span class="S10"&gt;.&lt;/span&gt;save&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;self&lt;/span&gt;&lt;span class="S10"&gt;.&lt;/span&gt;id&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;new_id&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S5"&gt;class&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S8"&gt;Post&lt;/span&gt;&lt;span class="S0"&gt; &amp;nbsp;&lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;ActiveRecord&lt;span class="S10"&gt;::&lt;/span&gt;Base&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;before_create&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:generate_key&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;def&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S9"&gt;generate_key&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;type_name&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S6"&gt;"document"&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;key&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;Sequence&lt;span class="S10"&gt;.&lt;/span&gt;find&lt;span class="S10"&gt;(&lt;/span&gt;&lt;span class="S14"&gt;:first&lt;/span&gt;&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:conditions&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;[&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S6"&gt;"type_name = ?"&lt;/span&gt;&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;type_name&lt;span class="S10"&gt;])&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;new_id&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;key&lt;span class="S10"&gt;.&lt;/span&gt;value&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;+&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S4"&gt;1&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;key&lt;span class="S10"&gt;.&lt;/span&gt;value&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;new_id&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;key&lt;span class="S10"&gt;.&lt;/span&gt;save&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;self&lt;/span&gt;&lt;span class="S10"&gt;.&lt;/span&gt;id&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;new_id&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;

  &lt;/pre&gt;
&lt;p&gt;That will take care of those &lt;code&gt;Document&lt;/code&gt; objects getting their correct id, then I just have to make the &lt;code&gt;Assignment&lt;/code&gt; reference it. This will work fine as a &lt;code&gt;belongs_to&lt;/code&gt; since there can only be one match per row.&lt;/p&gt;

&lt;pre class="code-block"&gt;
  &lt;span&gt;&lt;span class="S5"&gt;class&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S8"&gt;Assignment&lt;/span&gt;&lt;span class="S0"&gt; &amp;nbsp;&lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;ActiveRecord&lt;span class="S10"&gt;::&lt;/span&gt;Base&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;belongs_to&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:project&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;belongs_to&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:article&lt;/span&gt;&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:foreign_key&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S7"&gt;'document_id'&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;belongs_to&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:book&lt;/span&gt;&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:foreign_key&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S7"&gt;'document_id'&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;belongs_to&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:post&lt;/span&gt;&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:foreign_key&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S7"&gt;'document_id'&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
&lt;/pre&gt;

&lt;p&gt;That's all fine and good, but I wanted to clean up my code for 3 reasons:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;I've had to copy and paste the &lt;code&gt;generate_key&lt;/code&gt; and &lt;code&gt;before_create :generate_key&lt;/code&gt; code 3 times already. Not very &lt;code&gt;&lt;a href="http://c2.com/cgi/wiki?DontRepeatYourself"&gt;DRY&lt;/a&gt;&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;There is nothing in the code itself to indicate that there is a relationship between a &lt;code&gt;Book&lt;/code&gt;, an &lt;code&gt;Article&lt;/code&gt; and a &lt;code&gt;Post&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;I have to type in &lt;code&gt;belongs_to&lt;/code&gt; for each type of object. I figure there must be a way to make this more succinct.&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;Second Try&lt;/h3&gt;
&lt;p&gt;First order of business: fix all those redundant &lt;code&gt;generate_key&lt;/code&gt; methods. Well, how do you go about adding both class method (before_create) and an instance method to a class? Answer: a &lt;code&gt;Module&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The Rails library likes to use the &lt;code&gt;base.extend(SomeModule)&lt;/code&gt; convention by defining &lt;code&gt;ClassMethods&lt;/code&gt; and &lt;code&gt;InstanceMethods&lt;/code&gt; submodules. This works great and is a good convention to follow. If there are not many instance methods, though, I've found calling &lt;code&gt;module_eval&lt;/code&gt; within &lt;code&gt;ClassMethods&lt;/code&gt; will work just as well (see below).&lt;/p&gt;

&lt;pre class="code-block"&gt;
  &lt;span&gt;&lt;span class="S5"&gt;module&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S15"&gt;GeneratedKey&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;def&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S9"&gt;self&lt;/span&gt;&lt;span class="S10"&gt;.&lt;/span&gt;included&lt;span class="S10"&gt;(&lt;/span&gt;base&lt;span class="S10"&gt;)&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;base&lt;span class="S10"&gt;.&lt;/span&gt;extend&lt;span class="S10"&gt;(&lt;/span&gt;ClassMethods&lt;span class="S10"&gt;)&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;module&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S15"&gt;ClassMethods&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;def&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S9"&gt;has_generated_key&lt;/span&gt;&lt;span class="S10"&gt;(&lt;/span&gt;object_name&lt;span class="S10"&gt;)&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;object_name&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;object_name&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# add the before_create hook and make sure&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# we can access the object_name in instance&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# methods&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;class_eval&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="S20"&gt;-CODE&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;before_create :generate_key&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cattr_accessor :object_name&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@@object_name = object_name&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S20"&gt;CODE&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# add the generate_key instance method&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# NOTE: i've skimmed over worrying about Symbol&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# vs. String for clarity&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;module_eval&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="S20"&gt;-CODE&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def generate_key&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;type_name = object_name.to_s&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;key = Sequence.find(:first, :conditions =&amp;gt; [ "type_name = ?", type_name])&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new_id = key.value + 1&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;key.value = new_id&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;key.save&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.id = new_id&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S20"&gt;CODE&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
  &lt;/pre&gt;
  
&lt;p&gt;This means when I put &lt;code&gt;has_generated_key :some_value&lt;/code&gt; in the preface of a class definition, the class will call the &lt;code&gt;generate_key&lt;/code&gt; function before it runs create to generate the &lt;code&gt;id&lt;/code&gt; value from the sequences table. Now I can set up my classes using inheritance. The only odd thing about that is that I have to set the table name on subclasses (otherwise it will always look for a 'documents' table).&lt;/p&gt;

&lt;pre class="code-block"&gt;
  &lt;span&gt;&lt;span class="S5"&gt;class&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S8"&gt;Document&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;ActiveRecord&lt;span class="S10"&gt;::&lt;/span&gt;Base&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;include&lt;span class="S0"&gt; &lt;/span&gt;GeneratedKey&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;has_generated_key&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:document&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S5"&gt;class&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S8"&gt;Article&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;Document&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;set_table_name&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S6"&gt;"articles"&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S5"&gt;class&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S8"&gt;Book&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;Document&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;set_table_name&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S6"&gt;"books"&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
    &lt;br /&gt;
    &lt;span class="S5"&gt;class&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S8"&gt;Post&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;Document&lt;br /&gt;
    &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;set_table_name&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S6"&gt;"posts"&lt;/span&gt;&lt;br /&gt;
    &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;

  &lt;/pre&gt;
  
&lt;p&gt;The only thing left is to clean up that &lt;code&gt;belongs_to :book, belongs_to :article&lt;/code&gt; etc. stuff in the &lt;code&gt;Assignment&lt;/code&gt; class. Once again, I can just factor it out into a module.&lt;/p&gt;

  &lt;pre class="code-block"&gt;
    &lt;span&gt;&lt;span class="S5"&gt;module&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S15"&gt;GenericAssociations&lt;/span&gt;&lt;br /&gt;
      &lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;def&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S9"&gt;self&lt;/span&gt;&lt;span class="S10"&gt;.&lt;/span&gt;included&lt;span class="S10"&gt;(&lt;/span&gt;base&lt;span class="S10"&gt;)&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;base&lt;span class="S10"&gt;.&lt;/span&gt;extend&lt;span class="S10"&gt;(&lt;/span&gt;ClassMethods&lt;span class="S10"&gt;)&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
      &lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;module&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S15"&gt;ClassMethods&lt;/span&gt;&lt;br /&gt;
      &lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;def&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S9"&gt;belongs_to_type&lt;/span&gt;&lt;span class="S10"&gt;(&lt;/span&gt;object_name&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;implementations&lt;span class="S10"&gt;=[])&lt;/span&gt;&lt;br /&gt;
      &lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;object_name&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;object_name&lt;br /&gt;
      &lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# take the array of implementations and do belongs_to for&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# each one&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;implementations&lt;span class="S10"&gt;.&lt;/span&gt;each&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S5"&gt;do&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;|&lt;/span&gt;implementation&lt;span class="S10"&gt;|&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;code&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="S20"&gt;-CODE
        &lt;span class="S21"&gt;belongs_to &amp;nbsp;:#{implementation}, &lt;/span&gt;&lt;br /&gt;
        &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:foreign_key =&amp;gt; '#{object_name.to_s}_id'&lt;/span&gt;&lt;br /&gt;
        &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S20"&gt;CODE&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;class_eval&lt;span class="S0"&gt; &lt;/span&gt;code&lt;span class="S0"&gt; &lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
      &lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# build up an if for each type of object i.e. &lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# if article&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# &amp;nbsp;&amp;nbsp;return self.article&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# end&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# if book&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# &amp;nbsp;&amp;nbsp;return book&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# end&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# etc...&lt;/span&gt;&lt;br /&gt;
      &lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;if_loop&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S6"&gt;""&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;implementations&lt;span class="S10"&gt;.&lt;/span&gt;each&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S5"&gt;do&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;|&lt;/span&gt;implementation&lt;span class="S10"&gt;|&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;if_loop&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;+=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="S20"&gt;-CODE&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if self.#{object_name.to_s}_type == "#{implementation}"&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return self.#{implementation.to_s}&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S20"&gt;CODE&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
      &lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S2"&gt;# add the 'document' method&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;code&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="S20"&gt;-CODE&lt;br /&gt;
        &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def #{object_name.to_s}&lt;/span&gt;&lt;br /&gt;
        &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#{if_loop}&lt;/span&gt;&lt;br /&gt;
        &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end&lt;/span&gt;&lt;br /&gt;
        &lt;span class="S21"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S20"&gt;CODE&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
      &lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;module_eval&lt;span class="S10"&gt;(&lt;/span&gt;code&lt;span class="S10"&gt;)&lt;/span&gt;&lt;br /&gt;
      &lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
      &lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
      &lt;br /&gt;
      &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;

&lt;/pre&gt;

&lt;p&gt;This creates a method &lt;code&gt;belongs_to_type&lt;/code&gt; that is called during class definition, taking the type (as a &lt;code&gt;Symbol&lt;/code&gt;) and an &lt;code&gt;Array&lt;/code&gt; of the allowable subclasses as parameters. It also adds a method whose name is derived from the type (in this case &lt;code&gt;document&lt;/code&gt;) that will loop through all the values and pick out which kind it is based on a &lt;code&gt;*_type&lt;/code&gt; field value. Now I can just write this code for my &lt;code&gt;Assigment&lt;/code&gt; class:&lt;/p&gt;

&lt;pre class="code-block"&gt;
  &lt;span&gt;&lt;span class="S5"&gt;class&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S8"&gt;Assignment&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;&amp;lt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;ActiveRecord&lt;span class="S10"&gt;::&lt;/span&gt;Base&lt;br /&gt;
     &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;include&lt;span class="S0"&gt; &lt;/span&gt;GenericAssociations&lt;br /&gt;
      &lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;belongs_to&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:project&lt;/span&gt;&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:foreign_key&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S7"&gt;'project_id'&lt;/span&gt;&lt;br /&gt;
      &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;belongs_to_type&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:document&lt;/span&gt;&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;[&lt;/span&gt;&lt;span class="S14"&gt;:book&lt;/span&gt;&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:article&lt;/span&gt;&lt;span class="S10"&gt;,&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S14"&gt;:post&lt;/span&gt;&lt;span class="S10"&gt;]&lt;/span&gt;&lt;br /&gt;
       &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;

&lt;/pre&gt;


&lt;p&gt;So the only major surgery I might have had to do on the original database design was to add a &lt;code&gt;document_type&lt;/code&gt; field to the assigments table. But that's it and, in my case, there was already a field by that name anyway because it was still necessary for the UI. Now I can write code like this to get the &lt;code&gt;Document&lt;/code&gt; of an &lt;code&gt;Assignment&lt;/code&gt; (and because of duck-typing - any field I need I can just reference when I need it):&lt;/p&gt;

&lt;pre class="code-block"&gt;
   &lt;span&gt;project&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;Project&lt;span class="S10"&gt;.&lt;/span&gt;find&lt;span class="S10"&gt;(&lt;/span&gt;&lt;span class="S4"&gt;1&lt;/span&gt;&lt;span class="S10"&gt;)&lt;/span&gt;&lt;br /&gt;
          &lt;br /&gt;
          project&lt;span class="S10"&gt;.&lt;/span&gt;assignments&lt;span class="S10"&gt;.&lt;/span&gt;each&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S5"&gt;do&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;|&lt;/span&gt;assignment&lt;span class="S10"&gt;|&lt;/span&gt;&lt;br /&gt;
          &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;print&lt;span class="S0"&gt; &lt;/span&gt;assignment&lt;span class="S10"&gt;.&lt;/span&gt;document&lt;br /&gt;
          &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;

&lt;/pre&gt;

&lt;p&gt;I can still retrieve only books, or only articles:&lt;/p&gt;

&lt;pre class="code-block"&gt;
     &lt;span&gt;articles&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;Article&lt;span class="S10"&gt;.&lt;/span&gt;find&lt;span class="S10"&gt;(&lt;/span&gt;&lt;span class="S14"&gt;:all&lt;/span&gt;&lt;span class="S10"&gt;)&lt;/span&gt;&lt;br /&gt;
       articles&lt;span class="S10"&gt;.&lt;/span&gt;each&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S5"&gt;do&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;|&lt;/span&gt;article&lt;span class="S10"&gt;|&lt;/span&gt;&lt;br /&gt;
       &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;print&lt;span class="S0"&gt; &lt;/span&gt;article&lt;br /&gt;
       &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
       &lt;br /&gt;
       books&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;Book&lt;span class="S10"&gt;.&lt;/span&gt;find&lt;span class="S10"&gt;(&lt;/span&gt;&lt;span class="S14"&gt;:all&lt;/span&gt;&lt;span class="S10"&gt;)&lt;/span&gt;&lt;br /&gt;
       books&lt;span class="S10"&gt;.&lt;/span&gt;each&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S5"&gt;do&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;|&lt;/span&gt;book&lt;span class="S10"&gt;|&lt;/span&gt;&lt;br /&gt;
       &lt;span class="S0"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;print&lt;span class="S0"&gt; &lt;/span&gt;book&lt;br /&gt;
       &lt;span class="S5"&gt;end&lt;/span&gt;&lt;br /&gt;
 &lt;/pre&gt;
 
&lt;p&gt;And I can still create an &lt;code&gt;Article&lt;/code&gt;, &lt;code&gt;Book&lt;/code&gt; or &lt;code&gt;Post&lt;/code&gt; the same way I would any other object:&lt;/p&gt;

&lt;pre class="code-block"&gt;
    &lt;span&gt;article&lt;span class="S0"&gt; &lt;/span&gt;&lt;span class="S10"&gt;=&lt;/span&gt;&lt;span class="S0"&gt; &lt;/span&gt;Article&lt;span class="S10"&gt;.&lt;/span&gt;new&lt;br /&gt;
      article&lt;span class="S10"&gt;.&lt;/span&gt;save&lt;/span&gt;
  &lt;/pre&gt;
  
&lt;p&gt;Download files here: &lt;a href="http://www2.killswitchcollective.com/articles/multi_table_article.zip"&gt;multi_table_article.zip&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; The download includes a Rakefile in which you need to set up the db, username and password that matches your own. Then run &lt;code&gt;rake&lt;/code&gt;. The default task runs some tests using a Ruby version of &lt;code&gt;doctest&lt;/code&gt;, something from the Python world I'd like to see used in the Ruby world. The basic idea of doctest is that you can copy the results of an interactive interpreter session (in Ruby's case IRB) into comments in your Ruby code and rerun those transcripts as a form of regression testing. For example:&lt;/p&gt;

&lt;pre&gt;
#doctest Check that 1 + 1 = 2
&amp;gt;&amp;gt; 1 + 1
=&amp;gt; 2
&amp;gt;&amp;gt; 2 + 3
=&amp;gt; 5
=end
&lt;/pre&gt;

&lt;p&gt;I included a slightly modified version so that I could run the test from Rake. For a more official version see &lt;a href="http://code.google.com/p/ruby-roger-useful-functions/wiki/DocTest"&gt;http://code.google.com/p/ruby-roger-useful-functions/wiki/DocTest&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Fri, 18 Apr 2008 12:14:29 -0700</pubDate>
      <link>http://www.killswitchcollective.com/articles/15</link>
      <guid>http://www.killswitchcollective.com/articles/15</guid>
    </item>
  </channel>
</rss>
