lib/zendesk_api/association.rb in zendesk_api-0.1.5 vs lib/zendesk_api/association.rb in zendesk_api-0.1.6
- old
+ new
@@ -1,5 +1,7 @@
+require 'zendesk_api/helpers'
+
module ZendeskAPI
# Represents an association between two resources
class Association
# @return [Hash] Options passed into the association
attr_reader :options
@@ -46,12 +48,60 @@
end
namespace.join("/")
end
+ def side_load(resources, side_loads)
+ key = "#{options.name}_id"
+ plural_key = "#{Inflection.singular options.name.to_s}_ids"
+
+ resources.each do |resource|
+ if resource.key?(plural_key) # Grab associations from child_ids field on resource
+ ids = resource.send(plural_key)
+
+ resource.send("#{options.name}=", _side_load(resource, side_loads.select {|side_load|
+ ids.include?(side_load[options.include_key])
+ }))
+ elsif resource.key?(key) || options.singular
+ # Either grab association from child_id field on resource or parent_id on child resource
+ if resource.key?(key)
+ id = resource.send(key)
+ key = options.include_key
+ else
+ id = resource.id
+ key = "#{resource.class.singular_resource_name}_id"
+ end
+
+ next unless id
+
+ side_load = side_loads.detect do |side_load|
+ id == side_load[key]
+ end
+
+ resource.send("#{options.name}=", side_load) if side_load
+ else # Grab associations from parent_id field from multiple child resources
+ key = "#{resource.class.singular_resource_name}_id"
+
+ resource.send("#{options.name}=", _side_load(resource, side_loads.select {|side_load|
+ side_load[key] == resource.id
+ }))
+ end
+ end
+ end
+
private
+ def _side_load(resource, side_loads)
+ side_loads.map! do |side_load|
+ resource.send(:wrap_resource, side_load, options[:class], options)
+ end
+
+ ZendeskAPI::Collection.new(resource.client, options[:class]).tap do |collection|
+ collection.replace(side_loads)
+ end
+ end
+
def build_parent_namespace(parent_class, instance, options, original_options)
return unless association_on_parent = parent_class.associations.detect {|a| a[:class] == @options[:class] }
[
extract_parent_id(parent_class, instance, options, original_options),
@options.path || association_on_parent[:name].to_s
@@ -108,25 +158,41 @@
module ClassMethods
include Rescue
def associations
- @assocations ||= []
+ @associations ||= []
end
+ def associated_with(name)
+ associations.inject([]) do |associated_with, association|
+ if association[:include] == name.to_s
+ associated_with.push(Association.new(association))
+ end
+
+ associated_with
+ end
+ end
+
# Represents a parent-to-child association between resources. Options to pass in are: class, path.
# @param [Symbol] resource_name The underlying resource name
# @param [Hash] opts The options to pass to the method definition.
def has(resource_name, class_level_options = {})
klass = get_class(class_level_options.delete(:class)) || get_class(resource_name)
+
class_level_association = {
:class => klass,
:name => resource_name,
:inline => class_level_options.delete(:inline),
- :path => class_level_options.delete(:path)
+ :path => class_level_options.delete(:path),
+ :include => (class_level_options.delete(:include) || klass.resource_name).to_s,
+ :include_key => (class_level_options.delete(:include_key) || :id).to_s,
+ :singular => true
}
+
associations << class_level_association
+
id_column = "#{resource_name}_id"
define_method "#{resource_name}_used?" do
!!instance_variable_get("@#{resource_name}")
end
@@ -164,17 +230,22 @@
# Represents a parent-to-children association between resources. Options to pass in are: class, path.
# @param [Symbol] resource The underlying resource name
# @param [Hash] opts The options to pass to the method definition.
def has_many(resource_name, class_level_opts = {})
- klass = get_class(class_level_opts.delete(:class)) || get_class(resource_name.to_s.singular)
+ klass = get_class(class_level_opts.delete(:class)) || get_class(Inflection.singular(resource_name.to_s))
+
class_level_association = {
:class => klass,
:name => resource_name,
:inline => class_level_opts.delete(:inline),
- :path => class_level_opts.delete(:path)
+ :path => class_level_opts.delete(:path),
+ :include => (class_level_opts.delete(:include) || klass.resource_name).to_s,
+ :include_key => (class_level_opts.delete(:include_key) || :id).to_s,
+ :singular => false
}
+
associations << class_level_association
id_column = "#{resource_name}_ids"
define_method "#{resource_name}_used?" do
@@ -188,11 +259,11 @@
cached = instance_variable_get("@#{resource_name}")
return cached if cached && !instance_opts[:reload]
# find and cache association
instance_association = Association.new(class_level_association.merge(:parent => self))
- singular_resource_name = resource_name.to_s.singular
+ singular_resource_name = Inflection.singular(resource_name.to_s)
resources = if (ids = method_missing("#{singular_resource_name}_ids")) && ids.any?
ids.map do |id|
klass.find(@client, :id => id, :association => instance_association)
end.compact
@@ -225,11 +296,11 @@
# Allows using has and has_many without having class defined yet
# Guesses at Resource, if it's anything else and the class is later
# reopened under a different superclass, an error will be thrown
def get_class(resource)
return false if resource.nil?
- res = resource.to_s.modulize
+ res = ZendeskAPI::Helpers.modulize_string(resource.to_s)
begin
const_get(res)
rescue NameError, ArgumentError # ruby raises NameError, rails raises ArgumentError
ZendeskAPI.get_class(resource)
@@ -247,10 +318,10 @@
# Allows using has and has_many without having class defined yet
# Guesses at Resource, if it's anything else and the class is later
# reopened under a different superclass, an error will be thrown
def get_class(resource)
return false if resource.nil?
- res = resource.to_s.modulize.split("::")
+ res = ZendeskAPI::Helpers.modulize_string(resource.to_s).split("::")
begin
res[1..-1].inject(ZendeskAPI.const_get(res[0])) do |iter, k|
begin
iter.const_get(k)