lib/graphql/preload/instrument.rb in graphql-preload-1.0.4 vs lib/graphql/preload/instrument.rb in graphql-preload-2.0.0

- old
+ new

@@ -7,46 +7,53 @@ old_resolver = field.resolve_proc new_resolver = ->(obj, args, ctx) do return old_resolver.call(obj, args, ctx) unless obj - preload(obj, field.metadata[:preload]).then do + if field.metadata[:preload_scope] + scope = field.metadata[:preload_scope].call(args, ctx) + end + + preload(obj, field.metadata[:preload], scope).then do old_resolver.call(obj, args, ctx) end end field.redefine do resolve(new_resolver) end end - private def preload(record, associations) - raise TypeError, "Expected #{associations} to be a Symbol, not a String" if associations.is_a?(String) - return preload_single_association(record, associations) if associations.is_a?(Symbol) + private def preload(record, associations, scope) + if associations.is_a?(String) + raise TypeError, "Expected #{associations} to be a Symbol, not a String" + elsif associations.is_a?(Symbol) + return preload_single_association(record, associations, scope) + end promises = [] Array.wrap(associations).each do |association| case association when Symbol - promises << preload_single_association(record, association) + promises << preload_single_association(record, association, scope) when Array association.each do |sub_association| - promises << preload(record, sub_association) + promises << preload(record, sub_association, scope) end when Hash association.each do |sub_association, nested_association| - promises << preload_single_association(record, sub_association).then do + promises << preload_single_association(record, sub_association, scope).then do associated_records = record.public_send(sub_association) case associated_records when ActiveRecord::Base - preload(associated_records, nested_association) + preload(associated_records, nested_association, scope) else Promise.all( Array.wrap(associated_records).map do |associated_record| - preload(associated_record, nested_association) + preload(associated_record, nested_association, scope) end ) end end end @@ -54,11 +61,22 @@ end Promise.all(promises) end - private def preload_single_association(record, association) - GraphQL::Preload::Loader.for(record.class, association).load(record) + private def preload_single_association(record, association, scope) + # We would like to pass the `scope` (which is an `ActiveRecord::Relation`), + # directly into `Loader.for`. However, because the scope is + # created for each parent record, they are different objects and + # return different loaders, breaking batching. + # Therefore, we pass in `scope.to_sql`, which is the same for all the + # scopes and set the `scope` using an accessor. The actual scope + # object used will be the last one, which shouldn't make any difference, + # because even though they are different objects, they are all + # functionally equivalent. + loader = GraphQL::Preload::Loader.for(record.class, association, scope.try(:to_sql)) + loader.scope = scope + loader.load(record) end end end end