ach_builder 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a6370b31599af5467812e5b45be57e99c5e19a32
4
- data.tar.gz: 326db2ed6596c13a95ad0deeb8b5a9881270e857
3
+ metadata.gz: 13b50247a53e8f32fca29c49375e6d700bd039bf
4
+ data.tar.gz: 84d5a95e03d5f96f1a591c47d23e748cc613c1ca
5
5
  SHA512:
6
- metadata.gz: a039dc192619a516cdf9c09fdefe33dde1505655c6b67c9bdacfd7ab47301a5586b1d1b5cd01292ef244cbd1fece3aa81b7344a9c4168f40e0a68d6694ac31f5
7
- data.tar.gz: 5175200c43de130adb641b6d6c3d247b860b6e186cf2ff9ecc871dae6d95f0465f189a04f9d0e8f2430c811b77c74ad6272bbdea8b8109f6d667037aafce5fff
6
+ metadata.gz: 6442a48f3ac4122b01339c4f95d5b4306efe21437834633211ca5bb16495cb2e07a8a08e59f8a28406a898c80101fdd05e3c8269152bb8d679876dd79c6974b4
7
+ data.tar.gz: 65e1c78ea5211a84b37394cfa84c2ef296cc6248291139f26d7a72bebd1c152189d3a1bce1903c5f950869832b3785a01a1adc16050594c79d08dcbef8c4d62e
@@ -1 +1 @@
1
- 2.0.0-p247
1
+ 2.0.0
data/Gemfile CHANGED
@@ -4,10 +4,10 @@ source "https://rubygems.org"
4
4
  # Specify your gem's dependencies in ach_builder.gemspec
5
5
  gem 'i18n'
6
6
  gemspec
7
- gem 'rspec'
8
7
 
9
8
  group :development, :test do
10
9
  gem 'rspec'
10
+ gem 'yard'
11
11
 
12
12
  gem "simplecov" , :require => false
13
13
  gem 'simplecov-rcov-text', :require => false
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
6
6
  s.name = "ach_builder"
7
7
  s.version = ACH::VERSION
8
8
  s.authors = ["TMX Credit", "Artem Kuzko", "Sergey Potapov"]
9
- s.email = ["rubygems@tmxcredit.com", "AKuzko@sphereconsultinginc.com", "SPotapov@sphereconsultinginc.com"]
9
+ s.email = ["rubygems@tmxcredit.com", "AKuzko@sphereconsultinginc.com", "blake131313@gmail.com"]
10
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"
@@ -2,49 +2,66 @@ module ACH
2
2
  # Supports building a string representation of a particular instance of an
3
3
  # ACH batch. Supports building ACH lines. Included by ACH::File.
4
4
  module Batch::Builder
5
- # Returns +true+ if any of internal ACH entries has 'credit' transaction code
5
+ # Returns +true+ if any of internal ACH entries has 'credit' transaction code.
6
+ #
7
+ # @return [Boolean]
6
8
  def has_credit?
7
9
  entries.any?(&:credit?)
8
10
  end
9
11
 
10
- # Returns +true+ if any internal ACH entry has 'debit' transaction code
12
+ # Returns +true+ if any internal ACH entry has 'debit' transaction code.
13
+ #
14
+ # @return [Boolean]
11
15
  def has_debit?
12
16
  entries.any?(&:debit?)
13
17
  end
14
18
 
15
- # Returns total amount of entry and addenda records within batch
19
+ # Returns total amount of entry and addenda records within batch.
20
+ #
21
+ # @return [Fixnum]
16
22
  def entry_addenda_count
17
23
  entries.size + addendas.values.flatten.size
18
24
  end
19
25
 
20
26
  # Returns 'hashed' representation of all entries within batch. See NACHA
21
- # documentation for more details on entry hash
27
+ # documentation for more details on entry hash.
28
+ #
29
+ # @return [Fixnum]
22
30
  def entry_hash
23
31
  entries.map{ |entry| entry.routing_number.to_i / 10 }.compact.inject(&:+)
24
32
  end
25
33
 
26
- # Returns total amount of all 'debit' entries within a batch
34
+ # Returns total amount of all 'debit' entries within a batch.
35
+ #
36
+ # @return [Fixnum]
27
37
  def total_debit_amount
28
38
  amount_sum_for(:debit?)
29
39
  end
30
40
 
31
- # Returns total amount of all 'credit' entries within a batch
41
+ # Returns total amount of all 'credit' entries within a batch.
42
+ #
43
+ # @return [Fixnum]
32
44
  def total_credit_amount
33
45
  amount_sum_for(:credit?)
34
46
  end
35
47
 
36
- # Returns ACH record objects that represent the batch
48
+ # Returns ACH record objects that represent the batch.
49
+ #
50
+ # @return [Array<ACH::Record::Base>]
37
51
  def to_ach
38
52
  [header] + fetch_entries + [control]
39
53
  end
40
54
 
41
- # Helper method executed just before building a header record for the batch
55
+ # Helper method executed just before building a header record for the batch.
42
56
  def before_header
43
57
  attributes[:service_class_code] ||= (has_debit? && has_credit? ? 200 : has_debit? ? 225 : 220)
44
58
  end
45
59
  private :before_header
46
60
 
47
61
  # Helper method, returns total amount of all entries within a batch, filtered by +meth+
62
+ #
63
+ # @param [Symbol] meth
64
+ # @return [Fixnum]
48
65
  def amount_sum_for(meth)
49
66
  entries.select(&meth).map{ |entry| entry.amount.to_i }.compact.inject(&:+) || 0
50
67
  end
@@ -52,6 +69,8 @@ module ACH
52
69
 
53
70
  # Fetches all internal records (entries and addendas) in right order, i.e. addenda records
54
71
  # should be positioned right after corresponding entry records.
72
+ #
73
+ # @return [Array<ACH::Record::Base>]
55
74
  def fetch_entries
56
75
  entries.inject([]){ |all, entry| all << entry << addendas[entry] }.flatten.compact
57
76
  end
@@ -19,7 +19,11 @@ module ACH
19
19
 
20
20
  # Exception raised on attempt to assign a value to nonexistent field.
21
21
  class UnknownAttributeError < ArgumentError
22
- def initialize field, obj
22
+ # Initialize error with field and component.
23
+ #
24
+ # @param [String] field
25
+ # @param [ACH::Component] obj
26
+ def initialize(field, obj)
23
27
  super "Unrecognized attribute '#{field}' for #{obj}"
24
28
  end
25
29
  end
@@ -31,6 +35,9 @@ module ACH
31
35
 
32
36
  attr_reader :attributes
33
37
 
38
+ # When inherited, clone class-related properties.
39
+ #
40
+ # @param [Class] klass
34
41
  def self.inherited(klass)
35
42
  klass.default_attributes = default_attributes.dup
36
43
  klass.after_initialize_hooks = after_initialize_hooks.dup
@@ -46,6 +53,9 @@ module ACH
46
53
  # header record.
47
54
  #
48
55
  # Note that default values may be overwritten when building records.
56
+ #
57
+ # @param [Symbol] meth
58
+ # @param [*Object] args
49
59
  def self.method_missing(meth, *args)
50
60
  if Formatter.defined?(meth)
51
61
  default_attributes[meth] = args.first
@@ -54,16 +64,28 @@ module ACH
54
64
  end
55
65
  end
56
66
 
57
- def initialize(fields = {}, &block)
58
- @attributes = {}.merge(self.class.default_attributes)
67
+ # Initialize component with field values. If block is given, it is evaluated in context
68
+ # of component, allowing setting fields via method calls and declarations of nested
69
+ # components.
70
+ #
71
+ # @param [Hash] fields
72
+ # @raise [UnknownAttributeError]
73
+ def initialize(fields = {})
74
+ @attributes = self.class.default_attributes.dup
59
75
  fields.each do |name, value|
60
76
  raise UnknownAttributeError.new(name, self) unless Formatter.defined?(name)
61
77
  @attributes[name] = value
62
78
  end
63
79
  after_initialize
64
- instance_eval(&block) if block
80
+ instance_eval(&Proc.new) if block_given?
65
81
  end
66
82
 
83
+ # Missing messages are treated as accessor methods for a component if their
84
+ # message name is defined by {ACH::Formatter}.
85
+ #
86
+ # @param [Symbol] meth
87
+ # @param [*Object] args
88
+ # @return [String]
67
89
  def method_missing(meth, *args)
68
90
  if Formatter.defined?(meth)
69
91
  args.empty? ? @attributes[meth] : (@attributes[meth] = args.first)
@@ -72,7 +94,9 @@ module ACH
72
94
  end
73
95
  end
74
96
 
75
- def before_header # :nodoc:
97
+ # Hook that is called whenever component header record is created. To be
98
+ # overridden in subclasses.
99
+ def before_header
76
100
  end
77
101
  private :before_header
78
102
 
@@ -92,19 +116,26 @@ module ACH
92
116
  # == Example 3
93
117
  #
94
118
  # header # => just returns a header object
95
- def header(fields = {}, &block)
119
+ def header(fields = {})
96
120
  before_header
97
121
  merged_fields = fields_for(self.class::Header).merge(fields)
98
122
  @header ||= self.class::Header.new(merged_fields)
99
123
  @header.tap do |head|
100
- head.instance_eval(&block) if block
124
+ head.instance_eval(&Proc.new) if block_given?
101
125
  end
102
126
  end
103
127
 
104
- def build_header(str) # :nodoc:
128
+ # Build a component (+File+ or +Batch+) related +Header+ record from a given string.
129
+ #
130
+ # @param [String] str
131
+ # @return [ACH::Record::Base]
132
+ def build_header(str)
105
133
  @header = self.class::Header.from_s(str)
106
134
  end
107
135
 
136
+ # Build a component-related +Control+ record.
137
+ #
138
+ # @return [ACH::Record::Base]
108
139
  def control
109
140
  @control ||= begin
110
141
  klass = self.class::Control
@@ -113,10 +144,19 @@ module ACH
113
144
  end
114
145
  end
115
146
 
116
- def build_control(str) # :nodoc:
147
+ # Build a component-related +Control+ record from a given string.
148
+ #
149
+ # @param [String] str
150
+ # @return [ACH::Record::Base]
151
+ def build_control(str)
117
152
  @control = self.class::Control.from_s(str)
118
153
  end
119
-
154
+
155
+ # Return a set of fields, that is a subset of +attributes+ that can be used to
156
+ # initialize an instance of a +klass+. +Component+ uses +attributes+ itself.
157
+ #
158
+ # @param [Class] klass
159
+ # @return [Hash]
120
160
  def fields_for(klass)
121
161
  if klass < Component
122
162
  attributes
@@ -126,11 +166,13 @@ module ACH
126
166
  end
127
167
  end
128
168
 
129
- def after_initialize # :nodoc:
169
+ # Execute all +Proc+ objects contained in the +after_initialize_hooks+
170
+ # array in the context of the object.
171
+ def after_initialize
130
172
  self.class.after_initialize_hooks.each{ |hook| instance_exec(&hook) }
131
173
  end
132
174
 
133
- # Creates has many association.
175
+ # Creates a has_many association.
134
176
  #
135
177
  # == Example
136
178
  #
@@ -147,6 +189,9 @@ module ACH
147
189
  # The example above extends File with #batches and #batch instance methods:
148
190
  # * #batch is used to add new instance of Batch.
149
191
  # * #batches is used to get an array of batches which belong to file.
192
+ #
193
+ # @param [Symbol] plural_name
194
+ # @param [Hash] options
150
195
  def self.has_many(plural_name, options = {})
151
196
  association = HasManyAssociation.new(plural_name, options)
152
197
 
@@ -25,6 +25,10 @@ module ACH
25
25
  # created after Entry records. Each new Addenda record will be attached
26
26
  # to the latest Entry record.
27
27
  class NoLinkError < ArgumentError
28
+ # Initialize the error with a descriptive message.
29
+ #
30
+ # @param [String] link
31
+ # @param [Class] klass
28
32
  def initialize(link, klass)
29
33
  super "No #{link} was found to attach a new #{klass}"
30
34
  end
@@ -33,6 +37,10 @@ module ACH
33
37
  # Exception thrown if an association object, assigned for particular
34
38
  # owner object, is used to assign to another owner object
35
39
  class DoubleAssignmentError < StandardError
40
+ # Initialize the error with a descriptive message.
41
+ #
42
+ # @param [String] name
43
+ # @param [ACH::Component] owner
36
44
  def initialize(name, owner)
37
45
  super "Association #{name} has alredy been assigned to #{owner}"
38
46
  end
@@ -41,14 +49,23 @@ module ACH
41
49
  attr_reader :name, :linked_to, :proc_defaults
42
50
  private :linked_to, :proc_defaults
43
51
 
52
+ # Initialize the association with a plural name and options.
53
+ #
54
+ # @param [String] plural_name
55
+ # @param [Hash] options
56
+ # @option options [Symbol] :linked_to plural name of records to link associated ones
57
+ # @option options [Proc] :proc_defaults
44
58
  def initialize(plural_name, options = {})
45
59
  @name = plural_name.to_s
46
60
  @linked_to, @proc_defaults = options.values_at(:linked_to, :proc_defaults)
47
61
  end
48
62
 
49
- # Clones +self+ and assigns +owner+ to clone. Also, for newly created
63
+ # Clone +self+ and assign +owner+ to clone. Also, for newly created
50
64
  # clone association that has owner, aliases main methods so that +owner+
51
65
  # may delegate to them.
66
+ #
67
+ # @param [ACH::Component] owner
68
+ # @raise [DoubleAssignmentError]
52
69
  def for(owner)
53
70
  raise DoubleAssignmentError.new(@name, @owner) if @owner
54
71
 
@@ -63,51 +80,63 @@ module ACH
63
80
  end
64
81
  end
65
82
 
66
- # Returns an array of methods to be delegated by +owner+ of the association.
83
+ # Return an array of methods to be delegated by +owner+ of the association.
67
84
  # For example, for association named :items, it will include:
68
85
  # * +build_item+ - for instantiating Item from the string (used by parsing functionality)
69
86
  # * +item+ - for instantiating Item during common ACH File creation
70
- # * +items+ - that returns set of Item objects
87
+ # * +items+ - that returns set of Item objects.
88
+ #
89
+ # @return [Array<String>]
71
90
  def delegation_methods
72
91
  ["build_#{singular_name}", singular_name, name]
73
92
  end
74
93
 
75
- # Uses <tt>klass#from_s</tt> to instantiate object from a string. Thus, +klass+ should be
94
+ # Use <tt>klass#from_s</tt> to instantiate object from a string. Thus, +klass+ should be
76
95
  # descendant of ACH::Record::Base. Then pushes object to appropriate container.
96
+ #
97
+ # @param [String] str
98
+ # @return [Array<ACH::Record::Base>]
77
99
  def build(str)
78
100
  obj = klass.from_s(str)
79
101
  container_for_associated << obj
80
102
  end
81
103
 
82
- # Creates associated object using common to ACH controls pattern, and pushes it to
83
- # appropriate container. For example, for :items association, this method is
104
+ # Create an associated object using common to ACH controls pattern, and push it to
105
+ # an appropriate container. For example, for :items association, this method is
84
106
  # aliased to +item+, so you will have:
107
+ #
85
108
  # item(:code => 'WEB') do
86
109
  # other_code 'BEW'
87
110
  # # ...
88
111
  # end
89
- def create(*args, &block)
112
+ #
113
+ # @param [*Object] args
114
+ # @return [Object] instance of a class under ACH namespace
115
+ def create(*args)
90
116
  fields = args.first || {}
91
117
 
92
118
  defaults = proc_defaults ? @owner.instance_exec(&proc_defaults) : {}
93
119
 
94
120
  klass.new(@owner.fields_for(klass).merge(defaults).merge(fields)).tap do |component|
95
- component.instance_eval(&block) if block
121
+ component.instance_eval(&Proc.new) if block_given?
96
122
  container_for_associated << component
97
123
  end
98
124
  end
99
125
 
100
- # Returns main container for association. For plain (without :linked_to option), it is
126
+ # Return the main container for association. For plain (without :linked_to option), it is
101
127
  # array. For linked associations, it is a hash, which keys are records from linking
102
- # associations, and values are arrays for association's objects
128
+ # associations, and values are arrays for association's objects.
129
+ #
130
+ # @return [Hash, Array]
103
131
  def container
104
132
  @container ||= linked? ? {} : []
105
133
  end
106
134
 
107
- # Returns array for associated object to be pushed in. For plain associations, it is
108
- # equivalent to +container+. For linked associations, uses +@owner+ and linking
109
- # association's name to get the latest record from linking associations. If it does
110
- # not exist, +NoLinkError+ will be raised.
135
+ # Return an array onto which the associated object may be be pushed. For
136
+ # plain associations, it is equivalent to +container+. For linked
137
+ # associations, uses +@owner+ and linking association's name to get the
138
+ # latest record from linking associations. If it does not exist,
139
+ # +NoLinkError+ will be raised.
111
140
  #
112
141
  # Example:
113
142
  # class Batch < ACH::Component
@@ -124,6 +153,9 @@ module ACH
124
153
  # batch.entries # => [<Entry, amount=100>, <Entry, amount=200>]
125
154
  # batch.addendas # => {<Entry, amount=100> => [<Addenda, text='Foo'>],
126
155
  # # <Entry, amount=200> => [<Addenda, text='Bar'>, <Addenda, text='Baz'>]}
156
+ #
157
+ # @return [Array]
158
+ # @raise [NoLinkError]
127
159
  def container_for_associated
128
160
  return container unless linked?
129
161
 
@@ -132,21 +164,27 @@ module ACH
132
164
  container[last_link] ||= []
133
165
  end
134
166
 
135
- # Returns +true+ if association is linked to another association (thus, it's records must
136
- # be preceded by other association's records). Returns +false+ otherwise
167
+ # Return +true+ if the association is linked to another association (thus, its records must
168
+ # be preceded by other association's records). Returns +false+ otherwise.
169
+ #
170
+ # @return [Boolean]
137
171
  def linked?
138
172
  !!linked_to
139
173
  end
140
174
  private :linked?
141
175
 
142
- # Returns +klass+ that corresponds to association name. Should be defined either in
143
- # ACH module, or in ACH::Record module
176
+ # Return +klass+ that corresponds to the association name. Should be defined either in
177
+ # ACH module, or in ACH::Record module.
178
+ #
179
+ # @return [Class]
144
180
  def klass
145
181
  @klass ||= ACH.to_const(@name.classify.to_sym)
146
182
  end
147
183
  private :klass
148
184
 
149
- # Returns singular name of the association
185
+ # Return the singular name of the association.
186
+ #
187
+ # @return [String]
150
188
  def singular_name
151
189
  @singular_name ||= name.singularize
152
190
  end
@@ -1,4 +1,5 @@
1
1
  module ACH
2
+ # This module is a simple namespace for constants used in ACH routines.
2
3
  module Constants
3
4
  # The length of each record in characters.
4
5
  RECORD_SIZE = 94
@@ -11,11 +12,17 @@ module ACH
11
12
  # This character must be used to delimit each row.
12
13
  ROWS_DELIMITER = "\n"
13
14
 
15
+ # ACH defined value of File Header record.
14
16
  FILE_HEADER_RECORD_TYPE = 1
17
+ # ACH defined value of File Control record.
15
18
  FILE_CONTROL_RECORD_TYPE = 9
19
+ # ACH defined value of Batch Header record.
16
20
  BATCH_HEADER_RECORD_TYPE = 5
21
+ # ACH defined value of Batch Entry record.
17
22
  BATCH_ENTRY_RECORD_TYPE = 6
23
+ # ACH defined value of Batch Addenda record.
18
24
  BATCH_ADDENDA_RECORD_TYPE = 7
25
+ # ACH defined value of Batch Control record.
19
26
  BATCH_CONTROL_RECORD_TYPE = 8
20
27
  end
21
- end
28
+ end