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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba042028d0f9a6b8baebcfd86fcfddf9eb571014967498e1f0f609ad6445da18
4
- data.tar.gz: ccf1d4764c9178faceac2ef63dbeb30ba7b1a303dbfc5e299289be3ec56a9ddd
3
+ metadata.gz: e2c544bc2742643748c5e36c59b3414def73c22e6f7c4b5080504751ea564ecd
4
+ data.tar.gz: b61980525863502ea4f30eeb96fee11dbc11a906ab98b863ed0ece7f9d97d1e9
5
5
  SHA512:
6
- metadata.gz: 6025cf472c24989b66d47b3d7c79d6d5a5bdb8e71540c0e7c0ca7bc2bd2baf5b438206e978d1d4d5bbdd975e793e675d5370364ece2f8ca223aa4eabed263c9e
7
- data.tar.gz: 80e93356549005e70a9e3e66a6d87716aebcfc3a3b540e91ffb935d4fb18533df56164664a78c9371c69d07197e92be3aabfd54916428b94cdd19b7952e0d18a
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 ![CI](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/workflows/CI/badge.svg?branch=master)
1
+ # GraphQL::FragmentCache ![CI](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/actions/workflows/rspec.yml/badge.svg?branch=master) ![](https://ruby-gem-downloads-badge.herokuapp.com/graphql-fragment_cache?type=total)
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
- You can optionally define a namespace that will be prefixed to every cache key:
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 = "my-prefix"
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
- ].compact.join("/")
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
- @options.fetch(:path_cache_key) do
175
- lookahead = query.lookahead
184
+ @path_cache_key ||= begin
185
+ @options.fetch(:path_cache_key) do
186
+ lookahead = query.lookahead
176
187
 
177
- path.map { |field_name|
178
- # Handle cached fields inside collections:
179
- next field_name if field_name.is_a?(Integer)
188
+ path.map { |field_name|
189
+ # Handle cached fields inside collections:
190
+ next field_name if field_name.is_a?(Integer)
180
191
 
181
- lookahead = lookahead.selection_with_alias(field_name)
182
- raise "Failed to look ahead the field: #{field_name}" if lookahead.is_a?(::GraphQL::Execution::Lookahead::NullLookahead)
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
- next lookahead.field.name if lookahead.arguments.empty?
195
+ next lookahead.field.name if lookahead.arguments.empty?
185
196
 
186
- args = lookahead.arguments.map { |_1, _2| "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")
187
- "#{lookahead.field.name}(#{args})"
188
- }.join("/")
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
- ].compact.join("/")
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
- @options.fetch(:path_cache_key) do
175
- lookahead = query.lookahead
184
+ @path_cache_key ||= begin
185
+ @options.fetch(:path_cache_key) do
186
+ lookahead = query.lookahead
176
187
 
177
- path.map { |field_name|
178
- # Handle cached fields inside collections:
179
- next field_name if field_name.is_a?(Integer)
188
+ path.map { |field_name|
189
+ # Handle cached fields inside collections:
190
+ next field_name if field_name.is_a?(Integer)
180
191
 
181
- lookahead = lookahead.selection_with_alias(field_name)
182
- raise "Failed to look ahead the field: #{field_name}" if lookahead.is_a?(::GraphQL::Execution::Lookahead::NullLookahead)
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
- next lookahead.field.name if lookahead.arguments.empty?
195
+ next lookahead.field.name if lookahead.arguments.empty?
185
196
 
186
- args = lookahead.arguments.map { |_1, _2| "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")
187
- "#{lookahead.field.name}(#{args})"
188
- }.join("/")
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
- ].compact.join("/")
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
- @options.fetch(:path_cache_key) do
175
- lookahead = query.lookahead
184
+ @path_cache_key ||= begin
185
+ @options.fetch(:path_cache_key) do
186
+ lookahead = query.lookahead
176
187
 
177
- path.map { |field_name|
178
- # Handle cached fields inside collections:
179
- next field_name if field_name.is_a?(Integer)
188
+ path.map { |field_name|
189
+ # Handle cached fields inside collections:
190
+ next field_name if field_name.is_a?(Integer)
180
191
 
181
- lookahead = lookahead.selection_with_alias(field_name)
182
- raise "Failed to look ahead the field: #{field_name}" if lookahead.is_a?(::GraphQL::Execution::Lookahead::NullLookahead)
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
- next lookahead.field.name if lookahead.arguments.empty?
195
+ next lookahead.field.name if lookahead.arguments.empty?
185
196
 
186
- args = lookahead.arguments.map { "#{_1}:#{traverse_argument(_2)}" }.sort.join(",")
187
- "#{lookahead.field.name}(#{args})"
188
- }.join("/")
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
- FragmentCache.cache_store.write_multi(hash, **options)
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
 
@@ -70,6 +70,8 @@ module GraphQL
70
70
  object.object
71
71
  elsif @cache_key == :value
72
72
  resolved_value = yield(object, arguments)
73
+ elsif @cache_key.is_a?(Proc)
74
+ object.instance_exec(&@cache_key)
73
75
  end
74
76
 
75
77
  cache_fragment_options = @cache_options.merge(object: object_for_key)
@@ -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)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module FragmentCache
5
- VERSION = "1.15.0"
5
+ VERSION = "1.17.0"
6
6
  end
7
7
  end
@@ -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.15.0
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-10-27 00:00:00.000000000 Z
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.1.6
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: []