1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 
   2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
   3 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 
   5   <link rel="stylesheet" href="stylesheets/screen.css" type="text/css" media="screen" />
 
   6   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 
  10   <script src="javascripts/rounded_corners_lite.inc.js" type="text/javascript"></script>
 
  14   <script type="text/javascript">
 
  15     window.onload = function() {
 
  25       var versionBox = new curvyCorners(settings, document.getElementById("version"));
 
  26       versionBox.applyCornersToAll();
 
  33     <h1>Composite Primary Keys</h1>
 
  34     <div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/compositekeys"; return false'>
 
  36       <a href="http://rubyforge.org/projects/compositekeys" class="numbers">2.2.2</a>
 
  38     <h1>→ Ruby on Rails</h1>
 
  39 <h1>→ ActiveRecords</h1>
 
  41 <p>Ruby on Rails does not support composite primary keys. This free software is an extension <br />
 
  42 to the database layer of Rails – <a href="http://wiki.rubyonrails.com/rails/pages/ActiveRecord">ActiveRecords</a> – to support composite primary keys as transparently as possible.</p>
 
  43 <p>Any Ruby script using ActiveRecords can use Composite Primary Keys with this library.</p>
 
  45 <p><pre class="syntax"><span class="ident">sudo</span> <span class="ident">gem</span> <span class="ident">install</span> <span class="ident">composite_primary_keys</span></pre></p>
 
  46 <p>Rails: Add the following to the bottom of your <code>environment.rb</code> file</p>
 
  47 <p><pre class="syntax"><span class="ident">require</span> <span class="punct">'</span><span class="string">composite_primary_keys</span><span class="punct">'</span></pre></p>
 
  48 <p>Ruby scripts: Add the following to the top of your script</p>
 
  49 <p><pre class="syntax"><span class="ident">require</span> <span class="punct">'</span><span class="string">rubygems</span><span class="punct">'</span>
 
  50 <span class="ident">require</span> <span class="punct">'</span><span class="string">composite_primary_keys</span><span class="punct">'</span></pre></p>
 
  52 <p>A model with composite primary keys would look like…</p>
 
  53 <p><pre class="syntax"><span class="keyword">class </span><span class="class">Membership</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
 
  54   <span class="comment"># set_primary_keys *keys - turns on composite key functionality</span>
 
  55   <span class="ident">set_primary_keys</span> <span class="symbol">:user_id</span><span class="punct">,</span> <span class="symbol">:group_id</span>
 
  56   <span class="ident">belongs_to</span> <span class="symbol">:user</span>
 
  57   <span class="ident">belongs_to</span> <span class="symbol">:group</span>
 
  58   <span class="ident">has_many</span> <span class="symbol">:statuses</span><span class="punct">,</span> <span class="symbol">:class_name</span> <span class="punct">=></span> <span class="punct">'</span><span class="string">MembershipStatus</span><span class="punct">',</span> <span class="symbol">:foreign_key</span> <span class="punct">=></span> <span class="punct">[</span><span class="symbol">:user_id</span><span class="punct">,</span> <span class="symbol">:group_id</span><span class="punct">]</span>
 
  59 <span class="keyword">end</span></pre></p>
 
  60 <p>A model associated with a composite key model would be defined like…</p>
 
  61 <p><pre class="syntax"><span class="keyword">class </span><span class="class">MembershipStatus</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
 
  62   <span class="ident">belongs_to</span> <span class="symbol">:membership</span><span class="punct">,</span> <span class="symbol">:foreign_key</span> <span class="punct">=></span> <span class="punct">[</span><span class="symbol">:user_id</span><span class="punct">,</span> <span class="symbol">:group_id</span><span class="punct">]</span>
 
  63 <span class="keyword">end</span></pre></p>
 
  64 <p>That is, associations can include composite keys too. Nice.</p>
 
  65 <h2>Demonstration of usage</h2>
 
  66 <p>Once you’ve created your models to specify composite primary keys (such as the Membership class) and associations (such as MembershipStatus#membership), you can uses them like any normal model with associations.</p>
 
  67 <p>But first, lets check out our primary keys.</p>
 
  68 <p><pre class="syntax"><span class="constant">MembershipStatus</span><span class="punct">.</span><span class="ident">primary_key</span> <span class="comment"># => "id"    # normal single key</span>
 
  69 <span class="constant">Membership</span><span class="punct">.</span><span class="ident">primary_key</span>  <span class="comment"># => [:user_id, :group_id] # composite keys</span>
 
  70 <span class="constant">Membership</span><span class="punct">.</span><span class="ident">primary_key</span><span class="punct">.</span><span class="ident">to_s</span> <span class="comment"># => "user_id,group_id"</span></pre></p>
 
  71 <p>Now we want to be able to find instances using the same syntax we always use for ActiveRecords…</p>
 
  72 <p><pre class="syntax"><span class="constant">MembershipStatus</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="number">1</span><span class="punct">)</span>    <span class="comment"># single id returns single instance</span>
 
  73 <span class="punct">=></span> <span class="punct"><</span><span class="constant">MembershipStatus</span><span class="punct">:</span><span class="number">0x392a8c8</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span> <span class="punct">"</span><span class="string">status</span><span class="punct">"=>"</span><span class="string">Active</span><span class="punct">"}></span>
 
  74 <span class="constant">Membership</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="number">1</span><span class="punct">,</span><span class="number">1</span><span class="punct">)</span>  <span class="comment"># composite ids returns single instance</span>
 
  75 <span class="punct">=></span> <span class="punct"><</span><span class="constant">Membership</span><span class="punct">:</span><span class="number">0x39218b0</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">user_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span> <span class="punct">"</span><span class="string">group_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">"}></span></pre></p>
 
  76 <p>Using <a href="http://www.rubyonrails.org">Ruby on Rails</a>? You’ll want to your url_for helpers<br />
 
  77 to convert composite keys into strings and back again…</p>
 
  78 <p><pre class="syntax"><span class="constant">Membership</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="symbol">:first</span><span class="punct">).</span><span class="ident">to_param</span> <span class="comment"># => "1,1"</span></pre></p>
 
  79 <p>And then use the string id within your controller to find the object again</p>
 
  80 <p><pre class="syntax"><span class="ident">params</span><span class="punct">[</span><span class="symbol">:id</span><span class="punct">]</span> <span class="comment"># => '1,1'</span>
 
  81 <span class="constant">Membership</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="ident">params</span><span class="punct">[</span><span class="symbol">:id</span><span class="punct">])</span>
 
  82 <span class="punct">=></span> <span class="punct"><</span><span class="constant">Membership</span><span class="punct">:</span><span class="number">0x3904288</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">user_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span> <span class="punct">"</span><span class="string">group_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">"}></span></pre></p>
 
  83 <p>That is, an ActiveRecord supporting composite keys behaves transparently<br />
 
  84 throughout your application. Just like a normal ActiveRecord.</p>
 
  86 <h3>Pass a list of composite ids to the <code>#find</code> method</h3>
 
  87 <p><pre class="syntax"><span class="constant">Membership</span><span class="punct">.</span><span class="ident">find</span> <span class="punct">[</span><span class="number">1</span><span class="punct">,</span><span class="number">1</span><span class="punct">],</span> <span class="punct">[</span><span class="number">2</span><span class="punct">,</span><span class="number">1</span><span class="punct">]</span>
 
  88 <span class="punct">=></span> <span class="punct">[</span>
 
  89   <span class="punct"><</span><span class="constant">Membership</span><span class="punct">:</span><span class="number">0x394ade8</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">user_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span> <span class="punct">"</span><span class="string">group_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">"}>,</span> 
 
  90   <span class="punct"><</span><span class="constant">Membership</span><span class="punct">:</span><span class="number">0x394ada0</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">user_id</span><span class="punct">"=>"</span><span class="string">2</span><span class="punct">",</span> <span class="punct">"</span><span class="string">group_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">"}></span>
 
  91 <span class="punct">]</span></pre></p>
 
  92 <p>Perform <code>#count</code> operations</p>
 
  93 <p><pre class="syntax"><span class="constant">MembershipStatus</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="symbol">:first</span><span class="punct">).</span><span class="ident">memberships</span><span class="punct">.</span><span class="ident">count</span> <span class="comment"># => 1</span></pre></p>
 
  94 <h3>Routes with Rails</h3>
 
  95 <p>From Pete Sumskas:</p>
 
  97 <p>I ran into one problem that I didn’t see mentioned on <a href="http://groups.google.com/group/compositekeys">this list</a> – <br />
 
  98         and I   didn’t see any information about what I should do to address it in the<br />
 
  99         documentation (might have missed it).</p>
 
 100 <p>The problem was that the urls being generated for a ‘show’ action (for<br />
 
 101         example) had a syntax like:<br />
 
 103         <pre>/controller/show/123000,Bu70</pre></p>
 
 104 <p>for a two-field composite PK. The default routing would not match that,<br />
 
 105         so after working out how to do the routing I added:<br />
 
 107         <pre class="syntax"><span class="ident">map</span><span class="punct">.</span><span class="ident">connect</span> <span class="punct">'</span><span class="string">:controller/:action/:id</span><span class="punct">',</span> <span class="symbol">:id</span> <span class="punct">=></span> <span class="punct">/</span><span class="regex"><span class="escape">\w</span>+(,<span class="escape">\w</span>+)*</span><span class="punct">/</span></pre><br />
 
 109         to my <code>route.rb</code> file.</p>
 
 111 <p><a name="dbs"></a></p>
 
 112 <h2>Which databases?</h2>
 
 113 <p>A suite of unit tests have been run on the following databases supported by ActiveRecord:</p>
 
 117                 <th>Test Success</th>
 
 118                 <th>User feedback</th>
 
 122                 <td><span class=success><span class="caps">YES</span></span></td>
 
 123                 <td><span class=success><span class="caps">YES</span></span> (<a href="mailto:compositekeys@googlegroups.com?subject=Mysql+is+working">Yes!</a> or <a href="mailto:compositekeys@googlegroups.com?subject=Mysql+is+failing">No…</a>)</td>
 
 127                 <td><span class=success><span class="caps">YES</span></span></td>
 
 128                 <td><span class=success><span class="caps">YES</span></span> (<a href="mailto:compositekeys@googlegroups.com?subject=Sqlite3+is+working">Yes!</a> or <a href="mailto:compositekeys@googlegroups.com?subject=Sqlite3+is+failing">No…</a>)</td>
 
 132                 <td><span class=success><span class="caps">YES</span></span></td>
 
 133                 <td><span class=success><span class="caps">YES</span></span> (<a href="mailto:compositekeys@googlegroups.com?subject=Postgresql+is+working">Yes!</a> or <a href="mailto:compositekeys@googlegroups.com?subject=Postgresql+is+failing">No…</a>)</td>
 
 137                 <td><span class=success><span class="caps">YES</span></span></td>
 
 138                 <td><span class=success><span class="caps">YES</span></span> (<a href="mailto:compositekeys@googlegroups.com?subject=Oracle+is+working">Yes!</a> or <a href="mailto:compositekeys@googlegroups.com?subject=Oracle+is+failing">No…</a>)</td>
 
 142                 <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Help+with+SQLServer">I can help</a>)</td>
 
 143                 <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=SQLServer+is+working">Yes!</a> or <a href="mailto:compositekeys@googlegroups.com?subject=SQLServer+is+failing">No…</a>)</td>
 
 147                 <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Help+with+DB2">I can help</a>)</td>
 
 148                 <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=DB2+is+working">Yes!</a> or <a href="mailto:compositekeys@googlegroups.com?subject=DB2+is+failing">No…</a>)</td>
 
 152                 <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Help+with+Firebird">I can help</a>)</td>
 
 153                 <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Firebird+is+working">Yes!</a> or <a href="mailto:compositekeys@googlegroups.com?subject=Firebird+is+failing">No…</a>)</td>
 
 157                 <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Help+with+Sybase">I can help</a>)</td>
 
 158                 <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Sybase+is+working">Yes!</a> or <a href="mailto:compositekeys@googlegroups.com?subject=Sybase+is+failing">No…</a>)</td>
 
 162                 <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Help+with+Openbase">I can help</a>)</td>
 
 163                 <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Openbase+is+working">Yes!</a> or <a href="mailto:compositekeys@googlegroups.com?subject=Openbase+is+failing">No…</a>)</td>
 
 167                 <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Help+with+Frontbase">I can help</a>)</td>
 
 168                 <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Frontbase+is+working">Yes!</a> or <a href="mailto:compositekeys@googlegroups.com?subject=Frontbase+is+failing">No…</a>)</td>
 
 171 <h2>Dr Nic’s Blog</h2>
 
 172 <p><a href="http://www.drnicwilliams.com">http://www.drnicwilliams.com</a> – for future announcements and<br />
 
 173 other stories and things.</p>
 
 175 <p><a href="http://groups.google.com/group/compositekeys">http://groups.google.com/group/compositekeys</a></p>
 
 176 <h2>How to submit patches</h2>
 
 177 <p>Read the <a href="http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/">8 steps for fixing other people’s code</a> and for section <a href="http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/#8b-google-groups">8b: Submit patch to Google Groups</a>, use the Google Group above.</p>
 
 178 <p>The source for this project is available via git. You can <a href="http://github.com/drnic/composite_primary_keys/tree/master">browse and/or fork the source</a>, or to clone the project locally:<br />
 
 180 <pre>git clone git://github.com/drnic/composite_primary_keys.git</pre></p>
 
 182 <p>This code is free to use under the terms of the <span class="caps">MIT</span> licence.</p>
 
 184 <p>Comments are welcome. Send an email to <a href="mailto:drnicwilliams@gmail.com">Dr Nic Williams</a>.</p>
 
 186       <a href="mailto:drnicwilliams@gmail.com">Dr Nic</a>, 21st January 2009<br>
 
 187       Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
 
 191 <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
 
 193 <script type="text/javascript">
 
 194 _uacct = "UA-567811-2";