ach_builder 0.0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: []