lib/her/model/associations/has_many_association.rb
module Her
module Model
module Associations
class HasManyAssociation < Association
# @private
def self.attach(klass, name, opts)
opts = {
:class_name => name.to_s.classify,
:name => name,
:data_key => name,
:default => Her::Collection.new,
:path => "/#{name}",
:inverse_of => nil
}.merge(opts)
klass.associations[:has_many] << opts
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{name}
cached_name = :"@_her_association_#{name}"
cached_data = (instance_variable_defined?(cached_name) && instance_variable_get(cached_name))
cached_data || instance_variable_set(cached_name, Her::Model::Associations::HasManyAssociation.proxy(self, #{opts.inspect}))
end
RUBY
end
# @private
def self.parse(association, klass, data)
data_key = association[:data_key]
return {} unless data[data_key]
klass = klass.her_nearby_class(association[:class_name])
{ association[:name] => klass.instantiate_collection(klass, :data => data[data_key]) }
end
# Initialize a new object with a foreign key to the parent
#
# @example
# class User
# include Her::Model
# has_many :comments
# end
#
# class Comment
# include Her::Model
# end
#
# user = User.find(1)
# new_comment = user.comments.build(:body => "Hello!")
# new_comment # => #<Comment user_id=1 body="Hello!">
# TODO: This only merges the id of the parents, handle the case
# where this is more deeply nested
def build(attributes = {})
@klass.build(attributes.merge(:"#{@parent.singularized_resource_name}_id" => @parent.id))
end
# Create a new object, save it and add it to the associated collection
#
# @example
# class User
# include Her::Model
# has_many :comments
# end
#
# class Comment
# include Her::Model
# end
#
# user = User.find(1)
# user.comments.create(:body => "Hello!")
# user.comments # => [#<Comment id=2 user_id=1 body="Hello!">]
def create(attributes = {})
resource = build(attributes)
if resource.save
@parent.attributes[@name] ||= Her::Collection.new
@parent.attributes[@name] << resource
end
resource
end
# @private
def fetch
super.tap do |o|
inverse_of = @opts[:inverse_of] || @parent.singularized_resource_name
o.each { |entry| entry.attributes[inverse_of] = @parent }
end
end
# @private
def assign_nested_attributes(attributes)
data = attributes.is_a?(Hash) ? attributes.values : attributes
@parent.attributes[@name] = @klass.instantiate_collection(@klass, :data => data)
end
end
end
end
end