Class: Flat::File

Inherits:
Object show all
Includes:
Errors, Field, FileData, Layout, ReadOperations, Record
Defined in:
lib/flat/file.rb

Overview

Flat::File

Flat files are typically plain/text files containing many lines of text, or data. Each line, or record, consists of one or more fields. However, unlike CSV (comma separated value) files, there are no delimiters to define where values end or begin. Flat provides a mechanism of defining a file’s record structure, allowing users to iterate over a given file and access its data as typical Ruby objects (String, Numeric, Date, Booleans, etc.).

Specification

A flat file’s specification is defined within the subclass of Flat::File. The use of add_field and pad define and document the record structure.

# Actual plain text, flat file data, 29 bytes
#
#           10        20
# 012345678901234567890123456789
# Walt      Whitman   18190531
# Linus     Torvalds  19691228

class People < Flat::File
  add_field :first_name, width: 10, filter: :trim
  add_field :last_name,  width: 10, filter: ->(v) { v.strip }
  add_field :birthday,   width: 8,  filter: BirthdayFilter
  pad       :autoname,   width: 2

  def self.trim(v)
    v.strip
  end
end

You will notice the minimum required information is field name and width. The special case is with pad; you can specify a name but the general approach is to let Flat::File name it for you.

An alternate method of specifying fields is to pass a block to the add_field method. When using the block method you do not have to specify the name first. However, you do need to set the name inside the block. The value yielded to the block is an instance of Field::Definition.

class People < Flat::File
  add_field do |fd|
    fd.name = :first_name
    fd.width = 10
    fd.add_filter ->(v) { v.strip }
    fd.add_formatter ->(v) { v.strip }
  end

  add_field :last_name do |fd|
    fd.width = 10
    fd.add_filter ->(v) { v.strip }
    fd.add_formatter ->(v) { v.strip }
  end

end

Reading Data

After your Flat::File has been defined you will be able to read data from an actual file. Flat::File does not manage the opening and closing of Files or other IO instances (e.g. StringIO).

data_file = File.open('somefile.dat', 'r')

People.new.each_record(data_file) do |person|
  puts "First Name: #{person.first_name}" # => Walter
  puts "Last Name : #{person.last_name}"  # => White
  puts "Birthday  : #{person.birthday}"   # => 19590907
end

data_file.close

each_record yields a Record::Definition and the line of text that produced it. This object will respond to the field names defined by add_field. Plus, depending on the filters defined, the resulting data can be nearly any Ruby type: String, Symbol, Boolean, Date, etc. This allows you to deal with the data as it was intended.

Writing Data

Slated for a future release.

Filters

When adding new fields you can specify a filter through which the incoming data will be passed. In the absence of a filter the resulting values from the Record::Definition will be the raw text as found in the flat file.

You can define filters in 1 of four ways: Symbol, Proc, Class, or Object.

Operationally they all function the same way: used when reading the raw text in the file to produce a filtered value for storage in a Record::Definition.

Defined as a Symbol

add_field :first_name, width: 10, filter: :trim

trim, in this case, must be defined on the Flat::File subclass.

def self.trim(v)
  v.strip
end

Use this method when more control over the filtering is required, such as handling Date parsing errors.

Defined as a Proc

add_field :last_name,  width: 10, filter: ->(v) { v.strip }

The proc is stored and later called when needed. If the filtering requirements are easy and direct, this method is appropriate.

Defined as a Class or Object

add_field :birthday,   width: 8,  filter: BirthdayFilter
add_field :birthday,   width: 8,  filter: BirthdayFilter.new

The Class or Object used must respond to filter. This method is very similar to the Symbol method, expect there is only 1 method to define.

def self.filter(v)
  date = Date.parse(v) rescue $!
  return date unless date.is_a? ArgumentError
  nil
end

def filter(v)
  self.class.filter(v)
end

Formatters

Slated for a future release.

Layouts

Slated for a future release.

Exceptions

  • FlatFileError - Generic error class and superclass of all other errors raised by Flat.

  • LayoutConstructorError - The specified layout definition was not valid.

  • RecordLengthError - Generic error having to do with line lengths not meeting expectations.

  • ShortRecordError - The incoming line was shorter than expectations defined.

  • LongRecordError - The incoming line was longer than expectations defined.

Method Summary

Methods included from ReadOperations

included

Methods included from Record

included

Methods included from Field

included

Methods included from Layout

included

Methods included from FileData

included