headache 0.1.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/headache.gemspec +1 -0
- data/lib/headache/batch.rb +8 -25
- data/lib/headache/document.rb +11 -73
- data/lib/headache/document_parser.rb +51 -0
- data/lib/headache/exception.rb +16 -0
- data/lib/headache/formatters.rb +1 -1
- data/lib/headache/record/base.rb +1 -1
- data/lib/headache/version.rb +1 -1
- data/lib/headache.rb +2 -0
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 80f79889e015c7a29218886c2beebc3b2cb74402
|
4
|
+
data.tar.gz: 632e5c29ed41d97e1d01993e66ae7cdd76380a93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d59922f2b2bd1ba75043ccc16d703af3da7961f0c6ccbb8077e1ca8a0284274eac95174bd19536d1beeea40ded6fb69abad283002bf984b6262167c57f4ff84
|
7
|
+
data.tar.gz: dc9e297eb06621ff5e39a339cc1620d153f415f0b813f7e3261d2d959712472818f51ac453f731c2a6481ab9fe91ac1837c2d3e502ac2d386cc5ade4f3e3e580
|
data/headache.gemspec
CHANGED
data/lib/headache/batch.rb
CHANGED
@@ -8,25 +8,9 @@ module Headache
|
|
8
8
|
:entry_class_code, :entry_description, :descriptive_date,
|
9
9
|
:total_debit, :total_credit, :entry_hash
|
10
10
|
|
11
|
-
@@record_classes = { header: Record::BatchHeader,
|
12
|
-
control: Record::BatchControl }
|
13
|
-
|
14
|
-
def self.header=(klass)
|
15
|
-
set_class :header, klass
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.control=(klass)
|
19
|
-
set_class :control, klass
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.set_class(type, klass)
|
23
|
-
fail "unknown record class: #{type}" unless @@record_classes[type].present?
|
24
|
-
@@record_classes[type] = klass
|
25
|
-
end
|
26
|
-
|
27
11
|
def parse(records)
|
28
|
-
@header =
|
29
|
-
@control =
|
12
|
+
@header = Record::BatchHeader.new(self, @document).parse(records.shift)
|
13
|
+
@control = Record::BatchControl.new(self, @document).parse(records.pop)
|
30
14
|
records.each { |r| @members << Headache::Record::Entry.new(self, @document).parse(r) }
|
31
15
|
self
|
32
16
|
end
|
@@ -47,21 +31,21 @@ module Headache
|
|
47
31
|
def self.type_from_service_code(code)
|
48
32
|
return :credit if code.to_s == '220'
|
49
33
|
return :debit if code.to_s == '225'
|
50
|
-
fail "unknown service code: #{code.inspect} (expecting 220 or 225)"
|
34
|
+
fail Headache::UnknownServiceCode, "unknown service code: #{code.inspect} (expecting 220 or 225)"
|
51
35
|
end
|
52
36
|
|
53
37
|
def service_code
|
54
38
|
return '220' if type == :credit
|
55
39
|
return '225' if type == :debit
|
56
|
-
fail "unknown batch type: #{type.inspect} (expecting :credit or :debit)"
|
40
|
+
fail Headache::UnknownBatchType, "unknown batch type: #{type.inspect} (expecting :credit or :debit)"
|
57
41
|
end
|
58
42
|
|
59
43
|
def header
|
60
|
-
@header ||=
|
44
|
+
@header ||= Record::BatchHeader.new self, @document
|
61
45
|
end
|
62
46
|
|
63
47
|
def control
|
64
|
-
@control ||=
|
48
|
+
@control ||= Record::BatchControl.new self, @documentec
|
65
49
|
end
|
66
50
|
|
67
51
|
def entries
|
@@ -73,8 +57,8 @@ module Headache
|
|
73
57
|
end
|
74
58
|
|
75
59
|
def to_h
|
76
|
-
{
|
77
|
-
|
60
|
+
{ batch_header: @header.to_h,
|
61
|
+
entries: @members.to_a.map(&:to_h),
|
78
62
|
batch_control: @control.to_h }
|
79
63
|
end
|
80
64
|
|
@@ -103,6 +87,5 @@ module Headache
|
|
103
87
|
def total_credit
|
104
88
|
@total_credit || entries.map(&:amount).select { |amt| (amt || 0) > 0 }.sum
|
105
89
|
end
|
106
|
-
|
107
90
|
end
|
108
91
|
end
|
data/lib/headache/document.rb
CHANGED
@@ -2,89 +2,28 @@ module Headache
|
|
2
2
|
class Document < Fixy::Document
|
3
3
|
attr_reader :batches
|
4
4
|
|
5
|
-
LINE_SEPARATOR = "\r\n"
|
6
|
-
|
7
5
|
delegate :add_entry, :'<<', to: :first_batch
|
8
6
|
|
9
|
-
@@record_classes = { batch: Batch,
|
10
|
-
header: Record::FileHeader,
|
11
|
-
control: Record::FileControl }
|
12
|
-
|
13
|
-
def self.header_class
|
14
|
-
@@record_classes[:header]
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.control_class
|
18
|
-
@@record_classes[:control]
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.header=(klass)
|
22
|
-
set_class :header, klass
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.control=(klass)
|
26
|
-
set_class :control, klass
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.extract_lines(string_or_file)
|
30
|
-
string = string_or_file.respond_to?(:read) ? string_or_file.read : string_or_file
|
31
|
-
string.split(LINE_SEPARATOR).reject { |line| line == Headache::Record::Overflow.new.generate.strip }
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.cleanse_records(records)
|
35
|
-
invalid_lines = records.reject { |line| Headache::Record::FileHeader.record_type_codes.values.include?(line.first.to_i) }
|
36
|
-
raise "uknown record type(s): #{invalid_lines.map(&:first).inspect}" if invalid_lines.any?
|
37
|
-
records
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.parse(string_or_file)
|
41
|
-
records = cleanse_records(extract_lines string_or_file)
|
42
|
-
header = header_class.new(nil).parse(records.shift)
|
43
|
-
control = control_class.new(nil).parse(records.pop)
|
44
|
-
batches = get_batches(records).map { |b| Headache::Batch.new(self).parse(b) }
|
45
|
-
new header, control, batches
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.get_batches(records)
|
49
|
-
batches = []
|
50
|
-
batch = []
|
51
|
-
records.each do |line|
|
52
|
-
if line.starts_with?(Headache::Record::BatchHeader.record_type_codes[:batch_header].to_s)
|
53
|
-
batches << batch unless batches.empty? && batch == []
|
54
|
-
batch = [line]
|
55
|
-
else
|
56
|
-
batch << line
|
57
|
-
end
|
58
|
-
end
|
59
|
-
batches << batch
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.set_class(type, klass)
|
63
|
-
fail "unknown record class: #{type}" unless @@record_classes[type].present?
|
64
|
-
@@record_classes[type] = klass
|
65
|
-
end
|
66
|
-
|
67
7
|
def initialize(header = nil, control = nil, batches = [])
|
68
8
|
@header = header
|
69
9
|
@control = control
|
70
10
|
@batches = batches
|
71
|
-
@header.document
|
72
|
-
@control.document = self unless @control.nil?
|
11
|
+
[@header, @control].each { |r| r.try :document=, self }
|
73
12
|
end
|
74
13
|
|
75
14
|
def first_batch
|
76
|
-
add_batch
|
15
|
+
add_batch Batch.new(self) if @batches.empty?
|
77
16
|
@batches.first
|
78
17
|
end
|
79
18
|
|
80
19
|
def batch
|
81
|
-
fail 'multiple batches detected, be more explicit or use first_batch' if @batches.count > 1
|
20
|
+
fail Headache::AmbiguousBatch, 'multiple batches detected, be more explicit or use first_batch' if @batches.count > 1
|
82
21
|
first_batch
|
83
22
|
end
|
84
23
|
|
85
24
|
def add_batch(batch)
|
86
25
|
batch.document = self
|
87
|
-
@batches << batch
|
26
|
+
@batches << batch
|
88
27
|
end
|
89
28
|
|
90
29
|
def <<(batch)
|
@@ -97,17 +36,17 @@ module Headache
|
|
97
36
|
end
|
98
37
|
|
99
38
|
def header
|
100
|
-
@header ||=
|
39
|
+
@header ||= Record::FileHeader.new self
|
101
40
|
end
|
102
41
|
|
103
42
|
def control
|
104
|
-
@control ||=
|
43
|
+
@control ||= Record::FileControl.new self
|
105
44
|
end
|
106
45
|
|
107
46
|
def records
|
108
|
-
([
|
109
|
-
[
|
110
|
-
end << control).flatten
|
47
|
+
([header] << batches.map do |batch|
|
48
|
+
[batch.header, batch.entries, batch.control]
|
49
|
+
end << control).flatten
|
111
50
|
end
|
112
51
|
|
113
52
|
def lines
|
@@ -119,8 +58,8 @@ module Headache
|
|
119
58
|
end
|
120
59
|
|
121
60
|
def to_h
|
122
|
-
{
|
123
|
-
|
61
|
+
{ file_header: @header.to_h,
|
62
|
+
batches: @batches.map(&:to_h),
|
124
63
|
file_control: @control.to_h }
|
125
64
|
end
|
126
65
|
|
@@ -134,6 +73,5 @@ module Headache
|
|
134
73
|
append_record control
|
135
74
|
overflow_lines_needed.times { append_record Record::Overflow.new }
|
136
75
|
end
|
137
|
-
|
138
76
|
end
|
139
77
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Headache
|
2
|
+
class DocumentParser
|
3
|
+
LINE_SEPARATOR = "\r\n".freeze
|
4
|
+
|
5
|
+
def initialize(string_or_file)
|
6
|
+
@ach_string = string_or_file.respond_to?(:read) ? string_or_file.read : string_or_file
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse
|
10
|
+
fail Headache::InvalidRecordType, invalid_fail_message if invalid_ach?
|
11
|
+
|
12
|
+
Headache::Document.new(
|
13
|
+
Record::FileHeader.new(nil).parse(records.shift),
|
14
|
+
Record::FileControl.new(nil).parse(records.pop),
|
15
|
+
get_batches.map { |b| Headache::Batch.new(self).parse(b) }
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def records
|
22
|
+
@records ||= @ach_string.split(LINE_SEPARATOR).reject { |record| record == Headache::Record::Overflow.new.generate.strip }
|
23
|
+
end
|
24
|
+
|
25
|
+
def invalid_fail_message
|
26
|
+
"unknown record type(s): #{invalid_records.map(&:first).inspect}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def invalid_ach?
|
30
|
+
invalid_records.any?
|
31
|
+
end
|
32
|
+
|
33
|
+
def invalid_records
|
34
|
+
@invalid_records ||= records.reject { |record| Headache::Record::FileHeader.record_type_codes.values.include?(record.first.to_i) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_batches
|
38
|
+
batches = []
|
39
|
+
batch = []
|
40
|
+
records.each do |record|
|
41
|
+
if record.starts_with?(Headache::Record::BatchHeader.record_type_codes[:batch_header].to_s)
|
42
|
+
batches << batch unless batches.empty? && batch == []
|
43
|
+
batch = [record]
|
44
|
+
else
|
45
|
+
batch << record
|
46
|
+
end
|
47
|
+
end
|
48
|
+
batches << batch
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Headache
|
2
|
+
class Exception < ::RuntimeError
|
3
|
+
end
|
4
|
+
|
5
|
+
class InvalidRecordType < Headache::Exception
|
6
|
+
end
|
7
|
+
|
8
|
+
class AmbiguousBatch < Headache::Exception
|
9
|
+
end
|
10
|
+
|
11
|
+
class UnknownServiceCode < Headache::Exception
|
12
|
+
end
|
13
|
+
|
14
|
+
class UnknownBatchType < Headache::Exception
|
15
|
+
end
|
16
|
+
end
|
data/lib/headache/formatters.rb
CHANGED
@@ -36,7 +36,7 @@ module Headache
|
|
36
36
|
if input.is_a?(String) || input.is_a?(Fixnum)
|
37
37
|
chars = input.to_s.chars
|
38
38
|
minutes = chars.pop(2).join.to_i
|
39
|
-
hours = chars.join.to_i
|
39
|
+
hours = chars.first(2).join.to_i
|
40
40
|
input = Time.new 0, 1, 1, hours, minutes
|
41
41
|
end
|
42
42
|
fail "input #{input.inspect} does not respond to #strftime!" unless input.respond_to?(:strftime)
|
data/lib/headache/record/base.rb
CHANGED
data/lib/headache/version.rb
CHANGED
data/lib/headache.rb
CHANGED
@@ -4,9 +4,11 @@ require 'active_support/concern'
|
|
4
4
|
require 'active_support/core_ext/string'
|
5
5
|
require 'active_support/core_ext/enumerable'
|
6
6
|
|
7
|
+
require 'headache/exception'
|
7
8
|
require 'headache/formatters'
|
8
9
|
require 'headache/definitions'
|
9
10
|
require 'headache/record/base'
|
10
11
|
require 'headache/records'
|
11
12
|
require 'headache/batch'
|
12
13
|
require 'headache/document'
|
14
|
+
require 'headache/document_parser'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: headache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Payoff, Inc.
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-04-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: fixy
|
@@ -109,6 +109,20 @@ dependencies:
|
|
109
109
|
- - "~>"
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: '10.0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: pry
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - "~>"
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0.10'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - "~>"
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0.10'
|
112
126
|
description: Take the pain out of building ACH files. Wraps the Fixy gem for building
|
113
127
|
fixed-with ACH files as specified by NACHA.
|
114
128
|
email:
|
@@ -136,6 +150,8 @@ files:
|
|
136
150
|
- lib/headache/definition/file_header.rb
|
137
151
|
- lib/headache/definitions.rb
|
138
152
|
- lib/headache/document.rb
|
153
|
+
- lib/headache/document_parser.rb
|
154
|
+
- lib/headache/exception.rb
|
139
155
|
- lib/headache/formatters.rb
|
140
156
|
- lib/headache/record/base.rb
|
141
157
|
- lib/headache/record/batch_control.rb
|
@@ -166,9 +182,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
166
182
|
version: '0'
|
167
183
|
requirements: []
|
168
184
|
rubyforge_project:
|
169
|
-
rubygems_version: 2.4.
|
185
|
+
rubygems_version: 2.4.7
|
170
186
|
signing_key:
|
171
187
|
specification_version: 4
|
172
188
|
summary: Take the pain out of building ACH files.
|
173
189
|
test_files: []
|
174
|
-
has_rdoc:
|