graphql-fragment_cache 1.22.2 → 1.22.3
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 +10 -1
- data/README.md +4 -1
- data/lib/graphql/fragment_cache/cache_key_builder.rb +5 -5
- data/lib/graphql/fragment_cache/fragment.rb +2 -0
- data/lib/graphql/fragment_cache/schema/instrumentation.rb +0 -7
- data/lib/graphql/fragment_cache/version.rb +1 -1
- metadata +31 -6
- data/lib/.rbnext/2.3/graphql/fragment_cache/cache_key_builder.rb +0 -221
- data/lib/.rbnext/2.3/graphql/fragment_cache/memory_store.rb +0 -65
- data/lib/.rbnext/2.5/graphql/fragment_cache/cacher.rb +0 -71
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 076cc28fcf1a65116b294f3df5422e74a097e00f7568f5265143f6468c5bce35
|
|
4
|
+
data.tar.gz: e20103b9efc1672e5f0df675ba54eedcc6a2c1a0528227f6eb846fa5a61e41b4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bcb941215e44b94fce0f8dee4dfdd6899ecb1f17f017959ac4c98c95ab9e74f6b609ac5a414ca35c80e853eef0aa69cb919b387f08ac7a9a4079c81e772c5087
|
|
7
|
+
data.tar.gz: e10156565aaccda651550b94d27c60d217483ce371e17b325342eb6996801cb952bd05320da1e2bc98a80eb24eb6b3cff82e323280e55cf5acbe569fb26efabc
|
data/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## master
|
|
4
4
|
|
|
5
|
-
## 1.22.
|
|
5
|
+
## 1.22.3 (2026-11-07)
|
|
6
|
+
|
|
7
|
+
- [PR#144](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/144) Fix SystemStackError when cache_fragment is used inside a mutation
|
|
8
|
+
([@LRFalk01][])
|
|
9
|
+
- [PR#99](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/99) Fix for race condition when reading from cache
|
|
10
|
+
([@gsdean][])
|
|
11
|
+
|
|
12
|
+
## 1.22.2 (2025-06-07)
|
|
6
13
|
|
|
7
14
|
- [PR#137](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/137) Remove new_connections? check for gql > 2.3.10 in Tracer ([@DmitryTsepelev][])
|
|
8
15
|
|
|
@@ -226,3 +233,5 @@
|
|
|
226
233
|
[@Drowze]: https://github.com/Drowze
|
|
227
234
|
[@danielhartnell]: https://github.com/danielhartnell
|
|
228
235
|
[@mgruner]: https://github.com/mgruner
|
|
236
|
+
[@gsdean]: https://github.com/gsdean
|
|
237
|
+
[@LRFalk01]: https://github.com/LRFalk01
|
data/README.md
CHANGED
|
@@ -9,7 +9,10 @@ class PostType < BaseObject
|
|
|
9
9
|
end
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
## Professional Support
|
|
13
|
+
|
|
14
|
+
Need help with your GraphQL API architecture, schema design, or performance?
|
|
15
|
+
I'm available for consulting — [get in touch](https://dmitrytsepelev.dev/consulting).
|
|
13
16
|
|
|
14
17
|
## Getting started
|
|
15
18
|
|
|
@@ -46,9 +46,9 @@ module GraphQL
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
refine ::GraphQL::Execution::Lookahead do
|
|
49
|
-
def selection_with_alias(name, **
|
|
50
|
-
return selection(name, **
|
|
51
|
-
alias_selection(name, **
|
|
49
|
+
def selection_with_alias(name, **)
|
|
50
|
+
return selection(name, **) if selects?(name, **)
|
|
51
|
+
alias_selection(name, **)
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
})
|
|
@@ -56,8 +56,8 @@ module GraphQL
|
|
|
56
56
|
# Builds cache key for fragment
|
|
57
57
|
class CacheKeyBuilder
|
|
58
58
|
class << self
|
|
59
|
-
def call(**
|
|
60
|
-
new(**
|
|
59
|
+
def call(**)
|
|
60
|
+
new(**).build
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
@@ -87,6 +87,8 @@ module GraphQL
|
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
def value_from_cache
|
|
90
|
+
return nil unless FragmentCache.cache_store.exist?(cache_key)
|
|
91
|
+
|
|
90
92
|
FragmentCache.cache_store.read(cache_key).tap do |cached|
|
|
91
93
|
return NIL_IN_CACHE if cached.nil? && FragmentCache.cache_store.exist?(cache_key)
|
|
92
94
|
end
|
|
@@ -25,13 +25,6 @@ module GraphQL
|
|
|
25
25
|
|
|
26
26
|
# Instrumentations were deprecated in 2.2.5, this is a module to migrate to new interface
|
|
27
27
|
module Tracer
|
|
28
|
-
def execute_query(query:)
|
|
29
|
-
result = super
|
|
30
|
-
ensure
|
|
31
|
-
GraphQL::FragmentCache::Schema::Instrumentation.after_query(query)
|
|
32
|
-
result
|
|
33
|
-
end
|
|
34
|
-
|
|
35
28
|
def execute_multiplex(multiplex:)
|
|
36
29
|
result = super
|
|
37
30
|
multiplex.queries.each do |query|
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: graphql-fragment_cache
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.22.
|
|
4
|
+
version: 1.22.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- DmitryTsepelev
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: graphql
|
|
@@ -51,6 +51,20 @@ dependencies:
|
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: benchmark
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
54
68
|
- !ruby/object:Gem::Dependency
|
|
55
69
|
name: sqlite3
|
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -149,6 +163,20 @@ dependencies:
|
|
|
149
163
|
- - ">="
|
|
150
164
|
- !ruby/object:Gem::Version
|
|
151
165
|
version: '0'
|
|
166
|
+
- !ruby/object:Gem::Dependency
|
|
167
|
+
name: ostruct
|
|
168
|
+
requirement: !ruby/object:Gem::Requirement
|
|
169
|
+
requirements:
|
|
170
|
+
- - ">="
|
|
171
|
+
- !ruby/object:Gem::Version
|
|
172
|
+
version: '0'
|
|
173
|
+
type: :development
|
|
174
|
+
prerelease: false
|
|
175
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
176
|
+
requirements:
|
|
177
|
+
- - ">="
|
|
178
|
+
- !ruby/object:Gem::Version
|
|
179
|
+
version: '0'
|
|
152
180
|
description: Fragment cache for graphql-ruby
|
|
153
181
|
email:
|
|
154
182
|
- dmitry.a.tsepelev@gmail.com
|
|
@@ -161,9 +189,6 @@ files:
|
|
|
161
189
|
- README.md
|
|
162
190
|
- bin/console
|
|
163
191
|
- bin/setup
|
|
164
|
-
- lib/.rbnext/2.3/graphql/fragment_cache/cache_key_builder.rb
|
|
165
|
-
- lib/.rbnext/2.3/graphql/fragment_cache/memory_store.rb
|
|
166
|
-
- lib/.rbnext/2.5/graphql/fragment_cache/cacher.rb
|
|
167
192
|
- lib/graphql-fragment_cache.rb
|
|
168
193
|
- lib/graphql/fragment_cache.rb
|
|
169
194
|
- lib/graphql/fragment_cache/cache_key_builder.rb
|
|
@@ -205,7 +230,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
205
230
|
- !ruby/object:Gem::Version
|
|
206
231
|
version: '0'
|
|
207
232
|
requirements: []
|
|
208
|
-
rubygems_version:
|
|
233
|
+
rubygems_version: 4.0.3
|
|
209
234
|
specification_version: 4
|
|
210
235
|
summary: Fragment cache for graphql-ruby
|
|
211
236
|
test_files: []
|
|
@@ -1,221 +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 { |name, arg| "#{name}:#{traverse_argument(arg)}" }.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
|
-
field_alias = val.ast_nodes.map(&:alias).join
|
|
26
|
-
field_name = "#{field_alias}:#{field_name}" unless field_alias.empty?
|
|
27
|
-
|
|
28
|
-
unless val.arguments.empty?
|
|
29
|
-
args = val.arguments.map { |name, arg| "#{name}:#{traverse_argument(arg)}" }.sort.join(",")
|
|
30
|
-
field_name += "(#{args})"
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
"#{field_name}#{children}"
|
|
34
|
-
}.join(".")
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
refine ::GraphQL::Language::Nodes::AbstractNode do
|
|
39
|
-
def alias?(_)
|
|
40
|
-
false
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
refine ::GraphQL::Language::Nodes::Field do
|
|
45
|
-
def alias?(val)
|
|
46
|
-
self.alias == val
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
refine ::GraphQL::Execution::Lookahead do
|
|
51
|
-
def selection_with_alias(name, **kwargs)
|
|
52
|
-
return selection(name, **kwargs) if selects?(name, **kwargs)
|
|
53
|
-
alias_selection(name, **kwargs)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
if GraphRubyVersion.before_2_1_4?
|
|
57
|
-
def alias_selection(name, selected_type: @selected_type, arguments: nil)
|
|
58
|
-
return alias_selections[name] if alias_selections.key?(name)
|
|
59
|
-
|
|
60
|
-
alias_node = lookup_alias_node(ast_nodes, name)
|
|
61
|
-
return ::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD unless alias_node
|
|
62
|
-
|
|
63
|
-
next_field_name = alias_node.name
|
|
64
|
-
|
|
65
|
-
# From https://github.com/rmosolgo/graphql-ruby/blob/1a9a20f3da629e63ea8e5ee8400be82218f9edc3/lib/graphql/execution/lookahead.rb#L91
|
|
66
|
-
next_field_defn =
|
|
67
|
-
if GraphRubyVersion.before_2_0?
|
|
68
|
-
get_class_based_field(selected_type, next_field_name)
|
|
69
|
-
else
|
|
70
|
-
@query.get_field(selected_type, next_field_name)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
alias_selections[name] =
|
|
74
|
-
if next_field_defn
|
|
75
|
-
next_nodes = []
|
|
76
|
-
arguments = @query.arguments_for(alias_node, next_field_defn)
|
|
77
|
-
arguments = arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments) ? arguments.keyword_arguments : arguments
|
|
78
|
-
@ast_nodes.each do |ast_node|
|
|
79
|
-
ast_node.selections.each do |selection|
|
|
80
|
-
if GraphRubyVersion.after_2_0_13? && GraphRubyVersion.before_2_1_4?
|
|
81
|
-
find_selected_nodes(selection, next_field_defn, arguments: arguments, matches: next_nodes)
|
|
82
|
-
else
|
|
83
|
-
find_selected_nodes(selection, next_field_name, next_field_defn, arguments: arguments, matches: next_nodes)
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
if next_nodes.any?
|
|
89
|
-
::GraphQL::Execution::Lookahead.new(query: @query, ast_nodes: next_nodes, field: next_field_defn, owner_type: selected_type)
|
|
90
|
-
else
|
|
91
|
-
::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
|
92
|
-
end
|
|
93
|
-
else
|
|
94
|
-
::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def alias_selections
|
|
99
|
-
return @alias_selections if defined?(@alias_selections)
|
|
100
|
-
@alias_selections ||= {}
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def lookup_alias_node(nodes, name)
|
|
104
|
-
return if nodes.empty?
|
|
105
|
-
|
|
106
|
-
nodes.find do |node|
|
|
107
|
-
if node.is_a?(GraphQL::Language::Nodes::FragmentSpread)
|
|
108
|
-
node = @query.fragments[node.name]
|
|
109
|
-
raise("Invariant: Can't look ahead to nonexistent fragment #{node.name} (found: #{@query.fragments.keys})") unless node
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
return node if node.alias?(name)
|
|
113
|
-
child = lookup_alias_node(node.children, name)
|
|
114
|
-
return child if child
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
# Builds cache key for fragment
|
|
122
|
-
class CacheKeyBuilder
|
|
123
|
-
using RubyNext
|
|
124
|
-
|
|
125
|
-
class << self
|
|
126
|
-
def call(**options)
|
|
127
|
-
new(**options).build
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
attr_reader :query, :path, :object, :schema
|
|
132
|
-
|
|
133
|
-
def initialize(query:, path:, object: nil, **options)
|
|
134
|
-
@object = object
|
|
135
|
-
@query = query
|
|
136
|
-
@schema = query.schema
|
|
137
|
-
@path = path
|
|
138
|
-
@options = options
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def build
|
|
142
|
-
key_parts = [
|
|
143
|
-
GraphQL::FragmentCache.namespace,
|
|
144
|
-
simple_path_cache_key,
|
|
145
|
-
implicit_cache_key,
|
|
146
|
-
object_cache_key
|
|
147
|
-
]
|
|
148
|
-
|
|
149
|
-
key_parts
|
|
150
|
-
.compact
|
|
151
|
-
.map { |key_part| key_part.tr("/", "-") }
|
|
152
|
-
.join("/")
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
private
|
|
156
|
-
|
|
157
|
-
def implicit_cache_key
|
|
158
|
-
Digest::SHA1.hexdigest("#{schema_cache_key}/#{query_cache_key}")
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
def schema_cache_key
|
|
162
|
-
@options.fetch(:schema_cache_key) { schema.schema_cache_key }
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
def query_cache_key
|
|
166
|
-
@options.fetch(:query_cache_key) { "#{path_cache_key}[#{selections_cache_key}]" }
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
def selections_cache_key
|
|
170
|
-
current_root =
|
|
171
|
-
path.reduce(query.lookahead) { |lkhd, field_name|
|
|
172
|
-
# Handle cached fields inside collections:
|
|
173
|
-
next lkhd if field_name.is_a?(Integer)
|
|
174
|
-
|
|
175
|
-
lkhd.selection_with_alias(field_name)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
current_root.selections.to_selections_key
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def simple_path_cache_key
|
|
182
|
-
return if path_cache_key.nil?
|
|
183
|
-
|
|
184
|
-
path_cache_key.split("(").first
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
def path_cache_key
|
|
188
|
-
@path_cache_key ||= @options.fetch(:path_cache_key) do
|
|
189
|
-
lookahead = query.lookahead
|
|
190
|
-
|
|
191
|
-
path.map { |field_name|
|
|
192
|
-
# Handle cached fields inside collections:
|
|
193
|
-
next field_name if field_name.is_a?(Integer)
|
|
194
|
-
|
|
195
|
-
lookahead = lookahead.selection_with_alias(field_name)
|
|
196
|
-
raise "Failed to look ahead the field: #{field_name}" if lookahead.is_a?(::GraphQL::Execution::Lookahead::NullLookahead)
|
|
197
|
-
|
|
198
|
-
next lookahead.field.name if lookahead.arguments.empty?
|
|
199
|
-
|
|
200
|
-
args = lookahead.arguments.map { |name, arg| "#{name}:#{traverse_argument(arg)}" }.sort.join(",")
|
|
201
|
-
"#{lookahead.field.name}(#{args})"
|
|
202
|
-
}.join("/")
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
def traverse_argument(argument)
|
|
207
|
-
return argument unless argument.is_a?(GraphQL::Schema::InputObject)
|
|
208
|
-
|
|
209
|
-
"{#{argument.map { |name, arg| "#{name}:#{traverse_argument(arg)}" }.sort.join(",")}}"
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
def object_cache_key
|
|
213
|
-
@options[:object_cache_key] || object_key(object)
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
def object_key(obj)
|
|
217
|
-
((((__safe_lvar__ = obj) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__._graphql_cache_key)
|
|
218
|
-
end
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
end
|
|
@@ -1,65 +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
|
-
((((__safe_lvar__ = storage[key]) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.then { |entry|
|
|
37
|
-
if entry.expired?
|
|
38
|
-
delete(key)
|
|
39
|
-
next
|
|
40
|
-
end
|
|
41
|
-
entry.value
|
|
42
|
-
})
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def write(key, value, options = {})
|
|
46
|
-
expires_in = options[:expires_in] || default_expires_in
|
|
47
|
-
key = key.to_s
|
|
48
|
-
@storage[key] = Entry.new(value: value, expires_at: expires_in ? Time.now + expires_in : nil)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def delete(key)
|
|
52
|
-
key = key.to_s
|
|
53
|
-
storage.delete(key)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def clear
|
|
57
|
-
storage.clear
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
private
|
|
61
|
-
|
|
62
|
-
attr_reader :storage
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module GraphQL
|
|
4
|
-
module FragmentCache
|
|
5
|
-
using Ext
|
|
6
|
-
|
|
7
|
-
class WriteError < StandardError
|
|
8
|
-
attr_reader :key, :value, :original_error
|
|
9
|
-
|
|
10
|
-
def initialize(original_error, key, value)
|
|
11
|
-
@original_error = original_error
|
|
12
|
-
@key = key
|
|
13
|
-
@value = value
|
|
14
|
-
|
|
15
|
-
super(original_error.message)
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
class WriteMultiError < StandardError
|
|
20
|
-
attr_reader :values, :original_error
|
|
21
|
-
|
|
22
|
-
def initialize(original_error, values)
|
|
23
|
-
@original_error = original_error
|
|
24
|
-
@values = values
|
|
25
|
-
|
|
26
|
-
super(original_error.message)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Saves resolved fragment values to cache store
|
|
31
|
-
module Cacher
|
|
32
|
-
class << self
|
|
33
|
-
def call(query)
|
|
34
|
-
return unless query.context.fragments?
|
|
35
|
-
|
|
36
|
-
if FragmentCache.cache_store.respond_to?(:write_multi)
|
|
37
|
-
batched_persist(query)
|
|
38
|
-
else
|
|
39
|
-
persist(query)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
private
|
|
44
|
-
|
|
45
|
-
def batched_persist(query)
|
|
46
|
-
select_valid_fragments(query).group_by(&:options).each do |options, group|
|
|
47
|
-
hash = group.map { |fragment| [fragment.cache_key, fragment.value] }.to_h
|
|
48
|
-
|
|
49
|
-
begin
|
|
50
|
-
FragmentCache.cache_store.write_multi(hash, options)
|
|
51
|
-
rescue => e
|
|
52
|
-
raise WriteMultiError.new(e, hash)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def persist(query)
|
|
58
|
-
select_valid_fragments(query).each do |fragment|
|
|
59
|
-
begin;FragmentCache.cache_store.write(fragment.cache_key, fragment.value, fragment.options)
|
|
60
|
-
rescue => e
|
|
61
|
-
raise WriteError.new(e, fragment.cache_key, fragment.value), e;end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def select_valid_fragments(query)
|
|
66
|
-
query.context.fragments.select(&:with_final_value?)
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|