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