graphql-fragment_cache 0.1.3 → 1.0.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 +21 -0
- data/README.md +10 -5
- data/lib/.rbnext/2.7/graphql/fragment_cache/cache_key_builder.rb +78 -4
- data/lib/graphql-fragment_cache.rb +1 -1
- data/lib/graphql/fragment_cache.rb +7 -5
- data/lib/graphql/fragment_cache/cache_key_builder.rb +78 -4
- data/lib/graphql/fragment_cache/cacher.rb +1 -3
- data/lib/graphql/fragment_cache/fragment.rb +21 -5
- data/lib/graphql/fragment_cache/memory_store.rb +4 -0
- data/lib/graphql/fragment_cache/object_helpers.rb +24 -3
- data/lib/graphql/fragment_cache/railtie.rb +1 -1
- data/lib/graphql/fragment_cache/schema/instrumentation.rb +23 -0
- data/lib/graphql/fragment_cache/schema/patch.rb +16 -0
- data/lib/graphql/fragment_cache/schema/tracer.rb +44 -0
- data/lib/graphql/fragment_cache/version.rb +1 -1
- metadata +38 -10
- data/lib/.rbnext/2.7/graphql/fragment_cache/cacher.rb +0 -20
- data/lib/graphql/fragment_cache/instrumentation.rb +0 -21
- data/lib/graphql/fragment_cache/schema_patch.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96ef4de68daa875700330eed182cbbfeffb9c4dfff34dafd3661fc3513e06fc6
|
4
|
+
data.tar.gz: f24010337c683b12498c8c1cd949bb4ba43cb97dae80d9f617b0694a78329fdf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b5d84c411a488bac34c2e0c2237c3dcc7b51e44bd60195560ba81a8ee2cab6fd8d888f8946a103225b5dd14dc3202da4167a09901ad89264dee0bb3b34633fbd
|
7
|
+
data.tar.gz: 03bfa7468e8199ca87d81bcd8839f9dab0a7c6b4764538757727ad20d641c30da380af9b55a5496ef7ab77dafd444b2a8a39bd44d983d6e8eb53c0c63821f88f
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,27 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 1.0.0 (2020-06-13)
|
6
|
+
|
7
|
+
- [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][])
|
8
|
+
|
9
|
+
## 0.1.7 (2020-06-02)
|
10
|
+
|
11
|
+
- [PR#23](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/23) Avoid extra queries after restoring connection from cache ([@DmitryTsepelev][])
|
12
|
+
|
13
|
+
## 0.1.6 (2020-05-30)
|
14
|
+
|
15
|
+
- [PR#22](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/22) Properly cache entites inside collections ([@DmitryTsepelev][])
|
16
|
+
|
17
|
+
## 0.1.5 (2020-04-28)
|
18
|
+
|
19
|
+
- [PR#19](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/19) Add connections support ([@DmitryTsepelev][])
|
20
|
+
- [PR#18](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/18) Support aliases in cache key generation ([@palkan][], [@DmitryTsepelev][])
|
21
|
+
|
22
|
+
## 0.1.4 (2020-04-25)
|
23
|
+
|
24
|
+
- Fix railtie to set up null store for tests ([@DmitryTsepelev][])
|
25
|
+
|
5
26
|
## 0.1.3 (2020-04-24)
|
6
27
|
|
7
28
|
- [PR#17](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/17) Properly build cache keys based on input arguments ([@DmitryTsepelev][])
|
data/README.md
CHANGED
@@ -61,6 +61,15 @@ class QueryType < BaseObject
|
|
61
61
|
end
|
62
62
|
```
|
63
63
|
|
64
|
+
If you use [connections](https://graphql-ruby.org/pagination/connection_concepts.html) and plan to cache them—please turn on [brand new](https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/pagination/connections.rb#L5) connections hierarchy in your schema:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
class GraphqSchema < GraphQL::Schema
|
68
|
+
# ...
|
69
|
+
use GraphQL::Pagination::Connections
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
64
73
|
## Cache key generation
|
65
74
|
|
66
75
|
Cache keys consist of implicit and explicit (provided by user) parts.
|
@@ -243,7 +252,7 @@ Rails.application.configure do |config|
|
|
243
252
|
end
|
244
253
|
```
|
245
254
|
|
246
|
-
⚠️ Cache store must implement `#read(key)
|
255
|
+
⚠️ Cache store must implement `#read(key)`, `#write(key, value, **options)` and `#exist?(key)` methods.
|
247
256
|
|
248
257
|
The gem provides only in-memory store out-of-the-box (`GraphQL::FragmentCache::MemoryStore`). It's used by default.
|
249
258
|
|
@@ -266,10 +275,6 @@ class QueryType < BaseObject
|
|
266
275
|
end
|
267
276
|
```
|
268
277
|
|
269
|
-
## Limitations
|
270
|
-
|
271
|
-
- [Field aliases](https://spec.graphql.org/June2018/#sec-Field-Alias) are not currently supported (take a look at the failing spec [here](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/7))
|
272
|
-
|
273
278
|
## Credits
|
274
279
|
|
275
280
|
Based on the original [gist](https://gist.github.com/palkan/faad9f6ff1db16fcdb1c071ec50e4190) by [@palkan](https://github.com/palkan) and [@ssnickolay](https://github.com/ssnickolay).
|
@@ -18,6 +18,71 @@ module GraphQL
|
|
18
18
|
}.join(".")
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
refine ::GraphQL::Language::Nodes::AbstractNode do
|
23
|
+
def alias?(_)
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
refine ::GraphQL::Language::Nodes::Field do
|
29
|
+
def alias?(val)
|
30
|
+
self.alias == val
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
refine ::GraphQL::Execution::Lookahead do
|
35
|
+
def selection_with_alias(name, **kwargs)
|
36
|
+
return selection(name, **kwargs) if selects?(name, **kwargs)
|
37
|
+
alias_selection(name, **kwargs)
|
38
|
+
end
|
39
|
+
|
40
|
+
def alias_selection(name, selected_type: @selected_type, arguments: nil)
|
41
|
+
return alias_selections[name] if alias_selections.key?(name)
|
42
|
+
|
43
|
+
alias_node = lookup_alias_node(ast_nodes, name)
|
44
|
+
return ::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD unless alias_node
|
45
|
+
|
46
|
+
next_field_name = alias_node.name
|
47
|
+
|
48
|
+
# From https://github.com/rmosolgo/graphql-ruby/blob/1a9a20f3da629e63ea8e5ee8400be82218f9edc3/lib/graphql/execution/lookahead.rb#L91
|
49
|
+
next_field_defn = get_class_based_field(selected_type, next_field_name)
|
50
|
+
|
51
|
+
alias_selections[name] =
|
52
|
+
if next_field_defn
|
53
|
+
next_nodes = []
|
54
|
+
arguments = @query.arguments_for(alias_node, next_field_defn)
|
55
|
+
arguments = arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments) ? arguments.keyword_arguments : arguments
|
56
|
+
@ast_nodes.each do |ast_node|
|
57
|
+
ast_node.selections.each do |selection|
|
58
|
+
find_selected_nodes(selection, next_field_name, next_field_defn, arguments: arguments, matches: next_nodes)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if next_nodes.any?
|
63
|
+
::GraphQL::Execution::Lookahead.new(query: @query, ast_nodes: next_nodes, field: next_field_defn, owner_type: selected_type)
|
64
|
+
else
|
65
|
+
::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
66
|
+
end
|
67
|
+
else
|
68
|
+
::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def alias_selections
|
73
|
+
return @alias_selections if defined?(@alias_selections)
|
74
|
+
@alias_selections ||= {}
|
75
|
+
end
|
76
|
+
|
77
|
+
def lookup_alias_node(nodes, name)
|
78
|
+
return if nodes.empty?
|
79
|
+
nodes.find do |node|
|
80
|
+
return node if node.alias?(name)
|
81
|
+
child = lookup_alias_node(node.children, name)
|
82
|
+
return child if child
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
21
86
|
})
|
22
87
|
|
23
88
|
# Builds cache key for fragment
|
@@ -59,7 +124,12 @@ module GraphQL
|
|
59
124
|
|
60
125
|
def selections_cache_key
|
61
126
|
current_root =
|
62
|
-
path.reduce(query.lookahead) { |lkhd,
|
127
|
+
path.reduce(query.lookahead) { |lkhd, field_name|
|
128
|
+
# Handle cached fields inside collections:
|
129
|
+
next lkhd if field_name.is_a?(Integer)
|
130
|
+
|
131
|
+
lkhd.selection_with_alias(field_name)
|
132
|
+
}
|
63
133
|
|
64
134
|
current_root.selections.to_selections_key
|
65
135
|
end
|
@@ -68,12 +138,16 @@ module GraphQL
|
|
68
138
|
lookahead = query.lookahead
|
69
139
|
|
70
140
|
path.map { |field_name|
|
71
|
-
|
141
|
+
# Handle cached fields inside collections:
|
142
|
+
next field_name if field_name.is_a?(Integer)
|
143
|
+
|
144
|
+
lookahead = lookahead.selection_with_alias(field_name)
|
145
|
+
raise "Failed to look ahead the field: #{field_name}" if lookahead.is_a?(::GraphQL::Execution::Lookahead::NullLookahead)
|
72
146
|
|
73
|
-
next
|
147
|
+
next lookahead.field.name if lookahead.arguments.empty?
|
74
148
|
|
75
149
|
args = lookahead.arguments.map { |_1, _2| "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")
|
76
|
-
"#{
|
150
|
+
"#{lookahead.field.name}(#{args})"
|
77
151
|
}.join("/")
|
78
152
|
end
|
79
153
|
|
@@ -4,10 +4,11 @@ require "graphql"
|
|
4
4
|
|
5
5
|
require "graphql/fragment_cache/ext/context_fragments"
|
6
6
|
require "graphql/fragment_cache/ext/graphql_cache_key"
|
7
|
-
|
8
|
-
require "graphql/fragment_cache/schema_patch"
|
9
7
|
require "graphql/fragment_cache/object"
|
10
|
-
|
8
|
+
|
9
|
+
require "graphql/fragment_cache/schema/patch"
|
10
|
+
require "graphql/fragment_cache/schema/tracer"
|
11
|
+
require "graphql/fragment_cache/schema/instrumentation"
|
11
12
|
|
12
13
|
require "graphql/fragment_cache/memory_store"
|
13
14
|
|
@@ -22,8 +23,9 @@ module GraphQL
|
|
22
23
|
def use(schema_defn, options = {})
|
23
24
|
verify_interpreter!(schema_defn)
|
24
25
|
|
25
|
-
schema_defn.
|
26
|
-
schema_defn.
|
26
|
+
schema_defn.tracer(Schema::Tracer)
|
27
|
+
schema_defn.instrument(:query, Schema::Instrumentation)
|
28
|
+
schema_defn.extend(Schema::Patch)
|
27
29
|
end
|
28
30
|
|
29
31
|
def cache_store=(store)
|
@@ -18,6 +18,71 @@ module GraphQL
|
|
18
18
|
}.join(".")
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
refine ::GraphQL::Language::Nodes::AbstractNode do
|
23
|
+
def alias?(_)
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
refine ::GraphQL::Language::Nodes::Field do
|
29
|
+
def alias?(val)
|
30
|
+
self.alias == val
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
refine ::GraphQL::Execution::Lookahead do
|
35
|
+
def selection_with_alias(name, **kwargs)
|
36
|
+
return selection(name, **kwargs) if selects?(name, **kwargs)
|
37
|
+
alias_selection(name, **kwargs)
|
38
|
+
end
|
39
|
+
|
40
|
+
def alias_selection(name, selected_type: @selected_type, arguments: nil)
|
41
|
+
return alias_selections[name] if alias_selections.key?(name)
|
42
|
+
|
43
|
+
alias_node = lookup_alias_node(ast_nodes, name)
|
44
|
+
return ::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD unless alias_node
|
45
|
+
|
46
|
+
next_field_name = alias_node.name
|
47
|
+
|
48
|
+
# From https://github.com/rmosolgo/graphql-ruby/blob/1a9a20f3da629e63ea8e5ee8400be82218f9edc3/lib/graphql/execution/lookahead.rb#L91
|
49
|
+
next_field_defn = get_class_based_field(selected_type, next_field_name)
|
50
|
+
|
51
|
+
alias_selections[name] =
|
52
|
+
if next_field_defn
|
53
|
+
next_nodes = []
|
54
|
+
arguments = @query.arguments_for(alias_node, next_field_defn)
|
55
|
+
arguments = arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments) ? arguments.keyword_arguments : arguments
|
56
|
+
@ast_nodes.each do |ast_node|
|
57
|
+
ast_node.selections.each do |selection|
|
58
|
+
find_selected_nodes(selection, next_field_name, next_field_defn, arguments: arguments, matches: next_nodes)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if next_nodes.any?
|
63
|
+
::GraphQL::Execution::Lookahead.new(query: @query, ast_nodes: next_nodes, field: next_field_defn, owner_type: selected_type)
|
64
|
+
else
|
65
|
+
::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
66
|
+
end
|
67
|
+
else
|
68
|
+
::GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def alias_selections
|
73
|
+
return @alias_selections if defined?(@alias_selections)
|
74
|
+
@alias_selections ||= {}
|
75
|
+
end
|
76
|
+
|
77
|
+
def lookup_alias_node(nodes, name)
|
78
|
+
return if nodes.empty?
|
79
|
+
nodes.find do |node|
|
80
|
+
return node if node.alias?(name)
|
81
|
+
child = lookup_alias_node(node.children, name)
|
82
|
+
return child if child
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
21
86
|
})
|
22
87
|
|
23
88
|
# Builds cache key for fragment
|
@@ -59,7 +124,12 @@ module GraphQL
|
|
59
124
|
|
60
125
|
def selections_cache_key
|
61
126
|
current_root =
|
62
|
-
path.reduce(query.lookahead) { |lkhd,
|
127
|
+
path.reduce(query.lookahead) { |lkhd, field_name|
|
128
|
+
# Handle cached fields inside collections:
|
129
|
+
next lkhd if field_name.is_a?(Integer)
|
130
|
+
|
131
|
+
lkhd.selection_with_alias(field_name)
|
132
|
+
}
|
63
133
|
|
64
134
|
current_root.selections.to_selections_key
|
65
135
|
end
|
@@ -68,12 +138,16 @@ module GraphQL
|
|
68
138
|
lookahead = query.lookahead
|
69
139
|
|
70
140
|
path.map { |field_name|
|
71
|
-
|
141
|
+
# Handle cached fields inside collections:
|
142
|
+
next field_name if field_name.is_a?(Integer)
|
143
|
+
|
144
|
+
lookahead = lookahead.selection_with_alias(field_name)
|
145
|
+
raise "Failed to look ahead the field: #{field_name}" if lookahead.is_a?(::GraphQL::Execution::Lookahead::NullLookahead)
|
72
146
|
|
73
|
-
next
|
147
|
+
next lookahead.field.name if lookahead.arguments.empty?
|
74
148
|
|
75
149
|
args = lookahead.arguments.map { "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")
|
76
|
-
"#{
|
150
|
+
"#{lookahead.field.name}(#{args})"
|
77
151
|
}.join("/")
|
78
152
|
end
|
79
153
|
|
@@ -10,9 +10,7 @@ module GraphQL
|
|
10
10
|
def call(query)
|
11
11
|
return unless query.context.fragments?
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
query.context.fragments.each { _1.persist(final_value) }
|
13
|
+
query.context.fragments.each(&:persist)
|
16
14
|
end
|
17
15
|
end
|
18
16
|
end
|
@@ -8,18 +8,26 @@ module GraphQL
|
|
8
8
|
class Fragment
|
9
9
|
attr_reader :options, :path, :context
|
10
10
|
|
11
|
+
attr_accessor :resolved_value
|
12
|
+
|
11
13
|
def initialize(context, **options)
|
12
14
|
@context = context
|
13
15
|
@options = options
|
14
|
-
@path =
|
16
|
+
@path = interpreter_context[:current_path]
|
15
17
|
end
|
16
18
|
|
19
|
+
NIL_IN_CACHE = Object.new
|
20
|
+
|
17
21
|
def read
|
18
|
-
FragmentCache.cache_store.read(cache_key)
|
22
|
+
FragmentCache.cache_store.read(cache_key).tap do |cached|
|
23
|
+
return NIL_IN_CACHE if cached.nil? && FragmentCache.cache_store.exist?(cache_key)
|
24
|
+
end
|
19
25
|
end
|
20
26
|
|
21
|
-
def persist
|
22
|
-
|
27
|
+
def persist
|
28
|
+
# Connections are not available from the runtime object, so
|
29
|
+
# we rely on Schema::Tracer to save it for us
|
30
|
+
value = resolved_value || resolve_from_runtime
|
23
31
|
FragmentCache.cache_store.write(cache_key, value, **options)
|
24
32
|
end
|
25
33
|
|
@@ -29,9 +37,17 @@ module GraphQL
|
|
29
37
|
@cache_key ||= CacheKeyBuilder.call(path: path, query: context.query, **options)
|
30
38
|
end
|
31
39
|
|
32
|
-
def
|
40
|
+
def interpreter_context
|
41
|
+
context.namespace(:interpreter)
|
42
|
+
end
|
43
|
+
|
44
|
+
def resolve_from_runtime
|
33
45
|
final_value.dig(*path)
|
34
46
|
end
|
47
|
+
|
48
|
+
def final_value
|
49
|
+
@final_value ||= interpreter_context[:runtime].final_value
|
50
|
+
end
|
35
51
|
end
|
36
52
|
end
|
37
53
|
end
|
@@ -8,21 +8,42 @@ module GraphQL
|
|
8
8
|
|
9
9
|
# Adds #cache_fragment method
|
10
10
|
module ObjectHelpers
|
11
|
+
extend Forwardable
|
12
|
+
|
11
13
|
NO_OBJECT = Object.new
|
12
14
|
|
15
|
+
def_delegator :field, :connection?
|
16
|
+
|
13
17
|
def cache_fragment(object_to_cache = NO_OBJECT, **options, &block)
|
14
18
|
raise ArgumentError, "Block or argument must be provided" unless block_given? || object_to_cache != NO_OBJECT
|
15
19
|
|
16
20
|
options[:object] = object_to_cache if object_to_cache != NO_OBJECT
|
21
|
+
|
17
22
|
fragment = Fragment.new(context, options)
|
18
23
|
|
19
24
|
if (cached = fragment.read)
|
20
|
-
return
|
25
|
+
return nil if cached == Fragment::NIL_IN_CACHE
|
26
|
+
return restore_cached_value(cached)
|
27
|
+
end
|
28
|
+
|
29
|
+
(block_given? ? block.call : object_to_cache).tap do |resolved_value|
|
30
|
+
context.fragments << fragment
|
21
31
|
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
22
35
|
|
23
|
-
|
36
|
+
def restore_cached_value(cached)
|
37
|
+
# If we return connection object from resolver, Interpreter stops processing it
|
38
|
+
connection? ? cached : raw_value(cached)
|
39
|
+
end
|
40
|
+
|
41
|
+
def field
|
42
|
+
interpreter_context[:current_field]
|
43
|
+
end
|
24
44
|
|
25
|
-
|
45
|
+
def interpreter_context
|
46
|
+
@interpreter_context ||= context.namespace(:interpreter)
|
26
47
|
end
|
27
48
|
end
|
28
49
|
end
|
@@ -25,7 +25,7 @@ module GraphQL
|
|
25
25
|
config.graphql_fragment_cache = Config
|
26
26
|
|
27
27
|
if ENV["RACK_ENV"] == "test" || ENV["RAILS_ENV"] == "test"
|
28
|
-
|
28
|
+
config.graphql_fragment_cache.store = :null_store
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "graphql/fragment_cache/cacher"
|
4
|
+
|
5
|
+
module GraphQL
|
6
|
+
module FragmentCache
|
7
|
+
module Schema
|
8
|
+
# Adds hook for saving cached values after query is resolved
|
9
|
+
module Instrumentation
|
10
|
+
module_function
|
11
|
+
|
12
|
+
def before_query(query)
|
13
|
+
end
|
14
|
+
|
15
|
+
def after_query(query)
|
16
|
+
return unless query.valid?
|
17
|
+
|
18
|
+
Cacher.call(query)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest/sha1"
|
4
|
+
|
5
|
+
module GraphQL
|
6
|
+
module FragmentCache
|
7
|
+
module Schema
|
8
|
+
# Patches GraphQL::Schema to support fragment cache
|
9
|
+
module Patch
|
10
|
+
def schema_cache_key
|
11
|
+
@schema_cache_key ||= Digest::SHA1.hexdigest(to_definition)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
# Plugin definition
|
5
|
+
module FragmentCache
|
6
|
+
module Schema
|
7
|
+
class Tracer
|
8
|
+
using Ext
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def trace(key, data)
|
12
|
+
yield.tap do |resolved_value|
|
13
|
+
next unless connection_to_cache?(key, data)
|
14
|
+
|
15
|
+
# We need to attach connection object to fragment and save it later
|
16
|
+
context = data[:query].context
|
17
|
+
verify_connections!(context)
|
18
|
+
cache_connection(resolved_value, context)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def connection_to_cache?(key, data)
|
25
|
+
key == "execute_field" && data[:field].connection?
|
26
|
+
end
|
27
|
+
|
28
|
+
def verify_connections!(context)
|
29
|
+
return if context.schema.new_connections?
|
30
|
+
|
31
|
+
raise StandardError,
|
32
|
+
"GraphQL::Pagination::Connections should be enabled for connection caching"
|
33
|
+
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
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
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.0
|
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-06-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.10.
|
19
|
+
version: 1.10.8
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.10.
|
26
|
+
version: 1.10.8
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name: ruby-next
|
28
|
+
name: ruby-next
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.7.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.7.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: combustion
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +52,34 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activerecord
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sqlite3
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: rake
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -121,7 +149,6 @@ files:
|
|
121
149
|
- bin/console
|
122
150
|
- bin/setup
|
123
151
|
- lib/.rbnext/2.7/graphql/fragment_cache/cache_key_builder.rb
|
124
|
-
- lib/.rbnext/2.7/graphql/fragment_cache/cacher.rb
|
125
152
|
- lib/.rbnext/2.7/graphql/fragment_cache/ext/graphql_cache_key.rb
|
126
153
|
- lib/graphql-fragment_cache.rb
|
127
154
|
- lib/graphql/fragment_cache.rb
|
@@ -131,13 +158,14 @@ files:
|
|
131
158
|
- lib/graphql/fragment_cache/ext/graphql_cache_key.rb
|
132
159
|
- lib/graphql/fragment_cache/field_extension.rb
|
133
160
|
- lib/graphql/fragment_cache/fragment.rb
|
134
|
-
- lib/graphql/fragment_cache/instrumentation.rb
|
135
161
|
- lib/graphql/fragment_cache/memory_store.rb
|
136
162
|
- lib/graphql/fragment_cache/object.rb
|
137
163
|
- lib/graphql/fragment_cache/object_helpers.rb
|
138
164
|
- lib/graphql/fragment_cache/rails/cache_key_builder.rb
|
139
165
|
- lib/graphql/fragment_cache/railtie.rb
|
140
|
-
- lib/graphql/fragment_cache/
|
166
|
+
- lib/graphql/fragment_cache/schema/instrumentation.rb
|
167
|
+
- lib/graphql/fragment_cache/schema/patch.rb
|
168
|
+
- lib/graphql/fragment_cache/schema/tracer.rb
|
141
169
|
- lib/graphql/fragment_cache/version.rb
|
142
170
|
homepage: https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache
|
143
171
|
licenses:
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GraphQL
|
4
|
-
module FragmentCache
|
5
|
-
using Ext
|
6
|
-
|
7
|
-
# Saves resolved fragment values to cache store
|
8
|
-
module Cacher
|
9
|
-
class << self
|
10
|
-
def call(query)
|
11
|
-
return unless query.context.fragments?
|
12
|
-
|
13
|
-
final_value = query.context.namespace(:interpreter)[:runtime].final_value
|
14
|
-
|
15
|
-
query.context.fragments.each { |_1| _1.persist(final_value) }
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "graphql/fragment_cache/cacher"
|
4
|
-
|
5
|
-
module GraphQL
|
6
|
-
module FragmentCache
|
7
|
-
# Adds hook for saving cached values after query is resolved
|
8
|
-
module Instrumentation
|
9
|
-
module_function
|
10
|
-
|
11
|
-
def before_query(query)
|
12
|
-
end
|
13
|
-
|
14
|
-
def after_query(query)
|
15
|
-
return unless query.valid?
|
16
|
-
|
17
|
-
Cacher.call(query)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "digest/sha1"
|
4
|
-
|
5
|
-
module GraphQL
|
6
|
-
module FragmentCache
|
7
|
-
# Patches GraphQL::Schema to support fragment cache
|
8
|
-
module SchemaPatch
|
9
|
-
def schema_cache_key
|
10
|
-
@schema_cache_key ||= Digest::SHA1.hexdigest(to_definition)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|