mingusbabcock-composite_primary_keys 2.2.2 → 2.2.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/History.txt +156 -0
  2. data/Manifest.txt +122 -0
  3. data/README.txt +41 -0
  4. data/README_DB2.txt +33 -0
  5. data/Rakefile +65 -0
  6. data/init.rb +2 -0
  7. data/install.rb +30 -0
  8. data/lib/adapter_helper/base.rb +63 -0
  9. data/lib/adapter_helper/mysql.rb +13 -0
  10. data/lib/adapter_helper/oracle.rb +12 -0
  11. data/lib/adapter_helper/postgresql.rb +13 -0
  12. data/lib/adapter_helper/sqlite3.rb +13 -0
  13. data/lib/composite_primary_keys.rb +56 -0
  14. data/lib/composite_primary_keys/association_preload.rb +253 -0
  15. data/lib/composite_primary_keys/associations.rb +428 -0
  16. data/lib/composite_primary_keys/attribute_methods.rb +84 -0
  17. data/lib/composite_primary_keys/base.rb +341 -0
  18. data/lib/composite_primary_keys/calculations.rb +69 -0
  19. data/lib/composite_primary_keys/composite_arrays.rb +30 -0
  20. data/lib/composite_primary_keys/connection_adapters/ibm_db_adapter.rb +21 -0
  21. data/lib/composite_primary_keys/connection_adapters/oracle_adapter.rb +15 -0
  22. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +53 -0
  23. data/lib/composite_primary_keys/connection_adapters/sqlite3_adapter.rb +15 -0
  24. data/lib/composite_primary_keys/fixtures.rb +8 -0
  25. data/lib/composite_primary_keys/migration.rb +20 -0
  26. data/lib/composite_primary_keys/reflection.rb +19 -0
  27. data/lib/composite_primary_keys/version.rb +8 -0
  28. data/loader.rb +24 -0
  29. data/local/database_connections.rb.sample +10 -0
  30. data/local/paths.rb.sample +2 -0
  31. data/local/tasks.rb.sample +2 -0
  32. data/scripts/console.rb +48 -0
  33. data/scripts/txt2html +67 -0
  34. data/scripts/txt2js +59 -0
  35. data/tasks/activerecord_selection.rake +43 -0
  36. data/tasks/databases.rake +12 -0
  37. data/tasks/databases/mysql.rake +30 -0
  38. data/tasks/databases/oracle.rake +25 -0
  39. data/tasks/databases/postgresql.rake +26 -0
  40. data/tasks/databases/sqlite3.rake +28 -0
  41. data/tasks/deployment.rake +22 -0
  42. data/tasks/local_setup.rake +13 -0
  43. data/tasks/website.rake +18 -0
  44. data/test/README_tests.txt +67 -0
  45. data/test/abstract_unit.rb +94 -0
  46. data/test/connections/native_ibm_db/connection.rb +23 -0
  47. data/test/connections/native_mysql/connection.rb +13 -0
  48. data/test/connections/native_oracle/connection.rb +14 -0
  49. data/test/connections/native_postgresql/connection.rb +9 -0
  50. data/test/connections/native_sqlite/connection.rb +9 -0
  51. data/test/fixtures/article.rb +5 -0
  52. data/test/fixtures/articles.yml +6 -0
  53. data/test/fixtures/comment.rb +6 -0
  54. data/test/fixtures/comments.yml +16 -0
  55. data/test/fixtures/db_definitions/db2-create-tables.sql +113 -0
  56. data/test/fixtures/db_definitions/db2-drop-tables.sql +16 -0
  57. data/test/fixtures/db_definitions/mysql.sql +174 -0
  58. data/test/fixtures/db_definitions/oracle.drop.sql +39 -0
  59. data/test/fixtures/db_definitions/oracle.sql +188 -0
  60. data/test/fixtures/db_definitions/postgresql.sql +199 -0
  61. data/test/fixtures/db_definitions/sqlite.sql +160 -0
  62. data/test/fixtures/department.rb +5 -0
  63. data/test/fixtures/departments.yml +3 -0
  64. data/test/fixtures/employee.rb +4 -0
  65. data/test/fixtures/employees.yml +9 -0
  66. data/test/fixtures/group.rb +3 -0
  67. data/test/fixtures/groups.yml +3 -0
  68. data/test/fixtures/hack.rb +6 -0
  69. data/test/fixtures/hacks.yml +2 -0
  70. data/test/fixtures/membership.rb +7 -0
  71. data/test/fixtures/membership_status.rb +3 -0
  72. data/test/fixtures/membership_statuses.yml +10 -0
  73. data/test/fixtures/memberships.yml +6 -0
  74. data/test/fixtures/product.rb +7 -0
  75. data/test/fixtures/product_tariff.rb +5 -0
  76. data/test/fixtures/product_tariffs.yml +12 -0
  77. data/test/fixtures/products.yml +6 -0
  78. data/test/fixtures/reading.rb +4 -0
  79. data/test/fixtures/readings.yml +10 -0
  80. data/test/fixtures/reference_code.rb +7 -0
  81. data/test/fixtures/reference_codes.yml +28 -0
  82. data/test/fixtures/reference_type.rb +7 -0
  83. data/test/fixtures/reference_types.yml +9 -0
  84. data/test/fixtures/street.rb +3 -0
  85. data/test/fixtures/streets.yml +15 -0
  86. data/test/fixtures/suburb.rb +6 -0
  87. data/test/fixtures/suburbs.yml +9 -0
  88. data/test/fixtures/tariff.rb +6 -0
  89. data/test/fixtures/tariffs.yml +13 -0
  90. data/test/fixtures/user.rb +10 -0
  91. data/test/fixtures/users.yml +6 -0
  92. data/test/hash_tricks.rb +34 -0
  93. data/test/plugins/pagination.rb +405 -0
  94. data/test/plugins/pagination_helper.rb +135 -0
  95. data/test/test_associations.rb +160 -0
  96. data/test/test_attribute_methods.rb +22 -0
  97. data/test/test_attributes.rb +84 -0
  98. data/test/test_clone.rb +34 -0
  99. data/test/test_composite_arrays.rb +51 -0
  100. data/test/test_create.rb +68 -0
  101. data/test/test_delete.rb +96 -0
  102. data/test/test_dummy.rb +28 -0
  103. data/test/test_exists.rb +29 -0
  104. data/test/test_find.rb +73 -0
  105. data/test/test_ids.rb +97 -0
  106. data/test/test_miscellaneous.rb +39 -0
  107. data/test/test_pagination.rb +38 -0
  108. data/test/test_polymorphic.rb +31 -0
  109. data/test/test_santiago.rb +27 -0
  110. data/test/test_tutorial_examle.rb +26 -0
  111. data/test/test_update.rb +40 -0
  112. data/website/index.html +199 -0
  113. data/website/index.txt +159 -0
  114. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  115. data/website/stylesheets/screen.css +126 -0
  116. data/website/template.js +3 -0
  117. data/website/template.rhtml +53 -0
  118. data/website/version-raw.js +3 -0
  119. data/website/version-raw.txt +2 -0
  120. data/website/version.js +4 -0
  121. data/website/version.txt +3 -0
  122. metadata +180 -18
@@ -0,0 +1,31 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/comment'
3
+ require 'fixtures/user'
4
+ require 'fixtures/employee'
5
+ require 'fixtures/hack'
6
+
7
+ class TestPolymorphic < Test::Unit::TestCase
8
+ fixtures :users, :employees, :comments, :hacks
9
+
10
+ def test_polymorphic_has_many
11
+ comments = Hack.find('andrew').comments
12
+ assert_equal 'andrew', comments[0].person_id
13
+ end
14
+
15
+ def test_polymorphic_has_one
16
+ first_comment = Hack.find('andrew').first_comment
17
+ assert_equal 'andrew', first_comment.person_id
18
+ end
19
+
20
+ def test_has_many_through
21
+ user = users(:santiago)
22
+ article_names = user.articles.collect { |a| a.name }.sort
23
+ assert_equal ['Article One', 'Article Two'], article_names
24
+ end
25
+
26
+ def test_polymorphic_has_many_through
27
+ user = users(:santiago)
28
+ assert_equal ['andrew'], user.hacks.collect { |a| a.name }.sort
29
+ end
30
+
31
+ end
@@ -0,0 +1,27 @@
1
+ # Test cases devised by Santiago that broke the Composite Primary Keys
2
+ # code at one point in time. But no more!!!
3
+
4
+ require 'abstract_unit'
5
+ require 'fixtures/user'
6
+ require 'fixtures/article'
7
+ require 'fixtures/reading'
8
+
9
+ class TestSantiago < Test::Unit::TestCase
10
+ fixtures :suburbs, :streets, :users, :articles, :readings
11
+
12
+ def test_normal_and_composite_associations
13
+ assert_not_nil @suburb = Suburb.find(1,1)
14
+ assert_equal 1, @suburb.streets.length
15
+
16
+ assert_not_nil @street = Street.find(1)
17
+ assert_not_nil @street.suburb
18
+ end
19
+
20
+ def test_single_keys
21
+ @santiago = User.find(1)
22
+ assert_not_nil @santiago.articles
23
+ assert_equal 2, @santiago.articles.length
24
+ assert_not_nil @santiago.readings
25
+ assert_equal 2, @santiago.readings.length
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/user'
3
+ require 'fixtures/group'
4
+ require 'fixtures/membership_status'
5
+ require 'fixtures/membership'
6
+
7
+ class TestTutorialExample < Test::Unit::TestCase
8
+ fixtures :users, :groups, :memberships, :membership_statuses
9
+
10
+ def test_membership
11
+ assert(membership = Membership.find(1,1), "Cannot find a membership")
12
+ assert(membership.user)
13
+ assert(membership.group)
14
+ end
15
+
16
+ def test_status
17
+ assert(membership = Membership.find(1,1), "Cannot find a membership")
18
+ assert(statuses = membership.statuses, "No has_many association to status")
19
+ assert_equal(membership, statuses.first.membership)
20
+ end
21
+
22
+ def test_count
23
+ assert(membership = Membership.find(1,1), "Cannot find a membership")
24
+ assert_equal(1, membership.statuses.count)
25
+ end
26
+ end
@@ -0,0 +1,40 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/reference_type'
3
+ require 'fixtures/reference_code'
4
+
5
+ class TestUpdate < Test::Unit::TestCase
6
+ fixtures :reference_types, :reference_codes
7
+
8
+ CLASSES = {
9
+ :single => {
10
+ :class => ReferenceType,
11
+ :primary_keys => :reference_type_id,
12
+ :update => { :description => 'RT Desc' },
13
+ },
14
+ :dual => {
15
+ :class => ReferenceCode,
16
+ :primary_keys => [:reference_type_id, :reference_code],
17
+ :update => { :description => 'RT Desc' },
18
+ },
19
+ }
20
+
21
+ def setup
22
+ self.class.classes = CLASSES
23
+ end
24
+
25
+ def test_setup
26
+ testing_with do
27
+ assert_not_nil @klass_info[:update]
28
+ end
29
+ end
30
+
31
+ def test_update_attributes
32
+ testing_with do
33
+ assert @first.update_attributes(@klass_info[:update])
34
+ assert @first.reload
35
+ @klass_info[:update].each_pair do |attr_name, new_value|
36
+ assert_equal new_value, @first[attr_name], "Attribute #{attr_name} is incorrect"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,199 @@
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">
4
+ <head>
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" />
7
+ <title>
8
+ Composite Primary Keys
9
+ </title>
10
+ <script src="javascripts/rounded_corners_lite.inc.js" type="text/javascript"></script>
11
+ <style>
12
+
13
+ </style>
14
+ <script type="text/javascript">
15
+ window.onload = function() {
16
+ settings = {
17
+ tl: { radius: 10 },
18
+ tr: { radius: 10 },
19
+ bl: { radius: 10 },
20
+ br: { radius: 10 },
21
+ antiAlias: true,
22
+ autoPad: true,
23
+ validTags: ["div"]
24
+ }
25
+ var versionBox = new curvyCorners(settings, document.getElementById("version"));
26
+ versionBox.applyCornersToAll();
27
+ }
28
+ </script>
29
+ </head>
30
+ <body>
31
+ <div id="main">
32
+
33
+ <h1>Composite Primary Keys</h1>
34
+ <div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/compositekeys"; return false'>
35
+ Get Version
36
+ <a href="http://rubyforge.org/projects/compositekeys" class="numbers">2.2.2</a>
37
+ </div>
38
+ <h1>&#8594; Ruby on Rails</h1>
39
+ <h1>&#8594; ActiveRecords</h1>
40
+ <h2>What</h2>
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 &#8211; <a href="http://wiki.rubyonrails.com/rails/pages/ActiveRecord">ActiveRecords</a> &#8211; 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>
44
+ <h2>Installing</h2>
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>
51
+ <h2>The basics</h2>
52
+ <p>A model with composite primary keys would look like&#8230;</p>
53
+ <p><pre class="syntax"><span class="keyword">class </span><span class="class">Membership</span> <span class="punct">&lt;</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">=&gt;</span> <span class="punct">'</span><span class="string">MembershipStatus</span><span class="punct">',</span> <span class="symbol">:foreign_key</span> <span class="punct">=&gt;</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&#8230;</p>
61
+ <p><pre class="syntax"><span class="keyword">class </span><span class="class">MembershipStatus</span> <span class="punct">&lt;</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">=&gt;</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&#8217;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"># =&gt; &quot;id&quot; # normal single key</span>
69
+ <span class="constant">Membership</span><span class="punct">.</span><span class="ident">primary_key</span> <span class="comment"># =&gt; [: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"># =&gt; &quot;user_id,group_id&quot;</span></pre></p>
71
+ <p>Now we want to be able to find instances using the same syntax we always use for ActiveRecords&#8230;</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">=&gt;</span> <span class="punct">&lt;</span><span class="constant">MembershipStatus</span><span class="punct">:</span><span class="number">0x392a8c8</span> <span class="attribute">@attributes</span><span class="punct">={&quot;</span><span class="string">id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">status</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">Active</span><span class="punct">&quot;}&gt;</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">=&gt;</span> <span class="punct">&lt;</span><span class="constant">Membership</span><span class="punct">:</span><span class="number">0x39218b0</span> <span class="attribute">@attributes</span><span class="punct">={&quot;</span><span class="string">user_id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">group_id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;}&gt;</span></pre></p>
76
+ <p>Using <a href="http://www.rubyonrails.org">Ruby on Rails</a>? You&#8217;ll want to your url_for helpers<br />
77
+ to convert composite keys into strings and back again&#8230;</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"># =&gt; &quot;1,1&quot;</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"># =&gt; '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">=&gt;</span> <span class="punct">&lt;</span><span class="constant">Membership</span><span class="punct">:</span><span class="number">0x3904288</span> <span class="attribute">@attributes</span><span class="punct">={&quot;</span><span class="string">user_id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">group_id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;}&gt;</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>
85
+ <h2>Other tricks</h2>
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">=&gt;</span> <span class="punct">[</span>
89
+ <span class="punct">&lt;</span><span class="constant">Membership</span><span class="punct">:</span><span class="number">0x394ade8</span> <span class="attribute">@attributes</span><span class="punct">={&quot;</span><span class="string">user_id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">group_id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;}&gt;,</span>
90
+ <span class="punct">&lt;</span><span class="constant">Membership</span><span class="punct">:</span><span class="number">0x394ada0</span> <span class="attribute">@attributes</span><span class="punct">={&quot;</span><span class="string">user_id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">2</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">group_id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;}&gt;</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"># =&gt; 1</span></pre></p>
94
+ <h3>Routes with Rails</h3>
95
+ <p>From Pete Sumskas:</p>
96
+ <blockquote>
97
+ <p>I ran into one problem that I didn&#8217;t see mentioned on <a href="http://groups.google.com/group/compositekeys">this list</a> &#8211; <br />
98
+ and I didn&#8217;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 &#8216;show&#8217; action (for<br />
101
+ example) had a syntax like:<br />
102
+ <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 />
106
+ <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">=&gt;</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 />
108
+ <br />
109
+ to my <code>route.rb</code> file.</p>
110
+ </blockquote>
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>
114
+ <table>
115
+ <tr>
116
+ <th>Database</th>
117
+ <th>Test Success</th>
118
+ <th>User feedback</th>
119
+ </tr>
120
+ <tr>
121
+ <td>mysql </td>
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&#8230;</a>)</td>
124
+ </tr>
125
+ <tr>
126
+ <td>sqlite3 </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&#8230;</a>)</td>
129
+ </tr>
130
+ <tr>
131
+ <td>postgresql</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&#8230;</a>)</td>
134
+ </tr>
135
+ <tr>
136
+ <td>oracle </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&#8230;</a>)</td>
139
+ </tr>
140
+ <tr>
141
+ <td>sqlserver </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&#8230;</a>)</td>
144
+ </tr>
145
+ <tr>
146
+ <td>db2 </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&#8230;</a>)</td>
149
+ </tr>
150
+ <tr>
151
+ <td>firebird </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&#8230;</a>)</td>
154
+ </tr>
155
+ <tr>
156
+ <td>sybase </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&#8230;</a>)</td>
159
+ </tr>
160
+ <tr>
161
+ <td>openbase </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&#8230;</a>)</td>
164
+ </tr>
165
+ <tr>
166
+ <td>frontbase </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&#8230;</a>)</td>
169
+ </tr>
170
+ </table>
171
+ <h2>Dr Nic&#8217;s Blog</h2>
172
+ <p><a href="http://www.drnicwilliams.com">http://www.drnicwilliams.com</a> &#8211; for future announcements and<br />
173
+ other stories and things.</p>
174
+ <h2>Forum</h2>
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&#8217;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 />
179
+ <br />
180
+ <pre>git clone git://github.com/drnic/composite_primary_keys.git</pre></p>
181
+ <h2>Licence</h2>
182
+ <p>This code is free to use under the terms of the <span class="caps">MIT</span> licence.</p>
183
+ <h2>Contact</h2>
184
+ <p>Comments are welcome. Send an email to <a href="mailto:drnicwilliams@gmail.com">Dr Nic Williams</a>.</p>
185
+ <p class="coda">
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>
188
+ </p>
189
+ </div>
190
+
191
+ <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
192
+ </script>
193
+ <script type="text/javascript">
194
+ _uacct = "UA-567811-2";
195
+ urchinTracker();
196
+ </script>
197
+
198
+ </body>
199
+ </html>
data/website/index.txt ADDED
@@ -0,0 +1,159 @@
1
+ h1. Composite Primary Keys
2
+
3
+ h1. -> Ruby on Rails
4
+
5
+ h1. -> ActiveRecords
6
+
7
+ h2. What
8
+
9
+ Ruby on Rails does not support composite primary keys. This free software is an extension
10
+ to the database layer of Rails - "ActiveRecords":http://wiki.rubyonrails.com/rails/pages/ActiveRecord - to support composite primary keys as transparently as possible.
11
+
12
+ Any Ruby script using ActiveRecords can use Composite Primary Keys with this library.
13
+
14
+ h2. Installing
15
+
16
+ <pre syntax="ruby">sudo gem install composite_primary_keys</pre>
17
+
18
+ Rails: Add the following to the bottom of your <code>environment.rb</code> file
19
+
20
+ <pre syntax="ruby">require 'composite_primary_keys'</pre>
21
+
22
+ Ruby scripts: Add the following to the top of your script
23
+
24
+ <pre syntax="ruby">require 'rubygems'
25
+ require 'composite_primary_keys'</pre>
26
+
27
+ h2. The basics
28
+
29
+ A model with composite primary keys would look like...
30
+
31
+ <pre syntax="ruby">class Membership < ActiveRecord::Base
32
+ # set_primary_keys *keys - turns on composite key functionality
33
+ set_primary_keys :user_id, :group_id
34
+ belongs_to :user
35
+ belongs_to :group
36
+ has_many :statuses, :class_name => 'MembershipStatus', :foreign_key => [:user_id, :group_id]
37
+ end</pre>
38
+
39
+ A model associated with a composite key model would be defined like...
40
+
41
+ <pre syntax="ruby">class MembershipStatus < ActiveRecord::Base
42
+ belongs_to :membership, :foreign_key => [:user_id, :group_id]
43
+ end</pre>
44
+
45
+ That is, associations can include composite keys too. Nice.
46
+
47
+ h2. Demonstration of usage
48
+
49
+ 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.
50
+
51
+ But first, lets check out our primary keys.
52
+
53
+ <pre syntax="ruby">MembershipStatus.primary_key # => "id" # normal single key
54
+ Membership.primary_key # => [:user_id, :group_id] # composite keys
55
+ Membership.primary_key.to_s # => "user_id,group_id"</pre>
56
+
57
+ Now we want to be able to find instances using the same syntax we always use for ActiveRecords...
58
+
59
+ <pre syntax="ruby">MembershipStatus.find(1) # single id returns single instance
60
+ => <MembershipStatus:0x392a8c8 @attributes={"id"=>"1", "status"=>"Active"}>
61
+ Membership.find(1,1) # composite ids returns single instance
62
+ => <Membership:0x39218b0 @attributes={"user_id"=>"1", "group_id"=>"1"}></pre>
63
+
64
+ Using "Ruby on Rails":http://www.rubyonrails.org? You'll want to your url_for helpers
65
+ to convert composite keys into strings and back again...
66
+
67
+ <pre syntax="ruby">Membership.find(:first).to_param # => "1,1"</pre>
68
+
69
+ And then use the string id within your controller to find the object again
70
+
71
+ <pre syntax="ruby">params[:id] # => '1,1'
72
+ Membership.find(params[:id])
73
+ => <Membership:0x3904288 @attributes={"user_id"=>"1", "group_id"=>"1"}></pre>
74
+
75
+ That is, an ActiveRecord supporting composite keys behaves transparently
76
+ throughout your application. Just like a normal ActiveRecord.
77
+
78
+
79
+ h2. Other tricks
80
+
81
+ h3. Pass a list of composite ids to the <code>#find</code> method
82
+
83
+ <pre syntax="ruby">Membership.find [1,1], [2,1]
84
+ => [
85
+ <Membership:0x394ade8 @attributes={"user_id"=>"1", "group_id"=>"1"}>,
86
+ <Membership:0x394ada0 @attributes={"user_id"=>"2", "group_id"=>"1"}>
87
+ ]</pre>
88
+
89
+ Perform <code>#count</code> operations
90
+
91
+ <pre syntax="ruby">MembershipStatus.find(:first).memberships.count # => 1</pre>
92
+
93
+ h3. Routes with Rails
94
+
95
+ From Pete Sumskas:
96
+
97
+ <blockquote>
98
+ I ran into one problem that I didn't see mentioned on "this list":http://groups.google.com/group/compositekeys -
99
+ and I didn't see any information about what I should do to address it in the
100
+ documentation (might have missed it).
101
+
102
+ The problem was that the urls being generated for a 'show' action (for
103
+ example) had a syntax like:
104
+
105
+ <pre>/controller/show/123000,Bu70</pre>
106
+
107
+ for a two-field composite PK. The default routing would not match that,
108
+ so after working out how to do the routing I added:
109
+
110
+ <pre syntax="ruby">map.connect ':controller/:action/:id', :id => /\w+(,\w+)*/</pre>
111
+
112
+ to my <code>route.rb</code> file.
113
+
114
+ </blockquote>
115
+
116
+ <a name="dbs"></a>
117
+
118
+ h2. Which databases?
119
+
120
+
121
+ A suite of unit tests have been run on the following databases supported by ActiveRecord:
122
+
123
+ |_.Database|_.Test Success|_.User feedback|
124
+ |mysql |<span class=success>YES</span>|<span class=success>YES</span> ("Yes!":mailto:compositekeys@googlegroups.com?subject=Mysql+is+working or "No...":mailto:compositekeys@googlegroups.com?subject=Mysql+is+failing)|
125
+ |sqlite3 |<span class=success>YES</span>|<span class=success>YES</span> ("Yes!":mailto:compositekeys@googlegroups.com?subject=Sqlite3+is+working or "No...":mailto:compositekeys@googlegroups.com?subject=Sqlite3+is+failing)|
126
+ |postgresql|<span class=success>YES</span>|<span class=success>YES</span> ("Yes!":mailto:compositekeys@googlegroups.com?subject=Postgresql+is+working or "No...":mailto:compositekeys@googlegroups.com?subject=Postgresql+is+failing)|
127
+ |oracle |<span class=success>YES</span>|<span class=success>YES</span> ("Yes!":mailto:compositekeys@googlegroups.com?subject=Oracle+is+working or "No...":mailto:compositekeys@googlegroups.com?subject=Oracle+is+failing)|
128
+ |sqlserver |<span class=unknown>???</span> ("I can help":mailto:compositekeys@googlegroups.com?subject=Help+with+SQLServer)|<span class=unknown>???</span> ("Yes!":mailto:compositekeys@googlegroups.com?subject=SQLServer+is+working or "No...":mailto:compositekeys@googlegroups.com?subject=SQLServer+is+failing)|
129
+ |db2 |<span class=unknown>???</span> ("I can help":mailto:compositekeys@googlegroups.com?subject=Help+with+DB2)|<span class=unknown>???</span> ("Yes!":mailto:compositekeys@googlegroups.com?subject=DB2+is+working or "No...":mailto:compositekeys@googlegroups.com?subject=DB2+is+failing)|
130
+ |firebird |<span class=unknown>???</span> ("I can help":mailto:compositekeys@googlegroups.com?subject=Help+with+Firebird)|<span class=unknown>???</span> ("Yes!":mailto:compositekeys@googlegroups.com?subject=Firebird+is+working or "No...":mailto:compositekeys@googlegroups.com?subject=Firebird+is+failing)|
131
+ |sybase |<span class=unknown>???</span> ("I can help":mailto:compositekeys@googlegroups.com?subject=Help+with+Sybase)|<span class=unknown>???</span> ("Yes!":mailto:compositekeys@googlegroups.com?subject=Sybase+is+working or "No...":mailto:compositekeys@googlegroups.com?subject=Sybase+is+failing)|
132
+ |openbase |<span class=unknown>???</span> ("I can help":mailto:compositekeys@googlegroups.com?subject=Help+with+Openbase)|<span class=unknown>???</span> ("Yes!":mailto:compositekeys@googlegroups.com?subject=Openbase+is+working or "No...":mailto:compositekeys@googlegroups.com?subject=Openbase+is+failing)|
133
+ |frontbase |<span class=unknown>???</span> ("I can help":mailto:compositekeys@googlegroups.com?subject=Help+with+Frontbase)|<span class=unknown>???</span> ("Yes!":mailto:compositekeys@googlegroups.com?subject=Frontbase+is+working or "No...":mailto:compositekeys@googlegroups.com?subject=Frontbase+is+failing)|
134
+
135
+ h2. Dr Nic's Blog
136
+
137
+ "http://www.drnicwilliams.com":http://www.drnicwilliams.com - for future announcements and
138
+ other stories and things.
139
+
140
+ h2. Forum
141
+
142
+ "http://groups.google.com/group/compositekeys":http://groups.google.com/group/compositekeys
143
+
144
+ h2. How to submit patches
145
+
146
+ Read the "8 steps for fixing other people's code":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/ and for section "8b: Submit patch to Google Groups":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/#8b-google-groups, use the Google Group above.
147
+
148
+
149
+ The source for this project is available via git. You can "browse and/or fork the source":http://github.com/drnic/composite_primary_keys/tree/master, or to clone the project locally:
150
+
151
+ <pre>git clone git://github.com/drnic/composite_primary_keys.git</pre>
152
+
153
+ h2. Licence
154
+
155
+ This code is free to use under the terms of the MIT licence.
156
+
157
+ h2. Contact
158
+
159
+ Comments are welcome. Send an email to "Dr Nic Williams":mailto:drnicwilliams@gmail.com.