nacha 0.2.00 → 0.2.1
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/CHANGELOG.md +1 -1
- data/README.md +6 -0
- data/exe/nacha +11 -38
- data/lib/nacha/ach_file.rb +240 -0
- data/lib/nacha/formatter/base.rb +7 -3
- data/lib/nacha/formatter/json_formatter.rb +1 -31
- data/lib/nacha/parser.rb +6 -3
- data/lib/nacha/version.rb +1 -1
- data/lib/nacha.rb +6 -9
- data/nacha.gemspec +2 -0
- metadata +30 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2ae01e0c5a90efda594187387efe2150ff1548affb15c65b50fcb1fbc73dc6f
|
4
|
+
data.tar.gz: db181b0d320ab60664c14af3cdcccfc69a6879085bf167c80496303ee9bf6d6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17564ad39c737c944c7d7b86ed3340fd7ac4aaf0422879051095788b3161b6e487bb892042af646eb760a9a4c84d5a06d13de737fd95f00cddf0daad0e093e46
|
7
|
+
data.tar.gz: 358f227a837f75d3f64717b44164e86d35f7c7d52ae95fef851e53e9b06422b71d7e62d975a2b3fa42167ce9e620b52c47c05e3c4fc816ba097b8d7343300710
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -34,6 +34,12 @@ Or install it yourself as:
|
|
34
34
|
API may change at any time. Pull requests welcomed
|
35
35
|
|
36
36
|
|
37
|
+
```ruby
|
38
|
+
ach_file = Nacha.parse('path/to/file.ach')
|
39
|
+
|
40
|
+
puts ach_file.to_json
|
41
|
+
puts ach_file.to_markdown
|
42
|
+
```
|
37
43
|
|
38
44
|
```ruby
|
39
45
|
ach_string = "101 124000054 1240000540907021214A094101ZIONS FIRST NATIONAL BAZIONS FIRST NATIONAL BA 1"
|
data/exe/nacha
CHANGED
@@ -23,65 +23,38 @@ module Nacha
|
|
23
23
|
enum: %w[html json md markdown ach]
|
24
24
|
option :md_flavor, default: "common_mark", enum: %w[common_mark github]
|
25
25
|
def parse(file_path = nil)
|
26
|
-
|
27
|
-
input_file = $stdin
|
28
|
-
elsif File.exist?(file_path)
|
29
|
-
input_file = File.open(file_path)
|
30
|
-
else
|
31
|
-
puts "Error: File not found at #{file_path}"
|
32
|
-
exit 1
|
33
|
-
end
|
26
|
+
ach_file = Nacha::AchFile.new(file_path || $stdin)
|
34
27
|
|
35
|
-
|
36
|
-
ach_file = Nacha.parse(raw_records)
|
28
|
+
ach_file.parse
|
37
29
|
|
38
|
-
if ach_file
|
30
|
+
if ach_file.records.is_a?(Array) && !ach_file.records.empty?
|
39
31
|
if options[:output_file]
|
40
32
|
File.open(options[:output_file], "w") do |f|
|
41
|
-
write_output(ach_file, f
|
33
|
+
write_output(ach_file, f)
|
42
34
|
end
|
43
35
|
else
|
44
|
-
write_output(ach_file, $stdout
|
36
|
+
write_output(ach_file, $stdout)
|
45
37
|
end
|
46
38
|
else
|
47
39
|
puts "Could not parse the file or the file was empty."
|
48
40
|
end
|
49
41
|
rescue StandardError => e
|
50
42
|
puts "An error occurred during parsing: #{e.message}"
|
51
|
-
puts e.backtrace.join("\n")
|
52
43
|
exit 1
|
53
44
|
end
|
54
45
|
|
55
46
|
private
|
56
47
|
|
57
|
-
def write_output(
|
58
|
-
formatter_options = {
|
59
|
-
file_name: File.basename(file.path),
|
60
|
-
file_size: file.respond_to?(:size) ? file.size : nil,
|
61
|
-
number_of_lines: ach_records.size,
|
62
|
-
created_at: file.respond_to?(:ctime) ? file.ctime : Time.now,
|
63
|
-
modified_at: file.respond_to?(:mtime) ? file.mtime : Time.now,
|
64
|
-
preamble: HTML_PREAMBLE_FILE,
|
65
|
-
postamble: HTML_POSTAMBLE_FILE
|
66
|
-
}
|
67
|
-
|
48
|
+
def write_output(ach_file, io)
|
68
49
|
case options[:format]
|
69
50
|
when 'ach'
|
70
|
-
|
51
|
+
io.puts ach_file.to_ach
|
52
|
+
when 'html'
|
53
|
+
io.puts ach_file.to_html
|
71
54
|
when 'md', 'markdown'
|
72
|
-
|
73
|
-
formatter = Nacha::Formatter::FormatterFactory.get(:markdown, ach_records, formatter_options)
|
74
|
-
io.puts formatter.format
|
55
|
+
io.puts ach_file.to_markdown
|
75
56
|
else
|
76
|
-
|
77
|
-
formatter_options)
|
78
|
-
io.puts formatter.format
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def output_ach(ach_records, io)
|
83
|
-
ach_records.each do |record|
|
84
|
-
io.puts record.to_ach
|
57
|
+
io.puts ach_file.to_json
|
85
58
|
end
|
86
59
|
end
|
87
60
|
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri' # Required for URI.regexp
|
4
|
+
require 'openssl'
|
5
|
+
require 'httparty'
|
6
|
+
require 'nacha/formatter'
|
7
|
+
|
8
|
+
module Nacha
|
9
|
+
# Class for handling ACH files, which can be a URL, a file path, or a string of data.
|
10
|
+
# encapsulating the records and handling output
|
11
|
+
class AchFile
|
12
|
+
TEMPLATES_DIR = File.join(Gem::Specification.find_by_name("nacha").gem_dir,
|
13
|
+
"templates").freeze
|
14
|
+
HTML_PREAMBLE_FILE = File.join(TEMPLATES_DIR, "html_preamble.html")
|
15
|
+
HTML_POSTAMBLE_FILE = File.join(TEMPLATES_DIR, "html_postamble.html")
|
16
|
+
|
17
|
+
attr_reader :raw_input, :input_type, :records, :checksum, :input_source,
|
18
|
+
:created_at, :modified_at
|
19
|
+
|
20
|
+
def initialize(input_string)
|
21
|
+
@input_type = classify_input(input_string)
|
22
|
+
@records = []
|
23
|
+
case @input_type
|
24
|
+
when 'url'
|
25
|
+
@input_source = input_string
|
26
|
+
@raw_input = read_url_contents_httparty(input_string)
|
27
|
+
when 'filepath'
|
28
|
+
raise ArgumentError, "File not found: #{input_string}" unless File.exist?(input_string)
|
29
|
+
|
30
|
+
@input_source = input_string
|
31
|
+
@raw_input = File.read(input_string)
|
32
|
+
when 'string of data'
|
33
|
+
@input_source = "string"
|
34
|
+
@raw_input = input_string
|
35
|
+
else
|
36
|
+
if input_string.respond_to?(:read)
|
37
|
+
# If input_string is an IO object (like $stdin), read it directly
|
38
|
+
@input_source = "stdin"
|
39
|
+
@raw_input = input_string.read
|
40
|
+
else
|
41
|
+
# Otherwise, treat it as a simple string
|
42
|
+
@input_source = "string"
|
43
|
+
@raw_input = input_string.to_s
|
44
|
+
end
|
45
|
+
end
|
46
|
+
@checksum = OpenSSL::Digest::SHA256.hexdigest(@raw_input) if @raw_input
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse
|
50
|
+
@records = Nacha::Parser.new.parse_string(@raw_input)
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_ach
|
55
|
+
records.map(&:to_ach).join("\n")
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_json
|
59
|
+
formatter(:json).format
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_html
|
63
|
+
formatter(:html).format
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_markdown
|
67
|
+
formatter(:markdown).format
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def formatter_options
|
73
|
+
{
|
74
|
+
file_name: File.basename(@input_source),
|
75
|
+
file_size: @raw_input.size,
|
76
|
+
number_of_lines: records.size,
|
77
|
+
checksum: @checksum,
|
78
|
+
preamble: HTML_PREAMBLE_FILE,
|
79
|
+
postamble: HTML_POSTAMBLE_FILE
|
80
|
+
}.merge(file_info)
|
81
|
+
end
|
82
|
+
|
83
|
+
def file_info
|
84
|
+
return {} unless @input_type == 'filepath' && File.exist?(@input_source)
|
85
|
+
|
86
|
+
File.open(@input_source,'r') do |file|
|
87
|
+
{
|
88
|
+
created_at: file.ctime,
|
89
|
+
modified_at: file.mtime,
|
90
|
+
}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def formatter(format)
|
95
|
+
Formatter::FormatterFactory.get(format, self, formatter_options)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Reads the content of a URL using HTTParty.
|
99
|
+
#
|
100
|
+
# @param url [String] The URL to read.
|
101
|
+
# @return [String, nil] The content of the URL as a string, or nil if an error occurred.
|
102
|
+
def read_url_contents_httparty(url)
|
103
|
+
response = HTTParty.get(url, timeout: 60) # Set a timeout
|
104
|
+
if response.success?
|
105
|
+
response.body
|
106
|
+
else
|
107
|
+
puts "Error: HTTP request failed with status #{response.code} #{response.message} for #{url}"
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
rescue StandardError, HTTParty::Error => err
|
111
|
+
# HTTParty wraps various network and HTTP errors
|
112
|
+
puts "Error: An unexpected error occurred while reading #{url} - #{err.message}"
|
113
|
+
end
|
114
|
+
|
115
|
+
# Classifies an input string as a filepath, URL, or string of data.
|
116
|
+
# The classification order is: URL -> Filepath -> String of Data (if >= 94 chars) -> Generic String.
|
117
|
+
#
|
118
|
+
# @param input_arg [String, Object] The input to classify. It will be converted to a string.
|
119
|
+
# @return [String] One of 'url', 'filepath', 'string of data', or 'string'.
|
120
|
+
def classify_input(input_arg)
|
121
|
+
return 'file' if input_arg.respond_to?(:read)
|
122
|
+
# Ensure the input is treated as a string for consistent pattern matching.
|
123
|
+
# This handles cases where the input might be a number, nil, etc., by converting them to string.
|
124
|
+
input = input_arg.to_s
|
125
|
+
|
126
|
+
# 1. Check for URL
|
127
|
+
# URI::DEFAULT_PARSER.make_regexp is the standard way to match URIs according to RFCs.
|
128
|
+
# It primarily looks for a scheme (e.g., http://, https://, ftp://, file://).
|
129
|
+
# Note: It will typically *not* classify "www.example.com" as a URL without a scheme.
|
130
|
+
return 'url' if URI::DEFAULT_PARSER.make_regexp.match?(input)
|
131
|
+
|
132
|
+
# 2. Check for Filepath
|
133
|
+
# This is the most complex part due to the variety of path formats (absolute, relative,
|
134
|
+
# Windows, Unix-like) and the ambiguity with general strings.
|
135
|
+
is_filepath = false
|
136
|
+
|
137
|
+
# A. Absolute Path Patterns:
|
138
|
+
# - Unix-like absolute: `/path/to/file`
|
139
|
+
# - Windows drive letter: `C:\path\to\file` or `C:/path/to/file`
|
140
|
+
# - Windows UNC path: `\\server\share\path`
|
141
|
+
# - Home directory (Unix-like): `~/path/to/file`
|
142
|
+
if input =~ %r{
|
143
|
+
^ # Start of the string
|
144
|
+
(?: # Non-capturing group for different absolute path roots
|
145
|
+
/ | # Unix-like root (e.g., /usr/local)
|
146
|
+
[A-Za-z]:[\\/] | # Windows drive letter (e.g., C:\, D:/)
|
147
|
+
\\\\(?:[a-zA-Z0-9_.-]+[\\/])+[a-zA-Z0-9_.-]* | # Windows UNC path (e.g., \\server\share\folder or \\server\share)
|
148
|
+
~[\\/] # Unix-like home directory (e.g., ~/documents)
|
149
|
+
)
|
150
|
+
}x # The 'x' modifier allows whitespace and comments in the regex for readability
|
151
|
+
is_filepath = true
|
152
|
+
# B. Relative Path Starting with Common Indicators:
|
153
|
+
# - Current directory: `./file` or `.\file`
|
154
|
+
# - Parent directory: `../file` or `..\file`
|
155
|
+
elsif input =~ %r{^\.{1,2}[\\/]}
|
156
|
+
is_filepath = true
|
157
|
+
# C. Path containing separators AND ending with a common file extension:
|
158
|
+
# e.g., "folder/image.jpg", "document.pdf" (if it contains a slash or just "document.pdf")
|
159
|
+
# This handles cases like "my_photo.png" or "data/report.xlsx".
|
160
|
+
elsif (input.include?('/') || input.include?('\\')) && input =~ /\.[a-zA-Z0-9]{2,5}$/
|
161
|
+
is_filepath = true
|
162
|
+
# D. Path with separators and multiple components or ending in a separator:
|
163
|
+
# e.g., "folder/file", "dir1/dir2/file", "dir/subdir/", "/another_folder/"
|
164
|
+
# This tries to identify directory structures.
|
165
|
+
elsif (input.include?('/') || input.include?('\\'))
|
166
|
+
# Split the path by either / or \ and remove any empty strings that result
|
167
|
+
# (e.g., "/a/b" splits to ["", "a", "b"]; rejecting empty ones yields ["a", "b"]).
|
168
|
+
segments = input.split(/[\/\\]/).reject(&:empty?)
|
169
|
+
if segments.length >= 2 || (segments.length >= 1 && (input.end_with?('/') || input.end_with?('\\')))
|
170
|
+
# Classify as filepath if:
|
171
|
+
# - It has two or more segments (e.g., "folder/file", "/folder/sub/file").
|
172
|
+
# - OR it has at least one segment AND ends with a path separator (e.g., "folder/", "/folder/").
|
173
|
+
is_filepath = true
|
174
|
+
end
|
175
|
+
# E. Simple filename with a common extension, not caught by other rules (e.g., "report.txt")
|
176
|
+
# This handles cases like "my_document.docx" which have no directory separators but are filenames.
|
177
|
+
elsif input =~ /^[a-zA-Z0-9_.-]+\.[a-zA-Z0-9]{2,5}$/
|
178
|
+
# Basic check for "name.ext" pattern, allowing common filename characters.
|
179
|
+
is_filepath = true
|
180
|
+
end
|
181
|
+
|
182
|
+
if is_filepath
|
183
|
+
return 'filepath'
|
184
|
+
end
|
185
|
+
|
186
|
+
# 3. Check for "string of data"
|
187
|
+
# This is the fallback for anything that isn't clearly a URL or a filepath,
|
188
|
+
# AND meets the minimum length requirement as per the problem description.
|
189
|
+
if input.length >= 94
|
190
|
+
return 'string of data'
|
191
|
+
end
|
192
|
+
|
193
|
+
# 4. Default: Just a regular string
|
194
|
+
# If it doesn't fit any of the above categories (URL, filepath, long data string),
|
195
|
+
# it's considered a generic string (e.g., "Hello World", "short_name", "a_simple_word").
|
196
|
+
return 'string'
|
197
|
+
end
|
198
|
+
|
199
|
+
# # --- Test Cases ---
|
200
|
+
|
201
|
+
# puts "--- URLs ---"
|
202
|
+
# puts "http://example.com => #{classify_input("http://example.com")}"
|
203
|
+
# puts "https://www.google.com/search?q=ruby+regex => #{classify_input("https://www.google.com/search?q=ruby+regex")}"
|
204
|
+
# puts "ftp://user:pass@ftp.example.com/file.txt => #{classify_input("ftp://user:pass@ftp.example.com/file.txt")}"
|
205
|
+
# puts "file:///C:/Users/user/Documents/report.pdf => #{classify_input("file:///C:/Users/user/Documents/report.pdf")}"
|
206
|
+
# puts "www.example.com => #{classify_input("www.example.com")} (Expected: string, as it lacks a scheme for URI.regexp)"
|
207
|
+
|
208
|
+
# puts "\n--- Filepaths ---"
|
209
|
+
# puts "/usr/local/bin/myscript.sh => #{classify_input("/usr/local/bin/myscript.sh")}"
|
210
|
+
# puts "C:\\Program Files\\App\\config.ini => #{classify_input("C:\\Program Files\\App\\config.ini")}"
|
211
|
+
# puts "/home/user/documents/report.pdf => #{classify_input("/home/user/documents/report.pdf")}"
|
212
|
+
# puts "relative/path/to/file.txt => #{classify_input("relative/path/to/file.txt")}"
|
213
|
+
# puts "./current_dir_file.log => #{classify_input("./current_dir_file.log")}"
|
214
|
+
# puts "../parent_dir/other_file.csv => #{classify_input("../parent_dir/other_file.csv")}"
|
215
|
+
# puts "my_document.docx => #{classify_input("my_document.docx")}"
|
216
|
+
# puts "folder/another_folder/image.png => #{classify_input("folder/another_folder/image.png")}"
|
217
|
+
# puts "just_a_folder_name/ => #{classify_input("just_a_folder_name/")}"
|
218
|
+
# puts "root_dir/ => #{classify_input("root_dir/")}"
|
219
|
+
# puts "C:/ => #{classify_input("C:/")}"
|
220
|
+
# puts "/ => #{classify_input("/")}"
|
221
|
+
# puts "\\\\server\\share\\file.txt => #{classify_input("\\\\server\\share\\file.txt")}"
|
222
|
+
# puts "\\\\server\\share\\ => #{classify_input("\\\\server\\share\\")}"
|
223
|
+
|
224
|
+
# puts "\n--- Strings of Data (>= 94 characters) ---"
|
225
|
+
# long_data_string = "This is a very long string that serves as an example of generic data. It must be at least 94 characters long to be classified as 'string of data' by our function. This particular string is precisely 160 characters long. Ruby is fun!"
|
226
|
+
# puts "#{long_data_string.slice(0, 50)}... (length #{long_data_string.length}) => #{classify_input(long_data_string)}"
|
227
|
+
|
228
|
+
# long_data_94_chars = "a" * 94 # A string of exactly 94 'a' characters
|
229
|
+
# puts "#{long_data_94_chars.slice(0, 50)}... (length #{long_data_94_chars.length}) => #{classify_input(long_data_94_chars)}"
|
230
|
+
|
231
|
+
# puts "\n--- Regular Strings (short, ambiguous) ---"
|
232
|
+
# puts "Hello World => #{classify_input("Hello World")}"
|
233
|
+
# puts "short_string => #{classify_input("short_string")}"
|
234
|
+
# puts "a_simple_word => #{classify_input("a_simple_word")}"
|
235
|
+
# puts "12345 => #{classify_input("12345")}"
|
236
|
+
# puts "" => #{classify_input("")}" # Empty string
|
237
|
+
# puts "folder_name_only => #{classify_input("folder_name_only")}" # Not a path without separator or extension
|
238
|
+
# puts "not_a_path/but_looks_like_it_if_short_and_not_a_file_with_extension => #{classify_input("not_a_path/but_looks_like_it_if_short_and_not_a_file_with_extension")}" # This is still a filepath because of the / and multi-segments logic. This is correct behavior for a path-like string.
|
239
|
+
end
|
240
|
+
end
|
data/lib/nacha/formatter/base.rb
CHANGED
@@ -5,13 +5,17 @@ require 'digest'
|
|
5
5
|
module Nacha
|
6
6
|
module Formatter
|
7
7
|
class Base
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :ach_file, :options
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@
|
10
|
+
def initialize(ach_file, options = {})
|
11
|
+
@ach_file = ach_file
|
12
12
|
@options = options
|
13
13
|
end
|
14
14
|
|
15
|
+
def records
|
16
|
+
@ach_file.records
|
17
|
+
end
|
18
|
+
|
15
19
|
def format
|
16
20
|
raise NotImplementedError, 'Subclasses must implement a format method'
|
17
21
|
end
|
@@ -9,41 +9,11 @@ module Nacha
|
|
9
9
|
def format
|
10
10
|
output = {
|
11
11
|
file: file_statistics,
|
12
|
-
records: records.map
|
12
|
+
records: records.map(&:to_h)
|
13
13
|
}
|
14
14
|
|
15
15
|
JSON.pretty_generate(output)
|
16
16
|
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def record_to_h(record)
|
21
|
-
{
|
22
|
-
nacha_record_type: record.record_type,
|
23
|
-
metadata: {
|
24
|
-
klass: record.class.name,
|
25
|
-
errors: record.errors,
|
26
|
-
line_number: record.line_number,
|
27
|
-
original_input_line: record.original_input_line
|
28
|
-
}
|
29
|
-
}.merge(
|
30
|
-
record.fields.keys.to_h do |key|
|
31
|
-
[key, field_to_json_output(record.fields[key])]
|
32
|
-
end
|
33
|
-
)
|
34
|
-
end
|
35
|
-
|
36
|
-
def field_to_json_output(field)
|
37
|
-
if field.json_output
|
38
|
-
# rubocop:disable GitlabSecurity/PublicSend
|
39
|
-
field.json_output.reduce(field.raw) do |memo, operation|
|
40
|
-
memo&.public_send(*operation)
|
41
|
-
end
|
42
|
-
# rubocop:enable GitlabSecurity/PublicSend
|
43
|
-
else
|
44
|
-
field.to_s
|
45
|
-
end
|
46
|
-
end
|
47
17
|
end
|
48
18
|
end
|
49
19
|
end
|
data/lib/nacha/parser.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'nacha'
|
4
4
|
require 'nacha/parser_context'
|
5
|
+
require 'nacha/ach_file'
|
5
6
|
|
6
7
|
# Nacha Parser - deal with figuring out what record type a line is
|
7
8
|
class Nacha::Parser
|
@@ -18,9 +19,9 @@ class Nacha::Parser
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def parse_file(file)
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
@context.parser_started_at = Time.now.utc
|
23
|
+
@context.file_name = file
|
24
|
+
parse_string(file.read)
|
24
25
|
end
|
25
26
|
|
26
27
|
def detect_possible_record_types(line)
|
@@ -30,6 +31,8 @@ class Nacha::Parser
|
|
30
31
|
end
|
31
32
|
|
32
33
|
def parse_string(str)
|
34
|
+
return [] unless str.is_a?(String) && !str.empty?
|
35
|
+
|
33
36
|
line_num = -1
|
34
37
|
records = []
|
35
38
|
@context.parser_started_at ||= Time.now.utc
|
data/lib/nacha/version.rb
CHANGED
data/lib/nacha.rb
CHANGED
@@ -10,6 +10,7 @@
|
|
10
10
|
# end
|
11
11
|
|
12
12
|
require 'yaml'
|
13
|
+
require 'nacha/ach_file'
|
13
14
|
require 'nacha/version'
|
14
15
|
require 'nacha/aba_number'
|
15
16
|
require 'nacha/ach_date'
|
@@ -66,18 +67,14 @@ module Nacha
|
|
66
67
|
types_hash
|
67
68
|
end
|
68
69
|
|
69
|
-
# Parses a NACHA file or
|
70
|
+
# Parses a NACHA file, string or url into a structured object representation.
|
70
71
|
#
|
71
|
-
# @param object [String, File, IO] The input to parse, either a string containing
|
72
|
+
# @param object [String, File, IO, URL] The input to parse, either a string containing
|
72
73
|
# NACHA data or an IO object (e.g., a File) representing the NACHA file.
|
73
|
-
# @return [Nacha::
|
74
|
+
# @return [Nacha::AchFile] The parsed NACHA file object.
|
74
75
|
def parse(object)
|
75
|
-
|
76
|
-
|
77
|
-
parser.parse_string(object)
|
78
|
-
else
|
79
|
-
parser.parse_file(object)
|
80
|
-
end
|
76
|
+
ach_file = AchFile.new(object)
|
77
|
+
ach_file.parse
|
81
78
|
end
|
82
79
|
|
83
80
|
# Converts a given string into a underscored, lowercase record name.
|
data/nacha.gemspec
CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
29
29
|
spec.require_paths = ["lib"]
|
30
30
|
spec.add_dependency 'bigdecimal'
|
31
|
+
spec.add_dependency 'httparty'
|
31
32
|
|
32
33
|
spec.add_development_dependency 'bundler'
|
33
34
|
spec.add_development_dependency 'byebug' if RUBY_ENGINE == 'ruby'
|
@@ -40,6 +41,7 @@ Gem::Specification.new do |spec|
|
|
40
41
|
spec.add_development_dependency 'rake'
|
41
42
|
spec.add_development_dependency 'reek'
|
42
43
|
spec.add_development_dependency 'rspec'
|
44
|
+
spec.add_development_dependency 'webmock'
|
43
45
|
spec.add_development_dependency 'rubocop'
|
44
46
|
spec.add_development_dependency 'rubocop-performance'
|
45
47
|
spec.add_development_dependency 'rubocop-rspec'
|
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.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David H. Wilkins
|
@@ -23,6 +23,20 @@ dependencies:
|
|
23
23
|
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
25
|
version: '0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: httparty
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
26
40
|
- !ruby/object:Gem::Dependency
|
27
41
|
name: bundler
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
@@ -163,6 +177,20 @@ dependencies:
|
|
163
177
|
- - ">="
|
164
178
|
- !ruby/object:Gem::Version
|
165
179
|
version: '0'
|
180
|
+
- !ruby/object:Gem::Dependency
|
181
|
+
name: webmock
|
182
|
+
requirement: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
type: :development
|
188
|
+
prerelease: false
|
189
|
+
version_requirements: !ruby/object:Gem::Requirement
|
190
|
+
requirements:
|
191
|
+
- - ">="
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: '0'
|
166
194
|
- !ruby/object:Gem::Dependency
|
167
195
|
name: rubocop
|
168
196
|
requirement: !ruby/object:Gem::Requirement
|
@@ -278,6 +306,7 @@ files:
|
|
278
306
|
- lib/nacha.rb
|
279
307
|
- lib/nacha/aba_number.rb
|
280
308
|
- lib/nacha/ach_date.rb
|
309
|
+
- lib/nacha/ach_file.rb
|
281
310
|
- lib/nacha/field.rb
|
282
311
|
- lib/nacha/formatter.rb
|
283
312
|
- lib/nacha/formatter/base.rb
|