iknow_view_models 3.14.2 → 3.14.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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32034ee1a16051289f76e317a76a7356f32093fd4439aa75503ffa00ab1f8b95
|
4
|
+
data.tar.gz: cb8a4669e79d68888c14d76b9ed718c4f2d8336da0989a22a7eafc779aff3bca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8effb32744fe38236249ff4a483ff9a778efdd92ca00693eb8c3f00546fedfc4de7792456fcbd04d652659a713a4ecd8d3efefde43633bd9d19a666c93e930fb
|
7
|
+
data.tar.gz: 3915fbdfa63db615439e186da904c7c2899727e66a0326abf31b6d7398423761dd79db1c544ae69ba5983200d91d5ed1929449307cf961da2073cc02e3909307
|
@@ -19,14 +19,15 @@ module ViewModel::MigratableView
|
|
19
19
|
@migrations_lock = Monitor.new
|
20
20
|
@migration_classes = {}
|
21
21
|
@migration_paths = {}
|
22
|
-
@realized_migration_paths = true
|
23
22
|
end
|
24
23
|
|
25
24
|
def migration_path(from:, to:)
|
26
25
|
@migrations_lock.synchronize do
|
27
|
-
|
26
|
+
unless @migration_paths.has_key?(to)
|
27
|
+
@migration_paths[to] = compute_migration_paths(to)
|
28
|
+
end
|
28
29
|
|
29
|
-
migrations = @migration_paths.fetch(
|
30
|
+
migrations = @migration_paths[to].fetch(from) do
|
30
31
|
raise ViewModel::Migration::NoPathError.new(self, from, to)
|
31
32
|
end
|
32
33
|
|
@@ -64,25 +65,35 @@ module ViewModel::MigratableView
|
|
64
65
|
const_set(:"Migration_#{from}_To_#{to}", migration_class)
|
65
66
|
@migration_classes[[from, to]] = migration_class
|
66
67
|
|
67
|
-
|
68
|
+
clear_migration_paths!
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def clear_migration_paths!
|
73
|
+
@migrations_lock.synchronize do
|
74
|
+
@migration_paths.clear
|
68
75
|
end
|
69
76
|
end
|
70
77
|
|
71
78
|
# Internal: find and record possible paths to the current schema version.
|
72
|
-
def
|
73
|
-
|
79
|
+
def compute_migration_paths(to_version)
|
80
|
+
unless to_version <= self.schema_version
|
81
|
+
raise RuntimeError.new("Cannot compute path to future version '#{to_version}'")
|
82
|
+
end
|
74
83
|
|
75
84
|
graph = RGL::DirectedAdjacencyGraph.new
|
76
85
|
|
77
|
-
# Add a vertex for the
|
78
|
-
graph.add_vertex(
|
86
|
+
# Add a vertex for the destination version, in case no edges reach it
|
87
|
+
graph.add_vertex(to_version)
|
79
88
|
|
80
89
|
# Add edges backwards, as we care about paths from the latest version
|
81
90
|
@migration_classes.each_key do |from, to|
|
82
91
|
graph.add_edge(to, from)
|
83
92
|
end
|
84
93
|
|
85
|
-
paths = graph.dijkstra_shortest_paths(Hash.new { 1 },
|
94
|
+
paths = graph.dijkstra_shortest_paths(Hash.new { 1 }, to_version)
|
95
|
+
|
96
|
+
result = {}
|
86
97
|
|
87
98
|
paths.each do |target_version, path|
|
88
99
|
next if path.nil? || path.length == 1
|
@@ -92,12 +103,10 @@ module ViewModel::MigratableView
|
|
92
103
|
@migration_classes.fetch([from, to])
|
93
104
|
end
|
94
105
|
|
95
|
-
|
96
|
-
|
97
|
-
@migration_paths[key] = path_migration_classes.map(&:new)
|
106
|
+
result[target_version] = path_migration_classes.map(&:new)
|
98
107
|
end
|
99
108
|
|
100
|
-
|
109
|
+
result
|
101
110
|
end
|
102
111
|
end
|
103
112
|
end
|
data/lib/view_model/migrator.rb
CHANGED
@@ -23,16 +23,15 @@ class ViewModel
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
MigrationPlan = Struct.new(:path, :required_version, :current_version, :viewmodel_class)
|
27
|
+
|
26
28
|
def initialize(required_versions)
|
27
|
-
@
|
28
|
-
|
29
|
-
|
30
|
-
h[viewmodel_class.view_name] = path
|
31
|
-
end
|
32
|
-
end
|
29
|
+
@plans = required_versions.each_with_object({}) do |(viewmodel_class, required_version), h|
|
30
|
+
current_version = viewmodel_class.schema_version
|
31
|
+
next if required_version == current_version
|
33
32
|
|
34
|
-
|
35
|
-
h[viewmodel_class.view_name] =
|
33
|
+
path = viewmodel_class.migration_path(from: required_version, to: current_version)
|
34
|
+
h[viewmodel_class.view_name] = MigrationPlan.new(path, required_version, current_version, viewmodel_class)
|
36
35
|
end
|
37
36
|
end
|
38
37
|
|
@@ -128,23 +127,22 @@ class ViewModel
|
|
128
127
|
end
|
129
128
|
|
130
129
|
def migrate_viewmodel!(view_name, source_version, view_hash, references)
|
131
|
-
|
132
|
-
return false unless
|
130
|
+
plan = @plans[view_name]
|
131
|
+
return false unless plan
|
133
132
|
|
134
|
-
|
135
|
-
return false if source_version == current_version
|
133
|
+
return false if source_version == plan.current_version
|
136
134
|
|
137
135
|
# We assume that an unspecified source version is the same as the required
|
138
136
|
# version (i.e. the version demanded by the client request).
|
139
|
-
unless source_version.nil? || source_version == required_version
|
137
|
+
unless source_version.nil? || source_version == plan.required_version
|
140
138
|
raise ViewModel::Migration::UnspecifiedVersionError.new(view_name, source_version)
|
141
139
|
end
|
142
140
|
|
143
|
-
path.each do |migration|
|
141
|
+
plan.path.each do |migration|
|
144
142
|
migration.up(view_hash, references)
|
145
143
|
end
|
146
144
|
|
147
|
-
view_hash[ViewModel::VERSION_ATTRIBUTE] = current_version
|
145
|
+
view_hash[ViewModel::VERSION_ATTRIBUTE] = plan.current_version
|
148
146
|
|
149
147
|
true
|
150
148
|
end
|
@@ -156,15 +154,23 @@ class ViewModel
|
|
156
154
|
private
|
157
155
|
|
158
156
|
def migrate_viewmodel!(view_name, source_version, view_hash, references)
|
159
|
-
|
160
|
-
return false unless
|
157
|
+
plan = @plans[view_name]
|
158
|
+
return false unless plan
|
161
159
|
|
162
160
|
# In a serialized output, the source version should always be the present
|
163
161
|
# and the current version, unless already modified by a parent migration
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
162
|
+
return false if source_version == plan.required_version
|
163
|
+
|
164
|
+
# If a parent migration has already partially migrated us to an
|
165
|
+
# intermediate version, we need to construct a new path to the required
|
166
|
+
# version.
|
167
|
+
if source_version == plan.current_version
|
168
|
+
path = plan.path
|
169
|
+
elsif view_hash[ViewModel::MIGRATED_ATTRIBUTE] &&
|
170
|
+
source_version > plan.required_version &&
|
171
|
+
source_version < plan.current_version
|
172
|
+
path = plan.viewmodel_class.migration_path(from: plan.required_version, to: source_version)
|
173
|
+
else
|
168
174
|
raise ViewModel::Migration::UnspecifiedVersionError.new(view_name, source_version)
|
169
175
|
end
|
170
176
|
|
@@ -172,7 +178,7 @@ class ViewModel
|
|
172
178
|
migration.down(view_hash, references)
|
173
179
|
end
|
174
180
|
|
175
|
-
view_hash[ViewModel::VERSION_ATTRIBUTE] = required_version
|
181
|
+
view_hash[ViewModel::VERSION_ATTRIBUTE] = plan.required_version
|
176
182
|
|
177
183
|
true
|
178
184
|
end
|
@@ -483,6 +483,87 @@ class ViewModel::ActiveRecord::Migration < ActiveSupport::TestCase
|
|
483
483
|
end
|
484
484
|
end
|
485
485
|
|
486
|
+
describe 'partially migrating children' do
|
487
|
+
include ViewModelSpecHelpers::ParentAndBelongsToChild
|
488
|
+
let(:migration_versions) { { viewmodel_class => 1, child_viewmodel_class => 1 } }
|
489
|
+
|
490
|
+
# Parent 2->1 requires premigrating child 3->2
|
491
|
+
def model_attributes
|
492
|
+
child_viewmodel_class = self.child_viewmodel_class
|
493
|
+
super.merge(
|
494
|
+
viewmodel: ->(_v) {
|
495
|
+
self.schema_version = 2
|
496
|
+
|
497
|
+
# The migration from 1 -> 2 relies on a field only present in child v2
|
498
|
+
migrates from: 1, to: 2 do
|
499
|
+
down do |view, references|
|
500
|
+
child = view['child']
|
501
|
+
|
502
|
+
if child['_version'] > 2
|
503
|
+
migrator = ViewModel::DownMigrator.new({ child_viewmodel_class => 2 })
|
504
|
+
migrator.migrate!({ 'data' => child, 'references' => references.deep_dup })
|
505
|
+
end
|
506
|
+
|
507
|
+
view['child_v2_field'] = child['v2_field']
|
508
|
+
end
|
509
|
+
end
|
510
|
+
},
|
511
|
+
)
|
512
|
+
end
|
513
|
+
|
514
|
+
def child_attributes
|
515
|
+
super().merge(
|
516
|
+
viewmodel: ->(_v) {
|
517
|
+
self.schema_version = 3
|
518
|
+
migrates from: 1, to: 2 do
|
519
|
+
down do |view, _references|
|
520
|
+
view.delete('v2_field')
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
migrates from: 2, to: 3 do
|
525
|
+
down do |view, _references|
|
526
|
+
view['v2_field'] = 'v2_field_data'
|
527
|
+
end
|
528
|
+
end
|
529
|
+
})
|
530
|
+
end
|
531
|
+
|
532
|
+
let(:v1_serialization_data) do
|
533
|
+
{
|
534
|
+
ViewModel::TYPE_ATTRIBUTE => viewmodel_class.view_name,
|
535
|
+
ViewModel::VERSION_ATTRIBUTE => 1,
|
536
|
+
ViewModel::ID_ATTRIBUTE => viewmodel.id,
|
537
|
+
'name' => viewmodel.name,
|
538
|
+
'child' => {
|
539
|
+
ViewModel::TYPE_ATTRIBUTE => child_viewmodel_class.view_name,
|
540
|
+
ViewModel::VERSION_ATTRIBUTE => 1,
|
541
|
+
ViewModel::ID_ATTRIBUTE => viewmodel.child.id,
|
542
|
+
'name' => viewmodel.child.name,
|
543
|
+
},
|
544
|
+
'child_v2_field' => 'v2_field_data',
|
545
|
+
}
|
546
|
+
end
|
547
|
+
|
548
|
+
let(:migrator) { down_migrator }
|
549
|
+
let(:subject) do
|
550
|
+
current_serialization.deep_dup
|
551
|
+
end
|
552
|
+
|
553
|
+
let(:expected_result) do
|
554
|
+
data = v1_serialization_data.deep_dup
|
555
|
+
data[ViewModel::MIGRATED_ATTRIBUTE] = true
|
556
|
+
data['child'][ViewModel::MIGRATED_ATTRIBUTE] = true
|
557
|
+
|
558
|
+
{ 'data' => data }
|
559
|
+
end
|
560
|
+
|
561
|
+
it 'migrates' do
|
562
|
+
migrate!
|
563
|
+
assert_equal(expected_result, subject)
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
486
567
|
describe 'concurrently inserting a reference' do
|
487
568
|
include ViewModelSpecHelpers::ReferencedList
|
488
569
|
let(:migration_versions) { { viewmodel_class => 1 } }
|
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.14.
|
4
|
+
version: 3.14.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- iKnow Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-09-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|