module Eddy::Util

Assorted helper functions.

Public Class Methods

clean_folder(path) click to toggle source

Delete all files from a folder; returns number of deleted files.

Fails if the folder contains any symlinks.

@param path [String] Path to the folder. @return [Integer]

# File lib/eddy/util/clean_folder.rb, line 9
def self.clean_folder(path)
  dir = File.expand_path(path)
  return 0 unless Dir.exist?(dir)
  children = Dir.entries(dir).select { |f| File.file?(File.join(dir, f)) }
  deleted = File.unlink(*children)
  return deleted
end
data_dir() click to toggle source

Directory containing EDI data & definitions. @return [String]

# File lib/eddy/util/paths.rb, line 13
def self.data_dir
  return File.join(self.root_dir, "data")
end
element_ids() click to toggle source

Returns a hash where the keys are Element ids and the values are unprocessed Element names.

@example Example return value

{
  "1"  => "Route Code",
  "2"  => "Number of Accepted Transaction Sets",
  "3"  => "Free Form Message",
  "4"  => "Air Carrier Code",
  "5"  => "Airport Code",
  "7"  => "Bank Account Number",
  "8"  => "Bank Client Code",
  "9"  => "Late Reason Code",
  "11" => "Billing Code",
  ...,
}

@return [Hash<String, String>]

# File lib/eddy/util/edi_data.rb, line 38
def self.element_ids()
  file = File.join(Eddy::Util.data_dir, "elements-short.tsv")
  data = {}
  CSV.foreach(file, { col_sep: "\t", quote_char: "\x00", headers: false }) do |row|
    next if row ==  ["id", "name"]
    data[row[0]] = row[1]
  end
  return data
end
element_name_by_id(id) click to toggle source

Return the full name of an Element with the given id.

@example

Eddy::Util.element_name_by_id("93") #=> "Name"

@param id [String] ID of the Element to look up. @return [Hash]

# File lib/eddy/util/edi_data.rb, line 80
def self.element_name_by_id(id)
  data = Eddy::Util.element_ids()
  return data[id] if data.key?(id)
  raise Eddy::Errors::Error, "No element found with id #{id}"
end
list_built_elements() click to toggle source

List names of Elements with Ruby files currently in `Eddy.config.build_dir/elements`.

@example

Eddy::Util.list_built_elements() #=> []

@return [Array<String>]

# File lib/eddy/util/edi_data.rb, line 145
def self.list_built_elements()
  dir = File.join(Eddy.config.build_dir, "elements", "**", "*.rb")
  files = Dir.glob(dir)
  return files.map { |f| File.basename(f).sub(/\..*/, "").upcase }
end
list_element_classes() click to toggle source

List the names of Elements for which Ruby classes have already been built.

@example

Eddy::Util.list_element_classes() #=> ["166", "326", "349", "234", ...]

@return [Array<String>]

# File lib/eddy/util/edi_data.rb, line 105
def self.list_element_classes()
  dir = File.join(Eddy::Util.root_dir, "lib", "definitions", "elements", "**", "*.rb")
  files = Dir.glob(dir)
  return files.map { |f| File.basename(f).sub(/\..*/, "").upcase }
end
list_segment_classes() click to toggle source

List the names of Segments for which Ruby classes have already been built.

@example

Eddy::Util.list_segment_classes() #=> ["TD5", "N4", "TD1", "BIG", ...]

@return [Array<String>]

# File lib/eddy/util/edi_data.rb, line 117
def self.list_segment_classes()
  dir = File.join(Eddy::Util.root_dir, "lib", "definitions", "segments", "**", "*.rb")
  files = Dir.glob(dir)
  return files.map { |f| File.basename(f).sub(/\..*/, "").upcase }
end
list_segment_definitions() click to toggle source

List Segment definition files in `data/segments`.

@example Example return value

[
  "~/.rbenv/versions/2.6.5/lib/gems/eddy-0.0.0/data/segments/ack.segment.yml",
  "~/.rbenv/versions/2.6.5/lib/gems/eddy-0.0.0/data/segments/bak.segment.yml",
  ...,
]

@return [Array<String>]

# File lib/eddy/util/edi_data.rb, line 133
def self.list_segment_definitions()
  dir = File.join(Eddy::Util.data_dir, "segments")
  files = Dir.entries(dir).select { |f| File.file?(File.join(dir, f)) }
  return files.map { |f| File.join(dir, f) }.sort
end
new_number(existing) click to toggle source

Given an array of numbers, returns the lowest number not included in the array.

@param existing [Array<Integer>] @return [Integer]

# File lib/eddy/util/new_number.rb, line 8
def self.new_number(existing)
  n = nil
  i = 1
  loop do
    if existing.include?(i)
      i += 1
      next
    else
      n = i
      break
    end
  end
  return n
end
normalize_id(id) click to toggle source

Given an Element Id (positive number under 1688, or I01-I64), returns a string sutable for use as a Ruby class name.

@param id [String] @return [String]

# File lib/eddy/util/normalize.rb, line 9
def self.normalize_id(id)
  name_regex = /\A(?<prefix>[iI]{1})?(?<numbers>\d+)\Z/
  res = ""
  if matches = id.match(name_regex)
    if matches[:prefix]
      res << "I"
    else
      res << "E"
    end
    res << matches[:numbers]
  else
    raise Eddy::Errors::BuildError, "invalid element id"
  end
end
normalize_name(name) click to toggle source

Convert a string to [PascalCase](wiki.c2.com/?PascalCase), or UpperCamelCase Remove dashes, slashes, underscores, spaces, periods, and parens from a string then titleize it.

@param name [String] @return [String]

# File lib/eddy/util/normalize.rb, line 29
def self.normalize_name(name)
  return name.gsub(/\s*\(.*\)|[']/, "")
             .gsub(%r{[.,_\-/]}, " ")
             .split(" ")
             .map(&:capitalize)
             .join("")
end
parse_tsv(filepath) click to toggle source

Read a TSV file and return its contents as an array of hashes.

@param filepath [String] Path to the TSV file. @return [Array<Hash{Symbol => String}>]

# File lib/eddy/util/read.rb, line 12
def self.parse_tsv(filepath)
  return CSV.read(
    filepath,
    col_sep: "\t",
    headers: true,
    quote_char: "\x00",
    header_converters: :symbol,
  ).map(&:to_hash)
end
raw_element_data() click to toggle source

Return raw data from `data/elements.tsv`.

@example Example return value

[
  {:id=>"1", :name=>"Route Code", :type=>"AN", :min=>"1", :max=>"13", :description=>"Mutually defined route code"},
  {:id=>"2", :name=>"Number of Accepted Transaction Sets", :type=>"N0", :min=>"1", :max=>"6", :description=>"Number of accepted Transaction Sets in a Functional Group"},
  {:id=>"3", :name=>"Free Form Message", :type=>"AN", :min=>"1", :max=>"60", :description=>"Free-form text"},
  ...,
]

@return [Array<Hash>]

# File lib/eddy/util/edi_data.rb, line 17
def self.raw_element_data()
  return Eddy::Util.parse_tsv(File.join(Eddy::Util.data_dir, "elements.tsv"))
end
read_json_or_yaml(path, symbolize: true) click to toggle source

Read data in from either a JSON or YAML file.

@param path [String] Path to the file. @param symbolize [Boolean] (true) @return [Hash{Symbol => Object}]

# File lib/eddy/util/read.rb, line 27
def self.read_json_or_yaml(path, symbolize: true)
  path = File.expand_path(path)
  data = case File.extname(path).downcase
         when /\.ya?ml/ then YAML.safe_load(File.read(path), symbolize_names: symbolize)
         when ".json"   then JSON.parse(File.read(path), symbolize_names: symbolize)
         else raise Eddy::Errors::Error
         end
  return data
end
root_dir() click to toggle source

Directory where the gem is located. @return [String]

# File lib/eddy/util/paths.rb, line 7
def self.root_dir
  return File.expand_path("../../../..", __FILE__)
end
segment_ids() click to toggle source

Returns a hash where the keys are Segment ids and the values are unprocessed Segment names.

@example Example return value

{
  "AAA" => "Request Validation",
  "ACD" => "Account Description",
  "ACK" => "Line Item Acknowledgment",
  "ACS" => "Ancillary Charges",
  "ACT" => "Account Identification",
  "AD1" => "Adjustment Amount",
  "ADI" => "Animal Disposition",
  ...,
}

@return [Hash<String, String>]

# File lib/eddy/util/edi_data.rb, line 63
def self.segment_ids()
  file = File.join(Eddy::Util.data_dir, "segments.tsv")
  data = {}
  CSV.foreach(file, { col_sep: "\t", quote_char: "\x00" }) do |row|
    next if row ==  ["id", "name"]
    data[row[0]] = row[1]
  end
  return data
end
segment_name_by_id(id) click to toggle source

Return the full name of a Segment with the given id.

@example

Eddy::Util.segment_name_by_id("N2") #=> "Additional Name Information"

@param id [String] ID of the Segment to look up. @return [Hash]

# File lib/eddy/util/edi_data.rb, line 93
def self.segment_name_by_id(id)
  data = Eddy::Util.segment_ids()
  return data[id] if data.key?(id)
  raise Eddy::Errors::Error, "No segment found with id #{id}"
end
snake_case(name) click to toggle source

Convert a string to [snake_case](en.wikipedia.org/wiki/Snake_case)

@param name [String] @return [String]

# File lib/eddy/util/normalize.rb, line 41
def self.snake_case(name)
  return name.gsub(/\s*\(.*\)|['.]/, "")
             .gsub(%r{[,_\-/]}, " ")
             .split(" ")
             .map(&:downcase)
             .join("_")
end
timestamp() click to toggle source

@return [String]

# File lib/eddy/util/timestamp.rb, line 7
def self.timestamp
  return ::Time.now.strftime("%m-%d-%Y-%H-%M-%S")
end
trim_delims_from_interchange(itch, element_separator: "*", segment_separator: "~") click to toggle source

See: [Trailing delimiters and 999 response - X12 RFI](www.x12.org/rfis/Trailing%20delimiters%20and%20999%20response.pdf)

@param itch [String] String containing an EDI Interchange. @param element_separator [String] (“*”) @param segment_separator [String] (“~”) @return [String]

# File lib/eddy/util/trim.rb, line 11
def self.trim_delims_from_interchange(itch, element_separator: "*", segment_separator: "~")
  e_sep = Regexp.escape(element_separator)
  s_sep = Regexp.escape(segment_separator)
  return itch.gsub(/#{e_sep}+(?=#{s_sep})/, "")
end
trim_delims_from_segment(segment, separator: "*") click to toggle source

See: [Trailing delimiters and 999 response - X12 RFI](www.x12.org/rfis/Trailing%20delimiters%20and%20999%20response.pdf)

@param segment [String] String containing an EDI segment. @param separator [String] (“*”) @return [String]

# File lib/eddy/util/trim.rb, line 22
def self.trim_delims_from_segment(segment, separator: "*")
  e_sep = Regexp.escape(separator)
  return segment.gsub(/#{e_sep}+(?=$)/, "")
end