ach_builder 0.0.2 → 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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -1
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +10 -0
  7. data/Gemfile +11 -2
  8. data/MIT-LICENSE +1 -1
  9. data/README.md +19 -11
  10. data/Rakefile +44 -0
  11. data/ach_builder.gemspec +9 -5
  12. data/lib/ach/batch.rb +10 -32
  13. data/lib/ach/batch/builder.rb +60 -0
  14. data/lib/ach/batch/control.rb +18 -3
  15. data/lib/ach/batch/header.rb +21 -4
  16. data/lib/ach/component.rb +125 -43
  17. data/lib/ach/component/has_many_association.rb +155 -0
  18. data/lib/ach/constants.rb +15 -1
  19. data/lib/ach/file.rb +50 -44
  20. data/lib/ach/file/builder.rb +81 -0
  21. data/lib/ach/file/control.rb +15 -3
  22. data/lib/ach/file/header.rb +20 -3
  23. data/lib/ach/file/reader.rb +103 -0
  24. data/lib/ach/file/transmission_header.rb +77 -0
  25. data/lib/ach/formatter.rb +113 -76
  26. data/lib/ach/formatter/rule.rb +27 -0
  27. data/lib/ach/record.rb +13 -64
  28. data/lib/ach/record/addenda.rb +25 -0
  29. data/lib/ach/record/base.rb +109 -0
  30. data/lib/ach/record/dynamic.rb +58 -0
  31. data/lib/ach/record/entry.rb +49 -0
  32. data/lib/ach/record/tail.rb +10 -0
  33. data/lib/ach/validations.rb +5 -3
  34. data/lib/ach/version.rb +1 -1
  35. data/lib/ach_builder.rb +20 -33
  36. data/spec/batch_spec.rb +23 -11
  37. data/spec/componenet/has_many_association_spec.rb +111 -0
  38. data/spec/file_spec.rb +173 -81
  39. data/spec/formatter_spec.rb +9 -9
  40. data/spec/reader_spec.rb +55 -0
  41. data/spec/record/addenda_spec.rb +8 -0
  42. data/spec/record/base_spec.rb +53 -0
  43. data/spec/record/dynamic_spec.rb +74 -0
  44. data/spec/record/entry_spec.rb +7 -0
  45. data/spec/record/tail_spec.rb +7 -0
  46. data/spec/spec_helper.rb +1 -0
  47. data/spec/support/ach_file_factory.rb +70 -0
  48. data/spec/support/examples/well_fargo_empty.ach +2 -0
  49. data/spec/support/examples/well_fargo_with_data.ach +6 -0
  50. data/spec/support/helpers/ach_files_examples.rb +17 -0
  51. metadata +69 -26
  52. data/lib/ach/entry.rb +0 -31
  53. data/lib/ach/tail.rb +0 -6
  54. data/spec/entry_spec.rb +0 -7
  55. data/spec/record_spec.rb +0 -34
  56. data/spec/tail_spec.rb +0 -7
@@ -0,0 +1,58 @@
1
+ module ACH
2
+ module Record
3
+ # Descendants of +ACH::Record::Dynamic+ class are the only
4
+ # records allowed to have previously undefined fields. However,
5
+ # each new field definition requires a rule string to be passed.
6
+ # +UndefinedFormatError+ will be raised otherwise. If the field
7
+ # has already been defined, and a new rule string is passed,
8
+ # a +DuplicateFormatError+ is raised.
9
+ #
10
+ # == Example
11
+ #
12
+ # class CustomHeader < ACH::Record::Dynamic
13
+ # request_type '<-9-' => '$$ADD ID='
14
+ # remote_id '<-8-'
15
+ # file_type '<-6-', 'NWFACH'
16
+ # end
17
+ #
18
+ # This example declares a +CustomHeader+ record type with following
19
+ # fields:
20
+ # * +request_type+, defined by rule '<-9-' with a default
21
+ # value of '$$ADD ID='
22
+ # * +remote_id+ with no default value
23
+ # * +file_type+ with default value of 'NWFACH'
24
+ #
25
+ # Note: passing two arguments to method call is equivalent to
26
+ # passing a hash with a single key-value pair.
27
+ class Dynamic < Base
28
+ class DuplicateFormatError < ArgumentError
29
+ def initialize(field_name)
30
+ super "Rule #{field_name} has already been defined"
31
+ end
32
+ end
33
+
34
+ class UndefinedFormatError < ArgumentError
35
+ def initialize(field_name)
36
+ super "Unknown field #{field_name} should be supplied by format"
37
+ end
38
+ end
39
+
40
+ def self.method_missing(field, *args)
41
+ format, default = args.first.is_a?(Hash) ? args.first.first : args
42
+ unless format =~ Formatter::Rule::RULE_PARSER_REGEX
43
+ default, format = format, nil
44
+ end
45
+
46
+ unless Formatter.defined? field
47
+ raise UndefinedFormatError.new(field) if format.nil?
48
+ Formatter.define field, format
49
+ else
50
+ raise DuplicateFormatError.new(field) if format
51
+ end
52
+ define_field_methods(field)
53
+ (@fields ||= []) << field
54
+ (@defaults ||= {})[field] = default if default
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,49 @@
1
+ module ACH
2
+ module Record
3
+ # A subclass of ACH::Record::Base, an Entry appears in an ACH::Batch
4
+ # component. It is the main record for representing a particular
5
+ # transaction.
6
+ #
7
+ # == Fields
8
+ #
9
+ # * record_type
10
+ # * transaction_code
11
+ # * routing_number
12
+ # * bank_account
13
+ # * amount
14
+ # * customer_acct
15
+ # * customer_name
16
+ # * transaction_type
17
+ # * addenda
18
+ # * bank_15
19
+ class Entry < Base
20
+ CREDIT_TRANSACTION_CODE_ENDING_DIGITS = ('0'..'4').to_a.freeze
21
+
22
+ fields :record_type,
23
+ :transaction_code,
24
+ :routing_number,
25
+ :bank_account,
26
+ :amount,
27
+ :customer_acct,
28
+ :customer_name,
29
+ :transaction_type,
30
+ :addenda,
31
+ :bank_15
32
+
33
+ defaults :record_type => BATCH_ENTRY_RECORD_TYPE,
34
+ :transaction_code => 27,
35
+ :transaction_type => 'S',
36
+ :customer_acct => '',
37
+ :addenda => 0,
38
+ :bank_15 => ''
39
+
40
+ def debit?
41
+ !credit?
42
+ end
43
+
44
+ def credit?
45
+ CREDIT_TRANSACTION_CODE_ENDING_DIGITS.include? transaction_code.to_s[1..1]
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,10 @@
1
+ module ACH
2
+ module Record
3
+ # Since a number of records in ACH file must be multiple of 10, tail records
4
+ # are used to populate empty records at the end of the file with "9" characters.
5
+ class Tail < Base
6
+ fields :nines
7
+ defaults :nines => '9' * RECORD_SIZE
8
+ end
9
+ end
10
+ end
@@ -1,4 +1,6 @@
1
1
  module ACH
2
+ # This module hosts the most basic validations for both +components+ and
3
+ # +records+. The only validation being performed is presence validation.
2
4
  module Validations
3
5
  def valid?
4
6
  reset_errors!
@@ -7,11 +9,11 @@ module ACH
7
9
  end
8
10
 
9
11
  def valid_component?
10
- counts = {}
12
+ counts = Hash.new(0)
11
13
  to_ach.each do |record|
12
- counts[record.class] ||= 0
13
14
  unless record.valid?
14
- errors["#{record.class.name}##{counts[record.class] += 1}"] = record.errors
15
+ klass = record.class
16
+ errors["#{klass}##{counts[klass] += 1}"] = record.errors
15
17
  end
16
18
  end
17
19
  end
@@ -1,3 +1,3 @@
1
1
  module ACH
2
- VERSION = "0.0.2"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -1,41 +1,28 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/dependencies/autoload'
1
3
  require 'active_support/inflector'
2
4
  require 'active_support/ordered_hash'
5
+ require 'active_support/core_ext/module/delegation'
6
+ require 'active_support/core_ext/module/attribute_accessors'
7
+ require 'active_support/core_ext/class/attribute'
3
8
 
4
9
  require "ach/version"
5
10
 
6
- require 'ach/constants'
7
- require 'ach/formatter'
8
- require 'ach/validations'
9
- require 'ach/component'
10
- require 'ach/record'
11
- require 'ach/entry'
12
- require 'ach/tail'
13
- require 'ach/batch'
14
- require 'ach/batch/header'
15
- require 'ach/batch/control'
16
- require 'ach/file'
17
- require 'ach/file/header'
18
- require 'ach/file/control'
19
-
11
+ # Support for building the files necessary for the bulk exchange of debits and
12
+ # credits with financial institutions via the Automated Clearing House system,
13
+ # governed by NACHA ( http://www.nacha.org/ ).
20
14
  module ACH
21
- def self.sample_file
22
- File.new(:company_id => '11-11111', :company_name => 'MY COMPANY') do
23
- immediate_dest '123123123'
24
- immediate_dest_name 'COMMERCE BANK'
25
- immediate_origin '123123123'
26
- immediate_origin_name 'MYCOMPANY'
27
-
28
- ['WEB', 'TEL'].each do |code|
29
- batch(:entry_class_code => code, :company_entry_descr => 'TV-TELCOM') do
30
- effective_date Time.now.strftime('%y%m%d')
31
- origin_dfi_id "00000000"
32
- entry :customer_name => 'JOHN SMITH',
33
- :customer_acct => '61242882282',
34
- :amount => '2501',
35
- :routing_number => '010010101',
36
- :bank_account => '103030030'
37
- end
38
- end
39
- end
15
+ extend ActiveSupport::Autoload
16
+
17
+ autoload :Constants
18
+ autoload :Formatter
19
+ autoload :Validations
20
+ autoload :Component
21
+ autoload :Record
22
+ autoload :Batch
23
+ autoload :File
24
+
25
+ def self.to_const(name)
26
+ [self, self::Record].detect{ |mod| mod.const_defined?(name) }.const_get(name)
40
27
  end
41
28
  end
@@ -3,22 +3,36 @@ require 'spec_helper'
3
3
  describe ACH::Batch do
4
4
  before(:each) do
5
5
  @batch = ACH::Batch.new
6
- @file = ACH.sample_file
6
+ @file = ACH::FileFactory.sample_file
7
7
  end
8
8
 
9
- it "should create entry with attributes" do
9
+ it "should create entry with attributes in hash form" do
10
10
  entry = @batch.entry :amount => 100
11
- entry.should be_instance_of(ACH::Entry)
11
+ entry.should be_instance_of(ACH::Record::Entry)
12
12
  entry.amount.should == 100
13
13
  end
14
14
 
15
- it "should create entry with attributes" do
15
+ it "should create entry with attributes in block form" do
16
16
  entry = @batch.entry do
17
17
  amount 100
18
18
  end
19
19
  entry.amount.should == 100
20
20
  end
21
21
 
22
+ it "should raise error when adding addenda records without any entry" do
23
+ batch = ACH::Batch.new
24
+ expect{ batch.addenda(:payment_related_info =>'foo bar') }.to raise_error(ACH::Component::HasManyAssociation::NoLinkError)
25
+ end
26
+
27
+ it "should append addenda records after entry records" do
28
+ batch = ACH::Batch.new
29
+ 3.times do |i|
30
+ batch.entry(:amount => 100)
31
+ i.times{ batch.addenda(:payment_related_info => 'foo bar') }
32
+ end
33
+ batch.to_ach.map(&:class)[1...-1].should == [ACH::Record::Entry, ACH::Record::Entry, ACH::Record::Addenda, ACH::Record::Entry, ACH::Record::Addenda, ACH::Record::Addenda]
34
+ end
35
+
22
36
  it "should return false for has_credit? and has_debit? for empty entries" do
23
37
  @batch.has_credit?.should be_false
24
38
  @batch.has_debit?.should be_false
@@ -50,12 +64,10 @@ describe ACH::Batch do
50
64
  @batch.header.service_class_code.should == 200
51
65
  end
52
66
 
53
- it "should have header record with length of 94" do
54
- @file.batch(0).header.to_s!.length.should == ACH::Constants::RECORD_SIZE
55
- end
56
-
57
- it "should have control record with length of 94" do
58
- @file.batch(0).send(:before_header) # to fill service_class_code value
59
- @file.batch(0).control.to_s!.length.should == ACH::Constants::RECORD_SIZE
67
+ it "should have header and control record with length of 94" do
68
+ [:header, :control].each do |record|
69
+ @file.batches[0].send(record).to_s!.length.should == ACH::Constants::RECORD_SIZE
70
+ end
60
71
  end
72
+
61
73
  end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ describe ACH::Component::HasManyAssociation do
4
+ before(:all) do
5
+ ACH::Element = Class.new(ACH::Component)
6
+ ACH::Record::Item = Class.new(ACH::Record::Dynamic) do
7
+ item_id '<-3' => 'foo'
8
+ end
9
+ end
10
+
11
+ after(:all) do
12
+ ACH.remove_const(:Element)
13
+ ACH::Record.remove_const(:Item)
14
+ end
15
+
16
+ describe "properties" do
17
+ before(:each) do
18
+ @association = ACH::Component::HasManyAssociation.new(:elements)
19
+ end
20
+
21
+ it "should have proper klass" do
22
+ @association.send(:klass).should == ACH::Element
23
+ end
24
+
25
+ it "should have proper singular name" do
26
+ @association.send(:singular_name).should == 'element'
27
+ end
28
+
29
+ it "should have proper set of delegation methods" do
30
+ @association.delegation_methods.to_set.should == %w{elements element build_element}.to_set
31
+ end
32
+
33
+ describe "owner assignment" do
34
+ before(:each) do
35
+ @assigned = @association.for(ACH::Element.new)
36
+ end
37
+
38
+ it "should have @owner assigned" do
39
+ @assigned.instance_variable_get('@owner').should_not be_nil
40
+ end
41
+
42
+ it "should not share containers" do
43
+ @other = @association.for(ACH::Element.new)
44
+ @assigned.container.object_id.should_not == @other.container.object_id
45
+ end
46
+
47
+ it "should have method aliases for owner to delegate" do
48
+ @association.delegation_methods.each do |method_name|
49
+ @assigned.should respond_to method_name
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "reassignment" do
55
+ it "should raise error" do
56
+ assigned = @association.for(Object.new)
57
+ expect{ assigned.for(Object.new) }.to raise_error(ACH::Component::HasManyAssociation::DoubleAssignmentError)
58
+ end
59
+ end
60
+
61
+ describe "without :linked_to option" do
62
+ it "should have Array as container" do
63
+ @association.container.should be_an Array
64
+ end
65
+
66
+ it "container_for_associated should be the same as container" do
67
+ @association.container_for_associated.object_id.should == @association.container.object_id
68
+ end
69
+ end
70
+
71
+ describe "with :linked_to option" do
72
+ before(:each) do
73
+ @linked_association = ACH::Component::HasManyAssociation.new(:items, :linked_to => :elements)
74
+ @assigned = @association.for(ACH::Element.new)
75
+ @linked_assigned = @linked_association.for(@assigned)
76
+ end
77
+
78
+ it "should have proper klass" do
79
+ @linked_association.send(:klass).should == ACH::Record::Item
80
+ end
81
+
82
+ it "should have Hash as container" do
83
+ @linked_association.container.should be_a Hash
84
+ end
85
+
86
+ it "container_for_associated should raise NoLinkError if there is no parent record created" do
87
+ expect {
88
+ @linked_assigned.container_for_associated
89
+ }.to raise_error(ACH::Component::HasManyAssociation::NoLinkError)
90
+ end
91
+
92
+ it "container_for_associated should be array if there is parent record" do
93
+ @assigned.create
94
+ @linked_assigned.container_for_associated.should be_an Array
95
+ end
96
+ end
97
+
98
+ describe "with :proc_defaults option" do
99
+ before(:each) do
100
+ @proc_association = ACH::Component::HasManyAssociation.new(
101
+ :items, :proc_defaults => lambda{ {:item_id => 'bar'} }
102
+ ).for(ACH::Element.new)
103
+ end
104
+
105
+ it "created item should have proper value" do
106
+ item = @proc_association.create
107
+ item.item_id.should == 'bar'
108
+ end
109
+ end
110
+ end
111
+ end
@@ -1,90 +1,182 @@
1
1
  require 'spec_helper'
2
2
 
3
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
- @sample_file = ACH.sample_file
18
- end
19
-
20
- it "should correctly assign attributes" do
21
- @file.company_id.should == '11-11111'
22
- end
23
-
24
- it "should be modified by calling attribute methods in block" do
25
- file = ACH::File.new(@attributes) do
26
- company_name "MINE COMPANY"
4
+
5
+ context "building the ACH file" do
6
+ before(:each) do
7
+ @attributes = {
8
+ :company_id => '11-11111',
9
+ :company_name => 'MY COMPANY',
10
+ :immediate_dest => '123123123',
11
+ :immediate_dest_name => 'COMMERCE BANK',
12
+ :immediate_origin => '123123123',
13
+ :immediate_origin_name => 'MYCOMPANY' }
14
+ @invalid_attributes = {:foo => 'bar'}
15
+ @file = ACH::File.new(@attributes)
16
+ @file_with_batch = ACH::File.new(@attributes) do
17
+ batch :entry_class_code => 'WEB'
18
+ end
19
+ @sample_file = ACH::FileFactory.sample_file
27
20
  end
28
- file.company_name.should == "MINE COMPANY"
29
- end
30
-
31
- it "should fetch and return header" do
32
- head = @file.header
33
- head.should be_instance_of(ACH::File::Header)
34
- head.immediate_dest.should == '123123123'
35
- end
36
-
37
- it "should be able to modify header info in block form" do
38
- file = ACH::File.new(@attributes) do
39
- header(:immediate_dest => '321321321') do
40
- immediate_dest_name 'BANK COMMERCE'
21
+
22
+ it "should correctly assign attributes" do
23
+ @file.company_id.should == '11-11111'
24
+ end
25
+
26
+ it "should be modified by calling attribute methods in block" do
27
+ file = ACH::File.new(@attributes) do
28
+ company_name "MINE COMPANY"
41
29
  end
30
+ file.company_name.should == "MINE COMPANY"
31
+ end
32
+
33
+ it "should fetch and return header" do
34
+ head = @file.header
35
+ head.should be_instance_of(ACH::File::Header)
36
+ head.immediate_dest.should == '123123123'
37
+ end
38
+
39
+ it "should be able to modify header info in block form" do
40
+ file = ACH::File.new(@attributes) do
41
+ header(:immediate_dest => '321321321') do
42
+ immediate_dest_name 'BANK COMMERCE'
43
+ end
44
+ end
45
+ head = file.header
46
+ head.immediate_dest.should == '321321321'
47
+ head.immediate_dest_name.should == 'BANK COMMERCE'
48
+ end
49
+
50
+ it "should raise exception on unknown attribute assignement" do
51
+ lambda {
52
+ ACH::File.new(@invalid_attributes)
53
+ }.should raise_error(ACH::Component::UnknownAttributeError)
54
+ end
55
+
56
+ it "should be able to create a batch" do
57
+ @file_with_batch.batches.should_not be_empty
58
+ end
59
+
60
+ it "should return a batch when index is passed" do
61
+ @file_with_batch.batches[0].should be_instance_of(ACH::Batch)
62
+ end
63
+
64
+ it "should assign a batch_number to a batch" do
65
+ batch = @file_with_batch.batches[0]
66
+ batch.batch_number.should == 1
67
+ batch = @file_with_batch.batch(:entry_class_code => 'WEB')
68
+ batch.batch_number.should == 2
69
+ end
70
+
71
+ it "should assign attributes to a batch" do
72
+ batch = @file_with_batch.batches[0]
73
+ batch.attributes.should include(@file_with_batch.attributes)
74
+ end
75
+
76
+ it "should have correct record count" do
77
+ @sample_file.record_count.should == 10
78
+ end
79
+
80
+ it "should have header and control record with length of 94" do
81
+ [:header, :control].each do |record|
82
+ @sample_file.send(record).to_s!.length.should == ACH::Constants::RECORD_SIZE
83
+ end
84
+ end
85
+
86
+ it "should have length devisible by 94 (record size)" do
87
+ (@sample_file.to_s!.gsub(ACH::Constants::ROWS_DELIMITER, '').length % ACH::Constants::RECORD_SIZE).should be_zero
88
+ end
89
+
90
+
91
+ describe 'transmission header' do
92
+ before(:all) do
93
+ @with_transmission_header
94
+ attrs = {:remote_id => 'ZYXWVUTS', :application_id => '98765432'}
95
+ ach_file = ACH::FileFactory.with_transmission_header(attrs)
96
+ @transmission_header = ach_file.to_s!.split(ACH::Constants::ROWS_DELIMITER).first
97
+ end
98
+
99
+ it "should raise error when defining empty transmission header" do
100
+ expect do
101
+ Class.new(ACH::File) do
102
+ transmission_header
103
+ end
104
+ end.to raise_error(ACH::File::EmptyTransmissionHeaderError)
105
+ end
106
+
107
+ it "have_transmission_header? method should return proper value" do
108
+ without_header = Class.new(ACH::File)
109
+ with_header = Class.new(ACH::File) do
110
+ transmission_header do
111
+ application_id
112
+ end
113
+ end
114
+
115
+ without_header.have_transmission_header?.should be_false
116
+ with_header.have_transmission_header?.should be_true
117
+ end
118
+
119
+ it "has length of 38" do
120
+ @transmission_header.length.should == 38
121
+ end
122
+
123
+ it "has specified remote_id" do
124
+ @transmission_header[9..16].should == 'ZYXWVUTS'
125
+ end
126
+
127
+ it "has specified application_id" do
128
+ @transmission_header[29..36].should == '98765432'
129
+ end
130
+ end
131
+
132
+ it 'number of records is multiple of 10 (transmission header is ignored)' do
133
+ records = ACH::FileFactory.sample_file.to_s!.split(ACH::Constants::ROWS_DELIMITER)
134
+ (records.size % 10).should == 0
135
+
136
+ records = ACH::FileFactory.with_transmission_header.to_s!.split(ACH::Constants::ROWS_DELIMITER)
137
+ ((records.size - 1) % 10).should == 0
138
+ end
139
+
140
+ describe 'inherited class' do
141
+ before(:all) do
142
+ @custom_file_class = Class.new(ACH::File) do
143
+ immediate_dest_name 'CUSTOM VALUE'
144
+ customer_name "PETER PARKER"
145
+ end
146
+
147
+ @custom_file = @custom_file_class.new do
148
+ immediate_dest '123123123'
149
+ immediate_origin '123123123'
150
+ immediate_origin_name 'MYCOMPANY'
151
+ batch(:entry_class_code => "WEB", :company_entry_descr => 'TV-TELCOM') do
152
+ effective_date Time.now.strftime('%y%m%d')
153
+ desc_date Time.now.strftime('%b %d').upcase
154
+ origin_dfi_id "00000000"
155
+ entry :customer_acct => '61242882282',
156
+ :amount => '2501',
157
+ :routing_number => '010010101',
158
+ :bank_account => '103030030'
159
+ end
160
+ end
161
+ end
162
+
163
+ it 'should use default values defined in inherited class' do
164
+ header = @custom_file.header
165
+ header.immediate_dest_name.should == "CUSTOM VALUE"
166
+ entry = @custom_file.batches.first.entries.first
167
+ entry.customer_name.should == "PETER PARKER"
168
+ end
169
+ end
170
+
171
+ describe "file control" do
172
+ subject { @file.control }
173
+ it { should be_an ACH::File::Control }
42
174
  end
43
- head = file.header
44
- head.immediate_dest.should == '321321321'
45
- head.immediate_dest_name.should == 'BANK COMMERCE'
46
- end
47
-
48
- it "should raise exception on unknown attribute assignement" do
49
- lambda {
50
- ACH::File.new(@invalid_attributes)
51
- }.should raise_error(ACH::Component::UnknownAttribute)
52
- end
53
-
54
- it "should be able to create a batch" do
55
- @file_with_batch.batches.should_not be_empty
56
- end
57
-
58
- it "should return a batch when index is passed" do
59
- @file_with_batch.batch(0).should be_instance_of(ACH::Batch)
60
- end
61
-
62
- it "should assign a batch_number to a batch" do
63
- batch = @file_with_batch.batch(0)
64
- batch.batch_number.should == 1
65
- batch = @file_with_batch.batch(:entry_class_code => 'WEB')
66
- batch.batch_number.should == 2
67
- end
68
-
69
- it "should assign attributes to a batch" do
70
- batch = @file_with_batch.batch(0)
71
- batch.attributes.should include(@file_with_batch.attributes)
72
- end
73
-
74
- it "should have correct record count" do
75
- @sample_file.record_count.should == 8
76
- end
77
-
78
- it "should have header record with length of 94" do
79
- @sample_file.header.to_s!.length.should == ACH::Constants::RECORD_SIZE
80
- end
81
-
82
- it "should have control record with length of 94" do
83
- @sample_file.control.to_s!.length.should == ACH::Constants::RECORD_SIZE
84
175
  end
85
-
86
- it "should have length devisible by 94 (record size)" do
87
- (@sample_file.to_s!.gsub("\r\n", '').length % ACH::Constants::RECORD_SIZE).should be_zero
176
+
177
+ context "reading the ACH file" do
178
+ before(:each) { @result = ACH::File.read well_fargo_empty_filename }
179
+ subject { @result }
180
+ it { should be_an ACH::File }
88
181
  end
89
182
  end
90
-