graphql-fragment_cache 1.15.0 → 1.17.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 +11 -0
- data/README.md +21 -9
- data/lib/.rbnext/2.3/graphql/fragment_cache/cache_key_builder.rb +25 -13
- data/lib/.rbnext/2.7/graphql/fragment_cache/cache_key_builder.rb +25 -13
- data/lib/graphql/fragment_cache/cache_key_builder.rb +25 -13
- data/lib/graphql/fragment_cache/cacher.rb +31 -1
- data/lib/graphql/fragment_cache/field_extension.rb +2 -0
- data/lib/graphql/fragment_cache/object_helpers.rb +3 -0
- data/lib/graphql/fragment_cache/version.rb +1 -1
- data/lib/graphql/fragment_cache.rb +3 -0
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e2c544bc2742643748c5e36c59b3414def73c22e6f7c4b5080504751ea564ecd
|
|
4
|
+
data.tar.gz: b61980525863502ea4f30eeb96fee11dbc11a906ab98b863ed0ece7f9d97d1e9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aac16bb6d2541e573796043ebf2da2ffa171918730ad95f4613db1f6b20cb1156a4a1cb3d70b7e0c39be57a6d56d2cfd3a27d95470c0c316f8f95a756bf83377
|
|
7
|
+
data.tar.gz: 85349b218d754a716db3aa8165e8de64a92b48c22e4b4b1615e612d041000701a090c643fa6b6e68326bb51a3688df2eb71f84604a1bfd19034eb01201453b26
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## master
|
|
4
4
|
|
|
5
|
+
## 1.17.0 (2022-11-09)
|
|
6
|
+
|
|
7
|
+
- [PR#92](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/92) Make cache keys human-readable ([@jeromedalbert][])
|
|
8
|
+
|
|
9
|
+
## 1.16.0 (2022-11-06)
|
|
10
|
+
|
|
11
|
+
- [PR#42](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/42) Raise helpful errors when write or write_multi fails ([@DmitryTsepelev][])
|
|
12
|
+
- [PR#86](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/86) Support passing Procs to `cache_key:` ([@jeromedalbert][])
|
|
13
|
+
- [PR#90](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/90) Add option to disable the cache ([@jeromedalbert][])
|
|
14
|
+
- [PR#89](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/89) Use a "graphql" cache namespace by default ([@jeromedalbert][])
|
|
15
|
+
|
|
5
16
|
## 1.15.0 (2022-10-27)
|
|
6
17
|
|
|
7
18
|
- [PR#43](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/43) Implement `skip_cache_when_query_has_errors` option to skip caching when query was resolved with errors ([@DmitryTsepelev][])
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# GraphQL::FragmentCache  
|
|
2
2
|
|
|
3
3
|
`GraphQL::FragmentCache` powers up [graphql-ruby](https://graphql-ruby.org) with the ability to cache response _fragments_: you can mark any field as cached and it will never be resolved again (at least, while cache is valid). For instance, the following code caches `title` for each post:
|
|
4
4
|
|
|
@@ -9,12 +9,6 @@ class PostType < BaseObject
|
|
|
9
9
|
end
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
<p align="center">
|
|
13
|
-
<a href="https://evilmartians.com/?utm_source=graphql-ruby-fragment_cache">
|
|
14
|
-
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54">
|
|
15
|
-
</a>
|
|
16
|
-
</p>
|
|
17
|
-
|
|
18
12
|
## Getting started
|
|
19
13
|
|
|
20
14
|
Add the gem to your Gemfile `gem 'graphql-fragment_cache'` and add the plugin to your schema class:
|
|
@@ -72,10 +66,10 @@ Cache keys consist of the following parts: namespace, implicit key, and explicit
|
|
|
72
66
|
|
|
73
67
|
### Cache namespace
|
|
74
68
|
|
|
75
|
-
|
|
69
|
+
The namespace is prefixed to every cached key. The default namespace is `graphql`, which is configurable:
|
|
76
70
|
|
|
77
71
|
```ruby
|
|
78
|
-
GraphQL::FragmentCache.namespace = "
|
|
72
|
+
GraphQL::FragmentCache.namespace = "graphql"
|
|
79
73
|
```
|
|
80
74
|
|
|
81
75
|
### Implicit cache key
|
|
@@ -237,6 +231,14 @@ def post(id:)
|
|
|
237
231
|
end
|
|
238
232
|
```
|
|
239
233
|
|
|
234
|
+
If you need more control, you can set `cache_key:` to any custom code:
|
|
235
|
+
|
|
236
|
+
```ruby
|
|
237
|
+
field :posts,
|
|
238
|
+
Types::Objects::PostType.connection_type,
|
|
239
|
+
cache_fragment: {cache_key: -> { object.posts.maximum(:created_at) }}
|
|
240
|
+
```
|
|
241
|
+
|
|
240
242
|
The way cache key part is generated for the passed argument is the following:
|
|
241
243
|
|
|
242
244
|
- Use `object_cache_key: "some_cache_key"` if passed to `cache_fragment`
|
|
@@ -434,6 +436,14 @@ GraphQL::FragmentCache.skip_cache_when_query_has_errors = true
|
|
|
434
436
|
|
|
435
437
|
As a result, caching will be skipped when `errors` array is not empty.
|
|
436
438
|
|
|
439
|
+
## Disabling the cache
|
|
440
|
+
|
|
441
|
+
Cache processing can be disabled if needed. For example:
|
|
442
|
+
|
|
443
|
+
```ruby
|
|
444
|
+
GraphQL::FragmentCache.enabled = false if Rails.env.test?
|
|
445
|
+
```
|
|
446
|
+
|
|
437
447
|
## Limitations
|
|
438
448
|
|
|
439
449
|
1. `Schema#execute`, [graphql-batch](https://github.com/Shopify/graphql-batch) and _graphql-ruby-fragment_cache_ do not [play well](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/issues/45) together. The problem appears when `cache_fragment` is _inside_ the `.then` block:
|
|
@@ -473,6 +483,8 @@ end
|
|
|
473
483
|
|
|
474
484
|
Based on the original [gist](https://gist.github.com/palkan/faad9f6ff1db16fcdb1c071ec50e4190) by [@palkan](https://github.com/palkan) and [@ssnickolay](https://github.com/ssnickolay).
|
|
475
485
|
|
|
486
|
+
Initially sponsored by [Evil Martians](http://evilmartians.com).
|
|
487
|
+
|
|
476
488
|
## Contributing
|
|
477
489
|
|
|
478
490
|
Bug reports and pull requests are welcome on GitHub at [https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache).
|
|
@@ -137,11 +137,17 @@ module GraphQL
|
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
def build
|
|
140
|
-
[
|
|
140
|
+
key_parts = [
|
|
141
141
|
GraphQL::FragmentCache.namespace,
|
|
142
|
+
simple_path_cache_key,
|
|
142
143
|
implicit_cache_key,
|
|
143
144
|
object_cache_key
|
|
144
|
-
]
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
key_parts
|
|
148
|
+
.compact
|
|
149
|
+
.map { |key_part| key_part.tr("/", "-") }
|
|
150
|
+
.join("/")
|
|
145
151
|
end
|
|
146
152
|
|
|
147
153
|
private
|
|
@@ -170,22 +176,28 @@ module GraphQL
|
|
|
170
176
|
current_root.selections.to_selections_key
|
|
171
177
|
end
|
|
172
178
|
|
|
179
|
+
def simple_path_cache_key
|
|
180
|
+
path_cache_key.split("(").first
|
|
181
|
+
end
|
|
182
|
+
|
|
173
183
|
def path_cache_key
|
|
174
|
-
@
|
|
175
|
-
|
|
184
|
+
@path_cache_key ||= begin
|
|
185
|
+
@options.fetch(:path_cache_key) do
|
|
186
|
+
lookahead = query.lookahead
|
|
176
187
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
188
|
+
path.map { |field_name|
|
|
189
|
+
# Handle cached fields inside collections:
|
|
190
|
+
next field_name if field_name.is_a?(Integer)
|
|
180
191
|
|
|
181
|
-
|
|
182
|
-
|
|
192
|
+
lookahead = lookahead.selection_with_alias(field_name)
|
|
193
|
+
raise "Failed to look ahead the field: #{field_name}" if lookahead.is_a?(::GraphQL::Execution::Lookahead::NullLookahead)
|
|
183
194
|
|
|
184
|
-
|
|
195
|
+
next lookahead.field.name if lookahead.arguments.empty?
|
|
185
196
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
args = lookahead.arguments.map { |_1, _2| "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")
|
|
198
|
+
"#{lookahead.field.name}(#{args})"
|
|
199
|
+
}.join("/")
|
|
200
|
+
end
|
|
189
201
|
end
|
|
190
202
|
end
|
|
191
203
|
|
|
@@ -137,11 +137,17 @@ module GraphQL
|
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
def build
|
|
140
|
-
[
|
|
140
|
+
key_parts = [
|
|
141
141
|
GraphQL::FragmentCache.namespace,
|
|
142
|
+
simple_path_cache_key,
|
|
142
143
|
implicit_cache_key,
|
|
143
144
|
object_cache_key
|
|
144
|
-
]
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
key_parts
|
|
148
|
+
.compact
|
|
149
|
+
.map { |key_part| key_part.tr("/", "-") }
|
|
150
|
+
.join("/")
|
|
145
151
|
end
|
|
146
152
|
|
|
147
153
|
private
|
|
@@ -170,22 +176,28 @@ module GraphQL
|
|
|
170
176
|
current_root.selections.to_selections_key
|
|
171
177
|
end
|
|
172
178
|
|
|
179
|
+
def simple_path_cache_key
|
|
180
|
+
path_cache_key.split("(").first
|
|
181
|
+
end
|
|
182
|
+
|
|
173
183
|
def path_cache_key
|
|
174
|
-
@
|
|
175
|
-
|
|
184
|
+
@path_cache_key ||= begin
|
|
185
|
+
@options.fetch(:path_cache_key) do
|
|
186
|
+
lookahead = query.lookahead
|
|
176
187
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
188
|
+
path.map { |field_name|
|
|
189
|
+
# Handle cached fields inside collections:
|
|
190
|
+
next field_name if field_name.is_a?(Integer)
|
|
180
191
|
|
|
181
|
-
|
|
182
|
-
|
|
192
|
+
lookahead = lookahead.selection_with_alias(field_name)
|
|
193
|
+
raise "Failed to look ahead the field: #{field_name}" if lookahead.is_a?(::GraphQL::Execution::Lookahead::NullLookahead)
|
|
183
194
|
|
|
184
|
-
|
|
195
|
+
next lookahead.field.name if lookahead.arguments.empty?
|
|
185
196
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
args = lookahead.arguments.map { |_1, _2| "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")
|
|
198
|
+
"#{lookahead.field.name}(#{args})"
|
|
199
|
+
}.join("/")
|
|
200
|
+
end
|
|
189
201
|
end
|
|
190
202
|
end
|
|
191
203
|
|
|
@@ -137,11 +137,17 @@ module GraphQL
|
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
def build
|
|
140
|
-
[
|
|
140
|
+
key_parts = [
|
|
141
141
|
GraphQL::FragmentCache.namespace,
|
|
142
|
+
simple_path_cache_key,
|
|
142
143
|
implicit_cache_key,
|
|
143
144
|
object_cache_key
|
|
144
|
-
]
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
key_parts
|
|
148
|
+
.compact
|
|
149
|
+
.map { |key_part| key_part.tr("/", "-") }
|
|
150
|
+
.join("/")
|
|
145
151
|
end
|
|
146
152
|
|
|
147
153
|
private
|
|
@@ -170,22 +176,28 @@ module GraphQL
|
|
|
170
176
|
current_root.selections.to_selections_key
|
|
171
177
|
end
|
|
172
178
|
|
|
179
|
+
def simple_path_cache_key
|
|
180
|
+
path_cache_key.split("(").first
|
|
181
|
+
end
|
|
182
|
+
|
|
173
183
|
def path_cache_key
|
|
174
|
-
@
|
|
175
|
-
|
|
184
|
+
@path_cache_key ||= begin
|
|
185
|
+
@options.fetch(:path_cache_key) do
|
|
186
|
+
lookahead = query.lookahead
|
|
176
187
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
188
|
+
path.map { |field_name|
|
|
189
|
+
# Handle cached fields inside collections:
|
|
190
|
+
next field_name if field_name.is_a?(Integer)
|
|
180
191
|
|
|
181
|
-
|
|
182
|
-
|
|
192
|
+
lookahead = lookahead.selection_with_alias(field_name)
|
|
193
|
+
raise "Failed to look ahead the field: #{field_name}" if lookahead.is_a?(::GraphQL::Execution::Lookahead::NullLookahead)
|
|
183
194
|
|
|
184
|
-
|
|
195
|
+
next lookahead.field.name if lookahead.arguments.empty?
|
|
185
196
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
args = lookahead.arguments.map { "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")
|
|
198
|
+
"#{lookahead.field.name}(#{args})"
|
|
199
|
+
}.join("/")
|
|
200
|
+
end
|
|
189
201
|
end
|
|
190
202
|
end
|
|
191
203
|
|
|
@@ -4,6 +4,29 @@ module GraphQL
|
|
|
4
4
|
module FragmentCache
|
|
5
5
|
using Ext
|
|
6
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
|
+
|
|
7
30
|
# Saves resolved fragment values to cache store
|
|
8
31
|
module Cacher
|
|
9
32
|
class << self
|
|
@@ -22,13 +45,20 @@ module GraphQL
|
|
|
22
45
|
def batched_persist(query)
|
|
23
46
|
select_valid_fragments(query).group_by(&:options).each do |options, group|
|
|
24
47
|
hash = group.map { |fragment| [fragment.cache_key, fragment.value] }.to_h
|
|
25
|
-
|
|
48
|
+
|
|
49
|
+
begin
|
|
50
|
+
FragmentCache.cache_store.write_multi(hash, **options)
|
|
51
|
+
rescue => e
|
|
52
|
+
raise WriteMultiError.new(e, hash)
|
|
53
|
+
end
|
|
26
54
|
end
|
|
27
55
|
end
|
|
28
56
|
|
|
29
57
|
def persist(query)
|
|
30
58
|
select_valid_fragments(query).each do |fragment|
|
|
31
59
|
FragmentCache.cache_store.write(fragment.cache_key, fragment.value, **fragment.options)
|
|
60
|
+
rescue => e
|
|
61
|
+
raise WriteError.new(e, fragment.cache_key, fragment.value)
|
|
32
62
|
end
|
|
33
63
|
end
|
|
34
64
|
|
|
@@ -25,6 +25,9 @@ module GraphQL
|
|
|
25
25
|
|
|
26
26
|
def cache_fragment(object_to_cache = NO_OBJECT, **options, &block)
|
|
27
27
|
raise ArgumentError, "Block or argument must be provided" unless block_given? || object_to_cache != NO_OBJECT
|
|
28
|
+
unless GraphQL::FragmentCache.enabled
|
|
29
|
+
return block_given? ? block.call : object_to_cache
|
|
30
|
+
end
|
|
28
31
|
|
|
29
32
|
unless options.delete(:default_options_merged)
|
|
30
33
|
options = GraphQL::FragmentCache.default_options.merge(options)
|
|
@@ -22,6 +22,7 @@ module GraphQL
|
|
|
22
22
|
module FragmentCache
|
|
23
23
|
class << self
|
|
24
24
|
attr_reader :cache_store
|
|
25
|
+
attr_accessor :enabled
|
|
25
26
|
attr_accessor :namespace
|
|
26
27
|
attr_accessor :default_options
|
|
27
28
|
|
|
@@ -87,6 +88,8 @@ module GraphQL
|
|
|
87
88
|
end
|
|
88
89
|
|
|
89
90
|
self.cache_store = MemoryStore.new
|
|
91
|
+
self.enabled = true
|
|
92
|
+
self.namespace = "graphql"
|
|
90
93
|
self.default_options = {}
|
|
91
94
|
self.skip_cache_when_query_has_errors = false
|
|
92
95
|
end
|
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.17.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: 2022-
|
|
11
|
+
date: 2022-11-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: graphql
|
|
@@ -220,7 +220,7 @@ metadata:
|
|
|
220
220
|
homepage_uri: https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache
|
|
221
221
|
source_code_uri: https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache
|
|
222
222
|
changelog_uri: https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/CHANGELOG.md
|
|
223
|
-
post_install_message:
|
|
223
|
+
post_install_message:
|
|
224
224
|
rdoc_options: []
|
|
225
225
|
require_paths:
|
|
226
226
|
- lib
|
|
@@ -235,8 +235,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
235
235
|
- !ruby/object:Gem::Version
|
|
236
236
|
version: '0'
|
|
237
237
|
requirements: []
|
|
238
|
-
rubygems_version: 3.
|
|
239
|
-
signing_key:
|
|
238
|
+
rubygems_version: 3.2.33
|
|
239
|
+
signing_key:
|
|
240
240
|
specification_version: 4
|
|
241
241
|
summary: Fragment cache for graphql-ruby
|
|
242
242
|
test_files: []
|