graphql-fragment_cache 0.1.7 → 1.0.4
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 +22 -0
- data/README.md +13 -1
- data/lib/.rbnext/2.3/graphql/fragment_cache/memory_store.rb +64 -0
- data/lib/.rbnext/2.7/graphql/fragment_cache/cache_key_builder.rb +8 -2
- data/lib/graphql/fragment_cache.rb +4 -0
- data/lib/graphql/fragment_cache/cache_key_builder.rb +8 -2
- data/lib/graphql/fragment_cache/connections/patch.rb +26 -0
- data/lib/graphql/fragment_cache/fragment.rb +6 -10
- data/lib/graphql/fragment_cache/memory_store.rb +8 -0
- data/lib/graphql/fragment_cache/object_helpers.rb +1 -18
- data/lib/graphql/fragment_cache/schema/tracer.rb +3 -12
- data/lib/graphql/fragment_cache/version.rb +1 -1
- metadata +22 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a52d1b703c963cd27803c1c28ca0e8c1a66775a84ad8963f88a0e414ec7ed6c
|
4
|
+
data.tar.gz: 4739e71ae750d7fbb8c9366e17da84ad9ae921409308a1b109fc60d8add0557a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 889510e3937c874742624f6da651914ed98c6a996842ec57fcda8121287eead193e12b974247733b38ffc7662e542eb1a82b2fb43d18ef5a121cc41607f811fb
|
7
|
+
data.tar.gz: 4e9dbeb855f55bf37fbe14900b7c4b1b398155902759fd905a87c70da6dfd67850102be48c5b350d6b3126090ced6aaf0bb5b95a7f16a990dd8b070b589e865c
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,27 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 1.0.4 (2020-10-12)
|
6
|
+
|
7
|
+
- [PR#34](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/34) Avoid unneded default calculation in CacheKeyBuilder ([@DmitryTsepelev][])
|
8
|
+
- [PR#31](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/31) Do not patch Connection#wrap in graphql >= 1.10.5 ([@DmitryTsepelev][])
|
9
|
+
|
10
|
+
## 1.0.3 (2020-08-31)
|
11
|
+
|
12
|
+
- [PR#29](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/29) Cache result JSON instead of connection objects ([@DmitryTsepelev][])
|
13
|
+
|
14
|
+
## 1.0.2 (2020-08-19)
|
15
|
+
|
16
|
+
- [PR#28](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/28) Support #keys method for GraphQL::FragmentCache::MemoryStore instance ([@reabiliti][])
|
17
|
+
|
18
|
+
## 1.0.1 (2020-06-17)
|
19
|
+
|
20
|
+
- [PR#25](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/25) Support fragments with aliases for CacheKeyBuilder ([@DmitryTsepelev][])
|
21
|
+
|
22
|
+
## 1.0.0 (2020-06-13)
|
23
|
+
|
24
|
+
- [PR#24](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/24) Add nil caching. **BREAKING CHANGE**: custom cache stores must also implement `#exist?(key)` method ([@DmitryTsepelev][])
|
25
|
+
|
5
26
|
## 0.1.7 (2020-06-02)
|
6
27
|
|
7
28
|
- [PR#23](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/23) Avoid extra queries after restoring connection from cache ([@DmitryTsepelev][])
|
@@ -39,3 +60,4 @@
|
|
39
60
|
[@DmitryTsepelev]: https://github.com/DmitryTsepelev
|
40
61
|
[@palkan]: https://github.com/palkan
|
41
62
|
[@ssnickolay]: https://github.com/ssnickolay
|
63
|
+
[@reabiliti]: https://github.com/reabiliti
|
data/README.md
CHANGED
@@ -252,7 +252,7 @@ Rails.application.configure do |config|
|
|
252
252
|
end
|
253
253
|
```
|
254
254
|
|
255
|
-
⚠️ Cache store must implement `#read(key)
|
255
|
+
⚠️ Cache store must implement `#read(key)`, `#write(key, value, **options)` and `#exist?(key)` methods.
|
256
256
|
|
257
257
|
The gem provides only in-memory store out-of-the-box (`GraphQL::FragmentCache::MemoryStore`). It's used by default.
|
258
258
|
|
@@ -275,6 +275,18 @@ class QueryType < BaseObject
|
|
275
275
|
end
|
276
276
|
```
|
277
277
|
|
278
|
+
## Limitations
|
279
|
+
|
280
|
+
Caching does not work for Union types, because of the `Lookahead` implementation: it requires the exact type to be passed to the `selection` method (you can find the [discussion](https://github.com/rmosolgo/graphql-ruby/pull/3007) here). This method is used for cache key building, and I haven't found a workaround yet ([PR in progress](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/30)). If you get `Failed to look ahead the field` error — please pass `query_cache_key` explicitly:
|
281
|
+
|
282
|
+
```ruby
|
283
|
+
field :cached_avatar_url, String, null: false
|
284
|
+
|
285
|
+
def cached_avatar_url
|
286
|
+
cache_fragment(query_cache_key: "post_avatar_url(#{object.id})") { object.avatar_url }
|
287
|
+
end
|
288
|
+
```
|
289
|
+
|
278
290
|
## Credits
|
279
291
|
|
280
292
|
Based on the original [gist](https://gist.github.com/palkan/faad9f6ff1db16fcdb1c071ec50e4190) by [@palkan](https://github.com/palkan) and [@ssnickolay](https://github.com/ssnickolay).
|
@@ -0,0 +1,64 @@
|
|
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
|
@@ -76,7 +76,13 @@ module GraphQL
|
|
76
76
|
|
77
77
|
def lookup_alias_node(nodes, name)
|
78
78
|
return if nodes.empty?
|
79
|
+
|
79
80
|
nodes.find do |node|
|
81
|
+
if node.is_a?(GraphQL::Language::Nodes::FragmentSpread)
|
82
|
+
node = @query.fragments[node.name]
|
83
|
+
raise("Invariant: Can't look ahead to nonexistent fragment #{node.name} (found: #{@query.fragments.keys})") unless node
|
84
|
+
end
|
85
|
+
|
80
86
|
return node if node.alias?(name)
|
81
87
|
child = lookup_alias_node(node.children, name)
|
82
88
|
return child if child
|
@@ -115,11 +121,11 @@ module GraphQL
|
|
115
121
|
private
|
116
122
|
|
117
123
|
def schema_cache_key
|
118
|
-
@options.fetch(:schema_cache_key
|
124
|
+
@options.fetch(:schema_cache_key) { schema.schema_cache_key }
|
119
125
|
end
|
120
126
|
|
121
127
|
def query_cache_key
|
122
|
-
@options.fetch(:query_cache_key
|
128
|
+
@options.fetch(:query_cache_key) { "#{path_cache_key}[#{selections_cache_key}]" }
|
123
129
|
end
|
124
130
|
|
125
131
|
def selections_cache_key
|
@@ -6,6 +6,8 @@ require "graphql/fragment_cache/ext/context_fragments"
|
|
6
6
|
require "graphql/fragment_cache/ext/graphql_cache_key"
|
7
7
|
require "graphql/fragment_cache/object"
|
8
8
|
|
9
|
+
require "graphql/fragment_cache/connections/patch"
|
10
|
+
|
9
11
|
require "graphql/fragment_cache/schema/patch"
|
10
12
|
require "graphql/fragment_cache/schema/tracer"
|
11
13
|
require "graphql/fragment_cache/schema/instrumentation"
|
@@ -26,6 +28,8 @@ module GraphQL
|
|
26
28
|
schema_defn.tracer(Schema::Tracer)
|
27
29
|
schema_defn.instrument(:query, Schema::Instrumentation)
|
28
30
|
schema_defn.extend(Schema::Patch)
|
31
|
+
|
32
|
+
GraphQL::Pagination::Connections.prepend(Connections::Patch)
|
29
33
|
end
|
30
34
|
|
31
35
|
def cache_store=(store)
|
@@ -76,7 +76,13 @@ module GraphQL
|
|
76
76
|
|
77
77
|
def lookup_alias_node(nodes, name)
|
78
78
|
return if nodes.empty?
|
79
|
+
|
79
80
|
nodes.find do |node|
|
81
|
+
if node.is_a?(GraphQL::Language::Nodes::FragmentSpread)
|
82
|
+
node = @query.fragments[node.name]
|
83
|
+
raise("Invariant: Can't look ahead to nonexistent fragment #{node.name} (found: #{@query.fragments.keys})") unless node
|
84
|
+
end
|
85
|
+
|
80
86
|
return node if node.alias?(name)
|
81
87
|
child = lookup_alias_node(node.children, name)
|
82
88
|
return child if child
|
@@ -115,11 +121,11 @@ module GraphQL
|
|
115
121
|
private
|
116
122
|
|
117
123
|
def schema_cache_key
|
118
|
-
@options.fetch(:schema_cache_key
|
124
|
+
@options.fetch(:schema_cache_key) { schema.schema_cache_key }
|
119
125
|
end
|
120
126
|
|
121
127
|
def query_cache_key
|
122
|
-
@options.fetch(:query_cache_key
|
128
|
+
@options.fetch(:query_cache_key) { "#{path_cache_key}[#{selections_cache_key}]" }
|
123
129
|
end
|
124
130
|
|
125
131
|
def selections_cache_key
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module FragmentCache
|
5
|
+
module Connections
|
6
|
+
# Patches GraphQL::Pagination::Connections to support raw values
|
7
|
+
module Patch
|
8
|
+
if Gem::Dependency.new("graphql", "< 1.11.0").match?("graphql", GraphQL::VERSION)
|
9
|
+
def wrap(field, object, arguments, context, *options)
|
10
|
+
raw_value?(object) ? object : super
|
11
|
+
end
|
12
|
+
elsif Gem::Dependency.new("graphql", "< 1.11.5").match?("graphql", GraphQL::VERSION)
|
13
|
+
def wrap(field, parent, items, arguments, context, *options)
|
14
|
+
raw_value?(items) ? items : super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def raw_value?(value)
|
21
|
+
GraphQL::Execution::Interpreter::RawValue === value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -8,22 +8,22 @@ module GraphQL
|
|
8
8
|
class Fragment
|
9
9
|
attr_reader :options, :path, :context
|
10
10
|
|
11
|
-
attr_accessor :resolved_value
|
12
|
-
|
13
11
|
def initialize(context, **options)
|
14
12
|
@context = context
|
15
13
|
@options = options
|
16
14
|
@path = interpreter_context[:current_path]
|
17
15
|
end
|
18
16
|
|
17
|
+
NIL_IN_CACHE = Object.new
|
18
|
+
|
19
19
|
def read
|
20
|
-
FragmentCache.cache_store.read(cache_key)
|
20
|
+
FragmentCache.cache_store.read(cache_key).tap do |cached|
|
21
|
+
return NIL_IN_CACHE if cached.nil? && FragmentCache.cache_store.exist?(cache_key)
|
22
|
+
end
|
21
23
|
end
|
22
24
|
|
23
25
|
def persist
|
24
|
-
|
25
|
-
# we rely on Schema::Tracer to save it for us
|
26
|
-
value = resolved_value || resolve_from_runtime
|
26
|
+
value = final_value.dig(*path)
|
27
27
|
FragmentCache.cache_store.write(cache_key, value, **options)
|
28
28
|
end
|
29
29
|
|
@@ -37,10 +37,6 @@ module GraphQL
|
|
37
37
|
context.namespace(:interpreter)
|
38
38
|
end
|
39
39
|
|
40
|
-
def resolve_from_runtime
|
41
|
-
final_value.dig(*path)
|
42
|
-
end
|
43
|
-
|
44
40
|
def final_value
|
45
41
|
@final_value ||= interpreter_context[:runtime].final_value
|
46
42
|
end
|
@@ -12,8 +12,6 @@ module GraphQL
|
|
12
12
|
|
13
13
|
NO_OBJECT = Object.new
|
14
14
|
|
15
|
-
def_delegator :field, :connection?
|
16
|
-
|
17
15
|
def cache_fragment(object_to_cache = NO_OBJECT, **options, &block)
|
18
16
|
raise ArgumentError, "Block or argument must be provided" unless block_given? || object_to_cache != NO_OBJECT
|
19
17
|
|
@@ -22,28 +20,13 @@ module GraphQL
|
|
22
20
|
fragment = Fragment.new(context, options)
|
23
21
|
|
24
22
|
if (cached = fragment.read)
|
25
|
-
return
|
23
|
+
return cached == Fragment::NIL_IN_CACHE ? nil : raw_value(cached)
|
26
24
|
end
|
27
25
|
|
28
26
|
(block_given? ? block.call : object_to_cache).tap do |resolved_value|
|
29
27
|
context.fragments << fragment
|
30
28
|
end
|
31
29
|
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def restore_cached_value(cached)
|
36
|
-
# If we return connection object from resolver, Interpreter stops processing it
|
37
|
-
connection? ? cached : raw_value(cached)
|
38
|
-
end
|
39
|
-
|
40
|
-
def field
|
41
|
-
interpreter_context[:current_field]
|
42
|
-
end
|
43
|
-
|
44
|
-
def interpreter_context
|
45
|
-
@interpreter_context ||= context.namespace(:interpreter)
|
46
|
-
end
|
47
30
|
end
|
48
31
|
end
|
49
32
|
end
|
@@ -10,18 +10,15 @@ module GraphQL
|
|
10
10
|
class << self
|
11
11
|
def trace(key, data)
|
12
12
|
yield.tap do |resolved_value|
|
13
|
-
next unless
|
13
|
+
next unless connection_field?(key, data)
|
14
14
|
|
15
|
-
|
16
|
-
context = data[:query].context
|
17
|
-
verify_connections!(context)
|
18
|
-
cache_connection(resolved_value, context)
|
15
|
+
verify_connections!(data[:query].context)
|
19
16
|
end
|
20
17
|
end
|
21
18
|
|
22
19
|
private
|
23
20
|
|
24
|
-
def
|
21
|
+
def connection_field?(key, data)
|
25
22
|
key == "execute_field" && data[:field].connection?
|
26
23
|
end
|
27
24
|
|
@@ -31,12 +28,6 @@ module GraphQL
|
|
31
28
|
raise StandardError,
|
32
29
|
"GraphQL::Pagination::Connections should be enabled for connection caching"
|
33
30
|
end
|
34
|
-
|
35
|
-
def cache_connection(resolved_value, context)
|
36
|
-
current_path = context.namespace(:interpreter)[:current_path]
|
37
|
-
fragment = context.fragments.find { |fragment| fragment.path == current_path }
|
38
|
-
fragment.resolved_value = resolved_value if fragment
|
39
|
-
end
|
40
31
|
end
|
41
32
|
end
|
42
33
|
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: 0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- DmitryTsepelev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.10.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
40
|
+
version: 0.10.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: combustion
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,14 +128,28 @@ dependencies:
|
|
128
128
|
requirements:
|
129
129
|
- - ">="
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: '0.
|
131
|
+
version: '0.10'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '0.
|
138
|
+
version: '0.10'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: unparser
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.4.9
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.4.9
|
139
153
|
description: Fragment cache for graphql-ruby
|
140
154
|
email:
|
141
155
|
- dmitry.a.tsepelev@gmail.com
|
@@ -148,12 +162,14 @@ files:
|
|
148
162
|
- README.md
|
149
163
|
- bin/console
|
150
164
|
- bin/setup
|
165
|
+
- lib/.rbnext/2.3/graphql/fragment_cache/memory_store.rb
|
151
166
|
- lib/.rbnext/2.7/graphql/fragment_cache/cache_key_builder.rb
|
152
167
|
- lib/.rbnext/2.7/graphql/fragment_cache/ext/graphql_cache_key.rb
|
153
168
|
- lib/graphql-fragment_cache.rb
|
154
169
|
- lib/graphql/fragment_cache.rb
|
155
170
|
- lib/graphql/fragment_cache/cache_key_builder.rb
|
156
171
|
- lib/graphql/fragment_cache/cacher.rb
|
172
|
+
- lib/graphql/fragment_cache/connections/patch.rb
|
157
173
|
- lib/graphql/fragment_cache/ext/context_fragments.rb
|
158
174
|
- lib/graphql/fragment_cache/ext/graphql_cache_key.rb
|
159
175
|
- lib/graphql/fragment_cache/field_extension.rb
|