Class: GraphQL::Dataloader::Source
- Inherits:
-
Object
- Object
- GraphQL::Dataloader::Source
- Defined in:
- lib/graphql/dataloader/source.rb
Constant Summary collapse
- MAX_ITERATIONS =
1000
Instance Attribute Summary collapse
-
#dataloader ⇒ Object
readonly
Returns the value of attribute dataloader.
-
#pending ⇒ Object
readonly
Returns the value of attribute pending.
-
#results ⇒ Object
readonly
Returns the value of attribute results.
Class Method Summary collapse
-
.batch_key_for(*batch_args, **batch_kwargs) ⇒ Object
These arguments are given to
dataloader.with(source_class, ...)
.
Instance Method Summary collapse
-
#clear_cache ⇒ void
Clear any already-loaded objects for this source.
-
#fetch(keys) ⇒ Array<Object>
Subclasses must implement this method to return a value for each of
keys
. -
#load(value) ⇒ Object
The result from #fetch for
key
. -
#load_all(values) ⇒ Object
The result from #fetch for
keys
. -
#merge(new_results) ⇒ void
Add these key-value pairs to this source's cache (future loads will use these merged values).
-
#pending? ⇒ Boolean
True if this source has any pending requests for data.
-
#request(value) ⇒ Dataloader::Request
A pending request for a value from
key
. -
#request_all(values) ⇒ Dataloader::Request
A pending request for a values from
keys
. -
#result_key_for(value) ⇒ Object
Implement this method to return a stable identifier if different key objects should load the same data value.
-
#run_pending_keys ⇒ void
private
Called by GraphQL::Dataloader to resolve and pending requests to this source.
-
#setup(dataloader) ⇒ Object
private
Called by GraphQL::Dataloader to prepare the Source's internal state.
-
#sync(pending_result_keys) ⇒ void
Wait for a batch, if there's anything to batch.
Instance Attribute Details
#dataloader ⇒ Object (readonly)
Returns the value of attribute dataloader.
18 19 20 |
# File 'lib/graphql/dataloader/source.rb', line 18 def dataloader @dataloader end |
#pending ⇒ Object (readonly)
Returns the value of attribute pending.
172 173 174 |
# File 'lib/graphql/dataloader/source.rb', line 172 def pending @pending end |
#results ⇒ Object (readonly)
Returns the value of attribute results.
172 173 174 |
# File 'lib/graphql/dataloader/source.rb', line 172 def results @results end |
Class Method Details
.batch_key_for(*batch_args, **batch_kwargs) ⇒ Object
These arguments are given to dataloader.with(source_class, ...)
. The object
returned from this method is used to de-duplicate batch loads under the hood
by using it as a Hash key.
By default, the arguments are all put in an Array. To customize how this source's batches are merged, override this method to return something else.
For example, if you pass ActiveRecord::Relation
s to .with(...)
, you could override
this method to call .to_sql
on them, thus merging .load(...)
calls when they apply
to equivalent relations.
161 162 163 |
# File 'lib/graphql/dataloader/source.rb', line 161 def self.batch_key_for(*batch_args, **batch_kwargs) [*batch_args, **batch_kwargs] end |
Instance Method Details
#clear_cache ⇒ void
This method returns an undefined value.
Clear any already-loaded objects for this source
167 168 169 170 |
# File 'lib/graphql/dataloader/source.rb', line 167 def clear_cache @results.clear nil end |
#fetch(keys) ⇒ Array<Object>
Subclasses must implement this method to return a value for each of keys
86 87 88 89 |
# File 'lib/graphql/dataloader/source.rb', line 86 def fetch(keys) # somehow retrieve these from the backend raise "Implement `#{self.class}#fetch(#{keys.inspect}) to return a record for each of the keys" end |
#load(value) ⇒ Object
Returns The result from #fetch for key
. If key
hasn't been loaded yet, the Fiber will yield until it's loaded.
51 52 53 54 55 56 57 58 59 60 |
# File 'lib/graphql/dataloader/source.rb', line 51 def load(value) result_key = result_key_for(value) if @results.key?(result_key) result_for(result_key) else @pending[result_key] ||= value sync([result_key]) result_for(result_key) end end |
#load_all(values) ⇒ Object
Returns The result from #fetch for keys
. If keys
haven't been loaded yet, the Fiber will yield until they're loaded.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/graphql/dataloader/source.rb', line 64 def load_all(values) result_keys = [] pending_keys = [] values.each { |v| k = result_key_for(v) result_keys << k if !@results.key?(k) @pending[k] ||= v pending_keys << k end } if pending_keys.any? sync(pending_keys) end result_keys.map { |k| result_for(k) } end |
#merge(new_results) ⇒ void
This method returns an undefined value.
Add these key-value pairs to this source's cache (future loads will use these merged values).
117 118 119 120 121 122 123 |
# File 'lib/graphql/dataloader/source.rb', line 117 def merge(new_results) new_results.each do |new_k, new_v| key = result_key_for(new_k) @results[key] = new_v end nil end |
#pending? ⇒ Boolean
Returns True if this source has any pending requests for data.
109 110 111 |
# File 'lib/graphql/dataloader/source.rb', line 109 def pending? !@pending.empty? end |
#request(value) ⇒ Dataloader::Request
Returns a pending request for a value from key
. Call .load
on that object to wait for the result.
21 22 23 24 25 26 27 |
# File 'lib/graphql/dataloader/source.rb', line 21 def request(value) res_key = result_key_for(value) if !@results.key?(res_key) @pending[res_key] ||= value end Dataloader::Request.new(self, value) end |
#request_all(values) ⇒ Dataloader::Request
Returns a pending request for a values from keys
. Call .load
on that object to wait for the results.
39 40 41 42 43 44 45 46 47 |
# File 'lib/graphql/dataloader/source.rb', line 39 def request_all(values) values.each do |v| res_key = result_key_for(v) if !@results.key?(res_key) @pending[res_key] ||= v end end Dataloader::RequestAll.new(self, values) end |
#result_key_for(value) ⇒ Object
Implement this method to return a stable identifier if different key objects should load the same data value.
34 35 36 |
# File 'lib/graphql/dataloader/source.rb', line 34 def result_key_for(value) value end |
#run_pending_keys ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Called by GraphQL::Dataloader to resolve and pending requests to this source.
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/graphql/dataloader/source.rb', line 128 def run_pending_keys if !@fetching.empty? @fetching.each_key { |k| @pending.delete(k) } end return if @pending.empty? fetch_h = @pending @pending = {} @fetching.merge!(fetch_h) results = fetch(fetch_h.values) fetch_h.each_with_index do |(key, _value), idx| @results[key] = results[idx] end nil rescue StandardError => error fetch_h.each_key { |key| @results[key] = error } ensure fetch_h && fetch_h.each_key { |k| @fetching.delete(k) } end |
#setup(dataloader) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Called by GraphQL::Dataloader to prepare the GraphQL::Dataloader::Source's internal state
8 9 10 11 12 13 14 15 16 |
# File 'lib/graphql/dataloader/source.rb', line 8 def setup(dataloader) # These keys have been requested but haven't been fetched yet @pending = {} # These keys have been passed to `fetch` but haven't been finished yet @fetching = {} # { key => result } @results = {} @dataloader = dataloader end |
#sync(pending_result_keys) ⇒ void
This method returns an undefined value.
Wait for a batch, if there's anything to batch. Then run the batch and update the cache.
95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/graphql/dataloader/source.rb', line 95 def sync(pending_result_keys) @dataloader.yield iterations = 0 while pending_result_keys.any? { |key| !@results.key?(key) } iterations += 1 if iterations > MAX_ITERATIONS raise "#{self.class}#sync tried #{MAX_ITERATIONS} times to load pending keys (#{pending_result_keys}), but they still weren't loaded. There is likely a circular dependency#{@dataloader.fiber_limit ? " or `fiber_limit: #{@dataloader.fiber_limit}` is set too low" : ""}." end @dataloader.yield end nil end |