class Bai2::Record
This class represents a record. It knows how to parse the single record information, but has no knowledge of the structure of the file.
Constants
- AssertVersion2
This block ensures that only version 2 of the BAI standard is accepted
- CleanContinuedText
Cleans up text in continuations, removing leading commas
- ParseDate
Returns a date object
- ParseMilitaryTime
Returns a time interval in seconds, to be added to the date
- ParseTypeCode
Parses a type code, returns a structured informative hash
- RECORD_CODES
- SIMPLE_FIELD_MAP
For each record code, this defines a simple way to automatically parse the fields. Each field has a list of the keys. Some keys are not simply string types, in which case they will be formatted as a tuple (key, fn), where fn is a block (or anything that responds to `to_proc`) that will be called to cast the value (e.g. `:to_i`).
Attributes
Public Class Methods
# File lib/bai2/record.rb, line 115 def initialize(line, physical_record_count = 1, options: {}) @code = RECORD_CODES[line[0..1]] @physical_record_count = physical_record_count # clean / delimiter @raw = if options[:continuations_slash_delimit_end_of_line_only] # Continuation records for transaction details extend the text fields # and they may begin with 88,/ but should include the rest of the line. # A proper fix would involve each continuation record knowing what field it was extending. line.sub(/\/$/, '') else line.sub(/,\/.+$/, '').sub(/\/$/, '') end end
Public Instance Methods
A record can be accessed like a hash.
# File lib/bai2/record.rb, line 140 def [](key) fields[key] end
NOTE: fields is called upon first use, so as not to parse records right away in case they might be merged with a continuation.
# File lib/bai2/record.rb, line 134 def fields @fields ||= parse_raw(@code, @raw) end
Private Instance Methods
# File lib/bai2/record.rb, line 194 def parse_account_identifier_fields(record) # split out the constant bits record_code, customer, currency_code, rest = record.split(',', 4).map(&:strip) common = { record_code: record_code, customer: customer, currency_code: currency_code, summaries: [], } # sadly, imperative style seems cleaner. would prefer it functional. until rest.nil? || rest.empty? type_code, amount, items_count, funds_type, rest \ = rest.split(',', 5).map(&:strip) amount_details = { type: ParseTypeCode[type_code], amount: amount.to_i, items_count: items_count, funds_type: funds_type, } # handle funds_type logic funds_info, rest = *parse_funds_type(funds_type, rest) with_funds_availability = amount_details.merge(funds_info) common[:summaries] << with_funds_availability end common end
Takes a `fund_type` field, and the rest, and return a hashed of interpreted values, and the new rest.
funds_type, rest = ... funds_info, rest = *parse_funds_type(funds_type, rest)
# File lib/bai2/record.rb, line 235 def parse_funds_type(funds_type, rest) info = \ case funds_type when 'S' now, next_day, later, rest = rest.split(',', 4).map(&:strip) { availability: [ {day: 0, amount: now}, {day: 1, amount: now}, {day: '>1', amount: now}, ] } when 'V' value_date, value_hour, rest = rest.split(',', 3).map(&:strip) value_hour = '2400' if value_hour == '9999' { value_dated: {date: value_date, hour: value_hour} } when 'D' field_count, rest = rest.split(',', 2).map(&:strip) availability = field_count.to_i.times.map do days, amount, rest = rest.split(',', 3).map(&:strip) {days: days.to_i, amount: amount} end {availability: availability} else {} end [info, rest] end
# File lib/bai2/record.rb, line 146 def parse_raw(code, line) fields = (SIMPLE_FIELD_MAP[code] || []) if !fields.empty? split = line.split(',', fields.count).map(&:strip) Hash[fields.zip(split).map do |k,v| next [k,v] if k.is_a?(Symbol) key, block = k [key, block.to_proc.call(v)] end] elsif respond_to?("parse_#{code}_fields".to_sym, true) send("parse_#{code}_fields".to_sym, line) else raise ParseError.new('Unknown record code.') end end
Special cases need special implementations.
The rules here are pulled from the specification at this URL: www.bai.org/Libraries/Site-General-Downloads/Cash_Management_2005.sflb.ashx
# File lib/bai2/record.rb, line 168 def parse_transaction_detail_fields(record) # split out the constant bits record_code, type_code, amount, funds_type, rest = record.split(',', 5).map(&:strip) common = { record_code: record_code, type: ParseTypeCode[type_code], amount: amount.to_i, funds_type: funds_type, } # handle funds_type logic funds_info, rest = *parse_funds_type(funds_type, rest) with_funds_availability = common.merge(funds_info) # split the rest of the constant fields bank_ref, customer_ref, text = rest.split(',', 3).map(&:strip) with_funds_availability.merge( bank_reference: bank_ref, customer_reference: customer_ref, text: CleanContinuedText[text], ) end