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)