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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -1
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +10 -0
- data/Gemfile +11 -2
- data/MIT-LICENSE +1 -1
- data/README.md +19 -11
- data/Rakefile +44 -0
- data/ach_builder.gemspec +9 -5
- data/lib/ach/batch.rb +10 -32
- data/lib/ach/batch/builder.rb +60 -0
- data/lib/ach/batch/control.rb +18 -3
- data/lib/ach/batch/header.rb +21 -4
- data/lib/ach/component.rb +125 -43
- data/lib/ach/component/has_many_association.rb +155 -0
- data/lib/ach/constants.rb +15 -1
- data/lib/ach/file.rb +50 -44
- data/lib/ach/file/builder.rb +81 -0
- data/lib/ach/file/control.rb +15 -3
- data/lib/ach/file/header.rb +20 -3
- data/lib/ach/file/reader.rb +103 -0
- data/lib/ach/file/transmission_header.rb +77 -0
- data/lib/ach/formatter.rb +113 -76
- data/lib/ach/formatter/rule.rb +27 -0
- data/lib/ach/record.rb +13 -64
- data/lib/ach/record/addenda.rb +25 -0
- data/lib/ach/record/base.rb +109 -0
- data/lib/ach/record/dynamic.rb +58 -0
- data/lib/ach/record/entry.rb +49 -0
- data/lib/ach/record/tail.rb +10 -0
- data/lib/ach/validations.rb +5 -3
- data/lib/ach/version.rb +1 -1
- data/lib/ach_builder.rb +20 -33
- data/spec/batch_spec.rb +23 -11
- data/spec/componenet/has_many_association_spec.rb +111 -0
- data/spec/file_spec.rb +173 -81
- data/spec/formatter_spec.rb +9 -9
- data/spec/reader_spec.rb +55 -0
- data/spec/record/addenda_spec.rb +8 -0
- data/spec/record/base_spec.rb +53 -0
- data/spec/record/dynamic_spec.rb +74 -0
- data/spec/record/entry_spec.rb +7 -0
- data/spec/record/tail_spec.rb +7 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/ach_file_factory.rb +70 -0
- data/spec/support/examples/well_fargo_empty.ach +2 -0
- data/spec/support/examples/well_fargo_with_data.ach +6 -0
- data/spec/support/helpers/ach_files_examples.rb +17 -0
- metadata +69 -26
- data/lib/ach/entry.rb +0 -31
- data/lib/ach/tail.rb +0 -6
- data/spec/entry_spec.rb +0 -7
- data/spec/record_spec.rb +0 -34
- data/spec/tail_spec.rb +0 -7
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a6370b31599af5467812e5b45be57e99c5e19a32
|
4
|
+
data.tar.gz: 326db2ed6596c13a95ad0deeb8b5a9881270e857
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a039dc192619a516cdf9c09fdefe33dde1505655c6b67c9bdacfd7ab47301a5586b1d1b5cd01292ef244cbd1fece3aa81b7344a9c4168f40e0a68d6694ac31f5
|
7
|
+
data.tar.gz: 5175200c43de130adb641b6d6c3d247b860b6e186cf2ff9ecc871dae6d95f0465f189a04f9d0e8f2430c811b77c74ad6272bbdea8b8109f6d667037aafce5fff
|
data/.gitignore
CHANGED
@@ -2,3 +2,15 @@
|
|
2
2
|
.bundle
|
3
3
|
Gemfile.lock
|
4
4
|
pkg/*
|
5
|
+
/.yardoc
|
6
|
+
.idea
|
7
|
+
doc/
|
8
|
+
|
9
|
+
# exclude everything in tmp
|
10
|
+
tmp/*
|
11
|
+
# except the metric_fu directory
|
12
|
+
!tmp/metric_fu/
|
13
|
+
# but exclude everything *in* the metric_fu directory
|
14
|
+
tmp/metric_fu/*
|
15
|
+
# except for the _data directory to track metrical outputs
|
16
|
+
!tmp/metric_fu/_data/
|
data/.rspec
CHANGED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ach_builder
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p247
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,5 +1,14 @@
|
|
1
|
-
source "
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
2
3
|
|
3
4
|
# Specify your gem's dependencies in ach_builder.gemspec
|
5
|
+
gem 'i18n'
|
4
6
|
gemspec
|
5
|
-
gem 'rspec'
|
7
|
+
gem 'rspec'
|
8
|
+
|
9
|
+
group :development, :test do
|
10
|
+
gem 'rspec'
|
11
|
+
|
12
|
+
gem "simplecov" , :require => false
|
13
|
+
gem 'simplecov-rcov-text', :require => false
|
14
|
+
end
|
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,41 +1,49 @@
|
|
1
1
|
#ACH-Builder
|
2
2
|
|
3
|
-
|
3
|
+
[](http://travis-ci.org/TMXCredit/ach_builder)
|
4
4
|
|
5
|
-
|
5
|
+
Ruby helper for creating ACH files. Its API is designed to be as flexible as possible.
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
[ACH::Builder](http://github.com/camerb/ACH-Builder) - Perl module
|
10
|
-
|
11
|
-
with similar functionality
|
7
|
+
Inspired by:
|
8
|
+
* [ach](http://github.com/jm81/ach) - Ruby helper
|
9
|
+
* [ACH::Builder](http://github.com/camerb/ACH-Builder) - Perl module
|
12
10
|
|
13
11
|
##Example
|
12
|
+
|
13
|
+
```ruby
|
14
14
|
# attributes for records may be passed as parameters, as well as modified in block
|
15
15
|
# these attributes will be passed to all inner entities in a cascade way, if required
|
16
16
|
file = ACH::File.new(:company_id => '11-11111', :company_name => 'MY COMPANY') do
|
17
17
|
immediate_dest_name 'COMMERCE BANK'
|
18
18
|
immediate_origin '123123123'
|
19
|
-
|
20
|
-
|
19
|
+
immediate_origin_name 'MYCOMPANY'
|
20
|
+
|
21
21
|
['WEB', 'TEL'].each do |code|
|
22
22
|
batch(:entry_class_code => code, :company_entry_descr => 'TV-TELCOM') do
|
23
23
|
effective_date Time.now.strftime('%y%m%d')
|
24
24
|
origin_dfi_id "00000000"
|
25
|
+
|
25
26
|
entry :customer_name => 'JOHN SMITH',
|
26
27
|
:customer_acct => '61242882282',
|
27
28
|
:amount => '2501',
|
28
29
|
:routing_number => '010010101',
|
29
30
|
:bank_account => '103030030'
|
31
|
+
addenda do
|
32
|
+
addenda_type_code '05'
|
33
|
+
payment_related_info 'foo bar'
|
34
|
+
addenda_sequence_num 1
|
35
|
+
entry_details_sequence_num 1
|
36
|
+
end
|
30
37
|
end
|
31
38
|
end
|
32
39
|
end
|
33
|
-
|
40
|
+
|
34
41
|
file.valid? # => false
|
35
42
|
file.errors # => {"ACH::File::Header#1"=>{:immediate_dest=>"is required"}}
|
36
43
|
file.header.immediate_dest = '123123123'
|
37
44
|
file.write('ach_01.txt')
|
45
|
+
```
|
38
46
|
|
39
47
|
##Copyright
|
40
48
|
|
41
|
-
Copyright (c)
|
49
|
+
Copyright (c) 2013 TMX Credit, released under the MIT license
|
data/Rakefile
CHANGED
@@ -1 +1,45 @@
|
|
1
|
+
task :default => :spec
|
2
|
+
|
1
3
|
require "bundler/gem_tasks"
|
4
|
+
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
7
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
def gemspec
|
11
|
+
@gem_spec ||= eval( open( `ls *.gemspec`.strip ){|file| file.read } )
|
12
|
+
end
|
13
|
+
|
14
|
+
def gem_version
|
15
|
+
gemspec.version
|
16
|
+
end
|
17
|
+
|
18
|
+
def gem_version_tag
|
19
|
+
"v#{gem_version}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def gem_name
|
23
|
+
gemspec.name
|
24
|
+
end
|
25
|
+
|
26
|
+
def gem_file_name
|
27
|
+
"#{gem_name}-#{gem_version}.gem"
|
28
|
+
end
|
29
|
+
|
30
|
+
namespace :git do
|
31
|
+
desc "Create git version tag #{gem_version}"
|
32
|
+
task :tag do
|
33
|
+
sh "git tag -a #{gem_version_tag} -m \"Version #{gem_version}\""
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "Push git tag to GitHub"
|
37
|
+
task :push_tags do
|
38
|
+
sh 'git push --tags'
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Create git version tag #{gem_version} and push to GitHub"
|
42
|
+
task :submit => [:tag, :push_tags] do
|
43
|
+
puts "Deployed to GitHub."
|
44
|
+
end
|
45
|
+
end
|
data/ach_builder.gemspec
CHANGED
@@ -5,9 +5,9 @@ require "ach/version"
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "ach_builder"
|
7
7
|
s.version = ACH::VERSION
|
8
|
-
s.authors = ["Artem Kuzko"]
|
9
|
-
s.email = ["
|
10
|
-
s.homepage = "
|
8
|
+
s.authors = ["TMX Credit", "Artem Kuzko", "Sergey Potapov"]
|
9
|
+
s.email = ["rubygems@tmxcredit.com", "AKuzko@sphereconsultinginc.com", "SPotapov@sphereconsultinginc.com"]
|
10
|
+
s.homepage = "https://github.com/TMXCredit/ach_builder"
|
11
11
|
s.summary = "Ruby tools for building ACH files"
|
12
12
|
s.description = "Ruby tools for building ACH (Automated Clearing House) files"
|
13
13
|
|
@@ -17,7 +17,11 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ["lib"]
|
20
|
+
s.licenses = ['MIT']
|
20
21
|
|
21
|
-
s.add_development_dependency "rspec", ">= 2.0.0"
|
22
|
-
s.add_runtime_dependency "
|
22
|
+
s.add_development_dependency "rspec" , ">= 2.0.0"
|
23
|
+
s.add_runtime_dependency "activesupport", ">= 2.3.0"
|
24
|
+
|
25
|
+
# Necessary for active_support/inflector:
|
26
|
+
s.add_runtime_dependency "i18n"
|
23
27
|
end
|
data/lib/ach/batch.rb
CHANGED
@@ -1,37 +1,15 @@
|
|
1
1
|
module ACH
|
2
|
+
# Represents an ACH::Component, which is located under ACH::File and
|
3
|
+
# contains a variable number of ACH::Record::Entry and ACH::Record::Addenda
|
4
|
+
# records itself.
|
2
5
|
class Batch < Component
|
6
|
+
autoload :Builder
|
7
|
+
autoload :Control
|
8
|
+
autoload :Header
|
9
|
+
|
10
|
+
include Builder
|
11
|
+
|
3
12
|
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
|
13
|
+
has_many :addendas, :linked_to => :entries
|
36
14
|
end
|
37
15
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ACH
|
2
|
+
# Supports building a string representation of a particular instance of an
|
3
|
+
# ACH batch. Supports building ACH lines. Included by ACH::File.
|
4
|
+
module Batch::Builder
|
5
|
+
# Returns +true+ if any of internal ACH entries has 'credit' transaction code
|
6
|
+
def has_credit?
|
7
|
+
entries.any?(&:credit?)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns +true+ if any internal ACH entry has 'debit' transaction code
|
11
|
+
def has_debit?
|
12
|
+
entries.any?(&:debit?)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns total amount of entry and addenda records within batch
|
16
|
+
def entry_addenda_count
|
17
|
+
entries.size + addendas.values.flatten.size
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns 'hashed' representation of all entries within batch. See NACHA
|
21
|
+
# documentation for more details on entry hash
|
22
|
+
def entry_hash
|
23
|
+
entries.map{ |entry| entry.routing_number.to_i / 10 }.compact.inject(&:+)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns total amount of all 'debit' entries within a batch
|
27
|
+
def total_debit_amount
|
28
|
+
amount_sum_for(:debit?)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns total amount of all 'credit' entries within a batch
|
32
|
+
def total_credit_amount
|
33
|
+
amount_sum_for(:credit?)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns ACH record objects that represent the batch
|
37
|
+
def to_ach
|
38
|
+
[header] + fetch_entries + [control]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Helper method executed just before building a header record for the batch
|
42
|
+
def before_header
|
43
|
+
attributes[:service_class_code] ||= (has_debit? && has_credit? ? 200 : has_debit? ? 225 : 220)
|
44
|
+
end
|
45
|
+
private :before_header
|
46
|
+
|
47
|
+
# Helper method, returns total amount of all entries within a batch, filtered by +meth+
|
48
|
+
def amount_sum_for(meth)
|
49
|
+
entries.select(&meth).map{ |entry| entry.amount.to_i }.compact.inject(&:+) || 0
|
50
|
+
end
|
51
|
+
private :amount_sum_for
|
52
|
+
|
53
|
+
# Fetches all internal records (entries and addendas) in right order, i.e. addenda records
|
54
|
+
# should be positioned right after corresponding entry records.
|
55
|
+
def fetch_entries
|
56
|
+
entries.inject([]){ |all, entry| all << entry << addendas[entry] }.flatten.compact
|
57
|
+
end
|
58
|
+
private :fetch_entries
|
59
|
+
end
|
60
|
+
end
|
data/lib/ach/batch/control.rb
CHANGED
@@ -1,8 +1,23 @@
|
|
1
1
|
module ACH
|
2
|
-
|
2
|
+
# Every ACH::Batch component ends with a batch control record.
|
3
|
+
#
|
4
|
+
# == Fields
|
5
|
+
#
|
6
|
+
# * record_type
|
7
|
+
# * service_class_code
|
8
|
+
# * entry_addenda_count
|
9
|
+
# * entry_hash
|
10
|
+
# * total_debit_amount
|
11
|
+
# * total_credit_amount
|
12
|
+
# * company_id
|
13
|
+
# * authen_code
|
14
|
+
# * bank_6
|
15
|
+
# * origin_dfi_id
|
16
|
+
# * batch_number
|
17
|
+
class Batch::Control < Record::Base
|
3
18
|
fields :record_type,
|
4
19
|
:service_class_code,
|
5
|
-
:
|
20
|
+
:entry_addenda_count,
|
6
21
|
:entry_hash,
|
7
22
|
:total_debit_amount,
|
8
23
|
:total_credit_amount,
|
@@ -12,7 +27,7 @@ module ACH
|
|
12
27
|
:origin_dfi_id,
|
13
28
|
:batch_number
|
14
29
|
|
15
|
-
defaults :record_type =>
|
30
|
+
defaults :record_type => BATCH_CONTROL_RECORD_TYPE,
|
16
31
|
:authen_code => '',
|
17
32
|
:bank_6 => ''
|
18
33
|
end
|
data/lib/ach/batch/header.rb
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
module ACH
|
2
|
-
|
2
|
+
# Every ACH::Batch component starts with a batch header record
|
3
|
+
#
|
4
|
+
# == Fields
|
5
|
+
#
|
6
|
+
# * record_type
|
7
|
+
# * service_class_code
|
8
|
+
# * company_name
|
9
|
+
# * company_note_data
|
10
|
+
# * company_id
|
11
|
+
# * entry_class_code
|
12
|
+
# * company_entry_descr
|
13
|
+
# * desc_date
|
14
|
+
# * effective_date
|
15
|
+
# * settlement_date
|
16
|
+
# * origin_status_code
|
17
|
+
# * origin_dfi_id
|
18
|
+
# * batch_number
|
19
|
+
class Batch::Header < Record::Base
|
3
20
|
fields :record_type,
|
4
21
|
:service_class_code,
|
5
22
|
:company_name,
|
@@ -7,18 +24,18 @@ module ACH
|
|
7
24
|
:company_id,
|
8
25
|
:entry_class_code,
|
9
26
|
:company_entry_descr,
|
10
|
-
:
|
27
|
+
:desc_date,
|
11
28
|
:effective_date,
|
12
29
|
:settlement_date,
|
13
30
|
:origin_status_code,
|
14
31
|
:origin_dfi_id,
|
15
32
|
:batch_number
|
16
33
|
|
17
|
-
defaults :record_type =>
|
34
|
+
defaults :record_type => BATCH_HEADER_RECORD_TYPE,
|
18
35
|
:service_class_code => 200,
|
19
36
|
:company_note_data => '',
|
20
37
|
:date => lambda{ Time.now.strftime("%y%m%d") },
|
21
38
|
:settlement_date => '',
|
22
|
-
:origin_status_code =>
|
39
|
+
:origin_status_code => 1
|
23
40
|
end
|
24
41
|
end
|
data/lib/ach/component.rb
CHANGED
@@ -1,39 +1,98 @@
|
|
1
1
|
module ACH
|
2
|
+
# Base class for ACH::File and ACH::Batch. Every component has its own number
|
3
|
+
# of entities, header and control records. So it provides ACH::Component#header,
|
4
|
+
# ACH::Component#control, ACH::Component.has_many methods to manage them.
|
5
|
+
#
|
6
|
+
# == Example
|
7
|
+
#
|
8
|
+
# class File < Component
|
9
|
+
# has_many :batches
|
10
|
+
# # implementation
|
11
|
+
# end
|
2
12
|
class Component
|
13
|
+
extend ActiveSupport::Autoload
|
14
|
+
|
3
15
|
include Validations
|
4
16
|
include Constants
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
17
|
+
|
18
|
+
autoload :HasManyAssociation
|
19
|
+
|
20
|
+
# Exception raised on attempt to assign a value to nonexistent field.
|
21
|
+
class UnknownAttributeError < ArgumentError
|
22
|
+
def initialize field, obj
|
23
|
+
super "Unrecognized attribute '#{field}' for #{obj}"
|
9
24
|
end
|
10
25
|
end
|
11
26
|
|
27
|
+
class_attribute :default_attributes
|
28
|
+
class_attribute :after_initialize_hooks
|
29
|
+
self.default_attributes = {}
|
30
|
+
self.after_initialize_hooks = []
|
31
|
+
|
12
32
|
attr_reader :attributes
|
13
|
-
|
14
|
-
def
|
15
|
-
|
33
|
+
|
34
|
+
def self.inherited(klass)
|
35
|
+
klass.default_attributes = default_attributes.dup
|
36
|
+
klass.after_initialize_hooks = after_initialize_hooks.dup
|
37
|
+
end
|
38
|
+
|
39
|
+
# Uses +method_missing+ pattern to specify default attributes for a
|
40
|
+
# +Component+. If method name is one of the defined rules, saves it to
|
41
|
+
# +default_attributes+ hash.
|
42
|
+
#
|
43
|
+
# These attributes are passed to inner components in a cascade way, i.e. when ACH
|
44
|
+
# File was defined with default value for 'company_name', this value will be passed
|
45
|
+
# to every Batch component within file, and from every Batch to corresponding batch
|
46
|
+
# header record.
|
47
|
+
#
|
48
|
+
# Note that default values may be overwritten when building records.
|
49
|
+
def self.method_missing(meth, *args)
|
50
|
+
if Formatter.defined?(meth)
|
51
|
+
default_attributes[meth] = args.first
|
52
|
+
else
|
53
|
+
super
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(fields = {}, &block)
|
58
|
+
@attributes = {}.merge(self.class.default_attributes)
|
16
59
|
fields.each do |name, value|
|
17
|
-
raise
|
60
|
+
raise UnknownAttributeError.new(name, self) unless Formatter.defined?(name)
|
18
61
|
@attributes[name] = value
|
19
62
|
end
|
20
|
-
after_initialize
|
63
|
+
after_initialize
|
21
64
|
instance_eval(&block) if block
|
22
65
|
end
|
23
|
-
|
24
|
-
def method_missing
|
25
|
-
if Formatter
|
66
|
+
|
67
|
+
def method_missing(meth, *args)
|
68
|
+
if Formatter.defined?(meth)
|
26
69
|
args.empty? ? @attributes[meth] : (@attributes[meth] = args.first)
|
27
70
|
else
|
28
71
|
super
|
29
72
|
end
|
30
73
|
end
|
31
|
-
|
32
|
-
def before_header
|
74
|
+
|
75
|
+
def before_header # :nodoc:
|
33
76
|
end
|
34
77
|
private :before_header
|
35
|
-
|
36
|
-
|
78
|
+
|
79
|
+
# Sets header fields if fields or block passed. Returns header record.
|
80
|
+
#
|
81
|
+
# == Example 1
|
82
|
+
#
|
83
|
+
# header :foo => "value 1", :bar => "value 2"
|
84
|
+
#
|
85
|
+
# == Example 2
|
86
|
+
#
|
87
|
+
# header do
|
88
|
+
# foo "value 1"
|
89
|
+
# bar "value 2"
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# == Example 3
|
93
|
+
#
|
94
|
+
# header # => just returns a header object
|
95
|
+
def header(fields = {}, &block)
|
37
96
|
before_header
|
38
97
|
merged_fields = fields_for(self.class::Header).merge(fields)
|
39
98
|
@header ||= self.class::Header.new(merged_fields)
|
@@ -41,39 +100,62 @@ module ACH
|
|
41
100
|
head.instance_eval(&block) if block
|
42
101
|
end
|
43
102
|
end
|
44
|
-
|
103
|
+
|
104
|
+
def build_header(str) # :nodoc:
|
105
|
+
@header = self.class::Header.from_s(str)
|
106
|
+
end
|
107
|
+
|
45
108
|
def control
|
46
|
-
|
47
|
-
|
48
|
-
|
109
|
+
@control ||= begin
|
110
|
+
klass = self.class::Control
|
111
|
+
fields = klass.fields.select{ |f| respond_to?(f) || attributes[f] }
|
112
|
+
klass.new Hash[*fields.zip(fields.map{ |f| send(f) }).flatten]
|
113
|
+
end
|
49
114
|
end
|
50
|
-
|
51
|
-
def
|
52
|
-
|
53
|
-
klass < Component ? attributes : attributes.select{ |k, v| klass.fields.include?(k) && attributes[k] }
|
115
|
+
|
116
|
+
def build_control(str) # :nodoc:
|
117
|
+
@control = self.class::Control.from_s(str)
|
54
118
|
end
|
55
119
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
define_method(singular_name) do |*args, &block|
|
63
|
-
index_or_fields = args.first || {}
|
64
|
-
return send(plural_name)[index_or_fields] if Fixnum === index_or_fields
|
65
|
-
|
66
|
-
defaults = proc_defaults ? instance_exec(&proc_defaults) : {}
|
67
|
-
|
68
|
-
klass.new(fields_for(singular_name).merge(defaults).merge(index_or_fields)).tap do |component|
|
69
|
-
component.instance_eval(&block) if block
|
70
|
-
send(plural_name) << component
|
71
|
-
end
|
120
|
+
def fields_for(klass)
|
121
|
+
if klass < Component
|
122
|
+
attributes
|
123
|
+
else
|
124
|
+
attrs = attributes.find_all{ |k, v| klass.fields.include?(k) && attributes[k] }
|
125
|
+
Hash[*attrs.flatten]
|
72
126
|
end
|
73
|
-
|
74
|
-
|
75
|
-
|
127
|
+
end
|
128
|
+
|
129
|
+
def after_initialize # :nodoc:
|
130
|
+
self.class.after_initialize_hooks.each{ |hook| instance_exec(&hook) }
|
131
|
+
end
|
132
|
+
|
133
|
+
# Creates has many association.
|
134
|
+
#
|
135
|
+
# == Example
|
136
|
+
#
|
137
|
+
# class File < Component
|
138
|
+
# has_many :batches
|
139
|
+
# end
|
140
|
+
#
|
141
|
+
# file = File.new do
|
142
|
+
# batch :foo => 1, :bar => 2
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# file.batches # => [#<Batch ...>]
|
146
|
+
#
|
147
|
+
# The example above extends File with #batches and #batch instance methods:
|
148
|
+
# * #batch is used to add new instance of Batch.
|
149
|
+
# * #batches is used to get an array of batches which belong to file.
|
150
|
+
def self.has_many(plural_name, options = {})
|
151
|
+
association = HasManyAssociation.new(plural_name, options)
|
152
|
+
|
153
|
+
association_variable_name = "@#{plural_name}_association"
|
154
|
+
association.delegation_methods.each do |method_name|
|
155
|
+
delegate method_name, :to => association_variable_name
|
76
156
|
end
|
157
|
+
|
158
|
+
after_initialize_hooks << lambda{ instance_variable_set(association_variable_name, association.for(self)) }
|
77
159
|
end
|
78
160
|
end
|
79
161
|
end
|