graphql-fragment_cache 0.1.6 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d83b3ff9d3e9cc91b6f013214d2cc4d97c6d2fc8bd377bf38d1f68dd7f935c23
4
- data.tar.gz: d349157e8c6856c06b5a3ae76915399ae77fc04153093ef9551bb75b45629cb4
3
+ metadata.gz: 8fd575d2efddb52ef6d0f878b40823baae40cedec5aa683ba5f6a1b39b98bffe
4
+ data.tar.gz: eba1c06d3650d395575bd96927f831fc206f797b47afa9ebfcfff9adbf2ecf1d
5
5
  SHA512:
6
- metadata.gz: 759813c8b9ae4373d9890519aa88573eb9a504771eca25ea7d7ddc2a35b972f172e1fb7e86bbd2f52447886e3c781178cfc7491c9a7257ce436ae411a32395fb
7
- data.tar.gz: 78c75d761fc3154dd4a4622a36c5b0f7bbc4b26c31eb4bf3e3c6be4423be8ae64923d7207f02217142c213fa80e039410e2d079403c0258efd82543d9866e41d
6
+ metadata.gz: 5fe016ab01f8b8076b1cc6680f8a09ad9bf1ab9d7f5c798a308465fd0d6d80dd6da24ef5855aa35660d9b8cce0529ab59cb6a45b7b971c58d4f8a9e761b80ff9
7
+ data.tar.gz: 0430b991c22524be88e3741f700d5c02ac65186f4cf40e7f06440b737c345bba8e44748a9e8d6ef1293356e21ee7560595b5e1563c28da7bb5f88008f7174360
@@ -2,6 +2,26 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.0.3 (2020-08-31)
6
+
7
+ - [PR#29](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/29) Cache result JSON instead of connection objects ([@DmitryTsepelev][])
8
+
9
+ ## 1.0.2 (2020-08-19)
10
+
11
+ - [PR#28](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/28) Support #keys method for GraphQL::FragmentCache::MemoryStore instance ([@reabiliti][])
12
+
13
+ ## 1.0.1 (2020-06-17)
14
+
15
+ - [PR#25](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/25) Support fragments with aliases for CacheKeyBuilder ([@DmitryTsepelev][])
16
+
17
+ ## 1.0.0 (2020-06-13)
18
+
19
+ - [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][])
20
+
21
+ ## 0.1.7 (2020-06-02)
22
+
23
+ - [PR#23](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/23) Avoid extra queries after restoring connection from cache ([@DmitryTsepelev][])
24
+
5
25
  ## 0.1.6 (2020-05-30)
6
26
 
7
27
  - [PR#22](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/pull/22) Properly cache entites inside collections ([@DmitryTsepelev][])
@@ -35,3 +55,4 @@
35
55
  [@DmitryTsepelev]: https://github.com/DmitryTsepelev
36
56
  [@palkan]: https://github.com/palkan
37
57
  [@ssnickolay]: https://github.com/ssnickolay
58
+ [@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)` and `#write(key, value, **options)` methods.
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
 
@@ -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
@@ -4,10 +4,13 @@ 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
- require "graphql/fragment_cache/instrumentation"
8
+
9
+ require "graphql/fragment_cache/connections/patch"
10
+
11
+ require "graphql/fragment_cache/schema/patch"
12
+ require "graphql/fragment_cache/schema/tracer"
13
+ require "graphql/fragment_cache/schema/instrumentation"
11
14
 
12
15
  require "graphql/fragment_cache/memory_store"
13
16
 
@@ -22,8 +25,11 @@ module GraphQL
22
25
  def use(schema_defn, options = {})
23
26
  verify_interpreter!(schema_defn)
24
27
 
25
- schema_defn.instrument(:query, Instrumentation)
26
- schema_defn.extend(SchemaPatch)
28
+ schema_defn.tracer(Schema::Tracer)
29
+ schema_defn.instrument(:query, Schema::Instrumentation)
30
+ schema_defn.extend(Schema::Patch)
31
+
32
+ GraphQL::Pagination::Connections.prepend(Connections::Patch)
27
33
  end
28
34
 
29
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
@@ -10,9 +10,7 @@ module GraphQL
10
10
  def call(query)
11
11
  return unless query.context.fragments?
12
12
 
13
- final_value = query.context.namespace(:interpreter)[:runtime].final_value
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
@@ -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, parent, items, arguments, context, *options)
10
+ raw_value?(items) ? items : super
11
+ end
12
+ else
13
+ def wrap(field, object, arguments, context, *options)
14
+ raw_value?(object) ? object : 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
@@ -6,22 +6,24 @@ module GraphQL
6
6
  module FragmentCache
7
7
  # Represents a single fragment to cache
8
8
  class Fragment
9
- attr_reader :options, :path, :context, :raw_connection
10
-
11
- attr_writer :raw_connection
9
+ attr_reader :options, :path, :context
12
10
 
13
11
  def initialize(context, **options)
14
12
  @context = context
15
13
  @options = options
16
- @path = context.namespace(:interpreter)[:current_path]
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
- def persist(final_value)
24
- value = raw_connection || resolve(final_value)
25
+ def persist
26
+ value = final_value.dig(*path)
25
27
  FragmentCache.cache_store.write(cache_key, value, **options)
26
28
  end
27
29
 
@@ -31,8 +33,12 @@ module GraphQL
31
33
  @cache_key ||= CacheKeyBuilder.call(path: path, query: context.query, **options)
32
34
  end
33
35
 
34
- def resolve(final_value)
35
- final_value.dig(*path)
36
+ def interpreter_context
37
+ context.namespace(:interpreter)
38
+ end
39
+
40
+ def final_value
41
+ @final_value ||= interpreter_context[:runtime].final_value
36
42
  end
37
43
  end
38
44
  end
@@ -23,6 +23,14 @@ module GraphQL
23
23
  @storage = {}
24
24
  end
25
25
 
26
+ def keys
27
+ storage.keys
28
+ end
29
+
30
+ def exist?(key)
31
+ storage.key?(key)
32
+ end
33
+
26
34
  def read(key)
27
35
  key = key.to_s
28
36
  storage[key]&.then do |entry|
@@ -6,8 +6,6 @@ module GraphQL
6
6
  module FragmentCache
7
7
  using Ext
8
8
 
9
- RawConnection = Struct.new(:items, :nodes, :paged_nodes_offset, :has_previous_page, :has_next_page)
10
-
11
9
  # Adds #cache_fragment method
12
10
  module ObjectHelpers
13
11
  extend Forwardable
@@ -24,41 +22,16 @@ module GraphQL
24
22
  fragment = Fragment.new(context, options)
25
23
 
26
24
  if (cached = fragment.read)
27
- return restore_cached_value(cached)
25
+ return cached == Fragment::NIL_IN_CACHE ? nil : raw_value(cached)
28
26
  end
29
27
 
30
28
  (block_given? ? block.call : object_to_cache).tap do |resolved_value|
31
- cache_value(resolved_value, fragment)
29
+ context.fragments << fragment
32
30
  end
33
31
  end
34
32
 
35
33
  private
36
34
 
37
- def restore_cached_value(cached)
38
- connection? ? restore_cached_connection(cached) : raw_value(cached)
39
- end
40
-
41
- def cache_value(resolved_value, fragment)
42
- if connection?
43
- unless context.schema.new_connections?
44
- raise StandardError,
45
- "GraphQL::Pagination::Connections should be enabled for connection caching"
46
- end
47
-
48
- connection = wrap_connection(resolved_value)
49
-
50
- fragment.raw_connection = RawConnection.new(
51
- connection.items,
52
- connection.nodes,
53
- connection.instance_variable_get(:@paged_nodes_offset),
54
- connection.has_previous_page,
55
- connection.has_next_page
56
- )
57
- end
58
-
59
- context.fragments << fragment
60
- end
61
-
62
35
  def field
63
36
  interpreter_context[:current_field]
64
37
  end
@@ -66,19 +39,6 @@ module GraphQL
66
39
  def interpreter_context
67
40
  @interpreter_context ||= context.namespace(:interpreter)
68
41
  end
69
-
70
- def restore_cached_connection(raw_connection)
71
- wrap_connection(raw_connection.items).tap do |connection|
72
- connection.instance_variable_set(:@nodes, raw_connection.nodes)
73
- connection.instance_variable_set(:@paged_nodes_offset, raw_connection.paged_nodes_offset)
74
- connection.instance_variable_set(:@has_previous_page, raw_connection.has_previous_page)
75
- connection.instance_variable_set(:@has_next_page, raw_connection.has_next_page)
76
- end
77
- end
78
-
79
- def wrap_connection(object)
80
- context.schema.connections.wrap(field, object, interpreter_context[:current_arguments], context)
81
- end
82
42
  end
83
43
  end
84
44
  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,35 @@
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_field?(key, data)
14
+
15
+ verify_connections!(data[:query].context)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def connection_field?(key, data)
22
+ key == "execute_field" && data[:field].connection?
23
+ end
24
+
25
+ def verify_connections!(context)
26
+ return if context.schema.new_connections?
27
+
28
+ raise StandardError,
29
+ "GraphQL::Pagination::Connections should be enabled for connection caching"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module FragmentCache
5
- VERSION = "0.1.6"
5
+ VERSION = "1.0.3"
6
6
  end
7
7
  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.1.6
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - DmitryTsepelev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-30 00:00:00.000000000 Z
11
+ date: 2020-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -136,6 +136,20 @@ dependencies:
136
136
  - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0.6'
139
+ - !ruby/object:Gem::Dependency
140
+ name: ruby-next-parser
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '='
144
+ - !ruby/object:Gem::Version
145
+ version: 2.8.0.7
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '='
151
+ - !ruby/object:Gem::Version
152
+ version: 2.8.0.7
139
153
  description: Fragment cache for graphql-ruby
140
154
  email:
141
155
  - dmitry.a.tsepelev@gmail.com
@@ -149,23 +163,24 @@ files:
149
163
  - bin/console
150
164
  - bin/setup
151
165
  - lib/.rbnext/2.7/graphql/fragment_cache/cache_key_builder.rb
152
- - lib/.rbnext/2.7/graphql/fragment_cache/cacher.rb
153
166
  - lib/.rbnext/2.7/graphql/fragment_cache/ext/graphql_cache_key.rb
154
167
  - lib/graphql-fragment_cache.rb
155
168
  - lib/graphql/fragment_cache.rb
156
169
  - lib/graphql/fragment_cache/cache_key_builder.rb
157
170
  - lib/graphql/fragment_cache/cacher.rb
171
+ - lib/graphql/fragment_cache/connections/patch.rb
158
172
  - lib/graphql/fragment_cache/ext/context_fragments.rb
159
173
  - lib/graphql/fragment_cache/ext/graphql_cache_key.rb
160
174
  - lib/graphql/fragment_cache/field_extension.rb
161
175
  - lib/graphql/fragment_cache/fragment.rb
162
- - lib/graphql/fragment_cache/instrumentation.rb
163
176
  - lib/graphql/fragment_cache/memory_store.rb
164
177
  - lib/graphql/fragment_cache/object.rb
165
178
  - lib/graphql/fragment_cache/object_helpers.rb
166
179
  - lib/graphql/fragment_cache/rails/cache_key_builder.rb
167
180
  - lib/graphql/fragment_cache/railtie.rb
168
- - lib/graphql/fragment_cache/schema_patch.rb
181
+ - lib/graphql/fragment_cache/schema/instrumentation.rb
182
+ - lib/graphql/fragment_cache/schema/patch.rb
183
+ - lib/graphql/fragment_cache/schema/tracer.rb
169
184
  - lib/graphql/fragment_cache/version.rb
170
185
  homepage: https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache
171
186
  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