lib/graphql/batch/loader.rb in graphql-batch-0.2.4 vs lib/graphql/batch/loader.rb in graphql-batch-0.2.5
- old
+ new
@@ -1,56 +1,98 @@
module GraphQL::Batch
class Loader
def self.for(*group_args)
loader_key = [self].concat(group_args)
- Executor.current.loaders[loader_key] ||= new(*group_args)
+ Executor.current.loaders[loader_key] ||= new(*group_args).tap do |loader|
+ loader.loader_key = loader_key
+ end
end
- def promises_by_key
- @promises_by_key ||= {}
+ def self.load(key)
+ self.for.load(key)
end
- def keys
- promises_by_key.keys
+ def self.load_many(keys)
+ self.for.load_many(keys)
end
+ attr_accessor :loader_key
+
def load(key)
- promises_by_key[key] ||= Promise.new
+ loader = Executor.current.loaders[loader_key] ||= self
+ if loader != self
+ raise "load called on loader that wasn't registered with executor"
+ end
+ cache[cache_key(key)] ||= begin
+ queue << key
+ Promise.new
+ end
end
def load_many(keys)
Promise.all(keys.map { |key| load(key) })
end
+ def resolve #:nodoc:
+ load_keys = queue
+ return if load_keys.empty?
+ @queue = nil
+ perform(load_keys)
+ check_for_broken_promises(load_keys)
+ rescue => err
+ each_pending_promise(load_keys) do |key, promise|
+ promise.reject(err)
+ end
+ end
+
+ protected
+
+ # Fulfill the key with provided value, for use in #perform
def fulfill(key, value)
- promises_by_key[key].fulfill(value)
+ promise_for(key).fulfill(value)
end
+ # Returns true when the key has already been fulfilled, otherwise returns false
def fulfilled?(key)
- promises_by_key[key].fulfilled?
+ promise_for(key).fulfilled?
end
- # batch load keys and fulfill promises
+ # Must override to load the keys and call #fulfill for each key
def perform(keys)
raise NotImplementedError
end
- def resolve
- perform(keys)
- check_for_broken_promises
- rescue => err
- promises_by_key.each do |key, promise|
- promise.reject(err)
- end
+ # Override to use a different key for the cache than the load key
+ def cache_key(load_key)
+ load_key
end
private
- def check_for_broken_promises
- promises_by_key.each do |key, promise|
+ def cache
+ @cache ||= {}
+ end
+
+ def queue
+ @queue ||= []
+ end
+
+ def promise_for(load_key)
+ cache.fetch(cache_key(load_key))
+ end
+
+ def each_pending_promise(load_keys)
+ load_keys.each do |key|
+ promise = promise_for(key)
if promise.pending?
- promise.reject(BrokenPromiseError.new("#{self.class} didn't fulfill promise for key #{key.inspect}"))
+ yield key, promise
end
+ end
+ end
+
+ def check_for_broken_promises(load_keys)
+ each_pending_promise(load_keys) do |key, promise|
+ promise.reject(BrokenPromiseError.new("#{self.class} didn't fulfill promise for key #{key.inspect}"))
end
end
end
end