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) ⇒ Array<ACH::Record::Base>
Use
klass#from_s
to instantiate object from a string. -
#container ⇒ Hash, Array
Return the main container for association.
-
#container_for_associated ⇒ Array
Return an array onto which the associated object may be be pushed.
-
#create(*args) ⇒ Object
Create an associated object using common to ACH controls pattern, and push it to an appropriate container.
-
#delegation_methods ⇒ Array<String>
Return an array of methods to be delegated by
owner
of the association. -
#for(owner) ⇒ Object
Clone
self
and assignowner
to clone. -
#initialize(plural_name, options = {}) ⇒ HasManyAssociation
constructor
Initialize the association with a plural name and options.
Constructor Details
#initialize(plural_name, options = {}) ⇒ HasManyAssociation
Initialize the association with a plural name and options.
58 59 60 61 |
# File 'lib/ach/component/has_many_association.rb', line 58 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.
49 50 51 |
# File 'lib/ach/component/has_many_association.rb', line 49 def name @name end |
Instance Method Details
#build(str) ⇒ Array<ACH::Record::Base>
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.
99 100 101 102 |
# File 'lib/ach/component/has_many_association.rb', line 99 def build(str) obj = klass.from_s(str) container_for_associated << obj end |
#container ⇒ Hash, Array
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.
131 132 133 |
# File 'lib/ach/component/has_many_association.rb', line 131 def container @container ||= linked? ? {} : [] end |
#container_for_associated ⇒ Array
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'>]}
159 160 161 162 163 164 165 |
# 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) ⇒ Object
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
115 116 117 118 119 120 121 122 123 124 |
# 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 ⇒ Array<String>
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.
90 91 92 |
# File 'lib/ach/component/has_many_association.rb', line 90 def delegation_methods ["build_#{singular_name}", singular_name, name] end |
#for(owner) ⇒ Object
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.
69 70 71 72 73 74 75 76 77 78 79 80 81 |
# 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 |