composite_primary_keys 0.7.5 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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>