--- kirbybase.rb	2007-04-18 10:23:23.000000000 -0500
+++ /home/greg/dev/kirbybase/kirbybase.rb	2007-06-04 19:40:34.000000000 -0500
@@ -205,28 +205,18 @@
         case data_type
         when :String
             if s =~ UNENCODE_RE
-                return s.gsub('&linefeed;', "\n").gsub('&carriage_return;',
+                s.gsub('&linefeed;', "\n").gsub('&carriage_return;',
                  "\r").gsub('&substitute;', "\032").gsub('&pipe;', "|"
                  ).gsub('&amp;', "&")
             else
-                return s
+                s
             end
-        when :Integer
-            return s.to_i
-        when :Float
-            return s.to_f
-        when :Boolean
-            if ['false', 'False', nil, false].include?(s)
-                return false
-            else
-                return true
-            end
-        when :Time
-            return Time.parse(s)    
-        when :Date
-            return Date.parse(s)
-        when :DateTime
-            return DateTime.parse(s)
+        when :Integer  then s.to_i
+        when :Float    then s.to_f
+        when :Boolean  then !(['false', 'False', nil, false].include?(s))
+        when :Time     then Time.parse(s)    
+        when :Date     then Date.parse(s)
+        when :DateTime then DateTime.parse(s)
         when :YAML
             # This code is here in case the YAML field is the last
             # field in the record.  Because YAML normally defines a
@@ -237,23 +227,17 @@
             # attempts to convert this value back using to_yaml,
             # you get an exception.
             if s == "---"
-                return nil
+                nil
             elsif s =~ UNENCODE_RE
                 y = s.gsub('&linefeed;', "\n").gsub('&carriage_return;',
                  "\r").gsub('&substitute;', "\032").gsub('&pipe;', "|"
                  ).gsub('&amp;', "&")
-                return YAML.load(y)
+                YAML.load(y)
             else
-                return YAML.load(s)
+                YAML.load(s)
             end
-        when :Memo
-            memo = KBMemo.new(@tbl.db, s)
-            memo.read_from_file
-            return memo
-        when :Blob
-            blob = KBBlob.new(@tbl.db, s)
-            blob.read_from_file
-            return blob
+        when :Memo then KBMemo.new(@tbl.db, s, nil, true)
+        when :Blob then KBBlob.new(@tbl.db, s, nil, true)
         else
             raise "Invalid field type: %s" % data_type
         end
@@ -272,26 +256,22 @@
         when :YAML
             y = value.to_yaml
             if y =~ ENCODE_RE
-                return y.gsub("&", '&amp;').gsub("\n", '&linefeed;').gsub(
+                y.gsub("&", '&amp;').gsub("\n", '&linefeed;').gsub(
                  "\r", '&carriage_return;').gsub("\032", '&substitute;'
                  ).gsub("|", '&pipe;')
             else
-                return y
+                y
             end
         when :String
             if value =~ ENCODE_RE
-                return value.gsub("&", '&amp;').gsub("\n", '&linefeed;'
+                value.gsub("&", '&amp;').gsub("\n", '&linefeed;'
                  ).gsub("\r", '&carriage_return;').gsub("\032",
                  '&substitute;').gsub("|", '&pipe;')
             else
-                return value
+                value
             end  
-        when :Memo
-            return value.filepath
-        when :Blob
-            return value.filepath
-        else
-            return value.to_s
+        when :Memo, :Blob then value.filepath
+        else value.to_s
         end
     end
 end
@@ -366,6 +346,14 @@
     end
 end
 
+module Meta
+  def invalidate_method( meth, exception_string="invalid method" )
+    define_method( meth ) do |*args|
+      raise exception_string
+    end
+  end
+end
+class Object; include Meta;end
 
 #---------------------------------------------------------------------------
 # KirbyBase
@@ -375,6 +363,7 @@
     include KBTypeConversionsMixin
 
     VERSION = "2.6"
+    CONNECTION_MODES = [:server, :client, :local]
 
     attr_reader :engine
 
@@ -422,7 +411,7 @@
 
         # Did user supply full and correct arguments to method?
         raise ArgumentError, 'Invalid connection type specified' unless (
-         [:local, :client, :server].include?(@connect_type))
+         CONNECTION_MODES.include?(@connect_type))
         raise "Must specify hostname or IP address!" if \
          @connect_type == :client and @host.nil?
         raise "Must specify port number!" if @connect_type == :client and \
@@ -456,45 +445,35 @@
         #
         # You can delay index creation until the first time the index is
         # used.
-        if @delay_index_creation
-        else    
+        unless @delay_index_creation
             @engine.tables.each do |tbl|
                 @table_hash[tbl] = \
                  KBTable.create_called_from_database_instance(self, tbl,
                  File.join(@path, tbl.to_s + @ext))
             end    
         end
-    end
 
-    #-----------------------------------------------------------------------
-    # server?
-    #-----------------------------------------------------------------------
-    #++
-    # Is this running as a server?
-    #
-    def server?
-        @connect_type == :server
+        # these could be moved to seperate classes that inherit from KirbyBase
+        if server?
+          class << self
+            [:get_table, :create_table].each do |meth|
+              invalidate_method meth,
+                "Do not call #{meth} from a server instance!"
+            end
+          end
+        elsif client?
+          class << self
+            invalidate_method :rename_table,
+              "Cannot rename table running in client mode!"
+          end
+        end
     end
 
-    #-----------------------------------------------------------------------
-    # client?
-    #-----------------------------------------------------------------------
-    #++
-    # Is this running as a client?
-    #
-    def client?
-        @connect_type == :client
-    end
-
-    #-----------------------------------------------------------------------
-    # local?
-    #-----------------------------------------------------------------------
-    #++
-    # Is this running in single-user, embedded mode?
-    #
-    def local?
-        @connect_type == :local
-    end
+    CONNECTION_MODES.each { |conn_mode|
+      define_method( (conn_mode.to_s << '?').to_sym ) do
+        @connect_type == conn_mode
+      end
+    }
 
     #-----------------------------------------------------------------------
     # tables
@@ -514,7 +493,6 @@
     # *name*:: Symbol of table name.
     #
     def get_table(name)
-        raise('Do not call this method from a server instance!') if server?
         raise(ArgumentError, 'Table name must be a symbol!') unless \
          name.is_a?(Symbol)
         raise('Table not found!') unless table_exists?(name)
@@ -544,7 +522,6 @@
     #                  will be associated with table records.
     #
     def create_table(name=nil, *field_defs)
-        raise "Can't call #create_table from server!" if server?
 
         t_struct = Struct.new(:name, :field_defs, :encrypt, :record_class)
         t = t_struct.new
@@ -559,8 +536,7 @@
         raise "No table name specified!" if t.name.nil?
         raise "No table field definitions specified!" if t.field_defs.nil?
 
-        # Can't create a table that already exists!
-        raise "Table already exists!" if table_exists?(t.name)
+        raise "Table already exists: #{t.name}" if table_exists?(t.name)
 
         raise 'Must have a field type for each field name' \
          unless t.field_defs.size.remainder(2) == 0
@@ -696,12 +672,12 @@
     # *new_tablename*:: Symbol of new table name.
     #
     def rename_table(old_tablename, new_tablename)
-        raise "Cannot rename table running in client mode!" if client?
         raise "Table does not exist!" unless table_exists?(old_tablename)
-        raise(ArgumentError, 'Existing table name must be a symbol!') \
-         unless old_tablename.is_a?(Symbol)
-        raise(ArgumentError, 'New table name must be a symbol!') unless \
-         new_tablename.is_a?(Symbol)
+        [old_tablename, new_tablename].each do |name|
+             unless name.is_a?(Symbol)
+                raise(ArgumentError, 'table name must be a symbol!')
+             end
+        end
         raise "Table already exists!" if table_exists?(new_tablename)
 
         @table_hash.delete(old_tablename)
@@ -723,7 +699,7 @@
         raise "Table does not exist!" unless table_exists?(tablename)
         @table_hash.delete(tablename)
 
-        return @engine.delete_table(tablename)
+        return @engine.delete_table(get_table(tablename))
     end
 
     #-----------------------------------------------------------------------
@@ -771,7 +747,15 @@
         @indexes = {}
 
         # This hash will hold the table locks if in client/server mode.
-        @mutex_hash = {} if @db.server?
+        if @db.server?
+          @mutex_hash = {}
+        else
+          class << self
+            def with_write_lock( *args )
+              yield
+            end
+          end
+        end
     end
 
     #-----------------------------------------------------------------------
@@ -956,11 +940,11 @@
     #-----------------------------------------------------------------------
     # delete_table
     #-----------------------------------------------------------------------
-    def delete_table(tablename)
-        with_write_lock(tablename) do
-            File.delete(File.join(@db.path, tablename.to_s + @db.ext))
-            remove_indexes(tablename)
-            remove_recno_index(tablename)
+    def delete_table(table)
+        with_write_lock(table.name) do
+            File.delete(File.join(@db.path, table.name.to_s + @db.ext))
+            remove_indexes(table)
+            remove_recno_index(table)
             return true
         end
     end
@@ -989,7 +973,7 @@
     # get_header_vars
     #-----------------------------------------------------------------------
     def get_header_vars(table)
-        with_table(table) do |fptr|
+        table.open do |fptr|
             encrypted, line = get_header_record(table, fptr)
 
             last_rec_no, del_ctr, record_class, *flds = line.split('|')
@@ -1021,7 +1005,7 @@
                     end
                 end
             end
-            return [encrypted, last_rec_no.to_i, del_ctr.to_i,
+            [encrypted, last_rec_no.to_i, del_ctr.to_i,
              record_class, field_names, field_types, field_indexes,
              field_defaults, field_requireds, field_extras]
         end
@@ -1034,7 +1018,7 @@
         encrypted = table.encrypted?
         recs = []
 
-        with_table(table) do |fptr|
+        table.open do |fptr|
             begin
                 # Skip header rec.
                 fptr.readline
@@ -1053,8 +1037,9 @@
             # Here's how we break out of the loop...
             rescue EOFError
             end
-            return recs
         end
+
+        return recs
     end
 
     #-----------------------------------------------------------------------
@@ -1065,7 +1050,7 @@
         recs = []
         recno_idx = get_recno_index(table)
 
-        with_table(table) do |fptr|
+        table.open do |fptr|
             # Skip header rec.
             fptr.readline
 
@@ -1083,8 +1068,9 @@
                 rec << r[0] << line_length
                 recs << rec
             end
-            return recs
         end
+
+        return recs
     end
 
     #-----------------------------------------------------------------------
@@ -1096,7 +1082,7 @@
 
         return nil unless recno_idx.has_key?(recno)
 
-        with_table(table) do |fptr|
+        table.open do |fptr|
             fptr.seek(recno_idx[recno])
             rec, line_length = line_to_rec(fptr.readline, encrypted)
 
@@ -1118,7 +1104,6 @@
         line.chomp!
         line_length = line.length
         line = unencrypt_str(line) if encrypted
-        line.strip!
 
         # Convert line to rec and return rec and line length.
         return line.split('|', -1), line_length
@@ -1139,7 +1124,7 @@
             fptr.seek(0, IO::SEEK_END)
             fpos = fptr.tell
 
-            write_record(table, fptr, 'end', rec.join('|'))
+            write_record(table, fptr, IO::SEEK_END, rec.join('|'))
 
             add_to_indexes(table, rec, fpos)
 
@@ -1156,31 +1141,41 @@
             recs.each do |rec|
                 line = rec[:rec].join('|')
 
-                # This doesn't actually 'delete' the line, it just
-                # makes it all spaces.  That way, if the updated
-                # record is the same or less length than the old
-                # record, we can write the record back into the
-                # same spot.  If the updated record is greater than
-                # the old record, we will leave the now spaced-out
-                # line and write the updated record at the end of
-                # the file.
-                write_record(table, fptr, rec[:fpos],
-                 ' ' * rec[:line_length])
-                if line.length > rec[:line_length]
+                rll = rec[:line_length]
+                diff = line.length - rll
+                case diff <=> 0
+
+                # If the updated record is the same length as the old
+                # record, write the record back into the same spot.
+                when 0
+                    write_record(table, fptr, rec[:fpos], line)
+
+                # If updated is greater than old, space out the previous line
+                # and write the update at the end of the file
+                when -1
+                    write_record(table, fptr, rec[:fpos], (' ' * rll) )
+
                     fptr.seek(0, IO::SEEK_END)
                     new_fpos = fptr.tell
-                    write_record(table, fptr, 'end', line)
+                    write_record(table, fptr, IO::SEEK_END, line)
                     incr_del_ctr(table, fptr)
 
                     update_recno_index(table, rec[:rec].first, new_fpos)
-                else
+
+                # If updated is less than old, write it in the same spot,
+                # and all the extra charcters will be spaces on a new line
+                when 1
+                    line << "\n" << (' ' * (diff - 1))
+
                     write_record(table, fptr, rec[:fpos], line)
+                    incr_del_ctr(table, fptr) # because we wrote a new line
                 end
+
                 update_to_indexes(table, rec[:rec])
             end
-            # Return the number of records updated.
-            return recs.size
         end
+        # Return the number of records updated.
+        return recs.size
     end
 
     #-----------------------------------------------------------------------
@@ -1585,7 +1580,7 @@
     # change_column_required
     #-----------------------------------------------------------------------
     def change_column_required(table, col_name, required)
-        with_write_lock(table.name) do
+        with_write_lock(table) do
             fptr = open(table.filename, 'r')
             new_fptr = open(table.filename+'temp', 'w')
 
@@ -1651,7 +1646,7 @@
     # pack_table
     #-----------------------------------------------------------------------
     def pack_table(table)
-        with_write_lock(table.name) do
+        with_write_lock(table) do
             fptr = open(table.filename, 'r')
             new_fptr = open(table.filename+'temp', 'w')
 
@@ -1702,92 +1697,26 @@
     end
 
     #-----------------------------------------------------------------------
-    # read_memo_file
-    #-----------------------------------------------------------------------
-    def read_memo_file(filepath)
-        begin
-            f = File.new(File.join(@db.memo_blob_path, filepath))
-            return f.read
-        ensure
-            f.close
-        end
-    end
-
-    #-----------------------------------------------------------------------
-    # write_memo_file
-    #-----------------------------------------------------------------------
-    def write_memo_file(filepath, contents)
-        begin
-            f = File.new(File.join(@db.memo_blob_path, filepath), 'w')
-            f.write(contents)
-        ensure
-            f.close
-        end
-    end
-
-    #-----------------------------------------------------------------------
-    # read_blob_file
-    #-----------------------------------------------------------------------
-    def read_blob_file(filepath)
-        begin
-            f = File.new(File.join(@db.memo_blob_path, filepath), 'rb')
-            return f.read
-        ensure
-            f.close
-        end
-    end
-
-    #-----------------------------------------------------------------------
-    # write_blob_file
-    #-----------------------------------------------------------------------
-    def write_blob_file(filepath, contents)
-        begin
-            f = File.new(File.join(@db.memo_blob_path, filepath), 'wb')
-            f.write(contents)
-        ensure
-            f.close
-        end
-    end
-    
-
-    #-----------------------------------------------------------------------
     # PRIVATE METHODS
     #-----------------------------------------------------------------------
     private
 
     #-----------------------------------------------------------------------
-    # with_table
-    #-----------------------------------------------------------------------
-    def with_table(table, access='r')
-        begin
-            yield fptr = open(table.filename, access)
-        ensure
-            fptr.close
-        end
-    end
-
-    #-----------------------------------------------------------------------
     # with_write_lock
     #-----------------------------------------------------------------------
     def with_write_lock(tablename)
-        begin
-            write_lock(tablename) if @db.server?
-            yield
-        ensure
-            write_unlock(tablename) if @db.server?
-        end
+        write_lock(tablename)
+        yield
+    ensure
+        write_unlock(tablename)
     end
 
     #-----------------------------------------------------------------------
     # with_write_locked_table
     #-----------------------------------------------------------------------
     def with_write_locked_table(table, access='r+')
-        begin
-            write_lock(table.name) if @db.server?
-            yield fptr = open(table.filename, access)
-        ensure
-            fptr.close
-            write_unlock(table.name) if @db.server?
+        with_write_lock(table.name) do
+            open(table.filename, access) { |fptr| yield fptr }
         end
     end
 
@@ -1827,9 +1756,9 @@
 
         # If record is to be appended, go to end of table and write
         # record, adding newline character.
-        if pos == 'end'
+        if pos == IO::SEEK_END
             fptr.seek(0, IO::SEEK_END)
-            fptr.write(temp_rec + "\n")
+            fptr.write(temp_rec << "\n")
         else
             # Otherwise, overwrite another record (that's why we don't
             # add the newline character).
@@ -1857,12 +1786,10 @@
     def get_header_record(table, fptr)
         fptr.seek(0)
 
-        line = fptr.readline.chomp
-
-        if line[0..0] == 'Z'
-            return [true, unencrypt_str(line[1..-1])]
+        if (line=fptr.readline.chomp)[0..0] == 'Z'
+            [true, unencrypt_str(line[1..-1])]
         else
-            return [false, line]
+            [false, line]
         end
     end
 
@@ -1900,7 +1827,20 @@
 #---------------------------------------------------------------------------
 # KBTable
 #---------------------------------------------------------------------------
-class KBTable
+#++ includes modifications for client/server mode
+module KBTable
+  def KBTable.create_called_from_database_instance(db, *args)
+    if db.local?
+      KBTable.create_called_from_database_instance(db, *args)
+    else
+      KBTableClientServer.create_called_from_database_instance(db, *args)
+    end
+  end
+
+  #---------------------------------------------------------------------------
+  # KBTable
+  #---------------------------------------------------------------------------
+  class KBTable
     include DRb::DRbUndumped
     include KBTypeConversionsMixin
 
@@ -1926,7 +1866,7 @@
     #
     # *field_type*:: Symbol specifying field type.
     #
-    def KBTable.valid_field_type?(field_type)
+    def self.valid_field_type?(field_type)
         VALID_FIELD_TYPES.include?(field_type)
     end
 
@@ -1939,32 +1879,20 @@
     # *data_type*:: Symbol specifying data type.
     # *value*:: Value to convert to String.
     #
-    def KBTable.valid_data_type?(data_type, value)
+    def self.valid_data_type?(data_type, value)
+        #TODO it seems Blob is listed twice here?
         case data_type
-        when /:String|:Blob/
-            return false unless value.respond_to?(:to_str)
-        when :Memo
-            return false unless value.is_a?(KBMemo) 
-        when :Blob
-            return false unless value.is_a?(KBBlob) 
-        when :Boolean
-            return false unless value.is_a?(TrueClass) or value.is_a?(
-             FalseClass)
-        when :Integer
-            return false unless value.respond_to?(:to_int)
-        when :Float
-            return false unless value.respond_to?(:to_f)
-        when :Time
-            return false unless value.is_a?(Time)     
-        when :Date
-            return false unless value.is_a?(Date)
-        when :DateTime
-            return false unless value.is_a?(DateTime)
-        when :YAML
-            return false unless value.respond_to?(:to_yaml)
+        when :String, :Blob then value.respond_to?(:to_str)
+        when :Memo     then value.is_a?(KBMemo) 
+        when :Blob     then value.is_a?(KBBlob) 
+        when :Boolean  then value.is_a?(TrueClass) or value.is_a?(FalseClass)
+        when :Integer  then value.respond_to?(:to_int)
+        when :Float    then value.respond_to?(:to_f)
+        when :Time     then value.is_a?(Time)     
+        when :Date     then value.is_a?(Date)
+        when :DateTime then value.is_a?(DateTime)
+        when :YAML     then value.respond_to?(:to_yaml)
         end
-    
-        return true
     end
 
     #-----------------------------------------------------------------------
@@ -1975,7 +1903,7 @@
     #
     # *field_type*:: Symbol specifying field type.
     #
-    def KBTable.valid_default_type?(field_type)
+    def self.valid_default_type?(field_type)
         VALID_DEFAULT_TYPES.include?(field_type)
     end
 
@@ -1987,7 +1915,7 @@
     #
     # *field_type*:: Symbol specifying field type.
     #
-    def KBTable.valid_index_type?(field_type)
+    def self.valid_index_type?(field_type)
         VALID_INDEX_TYPES.include?(field_type)
     end
 
@@ -1998,8 +1926,8 @@
     # Return a new instance of KBTable.  Should never be called directly by
     # your application.  Should only be called from KirbyBase#get_table.
     #
-    def KBTable.create_called_from_database_instance(db, name, filename)
-        return new(db, name, filename)
+    def self.create_called_from_database_instance(*args)
+        return new(*args)
     end
 
     #-----------------------------------------------------------------------
@@ -2035,11 +1963,7 @@
     # Returns true if table is encrypted.
     #
     def encrypted?
-        if @encrypted
-            return true
-        else
-            return false
-        end
+        @encrypted
     end
 
     #-----------------------------------------------------------------------
@@ -2103,6 +2027,13 @@
     end
 
     #-----------------------------------------------------------------------
+    # open
+    #-----------------------------------------------------------------------
+    def open
+        File.open(@filename, 'r') { |fptr| yield fptr }
+    end
+
+    #-----------------------------------------------------------------------
     # insert
     #-----------------------------------------------------------------------
     #++
@@ -2114,21 +2045,20 @@
     #                 data parameter are mutually exclusive.
     #
     def insert(*data, &insert_proc)
-        raise 'Cannot specify both a hash/array/struct and a ' + \
-         'proc for method #insert!' unless data.empty? or insert_proc.nil?
-
-        raise 'Must specify either hash/array/struct or insert ' + \
-         'proc for method #insert!' if data.empty? and insert_proc.nil?
-
-        # Update the header variables.
-        update_header_vars
-
-        # Convert input, which could be a proc, an array, a hash, or a
-        # Struct into a common format (i.e. hash).
-        if data.empty?
-            input_rec = convert_input_data(insert_proc)
+        input_rec = if data.empty?
+          if insert_proc.nil?
+            raise 'Must specify either hash/array/struct or insert ' + \
+             'proc for method #insert!'
+          end
+          update_header_vars
+          convert_input_data(insert_proc)
+
+        elsif !(insert_proc.nil?)
+          raise 'Cannot specify both a hash/array/struct and a ' + \
+           'proc for method #insert!'
         else
-            input_rec = convert_input_data(data)
+          update_header_vars
+          convert_input_data(data)
         end
 
         # Check the field values to make sure they are proper types.
@@ -2152,12 +2082,7 @@
             convert_to_encoded_string(ft, input_rec[fn])
         end)
 
-        # If there are any associated memo/blob fields, save their values.
-        input_rec.each { |r| r.write_to_file if r.is_a?(KBMemo) } if \
-         @field_types.include?(:Memo)
-        input_rec.each { |r| r.write_to_file if r.is_a?(KBBlob) } if \
-         @field_types.include?(:Blob)
-                        
+        update_memos_or_blobs( input_rec )
         return new_recno
     end
 
@@ -2262,7 +2187,7 @@
                 rescue NoMethodError
                     raise 'Invalid field name in code block: %s' % $!
                 end
-             else
+            else
                 @field_names.each { |fn| temp_rec[fn] = update_rec.fetch(fn,
                  temp_rec.send(fn)) }
             end
@@ -2292,12 +2217,7 @@
              temp_rec.send(fn)) }, :fpos => rec.fpos,
              :line_length => rec.line_length }
  
-
-            # Update any associated blob/memo fields.
-            temp_rec.each { |r| r.write_to_file if r.is_a?(KBMemo) } if \
-             @field_types.include?(:Memo)
-            temp_rec.each { |r| r.write_to_file if r.is_a?(KBBlob) } if \
-             @field_types.include?(:Blob)
+            update_memos_or_blobs( temp_rec )
         end
 
         # Take all of the update records and write them back out to the
@@ -2308,6 +2228,12 @@
         return recs.size
     end
 
+    def update_memos_or_blobs( rec )
+      @field_types.each_with_index do |type,i|
+        rec[i].write_to_file if type.is_a? KBMemoBlob
+      end
+    end
+
     #-----------------------------------------------------------------------
     # delete
     #-----------------------------------------------------------------------
@@ -2358,16 +2284,16 @@
     # *index*:: Array of Integer(s) specifying recno(s) you wish to select.
     #
     def [](*index)
-        return nil if index[0].nil?
+        if index.empty?
+          nil
+        elsif index.size == 1
+          get_match_by_recno(:select, @field_names, index[0])
 
-        return get_match_by_recno(:select, @field_names, index[0]) if \
-         index.size == 1
-
-        recs = select_by_recno_index(*@field_names) { |r|
-            index.include?(r.recno)
-        }
-
-        return recs
+        else
+          select_by_recno_index(*@field_names) do |r|
+              index.include?(r.recno)
+          end
+        end
     end
 
     #-----------------------------------------------------------------------
@@ -2428,9 +2354,6 @@
     # Remove blank records from table, return total removed.
     #
     def pack
-        raise "Do not execute this method in client/server mode!" if \
-         @db.client?
-
         lines_deleted = @db.engine.pack_table(self)
 
         update_header_vars
@@ -2456,9 +2379,6 @@
     # *new_col_name*:: Symbol of new column name.
     #
     def rename_column(old_col_name, new_col_name)
-        raise "Do not execute this method in client/server mode!" if \
-         @db.client?
-
         raise "Cannot rename recno column!" if old_col_name == :recno
         raise "Cannot give column name of recno!" if new_col_name == :recno
 
@@ -2492,15 +2412,12 @@
     # *col_type*:: Symbol of new column type.
     #
     def change_column_type(col_name, col_type)
-        raise "Do not execute this method in client/server mode!" if \
-         @db.client?
-
         raise "Cannot change type for recno column!" if col_name == :recno
         raise 'Invalid column name: ' % col_name unless \
          @field_names.include?(col_name)
         
         raise 'Invalid field type: %s' % col_type unless \
-         KBTable.valid_field_type?(col_type)
+         self.class.valid_field_type?(col_type)
 
         @db.engine.change_column_type(self, col_name, col_type)
 
@@ -2529,9 +2446,6 @@
     #           after.
     #
     def add_column(col_name, col_type, after=nil)
-        raise "Do not execute this method in client/server mode!" if \
-         @db.client?
-
         raise "Invalid column name in 'after': #{after}" unless after.nil? \
          or @field_names.include?(after)
 
@@ -2550,7 +2464,7 @@
         end
 
         raise 'Invalid field type: %s' % temp_type unless \
-         KBTable.valid_field_type?(temp_type)
+         self.class.valid_field_type?(temp_type)
 
         field_def = @db.build_header_field_string(col_name, col_type)
 
@@ -2577,9 +2491,6 @@
     # *col_name*:: Symbol of column name to add.
     #
     def drop_column(col_name)
-        raise "Do not execute this method in client/server mode!" if \
-         @db.client?
-
         raise 'Invalid column name: ' % col_name unless \
          @field_names.include?(col_name)
 
@@ -2608,9 +2519,6 @@
     # *col_names*:: Array containing column name(s) of new index.
     #
     def add_index(*col_names)
-        raise "Do not execute this method in client/server mode!" if \
-         @db.client?
-
         col_names.each do |c|
             raise "Invalid column name: #{c}" unless \
              @field_names.include?(c)
@@ -2651,9 +2559,6 @@
     # *col_names*:: Array containing column name(s) of new index.
     #
     def drop_index(*col_names)
-        raise "Do not execute this method in client/server mode!" if \
-         @db.client?
-
         col_names.each do |c|
             raise "Invalid column name: #{c}" unless \
              @field_names.include?(c)
@@ -2688,9 +2593,6 @@
     # *value*:: New default value for column.
     #
     def change_column_default_value(col_name, value)
-        raise "Do not execute this method in client/server mode!" if \
-         @db.client?
-
         raise ":recno cannot have a default value!" if col_name == :recno
 
         raise 'Invalid column name: ' % col_name unless \
@@ -2698,7 +2600,7 @@
 
         raise 'Cannot set default value for this type: ' + \
          '%s' % @field_types.index(col_name) unless \
-         KBTable.valid_default_type?(
+         self.class.valid_default_type?(
           @field_types[@field_names.index(col_name)])
         
         if value.nil?
@@ -2731,9 +2633,6 @@
     # *required*:: true or false.
     #
     def change_column_required(col_name, required)
-        raise "Do not execute this method in client/server mode!" if \
-         @db.client?
-
         raise ":recno is always required!" if col_name == :recno
 
         raise 'Invalid column name: ' % col_name unless \
@@ -2827,20 +2726,22 @@
             # Create this index on the engine.
             @db.engine.init_index(self, index_col_names) 
 
-            # For each index found, add an instance method for it so that
-            # it can be used for #selects.
-            select_meth_str = <<-END_OF_STRING
-            def select_by_#{index_col_names.join('_')}_index(*filter,
-             &select_cond)
-                result_set = []
-                validate_filter(filter)
-                filter = @field_names if filter.empty?
-                return get_matches_by_index(:select,
-                 [:#{index_col_names.join(',:')}], filter, select_cond)
-            end
-            END_OF_STRING
+            unless @db.server?
+              # For each index found, add an instance method for it so that
+              # it can be used for #selects.
+              select_meth_str = <<-END_OF_STRING
+              def select_by_#{index_col_names.join('_')}_index(*filter,
+               &select_cond)
+                  result_set = []
+                  validate_filter(filter)
+                  filter = @field_names if filter.empty?
+                  return get_matches_by_index(:select,
+                   [:#{index_col_names.join(',:')}], filter, select_cond)
+              end
+              END_OF_STRING
 
-            instance_eval(select_meth_str) unless @db.server?
+              instance_eval(select_meth_str)
+            end
            
             @idx_timestamps[index_col_names.join('_')] = nil
             @idx_arrs[index_col_names.join('_')] = nil
@@ -3012,7 +2913,8 @@
 
         # This only applies to Procs in #insert, Procs in #update are
         # handled in #set.
-        if values.is_a?(Proc)
+        case values
+        when Proc
             tbl_rec = Struct.new(*@field_names[1..-1]).new
             begin
                 values.call(tbl_rec)
@@ -3024,29 +2926,33 @@
                 temp_hash[f] = tbl_rec[f] unless tbl_rec[f].nil?
             end
 
-        # Is input data an instance of custom record class, Struct, or
-        # KBTableRec?
-        elsif values.first.is_a?(Object.full_const_get(@record_class)) or \
-         values.first.is_a?(Struct) or values.first.class == @table_class
-            @field_names[1..-1].each do |f|
-                temp_hash[f] = values.first.send(f) if \
-                 values.first.respond_to?(f)
-            end
-
-        # Is input data a hash?
-        elsif values.first.is_a?(Hash)
-            temp_hash = values.first.dup
-
-        # Is input data an array?
-        elsif values.is_a?(Array)
-            raise ArgumentError, 'Must specify all fields in input array!' \
-             unless values.size == @field_names[1..-1].size
+        when Array
+            case values.first
+            when Hash
+              temp_hash.merge!( values.first )
+
+            # Is input data an instance of custom record class, Struct, or
+            # KBTableRec?
+            when (Object.full_const_get(@record_class)), Struct, @table_class
+                @field_names[1..-1].each do |f|
+                    if values.first.respond_to?(f)
+                      temp_hash[f] = values.first.send(f)
+                    end
+                end
 
-            @field_names[1..-1].each do |f|
-                temp_hash[f] = values[@field_names.index(f)-1]
+            else
+              non_recno_fields = @field_names[1..-1]
+              unless values.size == non_recno_fields.size
+                raise ArgumentError, 'Must specify all fields in input array!'
+              end
+
+              non_recno_fields[1..-1].each do |f|
+                  temp_hash[f] = values[@field_names.index(f)-1]
+              end
             end
+
         else
-            raise(ArgumentError, 'Invalid type for values container!')
+            raise ArgumentError, 'Invalid type for values container!'
         end
 
         return temp_hash
@@ -3095,7 +3001,7 @@
             next if data[f].nil?
 
             raise 'Invalid data %s for column %s' % [data[f], f] unless \
-             KBTable.valid_data_type?(@field_types[@field_names.index(f)],
+             self.class.valid_data_type?(@field_types[@field_names.index(f)],
              data[f])
         end
     end
@@ -3128,14 +3034,11 @@
     #
     def get_result_struct(query_type, filter)
         case query_type
-        when :select
-            return Struct.new(*filter) if @record_class == 'Struct'
-        when :update
-            return Struct.new(*(filter + [:fpos, :line_length]))
-        when :delete
-            return Struct.new(:recno, :fpos, :line_length)
+        when :select then @record_class == 'Struct' ? Struct.new(*filter) : nil
+        when :update then Struct.new(*(filter + [:fpos, :line_length]))
+        when :delete then Struct.new(:recno, :fpos, :line_length)
+        else nil
         end
-        return nil
     end
 
     #-----------------------------------------------------------------------
@@ -3153,11 +3056,8 @@
              tbl_rec.send("#{f}_upd_res".to_sym) })
         elsif @record_class == 'Struct'
             result_rec = result_struct.new(*filter.collect do |f|
-                if tbl_rec.send(f).kb_nil?
-                    nil
-                else
-                    tbl_rec.send(f)
-                end
+                field = tbl_rec.send(f)
+                field.kb_nil? ? nil : field
             end)
         else
             if Object.full_const_get(@record_class).respond_to?(:kb_create)
@@ -3168,11 +3068,8 @@
                     # you specified in the result set, EVEN IF
                     # record_class is a custom class instead of Struct.
                     if filter.include?(f)
-                        if tbl_rec.send(f).kb_nil?
-                            nil
-                        else
-                            tbl_rec.send(f)
-                        end
+                        field = tbl_rec.send(f)
+                        field.kb_nil? ? nil : field
                     else
                         nil
                     end
@@ -3181,10 +3078,11 @@
              :kb_defaults)
                 result_rec = Object.full_const_get(@record_class).new(
                  *@field_names.collect do |f|
-                    if tbl_rec.send(f).kb_nil?
+                    field = tbl_rec.send(f)
+                    if field.kb_nil?
                         nil
                     else
-                        tbl_rec.send(f) || Object.full_const_get(
+                        field || Object.full_const_get(
                          @record_class).kb_defaults[@field_names.index(f)]
                     end
                 end)
@@ -3204,35 +3102,11 @@
             result_rec.fpos = rec[-2]
             result_rec.line_length = rec[-1]
         end
+        
         return result_rec
     end
 
     #-----------------------------------------------------------------------
-    # get_matches
-    #-----------------------------------------------------------------------
-    #++
-    # Return records from table that match select condition.
-    #
-    def get_matches(query_type, filter, select_cond)
-        result_struct = get_result_struct(query_type, filter)
-        match_array = KBResultSet.new(self, filter, filter.collect { |f|
-         @field_types[@field_names.index(f)] })
-
-        tbl_rec = @table_class.new(self)
-
-        # Loop through table.
-        @db.engine.get_recs(self).each do |rec|
-            tbl_rec.populate(rec)
-
-            next if select_cond and not select_cond.call(tbl_rec)
-
-            match_array << create_result_rec(query_type, filter,
-             result_struct, tbl_rec, rec)
-        end
-        return match_array
-    end
-
-    #-----------------------------------------------------------------------
     # get_matches_by_index
     #-----------------------------------------------------------------------
     #++
@@ -3277,6 +3151,7 @@
         return get_matches_by_recno(query_type, filter, good_matches)
     end
 
+
     #-----------------------------------------------------------------------
     # get_matches_by_recno_index
     #-----------------------------------------------------------------------
@@ -3301,17 +3176,34 @@
     end
 
     #-----------------------------------------------------------------------
+    # get_matches
+    #-----------------------------------------------------------------------
+    #++
+    # Return records from table that match select condition.
+    #
+    def get_matches(query_type, filter, select_cond)
+        result_struct, match_array, tbl_rec = match_init(query_type, filter)
+
+        # Loop through table.
+        match_array.concat(
+        @db.engine.get_recs(self).map! do |rec|
+            tbl_rec.populate(rec)
+
+            next if select_cond and not select_cond.call(tbl_rec)
+
+            create_result_rec(query_type, filter,
+             result_struct, tbl_rec, rec)
+        end.compact)
+    end
+
+    #-----------------------------------------------------------------------
     # get_match_by_recno
     #-----------------------------------------------------------------------
     #++
     # Return record from table that matches supplied recno.
     #
     def get_match_by_recno(query_type, filter, recno)
-        result_struct = get_result_struct(query_type, filter)
-        match_array = KBResultSet.new(self, filter, filter.collect { |f|
-         @field_types[@field_names.index(f)] })
-
-        tbl_rec = @table_class.new(self)
+        result_struct, match_array, tbl_rec = match_init(query_type, filter)
 
         rec = @db.engine.get_rec_by_recno(self, recno)
         return nil if rec.nil?
@@ -3328,83 +3220,99 @@
     # Return records from table that match select condition.
     #
     def get_matches_by_recno(query_type, filter, recnos)
-        result_struct = get_result_struct(query_type, filter)
-        match_array = KBResultSet.new(self, filter, filter.collect { |f|
-         @field_types[@field_names.index(f)] })
-
-        tbl_rec = @table_class.new(self)
+        result_struct, match_array, tbl_rec = match_init(query_type, filter)
 
-        @db.engine.get_recs_by_recno(self, recnos).each do |rec|
-            next if rec.nil?
-            tbl_rec.populate(rec)
-
-            match_array << create_result_rec(query_type, filter,
-             result_struct, tbl_rec, rec)
-        end
-        return match_array
-    end
+        match_array.concat(
+            @db.engine.get_recs_by_recno(self, recnos).map! do |rec|
+                next if rec.nil?
+                tbl_rec.populate(rec)
+
+                create_result_rec(query_type, filter, result_struct, tbl_rec, rec)
+        end.compact)
+    end
+
+    def match_init(query_type, filter)
+        return get_result_struct(query_type, filter),
+            KBResultSet.new(self, filter, filter.collect { |f|
+             @field_types[@field_names.index(f)] }),
+            @table_class.new(self)
+    end
+  end
+
+
+  #---------------------------------------------------------------------------
+  # KBTableClientServer
+  #---------------------------------------------------------------------------
+  #++ includes modifications for client/server mode
+  class KBTableClientServer < KBTable
+      [:pack, :rename_column, :change_column_type, :add_column, 
+       :drop_column, :add_index, :drop_index, :change_column_default_value,
+       :change_column_required ].each do |meth|
+        invalidate_method meth,
+          "Do not execute ##{meth} in client/server mode!"
+      end
+  end
 end
 
 
 #---------------------------------------------------------------------------
-# KBMemo
+# KBMemoBlob
 #---------------------------------------------------------------------------
-class KBMemo
+#++
+# abstract class for KBMemo and KBBlob classes
+#
+class KBMemoBlob
     attr_accessor :filepath, :contents
 
     #-----------------------------------------------------------------------
     # initialize
     #-----------------------------------------------------------------------
-    def initialize(db, filepath, contents='')
+    #++
+    # by default initialized to empty string
+    def initialize(db, filepath, contents=nil, read=false)
         @db = db
         @filepath = filepath
-        @contents = contents
+        @contents = contents || read ? read_from_file() : ''
     end
     
     #-----------------------------------------------------------------------
     # read_from_file
     #-----------------------------------------------------------------------
-    def read_from_file
-        @contents = @db.engine.read_memo_file(@filepath)
+    def read_from_file( mode )
+        @contents = 
+        File.open(File.join(@db.memo_blob_path, @filepath), mode) do |f|
+            f.read
+        end
     end
     
     #-----------------------------------------------------------------------
     # write_to_file
     #-----------------------------------------------------------------------
-    def write_to_file
-        @db.engine.write_memo_file(@filepath, @contents)
+    def write_to_file( mode )
+        File.open(File.join(@db.memo_blob_path, @filepath), mode) do |f|
+            f.write(@contents)
+        end
     end
 end
 
+#---------------------------------------------------------------------------
+# KBMemo
+#---------------------------------------------------------------------------
+class KBMemo < KBMemoBlob
+  READ_MODE = 'r'
+  WRITE_MODE = 'w'
+  def read_from_file;  super( READ_MODE ) end
+  def write_from_file; super( WRITE_MODE ) end
+end
 
 #---------------------------------------------------------------------------
 # KBBlob
 #---------------------------------------------------------------------------
-class KBBlob
-    attr_accessor :filepath, :contents
-
-    #-----------------------------------------------------------------------
-    # initialize
-    #-----------------------------------------------------------------------
-    def initialize(db, filepath, contents='')
-        @db = db
-        @filepath = filepath
-        @contents = contents
-    end
-    
-    #-----------------------------------------------------------------------
-    # read_from_file
-    #-----------------------------------------------------------------------
-    def read_from_file
-        @contents = @db.engine.read_blob_file(@filepath)
-    end
-
-    #-----------------------------------------------------------------------
-    # write_to_file
-    #-----------------------------------------------------------------------
-    def write_to_file
-        @db.engine.write_blob_file(@filepath, @contents)
-    end
+class KBBlob < KBMemoBlob
+  READ_MODE = 'rb'
+  WRITE_MODE = 'wb'
+  def read_from_file;  super( READ_MODE ) end
+  def write_from_file; super( WRITE_MODE ) end
 end
 
 
@@ -3632,6 +3540,12 @@
 # KBResultSet
 #---------------------------------------------------------------------------
 class KBResultSet < Array
+    DELIM = ' | '
+    NUMBER_FIELDS = [:Integer, :Float]
+    JUSTIFY_HASH = { :String => :ljust, :Integer => :rjust,
+     :Float => :rjust, :Boolean => :ljust, :Date => :ljust,
+     :Time => :ljust, :DateTime => :ljust }
+
     #-----------------------------------------------------------------------
     # KBResultSet.reverse
     #-----------------------------------------------------------------------
@@ -3647,20 +3561,33 @@
         @filter = filter
         @filter_types = filter_types
         super(*args)
+    end
 
-        @filter.each do |f|
-            get_meth_str = <<-END_OF_STRING
-            def #{f}()
-                if defined?(@#{f}) then
-                    return @#{f}
-                else
-                    @#{f} = self.collect { |x| x.#{f} }
-                    return @#{f}
-                end
-            end
-            END_OF_STRING
-            self.class.class_eval(get_meth_str)
+    # no need for eval when initializing
+    def method_missing(meth)
+      if @filter.include? meth
+        self.collect { |x| x.send(meth) }
+      else
+        fail NameError, "unknown method or attribute: #{meth}"
+      end
+    end
+
+    # this is suggested as a replacement for method_missing
+    # it will get rid of any namespacing issues
+    def []( key, *args, &block )
+      if key.is_a? Symbol
+        if !(@filter.include?( key ))
+          fail NameError, "unknown method or attribute: #{key}"
+
+        elsif self.first.is_a? Struct
+          self.map { |x| x[key] }
+        else
+          self.map { |x| x.send(key) }
         end
+
+      else
+        super( key, *args, &block )
+      end
     end
 
     #-----------------------------------------------------------------------
@@ -3677,111 +3604,123 @@
     # Update record(s) in table, return number of records updated.
     #
     def set(*updates, &update_cond)
-        raise 'Cannot specify both a hash and a proc for method #set!' \
-         unless updates.empty? or update_cond.nil?
+        if updates.empty?
+          unless update_cond
+            raise ArgumentError, 'Must specify update proc or hash for method #set!'
+          end
+          @table.set(self, update_cond)
 
-        raise 'Must specify update proc or hash for method #set!' if \
-         updates.empty? and update_cond.nil?
+        elsif update_cond
+            raise ArgumentError, 'Cannot specify both a hash and a proc for method #set!'
 
-        if updates.empty?
-            @table.set(self, update_cond)
         else
-            @table.set(self, updates)
+          @table.set(self, updates)
         end
     end
 
     #-----------------------------------------------------------------------
     # sort
     #-----------------------------------------------------------------------
-    def sort(*sort_fields)
-        sort_fields_arrs = []
-        sort_fields.each do |f|
-            if f.to_s[0..0] == '-'
-                sort_fields_arrs << [f.to_s[1..-1].to_sym, :desc]
-            elsif f.to_s[0..0] == '+'
-                sort_fields_arrs << [f.to_s[1..-1].to_sym, :asc]
-            else
-                sort_fields_arrs << [f, :asc]
-            end
-        end
+    #++
+    # calls sort! with a duplicate of self
+    def sort( *args, &block )
+      self.dup.sort!(*args, &block)
+    end
 
-        sort_fields_arrs.each do |f|
-            raise "Invalid sort field" unless @filter.include?(f[0])
+    #-----------------------------------------------------------------------
+    # sort!
+    #-----------------------------------------------------------------------
+    #++
+    # use the Array.sort! method
+    # so that a KBResultSet is returned instead of a new array
+    def sort!( *sort_fields, &sort_block)
+      if sort_fields.empty?
+        if sort_block then super( &sort_block )
+        else fail ArgumentError, "need fields to sort with"
+        end
+      elsif sort_block
+        fail ArgumentError, "cannot give both sort fields and a sort block"
+
+      else
+        sort_fields_arrs = sort_fields.map do |f|
+            sfa = case (f_str = f.to_s)[0,1]
+                  when '-' then [f_str[1..-1].to_sym, :desc]
+                  when '+' then [f_str[1..-1].to_sym, :asc]
+                  else [f, :asc]
+                  end
+            raise "Invalid sort field" unless (i = @filter.index(sfa[0]))
+            sfa.push i
         end
 
         super() { |a,b|
-            x = []
-            y = []
-            sort_fields_arrs.each do |s|
-                if [:Integer, :Float].include?(
-                 @filter_types[@filter.index(s[0])])
-                    a_value = a.send(s[0]) || 0
-                    b_value = b.send(s[0]) || 0
-                else
-                    a_value = a.send(s[0])
-                    b_value = b.send(s[0])
-                end
-                if s[1] == :desc
-                    x << b_value
-                    y << a_value
-                else
-                    x << a_value
-                    y << b_value
+            cmp = nil
+            sort_fields_arrs.each do |field, desc, filter_i|
+
+                a_value, b_value = a[filter_i], b[filter_i]
+
+                if NUMBER_FIELDS.include?( @filter_types[filter_i] )
+                    a_value ||= 0
+                    b_value ||= 0
                 end
-            end
 
-            x <=> y
+                cmp = if desc == :desc then b_value <=> a_value
+                      else                  a_value <=> b_value
+                      end
+
+                break unless cmp == 0
+            end
+            cmp
         }
+      end
     end
 
     #-----------------------------------------------------------------------
     # to_report
     #-----------------------------------------------------------------------
-    def to_report(recs_per_page=0, print_rec_sep=false)
-        result = collect { |r| @filter.collect {|f| r.send(f)} }
+    def to_report(recs_per_page=nil, print_rec_sep=false, *filter)
+        if !filter.first
+          filter, filter_types = @filter, @filter_types
+        else
+          filter_types = filter.map do |f|
+            @filter_types[@filter.index(f) || (fail "invalid filter: " << f.to_s)]
+          end
+        end
 
-        # How many records before a formfeed.
-        delim = ' | '
+        result = collect { |r| filter.collect {|f| r.send(f)} }
 
         # columns of physical rows
-        columns = [@filter].concat(result).transpose
+        columns = [filter].concat(result).transpose
 
         max_widths = columns.collect { |c|
             c.max { |a,b| a.to_s.length <=> b.to_s.length }.to_s.length
         }
 
         row_dashes = '-' * (max_widths.inject {|sum, n| sum + n} +
-         delim.length * (max_widths.size - 1))
+         DELIM.length * (max_widths.size - 1))
 
-        justify_hash = { :String => :ljust, :Integer => :rjust,
-         :Float => :rjust, :Boolean => :ljust, :Date => :ljust,
-         :Time => :ljust, :DateTime => :ljust }
-
-        header_line = @filter.zip(max_widths, @filter.collect { |f|
-            @filter_types[@filter.index(f)] }).collect { |x,y,z|
-                 x.to_s.send(justify_hash[z], y) }.join(delim)
-
-        output = ''
-        recs_on_page_cnt = 0
-
-        result.each do |row|
-            if recs_on_page_cnt == 0
-                output << header_line + "\n" << row_dashes + "\n"
-            end
-
-            output << row.zip(max_widths, @filter.collect { |f|
-                @filter_types[@filter.index(f)] }).collect { |x,y,z|
-                    x.to_s.send(justify_hash[z], y) }.join(delim) + "\n"
-
-            output << row_dashes + '\n' if print_rec_sep
-            recs_on_page_cnt += 1
-
-            if recs_per_page > 0 and (recs_on_page_cnt ==
-             num_recs_per_page)
-                output << '\f'
-                recs_on_page_count = 0
-            end
+        header_line = filter.zip(max_widths, filter.collect { |f|
+            filter_types[filter.index(f)] }).collect { |x,y,z|
+                 x.to_s.send(JUSTIFY_HASH[z], y) }.join(DELIM)
+
+        output = header_line + "\n" << row_dashes + "\n"
+        start = 0
+        recs_per_page ||= result.length
+
+        loop do
+            page_of_results = result[start..(start + recs_per_page - 1)]
+            break if page_of_results.empty?
+
+            output << page_of_results.map do |row|
+                row.zip(max_widths, filter.collect { |f|
+                    filter_types[filter.index(f)] }).collect { |x,y,z|
+                        x.to_s.send(JUSTIFY_HASH[z], y) }.join(DELIM) << "\n"
+            end.join('')
+
+            output << (row_dashes + "\n") if print_rec_sep
+            output << "\f" unless start == 0
+            start += recs_per_page
         end
+
         return output
     end
 end
@@ -3856,9 +3795,8 @@
 #---------------------------------------------------------------------------
 class Object
     def full_const_get(name)
-        list = name.split("::")
         obj = Object
-        list.each {|x| obj = obj.const_get(x) }
+        name.split("::").each {|x| obj = obj.const_get(x) }
         obj
     end
 
@@ -3879,7 +3817,7 @@
     # This allows you to put a minus sign in front of a field name in order
     # to specify descending sort order.
     def -@
-        ("-"+self.to_s).to_sym
+        ("-" << self.to_s).to_sym
     end
 
     #-----------------------------------------------------------------------
@@ -3889,8 +3827,6 @@
     # This allows you to put a plus sign in front of a field name in order
     # to specify ascending sort order.
     def +@
-        ("+"+self.to_s).to_sym
+        ("+" << self.to_s).to_sym
     end
 end
-
-
