composite_primary_keys 0.7.0 → 0.7.1
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/Rakefile +1 -1
- data/lib/composite_primary_keys/base.rb +4 -4
- data/lib/composite_primary_keys/version.rb +1 -1
- data/scripts/http-access2-2.0.6.gem +0 -0
- data/scripts/rubyforge +217 -0
- data/scripts/rubyforge-orig +217 -0
- data/scripts/txt2html +65 -0
- data/scripts/txt2js +58 -0
- data/website/index.html +201 -0
- data/website/index.txt +123 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +96 -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 +19 -2
data/scripts/txt2html
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'redcloth'
|
4
|
+
require 'syntax/convertors/html'
|
5
|
+
require 'erb'
|
6
|
+
require '../lib/composite_primary_keys/version.rb'
|
7
|
+
|
8
|
+
version = CompositePrimaryKeys::VERSION::STRING
|
9
|
+
download = 'http://rubyforge.org/projects/compositekeys'
|
10
|
+
|
11
|
+
class Fixnum
|
12
|
+
def ordinal
|
13
|
+
# teens
|
14
|
+
return 'th' if (10..19).include?(self % 100)
|
15
|
+
# others
|
16
|
+
case self % 10
|
17
|
+
when 1: return 'st'
|
18
|
+
when 2: return 'nd'
|
19
|
+
when 3: return 'rd'
|
20
|
+
else return 'th'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Time
|
26
|
+
def pretty
|
27
|
+
return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def convert_syntax(syntax, source)
|
32
|
+
return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
|
33
|
+
end
|
34
|
+
|
35
|
+
if ARGV.length >= 1
|
36
|
+
src, template = ARGV
|
37
|
+
template ||= 'template.rhtml'
|
38
|
+
else
|
39
|
+
puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
|
40
|
+
exit!
|
41
|
+
end
|
42
|
+
|
43
|
+
template = ERB.new(File.open(template).read)
|
44
|
+
|
45
|
+
title = nil
|
46
|
+
body = nil
|
47
|
+
File.open(src) do |fsrc|
|
48
|
+
title_text = fsrc.readline
|
49
|
+
body_text = fsrc.read
|
50
|
+
syntax_items = []
|
51
|
+
body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
|
52
|
+
ident = syntax_items.length
|
53
|
+
element, syntax, source = $1, $2, $3
|
54
|
+
syntax_items << "<#{element} class=\"syntax\">#{convert_syntax(syntax, source)}</#{element}>"
|
55
|
+
"syntax-temp-#{ident}"
|
56
|
+
}
|
57
|
+
title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
|
58
|
+
body = RedCloth.new(body_text).to_html
|
59
|
+
body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
|
60
|
+
end
|
61
|
+
stat = File.stat(src)
|
62
|
+
created = stat.ctime
|
63
|
+
modified = stat.mtime
|
64
|
+
|
65
|
+
$stdout << template.result(binding)
|
data/scripts/txt2js
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'redcloth'
|
4
|
+
require 'syntax/convertors/html'
|
5
|
+
require 'erb'
|
6
|
+
require 'active_support'
|
7
|
+
require '../lib/composite_primary_keys/version.rb'
|
8
|
+
|
9
|
+
version = CompositePrimaryKeys::VERSION::STRING
|
10
|
+
download = 'http://rubyforge.org/projects/compositekeys'
|
11
|
+
|
12
|
+
class Fixnum
|
13
|
+
def ordinal
|
14
|
+
# teens
|
15
|
+
return 'th' if (10..19).include?(self % 100)
|
16
|
+
# others
|
17
|
+
case self % 10
|
18
|
+
when 1: return 'st'
|
19
|
+
when 2: return 'nd'
|
20
|
+
when 3: return 'rd'
|
21
|
+
else return 'th'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Time
|
27
|
+
def pretty
|
28
|
+
return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def convert_syntax(syntax, source)
|
33
|
+
return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
|
34
|
+
end
|
35
|
+
|
36
|
+
if ARGV.length >= 1
|
37
|
+
src, template = ARGV
|
38
|
+
template ||= 'template.js'
|
39
|
+
else
|
40
|
+
puts("Usage: #{File.split($0).last} source.txt [template.js] > output.html")
|
41
|
+
exit!
|
42
|
+
end
|
43
|
+
|
44
|
+
template = ERB.new(File.open(template).read)
|
45
|
+
|
46
|
+
title = nil
|
47
|
+
body = nil
|
48
|
+
File.open(src) do |fsrc|
|
49
|
+
title_text = fsrc.readline
|
50
|
+
body_text = fsrc.read
|
51
|
+
title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
|
52
|
+
body = RedCloth.new(body_text)
|
53
|
+
end
|
54
|
+
stat = File.stat(src)
|
55
|
+
created = stat.ctime
|
56
|
+
modified = stat.mtime
|
57
|
+
|
58
|
+
$stdout << template.result(binding)
|
data/website/index.html
ADDED
@@ -0,0 +1,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 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.1</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 – ActiveRecords – 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…</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…</p>
|
59
|
+
|
60
|
+
|
61
|
+
<p><pre class="syntax"><span class="keyword">class </span><span class="class">ReferenceCode</span> <span class="punct"><</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">=></span> <span class="punct">"</span><span class="string">reference_type_id</span><span class="punct">"</span>
|
65
|
+
<span class="keyword">end</span></pre></p>
|
66
|
+
|
67
|
+
|
68
|
+
<p>Take two classes – <code class="syntax"><span class="constant">ReferenceType</span></code> and <code class="syntax"><span class="constant">ReferenceCode</span></code>
|
69
|
+
– 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">=></span> <span class="punct">"</span><span class="string">reference_type_id</span><span class="punct">"</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">=></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">=></span> <span class="punct">"</span><span class="string">reference_type_id,reference_code</span><span class="punct">"</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…</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">=></span> <span class="punct"><</span><span class="constant">ReferenceType</span><span class="punct">:</span><span class="number">0x392a8c8</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">reference_type_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span>
|
86
|
+
<span class="punct">"</span><span class="string">abbreviation</span><span class="punct">"=>"</span><span class="string">Name Prefix</span><span class="punct">",</span> <span class="punct">"</span><span class="string">type_label</span><span class="punct">"=>"</span><span class="string">NAME_PREFIX</span><span class="punct">"}></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">=></span> <span class="punct"><</span><span class="constant">ReferenceCode</span><span class="punct">:</span><span class="number">0x39218b0</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">reference_type_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span>
|
89
|
+
<span class="punct">"</span><span class="string">code_label</span><span class="punct">"=>"</span><span class="string">MR</span><span class="punct">",</span> <span class="punct">"</span><span class="string">abbreviation</span><span class="punct">"=>"</span><span class="string">Mr</span><span class="punct">",</span> <span class="punct">"</span><span class="string">reference_code</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">"}></span></pre></p>
|
90
|
+
|
91
|
+
|
92
|
+
<p>Using <a href="http://www.rubyonrails.org">Ruby on Rails</a>? You’ll want to your url_for helpers
|
93
|
+
to convert composite keys into strings and back again…</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">=></span> <span class="punct">"</span><span class="string">1,1</span><span class="punct">"</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">=></span> <span class="punct">=></span> <span class="punct"><</span><span class="constant">ReferenceCode</span><span class="punct">:</span><span class="number">0x3904288</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">reference_type_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span>
|
100
|
+
<span class="punct">"</span><span class="string">code_label</span><span class="punct">"=>"</span><span class="string">MR</span><span class="punct">",</span> <span class="punct">"</span><span class="string">abbreviation</span><span class="punct">"=>"</span><span class="string">Mr</span><span class="punct">",</span> <span class="punct">"</span><span class="string">reference_code</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">"}></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"><</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">=></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">=></span> <span class="symbol">:product_tariffs</span><span class="punct">,</span> <span class="symbol">:foreign_key</span> <span class="punct">=></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"><</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">=></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_keys</span> <span class="punct">=></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"><</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_keys</span> <span class="punct">=></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 can use either the
|
138
|
+
:foreign_key or :foreign_keys option to specific the ordered list of table
|
139
|
+
columns. If the column names in both tables match, then the :foreign_key(s)
|
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’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">=></span> <span class="punct">[</span>
|
157
|
+
<span class="punct"><</span><span class="constant">ReferenceCode</span><span class="punct">:</span><span class="number">0x394ade8</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">reference_type_id</span><span class="punct">"=>"</span><span class="string">2</span><span class="punct">",</span>
|
158
|
+
<span class="punct">"</span><span class="string">code_label</span><span class="punct">"=>"</span><span class="string">MALE</span><span class="punct">",</span> <span class="punct">"</span><span class="string">abbreviation</span><span class="punct">"=>"</span><span class="string">Male</span><span class="punct">",</span> <span class="punct">"</span><span class="string">reference_code</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">"}>,</span>
|
159
|
+
<span class="punct"><</span><span class="constant">ReferenceCode</span><span class="punct">:</span><span class="number">0x394ada0</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">reference_type_id</span><span class="punct">"=>"</span><span class="string">2</span><span class="punct">",</span>
|
160
|
+
<span class="punct">"</span><span class="string">code_label</span><span class="punct">"=>"</span><span class="string">FEMALE</span><span class="punct">",</span> <span class="punct">"</span><span class="string">abbreviation</span><span class="punct">"=>"</span><span class="string">Female</span><span class="punct">",</span> <span class="punct">"</span><span class="string">reference_code</span><span class="punct">"=>"</span><span class="string">2</span><span class="punct">"}></span>
|
161
|
+
<span class="punct">]</span></pre></p>
|
162
|
+
|
163
|
+
|
164
|
+
<h2>Dr Nic’s Blog</h2>
|
165
|
+
|
166
|
+
|
167
|
+
<p><a href="http://www.drnicwilliams.com">http://www.drnicwilliams.com</a> – 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>, 1st September 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>
|
data/website/index.txt
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
h1. Composite Primary Keys for Ruby on Rails/ActiveRecords
|
2
|
+
|
3
|
+
h2. What
|
4
|
+
|
5
|
+
Ruby on Rails does not support composite primary keys. This free software is an extension
|
6
|
+
to the database layer of Rails - ActiveRecords - to support composite primary keys
|
7
|
+
as transparently as possible.
|
8
|
+
|
9
|
+
h2. Installing
|
10
|
+
|
11
|
+
<pre syntax="ruby">gem install composite_primary_keys</pre>
|
12
|
+
|
13
|
+
To prepare ActiveRecords for composite primary keys...
|
14
|
+
|
15
|
+
<pre syntax="ruby">require 'composite_primary_keys'</pre>
|
16
|
+
|
17
|
+
A class with composite primary keys would look like...
|
18
|
+
|
19
|
+
<pre syntax="ruby">class ReferenceCode < ActiveRecord::Base
|
20
|
+
# set_primary_keys *keys - turns on composite key functionality
|
21
|
+
set_primary_keys :reference_type_id, :reference_code
|
22
|
+
belongs_to :reference_type, :foreign_key => "reference_type_id"
|
23
|
+
end</pre>
|
24
|
+
|
25
|
+
|
26
|
+
Take two classes - <code syntax="ruby">ReferenceType</code> and <code syntax="ruby">ReferenceCode</code>
|
27
|
+
- where ReferenceCode has a *composite primary key*,
|
28
|
+
one of which is a foreign key to a parent ReferenceType.
|
29
|
+
|
30
|
+
<pre syntax="ruby">ReferenceType.primary_key
|
31
|
+
=> "reference_type_id" # normal single key
|
32
|
+
ReferenceCode.primary_key
|
33
|
+
=> [:reference_type_id, :reference_code] # composite keys
|
34
|
+
ReferenceCode.primary_key.to_s
|
35
|
+
=> "reference_type_id,reference_code"</pre>
|
36
|
+
|
37
|
+
Now we want to be able to find instances using the same syntax we always use for ActiveRecords...
|
38
|
+
|
39
|
+
<pre syntax="ruby">ReferenceType.find 1 # single id returns single instance
|
40
|
+
=> <ReferenceType:0x392a8c8 @attributes={"reference_type_id"=>"1",
|
41
|
+
"abbreviation"=>"Name Prefix", "type_label"=>"NAME_PREFIX"}>
|
42
|
+
ReferenceCode.find 1,1 # composite ids returns single instance
|
43
|
+
=> <ReferenceCode:0x39218b0 @attributes={"reference_type_id"=>"1",
|
44
|
+
"code_label"=>"MR", "abbreviation"=>"Mr", "reference_code"=>"1"}></pre>
|
45
|
+
|
46
|
+
Using "Ruby on Rails":http://www.rubyonrails.org? You'll want to your url_for helpers
|
47
|
+
to convert composite keys into strings and back again...
|
48
|
+
|
49
|
+
<pre syntax="ruby">param_id = ReferenceCode.find_first.to_param
|
50
|
+
=> "1,1"
|
51
|
+
ReferenceCode.find param_id
|
52
|
+
=> => <ReferenceCode:0x3904288 @attributes={"reference_type_id"=>"1",
|
53
|
+
"code_label"=>"MR", "abbreviation"=>"Mr", "reference_code"=>"1"}></pre>
|
54
|
+
|
55
|
+
That is, an ActiveRecord supporting composite keys should behave transparently
|
56
|
+
throughout your application.
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
h2. Associations/Composite Foreign Keys
|
61
|
+
|
62
|
+
The <code syntax="ruby">has_many, has_one</code>, and <code syntax="ruby">belongs_to</code>
|
63
|
+
associations allow for composite foreign keys.
|
64
|
+
|
65
|
+
<pre syntax="ruby">
|
66
|
+
class Product < ActiveRecord::Base
|
67
|
+
set_primary_key :id # redundant
|
68
|
+
has_many :product_tariffs, :foreign_key => :product_id
|
69
|
+
has_many :tariffs, :through => :product_tariffs, :foreign_key => :product_id
|
70
|
+
end
|
71
|
+
class ProductTariff < ActiveRecord::Base
|
72
|
+
set_primary_keys :product_id, :tariff_id, :tariff_start_date
|
73
|
+
belongs_to :products, :foreign_key => :product_id
|
74
|
+
belongs_to :tariffs, :foreign_keys => [:tariff_id, :tariff_start_date]
|
75
|
+
end
|
76
|
+
class Tariff < ActiveRecord::Base
|
77
|
+
set_primary_keys [:tariff_id, :start_date]
|
78
|
+
has_many :product_tariffs, :foreign_keys => [:tariff_id, :tariff_start_date]
|
79
|
+
end
|
80
|
+
</pre>
|
81
|
+
|
82
|
+
The Tariff table has a composite primary key. Hence, the
|
83
|
+
<code syntax="ruby">belongs_to</code> association from ProductTariff to Tariff
|
84
|
+
(called :tariffs) must use a composite foreign key.
|
85
|
+
|
86
|
+
The expression can use either the
|
87
|
+
:foreign_key or :foreign_keys option to specific the ordered list of table
|
88
|
+
columns. If the column names in both tables match, then the :foreign_key(s)
|
89
|
+
option can be omitted.
|
90
|
+
|
91
|
+
Similarly, the <code syntax="ruby">has_many</code> and <code syntax="ruby">has_one</code>
|
92
|
+
commands require the same :foreign_key(s) options if the target table
|
93
|
+
doesn't have column names that match its own primary key column names.
|
94
|
+
|
95
|
+
The order of foreign keys should match the order of the primary keys in the
|
96
|
+
parent table.
|
97
|
+
|
98
|
+
h2. Other tricks
|
99
|
+
|
100
|
+
<pre syntax="ruby">ReferenceCode.find [2,1], [2,2] # list of composite ids
|
101
|
+
=> [
|
102
|
+
<ReferenceCode:0x394ade8 @attributes={"reference_type_id"=>"2",
|
103
|
+
"code_label"=>"MALE", "abbreviation"=>"Male", "reference_code"=>"1"}>,
|
104
|
+
<ReferenceCode:0x394ada0 @attributes={"reference_type_id"=>"2",
|
105
|
+
"code_label"=>"FEMALE", "abbreviation"=>"Female", "reference_code"=>"2"}>
|
106
|
+
]</pre>
|
107
|
+
|
108
|
+
h2. Dr Nic's Blog
|
109
|
+
|
110
|
+
"http://www.drnicwilliams.com":http://www.drnicwilliams.com - for future announcements and
|
111
|
+
other stories and things.
|
112
|
+
|
113
|
+
h2. Forum
|
114
|
+
|
115
|
+
"http://groups.google.com/group/compositekeys":http://groups.google.com/group/compositekeys
|
116
|
+
|
117
|
+
h2. Licence
|
118
|
+
|
119
|
+
This code is free to use under the terms of the MIT licence.
|
120
|
+
|
121
|
+
h2. Contact
|
122
|
+
|
123
|
+
Comments are welcome. Send an email to "Dr Nic Williams":mailto:drnicwilliams@gmail.com.
|