class Graphiti::Sideload::PolymorphicBelongsTo < Graphiti::Sideload::BelongsTo class Group attr_reader :name, :calls def initialize(name) @name = name @calls = [] end def method_missing(name, *args, &blk) @calls << [name, args, blk] end end class Grouper attr_reader :field_name def initialize(field_name) @field_name = field_name @groups = [] end def on(name, &blk) group = Group.new(name) @groups << group group end def apply(sideload, resource_class) @groups.each do |group| if group.calls.empty? group.belongs_to(group.name.to_s.underscore.to_sym) end group.calls.each do |call| args = call[1] opts = args.extract_options! opts.merge! as: sideload.name, parent: sideload, group_name: group.name, polymorphic_child: true if !sideload.resource.class.abstract_class? opts[:foreign_key] ||= sideload.foreign_key opts[:primary_key] ||= sideload.primary_key end args << opts resource_class.send(call[0], *args, &call[2]) end end end end class_attribute :grouper attr_accessor :children self.grouper = Grouper.new(:default) def type :polymorphic_belongs_to end def infer_foreign_key :"#{name}_id" end def self.group_by(name, &blk) self.grouper = Grouper.new(name) self.grouper.instance_eval(&blk) end def initialize(name, opts) super self.children = {} grouper.apply(self, parent_resource_class) end def child_for_type(type) children.values.find do |sideload| sideload.resource.type == type end end def resolve(parents, query) parents.group_by(&grouper.field_name).each_pair do |group_name, group| next if group_name.nil? match = ->(c) { c.group_name == group_name.to_sym } if sideload = children.values.find(&match) query = remove_invalid_sideloads(sideload.resource, query) sideload.resolve(group, query) else err = ::Graphiti::Errors::PolymorphicChildNotFound raise err.new(self, group_name) end end end private # We may be requesting a relationship that some subclasses support, # but not others. Remove anything we don't support. def remove_invalid_sideloads(resource, query) query = query.dup query.sideloads.each_pair do |key, value| unless resource.class.sideload(key) query.sideloads.delete(key) end end query end end