KirbyBase 2.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README +73 -0
- data/bin/kbserver.rb +20 -0
- data/changes.txt +105 -0
- data/examples/aaa_try_this_first/kbtest.rb +207 -0
- data/examples/add_column_test/add_column_test.rb +27 -0
- data/examples/calculated_field_test/calculated_field_test.rb +51 -0
- data/examples/change_column_type_test/change_column_type_test.rb +25 -0
- data/examples/column_required_test/column_required_test.rb +33 -0
- data/examples/crosstab_test/crosstab_test.rb +100 -0
- data/examples/csv_import_test/csv_import_test.rb +31 -0
- data/examples/csv_import_test/plane.csv +11 -0
- data/examples/default_value_test/default_value_test.rb +43 -0
- data/examples/drop_column_test/drop_column_test.rb +24 -0
- data/examples/indexes_test/add_index_test.rb +46 -0
- data/examples/indexes_test/drop_index_test.rb +66 -0
- data/examples/indexes_test/index_test.rb +71 -0
- data/examples/kbserver_as_win32_service/kbserver_daemon.rb +47 -0
- data/examples/kbserver_as_win32_service/kbserverctl.rb +75 -0
- data/examples/link_many_test/link_many_test.rb +70 -0
- data/examples/lookup_field_test/lookup_field_test.rb +55 -0
- data/examples/lookup_field_test/lookup_field_test_2.rb +62 -0
- data/examples/lookup_field_test/the_hal_fulton_feature_test.rb +69 -0
- data/examples/many_to_many_test/many_to_many_test.rb +65 -0
- data/examples/memo_test/memo_test.rb +63 -0
- data/examples/memo_test/memos/blank.txt +0 -0
- data/examples/record_class_test/record_class_test.rb +77 -0
- data/examples/rename_column_test/rename_column_test.rb +46 -0
- data/examples/rename_table_test/rename_table_test.rb +38 -0
- data/examples/yaml_field_test/yaml_field_test.rb +47 -0
- data/images/blank.png +0 -0
- data/images/callouts/1.png +0 -0
- data/images/callouts/10.png +0 -0
- data/images/callouts/11.png +0 -0
- data/images/callouts/12.png +0 -0
- data/images/callouts/13.png +0 -0
- data/images/callouts/14.png +0 -0
- data/images/callouts/15.png +0 -0
- data/images/callouts/2.png +0 -0
- data/images/callouts/3.png +0 -0
- data/images/callouts/4.png +0 -0
- data/images/callouts/5.png +0 -0
- data/images/callouts/6.png +0 -0
- data/images/callouts/7.png +0 -0
- data/images/callouts/8.png +0 -0
- data/images/callouts/9.png +0 -0
- data/images/caution.png +0 -0
- data/images/client_server.png +0 -0
- data/images/example.png +0 -0
- data/images/home.png +0 -0
- data/images/important.png +0 -0
- data/images/kirby1.jpg +0 -0
- data/images/next.png +0 -0
- data/images/note.png +0 -0
- data/images/prev.png +0 -0
- data/images/single_user.png +0 -0
- data/images/smallnew.png +0 -0
- data/images/tip.png +0 -0
- data/images/toc-blank.png +0 -0
- data/images/toc-minus.png +0 -0
- data/images/toc-plus.png +0 -0
- data/images/up.png +0 -0
- data/images/warning.png +0 -0
- data/kirbybaserubymanual.html +2243 -0
- data/lib/kirbybase.rb +3662 -0
- metadata +126 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
# This script is an example of how you can use KirbyBase's new Memo field
|
2
|
+
# type.
|
3
|
+
|
4
|
+
require 'kirbybase'
|
5
|
+
|
6
|
+
db = KirbyBase.new { |d| d.memo_blob_path = './memos' }
|
7
|
+
|
8
|
+
#db = KirbyBase.new(:client, 'localhost', 44444)
|
9
|
+
|
10
|
+
|
11
|
+
# If table exists, delete it.
|
12
|
+
db.drop_table(:plane) if db.table_exists?(:plane)
|
13
|
+
|
14
|
+
# Create a table.
|
15
|
+
plane_tbl = db.create_table(:plane, :name, :String, :country, :String,
|
16
|
+
:speed, :Integer, :range, :Integer, :descr, :Memo)
|
17
|
+
|
18
|
+
# Create a long string field with embedded newlines for the memo contents.
|
19
|
+
memo_string = <<END_OF_STRING
|
20
|
+
The P-51 Mustang was the premier Allied fighter aircraft of World War II.
|
21
|
+
It's performance and long range allowed it to escort Allied strategic
|
22
|
+
bombers on raids deep inside Germany.
|
23
|
+
END_OF_STRING
|
24
|
+
|
25
|
+
# Create an instance of KBMemo for the memo field.
|
26
|
+
memo = KBMemo.new(db, 'P-51.txt', memo_string)
|
27
|
+
|
28
|
+
# Insert the new record, including the memo field.
|
29
|
+
plane_tbl.insert('P-51', 'USA', 403, 1201, memo)
|
30
|
+
|
31
|
+
# Insert another record.
|
32
|
+
memo_string = <<END_OF_STRING
|
33
|
+
The FW-190 was a World War II German fighter. It was used primarily as an
|
34
|
+
interceptor against Allied strategic bombers.
|
35
|
+
END_OF_STRING
|
36
|
+
|
37
|
+
memo = KBMemo.new(db, 'FW-190.txt', memo_string)
|
38
|
+
plane_tbl.insert('FW-190', 'Germany', 399, 499, memo)
|
39
|
+
|
40
|
+
# Select all records, print name, country, speed and range on first line.
|
41
|
+
# Then, print contents of memo field below.
|
42
|
+
plane_tbl.select.each { |r|
|
43
|
+
puts "Name: %s Country: %s Speed: %d Range: %d\n\n" % [r.name,
|
44
|
+
r.country, r.speed, r.range]
|
45
|
+
puts r.descr.contents
|
46
|
+
puts "\n" + "-" * 75 + "\n\n"
|
47
|
+
}
|
48
|
+
|
49
|
+
|
50
|
+
puts "\n\nNow we will change the memo for the P-51:\n\n"
|
51
|
+
|
52
|
+
# Grab the P-51's record.
|
53
|
+
rec = plane_tbl.select { |r| r.name == 'P-51' }.first
|
54
|
+
|
55
|
+
# Grab the contents of the memo field and add a line to it.
|
56
|
+
memo = rec.descr
|
57
|
+
memo.contents += "This last line should show up if the memo was changed."
|
58
|
+
|
59
|
+
# Now, update the record with the changed contents of the memo field.
|
60
|
+
plane_tbl.update {|r| r.name == 'P-51'}.set(:descr => memo)
|
61
|
+
|
62
|
+
# Do another select to show that the memo field indeed changed.
|
63
|
+
puts plane_tbl.select { |r| r.name == 'P-51' }.first.descr.contents
|
File without changes
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#Test of returning result set composed of instances of user class.
|
2
|
+
|
3
|
+
require 'kirbybase'
|
4
|
+
|
5
|
+
class Foobar
|
6
|
+
attr_accessor(:recno, :name, :country, :role, :speed, :range,
|
7
|
+
:began_service, :still_flying, :alpha, :beta)
|
8
|
+
|
9
|
+
def Foobar.kb_create(recno, name, country, role, speed, range,
|
10
|
+
began_service, still_flying)
|
11
|
+
name ||= 'No Name!'
|
12
|
+
speed ||= 0
|
13
|
+
began_service ||= Date.today
|
14
|
+
Foobar.new do |x|
|
15
|
+
x.recno = recno
|
16
|
+
x.name = name
|
17
|
+
x.country = country
|
18
|
+
x.role = role
|
19
|
+
x.speed = speed
|
20
|
+
x.range = range
|
21
|
+
x.began_service = began_service
|
22
|
+
x.still_flying = still_flying
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(&block)
|
27
|
+
instance_eval(&block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# To run local, single-user, uncomment next line.
|
32
|
+
db = KirbyBase.new
|
33
|
+
|
34
|
+
# If table exists, delete it.
|
35
|
+
db.drop_table(:plane) if db.table_exists?(:plane)
|
36
|
+
|
37
|
+
# Create a table. Notice how you set record_class equal to your class.
|
38
|
+
plane_tbl = db.create_table do |t|
|
39
|
+
t.name = :plane
|
40
|
+
t.field_defs = [:name, :String, :country, :String, :role, :String,
|
41
|
+
:speed, :Integer, :range, :Integer, :began_service, :Date,
|
42
|
+
:still_flying, :Boolean]
|
43
|
+
t.encrypt = false
|
44
|
+
t.record_class = Foobar
|
45
|
+
end
|
46
|
+
|
47
|
+
plane_tbl = db.get_table(:plane)
|
48
|
+
|
49
|
+
# Insert a record using an instance of Foobar.
|
50
|
+
foo = Foobar.new do |x|
|
51
|
+
x.name = 'Spitfire'
|
52
|
+
x.country = 'Great Britain'
|
53
|
+
x.role = 'Fighter'
|
54
|
+
x.speed = 333
|
55
|
+
x.range = 454
|
56
|
+
x.began_service = Date.new(1936, 1, 1)
|
57
|
+
x.still_flying = true
|
58
|
+
x.alpha = "This variable won't be stored in KirbyBase."
|
59
|
+
x.beta = 'Neither will this one.'
|
60
|
+
end
|
61
|
+
plane_tbl.insert(foo)
|
62
|
+
|
63
|
+
# Notice how select returns instances of Foobar, since it was defined as the
|
64
|
+
# record class.
|
65
|
+
recs = plane_tbl.select
|
66
|
+
puts "Example using #kb_create"
|
67
|
+
puts recs[0].class
|
68
|
+
|
69
|
+
# Now we are going to change a couple of fields of the Spitfire's Foobar
|
70
|
+
# object and update the Spitfire record in the Plane table with the updated
|
71
|
+
# Foobar object.
|
72
|
+
recs[0].speed = 344
|
73
|
+
recs[0].range = 555
|
74
|
+
plane_tbl.update(recs[0]) {|r| r.name == 'Spitfire'}
|
75
|
+
|
76
|
+
# Here's proof that the table was updated.
|
77
|
+
p plane_tbl.select {|r| r.name == 'Spitfire'}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# This script is an example of how to rename a column.
|
2
|
+
#
|
3
|
+
require 'kirbybase'
|
4
|
+
|
5
|
+
db = KirbyBase.new
|
6
|
+
|
7
|
+
# If table exists, delete it.
|
8
|
+
db.drop_table(:address_book) if db.table_exists?(:address_book)
|
9
|
+
|
10
|
+
address_book_tbl = db.create_table(:address_book,
|
11
|
+
:firstname, :String, :lastname, :String, :street_address, :String,
|
12
|
+
:city, :String, :phone, :String, :category, :String
|
13
|
+
)
|
14
|
+
|
15
|
+
# Insert some contact info records.
|
16
|
+
address_book_tbl.insert('Bruce', 'Wayne', '1234 Bat Cave', 'Gotham City',
|
17
|
+
'111-111-1111', 'Super Hero')
|
18
|
+
address_book_tbl.insert('Bugs', 'Bunny', '1234 Rabbit Hole', 'The Forest',
|
19
|
+
'222-222-2222', 'Cartoon Character')
|
20
|
+
address_book_tbl.insert('George', 'Bush', '1600 Pennsylvania Ave',
|
21
|
+
'Washington', '333-333-3333', 'President')
|
22
|
+
address_book_tbl.insert('Silver', 'Surfer', '1234 Galaxy Way',
|
23
|
+
'Any City', '444-444-4444', 'Super Hero')
|
24
|
+
|
25
|
+
address_book_tbl.select { |r| r.category == 'Super Hero' }.each { |r|
|
26
|
+
puts '%s %s %s' % [r.firstname, r.lastname, r.phone]
|
27
|
+
}
|
28
|
+
puts;puts
|
29
|
+
|
30
|
+
address_book_tbl.rename_column(:phone, :phone_no)
|
31
|
+
|
32
|
+
begin
|
33
|
+
address_book_tbl.select { |r| r.category == 'Super Hero' }.each { |r|
|
34
|
+
puts '%s %s %s' % [r.firstname, r.lastname, r.phone]
|
35
|
+
}
|
36
|
+
rescue StandardError => e
|
37
|
+
puts e
|
38
|
+
puts;puts
|
39
|
+
end
|
40
|
+
|
41
|
+
address_book_tbl.select { |r| r.category == 'Super Hero' }.each { |r|
|
42
|
+
puts '%s %s %s' % [r.firstname, r.lastname, r.phone_no]
|
43
|
+
}
|
44
|
+
puts;puts
|
45
|
+
|
46
|
+
p address_book_tbl.field_names
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# This script is an example of how to rename a table.
|
2
|
+
#
|
3
|
+
require 'kirbybase'
|
4
|
+
|
5
|
+
db = KirbyBase.new
|
6
|
+
|
7
|
+
# If tables exist, delete them.
|
8
|
+
db.drop_table(:address_book) if db.table_exists?(:address_book)
|
9
|
+
db.drop_table(:contact_list) if db.table_exists?(:contact_list)
|
10
|
+
|
11
|
+
address_book_tbl = db.create_table(:address_book,
|
12
|
+
:firstname, :String, :lastname, :String, :street_address, :String,
|
13
|
+
:city, :String, :phone, :String, :category, :String
|
14
|
+
)
|
15
|
+
|
16
|
+
# Insert some contact info records.
|
17
|
+
address_book_tbl.insert('Bruce', 'Wayne', '1234 Bat Cave', 'Gotham City',
|
18
|
+
'111-111-1111', 'Super Hero')
|
19
|
+
address_book_tbl.insert('Bugs', 'Bunny', '1234 Rabbit Hole', 'The Forest',
|
20
|
+
'222-222-2222', 'Cartoon Character')
|
21
|
+
address_book_tbl.insert('George', 'Bush', '1600 Pennsylvania Ave',
|
22
|
+
'Washington', '333-333-3333', 'President')
|
23
|
+
address_book_tbl.insert('Silver', 'Surfer', '1234 Galaxy Way',
|
24
|
+
'Any City', '444-444-4444', 'Super Hero')
|
25
|
+
|
26
|
+
address_book_tbl.select { |r| r.category == 'Super Hero' }.each { |r|
|
27
|
+
puts '%s %s %s' % [r.firstname, r.lastname, r.phone]
|
28
|
+
}
|
29
|
+
puts;puts
|
30
|
+
|
31
|
+
contact_list_tbl = db.rename_table(:address_book, :contact_list)
|
32
|
+
|
33
|
+
contact_list_tbl.select { |r| r.category == 'Super Hero' }.each { |r|
|
34
|
+
puts '%s %s %s' % [r.firstname, r.lastname, r.phone]
|
35
|
+
}
|
36
|
+
puts;puts
|
37
|
+
|
38
|
+
p db.tables
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#Test of YAML field type.
|
2
|
+
|
3
|
+
require 'kirbybase'
|
4
|
+
|
5
|
+
# To run local, single-user, uncomment next line.
|
6
|
+
db = KirbyBase.new
|
7
|
+
|
8
|
+
# To run as a client in a multi-user environment, uncomment next line.
|
9
|
+
# Also, make sure kbserver.rb is running.
|
10
|
+
#db = KirbyBase.new do |d|
|
11
|
+
# d.connect_type = :client
|
12
|
+
# d.host = 'localhost'
|
13
|
+
# d.port = 44444
|
14
|
+
#end
|
15
|
+
|
16
|
+
# If table exists, delete it.
|
17
|
+
db.drop_table(:pet) if db.table_exists?(:pet)
|
18
|
+
|
19
|
+
# Create a table.
|
20
|
+
pet_tbl = db.create_table(:pet, :name, :String, :pet_type, :String,
|
21
|
+
:born, :Date, :characteristics, :YAML)
|
22
|
+
|
23
|
+
pet_tbl.insert('Kirby', 'dog', Date.new(2002, 06, 01),
|
24
|
+
['cute', 'stinky', 4, 55.6])
|
25
|
+
pet_tbl.insert('Mojo', 'cat', Date.new(2000, 04, 01),
|
26
|
+
['cute', 'soft', '6', 12.25])
|
27
|
+
pet_tbl.insert('Goldy', 'fish', Date.new(2004, 10, 10),
|
28
|
+
nil)
|
29
|
+
|
30
|
+
pet_tbl.select.each { |r|
|
31
|
+
puts '%s %s' % [r.name, r.characteristics[1]]
|
32
|
+
}
|
33
|
+
|
34
|
+
puts
|
35
|
+
|
36
|
+
pet_tbl.select { |r| r.characteristics.include?('stinky') }.each { |r|
|
37
|
+
puts '%s smells like a dog!' % r.name
|
38
|
+
}
|
39
|
+
|
40
|
+
pet_tbl.update { |r| r.name == 'Goldy' }.set(
|
41
|
+
:characteristics => ['small', 'slimy', 2, 0.02])
|
42
|
+
|
43
|
+
puts
|
44
|
+
pet_tbl.select.each { |r|
|
45
|
+
puts '%s %s' % [r.name, r.characteristics.join(' ')]
|
46
|
+
}
|
47
|
+
|
data/images/blank.png
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/images/caution.png
ADDED
Binary file
|
Binary file
|
data/images/example.png
ADDED
Binary file
|
data/images/home.png
ADDED
Binary file
|
Binary file
|
data/images/kirby1.jpg
ADDED
Binary file
|
data/images/next.png
ADDED
Binary file
|
data/images/note.png
ADDED
Binary file
|
data/images/prev.png
ADDED
Binary file
|
Binary file
|
data/images/smallnew.png
ADDED
Binary file
|
data/images/tip.png
ADDED
Binary file
|
Binary file
|
Binary file
|
data/images/toc-plus.png
ADDED
Binary file
|
data/images/up.png
ADDED
Binary file
|
data/images/warning.png
ADDED
Binary file
|
@@ -0,0 +1,2243 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
4
|
+
<head>
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
6
|
+
<meta name="generator" content="AsciiDoc 7.0.2" />
|
7
|
+
<style type="text/css">
|
8
|
+
/* Debug borders */
|
9
|
+
p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
|
10
|
+
/*
|
11
|
+
border: 1px solid red;
|
12
|
+
*/
|
13
|
+
}
|
14
|
+
|
15
|
+
body {
|
16
|
+
margin: 1em 5% 1em 5%;
|
17
|
+
}
|
18
|
+
|
19
|
+
a { color: blue; }
|
20
|
+
a:visited { color: fuchsia; }
|
21
|
+
|
22
|
+
em {
|
23
|
+
font-style: italic;
|
24
|
+
}
|
25
|
+
|
26
|
+
strong {
|
27
|
+
font-weight: bold;
|
28
|
+
}
|
29
|
+
|
30
|
+
tt {
|
31
|
+
color: navy;
|
32
|
+
}
|
33
|
+
|
34
|
+
h1, h2, h3, h4, h5, h6 {
|
35
|
+
color: #527bbd;
|
36
|
+
font-family: sans-serif;
|
37
|
+
margin-top: 1.2em;
|
38
|
+
margin-bottom: 0.5em;
|
39
|
+
line-height: 1.3;
|
40
|
+
}
|
41
|
+
|
42
|
+
h1 {
|
43
|
+
border-bottom: 2px solid silver;
|
44
|
+
}
|
45
|
+
h2 {
|
46
|
+
border-bottom: 2px solid silver;
|
47
|
+
padding-top: 0.5em;
|
48
|
+
}
|
49
|
+
|
50
|
+
div.sectionbody {
|
51
|
+
font-family: serif;
|
52
|
+
margin-left: 0;
|
53
|
+
}
|
54
|
+
|
55
|
+
hr {
|
56
|
+
border: 1px solid silver;
|
57
|
+
}
|
58
|
+
|
59
|
+
p {
|
60
|
+
margin-top: 0.5em;
|
61
|
+
margin-bottom: 0.5em;
|
62
|
+
}
|
63
|
+
|
64
|
+
pre {
|
65
|
+
padding: 0;
|
66
|
+
margin: 0;
|
67
|
+
}
|
68
|
+
|
69
|
+
span#author {
|
70
|
+
color: #527bbd;
|
71
|
+
font-family: sans-serif;
|
72
|
+
font-weight: bold;
|
73
|
+
font-size: 1.2em;
|
74
|
+
}
|
75
|
+
span#email {
|
76
|
+
}
|
77
|
+
span#revision {
|
78
|
+
font-family: sans-serif;
|
79
|
+
}
|
80
|
+
|
81
|
+
div#footer {
|
82
|
+
font-family: sans-serif;
|
83
|
+
font-size: small;
|
84
|
+
border-top: 2px solid silver;
|
85
|
+
padding-top: 0.5em;
|
86
|
+
margin-top: 4.0em;
|
87
|
+
}
|
88
|
+
div#footer-text {
|
89
|
+
float: left;
|
90
|
+
padding-bottom: 0.5em;
|
91
|
+
}
|
92
|
+
div#footer-badges {
|
93
|
+
float: right;
|
94
|
+
padding-bottom: 0.5em;
|
95
|
+
}
|
96
|
+
|
97
|
+
div#preamble,
|
98
|
+
div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
|
99
|
+
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
|
100
|
+
div.admonitionblock {
|
101
|
+
margin-right: 10%;
|
102
|
+
margin-top: 1.5em;
|
103
|
+
margin-bottom: 1.5em;
|
104
|
+
}
|
105
|
+
div.admonitionblock {
|
106
|
+
margin-top: 2.5em;
|
107
|
+
margin-bottom: 2.5em;
|
108
|
+
}
|
109
|
+
|
110
|
+
div.content { /* Block element content. */
|
111
|
+
padding: 0;
|
112
|
+
}
|
113
|
+
|
114
|
+
/* Block element titles. */
|
115
|
+
div.title, caption.title {
|
116
|
+
font-family: sans-serif;
|
117
|
+
font-weight: bold;
|
118
|
+
text-align: left;
|
119
|
+
margin-top: 1.0em;
|
120
|
+
margin-bottom: 0.5em;
|
121
|
+
}
|
122
|
+
div.title + * {
|
123
|
+
margin-top: 0;
|
124
|
+
}
|
125
|
+
|
126
|
+
td div.title:first-child {
|
127
|
+
margin-top: 0.0em;
|
128
|
+
}
|
129
|
+
div.content div.title:first-child {
|
130
|
+
margin-top: 0.0em;
|
131
|
+
}
|
132
|
+
div.content + div.title {
|
133
|
+
margin-top: 0.0em;
|
134
|
+
}
|
135
|
+
|
136
|
+
div.sidebarblock > div.content {
|
137
|
+
background: #ffffee;
|
138
|
+
border: 1px solid silver;
|
139
|
+
padding: 0.5em;
|
140
|
+
}
|
141
|
+
|
142
|
+
div.listingblock > div.content {
|
143
|
+
border: 1px solid silver;
|
144
|
+
background: #f4f4f4;
|
145
|
+
padding: 0.5em;
|
146
|
+
}
|
147
|
+
|
148
|
+
div.quoteblock > div.content {
|
149
|
+
padding-left: 2.0em;
|
150
|
+
}
|
151
|
+
div.quoteblock .attribution {
|
152
|
+
text-align: right;
|
153
|
+
}
|
154
|
+
|
155
|
+
div.admonitionblock .icon {
|
156
|
+
vertical-align: top;
|
157
|
+
font-size: 1.1em;
|
158
|
+
font-weight: bold;
|
159
|
+
text-decoration: underline;
|
160
|
+
color: #527bbd;
|
161
|
+
padding-right: 0.5em;
|
162
|
+
}
|
163
|
+
div.admonitionblock td.content {
|
164
|
+
padding-left: 0.5em;
|
165
|
+
border-left: 2px solid silver;
|
166
|
+
}
|
167
|
+
|
168
|
+
div.exampleblock > div.content {
|
169
|
+
border-left: 2px solid silver;
|
170
|
+
padding: 0.5em;
|
171
|
+
}
|
172
|
+
|
173
|
+
div.verseblock div.content {
|
174
|
+
white-space: pre;
|
175
|
+
}
|
176
|
+
|
177
|
+
div.imageblock div.content { padding-left: 0; }
|
178
|
+
div.imageblock img { border: 1px solid silver; }
|
179
|
+
span.image img { border-style: none; }
|
180
|
+
|
181
|
+
dl {
|
182
|
+
margin-top: 0.8em;
|
183
|
+
margin-bottom: 0.8em;
|
184
|
+
}
|
185
|
+
dt {
|
186
|
+
margin-top: 0.5em;
|
187
|
+
margin-bottom: 0;
|
188
|
+
font-style: italic;
|
189
|
+
}
|
190
|
+
dd > *:first-child {
|
191
|
+
margin-top: 0;
|
192
|
+
}
|
193
|
+
|
194
|
+
ul, ol {
|
195
|
+
list-style-position: outside;
|
196
|
+
}
|
197
|
+
ol.olist2 {
|
198
|
+
list-style-type: lower-alpha;
|
199
|
+
}
|
200
|
+
|
201
|
+
div.tableblock > table {
|
202
|
+
border-color: #527bbd;
|
203
|
+
border-width: 3px;
|
204
|
+
}
|
205
|
+
thead {
|
206
|
+
font-family: sans-serif;
|
207
|
+
font-weight: bold;
|
208
|
+
}
|
209
|
+
tfoot {
|
210
|
+
font-weight: bold;
|
211
|
+
}
|
212
|
+
|
213
|
+
div.hlist {
|
214
|
+
margin-top: 0.8em;
|
215
|
+
margin-bottom: 0.8em;
|
216
|
+
}
|
217
|
+
td.hlist1 {
|
218
|
+
vertical-align: top;
|
219
|
+
font-style: italic;
|
220
|
+
padding-right: 0.8em;
|
221
|
+
}
|
222
|
+
td.hlist2 {
|
223
|
+
vertical-align: top;
|
224
|
+
}
|
225
|
+
|
226
|
+
@media print {
|
227
|
+
div#footer-badges { display: none; }
|
228
|
+
}
|
229
|
+
/* Workarounds for IE6's broken and incomplete CSS2. */
|
230
|
+
|
231
|
+
div.sidebar-content {
|
232
|
+
background: #ffffee;
|
233
|
+
border: 1px solid silver;
|
234
|
+
padding: 0.5em;
|
235
|
+
}
|
236
|
+
div.sidebar-title, div.image-title {
|
237
|
+
font-family: sans-serif;
|
238
|
+
font-weight: bold;
|
239
|
+
margin-top: 0.0em;
|
240
|
+
margin-bottom: 0.5em;
|
241
|
+
}
|
242
|
+
|
243
|
+
div.listingblock div.content {
|
244
|
+
border: 1px solid silver;
|
245
|
+
background: #f4f4f4;
|
246
|
+
padding: 0.5em;
|
247
|
+
}
|
248
|
+
|
249
|
+
div.quoteblock-content {
|
250
|
+
padding-left: 2.0em;
|
251
|
+
}
|
252
|
+
|
253
|
+
div.exampleblock-content {
|
254
|
+
border-left: 2px solid silver;
|
255
|
+
padding-left: 0.5em;
|
256
|
+
}
|
257
|
+
</style>
|
258
|
+
<title>KirbyBase Manual (Ruby Version)</title>
|
259
|
+
</head>
|
260
|
+
<body>
|
261
|
+
<div id="header">
|
262
|
+
<h1>KirbyBase Manual (Ruby Version)</h1>
|
263
|
+
<span id="author">Jamey Cribbs</span><br />
|
264
|
+
<span id="email"><tt><<a href="mailto:jcribbs@twmi.rr.com">jcribbs@twmi.rr.com</a>></tt></span><br />
|
265
|
+
<span id="revision">version 2.5,</span>
|
266
|
+
1 December 2005
|
267
|
+
</div>
|
268
|
+
<div id="preamble">
|
269
|
+
<div class="sectionbody">
|
270
|
+
<div class="imageblock">
|
271
|
+
<div class="content">
|
272
|
+
<img src="images/kirby1.jpg" alt="images/kirby1.jpg"/>
|
273
|
+
</div>
|
274
|
+
<div class="image-title">Figure: Kirby pictured here in attack mode.</div>
|
275
|
+
</div>
|
276
|
+
</div>
|
277
|
+
</div>
|
278
|
+
<h2>Table of Contents</h2>
|
279
|
+
<div class="sectionbody">
|
280
|
+
<div class="sidebarblock">
|
281
|
+
<div class="sidebar-content">
|
282
|
+
<ol>
|
283
|
+
<li>
|
284
|
+
<p>
|
285
|
+
<a href="#introduction">Introduction</a>
|
286
|
+
</p>
|
287
|
+
</li>
|
288
|
+
<li>
|
289
|
+
<p>
|
290
|
+
<a href="#connecting-to-a-database">Connecting to a database</a>
|
291
|
+
</p>
|
292
|
+
</li>
|
293
|
+
<li>
|
294
|
+
<p>
|
295
|
+
<a href="#creating-a-new-table">Creating a new table</a>
|
296
|
+
</p>
|
297
|
+
<ol class="olist2">
|
298
|
+
<li>
|
299
|
+
<p>
|
300
|
+
<a href="#field-types">Database field types</a>
|
301
|
+
</p>
|
302
|
+
</li>
|
303
|
+
<li>
|
304
|
+
<p>
|
305
|
+
<a href="#recno">The recno field</a>
|
306
|
+
</p>
|
307
|
+
</li>
|
308
|
+
<li>
|
309
|
+
<p>
|
310
|
+
<a href="#defaults">Specifying default values</a>
|
311
|
+
</p>
|
312
|
+
</li>
|
313
|
+
<li>
|
314
|
+
<p>
|
315
|
+
<a href="#requireds">Specifying required fields</a>
|
316
|
+
</p>
|
317
|
+
</li>
|
318
|
+
<li>
|
319
|
+
<p>
|
320
|
+
<a href="#creating-indexes">Indexes</a>
|
321
|
+
</p>
|
322
|
+
</li>
|
323
|
+
<li>
|
324
|
+
<p>
|
325
|
+
<a href="#a-note-about-indexes">A note about indexes</a>
|
326
|
+
</p>
|
327
|
+
</li>
|
328
|
+
<li>
|
329
|
+
<p>
|
330
|
+
<a href="#creating-lookup-fields">Lookup Fields</a>
|
331
|
+
</p>
|
332
|
+
</li>
|
333
|
+
<li>
|
334
|
+
<p>
|
335
|
+
<a href="#creating-link-many-fields">Link_many Fields</a>
|
336
|
+
</p>
|
337
|
+
</li>
|
338
|
+
<li>
|
339
|
+
<p>
|
340
|
+
<a href="#creating-calculated-fields">Calculated Fields</a>
|
341
|
+
</p>
|
342
|
+
</li>
|
343
|
+
</ol>
|
344
|
+
</li>
|
345
|
+
<li>
|
346
|
+
<p>
|
347
|
+
<a href="#obtaining-a-reference-to-an-existing-table">Obtaining a reference to an existing table</a>
|
348
|
+
</p>
|
349
|
+
</li>
|
350
|
+
<li>
|
351
|
+
<p>
|
352
|
+
<a href="#insert-method">The insert method</a>
|
353
|
+
</p>
|
354
|
+
</li>
|
355
|
+
<li>
|
356
|
+
<p>
|
357
|
+
<a href="#how-to-select-records">How to select records</a>
|
358
|
+
</p>
|
359
|
+
<ol class="olist2">
|
360
|
+
<li>
|
361
|
+
<p>
|
362
|
+
<a href="#select-method">The select method</a>
|
363
|
+
</p>
|
364
|
+
</li>
|
365
|
+
<li>
|
366
|
+
<p>
|
367
|
+
<a href="#selecting-by-index">Selecting by index</a>
|
368
|
+
</p>
|
369
|
+
</li>
|
370
|
+
<li>
|
371
|
+
<p>
|
372
|
+
<a href="#selecting-by-recno-index">Selecting by :recno index</a>
|
373
|
+
</p>
|
374
|
+
</li>
|
375
|
+
<li>
|
376
|
+
<p>
|
377
|
+
<a href="#selects-involving-lookups-or-link-manys">Selects involving Lookups or Link_manys</a>
|
378
|
+
</p>
|
379
|
+
</li>
|
380
|
+
</ol>
|
381
|
+
</li>
|
382
|
+
<li>
|
383
|
+
<p>
|
384
|
+
<a href="#kbresultset">KBResultSet</a>
|
385
|
+
</p>
|
386
|
+
<ol class="olist2">
|
387
|
+
<li>
|
388
|
+
<p>
|
389
|
+
<a href="#sorting-a-result-set">Sorting a result set</a>
|
390
|
+
</p>
|
391
|
+
</li>
|
392
|
+
<li>
|
393
|
+
<p>
|
394
|
+
<a href="#to-report">Producing a report from a result set</a>
|
395
|
+
</p>
|
396
|
+
</li>
|
397
|
+
<li>
|
398
|
+
<p>
|
399
|
+
<a href="#crosstabs">CrossTabs or Pivot Tables or Column Arrays (i.e. I don't know what to call them!)</a>
|
400
|
+
</p>
|
401
|
+
</li>
|
402
|
+
</ol>
|
403
|
+
</li>
|
404
|
+
<li>
|
405
|
+
<p>
|
406
|
+
<a href="#how-to-update-records">How to update records</a>
|
407
|
+
</p>
|
408
|
+
<ol class="olist2">
|
409
|
+
<li>
|
410
|
+
<p>
|
411
|
+
<a href="#update-method">The update method</a>
|
412
|
+
</p>
|
413
|
+
</li>
|
414
|
+
<li>
|
415
|
+
<p>
|
416
|
+
<a href="#set-method">The set method</a>
|
417
|
+
</p>
|
418
|
+
</li>
|
419
|
+
<li>
|
420
|
+
<p>
|
421
|
+
<a href="#update-all-method">The update_all method</a>
|
422
|
+
</p>
|
423
|
+
</li>
|
424
|
+
</ol>
|
425
|
+
</li>
|
426
|
+
<li>
|
427
|
+
<p>
|
428
|
+
<a href="#how-to-delete-records">How to delete records</a>
|
429
|
+
</p>
|
430
|
+
<ol class="olist2">
|
431
|
+
<li>
|
432
|
+
<p>
|
433
|
+
<a href="#delete-method">The delete method</a>
|
434
|
+
</p>
|
435
|
+
</li>
|
436
|
+
<li>
|
437
|
+
<p>
|
438
|
+
<a href="#clear-method">The clear method</a>
|
439
|
+
</p>
|
440
|
+
</li>
|
441
|
+
</ol>
|
442
|
+
</li>
|
443
|
+
<li>
|
444
|
+
<p>
|
445
|
+
<a href="#pack-method">The pack method</a>
|
446
|
+
</p>
|
447
|
+
</li>
|
448
|
+
<li>
|
449
|
+
<p>
|
450
|
+
<a href="#memo-and-blob-fields">Memo and Blob Fields</a>
|
451
|
+
</p>
|
452
|
+
</li>
|
453
|
+
<li>
|
454
|
+
<p>
|
455
|
+
<a href="#misc-kirbybase-methods">Miscellaneous KirbyBase methods</a>
|
456
|
+
</p>
|
457
|
+
<ol class="olist2">
|
458
|
+
<li>
|
459
|
+
<p>
|
460
|
+
<a href="#drop-table">KirbyBase#drop_table</a>
|
461
|
+
</p>
|
462
|
+
</li>
|
463
|
+
<li>
|
464
|
+
<p>
|
465
|
+
<a href="#tables">KirbyBase#tables</a>
|
466
|
+
</p>
|
467
|
+
</li>
|
468
|
+
<li>
|
469
|
+
<p>
|
470
|
+
<a href="#table-exists">KirbyBase#table_exists?</a>
|
471
|
+
</p>
|
472
|
+
</li>
|
473
|
+
<li>
|
474
|
+
<p>
|
475
|
+
<a href="#rename-table">KirbyBase#rename_table</a>
|
476
|
+
</p>
|
477
|
+
</li>
|
478
|
+
</ol>
|
479
|
+
</li>
|
480
|
+
<li>
|
481
|
+
<p>
|
482
|
+
<a href="#misc-kbtable-methods">Miscellaneous KBTable methods</a>
|
483
|
+
</p>
|
484
|
+
<ol class="olist2">
|
485
|
+
<li>
|
486
|
+
<p>
|
487
|
+
<a href="#update-by-recno">KBTable#[]=</a>
|
488
|
+
</p>
|
489
|
+
</li>
|
490
|
+
<li>
|
491
|
+
<p>
|
492
|
+
<a href="#get-by-recno">KBTable#[]</a>
|
493
|
+
</p>
|
494
|
+
</li>
|
495
|
+
<li>
|
496
|
+
<p>
|
497
|
+
<a href="#field-names">KBTable#field_names</a>
|
498
|
+
</p>
|
499
|
+
</li>
|
500
|
+
<li>
|
501
|
+
<p>
|
502
|
+
<a href="#field-types">KBTable#field_types</a>
|
503
|
+
</p>
|
504
|
+
</li>
|
505
|
+
<li>
|
506
|
+
<p>
|
507
|
+
<a href="#total-recs">KBTable#total_recs</a>
|
508
|
+
</p>
|
509
|
+
</li>
|
510
|
+
<li>
|
511
|
+
<p>
|
512
|
+
<a href="#import-csv">KBTable#import_csv</a>
|
513
|
+
</p>
|
514
|
+
</li>
|
515
|
+
<li>
|
516
|
+
<p>
|
517
|
+
<a href="#add-column">KBTable#add_column</a>
|
518
|
+
</p>
|
519
|
+
</li>
|
520
|
+
<li>
|
521
|
+
<p>
|
522
|
+
<a href="#drop-column">KBTable#drop_column</a>
|
523
|
+
</p>
|
524
|
+
</li>
|
525
|
+
<li>
|
526
|
+
<p>
|
527
|
+
<a href="#rename-column">KBTable#rename_column</a>
|
528
|
+
</p>
|
529
|
+
</li>
|
530
|
+
<li>
|
531
|
+
<p>
|
532
|
+
<a href="#change-column-type">KBTable#change_column_type</a>
|
533
|
+
</p>
|
534
|
+
</li>
|
535
|
+
<li>
|
536
|
+
<p>
|
537
|
+
<a href="#change-column-default-value">KBTable#change_column_defaul_value</a>
|
538
|
+
</p>
|
539
|
+
</li>
|
540
|
+
<li>
|
541
|
+
<p>
|
542
|
+
<a href="#change-column-required">KBTable#change_column_required</a>
|
543
|
+
</p>
|
544
|
+
</li>
|
545
|
+
<li>
|
546
|
+
<p>
|
547
|
+
<a href="#add-index">KBTable#add_index</a>
|
548
|
+
</p>
|
549
|
+
</li>
|
550
|
+
<li>
|
551
|
+
<p>
|
552
|
+
<a href="#drop-index">KBTable#drop_index</a>
|
553
|
+
</p>
|
554
|
+
</li>
|
555
|
+
</ol>
|
556
|
+
</li>
|
557
|
+
<li>
|
558
|
+
<p>
|
559
|
+
<a href="#special-characters-in-data">Special characters in data</a>
|
560
|
+
</p>
|
561
|
+
</li>
|
562
|
+
<li>
|
563
|
+
<p>
|
564
|
+
<a href="#table-structure">Table Structure</a>
|
565
|
+
</p>
|
566
|
+
</li>
|
567
|
+
<li>
|
568
|
+
<p>
|
569
|
+
<a href="#server-notes">Server Notes</a>
|
570
|
+
</p>
|
571
|
+
</li>
|
572
|
+
<li>
|
573
|
+
<p>
|
574
|
+
<a href="#tips-on-improving-performance">Tips on improving performance</a>
|
575
|
+
</p>
|
576
|
+
</li>
|
577
|
+
<li>
|
578
|
+
<p>
|
579
|
+
<a href="#client-server-diagram">Client/Server memory space diagram</a>
|
580
|
+
</p>
|
581
|
+
</li>
|
582
|
+
<li>
|
583
|
+
<p>
|
584
|
+
<a href="#single-user-diagram">Single-user memory space diagram</a>
|
585
|
+
</p>
|
586
|
+
</li>
|
587
|
+
<li>
|
588
|
+
<p>
|
589
|
+
<a href="#license">License</a>
|
590
|
+
</p>
|
591
|
+
</li>
|
592
|
+
<li>
|
593
|
+
<p>
|
594
|
+
<a href="#credits">Credits</a>
|
595
|
+
</p>
|
596
|
+
</li>
|
597
|
+
</ol>
|
598
|
+
</div></div>
|
599
|
+
</div>
|
600
|
+
<h2><a id="introduction"></a>Introduction</h2>
|
601
|
+
<div class="sectionbody">
|
602
|
+
<p>KirbyBase is a simple, pure-Ruby, flat-file database management system.
|
603
|
+
Some of its features include:</p>
|
604
|
+
<ul>
|
605
|
+
<li>
|
606
|
+
<p>
|
607
|
+
Since KirbyBase is written in Ruby, it runs anywhere that Ruby runs. It
|
608
|
+
is easy to distribute, since the entire dbms is in one (approx. 100k) code
|
609
|
+
file.
|
610
|
+
</p>
|
611
|
+
</li>
|
612
|
+
<li>
|
613
|
+
<p>
|
614
|
+
All data is kept in plain-text, delimited files that can be edited by
|
615
|
+
hand. This gives you the ability to make changes by just opening the file
|
616
|
+
up in a text editor, or you can use another programming language to read the
|
617
|
+
file in and do things with it.
|
618
|
+
</p>
|
619
|
+
</li>
|
620
|
+
<li>
|
621
|
+
<p>
|
622
|
+
It can be used as an embedded database or in a client/server, multi-user
|
623
|
+
mode. To switch from one mode to the other, you only have to change one
|
624
|
+
line in your program. Included in the distribution is a sample database
|
625
|
+
server script using DRb.
|
626
|
+
</p>
|
627
|
+
</li>
|
628
|
+
<li>
|
629
|
+
<p>
|
630
|
+
Tables are kept on disk during use and accessed from disk when selecting,
|
631
|
+
updating, inserting, and deleting records. Changes to a table are written
|
632
|
+
immediately to disk. KirbyBase is not an "in-memory" database. Once you
|
633
|
+
update the database in your program, you can be assured that the change has
|
634
|
+
been saved to disk. The chance of lost data due to power interruptions, or
|
635
|
+
disk crashes is much reduced. Also, since the entire database does not have
|
636
|
+
to reside in memory at once, KirbyBase should run adequately on a
|
637
|
+
memory-constrained system.
|
638
|
+
</p>
|
639
|
+
</li>
|
640
|
+
<li>
|
641
|
+
<p>
|
642
|
+
You can specify the type of data that each field will hold. The available
|
643
|
+
data types are: String, Integer, Float, Boolean, Time, Date, DateTime, Memo,
|
644
|
+
Blob, and YAML.
|
645
|
+
</p>
|
646
|
+
</li>
|
647
|
+
<li>
|
648
|
+
<p>
|
649
|
+
The query syntax is very "rubyish". Instead of having to use another
|
650
|
+
language like SQL, you can express your query using Ruby code blocks.
|
651
|
+
</p>
|
652
|
+
</li>
|
653
|
+
<li>
|
654
|
+
<p>
|
655
|
+
All inserted records have an auto-incrementing primary key that is
|
656
|
+
guaranteed to uniquely identify the record throughout it's lifetime.
|
657
|
+
</p>
|
658
|
+
</li>
|
659
|
+
<li>
|
660
|
+
<p>
|
661
|
+
You can specify that the result set be sorted on multiple fields, each
|
662
|
+
one either ascending or descending.
|
663
|
+
</p>
|
664
|
+
</li>
|
665
|
+
<li>
|
666
|
+
<p>
|
667
|
+
You can specify that certain fields be indexed. Using an index in a select
|
668
|
+
query can greatly improve performance on large tables (I've seen 10x speed
|
669
|
+
improvements). Index maintenance is completely handled by KirbyBase.
|
670
|
+
</p>
|
671
|
+
</li>
|
672
|
+
<li>
|
673
|
+
<p>
|
674
|
+
You can specify that a field has a "lookup table". Whenever that field is
|
675
|
+
accessed, the corresponding record from the lookup table is automatically
|
676
|
+
available.
|
677
|
+
</p>
|
678
|
+
</li>
|
679
|
+
<li>
|
680
|
+
<p>
|
681
|
+
You can specify one-to-many links between tables, somewhat analogous to a
|
682
|
+
"join" in SQL.
|
683
|
+
</p>
|
684
|
+
</li>
|
685
|
+
<li>
|
686
|
+
<p>
|
687
|
+
You can create calculated fields that are computed at runtime.
|
688
|
+
</p>
|
689
|
+
</li>
|
690
|
+
<li>
|
691
|
+
<p>
|
692
|
+
It is fairly fast, comparing favorably to SQLite.
|
693
|
+
</p>
|
694
|
+
</li>
|
695
|
+
</ul>
|
696
|
+
<p>In meeting your DBMS needs, KirbyBase fits in somewhere between plain
|
697
|
+
text files and small SQL database management systems like SQLite and
|
698
|
+
MySQL.</p>
|
699
|
+
<div class="sidebarblock">
|
700
|
+
<div class="sidebar-content">
|
701
|
+
<div class="sidebar-title">Drop me a line!</div>
|
702
|
+
<p>If you find a use for KirbyBase, please send an email
|
703
|
+
telling me how you use it. I find that hearing how people have
|
704
|
+
put KirbyBase to use is one of the biggest rewards I get for working on it.</p>
|
705
|
+
</div></div>
|
706
|
+
</div>
|
707
|
+
<h2><a id="connecting-to-a-database"></a>Connecting to a database</h2>
|
708
|
+
<div class="sectionbody">
|
709
|
+
<p>To use Kirbybase, you first need to require the module:</p>
|
710
|
+
<div class="listingblock">
|
711
|
+
<div class="content">
|
712
|
+
<pre><tt>require 'kirbybase'</tt></pre>
|
713
|
+
</div></div>
|
714
|
+
<p>Then create an instance:</p>
|
715
|
+
<div class="listingblock">
|
716
|
+
<div class="content">
|
717
|
+
<pre><tt>db = KirbyBase.new</tt></pre>
|
718
|
+
</div></div>
|
719
|
+
<p>By default, the instance is a local connection using the same memory space
|
720
|
+
as your application. To specify a client/server connection, it would look
|
721
|
+
like this:</p>
|
722
|
+
<div class="listingblock">
|
723
|
+
<div class="content">
|
724
|
+
<pre><tt>db = KirbyBase.new(:client, 'localhost', 44444)</tt></pre>
|
725
|
+
</div></div>
|
726
|
+
<p>Of course, you would substitute your server's ip address and port number.</p>
|
727
|
+
<p>To specify a different location other than the current directory for the
|
728
|
+
database files, you need to pass the location as an argument, like so:</p>
|
729
|
+
<div class="listingblock">
|
730
|
+
<div class="content">
|
731
|
+
<pre><tt>db = KirbyBase.new(:local, nil, nil, './data')</tt></pre>
|
732
|
+
</div></div>
|
733
|
+
<p>KirbyBase treats every file in the specified directory that has the proper
|
734
|
+
extension as a database table. The default extension is ".tbl". To specify
|
735
|
+
a different extension, pass this as an argument, like so:</p>
|
736
|
+
<div class="listingblock">
|
737
|
+
<div class="content">
|
738
|
+
<pre><tt>db = KirbyBase.new(:local, nil, nil, './', '.dat')</tt></pre>
|
739
|
+
</div></div>
|
740
|
+
<p>You can also specify the arguments via a code block. So, if you don't want
|
741
|
+
to have to specify a bunch of arguments just to get to the one you want,
|
742
|
+
put it in a code block attached to the method call. You could re-code the
|
743
|
+
previous example like so:</p>
|
744
|
+
<div class="listingblock">
|
745
|
+
<div class="content">
|
746
|
+
<pre><tt>db = KirbyBase.new { |d| d.ext = '.dat' }</tt></pre>
|
747
|
+
</div></div>
|
748
|
+
</div>
|
749
|
+
<h2><a id="creating-a-new-table"></a>Creating a new table</h2>
|
750
|
+
<div class="sectionbody">
|
751
|
+
<p>To create a new table, you specify the table name, and a name and type for
|
752
|
+
each column. For example, to create a table containing information on World
|
753
|
+
War II planes:</p>
|
754
|
+
<div class="listingblock">
|
755
|
+
<div class="content">
|
756
|
+
<pre><tt>plane_tbl = db.create_table(:plane, :name, :String, :country, :String,
|
757
|
+
:role, :String, :speed, :Integer, :range, :Integer, :began_service, :Date,
|
758
|
+
:still_flying, :Boolean)</tt></pre>
|
759
|
+
</div></div>
|
760
|
+
<div class="sidebarblock">
|
761
|
+
<a id="field-types"></a>
|
762
|
+
<div class="sidebar-content">
|
763
|
+
<div class="sidebar-title">KirbyBase Field Types</div>
|
764
|
+
<div class="admonitionblock">
|
765
|
+
<table><tr>
|
766
|
+
<td class="icon">
|
767
|
+
<img src="./images/tip.png" alt="Tip" />
|
768
|
+
</td>
|
769
|
+
<td class="content">:String, :Integer, :Float, :Boolean(true/false), :Time, :Date,
|
770
|
+
:DateTime, :Memo, :Blob, and :YAML.</td>
|
771
|
+
</tr></table>
|
772
|
+
</div>
|
773
|
+
</div></div>
|
774
|
+
<div class="sidebarblock">
|
775
|
+
<a id="recno"></a>
|
776
|
+
<div class="sidebar-content">
|
777
|
+
<div class="sidebar-title">The recno field</div>
|
778
|
+
<div class="admonitionblock">
|
779
|
+
<table><tr>
|
780
|
+
<td class="icon">
|
781
|
+
<img src="./images/important.png" alt="Important" />
|
782
|
+
</td>
|
783
|
+
<td class="content">KirbyBase will automatically create a primary key field, called
|
784
|
+
recno, with a type of :Integer, for each table. This field will be
|
785
|
+
auto-incremented each time a record is inserted. You can use this field in
|
786
|
+
select, update, and delete queries, but you can't modify this field.</td>
|
787
|
+
</tr></table>
|
788
|
+
</div>
|
789
|
+
</div></div>
|
790
|
+
<p>You can also specify whether the table should be encrypted. This will save
|
791
|
+
the table to disk encrypted (more like obfuscated) using a Vignere Cipher.
|
792
|
+
This is similar to rot13, but it uses a key to determine character
|
793
|
+
substitution. Still, this encryption will only stymie the most casual
|
794
|
+
of hackers. Do not rely on it to keep sensitive data safe! You specify
|
795
|
+
encryption by using a code block attached to #create_table:</p>
|
796
|
+
<div class="listingblock">
|
797
|
+
<div class="content">
|
798
|
+
<pre><tt>plane_tbl = db.create_table(:plane, :name, :String...) do |t|
|
799
|
+
t.encrypt = true
|
800
|
+
end</tt></pre>
|
801
|
+
</div></div>
|
802
|
+
<p>You can also specify that you want records of the table to be returned to
|
803
|
+
you as instances of a class. To do this, simply define a class before you
|
804
|
+
call #create_table. This class needs to have an accessor for each fieldname
|
805
|
+
in the table. Also, this class needs to have a class method, called
|
806
|
+
#kb_create, that returns a new class instance. Here is an example class:</p>
|
807
|
+
<div class="listingblock">
|
808
|
+
<div class="content">
|
809
|
+
<pre><tt>class Foobar
|
810
|
+
attr_accessor(:recno, :name, :country, :role, :speed, :range,
|
811
|
+
:began_service, :still_flying, :alpha, :beta)
|
812
|
+
|
813
|
+
def Foobar.kb_create(recno, name, country, role, speed, range,
|
814
|
+
began_service, still_flying)
|
815
|
+
name ||= 'No Name!'
|
816
|
+
speed ||= 0
|
817
|
+
began_service ||= Date.today
|
818
|
+
|
819
|
+
Foobar.new do |x|
|
820
|
+
x.recno = recno
|
821
|
+
x.name = name
|
822
|
+
x.country = country
|
823
|
+
x.role = role
|
824
|
+
x.speed = speed
|
825
|
+
x.range = range
|
826
|
+
x.began_service = began_service
|
827
|
+
x.still_flying = still_flying
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
def initialize(&block)
|
832
|
+
instance_eval(&block)
|
833
|
+
end
|
834
|
+
end</tt></pre>
|
835
|
+
</div></div>
|
836
|
+
<p>Pass this class to #create_table in an attached code block, like so:</p>
|
837
|
+
<div class="listingblock">
|
838
|
+
<div class="content">
|
839
|
+
<pre><tt>plane_tbl = db.create_table(:plane, :name, :String...) do |t|
|
840
|
+
t.record_class = Foobar
|
841
|
+
end</tt></pre>
|
842
|
+
</div></div>
|
843
|
+
<p>Now, when you call #select, the result set will be made up of instances of
|
844
|
+
Foobar, instead of the default, which is instances of Struct. This also
|
845
|
+
works the other way. You can now specify instances of Foobar as input to
|
846
|
+
the #insert, #update and #set methods. More on those methods below.</p>
|
847
|
+
<div class="sidebarblock">
|
848
|
+
<div class="sidebar-content">
|
849
|
+
<div class="admonitionblock">
|
850
|
+
<table><tr>
|
851
|
+
<td class="icon">
|
852
|
+
<img src="./images/important.png" alt="Important" />
|
853
|
+
</td>
|
854
|
+
<td class="content">The #create_table method will return a reference to a KBTable
|
855
|
+
instance. This is the only way, besides calling KirbyBase#get_table, that
|
856
|
+
you can obtain a handle to a KBTable instance. You can not call KBTable#new
|
857
|
+
directly.</td>
|
858
|
+
</tr></table>
|
859
|
+
</div>
|
860
|
+
</div></div>
|
861
|
+
<h3>Specifying Advanced Field Type Information</h3>
|
862
|
+
<p>There are four types of advanced field type information that you can
|
863
|
+
specify:
|
864
|
+
Defaults, Requireds, Indexes and Extras (i.e. Lookup, Link_many,
|
865
|
+
Calculated).</p>
|
866
|
+
<h4><a id="defaults"></a>Default Field Values</h4>
|
867
|
+
<p>Normally, when you insert a record, if you don't specify a value for a field
|
868
|
+
or specify nil as the value, KirbyBase stores this as an empty string
|
869
|
+
(i.e. "") in the table's physical file, and returns it as a nil value when
|
870
|
+
you do a #select.</p>
|
871
|
+
<p>However, you can tell KirbyBase that you want a column to have a default
|
872
|
+
value. This will be applied whenever either no value or a nil value is
|
873
|
+
specified for the field when <strong>inserting</strong> a record only. When updating an
|
874
|
+
existing record, defaults will not be applied.</p>
|
875
|
+
<p>For example, to specify that the category column has a default value, you
|
876
|
+
would code:</p>
|
877
|
+
<div class="listingblock">
|
878
|
+
<div class="content">
|
879
|
+
<pre><tt>db.create_table(:addressbook, :firstname, :String,
|
880
|
+
:lastname, :String, :phone_no, :String,
|
881
|
+
:category, {:DataType=>:String, :Default=>'Business'})</tt></pre>
|
882
|
+
</div></div>
|
883
|
+
<p>Notice that, since we are specifying advanced field type information, we
|
884
|
+
cannot just simply say :String for the second half of the field definition.
|
885
|
+
Instead, we have to pass a hash, with a :DataType item set to :String.
|
886
|
+
Next, because we are specifying a default value, we have to include a hash
|
887
|
+
item called :Default with it's value set to whatever we want the default to
|
888
|
+
be. The default value must be of a type that is valid for the column.</p>
|
889
|
+
<h4><a id="requireds"></a>Required Fields</h4>
|
890
|
+
<p>Normally, when you insert or update a record, if you don't specify a value
|
891
|
+
for a field or specify nil as the value, KirbyBase stores this as an empty
|
892
|
+
string (i.e. "") in the table's physical file, and returns it as a nil
|
893
|
+
value when you do a #select.</p>
|
894
|
+
<p>However, you can tell KirbyBase that you want a column to be required
|
895
|
+
(i.e. you must supply a value and it can't be nil). When a record is
|
896
|
+
inserted or updated, an exception will be raised for any required field
|
897
|
+
that has not been given a value or been given a nil value.</p>
|
898
|
+
<p>For example, to specify that the category column is required, you would
|
899
|
+
code:</p>
|
900
|
+
<div class="listingblock">
|
901
|
+
<div class="content">
|
902
|
+
<pre><tt>db.create_table(:addressbook, :firstname, :String,
|
903
|
+
:lastname, :String, :phone_no, :String,
|
904
|
+
:category, {:DataType=>:String, :Required=>true})</tt></pre>
|
905
|
+
</div></div>
|
906
|
+
<p>Notice that, since we are specifying advanced field type information, we
|
907
|
+
cannot just simply say :String for the second half of the field definition.
|
908
|
+
Instead, we have to pass a hash, with a :DataType item set to :String.
|
909
|
+
Next, because we are specifying that a column is required, we have to
|
910
|
+
include a hash item called :Required with it's value set to true.</p>
|
911
|
+
<h4><a id="creating-indexes"></a>Indexes</h4>
|
912
|
+
<p>Indexes are in-memory arrays that have an entry that corresponds to each
|
913
|
+
table record, but just holds the field values specified when the index was
|
914
|
+
created, plus the :recno of the actual table record. Because index arrays
|
915
|
+
are smaller than the actual table and because they are in-memory instead of
|
916
|
+
on-disk, using an index in a select query is usually much faster than
|
917
|
+
selecting against the table itself, especially when the table is quite
|
918
|
+
large.</p>
|
919
|
+
<p>To specify that an index is to be created, you need to tell KirbyBase which
|
920
|
+
fields are to be included in a particular index. You can have up to 5
|
921
|
+
indexes per table. Indexes can either contain single or multiple fields.
|
922
|
+
For example, to create an index on firstname and lastname for a table called
|
923
|
+
:addressbook, you would code:</p>
|
924
|
+
<div class="listingblock">
|
925
|
+
<div class="content">
|
926
|
+
<pre><tt>db.create_table(:addressbook, :firstname, {:DataType=>:String, :Index=>1},
|
927
|
+
:lastname, {:DataType=>:String, :Index=>1},
|
928
|
+
:phone_no, :String)</tt></pre>
|
929
|
+
</div></div>
|
930
|
+
<p>Notice that, since we are specifying advanced field type information, we
|
931
|
+
cannot just simply say :String for the second half of the field definition.
|
932
|
+
Instead, we have to pass a hash, with a :DataType item set to :String.
|
933
|
+
Next, because we are creating an index, we have to include a hash item
|
934
|
+
called :Index with it's value set from 1 to 5. For compound indexes, like
|
935
|
+
the one in the above example, we need to make sure that all fields in the
|
936
|
+
index have the same number. To have another index for a table, just make
|
937
|
+
sure you increment the index number. So, for example, if we wanted to have
|
938
|
+
another index for the :addressbook table on the field phone number, we would
|
939
|
+
code:</p>
|
940
|
+
<div class="listingblock">
|
941
|
+
<div class="content">
|
942
|
+
<pre><tt>db.create_table(:addressbook, :firstname, {:DataType=>:String, :Index=>1},
|
943
|
+
:lastname, {:DataType=>:String, :Index=>1},
|
944
|
+
:phone_no, {:DataType=>:String, :Index=>2})</tt></pre>
|
945
|
+
</div></div>
|
946
|
+
<p>Notice how we just increment the index number to 2 for the :phone_no index.
|
947
|
+
Since there are no other fields with the same index number, this will create
|
948
|
+
an index with just the :phone_no field in it. You will see how to use
|
949
|
+
indexes in your select queries later.</p>
|
950
|
+
<div class="sidebarblock">
|
951
|
+
<a id="a-note-about-indexes"></a>
|
952
|
+
<div class="sidebar-content">
|
953
|
+
<div class="sidebar-title">A note about indexes</div>
|
954
|
+
<p>When KirbyBase initializes, it creates an instance for each table in
|
955
|
+
the database. It also creates each index array for each indexed field in
|
956
|
+
each table. So, if there are several large tables that have indexed fields,
|
957
|
+
this database initialization process could take several seconds. I decided
|
958
|
+
to have it operate this way, because I thought that it made more sense to
|
959
|
+
have a user application wait once upon initialization, rather than have it
|
960
|
+
wait the first time a table is referenced. A user is more used to waiting
|
961
|
+
for an application to properly initialize, so this initial delay, I thought,
|
962
|
+
would feel more natural to the user, rather than a wait time in the middle
|
963
|
+
of the application.</p>
|
964
|
+
<p>Please let me know if this poses a problem for any of your applications. I
|
965
|
+
have some ideas that I could implement that would allow the developer to
|
966
|
+
specify when indexes are created.</p>
|
967
|
+
<p>This is also an excellent reason to use the client/server capabilities of
|
968
|
+
KirbyBase. In client/server mode, the database initialization processing is
|
969
|
+
all done on the server, so, once this is done, any users starting their
|
970
|
+
client application and connecting to the server will experience no delay due
|
971
|
+
to index array creation.</p>
|
972
|
+
</div></div>
|
973
|
+
<h4><a id="creating-lookup-fields"></a>Lookup Fields</h4>
|
974
|
+
<p>Lookup fields are fields that hold a reference to a record in another table.
|
975
|
+
For example, say you have a table called :department that has fields called
|
976
|
+
:dept_id, :dept_name, and :manager. Now, let's say that you don't want the
|
977
|
+
:manager field to just hold the manager's name; you want it to point to the
|
978
|
+
manager's record in the :employee table, which has fields like
|
979
|
+
:employee_id, :firstname, :lastname, :ss_no, etc. What you want to do is
|
980
|
+
to tell KirbyBase that the :dept.manager field should actually point to the
|
981
|
+
manager's record in the :employee table (the "lookup" table). Here's how
|
982
|
+
you would do that:</p>
|
983
|
+
<div class="listingblock">
|
984
|
+
<div class="content">
|
985
|
+
<pre><tt>db.create_table(:department, :dept_id, :String, :dept_name, :String,
|
986
|
+
:manager, {:DataType=>:String, :Lookup=>[:employee, :employee_id]})</tt></pre>
|
987
|
+
</div></div>
|
988
|
+
<p>Ok, what we have done here is to tell KirbyBase a little bit "extra" about
|
989
|
+
the :manager field. We are specifying that whenever we ask for the value of
|
990
|
+
:manager, we want KirbyBase to do a #select against the :employee table that
|
991
|
+
compares the value of the :department.manager field to the value of the
|
992
|
+
:employee.employee_id field. If an index is available for the
|
993
|
+
:employee.employee_id field, KirbyBase will automatically use it.</p>
|
994
|
+
<p>There is a shortcut to specifying a lookup field. If the :employee_id field
|
995
|
+
has been designated a "key" field for the :employee table, we can even
|
996
|
+
shorten our code and KirbyBase will figure it all out. For example, if the
|
997
|
+
:employee table was created with this code:</p>
|
998
|
+
<div class="listingblock">
|
999
|
+
<div class="content">
|
1000
|
+
<pre><tt>db.create_table(:employee, :employee_id, {:DataType=>:String, :Key=>true},
|
1001
|
+
:firstname, :String, :lastname, :String)</tt></pre>
|
1002
|
+
</div></div>
|
1003
|
+
<p>Then the field definition for :manager could be re-written as:</p>
|
1004
|
+
<div class="listingblock">
|
1005
|
+
<div class="content">
|
1006
|
+
<pre><tt>db.create_table(:department, :dept_id, :String, :dept_name, :String,
|
1007
|
+
:manager, :employee)</tt></pre>
|
1008
|
+
</div></div>
|
1009
|
+
<p>KirbyBase will figure out that you want to compare :department.manager to
|
1010
|
+
:employee.employee_id.</p>
|
1011
|
+
<div class="sidebarblock">
|
1012
|
+
<div class="sidebar-content">
|
1013
|
+
<div class="admonitionblock">
|
1014
|
+
<table><tr>
|
1015
|
+
<td class="icon">
|
1016
|
+
<img src="./images/important.png" alt="Important" />
|
1017
|
+
</td>
|
1018
|
+
<td class="content">In order to use the shortcut when specifying a lookup field, the
|
1019
|
+
lookup table it is pointing to <strong>must</strong> already exist. This is so that
|
1020
|
+
KirbyBase can do the defaulting magic.</td>
|
1021
|
+
</tr></table>
|
1022
|
+
</div>
|
1023
|
+
</div></div>
|
1024
|
+
<h4><a id="creating-link-many-fields"></a>Link_many Fields</h4>
|
1025
|
+
<p>When you specify that a field has a Link_many, you are telling KirbyBase
|
1026
|
+
that you want to create a one-to-many link between this field and a subset
|
1027
|
+
of records from another table.</p>
|
1028
|
+
<p>For example, say you are creating an order entry system for your business.
|
1029
|
+
You have a master table called :orders that holds one record for each
|
1030
|
+
customer order that is placed. It has fields like: :order_id, :cust_id,
|
1031
|
+
:order_date, etc.</p>
|
1032
|
+
<p>Now, you also need a table that is going to have a record for each detail
|
1033
|
+
item type ordered. Let's call it :order_items and some of it's fields would
|
1034
|
+
be: :item_no, :qty, :order_id.</p>
|
1035
|
+
<p>Notice that the detail table has a field called :order_id. This will hold
|
1036
|
+
the order_id linking it back to the :orders.order_id table. If a customer
|
1037
|
+
order's only one type of item, then there will only be one record in the
|
1038
|
+
:order_items table that has that order_id. But, if the customer places an
|
1039
|
+
order for many different types of items, there will be many records in the
|
1040
|
+
:order_items table that have the same order_id. That's why we say that
|
1041
|
+
there is a "one to many" link between the master (or parent) table, :orders,
|
1042
|
+
and the detail (or child) table, :order_items.</p>
|
1043
|
+
<p>When we select an :order record, we want the ability to also select all the
|
1044
|
+
detail records from the :order_items table that belong to that order. We do
|
1045
|
+
this by telling KirbyBase about the one-to-many link between the two
|
1046
|
+
tables. Here's how:</p>
|
1047
|
+
<div class="listingblock">
|
1048
|
+
<div class="content">
|
1049
|
+
<pre><tt>db.create_table(:orders, :order_id, :Integer, :cust_id, :Integer,
|
1050
|
+
:order_date, :Date, :items, {:DataType=>:ResultSet, :Link_many=>[:order_id,
|
1051
|
+
:order_items, :order_id]})
|
1052
|
+
|
1053
|
+
db.create_table(:order_items, :item_no, :Integer, :qty, :Integer,
|
1054
|
+
:order_id, :Integer)</tt></pre>
|
1055
|
+
</div></div>
|
1056
|
+
<p>Now, look at the :Link_many item in the field type definition hash in the
|
1057
|
+
above example. We are specifying that a field, called :items, be created
|
1058
|
+
with a field type of :ResultSet. We tell KirbyBase that, when we ask for
|
1059
|
+
the value of :items, it should do a #select on :order_items and return a
|
1060
|
+
KBResultSet that contains :order_items whose :order_id field (the last item
|
1061
|
+
in the array above), equals the value of the :orders.order_id field (the
|
1062
|
+
first item of the array above).</p>
|
1063
|
+
<p>If you opened up the :orders table file in a text editor, you would notice
|
1064
|
+
that, for each record, the :items field is blank. No data is ever stored in
|
1065
|
+
this field, since it's value is always computed at runtime.</p>
|
1066
|
+
<h4><a id="creating-calculated-fields"></a>Calculated Fields</h4>
|
1067
|
+
<p>When you specify that a field is a Calculated field, you are telling
|
1068
|
+
KirbyBase to compute the value of the field at runtime, based on the code
|
1069
|
+
string that you pass it at table creation time.</p>
|
1070
|
+
<p>For example. let's say you have a table that is going to track your
|
1071
|
+
purchases. It will have fields like :purchase_date, :description, :qty, and
|
1072
|
+
:price.</p>
|
1073
|
+
<p>Let's say that you want to have a "virtual" field, called :total_cost, that
|
1074
|
+
is the result of quantity multiplied by price. You want this field
|
1075
|
+
calculated at runtime, so that if you change a record's quantity or price,
|
1076
|
+
the :total_cost field will automatically reflect the changes.
|
1077
|
+
Here's how you would define this table:</p>
|
1078
|
+
<div class="listingblock">
|
1079
|
+
<div class="content">
|
1080
|
+
<pre><tt>db.create_table(:purchases, :purchase_date, :Date, :description, :String,
|
1081
|
+
:qty, :Integer, :price, :Float, :total_cost, {:DataType=>:Float,
|
1082
|
+
:Calculated=>'qty*price'})</tt></pre>
|
1083
|
+
</div></div>
|
1084
|
+
<p>See how we tell KirbyBase how to calculate the field? By multiplying the
|
1085
|
+
:qty field by the :price field.</p>
|
1086
|
+
<p>If you opened up the :purchases file in a text editor, you would notice
|
1087
|
+
that, for each record, the :total_cost field is blank. No data is ever
|
1088
|
+
stored in this field, since it's value is always computed at runtime.</p>
|
1089
|
+
</div>
|
1090
|
+
<h2><a id="obtaining-a-reference-to-an-existing-table"></a>Obtaining a reference to an existing table</h2>
|
1091
|
+
<div class="sectionbody">
|
1092
|
+
<p>If a table already exists and you need to get a reference to it so that you
|
1093
|
+
can insert, select, etc., you would call the #get_table method from your
|
1094
|
+
KirbyBase instance. This is how to obtain a handle to an existing table.
|
1095
|
+
You cannot call KBTable#new directly.</p>
|
1096
|
+
<div class="listingblock">
|
1097
|
+
<div class="content">
|
1098
|
+
<pre><tt>plane_tbl_another_reference = db.get_table(:plane)</tt></pre>
|
1099
|
+
</div></div>
|
1100
|
+
<p>Then, you can use it just like you would use a reference you got when
|
1101
|
+
you created a new table.</p>
|
1102
|
+
</div>
|
1103
|
+
<h2><a id="insert-method"></a>The insert method</h2>
|
1104
|
+
<div class="sectionbody">
|
1105
|
+
<p>To insert records into a table, you use the insert method. You can use an
|
1106
|
+
array, a hash, a struct, a code block, or an instance of the table's custom
|
1107
|
+
record class to specify the insert values.</p>
|
1108
|
+
<h3>Insert a record using an array for the input values:</h3>
|
1109
|
+
<div class="listingblock">
|
1110
|
+
<div class="content">
|
1111
|
+
<pre><tt>plane_tbl.insert('FW-190', 'Germany', 'Fighter', 399, 499,
|
1112
|
+
Date.new(1942,12,1), false)</tt></pre>
|
1113
|
+
</div></div>
|
1114
|
+
<p>The length of the data array must equal the number of columns in the table,
|
1115
|
+
<strong>not</strong> including the :recno column. Also, the data types must match. In the
|
1116
|
+
above example, specifying "399", instead of 399, would have resulted in an
|
1117
|
+
error.</p>
|
1118
|
+
<h3>Insert a record using a hash for the input values:</h3>
|
1119
|
+
<div class="listingblock">
|
1120
|
+
<div class="content">
|
1121
|
+
<pre><tt>plane_tbl.insert(:name='P-51', :country='USA', :role='Fighter', :speed=403,
|
1122
|
+
:range=1201, :began_service=Date.new(1943,6,24), :still_flying=true)</tt></pre>
|
1123
|
+
</div></div>
|
1124
|
+
<h3>Insert a record using a Struct for the input values:</h3>
|
1125
|
+
<div class="listingblock">
|
1126
|
+
<div class="content">
|
1127
|
+
<pre><tt>InputRec = Struct.new(:name, :country, :role, :speed, :range,
|
1128
|
+
:began_service, :still_flying)
|
1129
|
+
rec = InputRec.new('P-47', 'USA', 'Fighter', 365, 888, Date.new(1943,3,12),
|
1130
|
+
false)
|
1131
|
+
|
1132
|
+
plane_tbl.insert(rec)</tt></pre>
|
1133
|
+
</div></div>
|
1134
|
+
<h3>Insert a record using a code block for the input values:</h3>
|
1135
|
+
<div class="listingblock">
|
1136
|
+
<div class="content">
|
1137
|
+
<pre><tt>plane_tbl.insert do |r|
|
1138
|
+
r.name = 'B-17'
|
1139
|
+
r.country = 'USA'
|
1140
|
+
r.role = 'Bomber'
|
1141
|
+
r.speed = 315
|
1142
|
+
r.range = 1400
|
1143
|
+
r.began_service = Date.new(1937, 5, 1)
|
1144
|
+
r.still_flying = true</tt></pre>
|
1145
|
+
</div></div>
|
1146
|
+
<h3>Insert a record using an instance of the table's custom record class:</h3>
|
1147
|
+
<div class="listingblock">
|
1148
|
+
<div class="content">
|
1149
|
+
<pre><tt>foo = Foobar.new do |x|
|
1150
|
+
x.name = 'Spitfire'
|
1151
|
+
x.country = 'Great Britain'
|
1152
|
+
x.role = 'Fighter'
|
1153
|
+
x.speed = 333
|
1154
|
+
x.range = 454
|
1155
|
+
x.began_service = Date.new(1936, 1, 1)
|
1156
|
+
x.still_flying = true
|
1157
|
+
x.alpha = "This variable won't be stored in KirbyBase."
|
1158
|
+
x.beta = 'Neither will this one.'
|
1159
|
+
end
|
1160
|
+
|
1161
|
+
plane_tbl.insert(foo)</tt></pre>
|
1162
|
+
</div></div>
|
1163
|
+
<div class="sidebarblock">
|
1164
|
+
<div class="sidebar-content">
|
1165
|
+
<div class="admonitionblock">
|
1166
|
+
<table><tr>
|
1167
|
+
<td class="icon">
|
1168
|
+
<img src="./images/note.png" alt="Note" />
|
1169
|
+
</td>
|
1170
|
+
<td class="content">The #insert method will return the record number of the newly created
|
1171
|
+
record. This is an auto-incremented integer generated by KirbyBase. This
|
1172
|
+
number will <strong>never</strong> change for the record and can be used as a unique
|
1173
|
+
identifier for the record.</td>
|
1174
|
+
</tr></table>
|
1175
|
+
</div>
|
1176
|
+
</div></div>
|
1177
|
+
</div>
|
1178
|
+
<h2><a id="how-to-select-records"></a>How to select records</h2>
|
1179
|
+
<div class="sectionbody">
|
1180
|
+
<p>The syntax you use to select records to perform operations on is the same
|
1181
|
+
for a select, update, or delete statement, so I wanted to cover, in
|
1182
|
+
general, how to create a query expression first, before getting to the
|
1183
|
+
specifics of select, update, and delete.</p>
|
1184
|
+
<p>In KirbyBase, query conditions are specified simply by using Ruby code
|
1185
|
+
blocks. Therefore, any code block that can be converted into a Proc object
|
1186
|
+
is valid. This allows for great flexibility, as you will see in the many
|
1187
|
+
examples below.</p>
|
1188
|
+
<div class="sidebarblock">
|
1189
|
+
<div class="sidebar-content">
|
1190
|
+
<div class="admonitionblock">
|
1191
|
+
<table><tr>
|
1192
|
+
<td class="icon">
|
1193
|
+
<img src="./images/tip.png" alt="Tip" />
|
1194
|
+
</td>
|
1195
|
+
<td class="content">You can find many examples of how to specify queries in the "examples"
|
1196
|
+
directory of the KirbyBase distribution.</td>
|
1197
|
+
</tr></table>
|
1198
|
+
</div>
|
1199
|
+
</div></div>
|
1200
|
+
<p>Now that we have a general understanding of how to select records to operate
|
1201
|
+
on, lets get more specific by looking at the select, update, and delete
|
1202
|
+
methods.</p>
|
1203
|
+
<h3><a id="select-method"></a>The select method</h3>
|
1204
|
+
<p>The select method allows you to ask for all records in a table that match
|
1205
|
+
certain selection criteria. Additionally, you can also specify which fields
|
1206
|
+
you want included in the result set. The select method returns an instance
|
1207
|
+
of KBResultSet, which is an array of records that satisfied the select
|
1208
|
+
criteria. KBResultSet is covered in more detail later in the manual.</p>
|
1209
|
+
<p>Here is the simplest example of a select statement:</p>
|
1210
|
+
<div class="listingblock">
|
1211
|
+
<div class="content">
|
1212
|
+
<pre><tt>result_set = plane_tbl.select</tt></pre>
|
1213
|
+
</div></div>
|
1214
|
+
<p>Since, no code block was specified, KirbyBase will include all records in
|
1215
|
+
the result set. Additionally, because a list of fields to include in the
|
1216
|
+
result set was not specified, KirbyBase will include all fields for each
|
1217
|
+
record in the result set.</p>
|
1218
|
+
<p>To specify that you only want a subset of fields in the result set, you list
|
1219
|
+
their field names as arguments to the select method. For example:</p>
|
1220
|
+
<div class="listingblock">
|
1221
|
+
<div class="content">
|
1222
|
+
<pre><tt>result_set = plane_tbl.select(:name, :speed)</tt></pre>
|
1223
|
+
</div></div>
|
1224
|
+
<p>To specify selection criteria, attach a code block to the select method
|
1225
|
+
call. For example, if you only wanted to select Japanese planes:</p>
|
1226
|
+
<div class="listingblock">
|
1227
|
+
<div class="content">
|
1228
|
+
<pre><tt>result_set = plane_tbl.select(:name, :speed) { |r| r.country == 'Japan' }</tt></pre>
|
1229
|
+
</div></div>
|
1230
|
+
<p>You can combine multiple expressions in the code block. For example, to
|
1231
|
+
select only US planes that have a speed greater than 350mph:</p>
|
1232
|
+
<div class="listingblock">
|
1233
|
+
<div class="content">
|
1234
|
+
<pre><tt>result_set = plane_tbl.select { |r| r.country == 'USA' and r.speed > 350 }</tt></pre>
|
1235
|
+
</div></div>
|
1236
|
+
<p>You can use regular expressions in the code block. Let's select all Axis
|
1237
|
+
fighters:</p>
|
1238
|
+
<div class="listingblock">
|
1239
|
+
<div class="content">
|
1240
|
+
<pre><tt>result_set = plane_tbl.select do |r|
|
1241
|
+
r.country =~ /Germany|Japan/ and r.role == 'Fighter'
|
1242
|
+
end</tt></pre>
|
1243
|
+
</div></div>
|
1244
|
+
<h3><a id="selecting-by-index"></a>Selecting by index</h3>
|
1245
|
+
<p>Performing a select query using an index is almost identical to performing a
|
1246
|
+
regular select query. You just have to specify the particular select
|
1247
|
+
method, based on the index you wish to use.</p>
|
1248
|
+
<p>For example, say you have created an index on the :speed field of the
|
1249
|
+
:plane table. You want to search for all planes with a speed greater than
|
1250
|
+
400 mph. Ruby automatically creates select methods for each one of the
|
1251
|
+
indexes of a table. So, you would code your select query like this:</p>
|
1252
|
+
<div class="listingblock">
|
1253
|
+
<div class="content">
|
1254
|
+
<pre><tt>plane_tbl.select_by_speed_index { |r| r.speed > 400 }</tt></pre>
|
1255
|
+
</div></div>
|
1256
|
+
<p>Notice that you simply insert the name of the field as part of the method
|
1257
|
+
name. It's as simple as that.</p>
|
1258
|
+
<p>For compound indexes, you just need to specify the
|
1259
|
+
indexed field names in the select method in the same order as they are in
|
1260
|
+
the table. So, let's say you have indexed the :plane table on :country and
|
1261
|
+
:role, in one index. To select on this compound index, you would code:</p>
|
1262
|
+
<div class="listingblock">
|
1263
|
+
<div class="content">
|
1264
|
+
<pre><tt>plane_tbl.select_by_country_role_index do |r|
|
1265
|
+
r.country == 'Germany' and r.role == 'Fighter' }
|
1266
|
+
end</tt></pre>
|
1267
|
+
</div></div>
|
1268
|
+
<p>Notice how you just list both fields as part of the name of the select
|
1269
|
+
method, separated by underscores.</p>
|
1270
|
+
<div class="sidebarblock">
|
1271
|
+
<div class="sidebar-content">
|
1272
|
+
<div class="admonitionblock">
|
1273
|
+
<table><tr>
|
1274
|
+
<td class="icon">
|
1275
|
+
<img src="./images/warning.png" alt="Warning" />
|
1276
|
+
</td>
|
1277
|
+
<td class="content">If you specify a select method that does not exist, you will get an
|
1278
|
+
error. Likewise, if you specify a query by an index, yet include a field
|
1279
|
+
name in the query that is not part of the index, you will get an error.</td>
|
1280
|
+
</tr></table>
|
1281
|
+
</div>
|
1282
|
+
</div></div>
|
1283
|
+
<h3><a id="selecting-by-recno-index"></a>Selecting by :recno index</h3>
|
1284
|
+
<p>For each table, a :recno index is automatically created, whether or not
|
1285
|
+
other indexes are explicitly created by you. You can alway use this index
|
1286
|
+
to select records based solely on :recno. For example:</p>
|
1287
|
+
<div class="listingblock">
|
1288
|
+
<div class="content">
|
1289
|
+
<pre><tt>plane_tbl.select_by_recno_index { |r| [3, 45, 152].include?(r.recno) }</tt></pre>
|
1290
|
+
</div></div>
|
1291
|
+
<h3><a id="selects-involving-lookups-or-link-manys"></a>Selects Involving Lookups or Link_manys</h3>
|
1292
|
+
<p>Selects that involve Lookup fields or Link_many fields have a special case
|
1293
|
+
because both field types return complex objects rather than simple data
|
1294
|
+
types. For example, consider the lookup field example that I described
|
1295
|
+
earlier. For reference, here are the two table defintions again:</p>
|
1296
|
+
<div class="listingblock">
|
1297
|
+
<div class="content">
|
1298
|
+
<pre><tt>department_tbl = db.create_table(:department, :dept_id, :String,
|
1299
|
+
:dept_name, :String, :manager, {:DataType=>:String, :Lookup=>[:employee,
|
1300
|
+
:employee_id]})
|
1301
|
+
|
1302
|
+
employee_tbl = db.create_table(:employee, :employee_id, {:DataType=>:String,
|
1303
|
+
:Key=>true}, :firstname, :String, :lastname, :String)</tt></pre>
|
1304
|
+
</div></div>
|
1305
|
+
<p>To find the department managed by John Doe, the select query would look like
|
1306
|
+
this:</p>
|
1307
|
+
<div class="listingblock">
|
1308
|
+
<div class="content">
|
1309
|
+
<pre><tt>department_tbl.select do |r|
|
1310
|
+
r.manager.firstname == 'John' and r.manager.lastname == 'Doe'
|
1311
|
+
end</tt></pre>
|
1312
|
+
</div></div>
|
1313
|
+
<p>To print out all departments including the manager's full name:</p>
|
1314
|
+
<div class="listingblock">
|
1315
|
+
<div class="content">
|
1316
|
+
<pre><tt>department_tbl.select.each do |r|
|
1317
|
+
puts 'Department: %s Manager: %s %s' % [r.dept_name,
|
1318
|
+
r.manager.firstname, r.manager.lastname]
|
1319
|
+
end</tt></pre>
|
1320
|
+
</div></div>
|
1321
|
+
<p>Selects involving Link_many fields are slightly different because they
|
1322
|
+
involve ResultSets instead of just single objects. Here's the table
|
1323
|
+
definitions from the earlier Link_many discussion:</p>
|
1324
|
+
<div class="listingblock">
|
1325
|
+
<div class="content">
|
1326
|
+
<pre><tt>orders_tbl = db.create_table(:orders, :order_id, :Integer,
|
1327
|
+
:cust_id, :Integer, :order_date, :Date, :items, {:DataType=>:ResultSet,
|
1328
|
+
:Link_many=>[:order_id, :order_items, :order_id]})
|
1329
|
+
|
1330
|
+
order_items_tbl = db.create_table(:order_items, :item_no, :Integer,
|
1331
|
+
:qty, :Integer, :order_id, :Integer)</tt></pre>
|
1332
|
+
</div></div>
|
1333
|
+
<p>To print an order and all of it's associated detail items:</p>
|
1334
|
+
<div class="listingblock">
|
1335
|
+
<div class="content">
|
1336
|
+
<pre><tt>result = order_tbl.select { |r| r.order_id == 501 }.first
|
1337
|
+
puts '%d %d %s' % [result.order_id, result.cust_id, result.order_date]
|
1338
|
+
|
1339
|
+
result.items.each { |item| puts '%d %d' % [item.item_no, item.qty] }</tt></pre>
|
1340
|
+
</div></div>
|
1341
|
+
<p>Notice how the items attribute in the ResultSet is itself a ResultSet
|
1342
|
+
containing all of the :order_items records that belong to the selected
|
1343
|
+
order.</p>
|
1344
|
+
</div>
|
1345
|
+
<h2><a id="kbresultset"></a>KBResultSet</h2>
|
1346
|
+
<div class="sectionbody">
|
1347
|
+
<p>As stated above, the select method returns an instance of KBResultSet, which
|
1348
|
+
is an array of Struct objects (or instances of the class specified in
|
1349
|
+
record_class), each one representing a record that satisfied the selection
|
1350
|
+
criteria.</p>
|
1351
|
+
<p>Since each item in KBResultSet is a Struct object, you can easily reference
|
1352
|
+
it's members using field names. So, to print the name and speed of each
|
1353
|
+
German plane in the table you would code:</p>
|
1354
|
+
<div class="listingblock">
|
1355
|
+
<div class="content">
|
1356
|
+
<pre><tt>plane_tbl.select(:name, :speed) { |r| r.country == 'German' }.each do |r|
|
1357
|
+
puts '%s %s' % [r.name, r.speed]
|
1358
|
+
end</tt></pre>
|
1359
|
+
</div></div>
|
1360
|
+
<h3><a id="sorting-a-result-set"></a>Sorting a result set</h3>
|
1361
|
+
<p>You can specify sort criteria by calling KBResultSet#sort. You must supply
|
1362
|
+
the sort method with a list of field names that you want to sort by. For
|
1363
|
+
example, to select all planes, include just name, country, and speed in the
|
1364
|
+
result set, and sort the result set by country (ascending) then name
|
1365
|
+
(ascending), you would code:</p>
|
1366
|
+
<div class="listingblock">
|
1367
|
+
<div class="content">
|
1368
|
+
<pre><tt>result = plane_tbl.select(:name, :country, :speed).sort(:country, :name)</tt></pre>
|
1369
|
+
</div></div>
|
1370
|
+
<p>To indicate that you want a particular field sorted in descending order
|
1371
|
+
rather than ascending order, you need to put a minus sign in front of it.
|
1372
|
+
For example, to select all planes, include just name, country, and speed in
|
1373
|
+
the result set, and sort the result set by country (ascending) then speed
|
1374
|
+
(descending), you would code:</p>
|
1375
|
+
<div class="listingblock">
|
1376
|
+
<div class="content">
|
1377
|
+
<pre><tt>result_set = plane_tbl.select(:name, :country, :speed).sort(:country,
|
1378
|
+
-:speed)</tt></pre>
|
1379
|
+
</div></div>
|
1380
|
+
<p>You can also explicitly specify that a field be sorted in ascending order by
|
1381
|
+
putting a plus sign in front of it. This is not necessary, since ascending
|
1382
|
+
is the default, but it's there if you prefer to use it.</p>
|
1383
|
+
<h3><a id="to-report"></a>Producing a report from a result set</h3>
|
1384
|
+
<p>Additionally, you can also transform the KBResultSet into a nicely formatted
|
1385
|
+
report, suitable for printing, by calling KBResultSet#to_report. To print
|
1386
|
+
a formatted report of all plane names, countries, and speeds, sorted by
|
1387
|
+
name, you would code the following:</p>
|
1388
|
+
<div class="listingblock">
|
1389
|
+
<div class="content">
|
1390
|
+
<pre><tt>puts plane_tbl.select(:name, :country, :speed).sort(:name).to_report</tt></pre>
|
1391
|
+
</div></div>
|
1392
|
+
<h3><a id="crosstabs"></a>CrossTabs or Pivot Tables or Column Arrays (i.e. I don't know what to call them!)</h3>
|
1393
|
+
<p>Every KBResultSet has an additional feature that can prove quite useful.
|
1394
|
+
When a result set is created, KirbyBase creates an array for each column
|
1395
|
+
name that has all of the values of that column in it. Perhaps an example
|
1396
|
+
would make this more clear. Let's say you have a table that looks like
|
1397
|
+
this:</p>
|
1398
|
+
<div class="tableblock">
|
1399
|
+
<table rules="all"
|
1400
|
+
frame="border"
|
1401
|
+
cellspacing="0" cellpadding="4">
|
1402
|
+
<col width="102" />
|
1403
|
+
<col width="114" />
|
1404
|
+
<col width="137" />
|
1405
|
+
<thead>
|
1406
|
+
<tr>
|
1407
|
+
<th align="left">
|
1408
|
+
name
|
1409
|
+
</th>
|
1410
|
+
<th align="left">
|
1411
|
+
speed
|
1412
|
+
</th>
|
1413
|
+
<th align="left">
|
1414
|
+
range
|
1415
|
+
</th>
|
1416
|
+
</tr>
|
1417
|
+
</thead>
|
1418
|
+
<tbody valign="top">
|
1419
|
+
<tr>
|
1420
|
+
<td align="left">
|
1421
|
+
P-51
|
1422
|
+
</td>
|
1423
|
+
<td align="left">
|
1424
|
+
402
|
1425
|
+
</td>
|
1426
|
+
<td align="left">
|
1427
|
+
1201
|
1428
|
+
</td>
|
1429
|
+
</tr>
|
1430
|
+
<tr>
|
1431
|
+
<td align="left">
|
1432
|
+
ME-109
|
1433
|
+
</td>
|
1434
|
+
<td align="left">
|
1435
|
+
354
|
1436
|
+
</td>
|
1437
|
+
<td align="left">
|
1438
|
+
544
|
1439
|
+
</td>
|
1440
|
+
</tr>
|
1441
|
+
<tr>
|
1442
|
+
<td align="left">
|
1443
|
+
Spitfire
|
1444
|
+
</td>
|
1445
|
+
<td align="left">
|
1446
|
+
343
|
1447
|
+
</td>
|
1448
|
+
<td align="left">
|
1449
|
+
501
|
1450
|
+
</td>
|
1451
|
+
</tr>
|
1452
|
+
</tbody>
|
1453
|
+
</table>
|
1454
|
+
</div>
|
1455
|
+
<p>If you do a select on the table, not only will the result set contain a
|
1456
|
+
row for each record that satisfied the select condition, but it will also
|
1457
|
+
contain an array for each column, which will hold all the column values.
|
1458
|
+
Here's an example, using the above mentioned table:</p>
|
1459
|
+
<div class="listingblock">
|
1460
|
+
<div class="content">
|
1461
|
+
<pre><tt>result = plane_tbl.select
|
1462
|
+
|
1463
|
+
puts result[0].name => P-51
|
1464
|
+
puts result[0].speed => 402
|
1465
|
+
|
1466
|
+
p result.speed => [402,354,343]</tt></pre>
|
1467
|
+
</div></div>
|
1468
|
+
<p>Notice how you can reference this "column array" by calling the column name
|
1469
|
+
as a method. KirbyBase returns an array that holds all the values, in this
|
1470
|
+
case, of the speed column. This can be very useful in some cases. For a
|
1471
|
+
good example of this, look in the examples\crosstab_test directory of the
|
1472
|
+
distribution.</p>
|
1473
|
+
</div>
|
1474
|
+
<h2><a id="how-to-update-records"></a>How to update records</h2>
|
1475
|
+
<div class="sectionbody">
|
1476
|
+
<p>You can update the data in a table either by using the KBTable#update method
|
1477
|
+
by itself, or using it in conjunction with the KBResultSet#set method.
|
1478
|
+
Both methods produce the same result. The main difference is that, while
|
1479
|
+
using the #update method by itself, you can use a Hash, Array, or Struct as
|
1480
|
+
your update criteria, using the #set method in conjunction with the #update
|
1481
|
+
method adds the ability to use a code block as your update criteria. You
|
1482
|
+
will see specific examples of this in "The update method" description below.</p>
|
1483
|
+
<h3><a id="update-method"></a>The update method</h3>
|
1484
|
+
<p>To update a table, you use the update method. You <strong>must</strong> specify a code
|
1485
|
+
block that indicates which records are to be updated. Additionally, you must
|
1486
|
+
specify the fields to be updated and the new values for those fields.</p>
|
1487
|
+
<p>You can update records using a Hash, Struct, an Array, or an instance of a
|
1488
|
+
class you defined. For example, to change the P-51's speed to 405mph and
|
1489
|
+
it's range to 1210 miles, you could code:</p>
|
1490
|
+
<div class="listingblock">
|
1491
|
+
<div class="content">
|
1492
|
+
<pre><tt>plane_tbl.update(:speed=>405, :range=>1210) { |r| r.name == 'P-51' }</tt></pre>
|
1493
|
+
</div></div>
|
1494
|
+
<p>or:</p>
|
1495
|
+
<div class="listingblock">
|
1496
|
+
<div class="content">
|
1497
|
+
<pre><tt>UpdateRec = Struct.new(:name, :country, :role, :speed, :range,
|
1498
|
+
:began_service, :still_flying)
|
1499
|
+
|
1500
|
+
rec = UpdateRec.new
|
1501
|
+
rec.speed = 405
|
1502
|
+
rec.range = 1210
|
1503
|
+
plane_tbl.update(rec) { |r| r.name == 'P-51' }</tt></pre>
|
1504
|
+
</div></div>
|
1505
|
+
<h3><a id="set-method"></a>The set method</h3>
|
1506
|
+
<p>You can also update records using a code block, via KBResultSet#set:</p>
|
1507
|
+
<div class="listingblock">
|
1508
|
+
<div class="content">
|
1509
|
+
<pre><tt>plane_tbl.update {|r| r.name == 'P-51'}.set do |r|
|
1510
|
+
r.speed = 405
|
1511
|
+
r.range = 1210
|
1512
|
+
end</tt></pre>
|
1513
|
+
</div></div>
|
1514
|
+
<p>You can also update records using a Hash, Struct, or an Array, via
|
1515
|
+
KBResultSet#set:</p>
|
1516
|
+
<div class="listingblock">
|
1517
|
+
<div class="content">
|
1518
|
+
<pre><tt>plane_tbl.update {|r| r.name == 'P-51'}.set(:speed=>405, :range=>1210)</tt></pre>
|
1519
|
+
</div></div>
|
1520
|
+
<div class="sidebarblock">
|
1521
|
+
<div class="sidebar-content">
|
1522
|
+
<div class="admonitionblock">
|
1523
|
+
<table><tr>
|
1524
|
+
<td class="icon">
|
1525
|
+
<img src="./images/important.png" alt="Important" />
|
1526
|
+
</td>
|
1527
|
+
<td class="content">When you don't supply a code block to the #select method,
|
1528
|
+
KirbyBase automatically selects all of the records in the table. I felt
|
1529
|
+
that having the #update method work the same way was too dangerous. It
|
1530
|
+
would be too easy for someone to accidentally update all of the records in
|
1531
|
+
a table if they forgot to supply a code block to #update. That is why the
|
1532
|
+
#update method <strong>requires</strong> a code block. To update all of the records in a
|
1533
|
+
table, use the #update_all method.</td>
|
1534
|
+
</tr></table>
|
1535
|
+
</div>
|
1536
|
+
</div></div>
|
1537
|
+
<div class="sidebarblock">
|
1538
|
+
<div class="sidebar-content">
|
1539
|
+
<div class="admonitionblock">
|
1540
|
+
<table><tr>
|
1541
|
+
<td class="icon">
|
1542
|
+
<img src="./images/note.png" alt="Note" />
|
1543
|
+
</td>
|
1544
|
+
<td class="content">The #update method will return an Integer specifying the number of
|
1545
|
+
records that were updated.</td>
|
1546
|
+
</tr></table>
|
1547
|
+
</div>
|
1548
|
+
</div></div>
|
1549
|
+
<h3><a id="update-all-method"></a>The update_all method</h3>
|
1550
|
+
<p>To update all records in a table, you can use KBTable#update_all. This
|
1551
|
+
works just like the update method, except that you don't specify a code
|
1552
|
+
block containing selection criteria.</p>
|
1553
|
+
<p>For example, to add 50 mph to every record's speed field, you would code:</p>
|
1554
|
+
<div class="listingblock">
|
1555
|
+
<div class="content">
|
1556
|
+
<pre><tt>plane_tbl.update_all { |r| r.speed = r.speed + 50 }</tt></pre>
|
1557
|
+
</div></div>
|
1558
|
+
<div class="sidebarblock">
|
1559
|
+
<div class="sidebar-content">
|
1560
|
+
<div class="admonitionblock">
|
1561
|
+
<table><tr>
|
1562
|
+
<td class="icon">
|
1563
|
+
<img src="./images/note.png" alt="Note" />
|
1564
|
+
</td>
|
1565
|
+
<td class="content">The #update_all method will return an Integer specifying the number
|
1566
|
+
of records that were updated.</td>
|
1567
|
+
</tr></table>
|
1568
|
+
</div>
|
1569
|
+
</div></div>
|
1570
|
+
</div>
|
1571
|
+
<h2><a id="how-to-delete-records"></a>How to delete records</h2>
|
1572
|
+
<div class="sectionbody">
|
1573
|
+
<p>Deleting records from a table is similar to performing a #select or an
|
1574
|
+
#update.</p>
|
1575
|
+
<h3><a id="delete-method"></a>The delete method</h3>
|
1576
|
+
<p>To use the #delete method, you <strong>must</strong> supply a code block that identifies
|
1577
|
+
which records should be deleted.</p>
|
1578
|
+
<p>For example, to delete the FW-190's record from the table:</p>
|
1579
|
+
<div class="listingblock">
|
1580
|
+
<div class="content">
|
1581
|
+
<pre><tt>plane_tbl.delete { |r| r.name == 'FW-190' }</tt></pre>
|
1582
|
+
</div></div>
|
1583
|
+
<div class="sidebarblock">
|
1584
|
+
<div class="sidebar-content">
|
1585
|
+
<div class="admonitionblock">
|
1586
|
+
<table><tr>
|
1587
|
+
<td class="icon">
|
1588
|
+
<img src="./images/important.png" alt="Important" />
|
1589
|
+
</td>
|
1590
|
+
<td class="content">When you don't supply a code block to the #select method,
|
1591
|
+
KirbyBase automatically selects all of the records in the table. I felt
|
1592
|
+
that having the #delete method work the same way was too dangerous. It
|
1593
|
+
would be too easy for someone to accidentally delete all of the records in
|
1594
|
+
a table if they forgot to supply a code block to #delete. That is why the
|
1595
|
+
#delete method <strong>requires</strong> a code block. To delete all of the records in a
|
1596
|
+
table, use the #clear method.</td>
|
1597
|
+
</tr></table>
|
1598
|
+
</div>
|
1599
|
+
</div></div>
|
1600
|
+
<div class="sidebarblock">
|
1601
|
+
<div class="sidebar-content">
|
1602
|
+
<div class="admonitionblock">
|
1603
|
+
<table><tr>
|
1604
|
+
<td class="icon">
|
1605
|
+
<img src="./images/note.png" alt="Note" />
|
1606
|
+
</td>
|
1607
|
+
<td class="content">The #delete method will return an Integer specifying the total number
|
1608
|
+
of records that were deleted.</td>
|
1609
|
+
</tr></table>
|
1610
|
+
</div>
|
1611
|
+
</div></div>
|
1612
|
+
<h3><a id="clear-method"></a>The clear (alias delete_all) method</h3>
|
1613
|
+
<p>To completely empty a table, use the clear method. For example:</p>
|
1614
|
+
<div class="listingblock">
|
1615
|
+
<div class="content">
|
1616
|
+
<pre><tt>plane_tbl.clear</tt></pre>
|
1617
|
+
</div></div>
|
1618
|
+
<div class="sidebarblock">
|
1619
|
+
<div class="sidebar-content">
|
1620
|
+
<div class="admonitionblock">
|
1621
|
+
<table><tr>
|
1622
|
+
<td class="icon">
|
1623
|
+
<img src="./images/important.png" alt="Important" />
|
1624
|
+
</td>
|
1625
|
+
<td class="content">By default, KirbyBase will reset the recno counter to 0. So, any
|
1626
|
+
new records inserted after a #clear will be given a :recno starting with 1.
|
1627
|
+
To prevent #clear from resetting the recno counter, pass false to #clear.</td>
|
1628
|
+
</tr></table>
|
1629
|
+
</div>
|
1630
|
+
</div></div>
|
1631
|
+
</div>
|
1632
|
+
<h2><a id="pack-method"></a>The pack method</h2>
|
1633
|
+
<div class="sectionbody">
|
1634
|
+
<p>When KirbyBase deletes a record, it really just fills the entire line in the
|
1635
|
+
file with spaces. Deleting the entire line and moving each subsequent line
|
1636
|
+
up one would take too much time. Also, when a record is updated, if the size
|
1637
|
+
of the updated record is greater than the size of the old record, KirbyBase
|
1638
|
+
spaces out that entire line in the file, and rewrites the updated record at
|
1639
|
+
the end of the file. Again, this is done so that the entire file doesn't
|
1640
|
+
have to be re-written just because one record got updated.</p>
|
1641
|
+
<p>However, this means that after a lot of deletes and updates, a table can
|
1642
|
+
have lots of blank lines in it. This slows down searches and makes the file
|
1643
|
+
bigger than it has to be. You can use the pack method to remove these blank
|
1644
|
+
lines:</p>
|
1645
|
+
<div class="listingblock">
|
1646
|
+
<div class="content">
|
1647
|
+
<pre><tt>result = plane_tbl.pack</tt></pre>
|
1648
|
+
</div></div>
|
1649
|
+
<div class="sidebarblock">
|
1650
|
+
<div class="sidebar-content">
|
1651
|
+
<div class="admonitionblock">
|
1652
|
+
<table><tr>
|
1653
|
+
<td class="icon">
|
1654
|
+
<img src="./images/note.png" alt="Note" />
|
1655
|
+
</td>
|
1656
|
+
<td class="content">The #pack method will return an Integer specifiying the number of
|
1657
|
+
blank lines that were removed from the table.</td>
|
1658
|
+
</tr></table>
|
1659
|
+
</div>
|
1660
|
+
</div></div>
|
1661
|
+
</div>
|
1662
|
+
<h2><a id="memo-and-blob-fields"></a>Memo and Blob Fields</h2>
|
1663
|
+
<div class="sectionbody">
|
1664
|
+
<p>Memo and Blob fields operate a little differently from standard field types.
|
1665
|
+
You specify their creation just like regular field types. Notice how you
|
1666
|
+
can specify a base path for where memo/blob files will be stored.</p>
|
1667
|
+
<div class="listingblock">
|
1668
|
+
<div class="content">
|
1669
|
+
<pre><tt>db.create_table(:plane, :name, :String, :speed, :Integer, :descr,
|
1670
|
+
:Memo) do |d|
|
1671
|
+
d.memo_blob_path = './memos'
|
1672
|
+
end</tt></pre>
|
1673
|
+
</div></div>
|
1674
|
+
<p>However, what you actually store in the Memo field upon an #insert is an
|
1675
|
+
instance of KBMemo. KBMemo has two attributes: :filepath and :contents.
|
1676
|
+
The first holds the path (including filename) to the text file that will
|
1677
|
+
hold the contents of the memo. This path will be relative to whatever
|
1678
|
+
path was specified as the memo_blob_path upon database initialization. Here
|
1679
|
+
is an example of how to do this:</p>
|
1680
|
+
<div class="listingblock">
|
1681
|
+
<div class="content">
|
1682
|
+
<pre><tt>memo_string = <<END_OF_STRING
|
1683
|
+
The FW-190 was a World War II German fighter. It was used primarily as an
|
1684
|
+
interceptor against Allied strategic bombers.
|
1685
|
+
END_OF_STRING
|
1686
|
+
|
1687
|
+
memo = KBMemo.new(db, 'FW-190.txt', memo_string)
|
1688
|
+
plane_tbl.insert('FW-190', 'Germany', 399, 499, memo)</tt></pre>
|
1689
|
+
</div></div>
|
1690
|
+
<p>Updates work similarly in that you would need to supply a KBMemo instance
|
1691
|
+
to the #update method for the :Memo field.</p>
|
1692
|
+
<p>Other than this difference, you can use a memo field just like a regular
|
1693
|
+
field. When you do a #select, KirbyBase goes out to the file that holds the
|
1694
|
+
memo data, reads in all the lines, and returns a KBMemo instance. Here is
|
1695
|
+
an example of how you can even query against a memo field:</p>
|
1696
|
+
<div class="listingblock">
|
1697
|
+
<div class="content">
|
1698
|
+
<pre><tt>plane_tbl.select { |r| r.descr.contents =~ /built in Detroit, Michigan/ }</tt></pre>
|
1699
|
+
</div></div>
|
1700
|
+
<p>And KirbyBase would select all records whose memo field contained the phrase
|
1701
|
+
"built in Detroit, Michigan".</p>
|
1702
|
+
<p>Blob fields work similarly, except that instead of doing a #readlines,
|
1703
|
+
KirbyBase opens the file in binary mode and reads in the whole file at once.</p>
|
1704
|
+
</div>
|
1705
|
+
<h2><a id="misc-kirbybase-methods"></a>Miscellaneous KirbyBase methods</h2>
|
1706
|
+
<div class="sectionbody">
|
1707
|
+
<div class="sidebarblock">
|
1708
|
+
<a id="drop-table"></a>
|
1709
|
+
<div class="sidebar-content">
|
1710
|
+
<div class="sidebar-title">KirbyBase#drop_table</div>
|
1711
|
+
<div class="listingblock">
|
1712
|
+
<div class="content">
|
1713
|
+
<pre><tt>db.drop_table(:plane)</tt></pre>
|
1714
|
+
</div></div>
|
1715
|
+
<p>Will delete a table from the database.</p>
|
1716
|
+
<p>Returns true if table was dropped.</p>
|
1717
|
+
</div></div>
|
1718
|
+
<div class="sidebarblock">
|
1719
|
+
<a id="tables"></a>
|
1720
|
+
<div class="sidebar-content">
|
1721
|
+
<div class="sidebar-title">KirbyBase#tables</div>
|
1722
|
+
<div class="listingblock">
|
1723
|
+
<div class="content">
|
1724
|
+
<pre><tt>db.tables</tt></pre>
|
1725
|
+
</div></div>
|
1726
|
+
<p>Returns an array of all table names in the database.</p>
|
1727
|
+
</div></div>
|
1728
|
+
<div class="sidebarblock">
|
1729
|
+
<a id="table-exists"></a>
|
1730
|
+
<div class="sidebar-content">
|
1731
|
+
<div class="sidebar-title">KirbyBase#table_exists?</div>
|
1732
|
+
<div class="listingblock">
|
1733
|
+
<div class="content">
|
1734
|
+
<pre><tt>db.table_exists?(:plane)</tt></pre>
|
1735
|
+
</div></div>
|
1736
|
+
<p>Returns true if table exists, false otherwise.</p>
|
1737
|
+
</div></div>
|
1738
|
+
<div class="sidebarblock">
|
1739
|
+
<a id="rename-table"></a>
|
1740
|
+
<div class="sidebar-content">
|
1741
|
+
<div class="sidebar-title">KirbyBase#rename_table</div>
|
1742
|
+
<div class="listingblock">
|
1743
|
+
<div class="content">
|
1744
|
+
<pre><tt>db.rename_table(:plane, :warplanes)</tt></pre>
|
1745
|
+
</div></div>
|
1746
|
+
<p>Rename table to new name.</p>
|
1747
|
+
<p>Returns a handle to the newly-renamed table.</p>
|
1748
|
+
</div></div>
|
1749
|
+
</div>
|
1750
|
+
<h2><a id="misc-kbtable-methods"></a>Miscellaneous KBTable methods</h2>
|
1751
|
+
<div class="sectionbody">
|
1752
|
+
<div class="sidebarblock">
|
1753
|
+
<a id="update-by-recno"></a>
|
1754
|
+
<div class="sidebar-content">
|
1755
|
+
<div class="sidebar-title">KBTable#[]=</div>
|
1756
|
+
<div class="listingblock">
|
1757
|
+
<div class="content">
|
1758
|
+
<pre><tt>plane_tbl[5] = {:country = 'Tasmania'}</tt></pre>
|
1759
|
+
</div></div>
|
1760
|
+
<p>You can quickly update an individual record by treating the table as a Hash
|
1761
|
+
and the keys as recno's. You can update it using a Hash, Array, or Struct.</p>
|
1762
|
+
<p>Returns Integer specifying number of records updated (should always be 1).</p>
|
1763
|
+
</div></div>
|
1764
|
+
<div class="sidebarblock">
|
1765
|
+
<a id="get-by-recno"></a>
|
1766
|
+
<div class="sidebar-content">
|
1767
|
+
<div class="sidebar-title">KBTable#[]</div>
|
1768
|
+
<div class="listingblock">
|
1769
|
+
<div class="content">
|
1770
|
+
<pre><tt>plane_tbl[5]</tt></pre>
|
1771
|
+
</div></div>
|
1772
|
+
<p>You can quickly select an individual record, by passing it's recno to a
|
1773
|
+
table as if it were a hash.</p>
|
1774
|
+
<p>Returns a single record either in the form of a Struct or an instance of
|
1775
|
+
the table's custom record class, if specified.</p>
|
1776
|
+
<div class="listingblock">
|
1777
|
+
<div class="content">
|
1778
|
+
<pre><tt>plane_tbl[5, 14, 33]</tt></pre>
|
1779
|
+
</div></div>
|
1780
|
+
<p>You can also use the [] method to get a group of records by passing it more
|
1781
|
+
than one recno.</p>
|
1782
|
+
<p>Returns a KBResultSet with the records having the recno's you passed in.</p>
|
1783
|
+
</div></div>
|
1784
|
+
<div class="sidebarblock">
|
1785
|
+
<a id="field-names"></a>
|
1786
|
+
<div class="sidebar-content">
|
1787
|
+
<div class="sidebar-title">KBTable#field_names</div>
|
1788
|
+
<div class="listingblock">
|
1789
|
+
<div class="content">
|
1790
|
+
<pre><tt>plane_tbl.field_names</tt></pre>
|
1791
|
+
</div></div>
|
1792
|
+
<p>Returns an array of the table's field names.</p>
|
1793
|
+
</div></div>
|
1794
|
+
<div class="sidebarblock">
|
1795
|
+
<a id="field-types"></a>
|
1796
|
+
<div class="sidebar-content">
|
1797
|
+
<div class="sidebar-title">KBTable#field_types</div>
|
1798
|
+
<div class="listingblock">
|
1799
|
+
<div class="content">
|
1800
|
+
<pre><tt>plane_tbl.field_types</tt></pre>
|
1801
|
+
</div></div>
|
1802
|
+
<p>Returns an array of the table's field types (i.e. :String, :Integer, :Float)</p>
|
1803
|
+
</div></div>
|
1804
|
+
<div class="sidebarblock">
|
1805
|
+
<a id="total-recs"></a>
|
1806
|
+
<div class="sidebar-content">
|
1807
|
+
<div class="sidebar-title">KBTable#total_recs</div>
|
1808
|
+
<div class="listingblock">
|
1809
|
+
<div class="content">
|
1810
|
+
<pre><tt>plane_tbl.total_recs</tt></pre>
|
1811
|
+
</div></div>
|
1812
|
+
<p>Returns an Integer specifying the total number of records in the table.</p>
|
1813
|
+
</div></div>
|
1814
|
+
<div class="sidebarblock">
|
1815
|
+
<a id="import-csv"></a>
|
1816
|
+
<div class="sidebar-content">
|
1817
|
+
<div class="sidebar-title">KBTable#import_csv</div>
|
1818
|
+
<div class="listingblock">
|
1819
|
+
<div class="content">
|
1820
|
+
<pre><tt>plane_tbl.import_csv(csv_filename)</tt></pre>
|
1821
|
+
</div></div>
|
1822
|
+
<p>This method allows you to import a csv file into the current table.
|
1823
|
+
KirbyBase will attempt to convert the values in the csv file into their
|
1824
|
+
corresponding KirbyBase field types, based upon the field types you
|
1825
|
+
designated when you created the table.</p>
|
1826
|
+
<p>Returns an Integer specifying the total number of records imported.</p>
|
1827
|
+
</div></div>
|
1828
|
+
<div class="sidebarblock">
|
1829
|
+
<a id="add-column"></a>
|
1830
|
+
<div class="sidebar-content">
|
1831
|
+
<div class="sidebar-title">KBTable#add_column</div>
|
1832
|
+
<div class="listingblock">
|
1833
|
+
<div class="content">
|
1834
|
+
<pre><tt>plane_tbl.add_column(:weight, :Integer, :range)</tt></pre>
|
1835
|
+
</div></div>
|
1836
|
+
<p>This method allows you to add a column to an existing table. You must
|
1837
|
+
specify a column name, and a column type. You can optionally specify a
|
1838
|
+
column that you want the new column added after. If this is not specified,
|
1839
|
+
KirbyBase will add the column to the end of the table.</p>
|
1840
|
+
<div class="admonitionblock">
|
1841
|
+
<table><tr>
|
1842
|
+
<td class="icon">
|
1843
|
+
<img src="./images/important.png" alt="Important" />
|
1844
|
+
</td>
|
1845
|
+
<td class="content">Because #add_column changes the structure of a table, you can
|
1846
|
+
only call this method when connect_type==:local.</td>
|
1847
|
+
</tr></table>
|
1848
|
+
</div>
|
1849
|
+
</div></div>
|
1850
|
+
<div class="sidebarblock">
|
1851
|
+
<a id="drop-column"></a>
|
1852
|
+
<div class="sidebar-content">
|
1853
|
+
<div class="sidebar-title">KBTable#drop_column</div>
|
1854
|
+
<div class="listingblock">
|
1855
|
+
<div class="content">
|
1856
|
+
<pre><tt>plane_tbl.drop_column(:speed)</tt></pre>
|
1857
|
+
</div></div>
|
1858
|
+
<p>This method allows you to remove a column from an existing table. You must
|
1859
|
+
specify the column name to be removed.</p>
|
1860
|
+
<div class="admonitionblock">
|
1861
|
+
<table><tr>
|
1862
|
+
<td class="icon">
|
1863
|
+
<img src="./images/important.png" alt="Important" />
|
1864
|
+
</td>
|
1865
|
+
<td class="content">You cannot drop the :recno column.</td>
|
1866
|
+
</tr></table>
|
1867
|
+
</div>
|
1868
|
+
<div class="admonitionblock">
|
1869
|
+
<table><tr>
|
1870
|
+
<td class="icon">
|
1871
|
+
<img src="./images/important.png" alt="Important" />
|
1872
|
+
</td>
|
1873
|
+
<td class="content">Because #drop_column changes the structure of a table, you can
|
1874
|
+
only call this method when connect_type==:local.</td>
|
1875
|
+
</tr></table>
|
1876
|
+
</div>
|
1877
|
+
</div></div>
|
1878
|
+
<div class="sidebarblock">
|
1879
|
+
<a id="rename-column"></a>
|
1880
|
+
<div class="sidebar-content">
|
1881
|
+
<div class="sidebar-title">KBTable#rename_column</div>
|
1882
|
+
<div class="listingblock">
|
1883
|
+
<div class="content">
|
1884
|
+
<pre><tt>plane_tbl.rename_column(:speed, :maximum_speed)</tt></pre>
|
1885
|
+
</div></div>
|
1886
|
+
<p>This method allows you to rename a column in an existing table. You must
|
1887
|
+
specify the column to be renamed and a new column name.</p>
|
1888
|
+
<div class="admonitionblock">
|
1889
|
+
<table><tr>
|
1890
|
+
<td class="icon">
|
1891
|
+
<img src="./images/important.png" alt="Important" />
|
1892
|
+
</td>
|
1893
|
+
<td class="content">You cannot rename the :recno column.</td>
|
1894
|
+
</tr></table>
|
1895
|
+
</div>
|
1896
|
+
<div class="admonitionblock">
|
1897
|
+
<table><tr>
|
1898
|
+
<td class="icon">
|
1899
|
+
<img src="./images/important.png" alt="Important" />
|
1900
|
+
</td>
|
1901
|
+
<td class="content">Because #rename_column changes the structure of a table, you can
|
1902
|
+
only call this method when connect_type==:local.</td>
|
1903
|
+
</tr></table>
|
1904
|
+
</div>
|
1905
|
+
</div></div>
|
1906
|
+
<div class="sidebarblock">
|
1907
|
+
<a id="change-column-type"></a>
|
1908
|
+
<div class="sidebar-content">
|
1909
|
+
<div class="sidebar-title">KBTable#change_column_type</div>
|
1910
|
+
<div class="listingblock">
|
1911
|
+
<div class="content">
|
1912
|
+
<pre><tt>plane_tbl.change_column_type(:weight, :Float)</tt></pre>
|
1913
|
+
</div></div>
|
1914
|
+
<p>This method allows you to change a column's type in an existing table. You
|
1915
|
+
must specify the column name and a new column type.</p>
|
1916
|
+
<div class="admonitionblock">
|
1917
|
+
<table><tr>
|
1918
|
+
<td class="icon">
|
1919
|
+
<img src="./images/important.png" alt="Important" />
|
1920
|
+
</td>
|
1921
|
+
<td class="content">You cannot change the type of the :recno column.</td>
|
1922
|
+
</tr></table>
|
1923
|
+
</div>
|
1924
|
+
<div class="admonitionblock">
|
1925
|
+
<table><tr>
|
1926
|
+
<td class="icon">
|
1927
|
+
<img src="./images/important.png" alt="Important" />
|
1928
|
+
</td>
|
1929
|
+
<td class="content">Because #change_column_type changes the structure of a table, you
|
1930
|
+
can only call this method when connect_type==:local.</td>
|
1931
|
+
</tr></table>
|
1932
|
+
</div>
|
1933
|
+
</div></div>
|
1934
|
+
<div class="sidebarblock">
|
1935
|
+
<a id="change-column-default-value"></a>
|
1936
|
+
<div class="sidebar-content">
|
1937
|
+
<div class="sidebar-title">KBTable#change_column_default_value</div>
|
1938
|
+
<div class="listingblock">
|
1939
|
+
<div class="content">
|
1940
|
+
<pre><tt>plane_tbl.change_column_default_value(:country, 'United States')</tt></pre>
|
1941
|
+
</div></div>
|
1942
|
+
<p>This method allows you to change a column's default value in an existing
|
1943
|
+
table. You must specify the column name and a default value. If the
|
1944
|
+
default value is equal to nil, this, in effect will make the column not have
|
1945
|
+
a default value any more.</p>
|
1946
|
+
<div class="admonitionblock">
|
1947
|
+
<table><tr>
|
1948
|
+
<td class="icon">
|
1949
|
+
<img src="./images/important.png" alt="Important" />
|
1950
|
+
</td>
|
1951
|
+
<td class="content">Since the :recno column cannot have a default value, you cannot
|
1952
|
+
change the default value of the :recno column.</td>
|
1953
|
+
</tr></table>
|
1954
|
+
</div>
|
1955
|
+
<div class="admonitionblock">
|
1956
|
+
<table><tr>
|
1957
|
+
<td class="icon">
|
1958
|
+
<img src="./images/important.png" alt="Important" />
|
1959
|
+
</td>
|
1960
|
+
<td class="content">Because #change_column_default_value changes the structure of a
|
1961
|
+
table, you can only call this method when connect_type==:local.</td>
|
1962
|
+
</tr></table>
|
1963
|
+
</div>
|
1964
|
+
</div></div>
|
1965
|
+
<div class="sidebarblock">
|
1966
|
+
<a id="change-column-required"></a>
|
1967
|
+
<div class="sidebar-content">
|
1968
|
+
<div class="sidebar-title">KBTable#change_column_required</div>
|
1969
|
+
<div class="listingblock">
|
1970
|
+
<div class="content">
|
1971
|
+
<pre><tt>plane_tbl.change_column_required(:country, true)</tt></pre>
|
1972
|
+
</div></div>
|
1973
|
+
<p>This method allows you to change whether a value for a column is required or
|
1974
|
+
not. You must specify the column name and either true or false for the
|
1975
|
+
required argument.</p>
|
1976
|
+
<div class="admonitionblock">
|
1977
|
+
<table><tr>
|
1978
|
+
<td class="icon">
|
1979
|
+
<img src="./images/important.png" alt="Important" />
|
1980
|
+
</td>
|
1981
|
+
<td class="content">You cannot specify whether the :recno column is required or not.</td>
|
1982
|
+
</tr></table>
|
1983
|
+
</div>
|
1984
|
+
<div class="admonitionblock">
|
1985
|
+
<table><tr>
|
1986
|
+
<td class="icon">
|
1987
|
+
<img src="./images/important.png" alt="Important" />
|
1988
|
+
</td>
|
1989
|
+
<td class="content">Because #change_column_required changes the structure of a table,
|
1990
|
+
you can only call this method when connect_type==:local.</td>
|
1991
|
+
</tr></table>
|
1992
|
+
</div>
|
1993
|
+
</div></div>
|
1994
|
+
<div class="sidebarblock">
|
1995
|
+
<a id="add-index"></a>
|
1996
|
+
<div class="sidebar-content">
|
1997
|
+
<div class="sidebar-title">KBTable#add_index</div>
|
1998
|
+
<div class="listingblock">
|
1999
|
+
<div class="content">
|
2000
|
+
<pre><tt>plane_tbl.add_index(:name, :country)</tt></pre>
|
2001
|
+
</div></div>
|
2002
|
+
<p>This method allows you to add an index to an existing table. This index can
|
2003
|
+
consist of one or more columns. You must specify one or more column names
|
2004
|
+
that you want to make up the index.</p>
|
2005
|
+
<div class="admonitionblock">
|
2006
|
+
<table><tr>
|
2007
|
+
<td class="icon">
|
2008
|
+
<img src="./images/important.png" alt="Important" />
|
2009
|
+
</td>
|
2010
|
+
<td class="content">Because #add_index changes the structure of a table, you can
|
2011
|
+
only call this method when connect_type==:local.</td>
|
2012
|
+
</tr></table>
|
2013
|
+
</div>
|
2014
|
+
</div></div>
|
2015
|
+
<div class="sidebarblock">
|
2016
|
+
<a id="drop-index"></a>
|
2017
|
+
<div class="sidebar-content">
|
2018
|
+
<div class="sidebar-title">KBTable#drop_index</div>
|
2019
|
+
<div class="listingblock">
|
2020
|
+
<div class="content">
|
2021
|
+
<pre><tt>plane_tbl.drop_index(:name, :country)</tt></pre>
|
2022
|
+
</div></div>
|
2023
|
+
<p>This method allows you to drop an index from an existing table. You must
|
2024
|
+
specify one or more column names that make up the index that you want to
|
2025
|
+
drop.</p>
|
2026
|
+
<div class="admonitionblock">
|
2027
|
+
<table><tr>
|
2028
|
+
<td class="icon">
|
2029
|
+
<img src="./images/important.png" alt="Important" />
|
2030
|
+
</td>
|
2031
|
+
<td class="content">Because #drop_index changes the structure of a table, you can
|
2032
|
+
only call this method when connect_type==:local.</td>
|
2033
|
+
</tr></table>
|
2034
|
+
</div>
|
2035
|
+
</div></div>
|
2036
|
+
</div>
|
2037
|
+
<h2><a id="special-characters-in-data"></a>Special characters in data</h2>
|
2038
|
+
<div class="sectionbody">
|
2039
|
+
<p>Since KirbyBase tables are just plain-text, newline-delimited files with
|
2040
|
+
each field delimited by a <em>|</em>, certain ASCII characters could cause problems
|
2041
|
+
when used as input. For example, entering a newline character (\n on Unix,
|
2042
|
+
\r\n on Windows) as part of a record's data would cause problems later when
|
2043
|
+
KirbyBase attempted to read this record. Likewise, using the <em>|</em> character
|
2044
|
+
in your data would also cause problems as KirbyBase uses this character as a
|
2045
|
+
field delimiter. Finally, it turns out that Python has problems handling
|
2046
|
+
octal code \032 under Windows (possibly because it equates to Ctrl-Z), so
|
2047
|
+
to keep compatibility between the Ruby and Python versions of KirbyBase,
|
2048
|
+
this issue needs to be handled.</p>
|
2049
|
+
<p>To handle the above special characters as data input, KirbyBase checks all
|
2050
|
+
:String and :YAML input data and replaces the special characters with
|
2051
|
+
encodings that are safe. The following table shows how replacements are
|
2052
|
+
done:</p>
|
2053
|
+
<div class="tableblock">
|
2054
|
+
<table rules="all"
|
2055
|
+
frame="border"
|
2056
|
+
cellspacing="0" cellpadding="4">
|
2057
|
+
<col width="182" />
|
2058
|
+
<col width="240" />
|
2059
|
+
<thead>
|
2060
|
+
<tr>
|
2061
|
+
<th align="left">
|
2062
|
+
Input Character
|
2063
|
+
</th>
|
2064
|
+
<th align="left">
|
2065
|
+
KirbyBase Replacement
|
2066
|
+
</th>
|
2067
|
+
</tr>
|
2068
|
+
</thead>
|
2069
|
+
<tbody valign="top">
|
2070
|
+
<tr>
|
2071
|
+
<td align="left">
|
2072
|
+
\n
|
2073
|
+
</td>
|
2074
|
+
<td align="left">
|
2075
|
+
&amp;linefeed;
|
2076
|
+
</td>
|
2077
|
+
</tr>
|
2078
|
+
<tr>
|
2079
|
+
<td align="left">
|
2080
|
+
\r
|
2081
|
+
</td>
|
2082
|
+
<td align="left">
|
2083
|
+
&amp;carriage_return;
|
2084
|
+
</td>
|
2085
|
+
</tr>
|
2086
|
+
<tr>
|
2087
|
+
<td align="left">
|
2088
|
+
\032
|
2089
|
+
</td>
|
2090
|
+
<td align="left">
|
2091
|
+
&amp;substitute;
|
2092
|
+
</td>
|
2093
|
+
</tr>
|
2094
|
+
<tr>
|
2095
|
+
<td align="left">
|
2096
|
+
|
|
2097
|
+
</td>
|
2098
|
+
<td align="left">
|
2099
|
+
&amp;pipe;
|
2100
|
+
</td>
|
2101
|
+
</tr>
|
2102
|
+
<tr>
|
2103
|
+
<td align="left">
|
2104
|
+
&
|
2105
|
+
</td>
|
2106
|
+
<td align="left">
|
2107
|
+
&amp;
|
2108
|
+
</td>
|
2109
|
+
</tr>
|
2110
|
+
</tbody>
|
2111
|
+
</table>
|
2112
|
+
</div>
|
2113
|
+
<p>KirbyBase will translate to and from the special characters as data is
|
2114
|
+
written to and read from a table. It should all be transparent to the user.
|
2115
|
+
The only time you would encounter the replacement words is if you were to
|
2116
|
+
open up the physical table file in a text editor or read it in as input
|
2117
|
+
outside of KirbyBase.</p>
|
2118
|
+
</div>
|
2119
|
+
<h2><a id="table-structure"></a>Table Structure</h2>
|
2120
|
+
<div class="sectionbody">
|
2121
|
+
<p>Every table in KirbyBase is represented by a physical, newline-delimited
|
2122
|
+
text-file. Here is an example:</p>
|
2123
|
+
<div class="listingblock">
|
2124
|
+
<div class="content">
|
2125
|
+
<pre><tt>000006|000000|Struct|recno:Integer|name:String|country:String|speed:Integer
|
2126
|
+
1|P-51|USA|403
|
2127
|
+
2|P-51|USA|365
|
2128
|
+
3|Sptitfire|England|345
|
2129
|
+
4|Oscar|Japan|361
|
2130
|
+
5|ME-109|Germany|366
|
2131
|
+
6|Zero|Japan|377</tt></pre>
|
2132
|
+
</div></div>
|
2133
|
+
<p>The first line is the header rec. Each field is delimited by a "|". The
|
2134
|
+
first field in the header is the record counter. It is incremented by
|
2135
|
+
KirbyBase to create new record numbers when records are inserted.</p>
|
2136
|
+
<p>The second field in the header is the deleted-records counter. Every time a
|
2137
|
+
line in the file is blanked-out (see "The pack method"), this number is
|
2138
|
+
incremented. You can use this field in a maintenance script so that the
|
2139
|
+
table is packed whenever the deleted-records counter reaches, say, 5,000
|
2140
|
+
records.</p>
|
2141
|
+
<p>The third field in the header is the record_class field. If you specified a
|
2142
|
+
class when you created the table, it's name will show up here and records
|
2143
|
+
returned from a #select will be instances of this class. The default is
|
2144
|
+
"Struct".</p>
|
2145
|
+
<p>The fourth field in the header is the :recno field. This field is
|
2146
|
+
automatically added to the table when it is created. The field name and
|
2147
|
+
field type are separated by a ":".</p>
|
2148
|
+
<p>The rest of the fields are whatever you specified when you created the
|
2149
|
+
table.</p>
|
2150
|
+
<p>If there is a Z in the first position of the header rec and the rest of the
|
2151
|
+
file is a bunch of random characters, this means that the table is
|
2152
|
+
encrypted.</p>
|
2153
|
+
<p>Each record after the header record is simply a line of text. Newline
|
2154
|
+
characters delimit records.</p>
|
2155
|
+
</div>
|
2156
|
+
<h2><a id="server-notes"></a>Server Notes</h2>
|
2157
|
+
<div class="sectionbody">
|
2158
|
+
<p>There is a server script included in the distribution called kbserver.rb.
|
2159
|
+
This script uses DRb to turn KirbyBase into a client/server, multi-user
|
2160
|
+
dbms. This dbms server handles table locking for you so you don't have to
|
2161
|
+
worry about it.</p>
|
2162
|
+
</div>
|
2163
|
+
<h2><a id="tips-on-improving-performance"></a>Tips on improving performance</h2>
|
2164
|
+
<div class="sectionbody">
|
2165
|
+
<h3>Beware of Date/DateTime</h3>
|
2166
|
+
<p>Converting a String (the format in which data is stored in a KirbyBase
|
2167
|
+
table) to a Date/DateTime is slow. If you have a large table with a
|
2168
|
+
Date/DateTime field, this can result in long query times.</p>
|
2169
|
+
<p>To get around this, you can specify the field type as a :String, instead of
|
2170
|
+
a :Date/:DateTime. Queries will still work correctly, because Date/DateTime
|
2171
|
+
fields that are in String format sort the same way they would if they were
|
2172
|
+
in native format. Here's an example. The following two expressions will
|
2173
|
+
result in the same result set being returned:</p>
|
2174
|
+
<div class="listingblock">
|
2175
|
+
<div class="content">
|
2176
|
+
<pre><tt>date_field <= Date.new(2005, 05, 11)</tt></pre>
|
2177
|
+
</div></div>
|
2178
|
+
<p>and</p>
|
2179
|
+
<div class="listingblock">
|
2180
|
+
<div class="content">
|
2181
|
+
<pre><tt>date_field_stored_as_string_field <= "2005-05-11"</tt></pre>
|
2182
|
+
</div></div>
|
2183
|
+
<h3>Create indexes on large tables</h3>
|
2184
|
+
<p>The larger a table becomes, the more sense it makes to create an index on
|
2185
|
+
one or more of it's fields. Even though you take a hit when KirbyBase first
|
2186
|
+
initializes because it has to create the index arrays, you make up for it
|
2187
|
+
after that in greatly improved query speeds. I have noticed speed-ups of
|
2188
|
+
as much as 10x on large tables.</p>
|
2189
|
+
<h3>Create indexes on foreign keys</h3>
|
2190
|
+
<p>If you have a Link_many on a table field, you might want to think about
|
2191
|
+
creating an index on the field in the child table that is being linked to.
|
2192
|
+
When performing a one-to-many link, KirbyBase will automatically take
|
2193
|
+
advantage of an index on the link field in the child table.</p>
|
2194
|
+
<p>By the way, the same holds true for Lookups.</p>
|
2195
|
+
<h3>When possible, always search by :recno</h3>
|
2196
|
+
<p>This might be a no-brainer, but, if you have the chance to select by
|
2197
|
+
:recno, use the built-in #select_by_recno_index method (or the #[] method).
|
2198
|
+
This is even faster than selecting on a regularly indexed field, because the
|
2199
|
+
:recno index that KirbyBase creates for each table is actually a Hash, not
|
2200
|
+
an Array like all of the regular indexes. So selects are even faster.</p>
|
2201
|
+
</div>
|
2202
|
+
<h2><a id="client-server-diagram"></a>Client/Server memory space diagram</h2>
|
2203
|
+
<div class="sectionbody">
|
2204
|
+
<div class="imageblock">
|
2205
|
+
<div class="content">
|
2206
|
+
<img src="images/client_server.png" alt="images/client_server.png"/>
|
2207
|
+
</div>
|
2208
|
+
<div class="image-title">Figure: Client/Server (separate memory spaces) mode.</div>
|
2209
|
+
</div>
|
2210
|
+
</div>
|
2211
|
+
<h2><a id="single-user-diagram"></a>Single-user memory space diagram</h2>
|
2212
|
+
<div class="sectionbody">
|
2213
|
+
<div class="imageblock">
|
2214
|
+
<div class="content">
|
2215
|
+
<img src="images/single_user.png" alt="images/single_user.png"/>
|
2216
|
+
</div>
|
2217
|
+
<div class="image-title">Figure: Single-user (embedded) mode.</div>
|
2218
|
+
</div>
|
2219
|
+
</div>
|
2220
|
+
<h2><a id="license"></a>License</h2>
|
2221
|
+
<div class="sectionbody">
|
2222
|
+
<p>KirbyBase is distributed under the same license terms as Ruby.</p>
|
2223
|
+
</div>
|
2224
|
+
<h2><a id="credits"></a>Credits</h2>
|
2225
|
+
<div class="sectionbody">
|
2226
|
+
<div class="admonitionblock">
|
2227
|
+
<table><tr>
|
2228
|
+
<td class="icon">
|
2229
|
+
<img src="./images/note.png" alt="Note" />
|
2230
|
+
</td>
|
2231
|
+
<td class="content">This manual was produced using the awesome text document formatter,
|
2232
|
+
<a href="http://www.methods.co.nz/asciidoc/">AsciiDoc</a>.</td>
|
2233
|
+
</tr></table>
|
2234
|
+
</div>
|
2235
|
+
</div>
|
2236
|
+
<div id="footer">
|
2237
|
+
<div id="footer-text">
|
2238
|
+
Version 2.5<br />
|
2239
|
+
Last updated 01-Dec-2005 13:28:12 Eastern Daylight Time
|
2240
|
+
</div>
|
2241
|
+
</div>
|
2242
|
+
</body>
|
2243
|
+
</html>
|