class GraphQL::Batch::Loader

Attributes

executor[RW]
loader_key[RW]

Public Class Methods

for(*group_args) click to toggle source
# File lib/graphql/batch/loader.rb, line 11
def self.for(*group_args)
  current_executor.loader(loader_key_for(*group_args)) { new(*group_args) }
end
load(key) click to toggle source
# File lib/graphql/batch/loader.rb, line 20
def self.load(key)
  self.for.load(key)
end
load_many(keys) click to toggle source
# File lib/graphql/batch/loader.rb, line 24
def self.load_many(keys)
  self.for.load_many(keys)
end
loader_key_for(*group_args, **group_kwargs) click to toggle source
# File lib/graphql/batch/loader.rb, line 16
def self.loader_key_for(*group_args, **group_kwargs)
  [self, group_kwargs, group_args]
end
new() click to toggle source
# File lib/graphql/batch/loader.rb, line 47
def initialize
  @loader_key = nil
  @executor = nil
  @queue = nil
  @cache = nil
end

Private Class Methods

current_executor() click to toggle source
# File lib/graphql/batch/loader.rb, line 31
def current_executor
  executor = Executor.current

  unless executor
    raise GraphQL::Batch::NoExecutorError, 'Cannot create loader without'\
      ' an Executor. Wrap the call to `for` with `GraphQL::Batch.batch`'\
      ' or use `GraphQL::Batch::SetupMultiplex` as a query instrumenter if'\
      ' using with `graphql-ruby`'
  end

  executor
end

Public Instance Methods

around_perform() { || ... } click to toggle source

Interface to add custom code for purposes such as instrumenting the performance of the loader.

# File lib/graphql/batch/loader.rb, line 80
def around_perform
  yield
end
load(key) click to toggle source
# File lib/graphql/batch/loader.rb, line 54
def load(key)
  cache[cache_key(key)] ||= begin
    queue << key
    ::Promise.new.tap { |promise| promise.source = self }
  end
end
load_many(keys) click to toggle source
# File lib/graphql/batch/loader.rb, line 61
def load_many(keys)
  ::Promise.all(keys.map { |key| load(key) })
end
resolved?() click to toggle source
# File lib/graphql/batch/loader.rb, line 93
def resolved?
  @queue.nil? || @queue.empty?
end

Protected Instance Methods

cache_key(load_key) click to toggle source

Override to use a different key for the cache than the load key

# File lib/graphql/batch/loader.rb, line 131
def cache_key(load_key)
  load_key
end
fulfill(key, value) click to toggle source

Fulfill the key with provided value, for use in perform

# File lib/graphql/batch/loader.rb, line 100
def fulfill(key, value)
  finish_resolve(key) do |promise|
    promise.fulfill(value)
  end
end
fulfilled?(key) click to toggle source

Returns true when the key has already been fulfilled, otherwise returns false

# File lib/graphql/batch/loader.rb, line 113
def fulfilled?(key)
  promise = promise_for(key)
  # When a promise is fulfilled through this class, it will either:
  #   become fulfilled, if fulfilled with a literal value
  #   become pending with a new source if fulfilled with a promise
  # Either of these is acceptable, promise.rb will automatically re-wait
  # on the new source promise as needed.
  return true if promise.fulfilled?

  promise.pending? && promise.source != self
end
perform(keys) click to toggle source

Must override to load the keys and call fulfill for each key

# File lib/graphql/batch/loader.rb, line 126
def perform(keys)
  raise NotImplementedError
end
reject(key, reason) click to toggle source
# File lib/graphql/batch/loader.rb, line 106
def reject(key, reason)
  finish_resolve(key) do |promise|
    promise.reject(reason)
  end
end

Private Instance Methods

cache() click to toggle source
# File lib/graphql/batch/loader.rb, line 145
def cache
  @cache ||= {}
end
check_for_broken_promises(load_keys) click to toggle source
# File lib/graphql/batch/loader.rb, line 165
def check_for_broken_promises(load_keys)
  load_keys.each do |key|
    promise = promise_for(key)
    # When a promise is fulfilled through this class, it will either:
    #   become not pending, if fulfilled with a literal value
    #   become pending with a new source if fulfilled with a promise
    # Either of these is acceptable, promise.rb will automatically re-wait
    # on the new source promise as needed.
    next unless promise.pending? && promise.source == self

    reject(key, ::Promise::BrokenError.new("#{self.class} didn't fulfill promise for key #{key.inspect}"))
  end
end
finish_resolve(key) { |promise| ... } click to toggle source
# File lib/graphql/batch/loader.rb, line 137
def finish_resolve(key)
  promise = promise_for(key)
  return yield(promise) unless executor
  executor.around_promise_callbacks do
    yield promise
  end
end
promise_for(load_key) click to toggle source
# File lib/graphql/batch/loader.rb, line 153
def promise_for(load_key)
  cache.fetch(cache_key(load_key))
end
queue() click to toggle source
# File lib/graphql/batch/loader.rb, line 149
def queue
  @queue ||= []
end
reject_pending_promises(load_keys, err) click to toggle source
# File lib/graphql/batch/loader.rb, line 157
def reject_pending_promises(load_keys, err)
  load_keys.each do |key|
    next unless promise_for(key).pending?

    reject(key, err)
  end
end