Class: GraphQL::Dataloader::Source
- Inherits:
-
Object
- Object
- GraphQL::Dataloader::Source
- Defined in:
- lib/graphql/dataloader/source.rb
Direct Known Subclasses
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).
- #normalize_fetch_key(value) ⇒ Object
-
#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.
184 185 186 |
# File 'lib/graphql/dataloader/source.rb', line 184 def pending @pending end |
#results ⇒ Object (readonly)
Returns the value of attribute results.
184 185 186 |
# File 'lib/graphql/dataloader/source.rb', line 184 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.
173 174 175 |
# File 'lib/graphql/dataloader/source.rb', line 173 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
179 180 181 182 |
# File 'lib/graphql/dataloader/source.rb', line 179 def clear_cache @results.clear nil end |
#fetch(keys) ⇒ Array<Object>
Subclasses must implement this method to return a value for each of keys
98 99 100 101 |
# File 'lib/graphql/dataloader/source.rb', line 98 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.
63 64 65 66 67 68 69 70 71 72 |
# File 'lib/graphql/dataloader/source.rb', line 63 def load(value) result_key = result_key_for(value) if @results.key?(result_key) result_for(result_key) else @pending[result_key] ||= normalize_fetch_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.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/graphql/dataloader/source.rb', line 76 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] ||= normalize_fetch_key(v) pending_keys << k end } if !pending_keys.empty? 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).
129 130 131 132 133 134 135 |
# File 'lib/graphql/dataloader/source.rb', line 129 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 |
#normalize_fetch_key(value) ⇒ Object
Implement this method if varying values given to #load (etc) should be consolidated or normalized before being handed off to your #fetch implementation.
This is different than #result_key_for because that method handles unification inside Dataloader’s cache, but this method changes the value passed into #fetch.
46 47 48 |
# File 'lib/graphql/dataloader/source.rb', line 46 def normalize_fetch_key(value) value end |
#pending? ⇒ Boolean
Returns True if this source has any pending requests for data.
121 122 123 |
# File 'lib/graphql/dataloader/source.rb', line 121 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] ||= normalize_fetch_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.
51 52 53 54 55 56 57 58 59 |
# File 'lib/graphql/dataloader/source.rb', line 51 def request_all(values) values.each do |v| res_key = result_key_for(v) if !@results.key?(res_key) @pending[res_key] ||= normalize_fetch_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.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/graphql/dataloader/source.rb', line 140 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.
107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/graphql/dataloader/source.rb', line 107 def sync(pending_result_keys) @dataloader.yield(self) 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(self) end nil end |