KirbyBase 2.5.2 → 2.6

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,4 +1,4 @@
1
- = KirbyBase 2.5.2
1
+ = KirbyBase 2.6
2
2
 
3
3
  A small, plain-text, dbms written in Ruby. It can be used either embedded
4
4
  or client/server.
@@ -31,12 +31,6 @@ See the examples directory for examples of how to use KirbyBase.
31
31
  * images directory - images used in manual.
32
32
 
33
33
 
34
- == Warning
35
-
36
- KirbyBase defines #method_missing for NilClass. This might bite you in the
37
- butt if you override NilClass.method_missing yourself.
38
-
39
-
40
34
  == Author
41
35
 
42
36
  Written in 2005 by Jamey Cribbs <mailto:jcribbs@twmi.rr.com>
@@ -66,7 +60,7 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
66
60
  Please send any bug reports, suggestions, ideas,
67
61
  improvements, to:
68
62
 
69
- jcribbs@twmi.rr.com
63
+ jcribbs@netpromi.com
70
64
 
71
65
  == Home Page
72
66
 
@@ -1,3 +1,18 @@
1
+ 2006-06-27:: Version 2.6
2
+ * Removed NilClass#method_missing. I have replaced it with a new class
3
+ called KBNilClass. Thanks to a host of people for help on this,
4
+ including: Assaph Mehr, James Edward Gray II, Matthew Desmarais,
5
+ Logan Capaldo, Trans, John Carter, Dave Burt and anyone else I missed.
6
+ * Added conditional require logic so that KirbyBase will use FasterCVS if
7
+ it is available. Thanks to James Edward Gray II for this.
8
+ * You can now delay index creation in local mode. Thanks to Nicholas Rahn
9
+ for this.
10
+ * Added ability to allow for a custom record class with no kb_create or
11
+ kb_defaults methods. KirbyBase will return each result record as an
12
+ instance of the custom record class, and will attempt to set attributes
13
+ with the same names as the table's field names equal to the values of
14
+ the table record's fields. Thanks to Hal Fulton for this idea.
15
+
1
16
  2005-12-30:: Version 2.5.2
2
17
  * Changed the behavior of KBTable#insert method. If user specifies nil
3
18
  for a field value and there is a default value for that field, the
@@ -83,6 +83,7 @@ plane_tbl.insert('P-39', 'USA', 'Fighter', nil, nil,
83
83
  nil, false)
84
84
  plane_tbl.insert('Zero', 'Japan', 'Fighter', 377, 912,
85
85
  Date.new(1937,5,15), true)
86
+ plane_tbl.insert('B-25', 'USA', '', 320, 1340, Date.new(1940,4,4), true)
86
87
 
87
88
  #--------------------- Update Examples -------------------------------------
88
89
  # Four different ways to update existing data in KirbyBase. In all three
@@ -195,6 +196,37 @@ puts plane_tbl.select { |r|
195
196
  r.country == plane_tbl.select { |x| x.name == 'Zero' }.first.country
196
197
  }
197
198
 
199
+ #-------------------------- Select Example 11 -------------------------------
200
+ print_divider('Select Example 11')
201
+ # Select all planes.
202
+ plane_tbl.select.each { |r| puts r }
203
+
204
+ #-------------------------- Select Example 12 -------------------------------
205
+ print_divider('Select Example 12')
206
+ # Select all planes with a speed of nil.
207
+
208
+ #**************** Note: This example also demonstrates the change from
209
+ # nil to kb_nil for KirbyBase's internal representation of a nil value. You
210
+ # should only encounter this different if you have to check for nil in your
211
+ # query, as this example does. Other than that, everything else should
212
+ # be transparent, since KirbyBase converts a kb_nil back into a nil when it
213
+ # returns a query's result set.
214
+ #***************
215
+ plane_tbl.select { |r| r.speed.kb_nil? }.each { |r| puts r }
216
+
217
+ #-------------------------- Select Example 13 -------------------------------
218
+ print_divider('Select Example 13')
219
+ # Same thing, but in a slightly different way.
220
+
221
+ #**************** Note: This example also demonstrates the change from
222
+ # nil to kb_nil for KirbyBase's internal representation of a nil value. You
223
+ # should only encounter this different if you have to check for nil in your
224
+ # query, as this example does. Other than that, everything else should
225
+ # be transparent, since KirbyBase converts a kb_nil back into a nil when it
226
+ # returns a query's result set.
227
+ #***************
228
+ plane_tbl.select { |r| r.speed == kb_nil }.each { |r| puts r }
229
+
198
230
  #-------------------------- Misc. Methods Examples -------------------------
199
231
  print_divider('Misc. Methods Examples')
200
232
  puts 'Total # of records in table: %d' % plane_tbl.total_recs
@@ -37,18 +37,23 @@ address_book_tbl = db.create_table(:address_book,
37
37
  :street_address, :String,
38
38
  :city, :String,
39
39
  :phone, :String,
40
- :category, {:DataType=>:String, :Index=>2}
40
+ :category, {:DataType=>:String, :Index=>2},
41
+ :age, {:DataType=>:Integer, :Index=>3}
41
42
  )
42
43
 
43
44
  # Insert some contact info records.
44
45
  address_book_tbl.insert('Bruce', 'Wayne', '1234 Bat Cave', 'Gotham City',
45
- '111-111-1111', 'Super Hero')
46
+ '111-111-1111', 'Super Hero', 39)
46
47
  address_book_tbl.insert('Bugs', 'Bunny', '1234 Rabbit Hole', 'The Forest',
47
- '222-222-2222', 'Cartoon Character')
48
+ '222-222-2222', 'Cartoon Character', 12)
48
49
  address_book_tbl.insert('George', 'Bush', '1600 Pennsylvania Ave',
49
- 'Washington', '333-333-3333', 'President')
50
+ 'Washington', '333-333-3333', 'President', 2)
50
51
  address_book_tbl.insert('Silver', 'Surfer', '1234 Galaxy Way',
51
- 'Any City', '444-444-4444', 'Super Hero')
52
+ 'Any City', '444-444-4444', 'Super Hero', 199)
53
+ address_book_tbl.insert('John', 'Doe', '1234 Robin Lane', 'Detroit',
54
+ '999-999-9999', nil, 54)
55
+ address_book_tbl.insert(nil, 'NoFirstName', 'Nowhere Road', 'Notown',
56
+ '000-000-0000', 'Nothing', nil)
52
57
 
53
58
  # Select all super heros without using the index.
54
59
  address_book_tbl.select { |r| r.category == 'Super Hero' }.each { |r|
@@ -68,4 +73,22 @@ address_book_tbl.select_by_firstname_lastname_index { |r|
68
73
  r.firstname == 'Bugs' and r.lastname == 'Bunny'
69
74
  }.each { |r| puts '%s %s %s' % [r.firstname, r.lastname, r.phone] }
70
75
 
76
+ puts;puts
77
+ # Select the guy with no first name using the firstname+lastname index.
78
+ address_book_tbl.select_by_firstname_lastname_index { |r|
79
+ r.lastname == 'NoFirstName'
80
+ }.each { |r| puts '%s %s %s' % [r.firstname, r.lastname, r.phone] }
81
+
82
+ puts;puts
83
+ # Select the guy with no first name using the firstname+lastname index.
84
+ # This time we will explicitly say firstname should be nil
85
+ address_book_tbl.select_by_firstname_lastname_index { |r|
86
+ r.firstname.nil? and r.lastname == 'NoFirstName'
87
+ }.each { |r| puts '%s %s %s' % [r.firstname, r.lastname, r.phone] }
88
+
89
+ puts;puts
90
+ address_book_tbl.select_by_age_index { |r| r.age > 30 }.each { |r|
91
+ puts '%s %s %d' % [r.firstname, r.lastname, r.age] }
92
+
93
+ puts;puts
71
94
  p address_book_tbl.field_indexes
@@ -0,0 +1,38 @@
1
+ #Test of returning result set composed of instances of user class. This
2
+ #example differs from the first in that there is no kb_create method, so
3
+ #KirbyBase will default to create a new instance of the record's custom
4
+ #class and setting each instance attribute to each field's value.
5
+
6
+ require 'kirbybase'
7
+
8
+ class Foobar
9
+ attr_accessor(:recno, :name, :country, :role, :speed, :range,
10
+ :began_service, :still_flying)
11
+ end
12
+
13
+ # To run local, single-user, uncomment next line.
14
+ db = KirbyBase.new
15
+
16
+ # If table exists, delete it.
17
+ db.drop_table(:plane) if db.table_exists?(:plane)
18
+
19
+ # Create a table. Notice how you set record_class equal to your class.
20
+ plane_tbl = db.create_table do |t|
21
+ t.name = :plane
22
+ t.field_defs = [:name, :String, :country, :String, :role, :String,
23
+ :speed, :Integer, :range, :Integer, :began_service, :Date,
24
+ :still_flying, :Boolean]
25
+ t.encrypt = false
26
+ t.record_class = Foobar
27
+ end
28
+
29
+ plane_tbl = db.get_table(:plane)
30
+
31
+ # Insert a record.
32
+ plane_tbl.insert('Spitfire','Great Britain','Fighter',333,454,
33
+ Date.new(1936, 1, 1),true)
34
+
35
+ # Notice how select returns instances of Foobar, even there is no
36
+ # kb_create method.
37
+ recs = plane_tbl.select
38
+ p recs.first
@@ -25,7 +25,7 @@ pet_tbl.insert('Kirby', 'dog', Date.new(2002, 06, 01),
25
25
  pet_tbl.insert('Mojo', 'cat', Date.new(2000, 04, 01),
26
26
  ['cute', 'soft', '6', 12.25])
27
27
  pet_tbl.insert('Goldy', 'fish', Date.new(2004, 10, 10),
28
- nil)
28
+ [])
29
29
 
30
30
  pet_tbl.select.each { |r|
31
31
  puts '%s %s' % [r.name, r.characteristics[1]]
@@ -261,8 +261,8 @@ div.exampleblock-content {
261
261
  <div id="header">
262
262
  <h1>KirbyBase Manual (Ruby Version)</h1>
263
263
  <span id="author">Jamey Cribbs</span><br />
264
- <span id="email"><tt>&lt;<a href="mailto:jcribbs@twmi.rr.com">jcribbs@twmi.rr.com</a>&gt;</tt></span><br />
265
- v2.5.2 December 2005
264
+ <span id="email"><tt>&lt;<a href="mailto:jcribbs@netpromi.com">jcribbs@netpromi.com</a>&gt;</tt></span><br />
265
+ v2.6 June 2006
266
266
  </div>
267
267
  <div id="preamble">
268
268
  <div class="sectionbody">
@@ -270,7 +270,7 @@ v2.5.2 December 2005
270
270
  <div class="content">
271
271
  <img src="images/kirby1.jpg" alt="images/kirby1.jpg"/>
272
272
  </div>
273
- <div class="image-title">Figure: Kirby pictured here in attack mode.</div>
273
+ <div class="image-title">Figure: Kirby, pictured here in attack mode.</div>
274
274
  </div>
275
275
  </div>
276
276
  </div>
@@ -386,6 +386,11 @@ v2.5.2 December 2005
386
386
  <a href="#selects-involving-lookups-or-link-manys">Selects involving Lookups or Link_manys</a>
387
387
  </p>
388
388
  </li>
389
+ <li>
390
+ <p>
391
+ <a href="#a-note-about-nil-values">A note about nil values</a>
392
+ </p>
393
+ </li>
389
394
  </ol>
390
395
  </li>
391
396
  <li>
@@ -708,9 +713,10 @@ MySQL.</p>
708
713
  <div class="sidebarblock">
709
714
  <div class="sidebar-content">
710
715
  <div class="sidebar-title">Drop me a line!</div>
711
- <p>If you find a use for KirbyBase, please send an email
712
- telling me how you use it. I find that hearing how people have
713
- put KirbyBase to use is one of the biggest rewards I get for working on it.</p>
716
+ <p>If you find a use for KirbyBase, please send me an email telling how you
717
+ use it, whether it is ok for me to mention your application on the
718
+ "KirbyBase Applications" webpage, and possibly a link to your application's
719
+ webpage (if you have one).</p>
714
720
  </div></div>
715
721
  </div>
716
722
  <h2><a id="connecting-to-a-database"></a>Connecting to a database</h2>
@@ -750,14 +756,14 @@ a different extension, pass this as an argument, like so:</p>
750
756
  memo/blob files, you need to pass the location as an argument, like so:</p>
751
757
  <div class="listingblock">
752
758
  <div class="content">
753
- <pre><tt>db = KirbyBase.new(:local, nil, './', '.tbl', './memos')</tt></pre>
759
+ <pre><tt>db = KirbyBase.new(:local, nil, nil, './', '.tbl', './memos')</tt></pre>
754
760
  </div></div>
755
761
  <p>If you don't want KirbyBase to spend time initially creating all of the
756
762
  indexes for the tables in the database, you can pass true as the
757
763
  delay_index_creation argument:</p>
758
764
  <div class="listingblock">
759
765
  <div class="content">
760
- <pre><tt>db = KirbyBase.new(:local, nil, './', '.tbl', './', true)</tt></pre>
766
+ <pre><tt>db = KirbyBase.new(:local, nil, nil, './', '.tbl', './', true)</tt></pre>
761
767
  </div></div>
762
768
  <p>KirbyBase will skip initial index creation and will create a table's
763
769
  indexes when the table is first referenced.</p>
@@ -828,8 +834,12 @@ end</tt></pre>
828
834
  <p>You can also specify that you want records of the table to be returned to
829
835
  you as instances of a class. To do this, simply define a class before you
830
836
  call #create_table. This class needs to have an accessor for each fieldname
831
- in the table. Also, this class needs to have a class method, called
832
- #kb_create, that returns a new class instance. Here is an example class:</p>
837
+ in the table.</p>
838
+ <p>If this class has a class method, called #kb_create, KirbyBase, when
839
+ creating each record of the result set, will call that method and pass it
840
+ the field values of the result record. #kb_create will need to handle
841
+ creating an instance of the record class itself.</p>
842
+ <p>Here is an example of #kb_create in action:</p>
833
843
  <div class="listingblock">
834
844
  <div class="content">
835
845
  <pre><tt>class Foobar
@@ -870,6 +880,8 @@ end</tt></pre>
870
880
  Foobar, instead of the default, which is instances of Struct. This also
871
881
  works the other way. You can now specify instances of Foobar as input to
872
882
  the #insert, #update and #set methods. More on those methods below.</p>
883
+ <p>If the custom record class does not respond to #kb_create, KirbyBase will
884
+ call the class's #new method instead, passing it all of the field values.</p>
873
885
  <div class="sidebarblock">
874
886
  <div class="sidebar-content">
875
887
  <div class="admonitionblock">
@@ -1372,6 +1384,35 @@ result.items.each { |item| puts '%d %d' % [item.item_no, item.qty] }</tt></pre>
1372
1384
  <p>Notice how the items attribute in the ResultSet is itself a ResultSet
1373
1385
  containing all of the :order_items records that belong to the selected
1374
1386
  order.</p>
1387
+ <h3><a id="a-note-about-nil-values"></a>A Note About nil Values</h3>
1388
+ <p>Beginning in version 2.6 of KirbyBase, nil values are no longer stored as
1389
+ the singleton instance of NilClass in the database. Now, they are stored
1390
+ as references to the singleton instance, kb_nil, of KBNilClass. KBNilClass
1391
+ is as similar to NilClass as possible, but since NilClass cannot
1392
+ be sub-classed, there are a few minor differences.</p>
1393
+ <p>However, this should all be transparent to you because KirbyBase converts
1394
+ kb_nil values to Ruby nil values before returning the results of a query.
1395
+ The only time you will need to be worried about kb_nil is when you need to
1396
+ explicitly test for a nil value in a query. For example:</p>
1397
+ <div class="listingblock">
1398
+ <div class="content">
1399
+ <pre><tt>plane_tbl.select {|r| r.speed == kb_nil}</tt></pre>
1400
+ </div></div>
1401
+ <p>which is the same as:</p>
1402
+ <div class="listingblock">
1403
+ <div class="content">
1404
+ <pre><tt>plane_tbl.select {|r| r.speed.kb_nil?}</tt></pre>
1405
+ </div></div>
1406
+ <p>Notice how it is very similar to how you would test for nil?</p>
1407
+ <p>The only other difference you will now notice, is if you open up a table in
1408
+ a text editor. Now, nil values are stored as "kb_nil", instead of being
1409
+ stored as an empty string (i.e. ""). This has the added advantage that
1410
+ KirbyBase can now distinguish between empty strings and nil values. In the
1411
+ past, if you saved an empty string as a field value, the next time you
1412
+ selected that record, KirbyBase would return that field's value as nil.</p>
1413
+ <p>The main reason for making this change was to eliminate the need to
1414
+ override NilClass#method_missing, which was cause for concern for some
1415
+ users.</p>
1375
1416
  </div>
1376
1417
  <h2><a id="kbresultset"></a>KBResultSet</h2>
1377
1418
  <div class="sectionbody">
@@ -1677,6 +1718,14 @@ lines:</p>
1677
1718
  <div class="content">
1678
1719
  <pre><tt>result = plane_tbl.pack</tt></pre>
1679
1720
  </div></div>
1721
+ <div class="admonitionblock">
1722
+ <table><tr>
1723
+ <td class="icon">
1724
+ <img src="./images/important.png" alt="Important" />
1725
+ </td>
1726
+ <td class="content">You can only call this method when connect_type==:local.</td>
1727
+ </tr></table>
1728
+ </div>
1680
1729
  <div class="sidebarblock">
1681
1730
  <div class="sidebar-content">
1682
1731
  <div class="admonitionblock">
@@ -1854,6 +1903,8 @@ than one recno.</p>
1854
1903
  KirbyBase will attempt to convert the values in the csv file into their
1855
1904
  corresponding KirbyBase field types, based upon the field types you
1856
1905
  designated when you created the table.</p>
1906
+ <p>If you have FasterCSV installed KirbyBase will use it instead of CSV from
1907
+ the standard library.</p>
1857
1908
  <p>Returns an Integer specifying the total number of records imported.</p>
1858
1909
  </div></div>
1859
1910
  <div class="sidebarblock">
@@ -2165,10 +2216,10 @@ text-file. Here is an example:</p>
2165
2216
  first field in the header is the record counter. It is incremented by
2166
2217
  KirbyBase to create new record numbers when records are inserted.</p>
2167
2218
  <p>The second field in the header is the deleted-records counter. Every time a
2168
- line in the file is blanked-out (see "The pack method"), this number is
2169
- incremented. You can use this field in a maintenance script so that the
2170
- table is packed whenever the deleted-records counter reaches, say, 5,000
2171
- records.</p>
2219
+ line in the file is blanked-out (see <a href="#pack-method">The pack method</a>), this
2220
+ number is incremented. You can use this field in a maintenance script so
2221
+ that the table is packed whenever the deleted-records counter reaches, say,
2222
+ 5,000 records.</p>
2172
2223
  <p>The third field in the header is the record_class field. If you specified a
2173
2224
  class when you created the table, its name will show up here and records
2174
2225
  returned from a #select will be instances of this class. The default is
@@ -2266,7 +2317,7 @@ an Array like all of the regular indexes. So selects are even faster.</p>
2266
2317
  </div>
2267
2318
  <div id="footer">
2268
2319
  <div id="footer-text">
2269
- Last updated 30-Dec-2005 10:16:41 Eastern Daylight Time
2320
+ Last updated 26-Jun-2006 14:36:38 EDT
2270
2321
  </div>
2271
2322
  </div>
2272
2323
  </body>
@@ -1,9 +1,14 @@
1
1
  require 'date'
2
2
  require 'time'
3
3
  require 'drb'
4
- require 'csv'
5
4
  require 'fileutils'
6
5
  require 'yaml'
6
+
7
+ begin # first choice--for speed
8
+ require 'faster_csv'
9
+ rescue LoadError # second choice--slower but standard
10
+ require 'csv'
11
+ end
7
12
 
8
13
  #
9
14
  # :main:KirbyBase
@@ -152,11 +157,32 @@ require 'yaml'
152
157
  # field, the default value will no longer override the user specified nil
153
158
  # value. Thanks to Assaph Mehr for suggesting this.
154
159
  #
160
+ # 2006-06-27:: Version 2.6
161
+ # * Removed NilClass#method_missing. I have replaced it with a new class
162
+ # called KBNilClass. Thanks to a host of people for help on this,
163
+ # including: Assaph Mehr, James Edward Gray II, Matthew Desmarais,
164
+ # Logan Capaldo, Trans, John Carter, Dave Burt and anyone else I missed.
165
+ # * Added conditional require logic so that KirbyBase will use FasterCVS if
166
+ # it is available. Thanks to James Edward Gray II for this.
167
+ # * You can now delay index creation in local mode. Thanks to Nicholas Rahn
168
+ # for this.
169
+ # * Added ability to allow for a custom record class with no kb_create or
170
+ # kb_defaults methods. KirbyBase will return each result record as an
171
+ # instance of the custom record class, and will attempt to set attributes
172
+ # with the same names as the table's field names equal to the values of
173
+ # the table record's fields. Thanks to Hal Fulton for this idea.
155
174
  #
156
175
  #---------------------------------------------------------------------------
157
176
  # KBTypeConversionsMixin
158
177
  #---------------------------------------------------------------------------
159
178
  module KBTypeConversionsMixin
179
+ # Constant that will represent a kb_nil in the physical table file.
180
+ # If you think you might need to write the value 'kb_nil' to a field
181
+ # yourself, comment out the following line and un-comment the line
182
+ # below that to use an alternative representation for kb_nil.
183
+ KB_NIL = 'kb_nil'
184
+ #KB_NIL = '&kb_nil;'
185
+
160
186
  # Regular expression used to determine if field needs to be un-encoded.
161
187
  UNENCODE_RE = /&(?:amp|linefeed|carriage_return|substitute|pipe);/
162
188
 
@@ -170,7 +196,11 @@ module KBTypeConversionsMixin
170
196
  # Return value converted from storage string to native field type.
171
197
  #
172
198
  def convert_to_native_type(data_type, s)
173
- return nil if s.empty? or s.nil?
199
+ return kb_nil if s == KB_NIL
200
+
201
+ # I added this line to keep KBTable#import_csv working after I made
202
+ # the kb_nil changes.
203
+ return nil if s.nil?
174
204
 
175
205
  case data_type
176
206
  when :String
@@ -236,6 +266,8 @@ module KBTypeConversionsMixin
236
266
  # Return value converted to encoded String object suitable for storage.
237
267
  #
238
268
  def convert_to_encoded_string(data_type, value)
269
+ return KB_NIL if value.nil?
270
+
239
271
  case data_type
240
272
  when :YAML
241
273
  y = value.to_yaml
@@ -342,7 +374,7 @@ class KirbyBase
342
374
  include DRb::DRbUndumped
343
375
  include KBTypeConversionsMixin
344
376
 
345
- VERSION = "2.5.2"
377
+ VERSION = "2.6"
346
378
 
347
379
  attr_reader :engine
348
380
 
@@ -422,13 +454,9 @@ class KirbyBase
422
454
  # happens when the table instance is first created, I go ahead and
423
455
  # create table instances right off the bat.
424
456
  #
425
- # Also, I let the server create table instances also. This is
426
- # strictly to get the indexes created, there is no other use for
427
- # the table instances on the server side as they will never be used.
428
- # Everything should and does go through the table instances created
429
- # on the client-side. You can turn this off by specifying true for
430
- # the delay_index_creation argument.
431
- if server? and @delay_index_creation
457
+ # You can delay index creation until the first time the index is
458
+ # used.
459
+ if @delay_index_creation
432
460
  else
433
461
  @engine.tables.each do |tbl|
434
462
  @table_hash[tbl] = \
@@ -2121,11 +2149,7 @@ class KBTable
2121
2149
 
2122
2150
  new_recno = @db.engine.insert_record(self, @field_names.zip(
2123
2151
  @field_types).collect do |fn, ft|
2124
- if input_rec[fn].nil?
2125
- ''
2126
- else
2127
- convert_to_encoded_string(ft, input_rec[fn])
2128
- end
2152
+ convert_to_encoded_string(ft, input_rec[fn])
2129
2153
  end)
2130
2154
 
2131
2155
  # If there are any associated memo/blob fields, save their values.
@@ -2751,7 +2775,9 @@ class KBTable
2751
2775
  records_inserted = 0
2752
2776
  tbl_rec = @table_class.new(self)
2753
2777
 
2754
- CSV.open(csv_filename, 'r') do |row|
2778
+ # read with FasterCSV if loaded, or the standard CSV otherwise
2779
+ (defined?(FasterCSV) ? FasterCSV : CSV).foreach(csv_filename
2780
+ ) do |row|
2755
2781
  tbl_rec.populate([nil] + row)
2756
2782
  insert(tbl_rec)
2757
2783
  records_inserted += 1
@@ -2923,12 +2949,12 @@ class KBTable
2923
2949
 
2924
2950
  get_meth_upd_res_str = <<-END_OF_STRING
2925
2951
  def #{field_name}_upd_res
2926
- return nil
2952
+ return kb_nil
2927
2953
  end
2928
2954
  END_OF_STRING
2929
2955
  set_meth_str = <<-END_OF_STRING
2930
2956
  def #{field_name}=(s)
2931
- @#{field_name} = nil
2957
+ @#{field_name} = kb_nil
2932
2958
  end
2933
2959
  END_OF_STRING
2934
2960
  end
@@ -2944,12 +2970,12 @@ class KBTable
2944
2970
  END_OF_STRING
2945
2971
  get_meth_upd_res_str = <<-END_OF_STRING
2946
2972
  def #{field_name}_upd_res()
2947
- return nil
2973
+ return kb_nil
2948
2974
  end
2949
2975
  END_OF_STRING
2950
2976
  set_meth_str = <<-END_OF_STRING
2951
2977
  def #{field_name}=(s)
2952
- @#{field_name} = nil
2978
+ @#{field_name} = kb_nil
2953
2979
  end
2954
2980
  END_OF_STRING
2955
2981
  end
@@ -3081,9 +3107,17 @@ class KBTable
3081
3107
  # Read header record and update instance variables.
3082
3108
  #
3083
3109
  def update_header_vars
3084
- @encrypted, @last_rec_no, @del_ctr, @record_class, @field_names, \
3085
- @field_types, @field_indexes, @field_defaults, @field_requireds, \
3086
- @field_extras = @db.engine.get_header_vars(self)
3110
+ @encrypted, @last_rec_no, @del_ctr, @record_class, @col_names, \
3111
+ @col_types, @col_indexes, @col_defaults, @col_requireds, \
3112
+ @col_extras = @db.engine.get_header_vars(self)
3113
+
3114
+ # These are deprecated.
3115
+ @field_names = @col_names
3116
+ @field_types = @col_types
3117
+ @field_indexes = @col_indexes
3118
+ @field_defaults = @col_defaults
3119
+ @field_requireds = @col_requireds
3120
+ @field_extras = @col_extras
3087
3121
  end
3088
3122
 
3089
3123
  #-----------------------------------------------------------------------
@@ -3118,30 +3152,51 @@ class KBTable
3118
3152
  result_rec = result_struct.new(*filter.collect { |f|
3119
3153
  tbl_rec.send("#{f}_upd_res".to_sym) })
3120
3154
  elsif @record_class == 'Struct'
3121
- result_rec = result_struct.new(*filter.collect { |f|
3122
- tbl_rec.send(f) })
3155
+ result_rec = result_struct.new(*filter.collect do |f|
3156
+ if tbl_rec.send(f).kb_nil?
3157
+ nil
3158
+ else
3159
+ tbl_rec.send(f)
3160
+ end
3161
+ end)
3123
3162
  else
3124
3163
  if Object.full_const_get(@record_class).respond_to?(:kb_create)
3125
3164
  result_rec = Object.full_const_get(@record_class
3126
- ).kb_create(*@field_names.collect { |f|
3127
- # Just a warning here: If you specify a filter on
3128
- # a select, you are only going to get those fields
3129
- # you specified in the result set, EVEN IF
3130
- # record_class is a custom class instead of Struct.
3131
- if filter.include?(f)
3132
- tbl_rec.send(f)
3133
- else
3134
- nil
3135
- end
3136
- })
3165
+ ).kb_create(*@field_names.collect do |f|
3166
+ # Just a warning here: If you specify a filter on
3167
+ # a select, you are only going to get those fields
3168
+ # you specified in the result set, EVEN IF
3169
+ # record_class is a custom class instead of Struct.
3170
+ if filter.include?(f)
3171
+ if tbl_rec.send(f).kb_nil?
3172
+ nil
3173
+ else
3174
+ tbl_rec.send(f)
3175
+ end
3176
+ else
3177
+ nil
3178
+ end
3179
+ end)
3137
3180
  elsif Object.full_const_get(@record_class).respond_to?(
3138
3181
  :kb_defaults)
3139
3182
  result_rec = Object.full_const_get(@record_class).new(
3140
- *@field_names.collect { |f|
3141
- tbl_rec.send(f) || Object.full_const_get(
3142
- @record_class).kb_defaults[@field_names.index(f)]
3143
- }
3144
- )
3183
+ *@field_names.collect do |f|
3184
+ if tbl_rec.send(f).kb_nil?
3185
+ nil
3186
+ else
3187
+ tbl_rec.send(f) || Object.full_const_get(
3188
+ @record_class).kb_defaults[@field_names.index(f)]
3189
+ end
3190
+ end)
3191
+ else
3192
+ result_rec = Object.full_const_get(@record_class).allocate
3193
+ @field_names.each do |fn|
3194
+ if tbl_rec.send(fn).kb_nil?
3195
+ result_rec.send("#{fn}=", nil)
3196
+ else
3197
+ result_rec.send("#{fn}=", tbl_rec.send(fn))
3198
+ end
3199
+ end
3145
3200
  end
3146
3201
  end
3147
3202
 
@@ -3413,30 +3468,12 @@ class KBIndex
3413
3468
  # Split the line up into fields.
3414
3469
  rec = line.split('|', @col_poss.max+2)
3415
3470
 
3416
- # Create the index record by pulling out the record fields
3417
- # that make up this index and converting them to their
3418
- # native types.
3419
- idx_rec = []
3420
- @col_poss.zip(@col_types).each do |col_pos, col_type|
3421
- idx_rec << convert_to_native_type(col_type,
3422
- rec[col_pos])
3423
- end
3424
-
3425
- # Were all the index fields for this record equal to NULL?
3426
- # Then don't add this index record to index array; skip to
3427
- # next record.
3428
- next if idx_rec.compact.empty?
3429
-
3430
- # Add recno to the end of this index record.
3431
- idx_rec << rec.first.to_i
3432
-
3433
- # Add index record to index array.
3434
- @idx_arr << idx_rec
3471
+ append_new_rec_to_index_array(rec)
3435
3472
  end
3436
3473
  # Here's how we break out of the loop...
3437
3474
  rescue EOFError
3438
3475
  end
3439
-
3476
+
3440
3477
  @last_update = Time.new
3441
3478
  end
3442
3479
 
@@ -3444,11 +3481,7 @@ class KBIndex
3444
3481
  # add_index_rec
3445
3482
  #-----------------------------------------------------------------------
3446
3483
  def add_index_rec(rec)
3447
- @idx_arr << @col_poss.zip(@col_types).collect do |col_pos, col_type|
3448
- convert_to_native_type(col_type, rec[col_pos])
3449
- end + [rec.first.to_i]
3450
-
3451
- @last_update = Time.new
3484
+ @last_upddate = Time.new if append_new_rec_to_index_array(rec)
3452
3485
  end
3453
3486
 
3454
3487
  #-----------------------------------------------------------------------
@@ -3467,6 +3500,22 @@ class KBIndex
3467
3500
  delete_index_rec(rec.first.to_i)
3468
3501
  add_index_rec(rec)
3469
3502
  end
3503
+
3504
+ #-----------------------------------------------------------------------
3505
+ # append_new_rec_to_index_array
3506
+ #-----------------------------------------------------------------------
3507
+ def append_new_rec_to_index_array(rec)
3508
+ idx_rec = []
3509
+ @col_poss.zip(@col_types).each do |col_pos, col_type|
3510
+ idx_rec << convert_to_native_type(col_type, rec[col_pos])
3511
+ end
3512
+
3513
+ return false if idx_rec.uniq == [kb_nil]
3514
+
3515
+ idx_rec << rec.first.to_i
3516
+ @idx_arr << idx_rec
3517
+ return true
3518
+ end
3470
3519
  end
3471
3520
 
3472
3521
 
@@ -3573,7 +3622,7 @@ class KBTableRec
3573
3622
 
3574
3623
  def clear
3575
3624
  @tbl.field_names.each do |fn|
3576
- send("#{fn}=", nil)
3625
+ send("#{fn}=", kb_nil)
3577
3626
  end
3578
3627
  end
3579
3628
  end
@@ -3680,6 +3729,7 @@ class KBResultSet < Array
3680
3729
  y << b_value
3681
3730
  end
3682
3731
  end
3732
+
3683
3733
  x <=> y
3684
3734
  }
3685
3735
  end
@@ -3737,6 +3787,70 @@ class KBResultSet < Array
3737
3787
  end
3738
3788
 
3739
3789
 
3790
+ #---------------------------------------------------------------------------
3791
+ # KBNilClass
3792
+ #---------------------------------------------------------------------------
3793
+ class KBNilClass
3794
+ include Comparable
3795
+
3796
+ class << self
3797
+ def new
3798
+ @kb_nil ||= KBNilClass.allocate
3799
+ end
3800
+ end
3801
+
3802
+ def inspect
3803
+ 'kb_nil'
3804
+ end
3805
+
3806
+ def kb_nil?
3807
+ true
3808
+ end
3809
+
3810
+ def to_s
3811
+ ""
3812
+ end
3813
+
3814
+ def to_i
3815
+ 0
3816
+ end
3817
+
3818
+ def to_f
3819
+ 0.0
3820
+ end
3821
+
3822
+ def to_a
3823
+ []
3824
+ end
3825
+
3826
+ def <=>(other)
3827
+ return 0 if other.kb_nil?
3828
+ return -1
3829
+ end
3830
+
3831
+ def coerce(other)
3832
+ return [other, to_i] if other.kind_of? Fixnum
3833
+ return [other, to_f] if other.kind_of? Float
3834
+
3835
+ raise "Didn't know how to coerce kb_nil to a #{other.class}"
3836
+ end
3837
+
3838
+ def method_missing(sym, *args)
3839
+ kb_nil
3840
+ end
3841
+ end
3842
+
3843
+
3844
+ #---------------------------------------------------------------------------
3845
+ # Kernel
3846
+ #---------------------------------------------------------------------------
3847
+ module Kernel
3848
+ def kb_nil
3849
+ KBNilClass.new
3850
+ end
3851
+ end
3852
+
3853
+
3740
3854
  #---------------------------------------------------------------------------
3741
3855
  # Object
3742
3856
  #---------------------------------------------------------------------------
@@ -3747,33 +3861,9 @@ class Object
3747
3861
  list.each {|x| obj = obj.const_get(x) }
3748
3862
  obj
3749
3863
  end
3750
- end
3751
3864
 
3752
-
3753
- #---------------------------------------------------------------------------
3754
- # NilClass
3755
- #---------------------------------------------------------------------------
3756
- class NilClass
3757
- #-----------------------------------------------------------------------
3758
- # method_missing
3759
- #-----------------------------------------------------------------------
3760
- #
3761
- # This code is necessary because if, inside a select condition code
3762
- # block, there is a case where you are trying to do an expression
3763
- # against a table field that is equal to nil, I don't want a method
3764
- # missing exception to occur. I just want the expression to be nil. I
3765
- # initially had this method returning false, but then I had an issue
3766
- # where I had a YAML field that was supposed to hold an Array. If the
3767
- # field was empty (i.e. nil) it was actually returning false when it
3768
- # should be returning nil. Since nil evaluates to false, it works if I
3769
- # return nil.
3770
- # Here's an example:
3771
- # #select { |r| r.speed > 300 }
3772
- # What happens if speed is nil (basically NULL in DBMS terms)? Without
3773
- # this code, an exception is going to be raised, which is not what we
3774
- # really want. We really want this expression to return nil.
3775
- def method_missing(method_id, *stuff)
3776
- return nil
3865
+ def kb_nil?
3866
+ false
3777
3867
  end
3778
3868
  end
3779
3869
 
@@ -3802,3 +3892,5 @@ class Symbol
3802
3892
  ("+"+self.to_s).to_sym
3803
3893
  end
3804
3894
  end
3895
+
3896
+
@@ -9,7 +9,7 @@ class TestDatabaseLocal < Test::Unit::TestCase
9
9
  include BaseTest
10
10
 
11
11
  def test_version
12
- assert_equal('2.5.2', KirbyBase::VERSION)
12
+ assert_equal('2.6', KirbyBase::VERSION)
13
13
  end
14
14
 
15
15
  def test_local?
@@ -766,5 +766,38 @@ class TestTableLocal < Test::Unit::TestCase
766
766
  tbl.update(:one => ['two', 3]) { |r| r.recno == 1 }
767
767
  assert_equal(['two', 3], tbl[1].one)
768
768
  end
769
+
770
+ def test_nil_values_001
771
+ tbl = @db.create_table(:nil_tests, :nil_value, :Integer,
772
+ :conditional, :Integer)
773
+ tbl.insert(nil, 100)
774
+
775
+ recs = []
776
+
777
+ assert_nothing_raised {rec = tbl.select {|r| r.nil_value > 100}}
778
+ assert_equal 0, recs.length
779
+
780
+ assert_nothing_raised {recs = tbl.select {|r| r.nil_value > 100 and
781
+ r.conditional > 100}}
782
+ assert_equal 0, recs.length
783
+
784
+ assert_nothing_raised {recs = tbl.select {|r| r.nil_value > 100 or
785
+ r.conditional > 100}}
786
+ assert_equal 0, recs.length
787
+
788
+ assert_nothing_raised {recs = tbl.select {|r| r.nil_value > 100 and
789
+ r.conditional == 100}}
790
+ assert_equal 0, recs.length
791
+
792
+ assert_nothing_raised {recs = tbl.select {|r| r.nil_value > 100 or
793
+ r.conditional == 100}}
794
+ assert_equal 1, recs.length
795
+
796
+ assert_nothing_raised {recs = tbl.select {|r| r.nil_value.kb_nil?}}
797
+ assert_equal 1, recs.length
798
+
799
+ assert_nothing_raised {recs = tbl.select {|r| r.nil_value == kb_nil}}
800
+ assert_equal 1, recs.length
801
+ end
769
802
  end
770
803
 
metadata CHANGED
@@ -1,131 +1,134 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.10
2
+ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: KirbyBase
5
5
  version: !ruby/object:Gem::Version
6
- version: 2.5.2
7
- date: 2006-01-03
8
- summary: "KirbyBase is a simple, pure-Ruby, plain-text, flat-file database management
9
- system."
6
+ version: "2.6"
7
+ date: 2006-06-27 00:00:00 -04:00
8
+ summary: KirbyBase is a simple, pure-Ruby, plain-text, flat-file database management system.
10
9
  require_paths:
11
- - lib
12
- email: jcribbs@twmi.rr.com
10
+ - lib
11
+ email: jcribbs@netpromi.com
13
12
  homepage: http://www.netpromi.com/kirbybase_ruby.html
14
13
  rubyforge_project: kirbybase
15
- description: "KirbyBase is a class that allows you to create and manipulate simple, plain-text
16
- databases. You can use it in either a single-user or client-server mode. You can
17
- select records for retrieval/updating using code blocks."
14
+ description: KirbyBase is a class that allows you to create and manipulate simple, plain-text databases. You can use it in either a single-user or client-server mode. You can select records for retrieval/updating using code blocks.
18
15
  autorequire: kirbybase
19
16
  default_executable:
20
17
  bindir: bin
21
18
  has_rdoc: true
22
19
  required_ruby_version: !ruby/object:Gem::Version::Requirement
23
20
  requirements:
24
- -
25
- - ">"
26
- - !ruby/object:Gem::Version
27
- version: 0.0.0
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
28
24
  version:
29
25
  platform: ruby
26
+ signing_key:
27
+ cert_chain:
30
28
  authors:
31
- - Jamey Cribbs
29
+ - Jamey Cribbs
32
30
  files:
33
- - lib/kirbybase.rb
34
- - bin/kbserver.rb
35
- - README
36
- - changes.txt
37
- - kirbybaserubymanual.html
38
- - test/base_test.rb
39
- - test/tc_local_db.rb
40
- - test/tc_local_table.rb
41
- - test/ts_local.rb
42
- - examples/aaa_try_this_first
43
- - examples/add_column_test
44
- - examples/calculated_field_test
45
- - examples/change_column_type_test
46
- - examples/column_required_test
47
- - examples/crosstab_test
48
- - examples/csv_import_test
49
- - examples/default_value_test
50
- - examples/drop_column_test
51
- - examples/indexes_test
52
- - examples/kbserver_as_win32_service
53
- - examples/link_many_test
54
- - examples/lookup_field_test
55
- - examples/many_to_many_test
56
- - examples/memo_test
57
- - examples/record_class_test
58
- - examples/rename_column_test
59
- - examples/rename_table_test
60
- - examples/yaml_field_test
61
- - examples/aaa_try_this_first/kbtest.rb
62
- - examples/add_column_test/add_column_test.rb
63
- - examples/calculated_field_test/calculated_field_test.rb
64
- - examples/change_column_type_test/change_column_type_test.rb
65
- - examples/column_required_test/column_required_test.rb
66
- - examples/crosstab_test/crosstab_test.rb
67
- - examples/csv_import_test/csv_import_test.rb
68
- - examples/csv_import_test/plane.csv
69
- - examples/default_value_test/default_value_test.rb
70
- - examples/drop_column_test/drop_column_test.rb
71
- - examples/indexes_test/add_index_test.rb
72
- - examples/indexes_test/drop_index_test.rb
73
- - examples/indexes_test/index_test.rb
74
- - examples/kbserver_as_win32_service/kbserverctl.rb
75
- - examples/kbserver_as_win32_service/kbserver_daemon.rb
76
- - examples/link_many_test/link_many_test.rb
77
- - examples/lookup_field_test/lookup_field_test.rb
78
- - examples/lookup_field_test/lookup_field_test_2.rb
79
- - examples/lookup_field_test/the_hal_fulton_feature_test.rb
80
- - examples/many_to_many_test/many_to_many_test.rb
81
- - examples/memo_test/memos
82
- - examples/memo_test/memo_test.rb
83
- - examples/memo_test/memos/blank.txt
84
- - examples/record_class_test/record_class_test.rb
85
- - examples/rename_column_test/rename_column_test.rb
86
- - examples/rename_table_test/rename_table_test.rb
87
- - examples/yaml_field_test/yaml_field_test.rb
88
- - images/blank.png
89
- - images/callouts
90
- - images/caution.png
91
- - images/client_server.png
92
- - images/example.png
93
- - images/home.png
94
- - images/important.png
95
- - images/kirby1.jpg
96
- - images/next.png
97
- - images/note.png
98
- - images/prev.png
99
- - images/single_user.png
100
- - images/smallnew.png
101
- - images/tip.png
102
- - images/toc-blank.png
103
- - images/toc-minus.png
104
- - images/toc-plus.png
105
- - images/up.png
106
- - images/warning.png
107
- - images/callouts/1.png
108
- - images/callouts/10.png
109
- - images/callouts/11.png
110
- - images/callouts/12.png
111
- - images/callouts/13.png
112
- - images/callouts/14.png
113
- - images/callouts/15.png
114
- - images/callouts/2.png
115
- - images/callouts/3.png
116
- - images/callouts/4.png
117
- - images/callouts/5.png
118
- - images/callouts/6.png
119
- - images/callouts/7.png
120
- - images/callouts/8.png
121
- - images/callouts/9.png
31
+ - lib/kirbybase.rb
32
+ - bin/kbserver.rb
33
+ - README
34
+ - changes.txt
35
+ - kirbybaserubymanual.html
36
+ - test/base_test.rb
37
+ - test/tc_local_db.rb
38
+ - test/tc_local_table.rb
39
+ - test/ts_local.rb
40
+ - examples/aaa_try_this_first
41
+ - examples/add_column_test
42
+ - examples/calculated_field_test
43
+ - examples/change_column_type_test
44
+ - examples/column_required_test
45
+ - examples/crosstab_test
46
+ - examples/csv_import_test
47
+ - examples/default_value_test
48
+ - examples/drop_column_test
49
+ - examples/indexes_test
50
+ - examples/kbserver_as_win32_service
51
+ - examples/link_many_test
52
+ - examples/lookup_field_test
53
+ - examples/many_to_many_test
54
+ - examples/memo_test
55
+ - examples/record_class_test
56
+ - examples/rename_column_test
57
+ - examples/rename_table_test
58
+ - examples/yaml_field_test
59
+ - examples/aaa_try_this_first/kbtest.rb
60
+ - examples/add_column_test/add_column_test.rb
61
+ - examples/calculated_field_test/calculated_field_test.rb
62
+ - examples/change_column_type_test/change_column_type_test.rb
63
+ - examples/column_required_test/column_required_test.rb
64
+ - examples/crosstab_test/crosstab_test.rb
65
+ - examples/csv_import_test/csv_import_test.rb
66
+ - examples/csv_import_test/plane.csv
67
+ - examples/default_value_test/default_value_test.rb
68
+ - examples/drop_column_test/drop_column_test.rb
69
+ - examples/indexes_test/add_index_test.rb
70
+ - examples/indexes_test/drop_index_test.rb
71
+ - examples/indexes_test/index_test.rb
72
+ - examples/kbserver_as_win32_service/kbserverctl.rb
73
+ - examples/kbserver_as_win32_service/kbserver_daemon.rb
74
+ - examples/link_many_test/link_many_test.rb
75
+ - examples/lookup_field_test/lookup_field_test.rb
76
+ - examples/lookup_field_test/lookup_field_test_2.rb
77
+ - examples/lookup_field_test/the_hal_fulton_feature_test.rb
78
+ - examples/many_to_many_test/many_to_many_test.rb
79
+ - examples/memo_test/memos
80
+ - examples/memo_test/memo_test.rb
81
+ - examples/memo_test/memos/blank.txt
82
+ - examples/record_class_test/record_class_test.rb
83
+ - examples/record_class_test/record_class_test2.rb
84
+ - examples/rename_column_test/rename_column_test.rb
85
+ - examples/rename_table_test/rename_table_test.rb
86
+ - examples/yaml_field_test/yaml_field_test.rb
87
+ - images/blank.png
88
+ - images/callouts
89
+ - images/caution.png
90
+ - images/client_server.png
91
+ - images/example.png
92
+ - images/home.png
93
+ - images/important.png
94
+ - images/kirby1.jpg
95
+ - images/next.png
96
+ - images/note.png
97
+ - images/prev.png
98
+ - images/single_user.png
99
+ - images/smallnew.png
100
+ - images/tip.png
101
+ - images/toc-blank.png
102
+ - images/toc-minus.png
103
+ - images/toc-plus.png
104
+ - images/up.png
105
+ - images/warning.png
106
+ - images/callouts/1.png
107
+ - images/callouts/10.png
108
+ - images/callouts/11.png
109
+ - images/callouts/12.png
110
+ - images/callouts/13.png
111
+ - images/callouts/14.png
112
+ - images/callouts/15.png
113
+ - images/callouts/2.png
114
+ - images/callouts/3.png
115
+ - images/callouts/4.png
116
+ - images/callouts/5.png
117
+ - images/callouts/6.png
118
+ - images/callouts/7.png
119
+ - images/callouts/8.png
120
+ - images/callouts/9.png
122
121
  test_files:
123
- - test/ts_local.rb
122
+ - test/ts_local.rb
124
123
  rdoc_options: []
124
+
125
125
  extra_rdoc_files: []
126
+
126
127
  executables:
127
- - kbserver.rb
128
+ - kbserver.rb
128
129
  extensions: []
130
+
129
131
  requirements:
130
- - none
131
- dependencies: []
132
+ - none
133
+ dependencies: []
134
+