composite_primary_keys 0.7.5 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/Manifest.txt +75 -0
  2. data/Rakefile +83 -101
  3. data/lib/composite_primary_keys.rb +8 -0
  4. data/lib/composite_primary_keys/associations.rb +66 -18
  5. data/lib/composite_primary_keys/base.rb +169 -159
  6. data/lib/composite_primary_keys/calculations.rb +63 -0
  7. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +11 -0
  8. data/lib/composite_primary_keys/version.rb +2 -2
  9. data/scripts/txt2html +4 -2
  10. data/scripts/txt2js +3 -2
  11. data/test/abstract_unit.rb +9 -2
  12. data/test/connections/native_mysql/connection.rb +5 -2
  13. data/test/connections/native_postgresql/connection.rb +15 -0
  14. data/test/connections/native_sqlite/connection.rb +10 -0
  15. data/test/fixtures/db_definitions/mysql.sql +20 -0
  16. data/test/fixtures/db_definitions/postgresql.sql +100 -0
  17. data/test/fixtures/db_definitions/sqlite.sql +84 -0
  18. data/test/fixtures/group.rb +3 -0
  19. data/test/fixtures/groups.yml +3 -0
  20. data/test/fixtures/membership.rb +7 -0
  21. data/test/fixtures/membership_status.rb +3 -0
  22. data/test/fixtures/membership_statuses.yml +8 -0
  23. data/test/fixtures/memberships.yml +6 -0
  24. data/test/{associations_test.rb → test_associations.rb} +22 -12
  25. data/test/{attributes_test.rb → test_attributes.rb} +4 -5
  26. data/test/{clone_test.rb → test_clone.rb} +2 -3
  27. data/test/{create_test.rb → test_create.rb} +2 -3
  28. data/test/{delete_test.rb → test_delete.rb} +2 -3
  29. data/test/{dummy_test.rb → test_dummy.rb} +4 -5
  30. data/test/{find_test.rb → test_find.rb} +3 -4
  31. data/test/{ids_test.rb → test_ids.rb} +4 -4
  32. data/test/{miscellaneous_test.rb → test_miscellaneous.rb} +2 -3
  33. data/test/{pagination_test.rb → test_pagination.rb} +4 -3
  34. data/test/{santiago_test.rb → test_santiago.rb} +5 -3
  35. data/test/test_tutorial_examle.rb +29 -0
  36. data/test/{update_test.rb → test_update.rb} +2 -3
  37. data/website/index.html +267 -201
  38. data/website/index.txt +74 -70
  39. data/website/stylesheets/screen.css +33 -3
  40. data/website/version-raw.js +1 -1
  41. data/website/version.js +1 -1
  42. metadata +80 -66
  43. data/scripts/http-access2-2.0.6.gem +0 -0
  44. data/scripts/rubyforge +0 -217
  45. data/scripts/rubyforge-orig +0 -217
  46. data/test/fixtures/db_definitions/mysql.drop.sql +0 -10
@@ -2,8 +2,7 @@ require 'abstract_unit'
2
2
  require 'fixtures/reference_type'
3
3
  require 'fixtures/reference_code'
4
4
 
5
- class CloneTest < Test::Unit::TestCase
6
- fixtures :reference_types, :reference_codes
5
+ class TestClone < Test::Unit::TestCase
7
6
 
8
7
  CLASSES = {
9
8
  :single => {
@@ -17,7 +16,7 @@ class CloneTest < Test::Unit::TestCase
17
16
  }
18
17
 
19
18
  def setup
20
- super
19
+ create_fixtures :reference_types, :reference_codes
21
20
  self.class.classes = CLASSES
22
21
  end
23
22
 
@@ -2,8 +2,7 @@ require 'abstract_unit'
2
2
  require 'fixtures/reference_type'
3
3
  require 'fixtures/reference_code'
4
4
 
5
- class DummyTest < Test::Unit::TestCase
6
- fixtures :reference_types, :reference_codes
5
+ class TestCreate < Test::Unit::TestCase
7
6
 
8
7
  CLASSES = {
9
8
  :single => {
@@ -19,7 +18,7 @@ class DummyTest < Test::Unit::TestCase
19
18
  }
20
19
 
21
20
  def setup
22
- super
21
+ create_fixtures :reference_types, :reference_codes
23
22
  self.class.classes = CLASSES
24
23
  end
25
24
 
@@ -2,8 +2,7 @@ require 'abstract_unit'
2
2
  require 'fixtures/reference_type'
3
3
  require 'fixtures/reference_code'
4
4
 
5
- class DeleteTest < Test::Unit::TestCase
6
- fixtures :reference_types, :reference_codes
5
+ class TestDelete < Test::Unit::TestCase
7
6
 
8
7
  CLASSES = {
9
8
  :single => {
@@ -17,7 +16,7 @@ class DeleteTest < Test::Unit::TestCase
17
16
  }
18
17
 
19
18
  def setup
20
- super
19
+ create_fixtures :reference_types, :reference_codes
21
20
  self.class.classes = CLASSES
22
21
  end
23
22
 
@@ -2,10 +2,9 @@ require 'abstract_unit'
2
2
  require 'fixtures/reference_type'
3
3
  require 'fixtures/reference_code'
4
4
 
5
- class DummyTest < Test::Unit::TestCase
6
- fixtures :reference_types, :reference_codes
5
+ class TestDummy < Test::Unit::TestCase
7
6
 
8
- CLASSES = {
7
+ classes = {
9
8
  :single => {
10
9
  :class => ReferenceType,
11
10
  :primary_keys => :reference_type_id,
@@ -17,8 +16,8 @@ class DummyTest < Test::Unit::TestCase
17
16
  }
18
17
 
19
18
  def setup
20
- super
21
- self.class.classes = CLASSES
19
+ create_fixtures :reference_types, :reference_codes
20
+ self.class.classes = classes
22
21
  end
23
22
 
24
23
  def test_truth
@@ -3,8 +3,7 @@ require 'fixtures/reference_type'
3
3
  require 'fixtures/reference_code'
4
4
 
5
5
  # Testing the find action on composite ActiveRecords with two primary keys
6
- class FindTest < Test::Unit::TestCase
7
- fixtures :reference_types, :reference_codes
6
+ class TestFind < Test::Unit::TestCase
8
7
 
9
8
  CLASSES = {
10
9
  :single => {
@@ -22,13 +21,13 @@ class FindTest < Test::Unit::TestCase
22
21
  }
23
22
 
24
23
  def setup
25
- super
24
+ create_fixtures :reference_types, :reference_codes
26
25
  self.class.classes = CLASSES
27
26
  end
28
27
 
29
28
  def test_find_first
30
29
  testing_with do
31
- obj = @klass.find_first
30
+ obj = @klass.find(:first)
32
31
  assert obj
33
32
  assert_equal @klass, obj.class
34
33
  end
@@ -2,8 +2,7 @@ require 'abstract_unit'
2
2
  require 'fixtures/reference_type'
3
3
  require 'fixtures/reference_code'
4
4
 
5
- class IdsTest < Test::Unit::TestCase
6
- fixtures :reference_types, :reference_codes
5
+ class TestIds < Test::Unit::TestCase
7
6
 
8
7
  CLASSES = {
9
8
  :single => {
@@ -21,7 +20,7 @@ class IdsTest < Test::Unit::TestCase
21
20
  }
22
21
 
23
22
  def setup
24
- super
23
+ create_fixtures :reference_types, :reference_codes
25
24
  self.class.classes = CLASSES
26
25
  end
27
26
 
@@ -40,7 +39,8 @@ class IdsTest < Test::Unit::TestCase
40
39
 
41
40
  def test_ids_to_s
42
41
  testing_with do
43
- to_test = @klass.find(:all)[0..1].map(&:id)
42
+ order = @klass.primary_key.is_a?(String) ? @klass.primary_key : @klass.primary_key.join(',')
43
+ to_test = @klass.find(:all, :order => order)[0..1].map(&:id)
44
44
  assert_equal '(1,1),(1,2)', @klass.ids_to_s(to_test) if @key_test == :dual
45
45
  assert_equal '1,1;1,2', @klass.ids_to_s(to_test, ',', ';', '', '') if @key_test == :dual
46
46
  end
@@ -2,8 +2,7 @@ require 'abstract_unit'
2
2
  require 'fixtures/reference_type'
3
3
  require 'fixtures/reference_code'
4
4
 
5
- class MiscellaneousTest < Test::Unit::TestCase
6
- fixtures :reference_types, :reference_codes
5
+ class TestMiscellaneous < Test::Unit::TestCase
7
6
 
8
7
  CLASSES = {
9
8
  :single => {
@@ -17,7 +16,7 @@ class MiscellaneousTest < Test::Unit::TestCase
17
16
  }
18
17
 
19
18
  def setup
20
- super
19
+ create_fixtures :reference_types, :reference_codes, :products
21
20
  self.class.classes = CLASSES
22
21
  end
23
22
 
@@ -3,10 +3,11 @@ require 'fixtures/reference_type'
3
3
  require 'fixtures/reference_code'
4
4
  require 'action_controller/pagination'
5
5
 
6
- class PaginationTest < Test::Unit::TestCase
7
- fixtures :reference_types, :reference_codes
6
+ class TestPagination < Test::Unit::TestCase
8
7
  include ActionController::Pagination
9
8
  DEFAULT_PAGE_SIZE = 2
9
+
10
+ attr_accessor :params
10
11
 
11
12
  CLASSES = {
12
13
  :single => {
@@ -22,7 +23,7 @@ class PaginationTest < Test::Unit::TestCase
22
23
  }
23
24
 
24
25
  def setup
25
- super
26
+ create_fixtures :reference_types, :reference_codes
26
27
  self.class.classes = CLASSES
27
28
  @params = {}
28
29
  end
@@ -6,8 +6,10 @@ require 'fixtures/user'
6
6
  require 'fixtures/article'
7
7
  require 'fixtures/reading'
8
8
 
9
- class AssociationTest < Test::Unit::TestCase
10
- fixtures :suburbs, :streets, :users, :articles, :readings
9
+ class TestSantiago < Test::Unit::TestCase
10
+ def setup
11
+ create_fixtures :suburbs, :streets, :users, :articles, :readings
12
+ end
11
13
 
12
14
  def test_normal_and_composite_associations
13
15
  assert_not_nil @suburb = Suburb.find(1,1)
@@ -18,7 +20,7 @@ class AssociationTest < Test::Unit::TestCase
18
20
  end
19
21
 
20
22
  def test_single_keys
21
- @santiago = users(:santiago)
23
+ @santiago = User.find(1)
22
24
  assert_not_nil @santiago.articles
23
25
  assert_equal 2, @santiago.articles.length
24
26
  assert_not_nil @santiago.readings
@@ -0,0 +1,29 @@
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
+
9
+ def setup
10
+ create_fixtures :users, :groups, :memberships, :membership_statuses
11
+ end
12
+
13
+ def test_membership
14
+ assert(membership = Membership.find(1,1), "Cannot find a membership")
15
+ assert(membership.user)
16
+ assert(membership.group)
17
+ end
18
+
19
+ def test_status
20
+ assert(membership = Membership.find(1,1), "Cannot find a membership")
21
+ assert(statuses = membership.statuses, "No has_many association to status")
22
+ assert_equal(membership, statuses.first.membership)
23
+ end
24
+
25
+ def test_count
26
+ assert(membership = Membership.find(1,1), "Cannot find a membership")
27
+ assert_equal(1, membership.statuses.count)
28
+ end
29
+ end
@@ -2,8 +2,7 @@ require 'abstract_unit'
2
2
  require 'fixtures/reference_type'
3
3
  require 'fixtures/reference_code'
4
4
 
5
- class UpdateTest < Test::Unit::TestCase
6
- fixtures :reference_types, :reference_codes
5
+ class TestUpdate < Test::Unit::TestCase
7
6
 
8
7
  CLASSES = {
9
8
  :single => {
@@ -19,7 +18,7 @@ class UpdateTest < Test::Unit::TestCase
19
18
  }
20
19
 
21
20
  def setup
22
- super
21
+ create_fixtures :reference_types, :reference_codes
23
22
  self.class.classes = CLASSES
24
23
  end
25
24
 
@@ -1,201 +1,267 @@
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 for Ruby on Rails/ActiveRecords
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 for Ruby on Rails/ActiveRecords</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">0.7.4</a>
37
- </div>
38
- <h2>What</h2>
39
-
40
-
41
- <p>Ruby on Rails does not support composite primary keys. This free software is an extension
42
- to the database layer of Rails &#8211; ActiveRecords &#8211; to support composite primary keys
43
- as transparently as possible.</p>
44
-
45
-
46
- <h2>Installing</h2>
47
-
48
-
49
- <p><pre class="syntax"><span class="ident">gem</span> <span class="ident">install</span> <span class="ident">composite_primary_keys</span></pre></p>
50
-
51
-
52
- <p>To prepare ActiveRecords for composite primary keys&#8230;</p>
53
-
54
-
55
- <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>
56
-
57
-
58
- <p>A class with composite primary keys would look like&#8230;</p>
59
-
60
-
61
- <p><pre class="syntax"><span class="keyword">class </span><span class="class">ReferenceCode</span> <span class="punct">&lt;</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
62
- <span class="comment"># set_primary_keys *keys - turns on composite key functionality</span>
63
- <span class="ident">set_primary_keys</span> <span class="symbol">:reference_type_id</span><span class="punct">,</span> <span class="symbol">:reference_code</span>
64
- <span class="ident">belongs_to</span> <span class="symbol">:reference_type</span><span class="punct">,</span> <span class="symbol">:foreign_key</span> <span class="punct">=&gt;</span> <span class="punct">&quot;</span><span class="string">reference_type_id</span><span class="punct">&quot;</span>
65
- <span class="keyword">end</span></pre></p>
66
-
67
-
68
- <p>Take two classes &#8211; <code class="syntax"><span class="constant">ReferenceType</span></code> and <code class="syntax"><span class="constant">ReferenceCode</span></code>
69
- &#8211; where ReferenceCode has a <strong>composite primary key</strong>,
70
- one of which is a foreign key to a parent ReferenceType.</p>
71
-
72
-
73
- <p><pre class="syntax"><span class="constant">ReferenceType</span><span class="punct">.</span><span class="ident">primary_key</span>
74
- <span class="punct">=&gt;</span> <span class="punct">&quot;</span><span class="string">reference_type_id</span><span class="punct">&quot;</span> <span class="comment"># normal single key</span>
75
- <span class="constant">ReferenceCode</span><span class="punct">.</span><span class="ident">primary_key</span>
76
- <span class="punct">=&gt;</span> <span class="punct">[</span><span class="symbol">:reference_type_id</span><span class="punct">,</span> <span class="symbol">:reference_code</span><span class="punct">]</span> <span class="comment"># composite keys</span>
77
- <span class="constant">ReferenceCode</span><span class="punct">.</span><span class="ident">primary_key</span><span class="punct">.</span><span class="ident">to_s</span>
78
- <span class="punct">=&gt;</span> <span class="punct">&quot;</span><span class="string">reference_type_id,reference_code</span><span class="punct">&quot;</span></pre></p>
79
-
80
-
81
- <p>Now we want to be able to find instances using the same syntax we always use for ActiveRecords&#8230;</p>
82
-
83
-
84
- <p><pre class="syntax"><span class="constant">ReferenceType</span><span class="punct">.</span><span class="ident">find</span> <span class="number">1</span> <span class="comment"># single id returns single instance</span>
85
- <span class="punct">=&gt;</span> <span class="punct">&lt;</span><span class="constant">ReferenceType</span><span class="punct">:</span><span class="number">0x392a8c8</span> <span class="attribute">@attributes</span><span class="punct">={&quot;</span><span class="string">reference_type_id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;,</span>
86
- <span class="punct">&quot;</span><span class="string">abbreviation</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">Name Prefix</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">type_label</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">NAME_PREFIX</span><span class="punct">&quot;}&gt;</span>
87
- <span class="constant">ReferenceCode</span><span class="punct">.</span><span class="ident">find</span> <span class="number">1</span><span class="punct">,</span><span class="number">1</span> <span class="comment"># composite ids returns single instance</span>
88
- <span class="punct">=&gt;</span> <span class="punct">&lt;</span><span class="constant">ReferenceCode</span><span class="punct">:</span><span class="number">0x39218b0</span> <span class="attribute">@attributes</span><span class="punct">={&quot;</span><span class="string">reference_type_id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;,</span>
89
- <span class="punct">&quot;</span><span class="string">code_label</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">MR</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">abbreviation</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">Mr</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">reference_code</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;}&gt;</span></pre></p>
90
-
91
-
92
- <p>Using <a href="http://www.rubyonrails.org">Ruby on Rails</a>? You&#8217;ll want to your url_for helpers
93
- to convert composite keys into strings and back again&#8230;</p>
94
-
95
-
96
- <p><pre class="syntax"><span class="ident">param_id</span> <span class="punct">=</span> <span class="constant">ReferenceCode</span><span class="punct">.</span><span class="ident">find_first</span><span class="punct">.</span><span class="ident">to_param</span>
97
- <span class="punct">=&gt;</span> <span class="punct">&quot;</span><span class="string">1,1</span><span class="punct">&quot;</span>
98
- <span class="constant">ReferenceCode</span><span class="punct">.</span><span class="ident">find</span> <span class="ident">param_id</span>
99
- <span class="punct">=&gt;</span> <span class="punct">=&gt;</span> <span class="punct">&lt;</span><span class="constant">ReferenceCode</span><span class="punct">:</span><span class="number">0x3904288</span> <span class="attribute">@attributes</span><span class="punct">={&quot;</span><span class="string">reference_type_id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;,</span>
100
- <span class="punct">&quot;</span><span class="string">code_label</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">MR</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">abbreviation</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">Mr</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">reference_code</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;}&gt;</span></pre></p>
101
-
102
-
103
- <p>That is, an ActiveRecord supporting composite keys should behave transparently
104
- throughout your application.</p>
105
-
106
-
107
- <h2>Associations/Composite Foreign Keys</h2>
108
-
109
-
110
- <p>The <code class="syntax"><span class="ident">has_many</span><span class="punct">,</span> <span class="ident">has_one</span></code>, and <code class="syntax"><span class="ident">belongs_to</span></code>
111
- associations allow for composite foreign keys.</p>
112
-
113
-
114
- <p><pre class="syntax">
115
- <span class="keyword">class </span><span class="class">Product</span> <span class="punct">&lt;</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
116
- <span class="ident">set_primary_key</span> <span class="symbol">:id</span> <span class="comment"># redundant</span>
117
- <span class="ident">has_many</span> <span class="symbol">:product_tariffs</span><span class="punct">,</span> <span class="symbol">:foreign_key</span> <span class="punct">=&gt;</span> <span class="symbol">:product_id</span>
118
- <span class="ident">has_many</span> <span class="symbol">:tariffs</span><span class="punct">,</span> <span class="symbol">:through</span> <span class="punct">=&gt;</span> <span class="symbol">:product_tariffs</span><span class="punct">,</span> <span class="symbol">:foreign_key</span> <span class="punct">=&gt;</span> <span class="symbol">:product_id</span>
119
- <span class="keyword">end</span>
120
- <span class="keyword">class </span><span class="class">ProductTariff</span> <span class="punct">&lt;</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
121
- <span class="ident">set_primary_keys</span> <span class="symbol">:product_id</span><span class="punct">,</span> <span class="symbol">:tariff_id</span><span class="punct">,</span> <span class="symbol">:tariff_start_date</span>
122
- <span class="ident">belongs_to</span> <span class="symbol">:products</span><span class="punct">,</span> <span class="symbol">:foreign_key</span> <span class="punct">=&gt;</span> <span class="symbol">:product_id</span>
123
- <span class="ident">belongs_to</span> <span class="symbol">:tariffs</span><span class="punct">,</span> <span class="symbol">:foreign_key</span> <span class="punct">=&gt;</span> <span class="punct">[</span><span class="symbol">:tariff_id</span><span class="punct">,</span> <span class="symbol">:tariff_start_date</span><span class="punct">]</span>
124
- <span class="keyword">end</span>
125
- <span class="keyword">class </span><span class="class">Tariff</span> <span class="punct">&lt;</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
126
- <span class="ident">set_primary_keys</span> <span class="punct">[</span><span class="symbol">:tariff_id</span><span class="punct">,</span> <span class="symbol">:start_date</span><span class="punct">]</span>
127
- <span class="ident">has_many</span> <span class="symbol">:product_tariffs</span><span class="punct">,</span> <span class="symbol">:foreign_key</span> <span class="punct">=&gt;</span> <span class="punct">[</span><span class="symbol">:tariff_id</span><span class="punct">,</span> <span class="symbol">:tariff_start_date</span><span class="punct">]</span>
128
- <span class="keyword">end</span>
129
- </pre></p>
130
-
131
-
132
- <p>The Tariff table has a composite primary key. Hence, the
133
- <code class="syntax"><span class="ident">belongs_to</span></code> association from ProductTariff to Tariff
134
- (called :tariffs) must use a composite foreign key.</p>
135
-
136
-
137
- <p>The expression must use the :foreign_key option (NOTE, <code>:foreign_keys</code> is not currently supported)
138
- to specific the ordered list of table
139
- columns. If the column names in both tables match, then the :foreign_key
140
- option can be omitted.</p>
141
-
142
-
143
- <p>Similarly, the <code class="syntax"><span class="ident">has_many</span></code> and <code class="syntax"><span class="ident">has_one</span></code>
144
- commands require the same :foreign_key(s) options if the target table
145
- doesn&#8217;t have column names that match its own primary key column names.</p>
146
-
147
-
148
- <p>The order of foreign keys should match the order of the primary keys in the
149
- parent table.</p>
150
-
151
-
152
- <h2>Other tricks</h2>
153
-
154
-
155
- <p><pre class="syntax"><span class="constant">ReferenceCode</span><span class="punct">.</span><span class="ident">find</span> <span class="punct">[</span><span class="number">2</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">2</span><span class="punct">]</span> <span class="comment"># list of composite ids</span>
156
- <span class="punct">=&gt;</span> <span class="punct">[</span>
157
- <span class="punct">&lt;</span><span class="constant">ReferenceCode</span><span class="punct">:</span><span class="number">0x394ade8</span> <span class="attribute">@attributes</span><span class="punct">={&quot;</span><span class="string">reference_type_id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">2</span><span class="punct">&quot;,</span>
158
- <span class="punct">&quot;</span><span class="string">code_label</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">MALE</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">abbreviation</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">Male</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">reference_code</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">1</span><span class="punct">&quot;}&gt;,</span>
159
- <span class="punct">&lt;</span><span class="constant">ReferenceCode</span><span class="punct">:</span><span class="number">0x394ada0</span> <span class="attribute">@attributes</span><span class="punct">={&quot;</span><span class="string">reference_type_id</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">2</span><span class="punct">&quot;,</span>
160
- <span class="punct">&quot;</span><span class="string">code_label</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">FEMALE</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">abbreviation</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">Female</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">reference_code</span><span class="punct">&quot;=&gt;&quot;</span><span class="string">2</span><span class="punct">&quot;}&gt;</span>
161
- <span class="punct">]</span></pre></p>
162
-
163
-
164
- <h2>Dr Nic&#8217;s Blog</h2>
165
-
166
-
167
- <p><a href="http://www.drnicwilliams.com">http://www.drnicwilliams.com</a> &#8211; for future announcements and
168
- other stories and things.</p>
169
-
170
-
171
- <h2>Forum</h2>
172
-
173
-
174
- <p><a href="http://groups.google.com/group/compositekeys">http://groups.google.com/group/compositekeys</a></p>
175
-
176
-
177
- <h2>Licence</h2>
178
-
179
-
180
- <p>This code is free to use under the terms of the <span class="caps">MIT</span> licence.</p>
181
-
182
-
183
- <h2>Contact</h2>
184
-
185
-
186
- <p>Comments are welcome. Send an email to <a href="mailto:drnicwilliams@gmail.com">Dr Nic Williams</a>.</p>
187
- <p class="coda">
188
- <a href="mailto:drnicwilliams@gmail.com">Dr Nic</a>, 24th October 2006<br>
189
- Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
190
- </p>
191
- </div>
192
-
193
- <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
194
- </script>
195
- <script type="text/javascript">
196
- _uacct = "UA-567811-2";
197
- urchinTracker();
198
- </script>
199
-
200
- </body>
201
- </html>
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">0.8.0</a>
37
+ </div>
38
+ <h1>&#x2192; Ruby on Rails</h1>
39
+
40
+
41
+ <h1>&#x2192; ActiveRecords</h1>
42
+
43
+
44
+ <h2>What</h2>
45
+
46
+
47
+ <p>Ruby on Rails does not support composite primary keys. This free software is an extension
48
+ 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>
49
+
50
+
51
+ <p>Any Ruby script using ActiveRecords can use Composite Primary Keys with this library.</p>
52
+
53
+
54
+ <h2>Installing</h2>
55
+
56
+
57
+ <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>
58
+
59
+
60
+ <p>Rails: Add the following to the bottom of your <code>environment.rb</code> file</p>
61
+
62
+
63
+ <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>
64
+
65
+
66
+ <p>Ruby scripts: Add the following to the top of your script</p>
67
+
68
+
69
+ <p><pre class="syntax"><span class="ident">require</span> <span class="punct">'</span><span class="string">rubygems</span><span class="punct">'</span>
70
+ <span class="ident">require</span> <span class="punct">'</span><span class="string">composite_primary_keys</span><span class="punct">'</span></pre></p>
71
+
72
+
73
+ <h2>The basics</h2>
74
+
75
+
76
+ <p>A model with composite primary keys would look like&#8230;</p>
77
+
78
+
79
+ <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>
80
+ <span class="comment"># set_primary_keys *keys - turns on composite key functionality</span>
81
+ <span class="ident">set_primary_keys</span> <span class="symbol">:user_id</span><span class="punct">,</span> <span class="symbol">:group_id</span>
82
+ <span class="ident">belongs_to</span> <span class="symbol">:user</span>
83
+ <span class="ident">belongs_to</span> <span class="symbol">:group</span>
84
+ <span class="ident">has_many</span> <span class="symbol">:statuses</span><span class="punct">,</span> <span class="symbol">:class</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>
85
+ <span class="keyword">end</span></pre></p>
86
+
87
+
88
+ <p>A model associated with a composite key model would be defined like&#8230;</p>
89
+
90
+
91
+ <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>
92
+ <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>
93
+ <span class="keyword">end</span></pre></p>
94
+
95
+
96
+ <p>That is, associations can include composite keys too. Nice.</p>
97
+
98
+
99
+ <h2>Demonstration of usage</h2>
100
+
101
+
102
+ <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>
103
+
104
+
105
+ <p>But first, lets check out our primary keys.</p>
106
+
107
+
108
+ <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>
109
+ <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>
110
+ <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>
111
+
112
+
113
+ <p>Now we want to be able to find instances using the same syntax we always use for ActiveRecords&#8230;</p>
114
+
115
+
116
+ <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>
117
+ <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>
118
+ <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>
119
+ <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>
120
+
121
+
122
+ <p>Using <a href="http://www.rubyonrails.org">Ruby on Rails</a>? You&#8217;ll want to your url_for helpers
123
+ to convert composite keys into strings and back again&#8230;</p>
124
+
125
+
126
+ <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>
127
+
128
+
129
+ <p>And then use the string id within your controller to find the object again</p>
130
+
131
+
132
+ <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>
133
+ <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>
134
+ <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>
135
+
136
+
137
+ <p>That is, an ActiveRecord supporting composite keys behaves transparently
138
+ throughout your application. Just like a normal ActiveRecord.</p>
139
+
140
+
141
+ <h2>Other tricks</h2>
142
+
143
+
144
+ <p>Pass a list of composite ids to the <code>#find</code> method</p>
145
+
146
+
147
+ <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>
148
+ <span class="punct">=&gt;</span> <span class="punct">[</span>
149
+ <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>
150
+ <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>
151
+ <span class="punct">]</span></pre></p>
152
+
153
+
154
+ <p>Perform <code>#count</code> operations</p>
155
+
156
+
157
+ <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>
158
+
159
+
160
+ <p><a name="dbs"></a></p>
161
+
162
+
163
+ <h2>Which databases?</h2>
164
+
165
+
166
+ <p>A suite of unit tests have been run on the following databases supported by ActiveRecord:</p>
167
+
168
+
169
+ <table>
170
+ <tr>
171
+ <th>Database</th>
172
+ <th>Test Success</th>
173
+ <th>User feedback (since 0.8.0)</th>
174
+ </tr>
175
+ <tr>
176
+ <td>mysql </td>
177
+ <td><span class=success><span class="caps">YES</span></span></td>
178
+ <td><span class=unknown>???</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>
179
+ </tr>
180
+ <tr>
181
+ <td>sqlite3 </td>
182
+ <td><span class=success><span class="caps">YES</span></span> (new 0.8.0)</td>
183
+ <td><span class=unknown>???</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>
184
+ </tr>
185
+ <tr>
186
+ <td>postgresql</td>
187
+ <td><span class=success><span class="caps">YES</span></span> (new 0.8.0)</td>
188
+ <td><span class=unknown>???</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>
189
+ </tr>
190
+ <tr>
191
+ <td>oracle </td>
192
+ <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Help+with+Oracle">I can help</a>)</td>
193
+ <td><span class=unknown>???</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>
194
+ </tr>
195
+ <tr>
196
+ <td>sqlserver </td>
197
+ <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Help+with+SQLServer">I can help</a>)</td>
198
+ <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>
199
+ </tr>
200
+ <tr>
201
+ <td>db2 </td>
202
+ <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Help+with+DB2">I can help</a>)</td>
203
+ <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>
204
+ </tr>
205
+ <tr>
206
+ <td>firebird </td>
207
+ <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Help+with+Firebird">I can help</a>)</td>
208
+ <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>
209
+ </tr>
210
+ <tr>
211
+ <td>sybase </td>
212
+ <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Help+with+Sybase">I can help</a>)</td>
213
+ <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>
214
+ </tr>
215
+ <tr>
216
+ <td>openbase </td>
217
+ <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Help+with+Openbase">I can help</a>)</td>
218
+ <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>
219
+ </tr>
220
+ <tr>
221
+ <td>frontbase </td>
222
+ <td><span class=unknown>???</span> (<a href="mailto:compositekeys@googlegroups.com?subject=Help+with+Frontbase">I can help</a>)</td>
223
+ <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>
224
+ </tr>
225
+ </table>
226
+
227
+
228
+
229
+
230
+ <h2>Dr Nic&#8217;s Blog</h2>
231
+
232
+
233
+ <p><a href="http://www.drnicwilliams.com">http://www.drnicwilliams.com</a> &#8211; for future announcements and
234
+ other stories and things.</p>
235
+
236
+
237
+ <h2>Forum</h2>
238
+
239
+
240
+ <p><a href="http://groups.google.com/group/compositekeys">http://groups.google.com/group/compositekeys</a></p>
241
+
242
+
243
+ <h2>Licence</h2>
244
+
245
+
246
+ <p>This code is free to use under the terms of the <span class="caps">MIT</span> licence.</p>
247
+
248
+
249
+ <h2>Contact</h2>
250
+
251
+
252
+ <p>Comments are welcome. Send an email to <a href="mailto:drnicwilliams@gmail.com">Dr Nic Williams</a>.</p>
253
+ <p class="coda">
254
+ <a href="mailto:drnicwilliams@gmail.com">Dr Nic</a>, 7th April 2007<br>
255
+ Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
256
+ </p>
257
+ </div>
258
+
259
+ <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
260
+ </script>
261
+ <script type="text/javascript">
262
+ _uacct = "UA-567811-2";
263
+ urchinTracker();
264
+ </script>
265
+
266
+ </body>
267
+ </html>