iknow_view_models 3.4.2 → 3.5.1
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 +4 -4
- data/iknow_view_models.gemspec +2 -2
- data/lib/iknow_view_models/version.rb +1 -1
- data/lib/view_model/active_record/association_data.rb +3 -1
- data/lib/view_model/active_record/association_manipulation.rb +186 -49
- data/lib/view_model/active_record/cloner.rb +13 -12
- data/lib/view_model/active_record/collection_nested_controller.rb +5 -2
- data/lib/view_model/active_record/controller_base.rb +45 -6
- data/lib/view_model/active_record/nested_controller_base.rb +125 -13
- data/lib/view_model/active_record/singular_nested_controller.rb +5 -2
- data/lib/view_model/active_record.rb +46 -5
- data/lib/view_model/callbacks.rb +1 -1
- data/lib/view_model/controller.rb +24 -2
- data/lib/view_model/migrator.rb +26 -0
- data/lib/view_model/record.rb +1 -1
- data/lib/view_model/schemas.rb +44 -0
- data/lib/view_model.rb +12 -0
- data/test/helpers/arvm_test_utilities.rb +65 -0
- data/test/helpers/controller_test_helpers.rb +65 -34
- data/test/unit/view_model/active_record/controller_nested_test.rb +599 -0
- data/test/unit/view_model/active_record/controller_test.rb +6 -362
- data/test/unit/view_model/active_record/has_many_test.rb +24 -7
- data/test/unit/view_model/active_record/has_many_through_test.rb +28 -12
- data/test/unit/view_model/active_record/migration_test.rb +29 -0
- data/test/unit/view_model/traversal_context_test.rb +15 -1
- metadata +7 -5
@@ -15,83 +15,19 @@ class ViewModel::ActiveRecord::ControllerTest < ActiveSupport::TestCase
|
|
15
15
|
include ControllerTestModels
|
16
16
|
include ControllerTestControllers
|
17
17
|
|
18
|
-
def
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
def each_hook_span(trace)
|
23
|
-
return enum_for(:each_hook_span, trace) unless block_given?
|
24
|
-
|
25
|
-
hook_nesting = []
|
26
|
-
|
27
|
-
trace.each_with_index do |t, i|
|
28
|
-
case t.hook
|
29
|
-
when ViewModel::Callbacks::Hook::OnChange,
|
30
|
-
ViewModel::Callbacks::Hook::BeforeValidate
|
31
|
-
# ignore
|
32
|
-
when ViewModel::Callbacks::Hook::BeforeVisit,
|
33
|
-
ViewModel::Callbacks::Hook::BeforeDeserialize
|
34
|
-
hook_nesting.push([t, i])
|
35
|
-
|
36
|
-
when ViewModel::Callbacks::Hook::AfterVisit,
|
37
|
-
ViewModel::Callbacks::Hook::AfterDeserialize
|
38
|
-
(nested_top, nested_index) = hook_nesting.pop
|
39
|
-
|
40
|
-
unless nested_top.hook.name == t.hook.name.sub(/^After/, 'Before')
|
41
|
-
raise "Invalid nesting, processing '#{t.hook.name}', expected matching '#{nested_top.hook.name}'"
|
42
|
-
end
|
43
|
-
|
44
|
-
unless nested_top.view == t.view
|
45
|
-
raise "Invalid nesting, processing '#{t.hook.name}', " \
|
46
|
-
"expected viewmodel '#{t.view}' to match '#{nested_top.view}'"
|
47
|
-
end
|
48
|
-
|
49
|
-
yield t.view, (nested_index..i), t.hook.name.sub(/^After/, '')
|
50
|
-
|
51
|
-
else
|
52
|
-
raise 'Unexpected hook type'
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def show_span(view, range, hook)
|
58
|
-
"#{view.class.name}(#{view.id}) #{range} #{hook}"
|
59
|
-
end
|
18
|
+
def before_all
|
19
|
+
super
|
60
20
|
|
61
|
-
|
62
|
-
spans.select do |_view, range, _hook|
|
63
|
-
inner_range != range && range.cover?(inner_range.min) && range.cover?(inner_range.max)
|
64
|
-
end
|
21
|
+
build_controller_test_models
|
65
22
|
end
|
66
23
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
spans.reject { |view, _range, _hook| view.class == ParentView }.each do |view, range, hook|
|
71
|
-
enclosing_spans = enclosing_hooks(spans, range)
|
72
|
-
|
73
|
-
enclosing_parent_hook = enclosing_spans.detect do |other_view, _other_range, other_hook|
|
74
|
-
other_hook == hook && other_view.class == ParentView
|
75
|
-
end
|
76
|
-
|
77
|
-
next if enclosing_parent_hook
|
78
|
-
|
79
|
-
self_str = show_span(view, range, hook)
|
80
|
-
enclosing_str = enclosing_spans.map { |ov, ora, oh| show_span(ov, ora, oh) }.join("\n")
|
81
|
-
assert_not_nil(
|
82
|
-
enclosing_parent_hook,
|
83
|
-
"Invalid nesting of hook: #{self_str}\nEnclosing hooks:\n#{enclosing_str}")
|
84
|
-
end
|
24
|
+
def visit(hook, view)
|
25
|
+
CallbackTracer::Visit.new(hook, view)
|
85
26
|
end
|
86
27
|
|
87
28
|
def setup
|
88
29
|
super
|
89
|
-
@parent =
|
90
|
-
children: [Child.new(name: 'c1', position: 1.0),
|
91
|
-
Child.new(name: 'c2', position: 2.0),],
|
92
|
-
label: Label.new,
|
93
|
-
target: Target.new)
|
94
|
-
|
30
|
+
@parent = make_parent
|
95
31
|
@parent_view = ParentView.new(@parent)
|
96
32
|
|
97
33
|
enable_logging!
|
@@ -317,296 +253,4 @@ class ViewModel::ActiveRecord::ControllerTest < ActiveSupport::TestCase
|
|
317
253
|
assert_equal(404, parentcontroller.status)
|
318
254
|
end
|
319
255
|
|
320
|
-
#### Controller for nested model
|
321
|
-
|
322
|
-
def test_nested_collection_index_associated
|
323
|
-
_distractor = Parent.create(name: 'p2', children: [Child.new(name: 'c3', position: 1)])
|
324
|
-
|
325
|
-
childcontroller = ChildController.new(params: { parent_id: @parent.id })
|
326
|
-
childcontroller.invoke(:index_associated)
|
327
|
-
|
328
|
-
assert_equal(200, childcontroller.status)
|
329
|
-
|
330
|
-
expected_children = @parent.children
|
331
|
-
assert_equal({ 'data' => expected_children.map { |c| ChildView.new(c).to_hash } },
|
332
|
-
childcontroller.hash_response)
|
333
|
-
|
334
|
-
assert_all_hooks_nested_inside_parent_hook(childcontroller.hook_trace)
|
335
|
-
end
|
336
|
-
|
337
|
-
def test_nested_collection_index
|
338
|
-
distractor = Parent.create(name: 'p2', children: [Child.new(name: 'c3', position: 1)])
|
339
|
-
childcontroller = ChildController.new
|
340
|
-
|
341
|
-
childcontroller.invoke(:index)
|
342
|
-
|
343
|
-
assert_equal(200, childcontroller.status)
|
344
|
-
|
345
|
-
expected_children = @parent.children + distractor.children
|
346
|
-
assert_equal({ 'data' => expected_children.map { |c| ChildView.new(c).to_hash } },
|
347
|
-
childcontroller.hash_response)
|
348
|
-
end
|
349
|
-
|
350
|
-
def test_nested_collection_append_one
|
351
|
-
data = { '_type' => 'Child', 'name' => 'c3' }
|
352
|
-
childcontroller = ChildController.new(params: { parent_id: @parent.id, data: data })
|
353
|
-
|
354
|
-
childcontroller.invoke(:append)
|
355
|
-
|
356
|
-
assert_equal(200, childcontroller.status, childcontroller.hash_response)
|
357
|
-
|
358
|
-
@parent.reload
|
359
|
-
|
360
|
-
assert_equal(%w[c1 c2 c3], @parent.children.order(:position).pluck(:name))
|
361
|
-
assert_equal({ 'data' => ChildView.new(@parent.children.last).to_hash },
|
362
|
-
childcontroller.hash_response)
|
363
|
-
|
364
|
-
assert_all_hooks_nested_inside_parent_hook(childcontroller.hook_trace)
|
365
|
-
end
|
366
|
-
|
367
|
-
def test_nested_collection_append_many
|
368
|
-
data = [{ '_type' => 'Child', 'name' => 'c3' },
|
369
|
-
{ '_type' => 'Child', 'name' => 'c4' },]
|
370
|
-
|
371
|
-
childcontroller = ChildController.new(params: { parent_id: @parent.id, data: data })
|
372
|
-
childcontroller.invoke(:append)
|
373
|
-
|
374
|
-
assert_equal(200, childcontroller.status, childcontroller.hash_response)
|
375
|
-
|
376
|
-
@parent.reload
|
377
|
-
|
378
|
-
assert_equal(%w[c1 c2 c3 c4], @parent.children.order(:position).pluck(:name))
|
379
|
-
new_children_hashes = @parent.children.last(2).map { |c| ChildView.new(c).to_hash }
|
380
|
-
assert_equal({ 'data' => new_children_hashes },
|
381
|
-
childcontroller.hash_response)
|
382
|
-
|
383
|
-
assert_all_hooks_nested_inside_parent_hook(childcontroller.hook_trace)
|
384
|
-
end
|
385
|
-
|
386
|
-
# FIXME: nested controllers really need to be to other roots; children aren't roots.
|
387
|
-
def test_nested_collection_replace
|
388
|
-
# Parent.children
|
389
|
-
old_children = @parent.children
|
390
|
-
|
391
|
-
data = [{ '_type' => 'Child', 'name' => 'newc1' },
|
392
|
-
{ '_type' => 'Child', 'name' => 'newc2' },]
|
393
|
-
|
394
|
-
childcontroller = ChildController.new(params: { parent_id: @parent.id, data: data })
|
395
|
-
childcontroller.invoke(:replace)
|
396
|
-
|
397
|
-
assert_equal(200, childcontroller.status, childcontroller.hash_response)
|
398
|
-
|
399
|
-
@parent.reload
|
400
|
-
|
401
|
-
assert_equal(%w[newc1 newc2], @parent.children.order(:position).pluck(:name))
|
402
|
-
assert_predicate(Child.where(id: old_children.map(&:id)), :empty?)
|
403
|
-
|
404
|
-
assert_all_hooks_nested_inside_parent_hook(childcontroller.hook_trace)
|
405
|
-
end
|
406
|
-
|
407
|
-
def test_nested_collection_replace_bad_data
|
408
|
-
data = [{ 'name' => 'nc' }]
|
409
|
-
childcontroller = ChildController.new(params: { parent_id: @parent.id, data: data })
|
410
|
-
|
411
|
-
childcontroller.invoke(:replace)
|
412
|
-
|
413
|
-
assert_equal(400, childcontroller.status)
|
414
|
-
|
415
|
-
assert_all_hooks_nested_inside_parent_hook(childcontroller.hook_trace)
|
416
|
-
end
|
417
|
-
|
418
|
-
def test_nested_collection_disassociate_one
|
419
|
-
old_child = @parent.children.first
|
420
|
-
childcontroller = ChildController.new(params: { parent_id: @parent.id, id: old_child.id })
|
421
|
-
childcontroller.invoke(:disassociate)
|
422
|
-
|
423
|
-
assert_equal(200, childcontroller.status, childcontroller.hash_response)
|
424
|
-
|
425
|
-
@parent.reload
|
426
|
-
|
427
|
-
assert_equal(%w[c2], @parent.children.order(:position).pluck(:name))
|
428
|
-
assert_predicate(Child.where(id: old_child.id), :empty?)
|
429
|
-
|
430
|
-
assert_all_hooks_nested_inside_parent_hook(childcontroller.hook_trace)
|
431
|
-
end
|
432
|
-
|
433
|
-
def test_nested_collection_disassociate_many
|
434
|
-
old_children = @parent.children
|
435
|
-
|
436
|
-
childcontroller = ChildController.new(params: { parent_id: @parent.id })
|
437
|
-
childcontroller.invoke(:disassociate_all)
|
438
|
-
|
439
|
-
assert_equal(200, childcontroller.status, childcontroller.hash_response)
|
440
|
-
|
441
|
-
@parent.reload
|
442
|
-
|
443
|
-
assert_predicate(@parent.children, :empty?)
|
444
|
-
assert_predicate(Child.where(id: old_children.map(&:id)), :empty?)
|
445
|
-
|
446
|
-
assert_all_hooks_nested_inside_parent_hook(childcontroller.hook_trace)
|
447
|
-
end
|
448
|
-
|
449
|
-
# direct methods on nested controller
|
450
|
-
def test_nested_collection_destroy
|
451
|
-
old_child = @parent.children.first
|
452
|
-
childcontroller = ChildController.new(params: { id: old_child.id })
|
453
|
-
childcontroller.invoke(:destroy)
|
454
|
-
|
455
|
-
assert_equal(200, childcontroller.status, childcontroller.hash_response)
|
456
|
-
|
457
|
-
@parent.reload
|
458
|
-
|
459
|
-
assert_equal(%w[c2], @parent.children.order(:position).pluck(:name))
|
460
|
-
assert_predicate(Child.where(id: old_child.id), :empty?)
|
461
|
-
end
|
462
|
-
|
463
|
-
def test_nested_collection_update
|
464
|
-
old_child = @parent.children.first
|
465
|
-
|
466
|
-
data = { 'id' => old_child.id,
|
467
|
-
'_type' => 'Child',
|
468
|
-
'name' => 'new_name' }
|
469
|
-
|
470
|
-
childcontroller = ChildController.new(params: { data: data })
|
471
|
-
childcontroller.invoke(:create)
|
472
|
-
|
473
|
-
assert_equal(200, childcontroller.status, childcontroller.hash_response)
|
474
|
-
|
475
|
-
old_child.reload
|
476
|
-
|
477
|
-
assert_equal('new_name', old_child.name)
|
478
|
-
assert_equal({ 'data' => ChildView.new(old_child).to_hash },
|
479
|
-
childcontroller.hash_response)
|
480
|
-
end
|
481
|
-
|
482
|
-
def test_nested_collection_show
|
483
|
-
old_child = @parent.children.first
|
484
|
-
|
485
|
-
childcontroller = ChildController.new(params: { id: old_child.id })
|
486
|
-
childcontroller.invoke(:show)
|
487
|
-
|
488
|
-
assert_equal({ 'data' => ChildView.new(old_child).to_hash },
|
489
|
-
childcontroller.hash_response)
|
490
|
-
|
491
|
-
assert_equal(200, childcontroller.status)
|
492
|
-
end
|
493
|
-
|
494
|
-
## Single association
|
495
|
-
|
496
|
-
def test_nested_singular_replace_from_parent
|
497
|
-
old_label = @parent.label
|
498
|
-
|
499
|
-
data = { '_type' => 'Label', 'text' => 'new label' }
|
500
|
-
labelcontroller = LabelController.new(params: { parent_id: @parent.id, data: data })
|
501
|
-
labelcontroller.invoke(:create_associated)
|
502
|
-
|
503
|
-
assert_equal(200, labelcontroller.status, labelcontroller.hash_response)
|
504
|
-
|
505
|
-
@parent.reload
|
506
|
-
|
507
|
-
assert_equal({ 'data' => { '_type' => 'Label',
|
508
|
-
'_version' => 1,
|
509
|
-
'id' => @parent.label.id,
|
510
|
-
'text' => 'new label' } },
|
511
|
-
labelcontroller.hash_response)
|
512
|
-
|
513
|
-
refute_equal(old_label, @parent.label)
|
514
|
-
assert_equal('new label', @parent.label.text)
|
515
|
-
|
516
|
-
assert_all_hooks_nested_inside_parent_hook(labelcontroller.hook_trace)
|
517
|
-
end
|
518
|
-
|
519
|
-
def test_nested_singular_show_from_parent
|
520
|
-
old_label = @parent.label
|
521
|
-
|
522
|
-
labelcontroller = LabelController.new(params: { parent_id: @parent.id })
|
523
|
-
labelcontroller.invoke(:show_associated)
|
524
|
-
|
525
|
-
assert_equal(200, labelcontroller.status, labelcontroller.hash_response)
|
526
|
-
|
527
|
-
assert_equal({ 'data' => LabelView.new(old_label).to_hash },
|
528
|
-
labelcontroller.hash_response)
|
529
|
-
|
530
|
-
assert_all_hooks_nested_inside_parent_hook(labelcontroller.hook_trace)
|
531
|
-
end
|
532
|
-
|
533
|
-
def test_nested_singular_destroy_from_parent
|
534
|
-
old_label = @parent.label
|
535
|
-
|
536
|
-
labelcontroller = LabelController.new(params: { parent_id: @parent.id })
|
537
|
-
labelcontroller.invoke(:destroy_associated)
|
538
|
-
|
539
|
-
@parent.reload
|
540
|
-
|
541
|
-
assert_equal(200, labelcontroller.status, labelcontroller.hash_response)
|
542
|
-
assert_equal({ 'data' => nil }, labelcontroller.hash_response)
|
543
|
-
|
544
|
-
assert_nil(@parent.label)
|
545
|
-
assert_predicate(Label.where(id: old_label.id), :empty?)
|
546
|
-
|
547
|
-
assert_all_hooks_nested_inside_parent_hook(labelcontroller.hook_trace)
|
548
|
-
end
|
549
|
-
|
550
|
-
def test_nested_singular_update_from_parent
|
551
|
-
old_label = @parent.label
|
552
|
-
|
553
|
-
data = { '_type' => 'Label', 'id' => old_label.id, 'text' => 'new label' }
|
554
|
-
labelcontroller = LabelController.new(params: { parent_id: @parent.id, data: data })
|
555
|
-
labelcontroller.invoke(:create_associated)
|
556
|
-
|
557
|
-
assert_equal(200, labelcontroller.status, labelcontroller.hash_response)
|
558
|
-
|
559
|
-
old_label.reload
|
560
|
-
|
561
|
-
assert_equal('new label', old_label.text)
|
562
|
-
assert_equal({ 'data' => LabelView.new(old_label).to_hash },
|
563
|
-
labelcontroller.hash_response)
|
564
|
-
|
565
|
-
assert_all_hooks_nested_inside_parent_hook(labelcontroller.hook_trace)
|
566
|
-
end
|
567
|
-
|
568
|
-
def test_nested_singular_show_from_id
|
569
|
-
old_label = @parent.label
|
570
|
-
|
571
|
-
labelcontroller = LabelController.new(params: { id: old_label.id })
|
572
|
-
labelcontroller.invoke(:show)
|
573
|
-
|
574
|
-
assert_equal(200, labelcontroller.status, labelcontroller.hash_response)
|
575
|
-
|
576
|
-
assert_equal({ 'data' => LabelView.new(old_label).to_hash },
|
577
|
-
labelcontroller.hash_response)
|
578
|
-
end
|
579
|
-
|
580
|
-
def test_nested_singular_destroy_from_id
|
581
|
-
# can't directly destroy pointed-to label that's referenced from parent:
|
582
|
-
# foreign key violation. Destroy target instead.
|
583
|
-
old_target = @parent.target
|
584
|
-
|
585
|
-
targetcontroller = TargetController.new(params: { id: old_target.id })
|
586
|
-
targetcontroller.invoke(:destroy)
|
587
|
-
|
588
|
-
@parent.reload
|
589
|
-
|
590
|
-
assert_equal(200, targetcontroller.status, targetcontroller.hash_response)
|
591
|
-
assert_equal({ 'data' => nil }, targetcontroller.hash_response)
|
592
|
-
|
593
|
-
assert_nil(@parent.target)
|
594
|
-
assert_predicate(Target.where(id: old_target.id), :empty?)
|
595
|
-
end
|
596
|
-
|
597
|
-
def test_nested_singular_update
|
598
|
-
old_label = @parent.label
|
599
|
-
|
600
|
-
data = { '_type' => 'Label', 'id' => old_label.id, 'text' => 'new label' }
|
601
|
-
labelcontroller = LabelController.new(params: { data: data })
|
602
|
-
labelcontroller.invoke(:create)
|
603
|
-
|
604
|
-
assert_equal(200, labelcontroller.status, labelcontroller.hash_response)
|
605
|
-
|
606
|
-
old_label.reload
|
607
|
-
|
608
|
-
assert_equal('new label', old_label.text)
|
609
|
-
assert_equal({ 'data' => LabelView.new(old_label).to_hash },
|
610
|
-
labelcontroller.hash_response)
|
611
|
-
end
|
612
256
|
end
|
@@ -195,8 +195,8 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase
|
|
195
195
|
update([{ '_type' => 'Child', 'id' => old_children.first.id, 'name' => 'renamed p1c1' }])
|
196
196
|
end
|
197
197
|
|
198
|
-
|
199
|
-
new_child =
|
198
|
+
updated = pv.replace_associated(:children, update, deserialize_context: context)
|
199
|
+
new_child = updated.detect { |c| c.name == 'new_child' }
|
200
200
|
|
201
201
|
expected_edit_checks = [pv.to_reference,
|
202
202
|
ViewModel::Reference.new(child_viewmodel_class, new_child.id),
|
@@ -206,8 +206,11 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase
|
|
206
206
|
assert_contains_exactly(expected_edit_checks,
|
207
207
|
context.valid_edit_refs)
|
208
208
|
|
209
|
-
assert_equal(
|
210
|
-
|
209
|
+
assert_equal(2, updated.size)
|
210
|
+
assert_contains_exactly(
|
211
|
+
['renamed p1c1', 'new_child'],
|
212
|
+
updated.map(&:name),
|
213
|
+
)
|
211
214
|
|
212
215
|
@model1.reload
|
213
216
|
assert_equal(['renamed p1c1', 'p1c2', 'new_child'], @model1.children.order(:position).map(&:name))
|
@@ -972,7 +975,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase
|
|
972
975
|
'children' => [
|
973
976
|
{ '_type' => 'Dog', 'dog_number' => 1 },
|
974
977
|
{ '_type' => 'Cat', 'cat_number' => 2 },
|
975
|
-
]
|
978
|
+
],
|
976
979
|
}
|
977
980
|
|
978
981
|
pv = ModelView.deserialize_from_view(view)
|
@@ -1010,7 +1013,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase
|
|
1010
1013
|
it 'functional updates' do
|
1011
1014
|
model = create_model!
|
1012
1015
|
|
1013
|
-
alter_by_view!(ModelView, model) do |view,
|
1016
|
+
alter_by_view!(ModelView, model) do |view, _refs|
|
1014
1017
|
view['children'] = build_fupdate do
|
1015
1018
|
append([{ '_type' => 'Cat', 'cat_number' => 100 }])
|
1016
1019
|
end
|
@@ -1021,6 +1024,18 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase
|
|
1021
1024
|
assert_kind_of(Cat, new_child)
|
1022
1025
|
assert_equal(100, new_child.cat_number)
|
1023
1026
|
end
|
1027
|
+
|
1028
|
+
it 'calculates eager_includes' do
|
1029
|
+
includes = viewmodel_class.eager_includes
|
1030
|
+
expected = DeepPreloader::Spec.new(
|
1031
|
+
'children' => DeepPreloader::PolymorphicSpec.new(
|
1032
|
+
{
|
1033
|
+
'Dog' => DeepPreloader::Spec.new,
|
1034
|
+
'Cat' => DeepPreloader::Spec.new,
|
1035
|
+
}))
|
1036
|
+
|
1037
|
+
assert_equal(includes, expected)
|
1038
|
+
end
|
1024
1039
|
end
|
1025
1040
|
|
1026
1041
|
describe 'owned reference children' do
|
@@ -1285,7 +1300,9 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase
|
|
1285
1300
|
it 'replaces children' do
|
1286
1301
|
view = create_viewmodel!
|
1287
1302
|
view.replace_associated(:children,
|
1288
|
-
|
1303
|
+
[{ '_ref' => 'the_child' }],
|
1304
|
+
references: { 'the_child' => { '_type' => 'Child', 'name' => 'newchildname' } }
|
1305
|
+
)
|
1289
1306
|
|
1290
1307
|
view.model.reload
|
1291
1308
|
|
@@ -438,19 +438,24 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
|
|
438
438
|
pv = ParentView.new(@parent1)
|
439
439
|
context = ParentView.new_deserialize_context
|
440
440
|
|
441
|
-
|
442
|
-
|
443
|
-
|
441
|
+
data = [{ ViewModel::REFERENCE_ATTRIBUTE => 'tag1' }]
|
442
|
+
references = { 'tag1' => { '_type' => 'Tag', 'name' => 'new_tag' } }
|
443
|
+
|
444
|
+
updated = pv.replace_associated(:tags,
|
445
|
+
data, references: references,
|
446
|
+
deserialize_context: context)
|
447
|
+
|
448
|
+
post_children = ParentView.new(Parent.find(@parent1.id))._read_association(:tags)
|
444
449
|
|
445
450
|
expected_edit_checks = [pv.to_reference,
|
446
|
-
*
|
451
|
+
*post_children.map(&:to_reference),]
|
447
452
|
|
448
453
|
assert_contains_exactly(expected_edit_checks,
|
449
454
|
context.valid_edit_refs)
|
450
455
|
|
451
|
-
assert_equal(1,
|
452
|
-
assert(
|
453
|
-
assert_equal('new_tag',
|
456
|
+
assert_equal(1, updated.size)
|
457
|
+
assert(updated[0].is_a?(TagView))
|
458
|
+
assert_equal('new_tag', updated[0].name)
|
454
459
|
|
455
460
|
@parent1.reload
|
456
461
|
assert_equal(['new_tag'], tags(@parent1).map(&:name))
|
@@ -467,13 +472,19 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
|
|
467
472
|
tag2 = @tag2
|
468
473
|
|
469
474
|
update = build_fupdate do
|
470
|
-
append([{
|
475
|
+
append([{ViewModel::REFERENCE_ATTRIBUTE => 'ref_new_tag'}])
|
471
476
|
remove([{ '_type' => 'Tag', 'id' => tag2.id }])
|
472
|
-
update([{
|
477
|
+
update([{ViewModel::REFERENCE_ATTRIBUTE => 'ref_tag1'}])
|
473
478
|
end
|
474
479
|
|
475
|
-
|
476
|
-
|
480
|
+
references = {
|
481
|
+
'ref_new_tag' => { '_type' => 'Tag', 'name' => 'new_tag' },
|
482
|
+
'ref_tag1' => { '_type' => 'Tag', 'id' => tag1.id, 'name' => 'renamed tag1' },
|
483
|
+
}
|
484
|
+
|
485
|
+
updated = pv.replace_associated(:tags, update, references: references, deserialize_context: context)
|
486
|
+
post_children = ParentView.new(Parent.find(@parent1.id))._read_association(:tags)
|
487
|
+
new_tag = post_children.detect { |t| t.name == 'new_tag' }
|
477
488
|
|
478
489
|
expected_edit_checks = [ViewModel::Reference.new(ParentView, @parent1.id),
|
479
490
|
ViewModel::Reference.new(TagView, @tag1.id),
|
@@ -482,7 +493,12 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
|
|
482
493
|
assert_contains_exactly(expected_edit_checks,
|
483
494
|
context.valid_edit_refs)
|
484
495
|
|
485
|
-
assert_equal(2,
|
496
|
+
assert_equal(2, post_children.size)
|
497
|
+
assert_equal(2, update.size)
|
498
|
+
assert_contains_exactly(
|
499
|
+
['new_tag', 'renamed tag1'],
|
500
|
+
updated.map(&:name),
|
501
|
+
)
|
486
502
|
|
487
503
|
@parent1.reload
|
488
504
|
assert_equal(['renamed tag1', 'new_tag'], tags(@parent1).map(&:name))
|
@@ -178,6 +178,35 @@ class ViewModel::ActiveRecord::Migration < ActiveSupport::TestCase
|
|
178
178
|
end
|
179
179
|
end
|
180
180
|
end
|
181
|
+
|
182
|
+
describe 'with a functional update' do
|
183
|
+
# note that this wouldn't actually be deserializable as child is not a collection
|
184
|
+
def subject_data
|
185
|
+
data = super()
|
186
|
+
data['child'] = wrap_with_fupdate(data['child'])
|
187
|
+
data
|
188
|
+
end
|
189
|
+
|
190
|
+
def expected_result
|
191
|
+
data = super()
|
192
|
+
data['data']['child'] = wrap_with_fupdate(data['data']['child'])
|
193
|
+
data
|
194
|
+
end
|
195
|
+
|
196
|
+
def wrap_with_fupdate(child)
|
197
|
+
# The 'after' and remove shouldn't get changed in migration, even though it has _type: Child
|
198
|
+
build_fupdate do
|
199
|
+
append([child], after: { '_type' => 'Child', 'id' => 9999 })
|
200
|
+
update([child.deep_merge('id' => 8888)])
|
201
|
+
remove([{ '_type' => 'Child', 'id' => 7777 }])
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'migrates' do
|
206
|
+
migrate!
|
207
|
+
assert_equal(expected_result, subject)
|
208
|
+
end
|
209
|
+
end
|
181
210
|
end
|
182
211
|
end
|
183
212
|
|
@@ -187,7 +187,21 @@ class ViewModel::TraversalContextTest < ActiveSupport::TestCase
|
|
187
187
|
it 'traverses as expected in replace_associated' do
|
188
188
|
ctx = vm_deserialize_context(viewmodel_class)
|
189
189
|
replacement = subject_association.collection? ? [new_child_hash] : new_child_hash
|
190
|
-
|
190
|
+
references = {}
|
191
|
+
|
192
|
+
# Referenced collections need to be presented as is, _except_ for when they're owned, as
|
193
|
+
# this is special cased in NestedControllerBase#write_association (+_bulk) for
|
194
|
+
# convenience.
|
195
|
+
if subject_association.referenced? && !subject_association.owned?
|
196
|
+
replacement = ViewModel::Utils.wrap_one_or_many(replacement) do |replacements|
|
197
|
+
replacements.map!.with_index do |update, index|
|
198
|
+
references["ref#{index}"] = update
|
199
|
+
{ ViewModel::REFERENCE_ATTRIBUTE => "ref#{index}" }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
vm.replace_associated(subject_association_name, replacement, references: references, deserialize_context: ctx)
|
191
205
|
|
192
206
|
expected = expected_parent_details
|
193
207
|
expected = expected.merge(expected_children_details) unless subject_association.referenced?
|
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.
|
4
|
+
version: 3.5.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: 2021-
|
11
|
+
date: 2021-10-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.0.
|
61
|
+
version: 1.0.2
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1.0.
|
68
|
+
version: 1.0.2
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: iknow_cache
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -362,7 +362,7 @@ dependencies:
|
|
362
362
|
version: '0'
|
363
363
|
description: ''
|
364
364
|
email:
|
365
|
-
-
|
365
|
+
- systems@iknow.jp
|
366
366
|
executables: []
|
367
367
|
extensions: []
|
368
368
|
extra_rdoc_files: []
|
@@ -453,6 +453,7 @@ files:
|
|
453
453
|
- test/unit/view_model/active_record/belongs_to_test.rb
|
454
454
|
- test/unit/view_model/active_record/cache_test.rb
|
455
455
|
- test/unit/view_model/active_record/cloner_test.rb
|
456
|
+
- test/unit/view_model/active_record/controller_nested_test.rb
|
456
457
|
- test/unit/view_model/active_record/controller_test.rb
|
457
458
|
- test/unit/view_model/active_record/counter_test.rb
|
458
459
|
- test/unit/view_model/active_record/customization_test.rb
|
@@ -514,6 +515,7 @@ test_files:
|
|
514
515
|
- test/unit/view_model/active_record/belongs_to_test.rb
|
515
516
|
- test/unit/view_model/active_record/cache_test.rb
|
516
517
|
- test/unit/view_model/active_record/cloner_test.rb
|
518
|
+
- test/unit/view_model/active_record/controller_nested_test.rb
|
517
519
|
- test/unit/view_model/active_record/controller_test.rb
|
518
520
|
- test/unit/view_model/active_record/counter_test.rb
|
519
521
|
- test/unit/view_model/active_record/customization_test.rb
|