ach_builder 0.0.1.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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ach_builder.gemspec
4
+ gemspec
5
+ gem 'rspec'
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Artem Kuzko
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ #ACH-Builder
2
+
3
+ Ruby helper for creating ACH files. It's API is designed to be as flexible as possible.
4
+
5
+ Inspired by
6
+
7
+ [ach](http://github.com/jm81/ach) - Ruby helper
8
+
9
+ [ACH::Builder](http://github.com/camerb/ACH-Builder) - Perl module
10
+
11
+ with similar functionality
12
+
13
+ ##Example
14
+ # attributes for records may be passed as parameters, as well as modified in block
15
+ # these attributes will be passed to all inner entities in a cascade way, if required
16
+ file = File.new(:company_id => '11-11111', :company_name => 'MY COMPANY') do
17
+ immediate_dest_name 'COMMERCE BANK'
18
+ immediate_origin '123123123'
19
+ immediate_oreigin_name 'MYCOMPANY'
20
+
21
+ ['WEB', 'TEL'].each do |code|
22
+ batch(:entry_class_code => code, :company_entry_descr => 'TV-TELCOM') do
23
+ effective_date Time.now.strftime('%y%m%d')
24
+ origin_dfi_id "00000000"
25
+ entry :customer_name => 'JOHN SMITH',
26
+ :customer_acct => '61242882282',
27
+ :amount => '2501',
28
+ :routing_number => '010010101',
29
+ :bank_account => '103030030'
30
+ end
31
+ end
32
+ end
33
+
34
+ file.valid? # => false
35
+ file.errors # => {"ACH::File::Header#1"=>{:immediate_dest=>"is required"}}
36
+ file.header.immediate_dest = '123123123'
37
+ file.write('ach_01.txt')
38
+
39
+ ##Copyright
40
+
41
+ Copyright (c) 2011 Artem Kuzko, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ach/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ach_builder"
7
+ s.version = ACH::VERSION
8
+ s.authors = ["Artem Kuzko"]
9
+ s.email = ["a.kuzko@gmail.com"]
10
+ s.homepage = "http://github.com/akuzko/ach_builder"
11
+ s.summary = "Ruby tools for building ACH files"
12
+ s.description = "Ruby tools for building ACH (Automated Clearing House) files"
13
+
14
+ #s.rubyforge_project = "ach_builder"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rspec", ">= 2.0.0"
22
+ s.add_runtime_dependency "active_support", ">= 2.3.0"
23
+ end
@@ -0,0 +1,19 @@
1
+ module ACH
2
+ class Batch::Control < Record
3
+ fields :record_type,
4
+ :service_class_code,
5
+ :entry_count,
6
+ :entry_hash,
7
+ :total_debit_amount,
8
+ :total_credit_amount,
9
+ :company_id,
10
+ :authen_code,
11
+ :bank_6,
12
+ :origin_dfi_id,
13
+ :batch_number
14
+
15
+ defaults :record_type => 8,
16
+ :authen_code => '',
17
+ :bank_6 => ''
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ module ACH
2
+ class Batch::Header < Record
3
+ fields :record_type,
4
+ :service_class_code,
5
+ :company_name,
6
+ :company_note_data,
7
+ :company_id,
8
+ :entry_class_code,
9
+ :company_entry_descr,
10
+ :date,
11
+ :effective_date,
12
+ :settlement_date,
13
+ :origin_status_code,
14
+ :origin_dfi_id,
15
+ :batch_number
16
+
17
+ defaults :record_type => 5,
18
+ :service_class_code => 200,
19
+ :company_note_data => '',
20
+ :date => lambda{ Time.now.strftime("%y%m%d") },
21
+ :settlement_date => '',
22
+ :origin_status_code => ''
23
+ end
24
+ end
data/lib/ach/batch.rb ADDED
@@ -0,0 +1,37 @@
1
+ module ACH
2
+ class Batch < Component
3
+ has_many :entries
4
+
5
+ def has_credit?
6
+ entries.any?(&:credit?)
7
+ end
8
+
9
+ def has_debit?
10
+ entries.any?(&:debit?)
11
+ end
12
+
13
+ def entry_count
14
+ entries.length
15
+ end
16
+
17
+ def entry_hash
18
+ entries.map{ |e| e.routing_number.to_i / 10 }.compact.inject(&:+)
19
+ end
20
+
21
+ def total_debit_amount
22
+ entries.select(&:debit?).map{ |e| e.amount.to_i }.compact.inject(&:+) || 0
23
+ end
24
+
25
+ def total_credit_amount
26
+ entries.select(&:credit?).map{ |e| e.amount.to_i }.compact.inject(&:+) || 0
27
+ end
28
+
29
+ def to_ach
30
+ [header] + entries + [control]
31
+ end
32
+
33
+ def before_header
34
+ attributes[:service_class_code] ||= (has_debit? && has_credit? ? 200 : has_debit? ? 225 : 220)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,78 @@
1
+ module ACH
2
+ class Component
3
+ include Validations
4
+
5
+ class UnknownAttribute < ArgumentError
6
+ def initialize field
7
+ super "Unrecognized attribute '#{field}'"
8
+ end
9
+ end
10
+
11
+ attr_reader :attributes
12
+
13
+ def initialize fields = {}, &block
14
+ @attributes = {}
15
+ fields.each do |name, value|
16
+ raise UnknownAttribute.new(name) unless Formatter::RULES.key?(name)
17
+ @attributes[name] = value
18
+ end
19
+ after_initialize if respond_to?(:after_initialize)
20
+ instance_eval(&block) if block
21
+ end
22
+
23
+ def method_missing meth, *args
24
+ if Formatter::RULES.key?(meth)
25
+ args.empty? ? @attributes[meth] : (@attributes[meth] = args.first)
26
+ else
27
+ super
28
+ end
29
+ end
30
+
31
+ def before_header
32
+ end
33
+ private :before_header
34
+
35
+ def header fields = {}, &block
36
+ before_header
37
+ merged_fields = fields_for(self.class::Header).merge(fields)
38
+ @header ||= self.class::Header.new(merged_fields)
39
+ @header.tap do |head|
40
+ head.instance_eval(&block) if block
41
+ end
42
+ end
43
+
44
+ def control
45
+ klass = self.class::Control
46
+ fields = klass.fields.select{ |f| respond_to?(f) || attributes[f] }
47
+ klass.new Hash[*fields.zip(fields.map{ |f| send(f) }).flatten]
48
+ end
49
+
50
+ def fields_for component_or_class
51
+ klass = component_or_class.is_a?(Class) ? component_or_class : "ACH::#{component_or_class.camelize}".constantize
52
+ klass < Component ? attributes : attributes.select{ |k, v| klass.fields.include?(k) && attributes[k] }
53
+ end
54
+
55
+ def self.has_many plural_name, proc_defaults = nil
56
+ attr_reader plural_name
57
+
58
+ singular_name = plural_name.to_s.singularize
59
+ klass = "ACH::#{singular_name.camelize}".constantize
60
+
61
+ define_method(singular_name) do |*args, &block|
62
+ index_or_fields = args.first || {}
63
+ return send(plural_name)[index_or_fields] if Fixnum === index_or_fields
64
+
65
+ defaults = proc_defaults ? instance_exec(&proc_defaults) : {}
66
+
67
+ klass.new(fields_for(singular_name).merge(defaults).merge(index_or_fields)).tap do |component|
68
+ component.instance_eval(&block) if block
69
+ send(plural_name) << component
70
+ end
71
+ end
72
+
73
+ define_method :after_initialize do
74
+ instance_variable_set("@#{plural_name}", [])
75
+ end
76
+ end
77
+ end
78
+ end
data/lib/ach/entry.rb ADDED
@@ -0,0 +1,31 @@
1
+ module ACH
2
+ class Entry < Record
3
+ CREDIT_TRANSACTION_CODE_ENDING_DIGITS = ('0'..'4').to_a.freeze
4
+
5
+ fields :record_type,
6
+ :transaction_code,
7
+ :routing_number,
8
+ :bank_account,
9
+ :amount,
10
+ :customer_acct,
11
+ :customer_name,
12
+ :transaction_type,
13
+ :addenda,
14
+ :bank_15
15
+
16
+ defaults :record_type => 6,
17
+ :transaction_code => 27,
18
+ :transaction_type => 'S',
19
+ :customer_acct => '',
20
+ :addenda => 0,
21
+ :bank_15 => ''
22
+
23
+ def debit?
24
+ !credit?
25
+ end
26
+
27
+ def credit?
28
+ CREDIT_TRANSACTION_CODE_ENDING_DIGITS.include? transaction_code.to_s[1..1]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ module ACH
2
+ class File::Control < Record
3
+ fields :record_type,
4
+ :batch_count,
5
+ :block_count,
6
+ :entry_count,
7
+ :entry_hash,
8
+ :total_debit_amount,
9
+ :total_credit_amount,
10
+ :bank_39
11
+
12
+ defaults :record_type => 9,
13
+ :bank_39 => ''
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ module ACH
2
+ class File::Header < Record
3
+ fields :record_type,
4
+ :priority_code,
5
+ :immediate_dest,
6
+ :immediate_origin,
7
+ :date,
8
+ :time,
9
+ :file_id_modifier,
10
+ :record_size,
11
+ :blocking_factor,
12
+ :format_code,
13
+ :immediate_dest_name,
14
+ :immediate_origin_name,
15
+ :reference_code
16
+
17
+ defaults :record_type => 1,
18
+ :priority_code => 1,
19
+ :reference_code => '',
20
+ :date => lambda{ Time.now.strftime("%y%m%d") },
21
+ :time => lambda{ Time.now.strftime("%H%M") },
22
+ :file_id_modifier => 'A',
23
+ :record_size => 94,
24
+ :blocking_factor => 10,
25
+ :format_code => 1,
26
+ :reference_code => ''
27
+ end
28
+ end
data/lib/ach/file.rb ADDED
@@ -0,0 +1,44 @@
1
+ module ACH
2
+ class File < Component
3
+ has_many :batches, lambda{ {:batch_number => batches.length + 1} }
4
+
5
+ def batch_count
6
+ batches.length
7
+ end
8
+
9
+ def block_count
10
+ (entry_count / 10.0).ceil
11
+ end
12
+
13
+ def entry_count
14
+ batches.map{ |b| b.entries.length }.inject(&:+) || 0
15
+ end
16
+
17
+ def entry_hash
18
+ batches.map(&:entry_hash).compact.inject(&:+)
19
+ end
20
+
21
+ def total_debit_amount
22
+ batches.map(&:total_debit_amount).compact.inject(&:+)
23
+ end
24
+
25
+ def total_credit_amount
26
+ batches.map(&:total_credit_amount).compact.inject(&:+)
27
+ end
28
+
29
+ def to_ach
30
+ [header] + batches.map(&:to_ach).flatten + [control]
31
+ end
32
+
33
+ def to_s!
34
+ to_ach.map(&:to_s!).join("\n")
35
+ end
36
+
37
+ def write filename
38
+ return false unless valid?
39
+ ::File.open(filename, 'w') do |fh|
40
+ fh.write(to_s!)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,80 @@
1
+ module ACH
2
+ module Formatter
3
+ extend self
4
+
5
+ RULES = {
6
+ :customer_name => '<-22',
7
+ :customer_acct => '<-15',
8
+ :amount => '->10',
9
+ :bank_2 => '<-2',
10
+ :transaction_type => '<-2',
11
+ :bank_15 => '<-15',
12
+ :addenda => '<-1',
13
+ :trace_num => '<-15',
14
+ :transaction_code => '<-2',
15
+ :record_type => '<-1',
16
+ :bank_account => '<-17',
17
+ :routing_number => '->9',
18
+ :priority_code => '->2',
19
+ :immediate_dest => '->10-',
20
+ :immediate_origin => '->10-',
21
+ :date => '<-6',
22
+ :time => '<-4',
23
+ :file_id_modifier => '<-1|upcase',
24
+ :record_size => '->3',
25
+ :blocking_factor => '->20',
26
+ :format_code => '<-1',
27
+ :immediate_dest_name => '<-23',
28
+ :immediate_origin_name => '<-23',
29
+ :reference_code => '<-8',
30
+ :service_class_code => '<-3',
31
+ :company_name => '<-16',
32
+ :company_note_data => '<-20',
33
+ :company_id => '<-10',
34
+ :entry_class_code => '<-3',
35
+ :company_entry_descr => '<-10',
36
+ :effective_date => '<-6',
37
+ :settlement_date => '<-3',
38
+ :origin_status_code => '<-1',
39
+ :origin_dfi_id => '<-8',
40
+ :batch_number => '->7',
41
+ :entry_count => '->6',
42
+ :entry_hash => '->10',
43
+ :total_debit_amount => '->12',
44
+ :total_credit_amount => '->12',
45
+ :authen_code => '<-19',
46
+ :bank_6 => '<-6',
47
+ :batch_count => '->6',
48
+ :block_count => '->6',
49
+ :entry_count => '->8',
50
+ :bank_39 => '<-39'
51
+ }.freeze
52
+
53
+ RULE_PARSER_REGEX = /^(<-|->)(\d+)(-)?(\|\w+)?$/
54
+
55
+ @@compiled_rules = {}
56
+
57
+ def format field_name, value
58
+ compile_rule(field_name) unless @@compiled_rules.key?(field_name)
59
+ @@compiled_rules[field_name].call(value)
60
+ end
61
+
62
+ def method_missing meth, *args
63
+ if RULES.key? meth
64
+ format meth, *args
65
+ else
66
+ super
67
+ end
68
+ end
69
+
70
+ def compile_rule field_name
71
+ just, width, pad, transf = RULES[field_name].match(RULE_PARSER_REGEX)[1..-1]
72
+ padmethod = just == '<-' ? :ljust : :rjust
73
+ length = width.to_i
74
+ padstr = padmethod == :ljust ? ' ' : pad == '-' ? ' ' : '0'
75
+ transform = transf[1..-1] if transf
76
+ @@compiled_rules[field_name] = lambda{ |val| val = val.to_s[0..length]; (transform ? val.send(transform) : val).send(padmethod, length, padstr) }
77
+ end
78
+ private :compile_rule
79
+ end
80
+ end
data/lib/ach/record.rb ADDED
@@ -0,0 +1,65 @@
1
+ module ACH
2
+ class Record
3
+ include Validations
4
+
5
+ class UnknownField < ArgumentError
6
+ def initialize field, class_name
7
+ super "Unrecognized field '#{field}' in class #{class_name}"
8
+ end
9
+ end
10
+
11
+ class EmptyField < ArgumentError
12
+ def initialize field
13
+ super "Empty field '#{field}' for #{inspect}"
14
+ end
15
+ end
16
+
17
+ def self.fields *field_names
18
+ return @fields if field_names.empty?
19
+ @fields = field_names
20
+ @fields.each do |field|
21
+ raise UnknownField.new(field, name) unless Formatter::RULES.key?(field)
22
+
23
+ define_method(field) do |*args|
24
+ args.empty? ? fields[field] : (fields[field] = args.first)
25
+ end
26
+
27
+ define_method("#{field}=") do |val|
28
+ fields[field] = val
29
+ end
30
+ end
31
+ end
32
+
33
+ def self.defaults default_values = nil
34
+ return @defaults if default_values.nil?
35
+ @defaults = default_values.freeze
36
+ end
37
+
38
+ def initialize fields = {}, &block
39
+ defaults.each do |key, value|
40
+ self.fields[key] = Proc === value ? value.call : value
41
+ end
42
+ self.fields.merge!(fields)
43
+ instance_eval(&block) if block
44
+ end
45
+
46
+ def defaults
47
+ self.class.defaults
48
+ end
49
+
50
+ def fields
51
+ @fields ||= {}
52
+ end
53
+
54
+ def []= name, val
55
+ fields[name] = val
56
+ end
57
+
58
+ def to_s!
59
+ self.class.fields.map do |name|
60
+ raise EmptyField.new(name) if @fields[name].nil?
61
+ Formatter.format name, @fields[name]
62
+ end.join
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,36 @@
1
+ module ACH
2
+ module Validations
3
+ def valid?
4
+ reset_errors!
5
+ is_a?(Component) ? valid_component? : valid_record?
6
+ errors.empty?
7
+ end
8
+
9
+ def valid_component?
10
+ counts = {}
11
+ to_ach.each do |record|
12
+ counts[record.class] ||= 0
13
+ unless record.valid?
14
+ errors["#{record.class.name}##{counts[record.class] += 1}"] = record.errors
15
+ end
16
+ end
17
+ end
18
+ private :valid_component?
19
+
20
+ def valid_record?
21
+ self.class.fields.each do |field|
22
+ errors[field] = "is required" unless fields[field]
23
+ end
24
+ end
25
+ private :valid_record?
26
+
27
+ def errors
28
+ @errors || reset_errors!
29
+ end
30
+
31
+ def reset_errors!
32
+ @errors = ActiveSupport::OrderedHash.new
33
+ end
34
+ private :reset_errors!
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module ACH
2
+ VERSION = "0.0.1.1"
3
+ end
@@ -0,0 +1,39 @@
1
+ require 'active_support/inflector'
2
+ require 'active_support/ordered_hash'
3
+
4
+ require "ach/version"
5
+
6
+ require 'ach/formatter'
7
+ require 'ach/validations'
8
+ require 'ach/component'
9
+ require 'ach/record'
10
+ require 'ach/entry'
11
+ require 'ach/batch'
12
+ require 'ach/batch/header'
13
+ require 'ach/batch/control'
14
+ require 'ach/file'
15
+ require 'ach/file/header'
16
+ require 'ach/file/control'
17
+
18
+ module ACH
19
+ def self.sample_file
20
+ File.new(:company_id => '11-11111', :company_name => 'MY COMPANY') do
21
+ immediate_dest '123123123'
22
+ immediate_dest_name 'COMMERCE BANK'
23
+ immediate_origin '123123123'
24
+ immediate_origin_name 'MYCOMPANY'
25
+
26
+ ['WEB', 'TEL'].each do |code|
27
+ batch(:entry_class_code => code, :company_entry_descr => 'TV-TELCOM') do
28
+ effective_date Time.now.strftime('%y%m%d')
29
+ origin_dfi_id "00000000"
30
+ entry :customer_name => 'JOHN SMITH',
31
+ :customer_acct => '61242882282',
32
+ :amount => '2501',
33
+ :routing_number => '010010101',
34
+ :bank_account => '103030030'
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe ACH::Batch do
4
+ before(:each) do
5
+ @batch = ACH::Batch.new
6
+ end
7
+
8
+ it "should create entry with attributes" do
9
+ entry = @batch.entry :amount => 100
10
+ entry.should be_instance_of(ACH::Entry)
11
+ entry.amount.should == 100
12
+ end
13
+
14
+ it "should create entry with attributes" do
15
+ entry = @batch.entry do
16
+ amount 100
17
+ end
18
+ entry.amount.should == 100
19
+ end
20
+
21
+ it "should return false for has_credit? and has_debit? for empty entries" do
22
+ @batch.has_credit?.should be_false
23
+ @batch.has_debit?.should be_false
24
+ end
25
+
26
+ it "should return true for has_credit? if contains credit entry" do
27
+ @batch.entry :amount => 100, :transaction_code => 21
28
+ @batch.has_credit?.should be_true
29
+ end
30
+
31
+ it "should return true for has_debit? if contains debit entry" do
32
+ @batch.entry :amount => 100
33
+ @batch.has_debit?.should be_true
34
+ end
35
+
36
+ it "should generate 225 service_class_code for header if with debit entry only" do
37
+ @batch.entry :amount => 100
38
+ @batch.header.service_class_code.should == 225
39
+ end
40
+
41
+ it "should generate 220 service_class_code for header if with credit entry only" do
42
+ @batch.entry :amount => 100, :transaction_code => 21
43
+ @batch.header.service_class_code.should == 220
44
+ end
45
+
46
+ it "should generate 200 service_class_code for header if with debit and credit entries" do
47
+ @batch.entry :amount => 100
48
+ @batch.entry :amount => 100, :transaction_code => 21
49
+ @batch.header.service_class_code.should == 200
50
+ end
51
+ end
data/spec/file_spec.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe ACH::File do
4
+ before(:each) do
5
+ @attributes = {
6
+ :company_id => '11-11111',
7
+ :company_name => 'MY COMPANY',
8
+ :immediate_dest => '123123123',
9
+ :immediate_dest_name => 'COMMERCE BANK',
10
+ :immediate_origin => '123123123',
11
+ :immediate_origin_name => 'MYCOMPANY' }
12
+ @invalid_attributes = {:foo => 'bar'}
13
+ @file = ACH::File.new(@attributes)
14
+ @file_with_batch = ACH::File.new(@attributes) do
15
+ batch :entry_class_code => 'WEB'
16
+ end
17
+ end
18
+
19
+ it "should correctly assign attributes" do
20
+ @file.company_id.should == '11-11111'
21
+ end
22
+
23
+ it "should be modified by calling attribute methods in block" do
24
+ file = ACH::File.new(@attributes) do
25
+ company_name "MINE COMPANY"
26
+ end
27
+ file.company_name.should == "MINE COMPANY"
28
+ end
29
+
30
+ it "should fetch and return header" do
31
+ head = @file.header
32
+ head.should be_instance_of(ACH::File::Header)
33
+ head.immediate_dest.should == '123123123'
34
+ end
35
+
36
+ it "should be able to modify header info in block form" do
37
+ file = ACH::File.new(@attributes) do
38
+ header(:immediate_dest => '321321321') do
39
+ immediate_dest_name 'BANK COMMERCE'
40
+ end
41
+ end
42
+ head = file.header
43
+ head.immediate_dest.should == '321321321'
44
+ head.immediate_dest_name.should == 'BANK COMMERCE'
45
+ end
46
+
47
+ it "should raise exception on unknown attribute assignement" do
48
+ lambda {
49
+ ACH::File.new(@invalid_attributes)
50
+ }.should raise_error(ACH::Component::UnknownAttribute)
51
+ end
52
+
53
+ it "should be able to create a batch" do
54
+ @file_with_batch.batches.should_not be_empty
55
+ end
56
+
57
+ it "should return a batch when index is passed" do
58
+ @file_with_batch.batch(0).should be_instance_of(ACH::Batch)
59
+ end
60
+
61
+ it "should assign a batch_number to a batch" do
62
+ batch = @file_with_batch.batch(0)
63
+ batch.batch_number.should == 1
64
+ batch = @file_with_batch.batch(:entry_class_code => 'WEB')
65
+ batch.batch_number.should == 2
66
+ end
67
+
68
+ it "should assign attributes to a batch" do
69
+ batch = @file_with_batch.batch(0)
70
+ batch.attributes.should include(@file_with_batch.attributes)
71
+ end
72
+ end
73
+
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe ACH::Formatter do
4
+ before(:all) do
5
+ module ACH::Formatter
6
+ # redefining RULES FOR new test values
7
+ RULES = RULES.dup
8
+ RULES[:ljust_10] = '<-10'
9
+ RULES[:ljust_10_transform] = '<-10|upcase'
10
+ RULES[:rjust_10] = '->10'
11
+ RULES[:rjust_10_spaced] = '->10-'
12
+ end
13
+ end
14
+
15
+ it{ ACH::Formatter.ljust_10('FOO').should == 'FOO'.ljust(10) }
16
+
17
+ it{ ACH::Formatter.ljust_10_transform('foo').should == 'FOO'.ljust(10) }
18
+
19
+ it{ ACH::Formatter.rjust_10(1599).should == '1599'.rjust(10, '0') }
20
+
21
+ it{ ACH::Formatter.rjust_10_spaced(1599).should == '1599'.rjust(10) }
22
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe ACH::Record do
4
+ before(:all) do
5
+ class Entry < ACH::Record
6
+ fields :customer_name, :amount
7
+ defaults :customer_name => 'JOHN SMITH'
8
+ end
9
+ end
10
+
11
+ it "should have 2 ordered fields" do
12
+ Entry.fields.should == [:customer_name, :amount]
13
+ end
14
+
15
+ it "should create a record with default value" do
16
+ Entry.new.customer_name.should == 'JOHN SMITH'
17
+ end
18
+
19
+ it "should overwrite default value" do
20
+ entry = Entry.new(:customer_name => 'SMITH JOHN')
21
+ entry.customer_name.should == 'SMITH JOHN'
22
+ end
23
+
24
+ it "should generate formatted string" do
25
+ entry = Entry.new :amount => 1599
26
+ entry.to_s!.should == "JOHN SMITH".ljust(22) + "1599".rjust(10, '0')
27
+ end
28
+
29
+ it "should raise exception with unfilled value" do
30
+ lambda{
31
+ Entry.new.to_s!
32
+ }.should raise_error(ACH::Record::EmptyField)
33
+ end
34
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/ach.rb')
5
+
6
+ RSpec.configure do |config|
7
+ # some (optional) config here
8
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ach_builder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Artem Kuzko
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-09-14 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &81171620 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 2.0.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *81171620
25
+ - !ruby/object:Gem::Dependency
26
+ name: active_support
27
+ requirement: &81171160 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 2.3.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *81171160
36
+ description: Ruby tools for building ACH (Automated Clearing House) files
37
+ email:
38
+ - a.kuzko@gmail.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - .rspec
45
+ - Gemfile
46
+ - MIT-LICENSE
47
+ - README.md
48
+ - Rakefile
49
+ - ach_builder.gemspec
50
+ - lib/ach/batch.rb
51
+ - lib/ach/batch/control.rb
52
+ - lib/ach/batch/header.rb
53
+ - lib/ach/component.rb
54
+ - lib/ach/entry.rb
55
+ - lib/ach/file.rb
56
+ - lib/ach/file/control.rb
57
+ - lib/ach/file/header.rb
58
+ - lib/ach/formatter.rb
59
+ - lib/ach/record.rb
60
+ - lib/ach/validations.rb
61
+ - lib/ach/version.rb
62
+ - lib/ach_builder.rb
63
+ - spec/batch_spec.rb
64
+ - spec/file_spec.rb
65
+ - spec/formatter_spec.rb
66
+ - spec/record_spec.rb
67
+ - spec/spec_helper.rb
68
+ homepage: http://github.com/akuzko/ach_builder
69
+ licenses: []
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project:
88
+ rubygems_version: 1.8.6
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: Ruby tools for building ACH files
92
+ test_files: []