Class: ACH::Component::HasManyAssociation
- Inherits:
-
Object
- Object
- ACH::Component::HasManyAssociation
- Defined in:
- lib/ach/component/has_many_association.rb
Overview
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)
Defined Under Namespace
Classes: DoubleAssignmentError, NoLinkError
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the value of attribute name.
Instance Method Summary collapse
-
#build(str) ⇒ Object
Uses
klass#from_s
to instantiate object from a string. -
#container ⇒ Object
Returns main container for association.
-
#container_for_associated ⇒ Object
Returns array for associated object to be pushed in.
-
#create(*args, &block) ⇒ Object
Creates associated object using common to ACH controls pattern, and pushes it to appropriate container.
-
#delegation_methods ⇒ Object
Returns an array of methods to be delegated by
owner
of the association. -
#for(owner) ⇒ Object
Clones
self
and assignsowner
to clone. -
#initialize(plural_name, options = {}) ⇒ HasManyAssociation
constructor
A new instance of HasManyAssociation.
Constructor Details
#initialize(plural_name, options = {}) ⇒ HasManyAssociation
Returns a new instance of HasManyAssociation.
44 45 46 47 |
# File 'lib/ach/component/has_many_association.rb', line 44 def initialize(plural_name, = {}) @name = plural_name.to_s @linked_to, @proc_defaults = .values_at(:linked_to, :proc_defaults) end |
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the value of attribute name.
41 42 43 |
# File 'lib/ach/component/has_many_association.rb', line 41 def name @name end |
Instance Method Details
#build(str) ⇒ Object
Uses klass#from_s
to instantiate object from a string. Thus, klass
should be descendant of ACH::Record::Base. Then pushes object to appropriate container.
77 78 79 80 |
# File 'lib/ach/component/has_many_association.rb', line 77 def build(str) obj = klass.from_s(str) container_for_associated << obj end |
#container ⇒ Object
Returns 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
103 104 105 |
# File 'lib/ach/component/has_many_association.rb', line 103 def container @container ||= linked? ? {} : [] end |
#container_for_associated ⇒ Object
Returns array for associated object to be pushed in. 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'>]}
127 128 129 130 131 132 133 |
# File 'lib/ach/component/has_many_association.rb', line 127 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, &block) ⇒ Object
Creates associated object using common to ACH controls pattern, and pushes it to 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
89 90 91 92 93 94 95 96 97 98 |
# File 'lib/ach/component/has_many_association.rb', line 89 def create(*args, &block) 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(&block) if block container_for_associated << component end end |
#delegation_methods ⇒ Object
Returns 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
71 72 73 |
# File 'lib/ach/component/has_many_association.rb', line 71 def delegation_methods ["build_#{singular_name}", singular_name, name] end |
#for(owner) ⇒ Object
Clones self
and assigns owner
to clone. Also, for newly created clone association that has owner, aliases main methods so that owner
may delegate to them.
52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/ach/component/has_many_association.rb', line 52 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 |