graphql-fragment_cache 0.1.6 → 1.0.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 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