class RADT::Table

RADT::Table is the primary interface to a single RADT file and provides methods for enumerating and searching the records.

Attributes

column_count[R]
columns[R]
data[R]
options[R]
record_count[R]

Public Class Methods

new(path) click to toggle source

Opens a RADT:Table Example: table = RADT::Table.new 'data.adt'

@param [String] path Path to the adt file

# File lib/radt/table.rb, line 18
def initialize(path)
  @data = File.open(path, 'rb')
  reload!
end

Public Instance Methods

close() click to toggle source

Closes the table

# File lib/radt/table.rb, line 24
def close
  @data.close
end
column(column_name) click to toggle source

Retrieve a Column by name

@param [String, Symbol] column_name @return [RADT::Column]

# File lib/radt/table.rb, line 40
def column(column_name)
  @columns.detect {|f| f.name == column_name.to_s}
end
each() { |record| ... } click to toggle source

Calls block once for each record in the table. The record may be nil if the record has been marked as deleted.

@yield [nil, RADT::Record]

# File lib/radt/table.rb, line 48
def each
  0.upto(@record_count - 1) do |n|
    seek_to_record(n)
    yield RADT::Record.new(self)
  end
end
find(command, options = {}, &block) click to toggle source

Find records using a simple ActiveRecord-like syntax.

Examples: table = RADT::Table.new 'mydata.adt'

# Find record number 5 table.find(5)

# Find all records for Chase Gray table.find :all, :first_name => “Chase”, :last_name => “Gray”

# Find first record table.find :first, :first_name => “Chase”

The command may be a record index, :all, or :first. options is optional and, if specified, should be a hash where the keys correspond to column names in the database. The values will be matched exactly with the value in the database. If you specify more than one key, all values must match in order for the record to be returned. The equivalent SQL would be “WHERE key1 = 'value1' AND key2 = 'value2'”.

@param [Fixnum, Symbol] command @param [optional, Hash] options Hash of search parameters @yield [optional, RADT::Record]

# File lib/radt/table.rb, line 147
def find(command, options = {}, &block)
  case command
  when Fixnum
    record(command)
  when Array
    command.map {|i| record(i)}
  when :all
    find_all(options, &block)
  when :first
    find_first(options)
  end
end
record(index) click to toggle source

Retrieve a record by index number

@param [Fixnum] index @return [RADT::Record]

# File lib/radt/table.rb, line 59
def record(index)
  seek_to_record(index)
  RADT::Record.new(self)
end
Also aliased as: row
reload!() click to toggle source

Reloads the database

# File lib/radt/table.rb, line 29
def reload!
  @records = nil
  get_header_info
  get_column_descriptors
end
row(index)
Alias for: record
schema(path = nil) click to toggle source

Generate an ActiveRecord::Schema

xBase data types are converted to generic types as follows:

  • Number columns with no decimals are converted to :integer

  • Number columns with decimals are converted to :float

  • Date columns are converted to :datetime

  • Logical columns are converted to :boolean

  • Memo columns are converted to :text

  • Character columns are converted to :string and the :limit option is set

to the length of the character column

Example: create_table “mydata” do |t| t.column :name, :string, :limit => 30 t.column :last_update, :datetime t.column :is_active, :boolean t.column :age, :integer t.column :notes, :text end

@param [optional String] path @return [String]

# File lib/radt/table.rb, line 89
def schema(path = nil)
  s = "ActiveRecord::Schema.define do\n"
  s << " create_table \"#{File.basename(@data.path, ".*")}\" do |t|\n"
  columns.each do |column|
    s << " t.column #{column.schema_definition}"
  end
  s << " end\nend"

  if path
    File.open(path, 'w') {|f| f.puts(s)}
  end

  s
end
to_a() click to toggle source
# File lib/radt/table.rb, line 104
def to_a
  records = []
  each {|record| records << record if record}
  records
end
to_csv(path = nil) click to toggle source

Dumps all records to a CSV file. If no filename is given then CSV is output to STDOUT.

@param [optional String] path Defaults to basename of adt file

# File lib/radt/table.rb, line 114
def to_csv(path = nil)
  path = File.basename(@data.path, '.adt') + '.csv' if path.nil?
  CSV.open(path, 'w', :force_quotes => true) do |csv|
    each do |record|
      csv << record.to_a
    end
  end
end

Private Instance Methods

all_values_match?(record, options) click to toggle source

Do all search parameters match?

@param [RADT::Record] record @param [Hash] options @return [Boolean]

# File lib/radt/table.rb, line 197
def all_values_match?(record, options)
  options.all? {|key, value| record.attributes[key.to_s.underscore] == value}
end
find_all(options) { |record| ... } click to toggle source

Find all matching

@param [Hash] options @yield [optional RADT::Record] @return [Array]

# File lib/radt/table.rb, line 167
def find_all(options, &block)
  results = []
  each do |record|
    if all_values_match?(record, options)
      if block_given?
        yield(record)
      else
        results << record
      end
    end
  end
  results
end
find_first(options) click to toggle source

Find first matching

@param [Hash] options @return [RADT::Record, nil]

# File lib/radt/table.rb, line 185
def find_first(options)
  each do |record|
    return record if all_values_match?(record, options)
  end
  nil
end
get_column_descriptors() click to toggle source

Retrieves column information from the database

# File lib/radt/table.rb, line 223
def get_column_descriptors
  #skip past header to get to column information
  @data.seek(HEADER_LENGTH)

  # column names are the first 128 bytes and column info takes up the last 72 bytes.
  # byte 130 contains a 16-bit column type
  # byte 136 contains a 16-bit length field
  @columns = []
  @column_count.times do
    name, type, length = @data.read(200).unpack('A128 x S x4 S')
    if length > 0
      @columns << Column.new(name.strip, type, length)
    end
  end
  # Reset the column count in case any were skipped
  @column_count = @columns.size

  @columns
end
get_header_info() click to toggle source

Determine record count, record_count, and record length

# File lib/radt/table.rb, line 213
def get_header_info
  @data.rewind

  #column_count_offset = 33, record_count_offset = 24, record_length_offset = 36
  @record_count, @data_offset, @record_length = data.read(HEADER_LENGTH).unpack("@24 I x4 I I")
  @column_count = (@data_offset-400)/200
end
replace_extname(path, extension) click to toggle source

Replace the file extension

@param [String] path @param [String] extension @return [String]

# File lib/radt/table.rb, line 207
def replace_extname(path, extension)
  path.sub(/#{File.extname(path)[1..-1]}$/, extension)
end
seek(offset) click to toggle source

Seek to a byte offset in the record data

@params [Fixnum] offset

# File lib/radt/table.rb, line 247
def seek(offset)
  @data.seek(@data_offset + offset)
end
seek_to_record(index) click to toggle source

Seek to a record

@param [Fixnum] index

# File lib/radt/table.rb, line 254
def seek_to_record(index)
  seek(index * @record_length)
end