Class: ACH::Component

Inherits:
Object
  • Object
show all
Extended by:
ActiveSupport::Autoload
Includes:
Constants, Validations
Defined in:
lib/ach/component.rb

Overview

Base class for ACH::File and ACH::Batch. Every component has its own number of entities, header and control records. So it provides ACH::Component#header, ACH::Component#control, ACH::Component.has_many methods to manage them.

Example

class File < Component
  has_many :batches
  # implementation
end

Direct Known Subclasses

Batch, File

Defined Under Namespace

Classes: HasManyAssociation, UnknownAttributeError

Constant Summary

Constants included from Constants

ACH::Constants::BATCH_ADDENDA_RECORD_TYPE, ACH::Constants::BATCH_CONTROL_RECORD_TYPE, ACH::Constants::BATCH_ENTRY_RECORD_TYPE, ACH::Constants::BATCH_HEADER_RECORD_TYPE, ACH::Constants::BLOCKING_FACTOR, ACH::Constants::FILE_CONTROL_RECORD_TYPE, ACH::Constants::FILE_HEADER_RECORD_TYPE, ACH::Constants::FORMAT_CODE, ACH::Constants::RECORD_SIZE, ACH::Constants::ROWS_DELIMITER

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Validations

#errors, #valid?

Constructor Details

#initialize(fields = {}, &block) ⇒ Component

Returns a new instance of Component.



57
58
59
60
61
62
63
64
65
# File 'lib/ach/component.rb', line 57

def initialize(fields = {}, &block)
  @attributes = {}.merge(self.class.default_attributes)
  fields.each do |name, value|
    raise UnknownAttributeError.new(name, self) unless Formatter.defined?(name)
    @attributes[name] = value
  end
  after_initialize
  instance_eval(&block) if block
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args) ⇒ Object



67
68
69
70
71
72
73
# File 'lib/ach/component.rb', line 67

def method_missing(meth, *args)
  if Formatter.defined?(meth)
    args.empty? ? @attributes[meth] : (@attributes[meth] = args.first)
  else
    super
  end
end

Instance Attribute Details

#attributesObject (readonly)

Returns the value of attribute attributes.



32
33
34
# File 'lib/ach/component.rb', line 32

def attributes
  @attributes
end

Class Method Details

.has_many(plural_name, options = {}) ⇒ Object

Creates has many association.

Example

class File < Component
  has_many :batches
end

file = File.new do
  batch :foo => 1, :bar => 2
end

file.batches  # => [#<Batch ...>]

The example above extends File with #batches and #batch instance methods:

  • #batch is used to add new instance of Batch.

  • #batches is used to get an array of batches which belong to file.



150
151
152
153
154
155
156
157
158
159
# File 'lib/ach/component.rb', line 150

def self.has_many(plural_name, options = {})
  association = HasManyAssociation.new(plural_name, options)

  association_variable_name = "@#{plural_name}_association"
  association.delegation_methods.each do |method_name|
    delegate method_name, :to => association_variable_name
  end

  after_initialize_hooks << lambda{ instance_variable_set(association_variable_name, association.for(self)) }
end

.inherited(klass) ⇒ Object



34
35
36
37
# File 'lib/ach/component.rb', line 34

def self.inherited(klass)
  klass.default_attributes = default_attributes.dup
  klass.after_initialize_hooks = after_initialize_hooks.dup
end

.method_missing(meth, *args) ⇒ Object

Uses method_missing pattern to specify default attributes for a Component. If method name is one of the defined rules, saves it to default_attributes hash.

These attributes are passed to inner components in a cascade way, i.e. when ACH File was defined with default value for ‘company_name’, this value will be passed to every Batch component within file, and from every Batch to corresponding batch header record.

Note that default values may be overwritten when building records.



49
50
51
52
53
54
55
# File 'lib/ach/component.rb', line 49

def self.method_missing(meth, *args)
  if Formatter.defined?(meth)
    default_attributes[meth] = args.first
  else
    super
  end
end

Instance Method Details

#after_initializeObject

:nodoc:



129
130
131
# File 'lib/ach/component.rb', line 129

def after_initialize # :nodoc:
  self.class.after_initialize_hooks.each{ |hook| instance_exec(&hook) }
end

#build_control(str) ⇒ Object

:nodoc:



116
117
118
# File 'lib/ach/component.rb', line 116

def build_control(str) # :nodoc:
  @control = self.class::Control.from_s(str)
end

#build_header(str) ⇒ Object

:nodoc:



104
105
106
# File 'lib/ach/component.rb', line 104

def build_header(str) # :nodoc:
  @header = self.class::Header.from_s(str)
end

#controlObject



108
109
110
111
112
113
114
# File 'lib/ach/component.rb', line 108

def control
  @control ||= begin
    klass  = self.class::Control
    fields = klass.fields.select{ |f| respond_to?(f) || attributes[f] }
    klass.new Hash[*fields.zip(fields.map{ |f| send(f) }).flatten]
  end
end

#fields_for(klass) ⇒ Object



120
121
122
123
124
125
126
127
# File 'lib/ach/component.rb', line 120

def fields_for(klass)
  if klass < Component
    attributes
  else
    attrs = attributes.find_all{ |k, v| klass.fields.include?(k) && attributes[k] }
    Hash[*attrs.flatten]
  end
end

#header(fields = {}, &block) ⇒ Object

Sets header fields if fields or block passed. Returns header record.

Example 1

header :foo => "value 1", :bar => "value 2"

Example 2

header do
  foo "value 1"
  bar "value 2"
end

Example 3

header # => just returns a header object


95
96
97
98
99
100
101
102
# File 'lib/ach/component.rb', line 95

def header(fields = {}, &block)
  before_header
  merged_fields = fields_for(self.class::Header).merge(fields)
  @header ||= self.class::Header.new(merged_fields)
  @header.tap do |head|
    head.instance_eval(&block) if block
  end
end