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.
- data/Manifest.txt +75 -0
- data/Rakefile +83 -101
- data/lib/composite_primary_keys.rb +8 -0
- data/lib/composite_primary_keys/associations.rb +66 -18
- data/lib/composite_primary_keys/base.rb +169 -159
- data/lib/composite_primary_keys/calculations.rb +63 -0
- data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +11 -0
- data/lib/composite_primary_keys/version.rb +2 -2
- data/scripts/txt2html +4 -2
- data/scripts/txt2js +3 -2
- data/test/abstract_unit.rb +9 -2
- data/test/connections/native_mysql/connection.rb +5 -2
- data/test/connections/native_postgresql/connection.rb +15 -0
- data/test/connections/native_sqlite/connection.rb +10 -0
- data/test/fixtures/db_definitions/mysql.sql +20 -0
- data/test/fixtures/db_definitions/postgresql.sql +100 -0
- data/test/fixtures/db_definitions/sqlite.sql +84 -0
- data/test/fixtures/group.rb +3 -0
- data/test/fixtures/groups.yml +3 -0
- data/test/fixtures/membership.rb +7 -0
- data/test/fixtures/membership_status.rb +3 -0
- data/test/fixtures/membership_statuses.yml +8 -0
- data/test/fixtures/memberships.yml +6 -0
- data/test/{associations_test.rb → test_associations.rb} +22 -12
- data/test/{attributes_test.rb → test_attributes.rb} +4 -5
- data/test/{clone_test.rb → test_clone.rb} +2 -3
- data/test/{create_test.rb → test_create.rb} +2 -3
- data/test/{delete_test.rb → test_delete.rb} +2 -3
- data/test/{dummy_test.rb → test_dummy.rb} +4 -5
- data/test/{find_test.rb → test_find.rb} +3 -4
- data/test/{ids_test.rb → test_ids.rb} +4 -4
- data/test/{miscellaneous_test.rb → test_miscellaneous.rb} +2 -3
- data/test/{pagination_test.rb → test_pagination.rb} +4 -3
- data/test/{santiago_test.rb → test_santiago.rb} +5 -3
- data/test/test_tutorial_examle.rb +29 -0
- data/test/{update_test.rb → test_update.rb} +2 -3
- data/website/index.html +267 -201
- data/website/index.txt +74 -70
- data/website/stylesheets/screen.css +33 -3
- data/website/version-raw.js +1 -1
- data/website/version.js +1 -1
- metadata +80 -66
- data/scripts/http-access2-2.0.6.gem +0 -0
- data/scripts/rubyforge +0 -217
- data/scripts/rubyforge-orig +0 -217
- 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
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
6
|
-
fixtures :reference_types, :reference_codes
|
5
|
+
class TestDummy < Test::Unit::TestCase
|
7
6
|
|
8
|
-
|
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
|
-
|
21
|
-
self.class.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
|
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
|
-
|
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.
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
10
|
-
|
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 =
|
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
|
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
|
-
|
21
|
+
create_fixtures :reference_types, :reference_codes
|
23
22
|
self.class.classes = CLASSES
|
24
23
|
end
|
25
24
|
|
data/website/index.html
CHANGED
@@ -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
|
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
|
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.
|
37
|
-
</div>
|
38
|
-
<
|
39
|
-
|
40
|
-
|
41
|
-
<
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
<
|
74
|
-
|
75
|
-
|
76
|
-
<
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
<span class="
|
86
|
-
|
87
|
-
|
88
|
-
<
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
<p
|
97
|
-
|
98
|
-
|
99
|
-
<
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
<span class="
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
</
|
130
|
-
|
131
|
-
|
132
|
-
<p>
|
133
|
-
<
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
<p>
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
</
|
192
|
-
|
193
|
-
<
|
194
|
-
</
|
195
|
-
<
|
196
|
-
|
197
|
-
|
198
|
-
</
|
199
|
-
|
200
|
-
|
201
|
-
</
|
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>→ Ruby on Rails</h1>
|
39
|
+
|
40
|
+
|
41
|
+
<h1>→ 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 – <a href="http://wiki.rubyonrails.com/rails/pages/ActiveRecord">ActiveRecords</a> – 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…</p>
|
77
|
+
|
78
|
+
|
79
|
+
<p><pre class="syntax"><span class="keyword">class </span><span class="class">Membership</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
|
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">=></span> <span class="punct">'</span><span class="string">MembershipStatus</span><span class="punct">',</span> <span class="symbol">:foreign_key</span> <span class="punct">=></span> <span class="punct">[</span><span class="symbol">:user_id</span><span class="punct">,</span> <span class="symbol">:group_id</span><span class="punct">]</span>
|
85
|
+
<span class="keyword">end</span></pre></p>
|
86
|
+
|
87
|
+
|
88
|
+
<p>A model associated with a composite key model would be defined like…</p>
|
89
|
+
|
90
|
+
|
91
|
+
<p><pre class="syntax"><span class="keyword">class </span><span class="class">MembershipStatus</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
|
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">=></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’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"># => "id" # normal single key</span>
|
109
|
+
<span class="constant">Membership</span><span class="punct">.</span><span class="ident">primary_key</span> <span class="comment"># => [:user_id, :group_id] # composite keys</span>
|
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"># => "user_id,group_id"</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…</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">=></span> <span class="punct"><</span><span class="constant">MembershipStatus</span><span class="punct">:</span><span class="number">0x392a8c8</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span> <span class="punct">"</span><span class="string">status</span><span class="punct">"=>"</span><span class="string">Active</span><span class="punct">"}></span>
|
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">=></span> <span class="punct"><</span><span class="constant">Membership</span><span class="punct">:</span><span class="number">0x39218b0</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">user_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span> <span class="punct">"</span><span class="string">group_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">"}></span></pre></p>
|
120
|
+
|
121
|
+
|
122
|
+
<p>Using <a href="http://www.rubyonrails.org">Ruby on Rails</a>? You’ll want to your url_for helpers
|
123
|
+
to convert composite keys into strings and back again…</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"># => "1,1"</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"># => '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">=></span> <span class="punct"><</span><span class="constant">Membership</span><span class="punct">:</span><span class="number">0x3904288</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">user_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span> <span class="punct">"</span><span class="string">group_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">"}></span></pre></p>
|
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">=></span> <span class="punct">[</span>
|
149
|
+
<span class="punct"><</span><span class="constant">Membership</span><span class="punct">:</span><span class="number">0x394ade8</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">user_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span> <span class="punct">"</span><span class="string">group_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">"}>,</span>
|
150
|
+
<span class="punct"><</span><span class="constant">Membership</span><span class="punct">:</span><span class="number">0x394ada0</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">user_id</span><span class="punct">"=>"</span><span class="string">2</span><span class="punct">",</span> <span class="punct">"</span><span class="string">group_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">"}></span>
|
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"># => 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…</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…</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…</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…</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…</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…</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…</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…</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…</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…</a>)</td>
|
224
|
+
</tr>
|
225
|
+
</table>
|
226
|
+
|
227
|
+
|
228
|
+
|
229
|
+
|
230
|
+
<h2>Dr Nic’s Blog</h2>
|
231
|
+
|
232
|
+
|
233
|
+
<p><a href="http://www.drnicwilliams.com">http://www.drnicwilliams.com</a> – 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>
|