class ACH::Component::HasManyAssociation

Objects of this class host essential functionality required to create associated object from within owner objects.

Newly instantiated HasManyAssociation object has no owner, and should be used to assign it’s copies to owners via for method. This technique has following application:

class Batch < ACH::Component
  association = HasManyAssociation.new(:entries)

  association.delegation_methods.each do |method_name|
    delegate method_name, :to => '@batches_association'
  end

  after_initialize_hooks << lambda{ instance_variable_set('@batches_association', association.for(self)) }
  # All these lines of code are macrosed by <tt>ACH::Component.has_many</tt> method
end
# Now, whenever new batch is created, it will have it's own @batches_association,
# and essential methods +batches+, +batch+, +build_batch+ delegated to it
# (accordingly, to +container+, +create+, and +build+ methods)

Attributes

linked_to[R]
name[R]
proc_defaults[R]

Public Class Methods

new(plural_name, options = {}) click to toggle source

Initialize the association with a plural name and options.

@param [String] plural_name @param [Hash] options @option options [Symbol] :linked_to plural name of records to link associated ones @option options [Proc] :proc_defaults

# File lib/ach/component/has_many_association.rb, line 58
def initialize(plural_name, options = {})
  @name = plural_name.to_s
  @linked_to, @proc_defaults = options.values_at(:linked_to, :proc_defaults)
end

Public Instance Methods

build(str) click to toggle source

Use klass#from_s to instantiate object from a string. Thus, klass should be descendant of ACH::Record::Base. Then pushes object to appropriate container.

@param [String] str @return [Array<ACH::Record::Base>]

# File lib/ach/component/has_many_association.rb, line 99
def build(str)
  obj = klass.from_s(str)
  container_for_associated << obj
end
container() click to toggle source

Return the main container for association. For plain (without :linked_to option), it is array. For linked associations, it is a hash, which keys are records from linking associations, and values are arrays for association’s objects.

@return [Hash, Array]

# File lib/ach/component/has_many_association.rb, line 131
def container
  @container ||= linked? ? {} : []
end
container_for_associated() click to toggle source

Return an array onto which the associated object may be be pushed. For plain associations, it is equivalent to container. For linked associations, uses +@owner+ and linking association’s name to get the latest record from linking associations. If it does not exist, NoLinkError will be raised.

Example:

class Batch < ACH::Component
  has_many :entries
  has_many :addendas, :linked_to => :entries
end
batch = Batch.new
batch.entry(:amount => 100)
batch.addenda(:text => 'Foo')
batch.entry(:amount => 200)
batch.addenda(:text => 'Bar')
batch.addenda(:text => 'Baz')

batch.entries  # => [<Entry, amount=100>, <Entry, amount=200>]
batch.addendas # => {<Entry, amount=100> => [<Addenda, text='Foo'>],
               #     <Entry, amount=200> => [<Addenda, text='Bar'>, <Addenda, text='Baz'>]}

@return [Array] @raise [NoLinkError]

# File lib/ach/component/has_many_association.rb, line 159
def container_for_associated
  return container unless linked?

  last_link = @owner.send(linked_to).last
  raise NoLinkError.new(linked_to.to_s.singularize, klass.name) unless last_link
  container[last_link] ||= []
end
create(*args) click to toggle source

Create an associated object using common to ACH controls pattern, and push it to an appropriate container. For example, for :items association, this method is aliased to item, so you will have:

item(:code => 'WEB') do
  other_code 'BEW'
  # ...
end

@param [*Object] args @return [Object] instance of a class under ACH namespace

# File lib/ach/component/has_many_association.rb, line 115
def create(*args)
  fields = args.first || {}

  defaults = proc_defaults ? @owner.instance_exec(&proc_defaults) : {}

  klass.new(@owner.fields_for(klass).merge(defaults).merge(fields)).tap do |component|
    component.instance_eval(&Proc.new) if block_given?
    container_for_associated << component
  end
end
delegation_methods() click to toggle source

Return an array of methods to be delegated by owner of the association. For example, for association named :items, it will include:

  • build_item - for instantiating Item from the string (used by parsing functionality)

  • item - for instantiating Item during common ACH File creation

  • items - that returns set of Item objects.

@return [Array<String>]

# File lib/ach/component/has_many_association.rb, line 90
def delegation_methods
  ["build_#{singular_name}", singular_name, name]
end
for(owner) click to toggle source

Clone self and assign owner to clone. Also, for newly created clone association that has owner, aliases main methods so that owner may delegate to them.

@param [ACH::Component] owner @raise [DoubleAssignmentError]

# File lib/ach/component/has_many_association.rb, line 69
def for(owner)
  raise DoubleAssignmentError.new(@name, @owner) if @owner

  clone.tap do |association|
    plural, singular = name, singular_name
    association.instance_variable_set('@owner', owner)
    association.singleton_class.class_eval do
      alias_method "build_#{singular}", :build
      alias_method singular, :create
      alias_method plural, :container
    end
  end
end

Private Instance Methods

klass() click to toggle source

Return klass that corresponds to the association name. Should be defined either in ACH module, or in ACH::Record module.

@return [Class]

# File lib/ach/component/has_many_association.rb, line 180
def klass
  @klass ||= ACH.to_const(@name.classify.to_sym)
end
linked?() click to toggle source

Return true if the association is linked to another association (thus, its records must be preceded by other association’s records). Returns false otherwise.

@return [Boolean]

# File lib/ach/component/has_many_association.rb, line 171
def linked?
  !!linked_to
end
singular_name() click to toggle source

Return the singular name of the association.

@return [String]

# File lib/ach/component/has_many_association.rb, line 188
def singular_name
  @singular_name ||= name.singularize
end