iknow_view_models 3.4.1 → 3.5.0
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.rb +12 -0
- data/lib/view_model/active_record.rb +46 -5
- 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 +126 -14
- data/lib/view_model/active_record/singular_nested_controller.rb +5 -2
- data/lib/view_model/callbacks.rb +1 -1
- data/lib/view_model/controller.rb +24 -2
- data/lib/view_model/record.rb +1 -1
- data/lib/view_model/schemas.rb +44 -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/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
|
|