Class: GraphQL::Dataloader::Source
- Inherits:
-
Object
- Object
- GraphQL::Dataloader::Source
- Defined in:
- lib/graphql/dataloader/source.rb
Instance Attribute Summary collapse
-
#dataloader ⇒ Object
readonly
Returns the value of attribute dataloader.
-
#pending_keys ⇒ Object
readonly
Returns the value of attribute pending_keys.
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
-
#fetch(keys) ⇒ Array<Object>
Subclasses must implement this method to return a value for each of
keys
. -
#load(key) ⇒ Object
The result from #fetch for
key
. -
#load_all(keys) ⇒ Object
The result from #fetch for
keys
. -
#pending? ⇒ Boolean
True if this source has any pending requests for data.
-
#request(key) ⇒ Dataloader::Request
A pending request for a value from
key
. -
#request_all(keys) ⇒ Dataloader::Request
A pending request for a values from
keys
. -
#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 ⇒ 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_keys ⇒ Object (readonly)
Returns the value of attribute pending_keys.
131 132 133 |
# File 'lib/graphql/dataloader/source.rb', line 131 def pending_keys @pending_keys 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.
127 128 129 |
# File 'lib/graphql/dataloader/source.rb', line 127 def self.batch_key_for(*batch_args, **batch_kwargs) [*batch_args, **batch_kwargs] end |
Instance Method Details
#fetch(keys) ⇒ Array<Object>
Subclasses must implement this method to return a value for each of keys
62 63 64 65 |
# File 'lib/graphql/dataloader/source.rb', line 62 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(key) ⇒ Object
Returns The result from #fetch for key
. If key
hasn't been loaded yet, the Fiber will yield until it's loaded.
37 38 39 40 41 42 43 44 45 |
# File 'lib/graphql/dataloader/source.rb', line 37 def load(key) if @results.key?(key) result_for(key) else @pending_keys << key sync result_for(key) end end |
#load_all(keys) ⇒ Object
Returns The result from #fetch for keys
. If keys
haven't been loaded yet, the Fiber will yield until they're loaded.
49 50 51 52 53 54 55 56 57 |
# File 'lib/graphql/dataloader/source.rb', line 49 def load_all(keys) if keys.any? { |k| !@results.key?(k) } pending_keys = keys.select { |k| !@results.key?(k) } @pending_keys.concat(pending_keys) sync end keys.map { |k| result_for(k) } end |
#pending? ⇒ Boolean
Returns True if this source has any pending requests for data.
85 86 87 |
# File 'lib/graphql/dataloader/source.rb', line 85 def pending? !@pending_keys.empty? end |
#request(key) ⇒ 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 |
# File 'lib/graphql/dataloader/source.rb', line 21 def request(key) if !@results.key?(key) @pending_keys << key end Dataloader::Request.new(self, key) end |
#request_all(keys) ⇒ Dataloader::Request
Returns a pending request for a values from keys
. Call .load
on that object to wait for the results.
29 30 31 32 33 |
# File 'lib/graphql/dataloader/source.rb', line 29 def request_all(keys) pending_keys = keys.select { |k| !@results.key?(k) } @pending_keys.concat(pending_keys) Dataloader::RequestAll.new(self, keys) 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.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/graphql/dataloader/source.rb', line 92 def run_pending_keys if !@fetching_keys.empty? @pending_keys -= @fetching_keys end return if @pending_keys.empty? fetch_keys = @pending_keys.uniq @fetching_keys.concat(fetch_keys) @pending_keys = [] results = fetch(fetch_keys) fetch_keys.each_with_index do |key, idx| @results[key] = results[idx] end nil rescue StandardError => error fetch_keys.each { |key| @results[key] = error } ensure if fetch_keys @fetching_keys -= fetch_keys end 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_keys = [] # These keys have been passed to `fetch` but haven't been finished yet @fetching_keys = [] # { key => result } @results = {} @dataloader = dataloader end |
#sync ⇒ 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.
70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/graphql/dataloader/source.rb', line 70 def sync pending_keys = @pending_keys.dup @dataloader.yield iterations = 0 while pending_keys.any? { |k| !@results.key?(k) } iterations += 1 if iterations > 1000 raise "#{self.class}#sync tried 1000 times to load pending keys (#{pending_keys}), but they still weren't loaded. There is likely a circular dependency." end @dataloader.yield end nil end |