module AdHocTemplate::RecordReader::CSVReader

Constants

COL_SEP

Public Class Methods

dump(config_data, col_sep=COL_SEP[:csv]) click to toggle source
# File lib/ad_hoc_template/record_reader.rb, line 59
def self.dump(config_data, col_sep=COL_SEP[:csv])
  data = RecordReader.parse_if_necessary(config_data)
  raise NotSupportedError unless csv_compatible_format?(data)

  kv_pairs = find_sub_records(data)
  records = kv_pairs ? hashes_to_arrays(kv_pairs) : data.to_a.transpose

  array_to_csv(records, col_sep)
end
read_record(csv_data, config={ csv: nil }) click to toggle source
# File lib/ad_hoc_template/record_reader.rb, line 49
def self.read_record(csv_data, config={ csv: nil })
  label, sep = parse_config(config)
  header, *data = csv_to_array(csv_data, sep, label)
  csv_records = data.map {|row| convert_to_hash(header, row) }
  if label && label.index('|')
    return compose_inner_iteration_records(csv_records, label)
  end
  compose_record(csv_records, label)
end

Private Class Methods

array_to_csv(records, col_sep) click to toggle source
# File lib/ad_hoc_template/record_reader.rb, line 145
def self.array_to_csv(records, col_sep)
  # I do not adopt "records.map {|rec| rec.to_csv }.join",
  # because I'm not sure if it is sufficient for certain data or not.
  # For example, a field value may contain carriage returns or line
  # feeds, and in that case, improper handling of the end of record
  # would be damaging.

  CSV.generate(+'', col_sep: col_sep) do |csv|
    records.each {|record| csv << record }
  end
end
compose_inner_iteration_records(csv_records, given_label, main_record={}) click to toggle source
# File lib/ad_hoc_template/record_reader.rb, line 105
def self.compose_inner_iteration_records(csv_records, given_label,
                                         main_record={})
  outer_label, inner_label, key = ('#' + given_label).split(/\|/, 3)
  values = inner_iteration_records(csv_records, key)
  labels = inner_iteration_labels(outer_label, inner_label, values.keys)
  unless main_record[outer_label]
    main_record[outer_label] = values.keys.map {|k| { key => k } }
  end
  values.keys.each {|k| main_record[labels[k]] = values[k] }
  main_record
end
compose_record(csv_records, label) click to toggle source
# File lib/ad_hoc_template/record_reader.rb, line 95
def self.compose_record(csv_records, label)
  if label
    { '#' + label => csv_records }
  elsif csv_records.length == 1
    csv_records[0]
  else
    csv_records
  end
end
convert_to_hash(header, row_array) click to toggle source
# File lib/ad_hoc_template/record_reader.rb, line 69
def self.convert_to_hash(header, row_array)
  {}.tap do |record|
    header.zip(row_array).each {|key, value| record[key] = value }
  end
  # if RUBY_VERSION >= 2.1.0: header.zip(row_array).to_h
end
csv_compatible_format?(data) click to toggle source
# File lib/ad_hoc_template/record_reader.rb, line 129
def self.csv_compatible_format?(data)
  iteration_blocks_count = data.values.count {|v| v.kind_of? Array }
  iteration_blocks_count.zero? ||
    (iteration_blocks_count == 1 && data.size == 1)
end
csv_to_array(csv_data, col_sep, label) click to toggle source
# File lib/ad_hoc_template/record_reader.rb, line 89
def self.csv_to_array(csv_data, col_sep, label)
  array = CSV.new(csv_data, col_sep: col_sep).to_a
  array = array.transpose if !label || label == HEADER_POSITION::LEFT
  array
end
find_sub_records(data) click to toggle source
# File lib/ad_hoc_template/record_reader.rb, line 141
def self.find_sub_records(data)
  data.values.find {|v| v.kind_of? Array }
end
hashes_to_arrays(data) click to toggle source
# File lib/ad_hoc_template/record_reader.rb, line 135
def self.hashes_to_arrays(data)
  headers = data.max_by {|h| h.keys.size }.keys
  records = data.map {|record| headers.map {|header| record[header] } }
  records.unshift headers
end
inner_iteration_labels(outer_label, inner_label, keys) click to toggle source
# File lib/ad_hoc_template/record_reader.rb, line 123
def self.inner_iteration_labels(outer_label, inner_label, keys)
  keys.each_with_object({}) do |key, labels|
    labels[key] = [outer_label, inner_label, key].join('|')
  end
end
inner_iteration_records(csv_records, key) click to toggle source
# File lib/ad_hoc_template/record_reader.rb, line 117
def self.inner_iteration_records(csv_records, key)
  values = Hash.new {|h, k| h[k] = [] }
  csv_records.each {|record| values[record[key]].push record }
  values
end
parse_config(config) click to toggle source
# File lib/ad_hoc_template/record_reader.rb, line 76
def self.parse_config(config)
  case config
  when Symbol
    format, label = config, nil
  when String
    format, label = :csv, config
  when Hash
    format, label = config.to_a[0]
  end
  col_sep = COL_SEP[format || :csv]
  [label, col_sep]
end