iknow_view_models 3.7.0 → 3.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/iknow_view_models/version.rb +1 -1
- data/lib/view_model/migrator.rb +23 -3
- data/lib/view_model/reference.rb +8 -0
- data/lib/view_model/references.rb +4 -3
- data/test/helpers/viewmodel_spec_helpers.rb +8 -0
- data/test/unit/view_model/active_record/migration_test.rb +98 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1fb4421931099dc325d159573afcd9ea3529f6e29b4ac80f60d3628c9812df4c
|
4
|
+
data.tar.gz: 97d730c4d0a28ec9ce257906f69a1dd79e1b99c5fe025067ad57ba4ca9774931
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 53aff699c92efd1c334333b2f417444498c504b2f91ea57a00741e3e6e1e259c988723b23e68a7f2c16fba1a6906786471748f5adf5dc2797ca2a4d0924f10df
|
7
|
+
data.tar.gz: b739f19369e3282f6e897a000f04bb56b432796acbe78ca70777e349eef8f39a4058488032f4b42eeda0c4c7c4f60e9b4dba1fba1f2cf253a3c2bd960ff753e3
|
data/lib/view_model/migrator.rb
CHANGED
@@ -39,7 +39,26 @@ class ViewModel
|
|
39
39
|
def migrate!(serialization)
|
40
40
|
references = (serialization['references'] ||= {})
|
41
41
|
|
42
|
-
|
42
|
+
# First visit everything except references; there's no issue with adding
|
43
|
+
# new references during this.
|
44
|
+
migrate_tree!(serialization.except('references'), references: references)
|
45
|
+
|
46
|
+
# While visiting references itself, we need to take care that we can
|
47
|
+
# concurrently modify them (e.g. by adding new referenced views).
|
48
|
+
# Moreover, such added references must themselves be visited, as they'll
|
49
|
+
# be synthesized at the current version and so may need to be migrated
|
50
|
+
# down to the client's requested version.
|
51
|
+
visited_refs = []
|
52
|
+
loop do
|
53
|
+
unvisited_refs = references.keys - visited_refs
|
54
|
+
break if unvisited_refs.empty?
|
55
|
+
|
56
|
+
unvisited_refs.each do |ref|
|
57
|
+
migrate_tree!(references[ref], references: references)
|
58
|
+
end
|
59
|
+
|
60
|
+
visited_refs.concat(unvisited_refs)
|
61
|
+
end
|
43
62
|
|
44
63
|
GarbageCollection.garbage_collect_references!(serialization)
|
45
64
|
|
@@ -112,10 +131,11 @@ class ViewModel
|
|
112
131
|
path = @paths[view_name]
|
113
132
|
return false unless path
|
114
133
|
|
115
|
-
# We assume that an unspecified source version is the same as the required
|
116
|
-
# version.
|
117
134
|
required_version, current_version = @versions[view_name]
|
135
|
+
return false if source_version == current_version
|
118
136
|
|
137
|
+
# We assume that an unspecified source version is the same as the required
|
138
|
+
# version (i.e. the version demanded by the client request).
|
119
139
|
unless source_version.nil? || source_version == required_version
|
120
140
|
raise ViewModel::Migration::UnspecifiedVersionError.new(view_name, source_version)
|
121
141
|
end
|
data/lib/view_model/reference.rb
CHANGED
@@ -29,5 +29,13 @@ class ViewModel
|
|
29
29
|
def hash
|
30
30
|
[viewmodel_class, model_id].hash
|
31
31
|
end
|
32
|
+
|
33
|
+
# Generate a stable reference key for this viewmodel using type name and id
|
34
|
+
def stable_reference
|
35
|
+
raise RuntimeError.new('Model id required to generate a stable reference') unless model_id
|
36
|
+
|
37
|
+
hash = Digest::SHA256.base64digest("#{viewmodel_class.name}.#{model_id}")
|
38
|
+
"ref:h:#{hash}"
|
39
|
+
end
|
32
40
|
end
|
33
41
|
end
|
@@ -37,12 +37,13 @@ class ViewModel
|
|
37
37
|
|
38
38
|
private
|
39
39
|
|
40
|
-
# Ensure stable reference
|
40
|
+
# Ensure stable reference keys for the same (persisted) viewmodels. For
|
41
|
+
# unpersisted viewmodels, use a counter to generate a reference key unique
|
42
|
+
# to this serialization.
|
41
43
|
def new_ref!(viewmodel)
|
42
44
|
vm_ref = viewmodel.to_reference
|
43
45
|
if vm_ref.model_id
|
44
|
-
|
45
|
-
"ref:h:#{hash}"
|
46
|
+
vm_ref.stable_reference
|
46
47
|
else
|
47
48
|
format('ref:i:%06<count>d', count: (@last_ref += 1))
|
48
49
|
end
|
@@ -295,6 +295,14 @@ module ViewModelSpecHelpers
|
|
295
295
|
end
|
296
296
|
end
|
297
297
|
|
298
|
+
module ReferencedList
|
299
|
+
extend ActiveSupport::Concern
|
300
|
+
include ViewModelSpecHelpers::List
|
301
|
+
def model_attributes
|
302
|
+
super.merge(viewmodel: ->(_v) { root! })
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
298
306
|
module ParentAndHasOneChild
|
299
307
|
extend ActiveSupport::Concern
|
300
308
|
include ViewModelSpecHelpers::Base
|
@@ -436,16 +436,17 @@ class ViewModel::ActiveRecord::Migration < ActiveSupport::TestCase
|
|
436
436
|
let(:migrator) { down_migrator }
|
437
437
|
let(:subject) do
|
438
438
|
ser = current_serialization.deep_dup
|
439
|
-
raise ArgumentError.new(
|
439
|
+
raise ArgumentError.new('Expected no references') if ser.has_key?('references')
|
440
|
+
|
440
441
|
ser
|
441
442
|
end
|
442
443
|
|
443
444
|
let(:expected_result) do
|
444
445
|
{
|
445
446
|
'data' => v1_serialization_data.deep_dup.deep_merge(
|
446
|
-
{ ViewModel::MIGRATED_ATTRIBUTE => true }
|
447
|
+
{ ViewModel::MIGRATED_ATTRIBUTE => true },
|
447
448
|
),
|
448
|
-
'references' => v1_serialization_references
|
449
|
+
'references' => v1_serialization_references,
|
449
450
|
}
|
450
451
|
end
|
451
452
|
|
@@ -460,7 +461,8 @@ class ViewModel::ActiveRecord::Migration < ActiveSupport::TestCase
|
|
460
461
|
let(:migrator) { up_migrator }
|
461
462
|
let(:subject) do
|
462
463
|
ser = v1_serialization.deep_dup
|
463
|
-
raise ArgumentError.new(
|
464
|
+
raise ArgumentError.new('Expected references') unless ser.has_key?('references')
|
465
|
+
|
464
466
|
ser
|
465
467
|
end
|
466
468
|
|
@@ -480,4 +482,96 @@ class ViewModel::ActiveRecord::Migration < ActiveSupport::TestCase
|
|
480
482
|
end
|
481
483
|
end
|
482
484
|
end
|
485
|
+
|
486
|
+
describe 'concurrently inserting a reference' do
|
487
|
+
include ViewModelSpecHelpers::ReferencedList
|
488
|
+
let(:migration_versions) { { viewmodel_class => 1 } }
|
489
|
+
|
490
|
+
# Use a list with two members
|
491
|
+
def new_model
|
492
|
+
model_class.new(name: 'root',
|
493
|
+
next: model_class.new(name: 'old-tail'))
|
494
|
+
end
|
495
|
+
|
496
|
+
# Define a down migration that matches the old tail to insert a new tail
|
497
|
+
# after it, and the new tail to change its name.
|
498
|
+
def model_attributes
|
499
|
+
super.merge(
|
500
|
+
viewmodel: ->(v) {
|
501
|
+
self.schema_version = 2
|
502
|
+
|
503
|
+
migrates from: 1, to: 2 do
|
504
|
+
down do |view, refs|
|
505
|
+
case view['name']
|
506
|
+
when 'old-tail'
|
507
|
+
view['next'] = { ViewModel::REFERENCE_ATTRIBUTE => 'ref:s:new_tail' }
|
508
|
+
refs['ref:s:new_tail'] = {
|
509
|
+
ViewModel::TYPE_ATTRIBUTE => v.view_name,
|
510
|
+
ViewModel::VERSION_ATTRIBUTE => v.schema_version,
|
511
|
+
'id' => 100, # entirely fake
|
512
|
+
'name' => 'new-tail',
|
513
|
+
'next' => nil,
|
514
|
+
}
|
515
|
+
|
516
|
+
when 'new-tail'
|
517
|
+
view['name'] = 'newer-tail'
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
},
|
522
|
+
)
|
523
|
+
end
|
524
|
+
|
525
|
+
let(:v1_serialization_data) do
|
526
|
+
{
|
527
|
+
ViewModel::TYPE_ATTRIBUTE => viewmodel_class.view_name,
|
528
|
+
ViewModel::VERSION_ATTRIBUTE => 1,
|
529
|
+
ViewModel::ID_ATTRIBUTE => viewmodel.id,
|
530
|
+
'name' => viewmodel.name,
|
531
|
+
'next' => { ViewModel::REFERENCE_ATTRIBUTE => viewmodel.next.to_reference.stable_reference },
|
532
|
+
ViewModel::MIGRATED_ATTRIBUTE => true,
|
533
|
+
}
|
534
|
+
end
|
535
|
+
|
536
|
+
let(:v1_serialization_references) do
|
537
|
+
old_tail = viewmodel.next
|
538
|
+
old_tail_ref = old_tail.to_reference.stable_reference
|
539
|
+
{
|
540
|
+
old_tail_ref => {
|
541
|
+
ViewModel::TYPE_ATTRIBUTE => viewmodel_class.view_name,
|
542
|
+
ViewModel::VERSION_ATTRIBUTE => 1,
|
543
|
+
ViewModel::ID_ATTRIBUTE => old_tail.id,
|
544
|
+
'name' => 'old-tail',
|
545
|
+
'next' => { ViewModel::REFERENCE_ATTRIBUTE => 'ref:s:new_tail' },
|
546
|
+
ViewModel::MIGRATED_ATTRIBUTE => true,
|
547
|
+
},
|
548
|
+
'ref:s:new_tail' => {
|
549
|
+
ViewModel::TYPE_ATTRIBUTE => viewmodel_class.view_name,
|
550
|
+
ViewModel::VERSION_ATTRIBUTE => 1,
|
551
|
+
ViewModel::ID_ATTRIBUTE => 100,
|
552
|
+
'name' => 'newer-tail',
|
553
|
+
'next' => nil,
|
554
|
+
ViewModel::MIGRATED_ATTRIBUTE => true,
|
555
|
+
},
|
556
|
+
}
|
557
|
+
end
|
558
|
+
|
559
|
+
let(:v1_serialization) do
|
560
|
+
{
|
561
|
+
'data' => v1_serialization_data,
|
562
|
+
'references' => v1_serialization_references,
|
563
|
+
}
|
564
|
+
end
|
565
|
+
|
566
|
+
describe 'downwards' do
|
567
|
+
let(:migrator) { down_migrator }
|
568
|
+
let(:subject) { current_serialization.deep_dup }
|
569
|
+
|
570
|
+
it 'migrates' do
|
571
|
+
migrate!
|
572
|
+
|
573
|
+
assert_equal(v1_serialization, subject)
|
574
|
+
end
|
575
|
+
end
|
576
|
+
end
|
483
577
|
end
|
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.7.
|
4
|
+
version: 3.7.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: 2022-11-
|
11
|
+
date: 2022-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|