# File lib/graphiti/sideload.rb, line 167 def primary_key @primary_key ||= :id end
class Graphiti::Sideload
Constants
- HOOK_ACTIONS
- TYPES
Attributes
group_name[R]
link[R]
name[R]
parent[R]
parent_resource_class[R]
polymorphic_as[R]
Public Class Methods
after_save(only: [], except: [], &blk)
click to toggle source
# File lib/graphiti/sideload.rb, line 310 def self.after_save(only: [], except: [], &blk) actions = HOOK_ACTIONS - except actions = only & actions actions = [:save] if only.empty? && except.empty? actions.each do |a| hooks[:"after_#{a}"] << blk end end
assign(&blk)
click to toggle source
# File lib/graphiti/sideload.rb, line 60 def self.assign(&blk) self.assign_proc = blk end
assign_each(&blk)
click to toggle source
# File lib/graphiti/sideload.rb, line 64 def self.assign_each(&blk) self.assign_each_proc = blk end
hooks()
click to toggle source
# File lib/graphiti/sideload.rb, line 319 def self.hooks @hooks ||= {}.tap do |h| HOOK_ACTIONS.each do |a| h[:"after_#{a}"] = [] h[:"before_#{a}"] = [] end end end
link(&blk)
click to toggle source
# File lib/graphiti/sideload.rb, line 76 def self.link(&blk) self.link_proc = blk end
new(name, opts)
click to toggle source
# File lib/graphiti/sideload.rb, line 20 def initialize(name, opts) @name = name validate_options!(opts) @parent_resource_class = opts[:parent_resource] @resource_class = opts[:resource] @primary_key = opts[:primary_key] @foreign_key = opts[:foreign_key] @type = opts[:type] @base_scope = opts[:base_scope] @readable = opts[:readable] @writable = opts[:writable] @as = opts[:as] @link = opts[:link] @single = opts[:single] @remote = opts[:remote] apply_belongs_to_many_filter if type == :many_to_many @description = opts[:description] # polymorphic has_many @polymorphic_as = opts[:polymorphic_as] # polymorphic_belongs_to-specific @group_name = opts[:group_name] @polymorphic_child = opts[:polymorphic_child] @parent = opts[:parent] @always_include_resource_ids = opts[:always_include_resource_ids] if polymorphic_child? parent.resource.polymorphic << resource_class end if remote? @resource_class = create_remote_resource end end
params(&blk)
click to toggle source
# File lib/graphiti/sideload.rb, line 68 def self.params(&blk) self.params_proc = blk end
pre_load(&blk)
click to toggle source
# File lib/graphiti/sideload.rb, line 72 def self.pre_load(&blk) self.pre_load_proc = blk end
scope(&blk)
click to toggle source
# File lib/graphiti/sideload.rb, line 56 def self.scope(&blk) self.scope_proc = blk end
Public Instance Methods
always_include_resource_ids?()
click to toggle source
# File lib/graphiti/sideload.rb, line 121 def always_include_resource_ids? !!@always_include_resource_ids end
assign(parents, children)
click to toggle source
# File lib/graphiti/sideload.rb, line 263 def assign(parents, children) track_associated = type == :has_one associated = [] if track_associated if performant_assign? map = child_map(children) end parents.each do |parent| relevant_children = if performant_assign? children_for(parent, map) || [] else fire_assign_each(parent, children) end if relevant_children.is_a?(Array) associated |= relevant_children if track_associated associate_all(parent, relevant_children) else associated << relevant_children if track_associated && relevant_children associate(parent, relevant_children) end end children.replace(associated) if track_associated end
assign_each(parent, children)
click to toggle source
# File lib/graphiti/sideload.rb, line 187 def assign_each(parent, children) raise "Override #assign_each in subclass" end
associate(parent, child)
click to toggle source
# File lib/graphiti/sideload.rb, line 341 def associate(parent, child) parent_resource.associate(parent, child, association_name, type) end
associate_all(parent, children)
click to toggle source
# File lib/graphiti/sideload.rb, line 337 def associate_all(parent, children) parent_resource.associate_all(parent, children, association_name, type) end
association_name()
click to toggle source
# File lib/graphiti/sideload.rb, line 175 def association_name @as || name end
base_scope()
click to toggle source
# File lib/graphiti/sideload.rb, line 204 def base_scope if @base_scope @base_scope.respond_to?(:call) ? @base_scope.call : @base_scope else resource.base_scope end end
build_resource_proxy(parents, query, graph_parent)
click to toggle source
# File lib/graphiti/sideload.rb, line 212 def build_resource_proxy(parents, query, graph_parent) params = nil opts = nil proxy = nil with_error_handling Errors::SideloadParamsError do params = load_params(parents, query) params_proc&.call(params, parents, context) return [] if blank_query?(params) opts = load_options(parents, query) opts[:sideload] = self opts[:parent] = graph_parent end with_error_handling(Errors::SideloadQueryBuildingError) do scope = base_scope scope[:foreign_key] = foreign_key if remote? proxy = resource.class._all(params, opts, scope) pre_load_proc&.call(proxy, parents) end proxy end
clear_resources()
click to toggle source
See github.com/graphiti-api/graphiti/issues/186
# File lib/graphiti/sideload.rb, line 258 def clear_resources @resource = nil @parent_resource = nil end
create_remote_resource()
click to toggle source
# File lib/graphiti/sideload.rb, line 80 def create_remote_resource remote_url = @remote klass = Class.new(Graphiti::Resource) { self.adapter = Graphiti::Adapters::GraphitiAPI self.model = OpenStruct self.remote = remote_url self.validate_endpoints = false } name = "#{parent_resource_class.name}.#{@name}.remote" klass.class_eval("def self.name;'#{name}';end", __FILE__, __LINE__) klass end
description()
click to toggle source
# File lib/graphiti/sideload.rb, line 199 def description return @description if @description.present? parent_resource_class.resolve_i18n_field_description(name, field_type: :relationships) end
disassociate(parent, child)
click to toggle source
# File lib/graphiti/sideload.rb, line 345 def disassociate(parent, child) parent_resource.disassociate(parent, child, association_name, type) end
errors()
click to toggle source
# File lib/graphiti/sideload.rb, line 93 def errors @errors ||= [] end
fire_hooks!(parent, objects, method)
click to toggle source
# File lib/graphiti/sideload.rb, line 328 def fire_hooks!(parent, objects, method) return unless self.class.hooks all = self.class.hooks[:"after_#{method}"] + self.class.hooks[:after_save] all.compact.each do |hook| resource.instance_exec(parent, objects, &hook) end end
foreign_key()
click to toggle source
# File lib/graphiti/sideload.rb, line 171 def foreign_key @foreign_key ||= infer_foreign_key end
ids_for_parents(parents)
click to toggle source
# File lib/graphiti/sideload.rb, line 349 def ids_for_parents(parents) parent_ids = parents.map(&primary_key) parent_ids.compact! parent_ids.uniq! parent_ids end
infer_foreign_key()
click to toggle source
Override in subclass
# File lib/graphiti/sideload.rb, line 242 def infer_foreign_key model = parent_resource_class.model namespace = namespace_for(model) model_name = model.name.gsub("#{namespace}::", "") :"#{model_name.underscore}_id" end
link?()
click to toggle source
# File lib/graphiti/sideload.rb, line 125 def link? return true if link_proc if @link.nil? !!@parent_resource_class.autolink else !!@link end end
link_extra_fields()
click to toggle source
# File lib/graphiti/sideload.rb, line 139 def link_extra_fields return unless context&.respond_to?(:params) extra_fields_name = [association_name, resource.type].find { |param| context.params.dig(:extra_fields, param) } if extra_fields_name extra_fields = context.params.dig(:extra_fields, extra_fields_name) {resource.type => extra_fields} end end
link_filter(parents)
click to toggle source
# File lib/graphiti/sideload.rb, line 135 def link_filter(parents) base_filter(parents) end
load(parents, query, graph_parent)
click to toggle source
# File lib/graphiti/sideload.rb, line 237 def load(parents, query, graph_parent) build_resource_proxy(parents, query, graph_parent).to_a end
load_params(parents, query)
click to toggle source
# File lib/graphiti/sideload.rb, line 195 def load_params(parents, query) raise "Override #load_params in subclass" end
parent_resource()
click to toggle source
# File lib/graphiti/sideload.rb, line 253 def parent_resource @parent_resource ||= parent_resource_class.new end
performant_assign?()
click to toggle source
# File lib/graphiti/sideload.rb, line 356 def performant_assign? !self.class.assign_each_proc end
polymorphic_child?()
click to toggle source
# File lib/graphiti/sideload.rb, line 163 def polymorphic_child? !!@polymorphic_child end
polymorphic_has_many?()
click to toggle source
# File lib/graphiti/sideload.rb, line 117 def polymorphic_has_many? !!@polymorphic_as end
polymorphic_has_one?()
click to toggle source
# File lib/graphiti/sideload.rb, line 113 def polymorphic_has_one? !!@polymorphic_as end
polymorphic_parent?()
click to toggle source
# File lib/graphiti/sideload.rb, line 159 def polymorphic_parent? resource.polymorphic? end
primary_key()
click to toggle source
readable?()
click to toggle source
# File lib/graphiti/sideload.rb, line 101 def readable? !!@readable end
remote?()
click to toggle source
# File lib/graphiti/sideload.rb, line 97 def remote? !!@remote end
resolve(parents, query, graph_parent)
click to toggle source
# File lib/graphiti/sideload.rb, line 288 def resolve(parents, query, graph_parent) if single? && parents.length > 1 raise Errors::SingularSideload.new(self, parents.length) end if self.class.scope_proc sideload_scope = fire_scope(parents) sideload_scope = Scope.new sideload_scope, resource, query, parent: graph_parent, sideload: self, sideload_parent_length: parents.length, default_paginate: false sideload_scope.resolve do |sideload_results| fire_assign(parents, sideload_results) end else load(parents, query, graph_parent) end end
resource()
click to toggle source
# File lib/graphiti/sideload.rb, line 249 def resource @resource ||= resource_class.new end
resource_class()
click to toggle source
# File lib/graphiti/sideload.rb, line 179 def resource_class @resource_class ||= infer_resource_class end
resource_class_loaded?()
click to toggle source
@api private
# File lib/graphiti/sideload.rb, line 361 def resource_class_loaded? resource_class true rescue Graphiti::Errors::ResourceNotFound false end
scope(parents)
click to toggle source
# File lib/graphiti/sideload.rb, line 183 def scope(parents) raise "No #scope defined for sideload with name '#{name}'. Make sure to define this in your adapter, or pass a block that defines the scope." end
single?()
click to toggle source
# File lib/graphiti/sideload.rb, line 109 def single? !!@single end
type()
click to toggle source
# File lib/graphiti/sideload.rb, line 191 def type @type || raise("Override #type in subclass. Should be one of #{TYPES.inspect}") end
writable?()
click to toggle source
# File lib/graphiti/sideload.rb, line 105 def writable? !!@writable end
Private Instance Methods
blank_query?(params)
click to toggle source
# File lib/graphiti/sideload.rb, line 370 def blank_query?(params) if (filter = params[:filter]) if filter.values == [""] return true end end false end
context()
click to toggle source
# File lib/graphiti/sideload.rb, line 465 def context Graphiti.context[:object] end
evaluate_flag(flag)
click to toggle source
TODO: call this at runtime to support procs
# File lib/graphiti/sideload.rb, line 452 def evaluate_flag(flag) return false if flag.blank? case flag.class.name when "Symbol", "String" resource.send(flag) when "Proc" resource.instance_exec(&flag) else !!flag end end
fire_assign(parents, children)
click to toggle source
# File lib/graphiti/sideload.rb, line 410 def fire_assign(parents, children) with_error_handling Errors::SideloadAssignError do if self.class.assign_proc instance_exec(parents, children, &self.class.assign_proc) else assign(parents, children) end end end
fire_assign_each(parent, children)
click to toggle source
# File lib/graphiti/sideload.rb, line 402 def fire_assign_each(parent, children) if self.class.assign_each_proc instance_exec(parent, children, &self.class.assign_each_proc) else assign_each(parent, children) end end
fire_scope(parents)
click to toggle source
# File lib/graphiti/sideload.rb, line 429 def fire_scope(parents) parent_ids = ids_for_parents(parents) if self.class.scope_proc instance_exec(parent_ids, parents, &self.class.scope_proc) else method = method(:scope) if [2, -2].include?(method.arity) scope(parent_ids, parents) else scope(parent_ids) end end end
infer_resource_class()
click to toggle source
# File lib/graphiti/sideload.rb, line 443 def infer_resource_class Util::Class.infer_resource_class(parent_resource.class, name) end
load_options(parents, query)
click to toggle source
# File lib/graphiti/sideload.rb, line 391 def load_options(parents, query) {}.tap do |opts| opts[:default_paginate] = false opts[:sideload_parent_length] = parents.length opts[:query] = query opts[:after_resolve] = ->(results) { fire_assign(parents, results) } end end
namespace_for(klass)
click to toggle source
# File lib/graphiti/sideload.rb, line 447 def namespace_for(klass) Util::Class.namespace_for(klass) end
validate_options!(opts)
click to toggle source
# File lib/graphiti/sideload.rb, line 379 def validate_options!(opts) if opts[:remote] if opts[:resource] raise Errors::SideloadConfig.new(@name, opts[:parent_resource], "cannot pass :remote and :resource options together") end if opts[:link] raise Errors::SideloadConfig.new(@name, opts[:parent_resource], "remote sideloads do not currently support :link") end end end
with_error_handling(error_class) { || ... }
click to toggle source
# File lib/graphiti/sideload.rb, line 420 def with_error_handling(error_class) begin result = yield rescue raise error_class.new(parent_resource_class, name) end result end