lib/dbf/table.rb in dbf-3.1.1 vs lib/dbf/table.rb in dbf-3.1.2

- old
+ new

@@ -26,18 +26,18 @@ '8b' => 'dBase IV with memo file', '8e' => 'dBase IV with SQL table', 'cb' => 'dBASE IV SQL table files, with memo', 'f5' => 'FoxPro with memo file', 'fb' => 'FoxPro without memo file' - } + }.freeze FOXPRO_VERSIONS = { '30' => 'Visual FoxPro', '31' => 'Visual FoxPro with AutoIncrement field', 'f5' => 'FoxPro with memo file', 'fb' => 'FoxPro without memo file' - } + }.freeze attr_accessor :encoding attr_writer :name # Opens a DBF::Table @@ -155,11 +155,11 @@ !!@memo end # @return [String] def name - @name ||= filename && File.basename(filename, ".*") + @name ||= filename && File.basename(filename, '.*') end # Retrieve a record by index number. # The record will be nil if it has been deleted, but not yet pruned from # the database. @@ -170,11 +170,11 @@ seek_to_record(index) return nil if deleted_record? DBF::Record.new(@data.read(header.record_length), columns, version, @memo) end - alias_method :row, :record + alias row record # Total number of records # # @return [Integer] def record_count @@ -206,90 +206,97 @@ VERSIONS[version] end private - def build_columns # nodoc - @data.seek(DBF_HEADER_SIZE) - columns = [] - until end_of_record? - column_data = @data.read(DBF_HEADER_SIZE) - name, type, length, decimal = column_data.unpack('a10 x a x4 C2') - columns << Column.new(self, name, type, length, decimal) + def build_columns # :nodoc: + safe_seek do + @data.seek(DBF_HEADER_SIZE) + columns = [] + until end_of_record? + column_data = @data.read(DBF_HEADER_SIZE) + name, type, length, decimal = column_data.unpack('a10 x a x4 C2') + columns << Column.new(self, name, type, length, decimal) + end + columns end - columns end - def deleted_record? # nodoc + def deleted_record? # :nodoc: flag = @data.read(1) flag ? flag.unpack('a') == ['*'] : true end - def end_of_record? # nodoc - original_pos = @data.pos - byte = @data.read(1) - @data.seek(original_pos) - byte.ord == 13 + def end_of_record? # :nodoc: + safe_seek { @data.read(1).ord == 13 } end - def find_all(options) # nodoc + def find_all(options) # :nodoc: map do |record| if record && record.match?(options) yield record if block_given? record end end.compact end - def find_first(options) # nodoc + def find_first(options) # :nodoc: detect { |record| record && record.match?(options) } end - def foxpro? # nodoc + def foxpro? # :nodoc: FOXPRO_VERSIONS.keys.include? version end - def header - @header ||= Header.new(@data.read DBF_HEADER_SIZE) + def header # :nodoc: + @header ||= safe_seek do + @data.seek(0) + Header.new(@data.read DBF_HEADER_SIZE) + end end - def memo_class # nodoc + def memo_class # :nodoc: @memo_class ||= begin if foxpro? Memo::Foxpro else version == '83' ? Memo::Dbase3 : Memo::Dbase4 end end end - def memo_search_path(io) # nodoc + def memo_search_path(io) # :nodoc: dirname = File.dirname(io) basename = File.basename(io, '.*') "#{dirname}/#{basename}*.{fpt,FPT,dbt,DBT}" end - def open_data(data) # nodoc + def open_data(data) # :nodoc: data.is_a?(StringIO) ? data : File.open(data, 'rb') rescue Errno::ENOENT raise DBF::FileNotFoundError, "file not found: #{data}" end - def open_memo(data, memo = nil) # nodoc + def open_memo(data, memo = nil) # :nodoc: if memo meth = memo.is_a?(StringIO) ? :new : :open memo_class.send(meth, memo, version) elsif !data.is_a?(StringIO) - files = Dir.glob(memo_search_path data) + files = Dir.glob(memo_search_path(data)) files.any? ? memo_class.open(files.first, version) : nil end end - def seek(offset) # nodoc + def safe_seek # :nodoc: + original_pos = @data.pos + yield.tap { @data.seek(original_pos) } + end + + def seek(offset) # :nodoc: @data.seek(header.header_length + offset) end - def seek_to_record(index) # nodoc + def seek_to_record(index) # :nodoc: seek(index * header.record_length) end end end