lib/her/model/associations/association.rb
module Her
module Model
module Associations
class Association
# @private
attr_accessor :params
# @private
def initialize(parent, opts = {})
@parent = parent
@opts = opts
@params = {}
@klass = @parent.class.her_nearby_class(@opts[:class_name])
@name = @opts[:name]
end
# @private
def self.proxy(parent, opts = {})
AssociationProxy.new new(parent, opts)
end
# @private
def self.parse_single(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_record(klass, data: data[data_key]) }
end
# @private
def assign_single_nested_attributes(attributes)
if @parent.attributes[@name].blank?
@parent.attributes[@name] = @klass.new(@klass.parse(attributes))
else
@parent.attributes[@name].assign_attributes(attributes)
end
end
# @private
def fetch(opts = {})
attribute_value = @parent.attributes[@name]
return @opts[:default].try(:dup) if @parent.attributes.include?(@name) && (attribute_value.nil? || !attribute_value.nil? && attribute_value.empty?) && @params.empty?
return @cached_result unless @params.any? || @cached_result.nil?
return @parent.attributes[@name] unless @params.any? || @parent.attributes[@name].blank?
return @opts[:default].try(:dup) if @parent.new?
path = build_association_path -> { "#{@parent.request_path(@params)}#{@opts[:path]}" }
@klass.get(path, @params).tap do |result|
@cached_result = result unless @params.any?
end
end
# @private
def build_association_path(code)
instance_exec(&code)
rescue Her::Errors::PathError
nil
end
# @private
def reset
@params = {}
@cached_result = nil
@parent.attributes.delete(@name)
end
# Add query parameters to the HTTP request performed to fetch the data
#
# @example
# class User
# include Her::Model
# has_many :comments
# end
#
# user = User.find(1)
# user.comments.where(:approved => 1) # Fetched via GET "/users/1/comments?approved=1
def where(params = {})
return self if params.blank? && @parent.attributes[@name].blank?
AssociationProxy.new clone.tap { |a| a.params = a.params.merge(params) }
end
alias all where
# Fetches the data specified by id
#
# @example
# class User
# include Her::Model
# has_many :comments
# end
#
# user = User.find(1)
# user.comments.find(3) # Fetched via GET "/users/1/comments/3
def find(id)
return nil if id.blank?
path = build_association_path -> { "#{@parent.request_path(@params)}#{@opts[:path]}/#{id}" }
@klass.get_resource(path, @params)
end
# Refetches the association and puts the proxy back in its initial state,
# which is unloaded. Cached associations are cleared.
#
# @example
# class User
# include Her::Model
# has_many :comments
# end
#
# class Comment
# include Her::Model
# end
#
# user = User.find(1)
# user.comments = [#<Comment(comments/2) id=2 body="Hello!">]
# user.comments.first.id = "Oops"
# user.comments.reload # => [#<Comment(comments/2) id=2 body="Hello!">]
# # Fetched again via GET "/users/1/comments"
def reload
reset
fetch
end
end
end
end
end