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