graphql-fragment_cache 1.7.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +14 -0
- data/lib/graphql/fragment_cache/cache_key_builder.rb +2 -0
- data/lib/graphql/fragment_cache/field_extension.rb +2 -1
- data/lib/graphql/fragment_cache/object_helpers.rb +4 -0
- data/lib/graphql/fragment_cache/version.rb +1 -1
- data/lib/graphql/fragment_cache.rb +2 -0
- metadata +21 -11
- data/lib/.rbnext/2.3/graphql/fragment_cache/cache_key_builder.rb +0 -196
- data/lib/.rbnext/2.3/graphql/fragment_cache/memory_store.rb +0 -64
- data/lib/.rbnext/2.7/graphql/fragment_cache/cache_key_builder.rb +0 -196
- data/lib/.rbnext/2.7/graphql/fragment_cache/ext/graphql_cache_key.rb +0 -93
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c0987cb1e081b19356cfd389164150d7678c2fff2a479986f56e157ac485e79
|
4
|
+
data.tar.gz: 72cc6cf9a79c003be0c83dc60447840128ba1d8395e60171ce26a5eaf8190f2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78be339e321b1c76066e63960d615d85fc286531ba663eef4af3c62baee8b3d598867047cf1c52ca6085b7fca3cbd86ec0038718548e014aff5c8273740a3c9a
|
7
|
+
data.tar.gz: 005efd512fba6e4f91ba5aec08637ce058217f7c2e580ef4afa8c6323fef035a70c72a5abd74d418892f02b607b6e5fff288adfb9830ee9125985b852153775e
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,22 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 1.10.0 (2022-01-30)
|
6
|
+
|
7
|
+
- [PR#77](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/77) Drop Ruby 2.5 support, add Ruby 3.0 ([@DmitryTsepelev][])
|
8
|
+
|
9
|
+
## 1.9.1 (2021-11-28)
|
10
|
+
|
11
|
+
- [PR#76](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/76) Freeze parser version ([@DmitryTsepelev][])
|
12
|
+
|
13
|
+
## 1.9.0 (2021-08-19)
|
14
|
+
|
15
|
+
- [PR#71](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/71) Add selection alias to cache keys ([@mretzak][])
|
16
|
+
|
17
|
+
## 1.8.0 (2021-05-13)
|
18
|
+
|
19
|
+
- [PR#65](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/65) Add default options ([@jeromedalbert][])
|
20
|
+
|
5
21
|
## 1.7.0 (2021-04-30)
|
6
22
|
|
7
23
|
- [PR#62](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/62) Add a way to force a cache miss ([@jeromedalbert][])
|
@@ -107,3 +123,4 @@
|
|
107
123
|
[@reabiliti]: https://github.com/reabiliti
|
108
124
|
[@bbugh]: https://github.com/bbugh
|
109
125
|
[@jeromedalbert]: https://github.com/jeromedalbert
|
126
|
+
[@mretzak]: https://github.com/mretzak
|
data/README.md
CHANGED
@@ -314,6 +314,20 @@ field :post, PostType, cache_fragment: {if: -> { current_user.nil? }} do
|
|
314
314
|
end
|
315
315
|
```
|
316
316
|
|
317
|
+
## Default options
|
318
|
+
|
319
|
+
You can configure default options that will be passed to all `cache_fragment`
|
320
|
+
calls and `cache_fragment:` configurations. For example:
|
321
|
+
|
322
|
+
```ruby
|
323
|
+
GraphQL::FragmentCache.configure do |config|
|
324
|
+
config.default_options = {
|
325
|
+
expires_in: 1.hour, # Expire cache keys after 1 hour
|
326
|
+
schema_cache_key: nil # Do not clear the cache on each schema change
|
327
|
+
}
|
328
|
+
end
|
329
|
+
```
|
330
|
+
|
317
331
|
## Renewing the cache
|
318
332
|
|
319
333
|
You can force the cache to renew during query execution by adding
|
@@ -22,6 +22,8 @@ module GraphQL
|
|
22
22
|
children = val.selections.empty? ? "" : "[#{val.selections.to_selections_key}]"
|
23
23
|
|
24
24
|
field_name = val.field.name
|
25
|
+
field_alias = val.ast_nodes.map(&:alias).join
|
26
|
+
field_name = "#{field_alias}:#{field_name}" unless field_alias.empty?
|
25
27
|
|
26
28
|
unless val.arguments.empty?
|
27
29
|
args = val.arguments.map { "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")
|
@@ -28,7 +28,8 @@ module GraphQL
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def initialize(options:, **_rest)
|
31
|
-
@cache_options = options || {}
|
31
|
+
@cache_options = GraphQL::FragmentCache.default_options.merge(options || {})
|
32
|
+
@cache_options[:default_options_merged] = true
|
32
33
|
|
33
34
|
@context_key = @cache_options.delete(:context_key)
|
34
35
|
@cache_key = @cache_options.delete(:cache_key)
|
@@ -25,6 +25,10 @@ module GraphQL
|
|
25
25
|
def cache_fragment(object_to_cache = NO_OBJECT, **options, &block)
|
26
26
|
raise ArgumentError, "Block or argument must be provided" unless block_given? || object_to_cache != NO_OBJECT
|
27
27
|
|
28
|
+
unless options.delete(:default_options_merged)
|
29
|
+
options = GraphQL::FragmentCache.default_options.merge(options)
|
30
|
+
end
|
31
|
+
|
28
32
|
if options.key?(:if) || options.key?(:unless)
|
29
33
|
disabled = options.key?(:if) ? !options.delete(:if) : options.delete(:unless)
|
30
34
|
if disabled
|
@@ -22,6 +22,7 @@ module GraphQL
|
|
22
22
|
class << self
|
23
23
|
attr_reader :cache_store
|
24
24
|
attr_accessor :namespace
|
25
|
+
attr_accessor :default_options
|
25
26
|
|
26
27
|
def use(schema_defn, options = {})
|
27
28
|
verify_interpreter_and_analysis!(schema_defn)
|
@@ -81,6 +82,7 @@ module GraphQL
|
|
81
82
|
end
|
82
83
|
|
83
84
|
self.cache_store = MemoryStore.new
|
85
|
+
self.default_options = {}
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-fragment_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- DmitryTsepelev
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: parser
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - '='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 3.0.2.0
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - '='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: 3.0.2.0
|
167
181
|
description: Fragment cache for graphql-ruby
|
168
182
|
email:
|
169
183
|
- dmitry.a.tsepelev@gmail.com
|
@@ -176,10 +190,6 @@ files:
|
|
176
190
|
- README.md
|
177
191
|
- bin/console
|
178
192
|
- bin/setup
|
179
|
-
- lib/.rbnext/2.3/graphql/fragment_cache/cache_key_builder.rb
|
180
|
-
- lib/.rbnext/2.3/graphql/fragment_cache/memory_store.rb
|
181
|
-
- lib/.rbnext/2.7/graphql/fragment_cache/cache_key_builder.rb
|
182
|
-
- lib/.rbnext/2.7/graphql/fragment_cache/ext/graphql_cache_key.rb
|
183
193
|
- lib/graphql-fragment_cache.rb
|
184
194
|
- lib/graphql/fragment_cache.rb
|
185
195
|
- lib/graphql/fragment_cache/cache_key_builder.rb
|
@@ -205,7 +215,7 @@ metadata:
|
|
205
215
|
homepage_uri: https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache
|
206
216
|
source_code_uri: https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache
|
207
217
|
changelog_uri: https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/CHANGELOG.md
|
208
|
-
post_install_message:
|
218
|
+
post_install_message:
|
209
219
|
rdoc_options: []
|
210
220
|
require_paths:
|
211
221
|
- lib
|
@@ -213,15 +223,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
213
223
|
requirements:
|
214
224
|
- - ">="
|
215
225
|
- !ruby/object:Gem::Version
|
216
|
-
version: '2.
|
226
|
+
version: '2.6'
|
217
227
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
218
228
|
requirements:
|
219
229
|
- - ">="
|
220
230
|
- !ruby/object:Gem::Version
|
221
231
|
version: '0'
|
222
232
|
requirements: []
|
223
|
-
rubygems_version: 3.1
|
224
|
-
signing_key:
|
233
|
+
rubygems_version: 3.0.3.1
|
234
|
+
signing_key:
|
225
235
|
specification_version: 4
|
226
236
|
summary: Fragment cache for graphql-ruby
|
227
237
|
test_files: []
|
@@ -1,196 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "json"
|
4
|
-
require "digest"
|
5
|
-
|
6
|
-
using RubyNext
|
7
|
-
|
8
|
-
module GraphQL
|
9
|
-
module FragmentCache
|
10
|
-
using Ext
|
11
|
-
|
12
|
-
using(Module.new {
|
13
|
-
refine Array do
|
14
|
-
def traverse_argument(argument)
|
15
|
-
return argument unless argument.is_a?(GraphQL::Schema::InputObject)
|
16
|
-
|
17
|
-
"{#{argument.map { |_1, _2| "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")}}"
|
18
|
-
end
|
19
|
-
|
20
|
-
def to_selections_key
|
21
|
-
map { |val|
|
22
|
-
children = val.selections.empty? ? "" : "[#{val.selections.to_selections_key}]"
|
23
|
-
|
24
|
-
field_name = val.field.name
|
25
|
-
|
26
|
-
unless val.arguments.empty?
|
27
|
-
args = val.arguments.map { |_1, _2| "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")
|
28
|
-
field_name += "(#{args})"
|
29
|
-
end
|
30
|
-
|
31
|
-
"#{field_name}#{children}"
|
32
|
-
}.join(".")
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
refine ::GraphQL::Language::Nodes::AbstractNode do
|
37
|
-
def alias?(_)
|
38
|
-
false
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
refine ::GraphQL::Language::Nodes::Field do
|
43
|
-
def alias?(val)
|
44
|
-
self.alias == val
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
refine ::GraphQL::Execution::Lookahead do
|
49
|
-
def selection_with_alias(name, **kwargs)
|
50
|
-
return selection(name, **kwargs) if selects?(name, **kwargs)
|
51
|
-
alias_selection(name, **kwargs)
|
52
|
-
end
|
53
|
-
|
54
|
-
def alias_selection(name, selected_type: @selected_type, arguments: nil)
|
55
|
-
return alias_selections[name] if alias_selections.key?(name)
|
56
|
-
|
57
|
-
alias_node = lookup_alias_node(ast_nodes, name)
|
58
|
-
return ::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD unless alias_node
|
59
|
-
|
60
|
-
next_field_name = alias_node.name
|
61
|
-
|
62
|
-
# From https://github.com/rmosolgo/graphql-ruby/blob/1a9a20f3da629e63ea8e5ee8400be82218f9edc3/lib/graphql/execution/lookahead.rb#L91
|
63
|
-
next_field_defn = get_class_based_field(selected_type, next_field_name)
|
64
|
-
|
65
|
-
alias_selections[name] =
|
66
|
-
if next_field_defn
|
67
|
-
next_nodes = []
|
68
|
-
arguments = @query.arguments_for(alias_node, next_field_defn)
|
69
|
-
arguments = arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments) ? arguments.keyword_arguments : arguments
|
70
|
-
@ast_nodes.each do |ast_node|
|
71
|
-
ast_node.selections.each do |selection|
|
72
|
-
find_selected_nodes(selection, next_field_name, next_field_defn, arguments: arguments, matches: next_nodes)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
if next_nodes.any?
|
77
|
-
::GraphQL::Execution::Lookahead.new(query: @query, ast_nodes: next_nodes, field: next_field_defn, owner_type: selected_type)
|
78
|
-
else
|
79
|
-
::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
80
|
-
end
|
81
|
-
else
|
82
|
-
::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def alias_selections
|
87
|
-
return @alias_selections if defined?(@alias_selections)
|
88
|
-
@alias_selections ||= {}
|
89
|
-
end
|
90
|
-
|
91
|
-
def lookup_alias_node(nodes, name)
|
92
|
-
return if nodes.empty?
|
93
|
-
|
94
|
-
nodes.find do |node|
|
95
|
-
if node.is_a?(GraphQL::Language::Nodes::FragmentSpread)
|
96
|
-
node = @query.fragments[node.name]
|
97
|
-
raise("Invariant: Can't look ahead to nonexistent fragment #{node.name} (found: #{@query.fragments.keys})") unless node
|
98
|
-
end
|
99
|
-
|
100
|
-
return node if node.alias?(name)
|
101
|
-
child = lookup_alias_node(node.children, name)
|
102
|
-
return child if child
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
})
|
107
|
-
|
108
|
-
# Builds cache key for fragment
|
109
|
-
class CacheKeyBuilder
|
110
|
-
using RubyNext
|
111
|
-
|
112
|
-
class << self
|
113
|
-
def call(**options)
|
114
|
-
new(**options).build
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
attr_reader :query, :path, :object, :schema
|
119
|
-
|
120
|
-
def initialize(object: nil, query:, path:, **options)
|
121
|
-
@object = object
|
122
|
-
@query = query
|
123
|
-
@schema = query.schema
|
124
|
-
@path = path
|
125
|
-
@options = options
|
126
|
-
end
|
127
|
-
|
128
|
-
def build
|
129
|
-
[
|
130
|
-
GraphQL::FragmentCache.namespace,
|
131
|
-
implicit_cache_key,
|
132
|
-
object_cache_key
|
133
|
-
].compact.join("/")
|
134
|
-
end
|
135
|
-
|
136
|
-
private
|
137
|
-
|
138
|
-
def implicit_cache_key
|
139
|
-
Digest::SHA1.hexdigest("#{schema_cache_key}/#{query_cache_key}")
|
140
|
-
end
|
141
|
-
|
142
|
-
def schema_cache_key
|
143
|
-
@options.fetch(:schema_cache_key) { schema.schema_cache_key }
|
144
|
-
end
|
145
|
-
|
146
|
-
def query_cache_key
|
147
|
-
@options.fetch(:query_cache_key) { "#{path_cache_key}[#{selections_cache_key}]" }
|
148
|
-
end
|
149
|
-
|
150
|
-
def selections_cache_key
|
151
|
-
current_root =
|
152
|
-
path.reduce(query.lookahead) { |lkhd, field_name|
|
153
|
-
# Handle cached fields inside collections:
|
154
|
-
next lkhd if field_name.is_a?(Integer)
|
155
|
-
|
156
|
-
lkhd.selection_with_alias(field_name)
|
157
|
-
}
|
158
|
-
|
159
|
-
current_root.selections.to_selections_key
|
160
|
-
end
|
161
|
-
|
162
|
-
def path_cache_key
|
163
|
-
@options.fetch(:path_cache_key) do
|
164
|
-
lookahead = query.lookahead
|
165
|
-
|
166
|
-
path.map { |field_name|
|
167
|
-
# Handle cached fields inside collections:
|
168
|
-
next field_name if field_name.is_a?(Integer)
|
169
|
-
|
170
|
-
lookahead = lookahead.selection_with_alias(field_name)
|
171
|
-
raise "Failed to look ahead the field: #{field_name}" if lookahead.is_a?(::GraphQL::Execution::Lookahead::NullLookahead)
|
172
|
-
|
173
|
-
next lookahead.field.name if lookahead.arguments.empty?
|
174
|
-
|
175
|
-
args = lookahead.arguments.map { |_1, _2| "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")
|
176
|
-
"#{lookahead.field.name}(#{args})"
|
177
|
-
}.join("/")
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def traverse_argument(argument)
|
182
|
-
return argument unless argument.is_a?(GraphQL::Schema::InputObject)
|
183
|
-
|
184
|
-
"{#{argument.map { |_1, _2| "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")}}"
|
185
|
-
end
|
186
|
-
|
187
|
-
def object_cache_key
|
188
|
-
@options[:object_cache_key] || object_key(object)
|
189
|
-
end
|
190
|
-
|
191
|
-
def object_key(obj)
|
192
|
-
((!obj.nil?) || nil) && obj._graphql_cache_key
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
@@ -1,64 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
using RubyNext
|
4
|
-
|
5
|
-
module GraphQL
|
6
|
-
module FragmentCache
|
7
|
-
# Memory adapter for storing cached fragments
|
8
|
-
class MemoryStore
|
9
|
-
using RubyNext
|
10
|
-
|
11
|
-
class Entry < Struct.new(:value, :expires_at, keyword_init: true)
|
12
|
-
def expired?
|
13
|
-
expires_at && expires_at < Time.now
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
attr_reader :default_expires_in
|
18
|
-
|
19
|
-
def initialize(expires_in: nil, **other)
|
20
|
-
raise ArgumentError, "Unsupported options: #{other.keys.join(",")}" unless other.empty?
|
21
|
-
|
22
|
-
@default_expires_in = expires_in
|
23
|
-
@storage = {}
|
24
|
-
end
|
25
|
-
|
26
|
-
def keys
|
27
|
-
storage.keys
|
28
|
-
end
|
29
|
-
|
30
|
-
def exist?(key)
|
31
|
-
storage.key?(key)
|
32
|
-
end
|
33
|
-
|
34
|
-
def read(key)
|
35
|
-
key = key.to_s
|
36
|
-
((!storage[key].nil?) || nil) && storage[key].then do |entry|
|
37
|
-
if entry.expired?
|
38
|
-
delete(key)
|
39
|
-
next
|
40
|
-
end
|
41
|
-
entry.value
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def write(key, value, expires_in: default_expires_in, **options)
|
46
|
-
key = key.to_s
|
47
|
-
@storage[key] = Entry.new(value: value, expires_at: expires_in ? Time.now + expires_in : nil)
|
48
|
-
end
|
49
|
-
|
50
|
-
def delete(key)
|
51
|
-
key = key.to_s
|
52
|
-
storage.delete(key)
|
53
|
-
end
|
54
|
-
|
55
|
-
def clear
|
56
|
-
storage.clear
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
attr_reader :storage
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
@@ -1,196 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "json"
|
4
|
-
require "digest"
|
5
|
-
|
6
|
-
using RubyNext
|
7
|
-
|
8
|
-
module GraphQL
|
9
|
-
module FragmentCache
|
10
|
-
using Ext
|
11
|
-
|
12
|
-
using(Module.new {
|
13
|
-
refine Array do
|
14
|
-
def traverse_argument(argument)
|
15
|
-
return argument unless argument.is_a?(GraphQL::Schema::InputObject)
|
16
|
-
|
17
|
-
"{#{argument.map { |_1, _2| "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")}}"
|
18
|
-
end
|
19
|
-
|
20
|
-
def to_selections_key
|
21
|
-
map { |val|
|
22
|
-
children = val.selections.empty? ? "" : "[#{val.selections.to_selections_key}]"
|
23
|
-
|
24
|
-
field_name = val.field.name
|
25
|
-
|
26
|
-
unless val.arguments.empty?
|
27
|
-
args = val.arguments.map { |_1, _2| "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")
|
28
|
-
field_name += "(#{args})"
|
29
|
-
end
|
30
|
-
|
31
|
-
"#{field_name}#{children}"
|
32
|
-
}.join(".")
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
refine ::GraphQL::Language::Nodes::AbstractNode do
|
37
|
-
def alias?(_)
|
38
|
-
false
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
refine ::GraphQL::Language::Nodes::Field do
|
43
|
-
def alias?(val)
|
44
|
-
self.alias == val
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
refine ::GraphQL::Execution::Lookahead do
|
49
|
-
def selection_with_alias(name, **kwargs)
|
50
|
-
return selection(name, **kwargs) if selects?(name, **kwargs)
|
51
|
-
alias_selection(name, **kwargs)
|
52
|
-
end
|
53
|
-
|
54
|
-
def alias_selection(name, selected_type: @selected_type, arguments: nil)
|
55
|
-
return alias_selections[name] if alias_selections.key?(name)
|
56
|
-
|
57
|
-
alias_node = lookup_alias_node(ast_nodes, name)
|
58
|
-
return ::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD unless alias_node
|
59
|
-
|
60
|
-
next_field_name = alias_node.name
|
61
|
-
|
62
|
-
# From https://github.com/rmosolgo/graphql-ruby/blob/1a9a20f3da629e63ea8e5ee8400be82218f9edc3/lib/graphql/execution/lookahead.rb#L91
|
63
|
-
next_field_defn = get_class_based_field(selected_type, next_field_name)
|
64
|
-
|
65
|
-
alias_selections[name] =
|
66
|
-
if next_field_defn
|
67
|
-
next_nodes = []
|
68
|
-
arguments = @query.arguments_for(alias_node, next_field_defn)
|
69
|
-
arguments = arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments) ? arguments.keyword_arguments : arguments
|
70
|
-
@ast_nodes.each do |ast_node|
|
71
|
-
ast_node.selections.each do |selection|
|
72
|
-
find_selected_nodes(selection, next_field_name, next_field_defn, arguments: arguments, matches: next_nodes)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
if next_nodes.any?
|
77
|
-
::GraphQL::Execution::Lookahead.new(query: @query, ast_nodes: next_nodes, field: next_field_defn, owner_type: selected_type)
|
78
|
-
else
|
79
|
-
::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
80
|
-
end
|
81
|
-
else
|
82
|
-
::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def alias_selections
|
87
|
-
return @alias_selections if defined?(@alias_selections)
|
88
|
-
@alias_selections ||= {}
|
89
|
-
end
|
90
|
-
|
91
|
-
def lookup_alias_node(nodes, name)
|
92
|
-
return if nodes.empty?
|
93
|
-
|
94
|
-
nodes.find do |node|
|
95
|
-
if node.is_a?(GraphQL::Language::Nodes::FragmentSpread)
|
96
|
-
node = @query.fragments[node.name]
|
97
|
-
raise("Invariant: Can't look ahead to nonexistent fragment #{node.name} (found: #{@query.fragments.keys})") unless node
|
98
|
-
end
|
99
|
-
|
100
|
-
return node if node.alias?(name)
|
101
|
-
child = lookup_alias_node(node.children, name)
|
102
|
-
return child if child
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
})
|
107
|
-
|
108
|
-
# Builds cache key for fragment
|
109
|
-
class CacheKeyBuilder
|
110
|
-
using RubyNext
|
111
|
-
|
112
|
-
class << self
|
113
|
-
def call(**options)
|
114
|
-
new(**options).build
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
attr_reader :query, :path, :object, :schema
|
119
|
-
|
120
|
-
def initialize(object: nil, query:, path:, **options)
|
121
|
-
@object = object
|
122
|
-
@query = query
|
123
|
-
@schema = query.schema
|
124
|
-
@path = path
|
125
|
-
@options = options
|
126
|
-
end
|
127
|
-
|
128
|
-
def build
|
129
|
-
[
|
130
|
-
GraphQL::FragmentCache.namespace,
|
131
|
-
implicit_cache_key,
|
132
|
-
object_cache_key
|
133
|
-
].compact.join("/")
|
134
|
-
end
|
135
|
-
|
136
|
-
private
|
137
|
-
|
138
|
-
def implicit_cache_key
|
139
|
-
Digest::SHA1.hexdigest("#{schema_cache_key}/#{query_cache_key}")
|
140
|
-
end
|
141
|
-
|
142
|
-
def schema_cache_key
|
143
|
-
@options.fetch(:schema_cache_key) { schema.schema_cache_key }
|
144
|
-
end
|
145
|
-
|
146
|
-
def query_cache_key
|
147
|
-
@options.fetch(:query_cache_key) { "#{path_cache_key}[#{selections_cache_key}]" }
|
148
|
-
end
|
149
|
-
|
150
|
-
def selections_cache_key
|
151
|
-
current_root =
|
152
|
-
path.reduce(query.lookahead) { |lkhd, field_name|
|
153
|
-
# Handle cached fields inside collections:
|
154
|
-
next lkhd if field_name.is_a?(Integer)
|
155
|
-
|
156
|
-
lkhd.selection_with_alias(field_name)
|
157
|
-
}
|
158
|
-
|
159
|
-
current_root.selections.to_selections_key
|
160
|
-
end
|
161
|
-
|
162
|
-
def path_cache_key
|
163
|
-
@options.fetch(:path_cache_key) do
|
164
|
-
lookahead = query.lookahead
|
165
|
-
|
166
|
-
path.map { |field_name|
|
167
|
-
# Handle cached fields inside collections:
|
168
|
-
next field_name if field_name.is_a?(Integer)
|
169
|
-
|
170
|
-
lookahead = lookahead.selection_with_alias(field_name)
|
171
|
-
raise "Failed to look ahead the field: #{field_name}" if lookahead.is_a?(::GraphQL::Execution::Lookahead::NullLookahead)
|
172
|
-
|
173
|
-
next lookahead.field.name if lookahead.arguments.empty?
|
174
|
-
|
175
|
-
args = lookahead.arguments.map { |_1, _2| "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")
|
176
|
-
"#{lookahead.field.name}(#{args})"
|
177
|
-
}.join("/")
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def traverse_argument(argument)
|
182
|
-
return argument unless argument.is_a?(GraphQL::Schema::InputObject)
|
183
|
-
|
184
|
-
"{#{argument.map { |_1, _2| "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")}}"
|
185
|
-
end
|
186
|
-
|
187
|
-
def object_cache_key
|
188
|
-
@options[:object_cache_key] || object_key(object)
|
189
|
-
end
|
190
|
-
|
191
|
-
def object_key(obj)
|
192
|
-
obj&._graphql_cache_key
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
@@ -1,93 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GraphQL
|
4
|
-
module FragmentCache
|
5
|
-
module Ext
|
6
|
-
# Adds #_graphql_cache_key method to Object,
|
7
|
-
# which just call #graphql_cache_key or #cache_key.
|
8
|
-
#
|
9
|
-
# For other core classes returns string representation.
|
10
|
-
#
|
11
|
-
# Raises ArgumentError otherwise.
|
12
|
-
#
|
13
|
-
# We use a refinement to avoid case/if statements for type checking
|
14
|
-
refine Object do
|
15
|
-
def _graphql_cache_key
|
16
|
-
return graphql_cache_key if respond_to?(:graphql_cache_key)
|
17
|
-
return cache_key if respond_to?(:cache_key)
|
18
|
-
return to_a._graphql_cache_key if respond_to?(:to_a)
|
19
|
-
|
20
|
-
to_s
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
refine Array do
|
25
|
-
def _graphql_cache_key
|
26
|
-
map { |_1| _1._graphql_cache_key }.join("/")
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
refine NilClass do
|
31
|
-
def _graphql_cache_key
|
32
|
-
""
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
refine TrueClass do
|
37
|
-
def _graphql_cache_key
|
38
|
-
"t"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
refine FalseClass do
|
43
|
-
def _graphql_cache_key
|
44
|
-
"f"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
refine String do
|
49
|
-
def _graphql_cache_key
|
50
|
-
self
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
refine Symbol do
|
55
|
-
def _graphql_cache_key
|
56
|
-
to_s
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
if RUBY_PLATFORM.match?(/java/i)
|
61
|
-
refine Integer do
|
62
|
-
def _graphql_cache_key
|
63
|
-
to_s
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
refine Float do
|
68
|
-
def _graphql_cache_key
|
69
|
-
to_s
|
70
|
-
end
|
71
|
-
end
|
72
|
-
else
|
73
|
-
refine Numeric do
|
74
|
-
def _graphql_cache_key
|
75
|
-
to_s
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
refine Time do
|
81
|
-
def _graphql_cache_key
|
82
|
-
to_s
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
refine Module do
|
87
|
-
def _graphql_cache_key
|
88
|
-
name
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|