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.
192 193 194 |
# File 'lib/graphql/dataloader/source.rb', line 192 def pending @pending end |
#results ⇒ Object (readonly)
Returns the value of attribute results.
192 193 194 |
# File 'lib/graphql/dataloader/source.rb', line 192 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::Relations to .with(...), you could override
this method to call .to_sql on them, thus merging .load(...) calls when they apply
to equivalent relations.
177 178 179 180 181 182 183 |
# File 'lib/graphql/dataloader/source.rb', line 177 def self.batch_key_for(*batch_args, **batch_kwargs) if batch_kwargs.any? # rubocop:disable Development/NoneWithoutBlockCop [*batch_args, **batch_kwargs] else batch_args end end |
Instance Method Details
#clear_cache ⇒ void
This method returns an undefined value.
Clear any already-loaded objects for this source
187 188 189 190 |
# File 'lib/graphql/dataloader/source.rb', line 187 def clear_cache @results.clear nil end |
#fetch(keys) ⇒ Array<Object>
Subclasses must implement this method to return a value for each of keys
99 100 101 102 |
# File 'lib/graphql/dataloader/source.rb', line 99 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 94 |
# 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) } result_keys 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).
130 131 132 133 134 135 136 |
# File 'lib/graphql/dataloader/source.rb', line 130 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.
122 123 124 |
# File 'lib/graphql/dataloader/source.rb', line 122 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.
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/graphql/dataloader/source.rb', line 141 def run_pending_keys @fetching.each_key { |k| @pending.delete(k) } return if @pending.empty? fetch_h = @pending @fetching.merge!(fetch_h) @pending = {} results = fetch(fetch_h.values) idx = 0 fetch_h.each_key do |key| @results[key] = results[idx] @fetching.delete(key) idx += 1 end nil rescue StandardError => error fetch_h.each_key { |key| @results[key] = error @fetching.delete(key) } 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.
108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/graphql/dataloader/source.rb', line 108 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 |