iknow_view_models 3.2.3 → 3.2.8
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.rb +9 -1
- data/lib/view_model/active_record.rb +4 -5
- data/lib/view_model/registry.rb +13 -1
- data/test/unit/view_model/record_test.rb +102 -84
- data/test/unit/view_model/registry_test.rb +38 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6955b0705dee2437a934217bad89cc3471bc947a17f7d5b082a1b536ac492dd
|
4
|
+
data.tar.gz: bb831ef743c2ac4f4cc5a0400e41dc9ea14ca71f8994e3e6a0f8f94b48a30035
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba2b82a74b00f528a9bbc8aca178ea22daf2689f243195586632ed4a82497e42b61924ac44603f67fa79caf5b1ae1841902f7fe9897f5be0c8d6e3cef77291e4
|
7
|
+
data.tar.gz: 56fa447075a0cb6bc401eb70b871ee0d72aae29e938a14aaaecedc17e0ea7c6fcecd1dea71f5b3058b19ae83fe796a6a2e21505a892bdb2fa3304281ff774542
|
data/lib/view_model.rb
CHANGED
@@ -243,9 +243,17 @@ class ViewModel
|
|
243
243
|
schema_version == self.schema_version
|
244
244
|
end
|
245
245
|
|
246
|
+
def schema_versions(viewmodels)
|
247
|
+
viewmodels.each_with_object({}) do |view, h|
|
248
|
+
h[view.view_name] = view.schema_version
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
246
252
|
def schema_hash(schema_versions)
|
247
253
|
version_string = schema_versions.to_a.sort.join(',')
|
248
|
-
|
254
|
+
# We want a short hash value, as this will be used in cache keys
|
255
|
+
hash = Digest::SHA256.digest(version_string).byteslice(0, 16)
|
256
|
+
Base64.urlsafe_encode64(hash, padding: false)
|
249
257
|
end
|
250
258
|
|
251
259
|
def preload_for_serialization(viewmodels, serialize_context: new_serialize_context, include_referenced: true, lock: nil)
|
@@ -235,7 +235,7 @@ class ViewModel::ActiveRecord < ViewModel::Record
|
|
235
235
|
next unless include_external || !data.external?
|
236
236
|
|
237
237
|
data.viewmodel_classes.each do |vm|
|
238
|
-
vm.dependent_viewmodels(seen, include_referenced: include_referenced)
|
238
|
+
vm.dependent_viewmodels(seen, include_referenced: include_referenced, include_external: include_external)
|
239
239
|
end
|
240
240
|
end
|
241
241
|
|
@@ -243,11 +243,10 @@ class ViewModel::ActiveRecord < ViewModel::Record
|
|
243
243
|
end
|
244
244
|
|
245
245
|
def deep_schema_version(include_referenced: true, include_external: true)
|
246
|
-
(@deep_schema_version ||= {})[include_referenced] ||=
|
246
|
+
(@deep_schema_version ||= {})[[include_referenced, include_external]] ||=
|
247
247
|
begin
|
248
|
-
dependent_viewmodels(include_referenced: include_referenced, include_external: include_external)
|
249
|
-
|
250
|
-
end.freeze
|
248
|
+
vms = dependent_viewmodels(include_referenced: include_referenced, include_external: include_external)
|
249
|
+
ViewModel.schema_versions(vms).freeze
|
251
250
|
end
|
252
251
|
end
|
253
252
|
|
data/lib/view_model/registry.rb
CHANGED
@@ -6,7 +6,8 @@ class ViewModel::Registry
|
|
6
6
|
DEFERRED_NAME = Object.new
|
7
7
|
|
8
8
|
class << self
|
9
|
-
delegate :for_view_name, :register, :default_view_name, :infer_model_class_name,
|
9
|
+
delegate :for_view_name, :register, :default_view_name, :infer_model_class_name,
|
10
|
+
:clear_removed_classes!, :all, :roots,
|
10
11
|
to: :instance
|
11
12
|
end
|
12
13
|
|
@@ -33,6 +34,17 @@ class ViewModel::Registry
|
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
37
|
+
def all
|
38
|
+
@lock.synchronize do
|
39
|
+
resolve_deferred_classes
|
40
|
+
@viewmodel_classes_by_name.values
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def roots
|
45
|
+
all.select { |c| c.root? }
|
46
|
+
end
|
47
|
+
|
36
48
|
def register(viewmodel, as: DEFERRED_NAME)
|
37
49
|
@lock.synchronize do
|
38
50
|
@deferred_viewmodel_classes << [viewmodel, as]
|
@@ -365,122 +365,140 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
|
|
365
365
|
end
|
366
366
|
end
|
367
367
|
|
368
|
-
|
368
|
+
describe 'nesting' do
|
369
|
+
let(:nested_model_class) do
|
370
|
+
klass = Struct.new(:member)
|
371
|
+
Object.const_set(:Nested, klass)
|
372
|
+
klass
|
373
|
+
end
|
369
374
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
+
let(:nested_viewmodel_class) do
|
376
|
+
mc = nested_model_class
|
377
|
+
klass = Class.new(TestViewModel) do
|
378
|
+
self.view_name = 'Nested'
|
379
|
+
self.model_class = mc
|
380
|
+
attribute :member
|
381
|
+
end
|
382
|
+
Object.const_set(:NestedView, klass)
|
383
|
+
klass
|
384
|
+
end
|
375
385
|
|
376
|
-
|
377
|
-
|
378
|
-
|
386
|
+
def teardown
|
387
|
+
Object.send(:remove_const, :Nested)
|
388
|
+
Object.send(:remove_const, :NestedView)
|
389
|
+
ActiveSupport::Dependencies::Reference.clear!
|
390
|
+
super
|
391
|
+
end
|
379
392
|
|
380
|
-
|
393
|
+
describe 'with nested viewmodel' do
|
394
|
+
let(:default_nested_model) { nested_model_class.new('member') }
|
395
|
+
let(:default_nested_view) { view_base.merge('_type' => 'Nested', 'member' => 'member') }
|
381
396
|
|
382
|
-
|
383
|
-
let(:default_model_values) { { nested: default_nested_model } }
|
397
|
+
let(:attributes) { { simple: {}, nested: { using: nested_viewmodel_class } } }
|
384
398
|
|
385
|
-
|
386
|
-
|
387
|
-
access_control: access_control)
|
388
|
-
end
|
399
|
+
let(:default_view_values) { { nested: default_nested_view } }
|
400
|
+
let(:default_model_values) { { nested: default_nested_model } }
|
389
401
|
|
390
|
-
|
391
|
-
|
392
|
-
|
402
|
+
let(:update_context) do
|
403
|
+
TestDeserializeContext.new(targets: [default_model, default_nested_model],
|
404
|
+
access_control: access_control)
|
405
|
+
end
|
393
406
|
|
394
|
-
|
395
|
-
|
407
|
+
include CanSerialize
|
408
|
+
include CanDeserializeToNew
|
409
|
+
include CanDeserializeToExisting
|
396
410
|
|
397
|
-
|
411
|
+
it 'can update the nested value' do
|
412
|
+
new_view = default_view.merge('nested' => default_nested_view.merge('member' => 'changed'))
|
398
413
|
|
399
|
-
|
400
|
-
assert(default_nested_model.equal?(vm.model.nested), 'returned nested model was not the same')
|
414
|
+
vm = viewmodel_class.deserialize_from_view(new_view, deserialize_context: update_context)
|
401
415
|
|
402
|
-
|
416
|
+
assert(default_model.equal?(vm.model), 'returned model was not the same')
|
417
|
+
assert(default_nested_model.equal?(vm.model.nested), 'returned nested model was not the same')
|
403
418
|
|
404
|
-
|
405
|
-
assert_edited(vm.nested, changed_attributes: [:member])
|
406
|
-
end
|
419
|
+
assert_equal('changed', default_model.nested.member)
|
407
420
|
|
408
|
-
|
409
|
-
|
410
|
-
|
421
|
+
assert_unchanged(vm)
|
422
|
+
assert_edited(vm.nested, changed_attributes: [:member])
|
423
|
+
end
|
411
424
|
|
412
|
-
|
413
|
-
|
425
|
+
it 'can replace the nested value' do
|
426
|
+
# The value will be unified if it is different after deserialization
|
427
|
+
new_view = default_view.merge('nested' => default_nested_view.merge('member' => 'changed'))
|
414
428
|
|
415
|
-
|
429
|
+
partial_update_context = TestDeserializeContext.new(targets: [default_model],
|
430
|
+
access_control: access_control)
|
416
431
|
|
417
|
-
|
418
|
-
refute(default_nested_model.equal?(vm.model.nested), 'returned nested model was the same')
|
432
|
+
vm = viewmodel_class.deserialize_from_view(new_view, deserialize_context: partial_update_context)
|
419
433
|
|
420
|
-
|
421
|
-
|
434
|
+
assert(default_model.equal?(vm.model), 'returned model was not the same')
|
435
|
+
refute(default_nested_model.equal?(vm.model.nested), 'returned nested model was the same')
|
436
|
+
|
437
|
+
assert_edited(vm, new: false, changed_attributes: [:nested])
|
438
|
+
assert_edited(vm.nested, new: true, changed_attributes: [:member])
|
439
|
+
end
|
422
440
|
end
|
423
|
-
end
|
424
441
|
|
425
|
-
|
426
|
-
|
427
|
-
|
442
|
+
describe 'with array of nested viewmodel' do
|
443
|
+
let(:default_nested_model_1) { nested_model_class.new('member1') }
|
444
|
+
let(:default_nested_view_1) { view_base.merge('_type' => 'Nested', 'member' => 'member1') }
|
428
445
|
|
429
|
-
|
430
|
-
|
446
|
+
let(:default_nested_model_2) { nested_model_class.new('member2') }
|
447
|
+
let(:default_nested_view_2) { view_base.merge('_type' => 'Nested', 'member' => 'member2') }
|
431
448
|
|
432
|
-
|
449
|
+
let(:attributes) { { simple: {}, nested: { using: nested_viewmodel_class, array: true } } }
|
433
450
|
|
434
|
-
|
435
|
-
|
451
|
+
let(:default_view_values) { { nested: [default_nested_view_1, default_nested_view_2] } }
|
452
|
+
let(:default_model_values) { { nested: [default_nested_model_1, default_nested_model_2] } }
|
436
453
|
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
454
|
+
let(:update_context) {
|
455
|
+
TestDeserializeContext.new(targets: [default_model, default_nested_model_1, default_nested_model_2],
|
456
|
+
access_control: access_control)
|
457
|
+
}
|
441
458
|
|
442
|
-
|
443
|
-
|
444
|
-
|
459
|
+
include CanSerialize
|
460
|
+
include CanDeserializeToNew
|
461
|
+
include CanDeserializeToExisting
|
445
462
|
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
463
|
+
it 'rejects change to attribute' do
|
464
|
+
new_view = default_view.merge('nested' => 'terrible')
|
465
|
+
ex = assert_raises(ViewModel::DeserializationError::InvalidAttributeType) do
|
466
|
+
viewmodel_class.deserialize_from_view(new_view, deserialize_context: update_context)
|
467
|
+
end
|
468
|
+
assert_equal('nested', ex.attribute)
|
469
|
+
assert_equal('Array', ex.expected_type)
|
470
|
+
assert_equal('String', ex.provided_type)
|
450
471
|
end
|
451
|
-
assert_equal('nested', ex.attribute)
|
452
|
-
assert_equal('Array', ex.expected_type)
|
453
|
-
assert_equal('String', ex.provided_type)
|
454
|
-
end
|
455
472
|
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
473
|
+
it 'can edit a nested value' do
|
474
|
+
default_view['nested'][0]['member'] = 'changed'
|
475
|
+
vm = viewmodel_class.deserialize_from_view(default_view, deserialize_context: update_context)
|
476
|
+
assert(default_model.equal?(vm.model), 'returned model was not the same')
|
477
|
+
assert_equal(2, vm.model.nested.size)
|
478
|
+
assert(default_nested_model_1.equal?(vm.model.nested[0]))
|
479
|
+
assert(default_nested_model_2.equal?(vm.model.nested[1]))
|
463
480
|
|
464
|
-
|
465
|
-
|
466
|
-
|
481
|
+
assert_unchanged(vm)
|
482
|
+
assert_edited(vm.nested[0], changed_attributes: [:member])
|
483
|
+
end
|
467
484
|
|
468
|
-
|
469
|
-
|
485
|
+
it 'can append a nested value' do
|
486
|
+
default_view['nested'] << view_base.merge('_type' => 'Nested', 'member' => 'member3')
|
470
487
|
|
471
|
-
|
488
|
+
vm = viewmodel_class.deserialize_from_view(default_view, deserialize_context: update_context)
|
472
489
|
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
490
|
+
assert(default_model.equal?(vm.model), 'returned model was not the same')
|
491
|
+
assert_equal(3, vm.model.nested.size)
|
492
|
+
assert(default_nested_model_1.equal?(vm.model.nested[0]))
|
493
|
+
assert(default_nested_model_2.equal?(vm.model.nested[1]))
|
477
494
|
|
478
|
-
|
479
|
-
|
480
|
-
|
495
|
+
vm.model.nested.each_with_index do |nvm, i|
|
496
|
+
assert_equal("member#{i + 1}", nvm.member)
|
497
|
+
end
|
481
498
|
|
482
|
-
|
483
|
-
|
499
|
+
assert_edited(vm, changed_attributes: [:nested])
|
500
|
+
assert_edited(vm.nested[2], new: true, changed_attributes: [:member])
|
501
|
+
end
|
484
502
|
end
|
485
503
|
end
|
486
504
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'minitest/unit'
|
5
|
+
require 'minitest/hooks'
|
6
|
+
|
7
|
+
require_relative '../../helpers/arvm_test_utilities'
|
8
|
+
require_relative '../../helpers/arvm_test_models'
|
9
|
+
require_relative '../../helpers/viewmodel_spec_helpers'
|
10
|
+
|
11
|
+
require 'view_model'
|
12
|
+
require 'view_model/active_record'
|
13
|
+
|
14
|
+
class ViewModel
|
15
|
+
class RegistryTest < ActiveSupport::TestCase
|
16
|
+
using ViewModel::Utils::Collections
|
17
|
+
include ARVMTestUtilities
|
18
|
+
extend Minitest::Spec::DSL
|
19
|
+
include ViewModelSpecHelpers::ParentAndBelongsToChild
|
20
|
+
|
21
|
+
before(:each) do
|
22
|
+
ViewModel::Registry.clear_removed_classes!
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'registers the views' do
|
26
|
+
assert_equal(ViewModel::Registry.for_view_name(view_name), viewmodel_class)
|
27
|
+
assert_equal(ViewModel::Registry.for_view_name(child_view_name), child_viewmodel_class)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'enumerates the views' do
|
31
|
+
assert_contains_exactly([ViewModel::ErrorView, viewmodel_class, child_viewmodel_class], ViewModel::Registry.all)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'enumerates the root views' do
|
35
|
+
assert_contains_exactly([viewmodel_class], ViewModel::Registry.roots)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
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.2.
|
4
|
+
version: 3.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- iKnow Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -468,6 +468,7 @@ files:
|
|
468
468
|
- test/unit/view_model/controller_test.rb
|
469
469
|
- test/unit/view_model/deserialization_error/unique_violation_test.rb
|
470
470
|
- test/unit/view_model/record_test.rb
|
471
|
+
- test/unit/view_model/registry_test.rb
|
471
472
|
- test/unit/view_model/traversal_context_test.rb
|
472
473
|
- test/unit/view_model_test.rb
|
473
474
|
homepage: https://github.com/iknow/cerego_view_models
|
@@ -527,5 +528,6 @@ test_files:
|
|
527
528
|
- test/unit/view_model/controller_test.rb
|
528
529
|
- test/unit/view_model/deserialization_error/unique_violation_test.rb
|
529
530
|
- test/unit/view_model/record_test.rb
|
531
|
+
- test/unit/view_model/registry_test.rb
|
530
532
|
- test/unit/view_model/traversal_context_test.rb
|
531
533
|
- test/unit/view_model_test.rb
|