Class: CSVobj

Inherits:
Object
  • Object
show all
Defined in:
lib/csvobj.rb

Overview

This class represents each row of a CSV file as an object in an array. Object attributes are automatically created from the header (first) row in the CSV. Note that this class dynamically redefines its own attributes and constructor so you should subclass it for any remotely serious work.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.header_to_attr(header) ⇒ Object

Convert a header (string) value to a an attribute symbol: Remove leading and trailing whitespace; replace (repeated) non-word characters with a single underscore; prefix leading digit with underscore; remove repeated adjacent underscores; convert to a lower-case symbol.



75
76
77
78
79
80
81
82
# File 'lib/csvobj.rb', line 75

def self.header_to_attr(header)
  header.strip. # remove trailing and leading whitespace
    gsub(/\W+/, '_').  # substitute underscore for non-word/digit characters
    sub(/^(\d)/, '_\1'). # if starts with a digit insert a leading underscore
    gsub(/_+/, '_'). # remove duplicate adjacent underscores
    downcase. # convert to lower case
    to_sym # convert to symbol
end

.parse(csv) ⇒ Object

Parse the given CSV, which may be a multi-line string or IO object (see #parse_s) or an array of row arrays (see #parse_a) and return an array of CSVobjs. Expects the first row to be the headers (these are used for the objects’ attributes).



22
23
24
# File 'lib/csvobj.rb', line 22

def self.parse(csv)
  csv.is_a?(Array) ? parse_a(csv) : parse_s(csv)
end

.parse_a(rows) ⇒ Object

Interpret an array (rows) of arrays (cells) and return an array of CSVobjs. Expects the first row to be the headers (these are used for the objects’ attributes).



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/csvobj.rb', line 39

def self.parse_a(rows)
  return [] if rows.empty?

  # Get attributes from first row.
  headers = rows.shift
  attr_symbols = headers.map{ |header| header_to_attr(header) }
  dupes = attr_symbols.uniq!
  raise CSVobjDuplicateHeader, "Duplicate derived headers\n#{dupes}" if dupes

  # Declare attributes and constructor. This will take the form:
  #     #new(header1, header2, ...).
  instance_variables = attr_symbols.map{ |attr| "@#{attr}" }
  class_eval do
    attr_accessor(*attr_symbols)
    define_method :initialize do |*args|
      instance_variables.zip(args).each do |instance_variable, arg|
        instance_variable_set(instance_variable, arg)
      end
    end
  end

  # Define class method #headers to return an array of the (unmangled)
  # CSV headers by defining the method on the metaclass.
  (class << self ; self end).send(:define_method, :headers) { headers }

  # Create and return array of CSVobj, one element for each CSV row
  # (minus the headers).
  rows.map { |row| new(*row) }
end

.parse_s(csv) ⇒ Object

Interpret the given multi-line string or IO object of CSV records and return an array of CSVobjs. Expects the first row to be the headers (these are used for the objects’ attributes).



30
31
32
33
# File 'lib/csvobj.rb', line 30

def self.parse_s(csv)
  rows = s_to_a(csv)
  parse_a(rows)
end

Instance Method Details

#to_aObject

Return an array of (string) values for this CSV object in the order that they were given. See dynamically defined method headers to get the equivalent array of headers.



87
88
89
90
91
92
# File 'lib/csvobj.rb', line 87

def to_a
  self.class.headers.map do |header|
    attr = self.class.header_to_attr(header)
    send(attr)
  end
end

#to_sObject

Return a CSV string representing this object. Does not include headers (see #to_s_with_headers).



96
97
98
# File 'lib/csvobj.rb', line 96

def to_s
  a_to_s(to_a)
end

#to_s_with_headersObject

Return a CSV string representing this object including an initial line of headers as originally given on object creation.



102
103
104
# File 'lib/csvobj.rb', line 102

def to_s_with_headers
  a_to_s(self.class.headers) + to_s
end