KirbyBase 2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/README +73 -0
  2. data/bin/kbserver.rb +20 -0
  3. data/changes.txt +105 -0
  4. data/examples/aaa_try_this_first/kbtest.rb +207 -0
  5. data/examples/add_column_test/add_column_test.rb +27 -0
  6. data/examples/calculated_field_test/calculated_field_test.rb +51 -0
  7. data/examples/change_column_type_test/change_column_type_test.rb +25 -0
  8. data/examples/column_required_test/column_required_test.rb +33 -0
  9. data/examples/crosstab_test/crosstab_test.rb +100 -0
  10. data/examples/csv_import_test/csv_import_test.rb +31 -0
  11. data/examples/csv_import_test/plane.csv +11 -0
  12. data/examples/default_value_test/default_value_test.rb +43 -0
  13. data/examples/drop_column_test/drop_column_test.rb +24 -0
  14. data/examples/indexes_test/add_index_test.rb +46 -0
  15. data/examples/indexes_test/drop_index_test.rb +66 -0
  16. data/examples/indexes_test/index_test.rb +71 -0
  17. data/examples/kbserver_as_win32_service/kbserver_daemon.rb +47 -0
  18. data/examples/kbserver_as_win32_service/kbserverctl.rb +75 -0
  19. data/examples/link_many_test/link_many_test.rb +70 -0
  20. data/examples/lookup_field_test/lookup_field_test.rb +55 -0
  21. data/examples/lookup_field_test/lookup_field_test_2.rb +62 -0
  22. data/examples/lookup_field_test/the_hal_fulton_feature_test.rb +69 -0
  23. data/examples/many_to_many_test/many_to_many_test.rb +65 -0
  24. data/examples/memo_test/memo_test.rb +63 -0
  25. data/examples/memo_test/memos/blank.txt +0 -0
  26. data/examples/record_class_test/record_class_test.rb +77 -0
  27. data/examples/rename_column_test/rename_column_test.rb +46 -0
  28. data/examples/rename_table_test/rename_table_test.rb +38 -0
  29. data/examples/yaml_field_test/yaml_field_test.rb +47 -0
  30. data/images/blank.png +0 -0
  31. data/images/callouts/1.png +0 -0
  32. data/images/callouts/10.png +0 -0
  33. data/images/callouts/11.png +0 -0
  34. data/images/callouts/12.png +0 -0
  35. data/images/callouts/13.png +0 -0
  36. data/images/callouts/14.png +0 -0
  37. data/images/callouts/15.png +0 -0
  38. data/images/callouts/2.png +0 -0
  39. data/images/callouts/3.png +0 -0
  40. data/images/callouts/4.png +0 -0
  41. data/images/callouts/5.png +0 -0
  42. data/images/callouts/6.png +0 -0
  43. data/images/callouts/7.png +0 -0
  44. data/images/callouts/8.png +0 -0
  45. data/images/callouts/9.png +0 -0
  46. data/images/caution.png +0 -0
  47. data/images/client_server.png +0 -0
  48. data/images/example.png +0 -0
  49. data/images/home.png +0 -0
  50. data/images/important.png +0 -0
  51. data/images/kirby1.jpg +0 -0
  52. data/images/next.png +0 -0
  53. data/images/note.png +0 -0
  54. data/images/prev.png +0 -0
  55. data/images/single_user.png +0 -0
  56. data/images/smallnew.png +0 -0
  57. data/images/tip.png +0 -0
  58. data/images/toc-blank.png +0 -0
  59. data/images/toc-minus.png +0 -0
  60. data/images/toc-plus.png +0 -0
  61. data/images/up.png +0 -0
  62. data/images/warning.png +0 -0
  63. data/kirbybaserubymanual.html +2243 -0
  64. data/lib/kirbybase.rb +3662 -0
  65. metadata +126 -0
@@ -0,0 +1,25 @@
1
+ # This script is an example of how to change a column type.
2
+ #
3
+ require 'kirbybase'
4
+
5
+ db = KirbyBase.new
6
+
7
+ # If table exists, delete it.
8
+ db.drop_table(:log) if db.table_exists?(:log)
9
+
10
+ log_tbl = db.create_table(:log, :log_timestamp, :DateTime, :msg, :String)
11
+
12
+ log_tbl.insert(DateTime.now, 'This is a log message')
13
+ log_tbl.insert(DateTime.now, 'This is a another log message')
14
+ log_tbl.insert(DateTime.now, 'This is the final log message')
15
+
16
+ p log_tbl.select
17
+ puts;puts
18
+
19
+ log_tbl.change_column_type(:log_timestamp, :String)
20
+
21
+ p log_tbl.select
22
+ puts;puts
23
+
24
+ p log_tbl.field_types
25
+
@@ -0,0 +1,33 @@
1
+ # This script is an example of how to specify that a value is required for a
2
+ # column.
3
+ #
4
+ require 'kirbybase'
5
+
6
+ db = KirbyBase.new
7
+
8
+ # If table exists, delete it.
9
+ db.drop_table(:address_book) if db.table_exists?(:address_book)
10
+
11
+ # Create a table. Notice how we specify a default value for :category.
12
+ address_book_tbl = db.create_table(:address_book,
13
+ :firstname, :String, :lastname, :String, :street_address, :String,
14
+ :city, :String, :phone, :String,
15
+ :category, {:DataType=>:String, :Required=>true})
16
+
17
+ begin
18
+ # Insert a record. Notice that I am passing nil for :category. This
19
+ # will cause KirbyBase to raise an exception.
20
+ address_book_tbl.insert('Bruce', 'Wayne', '1234 Bat Cave',
21
+ 'Gotham City', '111-111-1111', nil)
22
+ rescue StandardError => e
23
+ puts e
24
+ puts;puts
25
+ end
26
+
27
+ # Now, let's turn off the required flag for :category.
28
+ address_book_tbl.change_column_required(:category, false)
29
+
30
+ # And we will attempt to add the record again.
31
+ address_book_tbl.insert('Bruce', 'Wayne', '1234 Bat Cave',
32
+ 'Gotham City', '111-111-1111', nil)
33
+
@@ -0,0 +1,100 @@
1
+ # This script demonstrates how to use crosstab functionality of a
2
+ # KirbyBase result set. A KirbyBase result set automatically has an
3
+ # equivalent transposed array whereby all of the values of a column are
4
+ # available. I call this a crosstab, but I am probably using this term
5
+ # incorrectly. Perhaps the examples below will help explain what I am
6
+ # talking about.
7
+
8
+ # In this example, we have an order table and an order_item table. Each
9
+ # record in the order table represents a customer order. The order_item
10
+ # table holds the detail items for each order. We create a one-to-many link
11
+ # between the order table and the order_item table by providing extra
12
+ # information about the order.items field when we create the order table.
13
+
14
+ require 'kirbybase'
15
+
16
+ db = KirbyBase.new
17
+
18
+ # To run as a client in a multi-user environment, uncomment next line.
19
+ # Also, make sure kbserver.rb is running.
20
+ #db = KirbyBase.new do |d|
21
+ # d.connect_type = :client
22
+ # d.host = 'localhost'
23
+ # d.port = 44444
24
+ #end
25
+
26
+ # If tables exists, delete it.
27
+ db.drop_table(:order) if db.table_exists?(:order)
28
+ db.drop_table(:order_item) if db.table_exists?(:order_item)
29
+
30
+ # Create an order item table. This is the child table to the order table.
31
+ # Make sure you create the child table BEFORE you create the parent table
32
+ # so that KirbyBase can take advantage of any indexes that you have defined.
33
+ order_item_tbl = db.create_table(:order_item,
34
+ :item_id, :Integer,
35
+ :order_id, :Integer,
36
+ :descr, :String,
37
+ :qty, :Integer,
38
+ :price, :Float,
39
+ :total, {:DataType=>:Float, :Calculated=>'qty*price'}
40
+ )
41
+
42
+ # Create an order table. We are telling KirbyBase that the items field is
43
+ # to be linked to the order_item table by comparing the order.order_id
44
+ # field to the order_item.order_id field. By specifying :Link_many, we are
45
+ # telling KirbyBase to make this a one-to-many link. The result of this is
46
+ # that when you do a select, the items field of the order table is going to
47
+ # hold a reference to a ResultSet (i.e. Array) holding all order_item
48
+ # records whose order_id field match the order_id field in the order record.
49
+ order_tbl = db.create_table(:order,
50
+ :order_id, :Integer,
51
+ :customer, :String,
52
+ :items, {:DataType=> :ResultSet, :Link_many=> [:order_id, :order_item,
53
+ :order_id]}
54
+ )
55
+
56
+ # Insert some order records.
57
+ order_tbl.insert({:order_id=>345, :customer=>'Ford'})
58
+ order_tbl.insert({:order_id=>454, :customer=>'Microsoft'})
59
+ order_tbl.insert({:order_id=>17, :customer=>'Boeing'})
60
+
61
+ # Insert some order item records.
62
+ order_item_tbl.insert(1,345,'Steel',30,19.99,nil)
63
+ order_item_tbl.insert(2,345,'Glass',5,4.15,nil)
64
+ order_item_tbl.insert(5,454,'Floppies',750000,0.5,nil)
65
+ order_item_tbl.insert(3,17,'Wheels',200,2500.0,nil)
66
+ order_item_tbl.insert(4,17,'Wings',25,1000000.0,nil)
67
+
68
+
69
+ # Print all orders. Under each order print all items in that order. Notice
70
+ # that we are able to print the total for each order because we have access
71
+ # to the entire order_items.total column of the result set. We don't have
72
+ # to loop through all of the order item result set records to add up the
73
+ # total for each order.
74
+ puts "\nPrint all orders:\n"
75
+ order_tbl.select.each do |r|
76
+ puts "\nid: %3d customer: %-10s total charge: %11.2f" % [r.order_id,
77
+ r.customer, r.items.total.inject { |sum, n| sum + n }]
78
+
79
+ r.items.each do |i|
80
+ puts "\titem: %-10s %6d * %10.2f = %11.2f" % [i.descr,
81
+ i.qty, i.price, i.total]
82
+ end
83
+ end
84
+ puts '-' * 70;puts
85
+
86
+ # You can even use the ability to access an entire column of values in your
87
+ # select statements. In this example, we only want to select those orders
88
+ # whose total charges exceeds $100,000. We can do this because we have
89
+ # access to the entire total column of the child table, order items.
90
+ puts "Print only orders whose total charge exceeds $100,000:\n"
91
+ order_tbl.select { |r| r.items.total.inject { |sum, n| sum+n } > 100000
92
+ }.each do |r|
93
+ puts "\nid: %3d customer: %-10s total charge: %11.2f" % [r.order_id,
94
+ r.customer, r.items.total.inject { |sum, n| sum + n }]
95
+
96
+ r.items.each do |i|
97
+ puts "\titem: %-10s %6d * %10.2f = %11.2f" % [i.descr,
98
+ i.qty, i.price, i.total]
99
+ end
100
+ end
@@ -0,0 +1,31 @@
1
+ #Test of CSV file import.
2
+
3
+ require 'kirbybase'
4
+ require 'date'
5
+
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(:plane) if db.table_exists?(:plane)
18
+
19
+ # Create a table.
20
+ plane_tbl = db.create_table(:plane, :name, :String, :country, :String,
21
+ :role, :String, :speed, :Integer, :range, :Integer, :began_service, :Date,
22
+ :still_flying, :Boolean)
23
+
24
+ # Import csv file.
25
+ puts 'Records imported: %d' % plane_tbl.import_csv('plane.csv')
26
+
27
+ puts
28
+
29
+ # Now, lets show that the csv file did, in fact, get imported.
30
+ puts plane_tbl.select(:name, :country, :role, :speed, :range).sort(:name
31
+ ).to_report
@@ -0,0 +1,11 @@
1
+ FW-190,Germany,Fighter,399,499,1942-12-01,false
2
+ P-51,USA,Fighter,405,1210,1943-06-24,true
3
+ P-47,USA,Fighter,365,888,1943-03-12,false
4
+ B-17,USA,Bomber,315,1400,1937-05-01,true
5
+ Typhoon,Great Britain,Fighter-Bomber,389,690,1944-11-20,false
6
+ Spitfire,Great Britain,Fighter,345,540,1939-02-18,true
7
+ Oscar,Japan,Fighter,361,777,1943-12-31,false
8
+ ME-109,Germany,Fighter,366,514,1936-07-07,true
9
+ JU-88,Germany,Bomber,289,999,1937-01-19,false
10
+ P-39,USA,Fighter,,,,false
11
+ Zero,Japan,Fighter,377,912,1937-05-15,true
@@ -0,0 +1,43 @@
1
+ # This script is an example of how to specify a default value for 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
+ # Create a table. Notice how we specify a default value for :category.
11
+ address_book_tbl = db.create_table(:address_book,
12
+ :firstname, :String, :lastname, :String, :street_address, :String,
13
+ :city, :String, :phone, :String,
14
+ :category, {:DataType=>:String, :Default=>'Super Hero'})
15
+
16
+ # Insert a record. Notice that I am passing nil for :category. KirbyBase
17
+ # will insert the default value, 'Super Hero', in that field.
18
+ address_book_tbl.insert('Bruce', 'Wayne', '1234 Bat Cave', 'Gotham City',
19
+ '111-111-1111', nil)
20
+
21
+ # Insert another record. Here we supply the value for :category, so
22
+ # KirbyBase will use it instead of the default.
23
+ address_book_tbl.insert('Bugs', 'Bunny', '1234 Rabbit Hole', 'The Forest',
24
+ '222-222-2222', 'Cartoon Character')
25
+
26
+ # Now lets change the default value for :category to 'President'.
27
+ address_book_tbl.change_column_default_value(:category, 'President')
28
+
29
+ # And let's add another record without supplying a value for :category.
30
+ address_book_tbl.insert(firstname='George', lastname='Bush',
31
+ street_address='1600 Pennsylvania Ave', 'Washington', '333-333-3333', nil)
32
+
33
+ # Now, let's remove the default value for :category
34
+ address_book_tbl.change_column_default_value(:category, nil)
35
+
36
+ # And add another record. We won't specify a value for :category and,
37
+ # KirbyBase will not use a default value, because we removed it.
38
+ address_book_tbl.insert('Silver', 'Surfer', '1234 Galaxy Way',
39
+ 'Any City', '444-444-4444', nil)
40
+
41
+ # Now lets print the table out and you will see how all of the defaults
42
+ # worked.
43
+ puts address_book_tbl.select.to_report
@@ -0,0 +1,24 @@
1
+ #Test of drop_column method.
2
+
3
+ require 'kirbybase'
4
+
5
+ db = KirbyBase.new
6
+
7
+ # If table exists, delete it.
8
+ db.drop_table(:plane) if db.table_exists?(:plane)
9
+
10
+ # Create a table.
11
+ plane_tbl = db.create_table(:plane, :name, :String, :speed, :Integer,
12
+ :service_date, :Date, :still_flying, :Boolean)
13
+
14
+ # Insert a bunch more records so we can have some "select" fun below.
15
+ plane_tbl.insert('Spitfire', 345, Date.new(1939,2,18), true)
16
+ plane_tbl.insert('Oscar', 361, Date.new(1943,12,31), false)
17
+ plane_tbl.insert('ME-109', 366, Date.new(1936,7,7),true)
18
+ plane_tbl.insert('JU-88', 289, Date.new(1937,1,19), false)
19
+ plane_tbl.insert('P-39', nil, nil, false)
20
+ plane_tbl.insert('Zero', 377, Date.new(1937,5,15), true)
21
+
22
+ plane_tbl.drop_column(:speed)
23
+
24
+ puts plane_tbl.select.to_report
@@ -0,0 +1,46 @@
1
+ # This script is an example of how to add an index to an existing table.
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
+ # Insert some contact info records.
15
+ address_book_tbl.insert('Bruce', 'Wayne', '1234 Bat Cave', 'Gotham City',
16
+ '111-111-1111', 'Super Hero')
17
+ address_book_tbl.insert('Bugs', 'Bunny', '1234 Rabbit Hole', 'The Forest',
18
+ '222-222-2222', 'Cartoon Character')
19
+ address_book_tbl.insert('George', 'Bush', '1600 Pennsylvania Ave',
20
+ 'Washington', '333-333-3333', 'President')
21
+ address_book_tbl.insert('Silver', 'Surfer', '1234 Galaxy Way',
22
+ 'Any City', '444-444-4444', 'Super Hero')
23
+
24
+ # Select all super heros without using the index.
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.add_index(:category)
31
+
32
+ # Now, do the same query, but use the category index. These
33
+ # select_by_index methods are automatically created by KirbyBase when you
34
+ # specify that a column be indexed.
35
+ address_book_tbl.select_by_category_index { |r|
36
+ r.category == 'Super Hero' }.each { |r|
37
+ puts '%s %s %s' % [r.firstname, r.lastname, r.phone]
38
+ }
39
+ puts;puts
40
+
41
+ address_book_tbl.add_index(:firstname, :lastname)
42
+
43
+ # Select Bugs Bunny using the firstname+lastname index.
44
+ address_book_tbl.select_by_firstname_lastname_index { |r|
45
+ r.firstname == 'Bugs' and r.lastname == 'Bunny'
46
+ }.each { |r| puts '%s %s %s' % [r.firstname, r.lastname, r.phone] }
@@ -0,0 +1,66 @@
1
+ # This script is an example of how to drop an index from an existing table.
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, {:DataType=>:String, :Index=>1},
12
+ :lastname, {:DataType=>:String, :Index=>1},
13
+ :street_address, :String,
14
+ :city, :String,
15
+ :phone, :String,
16
+ :category, {:DataType=>:String, :Index=>2}
17
+ )
18
+
19
+ # Insert some contact info records.
20
+ address_book_tbl.insert('Bruce', 'Wayne', '1234 Bat Cave', 'Gotham City',
21
+ '111-111-1111', 'Super Hero')
22
+ address_book_tbl.insert('Bugs', 'Bunny', '1234 Rabbit Hole', 'The Forest',
23
+ '222-222-2222', 'Cartoon Character')
24
+ address_book_tbl.insert('George', 'Bush', '1600 Pennsylvania Ave',
25
+ 'Washington', '333-333-3333', 'President')
26
+ address_book_tbl.insert('Silver', 'Surfer', '1234 Galaxy Way',
27
+ 'Any City', '444-444-4444', 'Super Hero')
28
+
29
+ p address_book_tbl.field_indexes
30
+ puts;puts
31
+
32
+ address_book_tbl.select_by_category_index { |r|
33
+ r.category == 'Super Hero' }.each { |r|
34
+ puts '%s %s %s' % [r.firstname, r.lastname, r.phone]
35
+ }
36
+ puts;puts
37
+
38
+ address_book_tbl.drop_index(:category)
39
+
40
+ begin
41
+ address_book_tbl.select_by_category_index { |r|
42
+ r.category == 'Super Hero' }.each { |r|
43
+ puts '%s %s %s' % [r.firstname, r.lastname, r.phone]
44
+ }
45
+ rescue StandardError => e
46
+ puts e
47
+ puts;puts
48
+ end
49
+
50
+ # Select Bugs Bunny using the firstname+lastname index.
51
+ address_book_tbl.select_by_firstname_lastname_index { |r|
52
+ r.firstname == 'Bugs' and r.lastname == 'Bunny'
53
+ }.each { |r| puts '%s %s %s' % [r.firstname, r.lastname, r.phone] }
54
+
55
+ address_book_tbl.drop_index(:firstname, :lastname)
56
+
57
+ begin
58
+ address_book_tbl.select_by_firstname_lastname_index { |r|
59
+ r.firstname == 'Bugs' and r.lastname == 'Bunny'
60
+ }.each { |r| puts '%s %s %s' % [r.firstname, r.lastname, r.phone] }
61
+ rescue StandardError => e
62
+ puts e
63
+ puts;puts
64
+ end
65
+
66
+ p address_book_tbl.field_indexes
@@ -0,0 +1,71 @@
1
+ # This script is an example of how to use indexes in KirbyBase. Indexes
2
+ # allow for faster queries on large datasets.
3
+ #
4
+ # To use indexes, you must first specify which fields are to be indexed.
5
+ # You do this at table creation time. Both single and compound indexes can
6
+ # be created. Then, when performing a select query, you simply use the
7
+ # automatically created select_by_?????_index method, where ????? is
8
+ # replaced by the name(s) of the indexed field(s). That's it. Everything
9
+ # else concerning building and maintaing indexes is done by KirbyBase.
10
+ require 'kirbybase'
11
+
12
+ db = KirbyBase.new
13
+
14
+ # To run as a client in a multi-user environment, uncomment next line.
15
+ # Also, make sure kbserver.rb is running.
16
+ #db = KirbyBase.new do |d|
17
+ # d.connect_type = :client
18
+ # d.host = 'localhost'
19
+ # d.port = 44444
20
+ #end
21
+
22
+ # If table exists, delete it.
23
+ db.drop_table(:address_book) if db.table_exists?(:address_book)
24
+
25
+ # Here we are creating a table to hold contact info. We are going to create
26
+ # two indexes. One index is going to be a compound index containing the
27
+ # firstname and lastname fields. Notice how we group the firstname and
28
+ # lastname fields into one index by specifying :Index=>1 for both of them.
29
+ # This just tells KirbyBase that the two fields should be combined into one
30
+ # compound index because they both are using the same index number. The
31
+ # second index is going to be a single index containing the category field.
32
+ # Since we want it to be a separate index, we simply use the next available
33
+ # number, 2, as the value of it's :Index key.
34
+ address_book_tbl = db.create_table(:address_book,
35
+ :firstname, {:DataType=>:String, :Index=>1},
36
+ :lastname, {:DataType=>:String, :Index=>1},
37
+ :street_address, :String,
38
+ :city, :String,
39
+ :phone, :String,
40
+ :category, {:DataType=>:String, :Index=>2}
41
+ )
42
+
43
+ # Insert some contact info records.
44
+ address_book_tbl.insert('Bruce', 'Wayne', '1234 Bat Cave', 'Gotham City',
45
+ '111-111-1111', 'Super Hero')
46
+ address_book_tbl.insert('Bugs', 'Bunny', '1234 Rabbit Hole', 'The Forest',
47
+ '222-222-2222', 'Cartoon Character')
48
+ address_book_tbl.insert('George', 'Bush', '1600 Pennsylvania Ave',
49
+ 'Washington', '333-333-3333', 'President')
50
+ address_book_tbl.insert('Silver', 'Surfer', '1234 Galaxy Way',
51
+ 'Any City', '444-444-4444', 'Super Hero')
52
+
53
+ # Select all super heros without using the index.
54
+ address_book_tbl.select { |r| r.category == 'Super Hero' }.each { |r|
55
+ puts '%s %s %s' % [r.firstname, r.lastname, r.phone]
56
+ }
57
+ puts;puts
58
+ # Now, do the same query, but use the category index. These
59
+ # select_by_index methods are automatically created by KirbyBase, based on
60
+ # the indexes you specified at table creation.
61
+ address_book_tbl.select_by_category_index { |r|
62
+ r.category == 'Super Hero' }.each { |r|
63
+ puts '%s %s %s' % [r.firstname, r.lastname, r.phone]
64
+ }
65
+ puts;puts
66
+ # Select Bugs Bunny using the firstname+lastname index.
67
+ address_book_tbl.select_by_firstname_lastname_index { |r|
68
+ r.firstname == 'Bugs' and r.lastname == 'Bunny'
69
+ }.each { |r| puts '%s %s %s' % [r.firstname, r.lastname, r.phone] }
70
+
71
+ p address_book_tbl.field_indexes