nacha 0.1.0 → 0.1.2
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/.ruby-version +1 -1
- data/Gemfile +0 -5
- data/README.md +6 -3
- data/bin/console +0 -3
- data/exe/nacha +74 -0
- data/lib/nacha/ach_date.rb +33 -6
- data/lib/nacha/field.rb +50 -39
- data/lib/nacha/numeric.rb +5 -7
- data/lib/nacha/parser.rb +2 -6
- data/lib/nacha/record/base.rb +40 -25
- data/lib/nacha/record/validations/field_validations.rb +20 -18
- data/lib/nacha/record/validations/record_validations.rb +7 -6
- data/lib/nacha/version.rb +1 -1
- data/lib/nacha.rb +23 -13
- data/nacha.gemspec +4 -6
- data/templates/html_postamble.html +1 -0
- data/templates/html_preamble.html +68 -0
- metadata +29 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6d958fcc8edde96b13f6545611fb4fc86a751f4945955fe2f4960344bacc1ff
|
4
|
+
data.tar.gz: 6b9cbe9d4d61638f847595d76316b78263bf7b807ef1ed6c78f11e66348c7bc6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d559ecb5008d9e71e5c958dbc3400d35da7247c2a3f85f727adf50be020ac56c6b0ca30d470eff1247f45df05e94a603bc0754192ed7fc0baeee937abcd32ee
|
7
|
+
data.tar.gz: 718bebf3b746d052cfa2ad0c4e3390b1fef291dd16a588f25e493b1ef9f336c97ad4392951e6fe743aa5e4085430bed2b649940f7c2770733abc31c4f70b3c1e
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
3.4.4
|
data/Gemfile
CHANGED
@@ -2,8 +2,3 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in nacha.gemspec
|
4
4
|
gemspec
|
5
|
-
|
6
|
-
gem 'pry-byebug', group: %i[development test]
|
7
|
-
gem 'pry-highlight', group: %i[development test]
|
8
|
-
gem 'pry-remote', group: %i[development test]
|
9
|
-
gem 'pry-stack_explorer', group: %i[development test]
|
data/README.md
CHANGED
@@ -30,11 +30,10 @@ Or install it yourself as:
|
|
30
30
|
API may change at any time. Pull requests welcomed
|
31
31
|
|
32
32
|
|
33
|
-
`"101 124000054 1240000540907021214A094101ZIONS FIRST NATIONAL BAZIONS FIRST NATIONAL BA 1"`
|
34
33
|
|
35
34
|
```ruby
|
36
|
-
|
37
|
-
|
35
|
+
ach_string = "101 124000054 1240000540907021214A094101ZIONS FIRST NATIONAL BAZIONS FIRST NATIONAL BA 1"
|
36
|
+
ach_records = Nacha::Parser.parse_string(ach_string)
|
38
37
|
ach_records[0].class # => Nacha::Record::FileHeaderRecord
|
39
38
|
|
40
39
|
ach_records[0].to_json
|
@@ -57,6 +56,10 @@ API may change at any time. Pull requests welcomed
|
|
57
56
|
}
|
58
57
|
```
|
59
58
|
|
59
|
+
## Parse an ach file into an HTML file
|
60
|
+
|
61
|
+
```
|
62
|
+
nacha parse ach_file.ach > ach_file.html`
|
60
63
|
|
61
64
|
## Development
|
62
65
|
|
data/bin/console
CHANGED
@@ -6,9 +6,6 @@ require "nacha"
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
8
8
|
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
9
|
|
13
10
|
require "irb"
|
14
11
|
IRB.start(__FILE__)
|
data/exe/nacha
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
require 'nacha' # Assuming this loads the Nacha gem
|
5
|
+
|
6
|
+
module Nacha
|
7
|
+
class CLI < Thor
|
8
|
+
|
9
|
+
TEMPLATES_DIR = File.join(Gem::Specification.find_by_name("nacha").gem_dir,
|
10
|
+
"templates").freeze
|
11
|
+
|
12
|
+
HTML_PREAMBLE_FILE = File.join(TEMPLATES_DIR, "html_preamble.html")
|
13
|
+
HTML_POSTAMBLE_FILE = File.join(TEMPLATES_DIR, "html_postamble.html")
|
14
|
+
|
15
|
+
desc "parse FILE", "Parse an ACH file"
|
16
|
+
def parse(file_path)
|
17
|
+
begin
|
18
|
+
unless File.exist?(file_path)
|
19
|
+
puts "Error: File not found at #{file_path}"
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
|
23
|
+
ach_file = [Nacha.parse(File.open(file_path)).first] # Use Nacha.parse
|
24
|
+
|
25
|
+
# TODO: Determine a user-friendly way to output the parsed data.
|
26
|
+
# For now, let's print the records.
|
27
|
+
if ach_file && ach_file.is_a?(Array) && !ach_file.empty?
|
28
|
+
output_html(file_path, ach_file)
|
29
|
+
else
|
30
|
+
puts "Could not parse the file or the file was empty."
|
31
|
+
end
|
32
|
+
rescue StandardError => e
|
33
|
+
puts "An error occurred during parsing: #{e.message}"
|
34
|
+
puts e.backtrace.join("\n")
|
35
|
+
exit 1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def output_html(file_path, ach_file)
|
42
|
+
puts html_preamble
|
43
|
+
puts "<h1>Successfully parsed #{file_path}</h1>\n"
|
44
|
+
display_child(0, ach_file.first, 0) # Display the first record
|
45
|
+
puts html_postamble
|
46
|
+
end
|
47
|
+
|
48
|
+
def html_preamble
|
49
|
+
@html_preamble ||= File.read(HTML_PREAMBLE_FILE)
|
50
|
+
end
|
51
|
+
|
52
|
+
def html_postamble
|
53
|
+
@html_postamble ||= File.read(HTML_POSTAMBLE_FILE)
|
54
|
+
end
|
55
|
+
|
56
|
+
def display_child(level, record, index)
|
57
|
+
# Attempt to call a summary or to_s method if it exists,
|
58
|
+
# otherwise inspect the record.
|
59
|
+
level_indent = ' ' * level.to_i
|
60
|
+
puts "<html>"
|
61
|
+
puts record.to_html
|
62
|
+
if record.respond_to?(:children) && record.children.any?
|
63
|
+
if record.children.any?
|
64
|
+
record.children.each_with_index do |child_record, child_index|
|
65
|
+
display_child(level + 1, child_record, child_index)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
puts "</html>"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
Nacha::CLI.start(ARGV)
|
data/lib/nacha/ach_date.rb
CHANGED
@@ -4,14 +4,41 @@ require 'date'
|
|
4
4
|
# A Date object to handle some of the Ach formatted dates
|
5
5
|
|
6
6
|
class Nacha::AchDate < Date
|
7
|
-
def self.new(
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
def self.new(*args)
|
8
|
+
year, month, day = nil
|
9
|
+
|
10
|
+
case args[0]
|
11
|
+
when String
|
12
|
+
date_str = args[0]
|
13
|
+
# Use Date.strptime to parse the string into a temporary Date object
|
14
|
+
temp_date = Date.strptime(date_str, '%y%m%d')
|
15
|
+
year, month, day = temp_date.year, temp_date.month, temp_date.day
|
16
|
+
when Date
|
17
|
+
original_date = args[0]
|
18
|
+
year, month, day = original_date.year, original_date.month, original_date.day
|
19
|
+
when Integer # If it's a year integer, assume (year, month, day) or single Julian day
|
20
|
+
# If 3 arguments (year, month, day) are provided like Date.new(2023, 10, 26)
|
21
|
+
if args.length == 3 && args.all? { |arg| arg.is_a?(Integer) }
|
22
|
+
year, month, day = args[0], args[1], args[2]
|
23
|
+
else
|
24
|
+
# Fallback for other Date.new arguments like (jd) - let super handle directly
|
25
|
+
return super(*args) # IMPORTANT: Call super to create the instance
|
26
|
+
end
|
12
27
|
else
|
13
|
-
|
28
|
+
# If it's none of the above, pass arguments directly to Date.new.
|
29
|
+
# This handles cases like Date.new(2459918) (Julian day) or other Date constructors.
|
30
|
+
return super(*args)
|
14
31
|
end
|
32
|
+
|
33
|
+
# If year, month, day were successfully parsed, create a Nacha::AchDate instance
|
34
|
+
# by calling the parent's `new` with the explicit components.
|
35
|
+
# `super()` in this context will call `Date.new(year, month, day)` but for *your* class.
|
36
|
+
# This works because `Date.new` is designed to be effectively `allocate.initialize`.
|
37
|
+
super(year, month, day)
|
38
|
+
|
39
|
+
rescue ArgumentError => e
|
40
|
+
# Catch errors that might arise from strptime or invalid date components
|
41
|
+
raise ArgumentError, "Invalid date format for Nacha::AchDate: #{args.inspect}. Original error: #{e.message}"
|
15
42
|
end
|
16
43
|
|
17
44
|
def to_s
|
data/lib/nacha/field.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
3
4
|
require 'bigdecimal'
|
4
5
|
require 'nacha/numeric'
|
5
6
|
require 'nacha/aba_number'
|
6
7
|
require 'nacha/ach_date'
|
7
8
|
|
8
9
|
class Nacha::Field
|
9
|
-
|
10
|
-
attr_accessor :
|
11
|
-
|
12
|
-
attr_reader :input_data
|
10
|
+
attr_accessor :inclusion, :position
|
11
|
+
attr_accessor :name, :errors
|
12
|
+
attr_reader :contents, :data, :input_data
|
13
13
|
attr_reader :data_type
|
14
14
|
attr_reader :validator
|
15
15
|
attr_reader :justification
|
@@ -17,17 +17,17 @@ class Nacha::Field
|
|
17
17
|
attr_reader :output_conversion
|
18
18
|
attr_reader :json_output
|
19
19
|
|
20
|
-
def initialize
|
20
|
+
def initialize(opts = {})
|
21
21
|
@data_type = String
|
22
22
|
@errors = []
|
23
|
-
@name = 'Unknown'
|
23
|
+
@name = 'Unknown'
|
24
24
|
@justification = :ljust
|
25
|
-
@fill_character =
|
25
|
+
@fill_character = ' '
|
26
26
|
@json_output = [:to_s]
|
27
27
|
@output_conversion = [:to_s]
|
28
|
-
opts.each do |k,v|
|
28
|
+
opts.each do |k, v|
|
29
29
|
setter = "#{k}="
|
30
|
-
if
|
30
|
+
if respond_to?(setter)
|
31
31
|
send(setter, v) unless v.nil?
|
32
32
|
end
|
33
33
|
end
|
@@ -35,99 +35,111 @@ class Nacha::Field
|
|
35
35
|
|
36
36
|
# CXXX Constant
|
37
37
|
#
|
38
|
-
def contents=
|
38
|
+
def contents=(val)
|
39
39
|
@contents = val
|
40
40
|
case @contents
|
41
|
-
when /\AC(.*)\z/
|
42
|
-
@data =
|
41
|
+
when /\AC(.*)\z/ # Constant
|
42
|
+
@data = Regexp.last_match(1)
|
43
43
|
when /Numeric/
|
44
44
|
@data_type = Nacha::Numeric
|
45
45
|
@justification = :rjust
|
46
46
|
@json_output = [[:to_i]]
|
47
47
|
@output_conversion = [:to_i]
|
48
|
-
@fill_character = '0'
|
48
|
+
@fill_character = '0'
|
49
49
|
when /.?TTTTAAAAC/
|
50
50
|
@data_type = Nacha::AbaNumber
|
51
51
|
@validator = :valid?
|
52
52
|
@justification = :rjust
|
53
53
|
@output_conversion = [:to_s]
|
54
|
-
@fill_character = ' '
|
54
|
+
@fill_character = ' '
|
55
55
|
when 'YYMMDD'
|
56
56
|
@data_type = Nacha::AchDate
|
57
57
|
@justification = :rjust
|
58
58
|
@validator = :valid?
|
59
59
|
@json_output = [[:iso8601]]
|
60
60
|
@output_conversion = [:to_s]
|
61
|
-
@fill_character = ' '
|
61
|
+
@fill_character = ' '
|
62
62
|
when 'Alphameric'
|
63
63
|
@data_type = String
|
64
64
|
@justification = :ljust
|
65
65
|
@output_conversion = [:to_s]
|
66
|
-
@fill_character = ' '
|
67
|
-
when
|
66
|
+
@fill_character = ' '
|
67
|
+
when /\$+\u00a2\u00a2/
|
68
68
|
@data_type = Nacha::Numeric
|
69
69
|
@justification = :rjust
|
70
|
-
@json_output = [[:to_i],[:/,100.0]]
|
70
|
+
@json_output = [[:to_i], [:/, 100.0]]
|
71
71
|
@output_conversion = [:to_i]
|
72
|
-
@fill_character = '0'
|
72
|
+
@fill_character = '0'
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
def data=
|
76
|
+
def data=(val)
|
77
77
|
@data = @data_type.new(val)
|
78
78
|
@input_data = val
|
79
79
|
end
|
80
80
|
|
81
81
|
def mandatory?
|
82
|
-
@inclusion == 'M'
|
82
|
+
@inclusion == 'M'
|
83
83
|
end
|
84
84
|
|
85
85
|
def required?
|
86
|
-
@inclusion == 'R'
|
86
|
+
@inclusion == 'R'
|
87
87
|
end
|
88
88
|
|
89
89
|
def optional?
|
90
|
-
@inclusion == 'O'
|
90
|
+
@inclusion == 'O'
|
91
91
|
end
|
92
92
|
|
93
93
|
def valid?
|
94
94
|
@valid = inclusion && contents && position
|
95
|
-
|
96
|
-
@valid &&= @data.send(@validator)
|
97
|
-
end
|
95
|
+
@valid &&= @data.send(@validator) if @validator && @data
|
98
96
|
@valid
|
99
97
|
end
|
100
98
|
|
101
|
-
def add_error
|
99
|
+
def add_error(err_string)
|
102
100
|
errors << err_string
|
103
101
|
end
|
104
102
|
|
105
|
-
def self.unpack_str(definition = {
|
106
|
-
if
|
107
|
-
'a'
|
103
|
+
def self.unpack_str(definition = {})
|
104
|
+
if definition[:contents] =~ /(Numeric|\$+\u00a2\u00a2)/
|
105
|
+
'a'
|
108
106
|
else
|
109
|
-
'A'
|
107
|
+
'A'
|
110
108
|
end + definition[:position].size.to_s
|
111
109
|
end
|
112
110
|
|
113
111
|
def to_ach
|
114
112
|
str = to_s
|
115
113
|
fill_char = @fill_character
|
116
|
-
fill_char = ' '
|
117
|
-
str ||= ''
|
118
|
-
str.send(justification,position.count, fill_char)
|
114
|
+
fill_char = ' ' unless str
|
115
|
+
str ||= ''
|
116
|
+
str.send(justification, position.count, fill_char)
|
119
117
|
end
|
120
118
|
|
121
119
|
def to_json_output
|
122
|
-
if
|
123
|
-
@json_output.reduce(@data)
|
120
|
+
if @json_output
|
121
|
+
@json_output.reduce(@data) do |output, operation|
|
124
122
|
output = output.send(*operation) if output
|
125
|
-
|
123
|
+
end
|
126
124
|
else
|
127
125
|
to_s
|
128
126
|
end
|
129
127
|
end
|
130
128
|
|
129
|
+
def human_name
|
130
|
+
# @human_name ||= @name.to_s.gsub('_', ' ').capitalize
|
131
|
+
@human_name ||= @name.to_s.split('_').map(&:capitalize).join(' ')
|
132
|
+
end
|
133
|
+
|
134
|
+
def to_html
|
135
|
+
tooltip_text = "<span class=\"tooltiptext\" >#{human_name}</span>"
|
136
|
+
field_classes = "nacha-field tooltip data-field-name=\"#{@name}\""
|
137
|
+
ach_string = to_ach.gsub(' ', ' ')
|
138
|
+
"<span class=\"#{field_classes}\" data-name=\"#{@name}\">#{ach_string}" +
|
139
|
+
tooltip_text.to_s +
|
140
|
+
"</span>"
|
141
|
+
end
|
142
|
+
|
131
143
|
def to_s
|
132
144
|
@data.send(*output_conversion).to_s
|
133
145
|
end
|
@@ -135,5 +147,4 @@ class Nacha::Field
|
|
135
147
|
def raw
|
136
148
|
@data
|
137
149
|
end
|
138
|
-
|
139
150
|
end
|
data/lib/nacha/numeric.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
class Nacha::Numeric
|
3
|
-
|
4
|
-
def initialize(val = nil)
|
3
|
+
def initialize val = nil
|
5
4
|
self.value = val
|
6
5
|
end
|
7
6
|
|
@@ -20,7 +19,7 @@ class Nacha::Numeric
|
|
20
19
|
def value=(val)
|
21
20
|
if val.is_a?(String)
|
22
21
|
@value = val.dup
|
23
|
-
if
|
22
|
+
if(val.strip.length > 0)
|
24
23
|
@value = BigDecimal(val.strip)
|
25
24
|
@op_value = @value
|
26
25
|
else
|
@@ -30,15 +29,13 @@ class Nacha::Numeric
|
|
30
29
|
@value = BigDecimal(val)
|
31
30
|
@op_value = @value
|
32
31
|
end
|
33
|
-
@value
|
34
32
|
end
|
35
33
|
|
36
34
|
def to_s
|
37
35
|
@value ? @value.to_s : nil
|
38
36
|
end
|
39
37
|
|
40
|
-
def
|
41
|
-
puts "In respond_to? #{method_name}"
|
38
|
+
def respond_to_missing?(method_name, include_private = false)
|
42
39
|
@op_value.respond_to? method_name
|
43
40
|
end
|
44
41
|
|
@@ -47,14 +44,15 @@ class Nacha::Numeric
|
|
47
44
|
# should be checked to see if the operation is valid for it, not
|
48
45
|
# necessarily the potentially string @value
|
49
46
|
def method_missing(method_name, *args, &block)
|
50
|
-
puts "In method_missing for #{method_name} value_class = #{@value.class}, op_value_class = #{@op_value.class}"
|
51
47
|
if @op_value.respond_to? method_name
|
52
48
|
old_op_value = @op_value.dup
|
53
49
|
if /!\z/.match?(method_name.to_s)
|
50
|
+
# rubocop:disable GitlabSecurity/PublicSend
|
54
51
|
@op_value.send(method_name, *args, &block)
|
55
52
|
return_value = @op_value
|
56
53
|
else
|
57
54
|
return_value = @op_value.send(method_name, *args, &block)
|
55
|
+
# rubocop:enable GitlabSecurity/PublicSend
|
58
56
|
end
|
59
57
|
if old_op_value != return_value
|
60
58
|
@value = return_value
|
data/lib/nacha/parser.rb
CHANGED
@@ -11,12 +11,7 @@ class Nacha::Parser
|
|
11
11
|
def reset!; end
|
12
12
|
|
13
13
|
def parse_file(file)
|
14
|
-
|
15
|
-
records = []
|
16
|
-
File.foreach(file).with_index do |line, line_num|
|
17
|
-
records << process(line, line_num, records.last)
|
18
|
-
parent = records.last if records.lasts.class.child_record_types.any?
|
19
|
-
end
|
14
|
+
parse_string(file.read)
|
20
15
|
end
|
21
16
|
|
22
17
|
def parse_string(str)
|
@@ -41,6 +36,7 @@ class Nacha::Parser
|
|
41
36
|
parent = parent.parent
|
42
37
|
record_types = valid_record_types(parent)
|
43
38
|
end
|
39
|
+
record.line_number = line_num if record
|
44
40
|
add_child(parent, record)
|
45
41
|
record
|
46
42
|
end
|
data/lib/nacha/record/base.rb
CHANGED
@@ -7,23 +7,22 @@ require 'nacha/record/validations/field_validations'
|
|
7
7
|
module Nacha
|
8
8
|
module Record
|
9
9
|
class Base
|
10
|
-
include FieldValidations
|
10
|
+
include Validations::FieldValidations
|
11
11
|
|
12
12
|
attr_accessor :children, :parent, :fields
|
13
|
-
attr_reader :
|
14
|
-
|
15
|
-
|
16
|
-
@@unpack_str = nil
|
17
|
-
# @@matcher = nil
|
13
|
+
attr_reader :name, :validations
|
14
|
+
attr_accessor :line_number
|
18
15
|
|
19
16
|
def initialize(opts = {})
|
20
17
|
@children = []
|
21
18
|
create_fields_from_definition
|
22
19
|
opts.each do |k, v|
|
23
20
|
setter = "#{k}="
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
next unless respond_to? setter
|
22
|
+
|
23
|
+
# rubocop:disable GitlabSecurity/PublicSend
|
24
|
+
send(setter, v) unless v.nil?
|
25
|
+
# rubocop:enable GitlabSecurity/PublicSend
|
27
26
|
end
|
28
27
|
end
|
29
28
|
|
@@ -55,6 +54,15 @@ module Nacha
|
|
55
54
|
end.join
|
56
55
|
end
|
57
56
|
|
57
|
+
def to_html
|
58
|
+
"<div style=\"font-family: monospace;\"class='nacha-record tooltip #{record_type}'>" +
|
59
|
+
"<span class='tooltiptext'>#{record_type}</span>" +
|
60
|
+
"<span class='nacha-field' data-name='record-number'>#{"%05d" % [line_number]} | </span>" +
|
61
|
+
@fields.keys.collect do |key|
|
62
|
+
@fields[key].to_html
|
63
|
+
end.join + "</div>"
|
64
|
+
end
|
65
|
+
|
58
66
|
def inspect
|
59
67
|
"#<#{self.class.name}> #{to_h}"
|
60
68
|
end
|
@@ -63,12 +71,12 @@ module Nacha
|
|
63
71
|
definition[name] = { inclusion: inclusion,
|
64
72
|
contents: contents,
|
65
73
|
position: position,
|
66
|
-
name: name
|
74
|
+
name: name}
|
67
75
|
validation_method = "valid_#{name}".to_sym
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
76
|
+
return unless respond_to?(validation_method)
|
77
|
+
|
78
|
+
validations[name] ||= []
|
79
|
+
validations[name] << validation_method
|
72
80
|
end
|
73
81
|
|
74
82
|
def self.definition
|
@@ -79,8 +87,8 @@ module Nacha
|
|
79
87
|
@validations ||= {}
|
80
88
|
end
|
81
89
|
|
82
|
-
|
83
|
-
|
90
|
+
class << self
|
91
|
+
attr_reader :nacha_record_name
|
84
92
|
end
|
85
93
|
|
86
94
|
def definition
|
@@ -89,23 +97,27 @@ module Nacha
|
|
89
97
|
|
90
98
|
def validate
|
91
99
|
self.class.definition.keys.map do |field|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
100
|
+
next unless self.class.validations[field]
|
101
|
+
|
102
|
+
# rubocop:disable GitlabSecurity/PublicSend
|
103
|
+
field_data = send(field)
|
104
|
+
send(self.class.validations[:field], field_data)
|
105
|
+
# rubocop:enable GitlabSecurity/PublicSend
|
96
106
|
end
|
97
107
|
end
|
98
108
|
|
99
109
|
# look for invalid fields, if none, then return true
|
100
110
|
def valid?
|
101
|
-
|
111
|
+
statuses = self.class.definition.keys.map do |field_sym|
|
112
|
+
# rubocop:disable GitlabSecurity/PublicSend
|
102
113
|
field = send(field_sym)
|
103
|
-
|
114
|
+
# rubocop:enable GitlabSecurity/PublicSend
|
115
|
+
next true unless field.mandatory?
|
104
116
|
|
105
117
|
## TODO: levels of validity with 'R' and 'O' fields
|
106
|
-
|
107
118
|
field.valid?
|
108
|
-
end
|
119
|
+
end
|
120
|
+
!statuses.detect { |valid| valid == false }
|
109
121
|
end
|
110
122
|
|
111
123
|
def debit?
|
@@ -146,6 +158,7 @@ module Nacha
|
|
146
158
|
|
147
159
|
def respond_to?(method_name, include_private = false)
|
148
160
|
field_name = method_name.to_s.gsub(/=$/, '').to_sym
|
161
|
+
|
149
162
|
definition[field_name] || super
|
150
163
|
end
|
151
164
|
|
@@ -155,7 +168,9 @@ module Nacha
|
|
155
168
|
if @fields[field_name]
|
156
169
|
if is_assignment
|
157
170
|
# @fields[field_name].send(:data=,*args)
|
158
|
-
|
171
|
+
# rubocop:disable GitlabSecurity/PublicSend
|
172
|
+
@fields[field_name].public_send(:data=, *args)
|
173
|
+
# rubocop:enable GitlabSecurity/PublicSend
|
159
174
|
@dirty = true
|
160
175
|
else
|
161
176
|
# @fields[field_name].data
|
@@ -3,29 +3,31 @@
|
|
3
3
|
|
4
4
|
module Nacha
|
5
5
|
module Record
|
6
|
-
module
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
def check_field_error(field, message = nil, condition = nil)
|
12
|
-
(block_given? ? yield : condition) || (field.add_error("'#{field.data}' is invalid") && false)
|
6
|
+
module Validations
|
7
|
+
module FieldValidations
|
8
|
+
def self.included base
|
9
|
+
base.extend ClassMethods
|
13
10
|
end
|
11
|
+
module ClassMethods
|
12
|
+
def check_field_error(field, message = nil, condition = nil)
|
13
|
+
(block_given? ? yield : condition) || (field.add_error("'#{field.data}' is invalid") && false)
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
def valid_service_class_code field
|
17
|
+
check_field_error(field) { SERVICE_CLASS_CODES.include? field.to_s }
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def valid_standard_entry_class_code field
|
21
|
+
check_field_error(field) { STANDARD_ENTRY_CLASS_CODES.include? field.data }
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
def valid_transaction_code field
|
25
|
+
check_field_error(field) { TRANSACTION_CODES.include? field.to_s }
|
26
|
+
end
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
def valid_receiving_dfi_identification field
|
29
|
+
check_field_error(field) { field.valid? }
|
30
|
+
end
|
29
31
|
end
|
30
32
|
end
|
31
33
|
end
|
@@ -3,12 +3,13 @@
|
|
3
3
|
|
4
4
|
module Nacha
|
5
5
|
module Record
|
6
|
-
module
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
module Validations
|
7
|
+
module RecordValidations
|
8
|
+
def self.included base
|
9
|
+
base.extend ClassMethods
|
10
|
+
end
|
11
|
+
module ClassMethods
|
12
|
+
end
|
12
13
|
end
|
13
14
|
end
|
14
15
|
end
|
data/lib/nacha/version.rb
CHANGED
data/lib/nacha.rb
CHANGED
@@ -7,11 +7,11 @@ require 'nacha/ach_date'
|
|
7
7
|
require 'nacha/field'
|
8
8
|
require 'nacha/numeric'
|
9
9
|
|
10
|
-
|
10
|
+
Gem.find_files('nacha/record/*.rb').reject{|f| f =~ /\/spec\//}.each do |file|
|
11
11
|
require File.expand_path(file)
|
12
12
|
end
|
13
13
|
|
14
|
-
|
14
|
+
Gem.find_files('nacha/record/**/*.rb').reject{|f| f =~ /\/spec\//}.each do |file|
|
15
15
|
require File.expand_path(file)
|
16
16
|
end
|
17
17
|
|
@@ -30,16 +30,26 @@ module Nacha
|
|
30
30
|
48 49 55 56 82 84 86 88].freeze
|
31
31
|
|
32
32
|
TRANSACTION_CODES = (CREDIT_TRANSACTION_CODES + DEBIT_TRANSACTION_CODES).freeze
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
33
|
+
class << self
|
34
|
+
def parse(object)
|
35
|
+
parser = Nacha::Parser.new
|
36
|
+
if object.is_a?(String)
|
37
|
+
parser.parse_string(object)
|
38
|
+
else
|
39
|
+
parser.parse_file(object)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def record_name(str)
|
44
|
+
underscore(str.to_s).split('/').last
|
45
|
+
end
|
46
|
+
|
47
|
+
def underscore(str)
|
48
|
+
str.gsub(/::/, '/').
|
49
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
50
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
51
|
+
tr('-', '_').
|
52
|
+
downcase
|
53
|
+
end
|
44
54
|
end
|
45
55
|
end
|
data/nacha.gemspec
CHANGED
@@ -21,18 +21,16 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.bindir = "exe"
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
23
|
spec.require_paths = ["lib"]
|
24
|
+
spec.add_dependency 'bigdecimal'
|
24
25
|
|
25
26
|
spec.add_development_dependency "bundler", "~> 2.0"
|
26
|
-
spec.add_development_dependency "byebug"
|
27
27
|
spec.add_development_dependency "factory_bot"
|
28
28
|
spec.add_development_dependency "gitlab-styles"
|
29
29
|
spec.add_development_dependency "guard"
|
30
30
|
spec.add_development_dependency "guard-rspec"
|
31
|
-
spec.add_development_dependency "pry", "~> 0.10"
|
32
|
-
spec.add_development_dependency "pry-doc", "~> 0.10"
|
33
31
|
spec.add_development_dependency "rake", "~> 10.0"
|
34
32
|
spec.add_development_dependency "rspec", "~> 3.0"
|
35
|
-
spec.add_development_dependency "rubocop"
|
36
|
-
spec.add_development_dependency 'rubocop-performance'
|
37
|
-
spec.add_development_dependency 'rubocop-rspec'
|
33
|
+
spec.add_development_dependency "rubocop"
|
34
|
+
spec.add_development_dependency 'rubocop-performance'
|
35
|
+
spec.add_development_dependency 'rubocop-rspec'
|
38
36
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
</body></html>
|
@@ -0,0 +1,68 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang='en'>
|
3
|
+
<head>
|
4
|
+
<meta charset='UTF-8'>
|
5
|
+
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
6
|
+
<title>NACHA File Parser</title>
|
7
|
+
|
8
|
+
<style>
|
9
|
+
body {
|
10
|
+
font-family: Arial, sans-serif;
|
11
|
+
}
|
12
|
+
body, h1, h2, h3, p {
|
13
|
+
margin: 0;
|
14
|
+
padding: 0;
|
15
|
+
}
|
16
|
+
body {
|
17
|
+
padding: 20px;
|
18
|
+
}
|
19
|
+
h1 {
|
20
|
+
|
21
|
+
font-size: 24px;
|
22
|
+
margin-bottom: 20px;
|
23
|
+
}
|
24
|
+
|
25
|
+
span.nacha-field:hover {
|
26
|
+
background-color: yellow;
|
27
|
+
}
|
28
|
+
|
29
|
+
.tooltip {
|
30
|
+
position: relative;
|
31
|
+
display: inline-block;
|
32
|
+
/* border-bottom: 1px dotted black; *//* If you want dots under the hoverable text */
|
33
|
+
}
|
34
|
+
|
35
|
+
/* Tooltip text */
|
36
|
+
.tooltip > .tooltiptext {
|
37
|
+
visibility: hidden;
|
38
|
+
/* width: 120px; */
|
39
|
+
background-color: black;
|
40
|
+
color: #fff;
|
41
|
+
text-align: center;
|
42
|
+
padding: 5px 5px;
|
43
|
+
border-radius: 6px;
|
44
|
+
|
45
|
+
top: 100%;
|
46
|
+
left: 50%;
|
47
|
+
|
48
|
+
/* Position the tooltip text - see examples below! */
|
49
|
+
position: absolute;
|
50
|
+
z-index: 1;
|
51
|
+
}
|
52
|
+
|
53
|
+
.nacha-record > .tooltiptext {
|
54
|
+
top: -50%;
|
55
|
+
left: 100%;
|
56
|
+
background-color: gray;
|
57
|
+
}
|
58
|
+
|
59
|
+
/* Show the tooltip text when you mouse over the tooltip container */
|
60
|
+
.tooltip:hover > .tooltiptext {
|
61
|
+
visibility: visible;
|
62
|
+
}
|
63
|
+
|
64
|
+
|
65
|
+
</style>
|
66
|
+
</head>
|
67
|
+
<body>
|
68
|
+
<h1>NACHA File Parser</h1>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nacha
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David H. Wilkins
|
@@ -10,33 +10,33 @@ cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
|
-
name:
|
13
|
+
name: bigdecimal
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
15
15
|
requirements:
|
16
|
-
- - "
|
16
|
+
- - ">="
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: '
|
19
|
-
type: :
|
18
|
+
version: '0'
|
19
|
+
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
|
-
- - "
|
23
|
+
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: '
|
25
|
+
version: '0'
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
|
-
name:
|
27
|
+
name: bundler
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
29
29
|
requirements:
|
30
|
-
- - "
|
30
|
+
- - "~>"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '0'
|
32
|
+
version: '2.0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - "
|
37
|
+
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '0'
|
39
|
+
version: '2.0'
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: factory_bot
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
@@ -93,34 +93,6 @@ dependencies:
|
|
93
93
|
- - ">="
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
version: '0'
|
96
|
-
- !ruby/object:Gem::Dependency
|
97
|
-
name: pry
|
98
|
-
requirement: !ruby/object:Gem::Requirement
|
99
|
-
requirements:
|
100
|
-
- - "~>"
|
101
|
-
- !ruby/object:Gem::Version
|
102
|
-
version: '0.10'
|
103
|
-
type: :development
|
104
|
-
prerelease: false
|
105
|
-
version_requirements: !ruby/object:Gem::Requirement
|
106
|
-
requirements:
|
107
|
-
- - "~>"
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
version: '0.10'
|
110
|
-
- !ruby/object:Gem::Dependency
|
111
|
-
name: pry-doc
|
112
|
-
requirement: !ruby/object:Gem::Requirement
|
113
|
-
requirements:
|
114
|
-
- - "~>"
|
115
|
-
- !ruby/object:Gem::Version
|
116
|
-
version: '0.10'
|
117
|
-
type: :development
|
118
|
-
prerelease: false
|
119
|
-
version_requirements: !ruby/object:Gem::Requirement
|
120
|
-
requirements:
|
121
|
-
- - "~>"
|
122
|
-
- !ruby/object:Gem::Version
|
123
|
-
version: '0.10'
|
124
96
|
- !ruby/object:Gem::Dependency
|
125
97
|
name: rake
|
126
98
|
requirement: !ruby/object:Gem::Requirement
|
@@ -153,48 +125,49 @@ dependencies:
|
|
153
125
|
name: rubocop
|
154
126
|
requirement: !ruby/object:Gem::Requirement
|
155
127
|
requirements:
|
156
|
-
- - "
|
128
|
+
- - ">="
|
157
129
|
- !ruby/object:Gem::Version
|
158
|
-
version: 0
|
130
|
+
version: '0'
|
159
131
|
type: :development
|
160
132
|
prerelease: false
|
161
133
|
version_requirements: !ruby/object:Gem::Requirement
|
162
134
|
requirements:
|
163
|
-
- - "
|
135
|
+
- - ">="
|
164
136
|
- !ruby/object:Gem::Version
|
165
|
-
version: 0
|
137
|
+
version: '0'
|
166
138
|
- !ruby/object:Gem::Dependency
|
167
139
|
name: rubocop-performance
|
168
140
|
requirement: !ruby/object:Gem::Requirement
|
169
141
|
requirements:
|
170
|
-
- - "
|
142
|
+
- - ">="
|
171
143
|
- !ruby/object:Gem::Version
|
172
|
-
version:
|
144
|
+
version: '0'
|
173
145
|
type: :development
|
174
146
|
prerelease: false
|
175
147
|
version_requirements: !ruby/object:Gem::Requirement
|
176
148
|
requirements:
|
177
|
-
- - "
|
149
|
+
- - ">="
|
178
150
|
- !ruby/object:Gem::Version
|
179
|
-
version:
|
151
|
+
version: '0'
|
180
152
|
- !ruby/object:Gem::Dependency
|
181
153
|
name: rubocop-rspec
|
182
154
|
requirement: !ruby/object:Gem::Requirement
|
183
155
|
requirements:
|
184
|
-
- - "
|
156
|
+
- - ">="
|
185
157
|
- !ruby/object:Gem::Version
|
186
|
-
version:
|
158
|
+
version: '0'
|
187
159
|
type: :development
|
188
160
|
prerelease: false
|
189
161
|
version_requirements: !ruby/object:Gem::Requirement
|
190
162
|
requirements:
|
191
|
-
- - "
|
163
|
+
- - ">="
|
192
164
|
- !ruby/object:Gem::Version
|
193
|
-
version:
|
165
|
+
version: '0'
|
194
166
|
description: Ruby parser for ACH files.
|
195
167
|
email:
|
196
168
|
- dwilkins@conecuh.com
|
197
|
-
executables:
|
169
|
+
executables:
|
170
|
+
- nacha
|
198
171
|
extensions: []
|
199
172
|
extra_rdoc_files: []
|
200
173
|
files:
|
@@ -213,6 +186,7 @@ files:
|
|
213
186
|
- Rakefile
|
214
187
|
- bin/console
|
215
188
|
- bin/setup
|
189
|
+
- exe/nacha
|
216
190
|
- lib/nacha.rb
|
217
191
|
- lib/nacha/aba_number.rb
|
218
192
|
- lib/nacha/ach_date.rb
|
@@ -280,6 +254,8 @@ files:
|
|
280
254
|
- lib/nacha/record/xck_entry_detail.rb
|
281
255
|
- lib/nacha/version.rb
|
282
256
|
- nacha.gemspec
|
257
|
+
- templates/html_postamble.html
|
258
|
+
- templates/html_preamble.html
|
283
259
|
homepage: https://github.com/dwilkins/nacha
|
284
260
|
licenses:
|
285
261
|
- MIT
|