iknow_view_models 3.10.0 → 3.10.1

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: 85aa5f3dd882b989e6a4d1fa8d5d89be347f5f3c215af86851f1ef086d583472
4
- data.tar.gz: d202810ca019d425b0de503b7a077d164d5919dc9206cf069e93b37ddf432334
3
+ metadata.gz: 417eb3615156fa48cf438ead03e2ea33e323c5baa7d411b1e0fafcea5050acb1
4
+ data.tar.gz: cdede3ad865389a0d006725819595aec6da1ea417e9d061452ef3198a39af3d8
5
5
  SHA512:
6
- metadata.gz: 3005ecb2044fd83003ccd444fb2b22d9787b74c929f157573baa084d4d3dff1b2f24d86e30f1ee9bd1e03604a4a0f9a57ec7431cadaea28faa45eda235dbcd28
7
- data.tar.gz: 82971047b73442cbccaf9e3e80f1386dc373329b91f94260f2daeeb5794f99b3502cc47b911a62da38f7d0932b9fea49ae72319de9fb2d79f8be6035e0a34110
6
+ metadata.gz: ada26778b54ffb60a825d408fe40f05c8a4aa93cbac7b5519b2338b5afb4cadfda0d1eb7b359b65584f88fbb9cadea4a35a7d2ba0aad9fb8e4d8ec9d5f894e03
7
+ data.tar.gz: b70d2b42d4a599c3d284dbb96c4506c8ff7b6104108ebcb7126c554fcd671eb0bad04dc4df5a1006f76af272a71c5ca46f54736bf4961821dd7f22160d46fada
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IknowViewModels
4
- VERSION = '3.10.0'
4
+ VERSION = '3.10.1'
5
5
  end
@@ -30,8 +30,21 @@ class ViewModel::ActiveRecord::Cache
30
30
  end
31
31
 
32
32
  def render_from_cache(viewmodel_class, ids, initial_viewmodels: nil, migration_versions: {}, locked: false, serialize_context: viewmodel_class.new_serialize_context)
33
- worker = CacheWorker.new(migration_versions: migration_versions, serialize_context: serialize_context)
34
- worker.render_from_cache(viewmodel_class, ids, initial_viewmodels: initial_viewmodels, locked: locked)
33
+ ignore_existing = false
34
+ begin
35
+ worker = CacheWorker.new(migration_versions: migration_versions, serialize_context: serialize_context, ignore_existing: ignore_existing)
36
+ worker.render_from_cache(viewmodel_class, ids, initial_viewmodels: initial_viewmodels, locked: locked)
37
+ rescue StaleCachedReference
38
+ # If the cache contents contained a unresolvable stale reference, retry
39
+ # while ignoring existing cached values (thereby updating the cache). This
40
+ # will have the side-effect of duplicate Before/AfterVisit callbacks.
41
+ if ignore_existing
42
+ raise
43
+ else
44
+ ignore_existing = true
45
+ retry
46
+ end
47
+ end
35
48
  end
36
49
  end
37
50
 
@@ -76,18 +89,25 @@ class ViewModel::ActiveRecord::Cache
76
89
  migration_versions: migration_versions, serialize_context: serialize_context)
77
90
  end
78
91
 
92
+ class StaleCachedReference < StandardError
93
+ def initialize(error)
94
+ super("Cached value contained stale reference: #{error.message}")
95
+ end
96
+ end
97
+
79
98
  class CacheWorker
80
99
  SENTINEL = Object.new
81
100
  WorklistEntry = Struct.new(:ref_name, :viewmodel)
82
101
 
83
102
  attr_reader :migration_versions, :serialize_context, :resolved_references
84
103
 
85
- def initialize(migration_versions:, serialize_context:)
104
+ def initialize(migration_versions:, serialize_context:, ignore_existing: false)
86
105
  @worklist = {} # Hash[type_name, Hash[id, WorklistEntry]]
87
106
  @resolved_references = {} # Hash[refname, json]
88
107
  @migration_versions = migration_versions
89
108
  @migrated_cache_versions = {}
90
109
  @serialize_context = serialize_context
110
+ @ignore_existing = ignore_existing
91
111
  end
92
112
 
93
113
  def render_from_cache(viewmodel_class, ids, initial_viewmodels: nil, locked: false)
@@ -104,7 +124,7 @@ class ViewModel::ActiveRecord::Cache
104
124
 
105
125
  ids_to_render = ids.to_set
106
126
 
107
- if viewmodel_class < CacheableView
127
+ if viewmodel_class < CacheableView && !@ignore_existing
108
128
  # Load existing serializations from the cache
109
129
  cached_serializations = load_from_cache(viewmodel_class.viewmodel_cache, ids)
110
130
  cached_serializations.each do |id, data|
@@ -168,7 +188,7 @@ class ViewModel::ActiveRecord::Cache
168
188
  @resolved_references[entry.ref_name] = SENTINEL
169
189
  end
170
190
 
171
- if viewmodel_class < CacheableView
191
+ if viewmodel_class < CacheableView && !@ignore_existing
172
192
  cached_serializations = load_from_cache(viewmodel_class.viewmodel_cache, required_entries.keys)
173
193
  cached_serializations.each do |id, data|
174
194
  ref_name = required_entries.delete(id).ref_name
@@ -181,8 +201,18 @@ class ViewModel::ActiveRecord::Cache
181
201
  h[id] = entry.viewmodel if entry.viewmodel
182
202
  end
183
203
 
184
- viewmodels = find_and_preload_viewmodels(viewmodel_class, required_entries.keys,
185
- available_viewmodels: available_viewmodels)
204
+ viewmodels =
205
+ begin
206
+ find_and_preload_viewmodels(viewmodel_class, required_entries.keys,
207
+ available_viewmodels: available_viewmodels)
208
+ rescue ViewModel::DeserializationError::NotFound => e
209
+ # We encountered a reference to an entity that does not exist.
210
+ # If this reference was potentially found in cached data, it
211
+ # could be stale: we can retry without using the cache.
212
+ # If the reference was obtained directly, it indicates invalid
213
+ # data such as an invalid foreign key, and we cannot recover.
214
+ raise StaleCachedReference.new(e)
215
+ end
186
216
 
187
217
  loaded_serializations = serialize_and_cache(viewmodels)
188
218
  loaded_serializations.each do |id, data|
@@ -209,11 +209,27 @@ class ViewModel::ActiveRecord
209
209
  end
210
210
 
211
211
  it 'returns the right serialization with provided locked initial viewmodel' do
212
- locked_root_view = viewmodel_class.new(model_class.lock("FOR SHARE").find(root.id))
212
+ locked_root_view = viewmodel_class.new(model_class.lock('FOR SHARE').find(root.id))
213
213
  fetched_result = parse_result(fetch_with_cache(initial_viewmodels: [locked_root_view], locked: true))
214
214
 
215
215
  value(fetched_result).must_equal(serialize_from_database)
216
216
  end
217
+
218
+ it 'handles a stale cached reference' do
219
+ # Fetch to populate the cache
220
+ initial_result = parse_result(fetch_with_cache)
221
+ value(initial_result).must_equal(serialize_from_database)
222
+
223
+ # Destroy the shared child and its cache without invalidating the cached parent cache
224
+ root.update_columns(shared_id: nil)
225
+ shared.destroy!
226
+ shared_viewmodel_class.viewmodel_cache.clear
227
+
228
+ fetched_result = parse_result(fetch_with_cache)
229
+ value(fetched_result).must_equal(serialize_from_database)
230
+ fetched_view = fetched_result.first.first
231
+ value(fetched_view['shared']).must_be_nil
232
+ end
217
233
  end
218
234
 
219
235
  describe 'with migrations' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iknow_view_models
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.10.0
4
+ version: 3.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - iKnow Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-23 00:00:00.000000000 Z
11
+ date: 2024-05-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack