<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Perspectives : Writings from The Killswitch Collective</title>
    <link>http://www.killswitchcollective.com/blog/index.xml</link>
    <description>Perspectives is the blog of The Killswitch Collective, a Chicago-based web development, design and communication firm.</description>
    <language>en-us</language>
    <item>
      <title>Possibilities and Pitfalls with Adobe Air</title>
      <category>Development</category>
      <description>&lt;style&gt;
strong { font-weight: bold }
&lt;/style&gt;

&lt;h3&gt;From the Browser to the Desktop&lt;/h3&gt;

&lt;p&gt;As websites graduate to version 2.0 with their dynamic user-generated data-driven content we are adjusting our vocabulary to talk about them. We are no longer just building sites, we are launching applications. Indeed many applications from the desktop realm are being replicated online. Google Documents, for example, could hypothetically replace the need for something like Microsoft Office, but with the added benefit of everything being a part of the cloud so that a document may be accessed and edited by multiple users from multiple locations, independent of each users' operating system. &lt;/p&gt;

&lt;p&gt;This isn't a one way street, desktop applications do not all live in the 'desktop bubble'. At startup many applications check for updates and external information, like iTunes downloaded track information, album artwork and so on.&lt;/p&gt;  

&lt;p&gt;Lately the line between the internet and the hard drive has become a bit blurred. Several technologies are in development that allow web developers to create what appear to be full fledged desktop applications using HTML, CSS, Javascript, Actionscript &#8212; the Web oriented tools we already know. An overly simplified description of this is a browserless Web application that doesn't necessarily use the Web. Google Gears, Mozilla's Prism and Adobe AIR are a few of the efforts put forward recently to move the web onto the desktop to varying degrees.&lt;/p&gt; 

&lt;p&gt;I recently had the opportunity to try out one of these "horseless carriages". What follows is an overview of my experience with the Adobe Integrated Runtime (AIR), formerly known as Apollo.&lt;/p&gt;

&lt;h3&gt;The Pieces&lt;/h3&gt;

&lt;p&gt;There are two pieces to any AIR application. First there's the runtime environment which handles HTML rendering and all of the difficult behind-the-scenes stuff that us web developers would rather not worry about. The second piece is the actual application composed of your regular Web technologies (HTML, Javascript, PDF's, video, Flash, etc).&lt;/p&gt;

&lt;p&gt;When a user installs an AIR application for the first time they must install the runtime environment as well. Later, when the user installs another AIR app, they will not have to download the already installed runtime. The whole download and installation process is designed to be "seamless", appearing as if the user is downloading just one item regardless of whether they need to download AIR or not. As I'll explain later, however, it is not &lt;em&gt;always&lt;/em&gt; so smooth.&lt;/p&gt;

&lt;h3&gt;Benefits&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;So why build an AIR application?&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;Why put a Web application on your desktop? One of the advantages that all of the aforementioned tools cite is the ability to work offline. Granted, reliable internet access is becoming less and less of an issue, but what good is any Web application when you don't have a working internet connection? Working offline usually means storing information in a database and synching that information with a master database when a connection becomes available later. AIR comes packaged with SQLite for this purpose. Alternatively data can be stored as XML and can even be encrypted. Furthermore, if it is not necessary to store some information online, storing it locally can save some time because data does not need to be sent back and forth. This all makes for a smoother, persistent user experience.&lt;/p&gt; 

&lt;p&gt;&lt;strong&gt;Consistancy&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;AIR uses the WebKit HTML rendering engine which recently passed the Acid3 test for Web standards, so you can rest assured that your floats will properly float and your padding will properly pad. No need to hack together special code for &lt;em&gt;you-know-who&lt;/em&gt;.&lt;/p&gt; 

&lt;p&gt;&lt;strong&gt;OS integration&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;AIR provides a windowing API that allows you to interact with the operating system's windows. You can set the size and position of windows, restrict them to a range of sizes, dictate whether they can be resized or minimized, display your app fullscreen, or run the app with no window at all. You can even create your own custom window GUI. One of the advantages of having this kind of control is that browser windows often have elements (back/forward and refresh buttons, search bar, etc) that are not necessarily relevant to every application. Sometimes less is more. AIR also allows for other kinds of integration with the desktop including access to the file system, clipboard data, drag and drop events and so on.&lt;/p&gt; 

&lt;h3&gt;The Seams&lt;/h3&gt;
&lt;p&gt;While all of this is good and exciting and lets web developers pretend that we can build desktop applications, AIR's system is not perfect.&lt;/p&gt; 

&lt;p&gt;&lt;strong&gt;AIR detection&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As I mentioned before, any AIR app is composed of two pieces; the runtime environment and implementation code. If the user already has AIR installed it doesn't make much sense to install it again. Adobe provides a Flash movie that they call a "badge" that functions as a button for downloading the application. More importantly, it also performs a bit of magic to determine whether the user has AIR installed already or not. If they don't, it downloads both AIR and your app and installs them together all in one smooth and easy action.&lt;/p&gt; 

&lt;p&gt;This is all fine and well except for the fact that the badge requires the latest version of Flash in order to do this. A bit of plugin version detection can render an alternative page from which the user can download first AIR and then your app. This isn't so bad except for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If they don't install AIR first followed by your app second, the installation of your app will fail.&lt;/li&gt;
&lt;li&gt;Users often don't know what they have installed on their computer and may try to install AIR unnecessarily.&lt;/li&gt;
&lt;li&gt;Explaining to your users why they need to download and install item 'A' first in order to install item 'B' may lead to a bit of required reading and confusion. Such messages are in danger of being ignored as a bunch of legal mumbo jumbo or simply as a waste of time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Granted, the users who will be tripped up by such things are probably a small minority of users lacking in computer savviness and patience, but you should still be aware of who your audience is. If they are likely to have trouble with the two-piece download then chances are good that their version of Flash Player is not even current enough to do the AIR detection, thus all but defeating the purpose of the "seamless" download feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Certificates&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Because AIR apps can be given access to the user's system and transfer data over the internet there is an obvious security concern. Adobe requires that AIR apps be packaged with a security certificate (hopefully) verifying the authenticity of the application. This is designed to prevent someone else from maliciously altering your app and redistributing it, getting you into a heap of trouble.&lt;/p&gt;

&lt;p&gt;You certainly don't want to get into trouble because someone posing as you has done something nasty to your users, but preventing this isn't ideal either. The solution is to purchase a certificate from a certificate authority such as Thawte or Verisign for around $300 a year, but some developers or clients may consider this to be an unnecessary expense. This doesn't actually prevent anyone from creating a fraudulent version of your app, it just proves that you are the one who packaged your app.&lt;/p&gt; 

&lt;p&gt;The alternative is to create a self-signed certificate, and Adobe provides some tools for easily doing this. The only problem with self-signing your certificate is that when the user installs your app they are presented with this window:&lt;/p&gt;

&lt;p style="text-align: center;"&gt;&lt;img src="http://www2.killswitchcollective.com/articles/air/install_window.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;The language here is intentionally ominous. You are most likely creating something innocent, but your users may be scared off by the idea of giving someone of unknown identity unrestricted access to their computer. I don't know that there is a better solution to the security issue but it is something to consider.&lt;/p&gt;

&lt;p&gt;Perhaps I'm griping about things that are inevitable and/or insignificant, but there's my two cents. I do think AIR is worth a try, at least to impress your friends with your mad desktop app building skills.&lt;/p&gt;
</description>
      <pubDate>Fri, 16 May 2008 11:46:49 -0700</pubDate>
      <link>http://www.killswitchcollective.com/articles/20</link>
      <guid>http://www.killswitchcollective.com/articles/20</guid>
    </item>
    <item>
      <title>We Are Into Post-Digital Creation Culture</title>
      <category>Design</category>
      <description>As designers, we are constantly seeking new, innovative ways to communicate the messages of our clients. This May, our design team traveled to OFFF 2008 in Lisbon, Portugal to soak up some inspiration, see what other designers across the world are creating, and to bring that knowledge back to Killswitch to spark new ideas for future creations.
&lt;br&gt;&lt;br&gt;
OFFF is a 3-day design festival exploring the latest ideas in a new visual culture, covering everything from graphic design and visual communication to motion graphics and sonic landscapes to advanced forms of interaction, via physical space or digital space. Rather than thinking in terms of the post-apocalyptic world where machines have taken over&#8212;think &lt;i&gt;Terminator&lt;/i&gt; or &lt;i&gt;Blade Runner&lt;/i&gt;&#8212;as we did back in the '80s, the new post-digital creation culture considers what we can create next in this diverse, digital spectrum. Now that we have the technology in place and have studied and explored and created, the wi-fi artists of today are re-examining our relationship with technology to push and blur boundaries and to explore new digital avenues. Artists are turning to programming, programmers are turning to music, and musicians are turning to art. As these areas overlap, new breakthroughs emerge. The same code used to generate colors and patterns can be used to create artwork for the walls of your house. Personal data can be calculated and mapped to create a visual diary of communication patterns. A computer's capture and storing of real-time movement can be used to recreate those movements in a simulated virtual environment. These explorations are opening up brand new avenues of communication and interaction, and have exciting implications for the future of our digital culture.
&lt;br&gt;&lt;br&gt;
More than 2500 people attended OFFF this year, and the attendees included designers, artists, musicians, and developers from all over the world. Held at the LX Factory, an immense industrial space tucked away amongst brightly-tiled row-houses and cobblestone streets in the Alcantara area of Lisbon, the event boasted floor to ceiling presentation screens, interactive art installations, and a wealth of DJ entertainment. Every presentation in this talented line-up was inspiring, and each one explored a different area of visual communication. Karlssonwilker made us laugh with all of their crazy design experiences, Alex Trochut amazed us with his illustrative typography, Joshua Davis fascinated us with his dynamically generated works of art, Craig Swann motivated us with his playful outlook on the world, Andreas M&#252;ller mesmorized us with his swimming type and fluid forms, Fakepilot caught our attention with his Daft Punk-style intro, Hellohikimori engaged us with their colorful, immersive websites, Hi-Res! amused us with their rejection of the expected, and Fallon entertained us with their simple yet highly original advertisements, proving that a big budget is less powerful than a big idea.
&lt;br&gt;&lt;br&gt;
The diverse and groundbreaking ideas from speakers at this event prove that what we think of now as "new media" is just the tip of the iceberg. Today's designers and artists defy classification, reject the limitations of technology, and develop their own standards. OFFF was a truly memorable event and one that moves all of us forward.
&lt;br&gt;&lt;br&gt;
&lt;img src="http://www2.killswitchcollective.com/articles/offf_2008_preview.jpg"&gt;</description>
      <pubDate>Fri, 16 May 2008 10:53:52 -0700</pubDate>
      <link>http://www.killswitchcollective.com/articles/19</link>
      <guid>http://www.killswitchcollective.com/articles/19</guid>
    </item>
    <item>
      <title>Application States with ActsAsStateMachine</title>
      <category>Development</category>
      <description>&lt;style&gt;
pre {
  margin: 10px 0;
}
&lt;/style&gt;
&lt;p&gt;When mapping the flow of an application, we usually have various states for a single object. These states are then joined with lines showing how an object can transition from one state to another. Each state will be unique; it may be just simply have a status flag, or it could open up new functionality within the app. For example, an e-commerce application may have a system for package tracking. When customer pays for a product, that product will have a status of 'paid'. Based on the paid status, the seller will be notified that the product needs to be shipped and the customer will gain the ability to write a review.&lt;/p&gt;

&lt;p&gt;Traditionally, we would have to create methods that dealt with switching and checking states. Thankfully that has all changed with the wonderful ActsAsStateMachine plugin. What I like best about ActsAsStateMachine is that it has its own methods in handling states and event transitions that won't get lost with other methods within the model. I will go more into detail soon, but first we need to create a quick application to showcase the awesomeness of ActsAsStateMachine!&lt;/p&gt;

&lt;p&gt;Let's create a baseball application that will display scores of current games. Each game will be in the database before it starts, allowing users to view if tickets are available. When the game is live, ticket information will not be needed and a scoreboard with all the innings will be displayed instead. When the game is over, only the final score will appear with information for the next game.&lt;/p&gt;

&lt;p&gt;To get started, first install the plugin:&lt;/p&gt;
&lt;pre style="background-color:#2B2B2B;color:#E6E1DC;padding:6px;overflow:auto;line-height:12px;font-size:12px;padding:6px;"&gt;
ruby script/plugin install http://elitists.textdriven.com/svn/plugins/acts_as_state_machine/trunk/
&lt;/pre&gt;
&lt;p&gt;Then within our Game model, add the following:&lt;/p&gt;

&lt;pre style="background-color:#2B2B2B;color:#E6E1DC;padding:6px;overflow:auto;line-height:12px;font-size:12px;padding:6px;"&gt;&lt;pre&gt;&lt;span class="ident"&gt;acts_as_state_machine&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:column&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:game_state&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:initial&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:pending&lt;/span&gt;
&lt;/pre&gt;&lt;/pre&gt;

&lt;p&gt;Here we are simply telling the Game model to act as a finite state machine. We are setting the column that will hold the Game's state as &lt;code&gt;game_state&lt;/code&gt;. By default, ActsAsStateMachine will use the state column. This can cause problems since a state column in most applications will hold location information. So by specifying a column name, we can avoid column problems. The last portion will set the initial state of a newly created Game object to pending. It is similar to creating a migration and setting the column to a default value, but if that default value ever needs to change, a new migration would have to be created. By setting a initial value, we can easily change just that one line.
Now lets define all the various states for a game. Based on the app's requirements, a game will have three states: Pending, Live and Final. Defining states is as easy as adding:&lt;/p&gt;

&lt;pre style="background-color:#2B2B2B;color:#E6E1DC;padding:6px;overflow:auto;line-height:12px;font-size:12px;padding:6px;"&gt;&lt;pre&gt;&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:pending&lt;/span&gt;
&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:live&lt;/span&gt;
&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:final&lt;/span&gt;
&lt;/pre&gt;&lt;/pre&gt;

&lt;p&gt;Our Game model now has states and with each state definition new methods are created. These methods may come in handy for state-specific validations or can be used in conditionals within the views that will load certain partials. For example, instead of doing &lt;code&gt;@game.state == "live"&lt;/code&gt;, we can use &lt;code&gt;@game.live?&lt;/code&gt;.
With all the states for Game defined, it is time to add events that will transition the game from one state to another. ActsAsStateMachine has it's own methods for creating events, which is basically a loop. We start by defining the event loop, giving the event a name and then specifying transitions within.&lt;/p&gt;

&lt;pre style="background-color:#2B2B2B;color:#E6E1DC;padding:6px;overflow:auto;line-height:12px;font-size:12px;padding:6px;"&gt;&lt;pre&gt;&lt;span class="ident"&gt;event&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:start_game&lt;/span&gt; &lt;span style="color:#CC7833"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;transitions&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:from&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:pending&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:live&lt;/span&gt;
&lt;span style="color:#CC7833"&gt;end&lt;/span&gt;

&lt;span class="ident"&gt;event&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:end_game&lt;/span&gt; &lt;span style="color:#CC7833"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;transitions&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:from&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:live&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:final&lt;/span&gt;
&lt;span style="color:#CC7833"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/pre&gt;

&lt;p&gt;With these events we can now transition a game object's state by using &lt;code&gt;@game.start_game!&lt;/code&gt; and &lt;code&gt;@game.end_game!&lt;/code&gt;. Keep in mind each event can hold many transitions, we can combine the two event methods into one as long as the &lt;code&gt;:from&lt;/code&gt; portion is different. It wouldn't make much sense in the context of our example, however, because it is linear.&lt;/p&gt;

&lt;p&gt;We have had the app up for a few weeks now and baseball fans are really digging the slick up-to-the-minute scores that we update through AJAX calls. Eventually we will run into our first rain delay, which will lead to problems. Fans will start thinking the app has stopped updating and our servers will keep running expensive AJAX updates over a game that has turned idle. It is time to introduce the rain delay state and event, making our simple app less linear.&lt;/p&gt;

&lt;pre style="background-color:#2B2B2B;color:#E6E1DC;padding:6px;overflow:auto;line-height:12px;font-size:12px;padding:6px;"&gt;&lt;pre&gt;&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:rain_delay&lt;/span&gt;
  
&lt;span class="ident"&gt;event&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:rainout&lt;/span&gt; &lt;span style="color:#CC7833"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;transitions&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:from&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:live&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:rain_delay&lt;/span&gt;
&lt;span style="color:#CC7833"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/pre&gt;

&lt;p&gt;With a rain delay, a choice will be made by the umpires. If enough innings have been played, the game can end with the current score being final. Otherwise the game can be canceled and be rescheduled at a later date.  Let's add the new canceled state and transitions events from &lt;code&gt;rain_delay&lt;/code&gt;. Lets also add a new transition into the &lt;code&gt;end_game&lt;/code&gt; event for &lt;code&gt;rain_delay&lt;/code&gt; games that have played enough innings to be called final.&lt;/p&gt;

&lt;pre style="background-color:#2B2B2B;color:#E6E1DC;padding:6px;overflow:auto;line-height:12px;font-size:12px;padding:6px;"&gt;&lt;pre&gt;&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:canceled&lt;/span&gt;
  
&lt;span class="ident"&gt;event&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:end_game&lt;/span&gt; &lt;span style="color:#CC7833"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;transitions&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:from&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:live&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:final&lt;/span&gt;
  &lt;span class="ident"&gt;transitions&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:from&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:rain_delay&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:final&lt;/span&gt;
&lt;span style="color:#CC7833"&gt;end&lt;/span&gt;

&lt;span class="ident"&gt;event&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:cancel_game&lt;/span&gt; &lt;span style="color:#CC7833"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;transitions&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:from&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:rain_delay&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:canceled&lt;/span&gt;
&lt;span style="color:#CC7833"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/pre&gt;

&lt;p&gt;Let's say if a game is canceled it is still in our database for historical purposes and a new game will be created with some data from the canceled game. Let's also say that the &lt;code&gt;reschedule_game&lt;/code&gt; method will create a new game from a canceled game. ActsAsStateMachine lets us attach callbacks like state events. Basically we want to run the reschedule_game method whenever a game's state changes to canceled. To accomplish this, we simply add &lt;code&gt;:enter =&gt; methodname&lt;/code&gt; after defining the state as so:&lt;/p&gt;

&lt;pre style="background-color:#2B2B2B;color:#E6E1DC;padding:6px;overflow:auto;line-height:12px;font-size:12px;padding:6px;"&gt;&lt;pre&gt;&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:canceled&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:enter&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:reschedule_game&lt;/span&gt;
&lt;/pre&gt;&lt;/pre&gt;

&lt;p&gt;Now whenever a game's state switches to canceled, a copy will automatically be made through the &lt;code&gt;reschedule_game&lt;/code&gt; method. Besides enter, ActsAsStateMachine also has after and exit options. After would run after the state switch has been made and exit will be executed when the object is transitioning away from the state.&lt;/p&gt;

&lt;p&gt;ActsAsStateMachine comes in handy when a object's status goes through many changes. I hope I have shown you how easy it is to define states, create transitions and implement ActsAsStateMachine into a model. Here is what our final code looks like:&lt;/p&gt;

&lt;pre style="background-color:#2B2B2B;color:#E6E1DC;padding:6px;overflow:auto;line-height:12px;font-size:12px;padding:6px;"&gt;&lt;pre&gt;&lt;span class="ident"&gt;acts_as_state_machine&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:column&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:game_state&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:initial&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:pending&lt;/span&gt;

&lt;span style="color:#BC9458"&gt;# States&lt;/span&gt;
&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:pending&lt;/span&gt;
&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:live&lt;/span&gt;
&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:final&lt;/span&gt;
&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:rain_delay&lt;/span&gt;
&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:canceled&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:enter&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:reschedule_game&lt;/span&gt;

&lt;span style="color:#BC9458"&gt;# Transition Events&lt;/span&gt;
&lt;span class="ident"&gt;event&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:start_game&lt;/span&gt; &lt;span style="color:#CC7833"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;transitions&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:from&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:pending&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:live&lt;/span&gt;
&lt;span style="color:#CC7833"&gt;end&lt;/span&gt;

&lt;span class="ident"&gt;event&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:end_game&lt;/span&gt; &lt;span style="color:#CC7833"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;transitions&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:from&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:live&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:final&lt;/span&gt;
  &lt;span class="ident"&gt;transitions&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:from&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:rain_delay&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:final&lt;/span&gt;
&lt;span style="color:#CC7833"&gt;end&lt;/span&gt;

&lt;span class="ident"&gt;event&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:rainout&lt;/span&gt; &lt;span style="color:#CC7833"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;transitions&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:from&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:live&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:rain_delay&lt;/span&gt;
&lt;span style="color:#CC7833"&gt;end&lt;/span&gt;

&lt;span class="ident"&gt;event&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:cancel_game&lt;/span&gt; &lt;span style="color:#CC7833"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;transitions&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:from&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:rain_delay&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#6E9CBE"&gt;:canceled&lt;/span&gt;
&lt;span style="color:#CC7833"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/pre&gt;</description>
      <pubDate>Fri, 09 May 2008 14:13:18 -0700</pubDate>
      <link>http://www.killswitchcollective.com/articles/18</link>
      <guid>http://www.killswitchcollective.com/articles/18</guid>
    </item>
    <item>
      <title>Data Back-up Solutions</title>
      <category>Research</category>
      <description>&lt;h3&gt;Living with Regret&lt;/h3&gt;
&lt;p&gt;Many of my articles deal with researching and the effect it has on the company's dealings with clients. Now, I feel that another issue has to take the forefront: system and assets back-up. I have had the worst luck lately when dealing with computers, company data and the need to back things up. Working at a Web firm, it's easy to take backing up for granted because everything already has two copies: all the articles, client assets, help archives, etc. exist both on our local drives as well as on the Web servers. But this is the exception to the rule as most businesses don't have such redundancy in place.&lt;/p&gt;

&lt;p&gt;I still can recall my personal data loss tragedies... One day I accidentally erased my first PC's 500MB drive trying to retrieve a paper I wrote for history class. Another day I lost all my documents, media and impressive collection of ms-dos based games to a suddenly defective drive. The memories haunt me...&lt;/p&gt;

&lt;p&gt;Thanks to continually dropping technology prices, a back-up array or separate storage solution has fallen within reach of the average home user. Still, people are losing their data in their own personal tragedies every day, simply because data back-ups only gets serious consideration about five seconds too late.
&lt;/p&gt;
&lt;p&gt;A while ago, I consulted with a company on a new computer setup. Under normal circumstances, this should have been a simple, straight-forward, "Buy, Back-up, Program and Go." Unfortunately, their current computer was using Windows '98 and its drive had crashed an hour before I had gotten there. Once a Windows '98 drive has spun its last sector there's simply no hope. I had the unpleasant honor of explaining that their data and projects had gone to 'a better place', all because they hadn't taken my suggestion to pick up a 80GB back-up drive for an easy $70. 
&lt;/p&gt;
&lt;p&gt;The data of any business is a precious commodity, and working without a back-up system is walking on a tightrope without a net. Thankfully, there are many data storage solutions that can meet the needs and budgets of business and personal users.
&lt;/p&gt;

&lt;h3&gt;Hardware Solutions&lt;/h3&gt;

&lt;p&gt;One simple solution that a user can get either for business or personal use is a 1TB (terabyte) drive, an amount that is virtually impossible to fill without a vast media collection. While this drive is not lacking in capacity, I would be concerned that a drive failure would result in such an immense loss. Personally, I prefer to spread out all my data on multiple drives so that if one fails I have a copy of it somewhere else. 
&lt;/p&gt;

&lt;p&gt;For a business or user that needs to back-up multiple computers, a networked or wireless drive may be an excellent solution. The &lt;a href="http://www.allstarshop.com/shop/simprod.asp?pid=10673&amp;ad=pwatch"&gt;D-Link DSM-G600 Wireless G Network Storage Enclosure&lt;/a&gt; allows you to place drives on a shared network. Another solution would be an attached network storage like the &lt;a href="http://www.deal-stop.com/itemdesc.asp?ic=33349OBOX"&gt;Iomega StorCenter 1TB WI-FI Network storage drive&lt;/a&gt;. The drives are already formatted and ready to go, and have wireless connectivity and network adapters for additional server storage.
&lt;/p&gt;
&lt;p&gt;For those users that need storage on a tight budget I would recommend one of two choices. A 500GB external drive like the &lt;a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16822148235"&gt;Seagate Freeagent Desktop&lt;/a&gt; costs between $90-150. For the more ambitions, you can combine a 500GB hard drive (about $100 and getting cheaper) with an external enclosure (check out the &lt;a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16817193041"&gt;Eagle Consus W-Series SATA to USB&lt;/a&gt; and the &lt;a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16817193026"&gt;eSATA External Storage System (ET-CSWESU2-BK) Black&lt;/a&gt;).
&lt;/p&gt;
&lt;p&gt;I recently have been introduced to an innovative storage solution called &lt;a href="http://www.drobo.com/index.html"&gt;Drobo&lt;/a&gt;, the world's first storage robot. I have to say the concept looks promising, I wish that I could get my hands on one to play with. Based on information from their site, here are some pros and cons of the Drobo:
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Pros:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can swap drives (SATA only please) in and out of Drobo and data is automatically and redundantly redistributed to protect from drive failure. This is like RAID only better: I don't trust RAID based systems because of their rigid nature. With RAID, adding or removing a drive requires a full reboot and, depending on the RAID configuration, a single drive failure could still corrupt the whole array.&lt;/li&gt;

&lt;li&gt;No babysitting required, the Drobo takes care of itself.&lt;/li&gt;

&lt;li&gt;There's no loss of productivity between files and data while drive swapping.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;Cons:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Drobo's price of $499 and up is a significant roadblock to becoming an early adopter. Keep an eye out for this price to start dropping, however, as the technology spreads.&lt;/li&gt;

&lt;li&gt;It only comes with USB 2.0 connections, eSATA and standard networking capabilities would have been welcome features. &lt;/li&gt;        
&lt;/ul&gt;                 

&lt;p&gt;While using an external drive is sufficient for many purposes, some users are turning to software solutions that help automate the back-up process.
&lt;/p&gt;
&lt;h3&gt;Software Solutions for Macs&lt;/h3&gt;

&lt;p&gt;There is an integrated solution that ships with every new Mac called &lt;a href="http://www.apple.com/macosx/features/timemachine.html"&gt;Time Machine&lt;/a&gt;. Time Machine makes a full copy of your computer's data when it is initialized and then simply records the changes made to your data. Its automation is paired with a fun, intuitive user interface that let's a user 'fly through time' to find the file that was deleted, edited or saved over. Thanks to its excellent integration within OSX and other peripherals like &lt;a href="http://www.macmall.com/macmall/promotions/custom5.asp?p=timecapsule"&gt;Time Capsule&lt;/a&gt;, Time Machine is the likely choice for backing up Macs.
&lt;/p&gt;
&lt;p&gt;One other possible solution is &lt;a href="http://www.macbackups.com/"&gt;MacBackups&lt;/a&gt; which does online, offsite back-ups of your Mac.      
&lt;/p&gt;
&lt;h3&gt;Software Solutions for PCs&lt;/h3&gt;

&lt;p&gt;Nero offers a back-up feature in its &lt;a href="http://nero.com"&gt;Nero 8&lt;/a&gt; software. It can automate the back-up process and allows for manual back-ups as well
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.symantec.com/norton/products/overview.jsp?pcid=br&amp;pvid=ghost14"&gt;Norton Ghost&lt;/a&gt; is also an excellent option. It's not a tradition back-up program in that you have to restart and load PC-dos in order to make your back-up. The benefit to this approach is that it can restore your entire computer with your programs intact rather than simply your documents.
&lt;/p&gt;
&lt;p&gt;I hope this is a resource that can help make your back-ups easy and painless, but no matter what, please, PLEASE, back up your files!&lt;/p&gt;</description>
      <pubDate>Thu, 01 May 2008 07:31:44 -0700</pubDate>
      <link>http://www.killswitchcollective.com/articles/17</link>
      <guid>http://www.killswitchcollective.com/articles/17</guid>
    </item>
    <item>
      <title>Expanding Your Web Typography Arsenal</title>
      <category>Development</category>
      <description>&lt;style type="text/css"&gt;
  pre {
    font-family: courier, "courier new", serif;
    font-size:12px;
    background-color: #f0f0f0;	
    border: solid 1px #ccc;
    padding: 10px;
    margin: 5px 5px 5px 5px;
    overflow:auto;
  }
&lt;/style&gt;
    &lt;p&gt;A long standing complaint among web designers is the lack of variety in typefaces that can be reliably used and guaranteed to display across all web browsers and operating systems. For the most part, designers are stuck with Arial, Verdana, Georgia, Times, or the standard serif and sans-serif implementations, though this list can be supplemented by expanding the &lt;a href='http://24ways.org/2007/increase-your-font-stacks-with-font-matrix'&gt;font stack&lt;/a&gt;. Granted, the standard typefaces are optimized for web and will render well across the the board at various sizes, with anti-aliasing on or off, but occasionally designers need to spice things up with additional typefaces in an accessible, search-friendly manner without resorting to pre-rendered images. Two options currently exist to help remedy the situation: sIFR and Safari Embedded Fonts.&lt;/p&gt;

  &lt;h3&gt;sIFR (Scalable Inman Flash Replacement)&lt;/h3&gt;

   &lt;p&gt;&lt;a href='http://wiki.novemberborn.net/sifr/'&gt;sIFR&lt;/a&gt; has been available for a few years and has had a good reception across the web community. It was created as a way for designers to display &lt;em&gt;any&lt;/em&gt; typeface on a web site without requiring the visitor to have that typeface previously installed. It uses a combination of Adobe Flash and Javascript to dynamically replace text, and is completely accessible and search engine friendly. The current stable build of sIFR is version 2, with version 3 currently in development, hopefully to be released in a stable form in the near future.&lt;/p&gt;
   
   &lt;p&gt;sIFR requires both Javascript to be turned on and the Flash plugin to be installed. If either of these requirements are not met, the default styles designated by the designer will be used and the visitor will never know the difference. However, the wide adoption of Flash and Javascript will ensure it works for most visitors. sIFR is best used in moderation with elements like headlines, rather than entire blocks of body text.&lt;/p&gt;
   
   &lt;h3&gt;How to use it&lt;/h3&gt;
    
   &lt;p&gt;After &lt;a href='http://novemberborn.net/sifr/2.0.5'&gt;downloading sIFR&lt;/a&gt;, the first step is to open the &lt;code&gt;sifr.fla&lt;/code&gt; in Adobe Flash, replace the typeface with one of your choosing, and export the file as a Flash movie. This is explained in more detail in the &lt;a href='http://wiki.novemberborn.net/sifr/How+to+use'&gt;sIFR Documentation&lt;/a&gt;. Next, in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of your HTML document, insert links to the sIFR CSS and Javascript files:&lt;/p&gt;
      
&lt;pre&gt;
&amp;lt;head&amp;gt;
  .....
  &amp;lt;link rel="stylesheet" href="sIFR-screen.css" type="text/css" media="screen" /&amp;gt;
  &amp;lt;link rel="stylesheet" href="sIFR-print.css" type="text/css" media="print" /&amp;gt;
  &amp;lt;script src="sifr.js" type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script src="sifr-addons.js" type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;
  .....
&amp;lt;/head&amp;gt;
&lt;/pre&gt;
   
   &lt;p&gt;Before the the closing &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag of your HTML document, insert the Javascript to call the sIFR replacement methods on all the elements you want to transform:&lt;/p&gt;
   
&lt;pre&gt;
.....
&amp;lt;script type="text/javascript"&amp;gt;
if(typeof sIFR == "function"){
	  sIFR.replaceElement(named({sSelector:"body h1", sFlashSrc:"blackletter.swf", sColor:"#000000", sBgColor:"#dddddd", sFlashVars:"textalign=center"}));
  sIFR.replaceElement(named({sSelector:"body h2", sFlashSrc:"blackletter.swf", sColor:"#000000", sBgColor:"#FFFFFF"}));
};
&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
.....
&lt;/pre&gt;
    
   &lt;p&gt;The sIFR.replacementElement method takes a number of options such as color, background color, link and hover colors, padding, and several more as described in detail in the &lt;a href='http://wiki.novemberborn.net/sifr/How+to+use'&gt;sIFR Documentation&lt;/a&gt;. You may have to play around with these options and tinker with your CSS to perfect your transformations.&lt;/p&gt;  
   
   &lt;p&gt;&lt;a href='http://www2.killswitchcollective.com/articles/webtype/external/sifr/' title='View the example if sIFR in action'&gt;View the Example &amp;raquo;&lt;/a&gt;&lt;/p&gt;
   
   &lt;p&gt;You should see something like:&lt;/p&gt;
   
   &lt;img src='http://www2.killswitchcollective.com/articles/webtype/external/sifr.gif' alt='sIFR Example' /&gt;
   
   &lt;h3&gt;Pitfalls&lt;/h3&gt;
   
   &lt;p&gt;sIFR does a good job of working across a variety of browsers and degrades rather gracefully when it can't be used. That said, there are a few issues that arise with its use. Occasionally sIFR can slow the rendering of a web site, and can also create a visible 'flash' as the text is replaced by Flash. Additionally, visitors won't be able to copy and paste a combination of sIFR text and normal HTML text.&lt;/p&gt; 

  &lt;h3&gt;Embedding Fonts with @font-face and Safari&lt;/h3&gt;
      
    &lt;p&gt;With the recent release of &lt;a href='http://www.apple.com/safari/' title='Download Safari 3.1 (Mac or PC)'&gt;Safari 3.1&lt;/a&gt; from Apple comes the ability for a web designer to reliably display any TrueType font of their choosing on a web site without Flash or Javascript, allowing them to style it with CSS as they wish. Visitors' browsers will seamlessly download the typeface and render the design in all its glory as was intended by the designer. Surprisingly, Internet Explorer has actually &lt;a href='http://msdn2.microsoft.com/en-us/library/ms530757(VS.85).aspx'&gt;supported font-face embedding since version 4&lt;/a&gt;, but unsurprisingly, Microsoft's implementation only works with a proprietary .EOT font format.&lt;/p&gt;
        
    &lt;h3&gt;How to use it&lt;/h3&gt;
    
    &lt;p&gt;Implementing the &lt;code&gt;@font-face&lt;/code&gt; property is actually quite simple. It acts just like any other CSS selector as shown in the example code below. Define a name for your &lt;code&gt;font-family&lt;/code&gt; and then provide an absolute or relative &lt;code&gt;url()&lt;/code&gt; to the typeface file. The &lt;code&gt;url()&lt;/code&gt; acts just like &lt;code&gt;url()&lt;/code&gt; for &lt;code&gt;background-image&lt;/code&gt;. Once you've defined the typeface, simply use the new &lt;code&gt;font-family&lt;/code&gt; within any selector as you would normally.&lt;/p&gt;
    
&lt;pre&gt;
@font-face {
  font-family: "OldeEnglish";
  src: url(OldeEnglish.ttf) format("truetype");
}

h1 { font-family: "OldeEnglish"; }
&lt;/pre&gt;
    
    &lt;p&gt;&lt;a href='http://www2.killswitchcollective.com/articles/webtype/external/safari/' title='View the example of Safari embedded fonts in action'&gt;View the Example (works only in Safari 3.1+) &amp;raquo;&lt;/a&gt;&lt;/p&gt;
    
    &lt;p&gt;You should see something like:&lt;/p&gt;
    
    &lt;img src='http://www2.killswitchcollective.com/articles/webtype/external/safari.gif' alt='Safari Embedded Fonts Example' /&gt;
    
    &lt;h3&gt;Pitfalls&lt;/h3&gt;
    
    &lt;p&gt;One major downfall to using embedded typefaces is that the technique currently only works in Safari 3.1. However, other browsers will degrade gracefully and use the next font in the font stack. Unfortunately, a designer may be using different &lt;code&gt;line-height&lt;/code&gt;, &lt;code&gt;font-size&lt;/code&gt;, &lt;code&gt;letter-spacing&lt;/code&gt;, or &lt;code&gt;word-spacing&lt;/code&gt; specific custom typeface, making the next typeface in the stack unreadable. No word yet on whether future versions of Firefox, Internet Explorer, or Opera will join in the font embedding party, but a designer can hope.&lt;/p&gt;
    
    &lt;p&gt;Copyright issues also arise with &lt;code&gt;@font-face&lt;/code&gt;. Quality typefaces from type foundries often cost hundreds to thousands of dollars and don't generally include licenses that allow for legal distribution to the public. Since the fonts used with &lt;code&gt;@font-face&lt;/code&gt; are required to be publicly accessible, they could potentially be downloaded by any user who has a peek inside your stylesheet. There are many freely downloadable fonts available on the web, but a majority of those are rather low quality and should probably be avoided anyways.&lt;/p&gt;

  &lt;h3&gt;With Great Power Comes Great Responsibility&amp;hellip;&lt;/h3&gt;
  
    &lt;p&gt;The two tools are a welcome addition to a designers arsenal, especially when it comes to web typography. However, these new tools do open the door for typeface abuse and potentially illegible, tacky designs. Unless that's specifically what you're going for, as a designer you should still use sound judgement and restraint. Just because you &lt;em&gt;can&lt;/em&gt;, doesn't mean you &lt;em&gt;should&lt;/em&gt;. With that said, go forth and experiment!&lt;/p&gt;  
  
  &lt;h3&gt;Helpful Resources &amp;amp; References&lt;/h3&gt;

    &lt;ul&gt;
      &lt;li&gt;&lt;a href='http://wiki.novemberborn.net/sifr'&gt;sIFR Documentation&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href='http://webtypogrpahy.net'&gt;The Elements of Typographic Style Applied to the Web&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href='http://www.apaddedcell.com/web-fonts'&gt;Complete Guide to Pre-Installed Fonts&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href='http://www.safalra.com/web-design/typography/web-safe-fonts-myth/'&gt;Web Font Stacks&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href='http://24ways.org/2007/increase-your-font-stacks-with-font-matrix'&gt;Font Stack Matrix&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href='http://www.alistapart.com/articles/howtosizetextincss'&gt;How to size text in CSS&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href='http://www.alistapart.com/articles/cssatten'&gt;CSS @ Ten: The Next Big Thing&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href='http://moorstation.org/typoasis/designers/steffmann/samples/o/oldeEngl.htm'&gt;Olde English Typeface by Dieter Steffmann&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
</description>
      <pubDate>Fri, 25 Apr 2008 12:18:27 -0700</pubDate>
      <link>http://www.killswitchcollective.com/articles/16</link>
      <guid>http://www.killswitchcollective.com/articles/16</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 {
    font-family: courier, "courier new", serif;
    font-size:12px;
    background-color: #f0f0f0;	
    border: solid 1px #ccc;
    padding:10px;
    margin: 5px 5px 5px 5px;
    overflow:auto;
  }
  .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;div 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;/div&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;div 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;/div&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;div 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;/div&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;div 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;/div&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;div 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;/div&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;div 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;/div&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;div 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;/div&gt;

&lt;p&gt;I can still retrieve only books, or only articles:&lt;/p&gt;

&lt;div 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;/div&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;/scode&gt; the same way I would any other object:&lt;/p&gt;

&lt;div 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;/div&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>
