iknow_view_models 3.4.4 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -15,83 +15,19 @@ class ViewModel::ActiveRecord::ControllerTest < ActiveSupport::TestCase
15
15
  include ControllerTestModels
16
16
  include ControllerTestControllers
17
17
 
18
- def visit(hook, view)
19
- CallbackTracer::Visit.new(hook, view)
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
- def enclosing_hooks(spans, inner_range)
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 assert_all_hooks_nested_inside_parent_hook(trace)
68
- spans = each_hook_span(trace).to_a
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 = Parent.create(name: 'p',
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
- nc = pv.replace_associated(:children, update, deserialize_context: context)
199
- new_child = nc.detect { |c| c.name == '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(3, nc.size)
210
- assert_equal('renamed p1c1', nc[0].name)
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))
@@ -1297,7 +1300,9 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase
1297
1300
  it 'replaces children' do
1298
1301
  view = create_viewmodel!
1299
1302
  view.replace_associated(:children,
1300
- [{ '_type' => 'Child', 'name' => 'newchildname' }])
1303
+ [{ '_ref' => 'the_child' }],
1304
+ references: { 'the_child' => { '_type' => 'Child', 'name' => 'newchildname' } }
1305
+ )
1301
1306
 
1302
1307
  view.model.reload
1303
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
- nc = pv.replace_associated(:tags,
442
- [{ '_type' => 'Tag', 'name' => 'new_tag' }],
443
- deserialize_context: context)
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
- *nc.map(&:to_reference),]
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, nc.size)
452
- assert(nc[0].is_a?(TagView))
453
- assert_equal('new_tag', nc[0].name)
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([{ '_type' => 'Tag', 'name' => 'new_tag' }])
475
+ append([{ViewModel::REFERENCE_ATTRIBUTE => 'ref_new_tag'}])
471
476
  remove([{ '_type' => 'Tag', 'id' => tag2.id }])
472
- update([{ '_type' => 'Tag', 'id' => tag1.id, 'name' => 'renamed tag1' }])
477
+ update([{ViewModel::REFERENCE_ATTRIBUTE => 'ref_tag1'}])
473
478
  end
474
479
 
475
- nc = pv.replace_associated(:tags, update, deserialize_context: context)
476
- new_tag = nc.detect { |t| t.name == 'new_tag' }
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, nc.size)
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))