lib/dbf/table.rb in dbf-1.0.5 vs lib/dbf/table.rb in dbf-1.0.6

- old
+ new

@@ -1,42 +1,27 @@ module DBF class Table - # The total number of columns (columns) - attr_reader :column_count + include Enumerable - # An array of DBF::Column records - attr_reader :columns + attr_reader :column_count # The total number of columns (columns) + attr_reader :columns # An array of DBF::Column + attr_reader :version # Internal dBase version number + attr_reader :last_updated # Last updated datetime + attr_reader :memo_file_format # :fpt or :dpt + attr_reader :memo_block_size # The block size for memo records + attr_reader :options # The options hash that was used to initialize the table + attr_reader :data # DBF file handle + attr_reader :memo # Memo file handle - # Internal dBase version number - attr_reader :version - - # Last updated datetime - attr_reader :last_updated - - # Either :fpt or :dpt - attr_reader :memo_file_format - - # The block size for memo records - attr_reader :memo_block_size - - # The options that were used when initializing DBF::Table. This is a Hash. - attr_reader :options - - attr_reader :data - attr_reader :memo - - # Initialize a new DBF::Reader. + # Initializes a new DBF::Reader # Example: # reader = DBF::Reader.new 'data.dbf' def initialize(filename, options = {}) - @options = {:in_memory => true, :accessors => true}.merge(options) - - @in_memory = @options[:in_memory] - @accessors = @options[:accessors] @data = File.open(filename, 'rb') @memo = open_memo(filename) + @options = options reload! end # Reloads the database and memo files def reload! @@ -55,35 +40,41 @@ # The total number of active records. def record_count @db_index.size end - # Returns an instance of DBF::Column for <b>column_name</b>. <b>column_name</b> - # can be a symbol or a string. + # Returns an instance of DBF::Column for <b>column_name</b>. The <b>column_name</b> + # can be a specified as either a symbol or string. def column(column_name) @columns.detect {|f| f.name == column_name.to_s} end # An array of all the records contained in the database file. Each record is an instance # of DBF::Record (or nil if the record is marked for deletion). def records - if options[:in_memory] - @records ||= get_all_records_from_file - else - get_all_records_from_file + self.to_a + end + + alias_method :rows, :records + + def each + 0.upto(@record_count - 1) do |n| + seek_to_record(n) + unless deleted_record? + yield DBF::Record.new(self) + end end end - alias_method :rows, :records + # def get_record_from_file(index) + # seek_to_record(@db_index[index]) + # Record.new(self) + # end # Returns a DBF::Record (or nil if the record has been marked for deletion) for the record at <tt>index</tt>. def record(index) - if options[:in_memory] - records[index] - else - get_record_from_file(index) - end + records[index] end # Find records using a simple ActiveRecord-like syntax. # # Examples: @@ -156,25 +147,42 @@ else s end end + # Returns the record at <tt>index</tt> by seeking to the record in the + # physical database file. See the documentation for the records method for + # information on how these two methods differ. + def get_record_from_file(index) + seek_to_record(@db_index[index]) + Record.new(self) + end + private def open_memo(file) - %w(fpt FPT dbt DBT).each do |extension| - filename = file.sub(/#{File.extname(file)[1..-1]}$/, extension) + %w(fpt FPT dbt DBT).each do |extname| + filename = replace_extname(file, extname) if File.exists?(filename) - @memo_file_format = extension.downcase.to_sym + @memo_file_format = extname.downcase.to_sym return File.open(filename, 'rb') end end nil end + + def replace_extname(filename, extension) + filename.sub(/#{File.extname(filename)[1..-1]}$/, extension) + end def deleted_record? - @data.read(1).unpack('a') == ['*'] + if @data.read(1).unpack('a') == ['*'] + @data.rewind + true + else + false + end end def get_header_info @data.rewind @version, @record_count, @header_length, @record_length = @data.read(DBF_HEADER_SIZE).unpack('H2 x3 V v2') @@ -187,20 +195,21 @@ name, type, length, decimal = @data.read(32).unpack('a10 x a x4 C2') if length > 0 @columns << Column.new(name.strip, type, length, decimal) end end - # Reset the column count + # Reset the column count in case any were skipped @column_count = @columns.size @columns end def get_memo_header_info @memo.rewind if @memo_file_format == :fpt @memo_next_available_block, @memo_block_size = @memo.read(FPT_HEADER_SIZE).unpack('N x2 n') + @memo_block_size = 0 if @memo_block_size.nil? else @memo_block_size = 512 @memo_next_available_block = File.size(@memo.path) / @memo_block_size end end @@ -211,26 +220,9 @@ def seek_to_record(index) seek(index * @record_length) end - # Returns the record at <tt>index</tt> by seeking to the record in the - # physical database file. See the documentation for the records method for - # information on how these two methods differ. - def get_record_from_file(index) - seek_to_record(@db_index[index]) - deleted_record? ? nil : Record.new(self) - end - - def get_all_records_from_file - all_records = [] - 0.upto(@record_count - 1) do |n| - seek_to_record(n) - all_records << DBF::Record.new(self) unless deleted_record? - end - all_records - end - def build_db_index @db_index = [] @deleted_records = [] 0.upto(@record_count - 1) do |n| seek_to_record(n) \ No newline at end of file