iknow_view_models 2.9.0 → 3.0.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/iknow_view_models.gemspec +1 -1
  3. data/lib/iknow_view_models/version.rb +1 -1
  4. data/lib/view_model/active_record/association_data.rb +206 -92
  5. data/lib/view_model/active_record/association_manipulation.rb +22 -12
  6. data/lib/view_model/active_record/cache/cacheable_view.rb +3 -13
  7. data/lib/view_model/active_record/cache.rb +2 -2
  8. data/lib/view_model/active_record/cloner.rb +11 -11
  9. data/lib/view_model/active_record/controller.rb +0 -2
  10. data/lib/view_model/active_record/update_context.rb +21 -3
  11. data/lib/view_model/active_record/update_data.rb +43 -45
  12. data/lib/view_model/active_record/update_operation.rb +265 -153
  13. data/lib/view_model/active_record/visitor.rb +9 -6
  14. data/lib/view_model/active_record.rb +94 -74
  15. data/lib/view_model/after_transaction_runner.rb +3 -18
  16. data/lib/view_model/changes.rb +24 -16
  17. data/lib/view_model/config.rb +6 -2
  18. data/lib/view_model/deserialization_error.rb +31 -0
  19. data/lib/view_model/deserialize_context.rb +2 -6
  20. data/lib/view_model/error_view.rb +6 -5
  21. data/lib/view_model/record/attribute_data.rb +11 -6
  22. data/lib/view_model/record.rb +44 -24
  23. data/lib/view_model/serialize_context.rb +2 -63
  24. data/lib/view_model.rb +17 -8
  25. data/shell.nix +1 -1
  26. data/test/helpers/arvm_test_utilities.rb +6 -0
  27. data/test/helpers/controller_test_helpers.rb +5 -3
  28. data/test/helpers/viewmodel_spec_helpers.rb +63 -52
  29. data/test/unit/view_model/access_control_test.rb +88 -37
  30. data/test/unit/view_model/active_record/belongs_to_test.rb +110 -178
  31. data/test/unit/view_model/active_record/cache_test.rb +3 -2
  32. data/test/unit/view_model/active_record/cloner_test.rb +1 -1
  33. data/test/unit/view_model/active_record/controller_test.rb +12 -20
  34. data/test/unit/view_model/active_record/has_many_test.rb +540 -316
  35. data/test/unit/view_model/active_record/has_many_through_poly_test.rb +12 -15
  36. data/test/unit/view_model/active_record/has_many_through_test.rb +15 -58
  37. data/test/unit/view_model/active_record/has_one_test.rb +288 -135
  38. data/test/unit/view_model/active_record/poly_test.rb +0 -1
  39. data/test/unit/view_model/active_record/shared_test.rb +21 -39
  40. data/test/unit/view_model/active_record/version_test.rb +3 -2
  41. data/test/unit/view_model/active_record_test.rb +5 -63
  42. data/test/unit/view_model/callbacks_test.rb +1 -0
  43. data/test/unit/view_model/record_test.rb +0 -32
  44. data/test/unit/view_model/traversal_context_test.rb +13 -12
  45. metadata +5 -8
  46. data/test/unit/view_model/active_record/optional_attribute_view_test.rb +0 -58
@@ -20,6 +20,7 @@ class ViewModel::ActiveRecord::HasManyThroughPolyTest < ActiveSupport::TestCase
20
20
  end
21
21
 
22
22
  define_viewmodel do
23
+ root!
23
24
  attributes :name
24
25
  end
25
26
  end
@@ -37,6 +38,7 @@ class ViewModel::ActiveRecord::HasManyThroughPolyTest < ActiveSupport::TestCase
37
38
  end
38
39
 
39
40
  define_viewmodel do
41
+ root!
40
42
  attributes :name
41
43
  end
42
44
  end
@@ -53,8 +55,9 @@ class ViewModel::ActiveRecord::HasManyThroughPolyTest < ActiveSupport::TestCase
53
55
  end
54
56
 
55
57
  define_viewmodel do
58
+ root!
56
59
  attributes :name
57
- association :tags, shared: true, through: :parents_tags, through_order_attr: :position, viewmodels: [TagAView, TagBView]
60
+ association :tags, through: :parents_tags, through_order_attr: :position, viewmodels: [TagAView, TagBView]
58
61
  end
59
62
  end
60
63
  end
@@ -86,10 +89,6 @@ class ViewModel::ActiveRecord::HasManyThroughPolyTest < ActiveSupport::TestCase
86
89
  self.class.build_parent_tag_join_model(self)
87
90
  end
88
91
 
89
- private def context_with(*args)
90
- ParentView.new_serialize_context(include: args)
91
- end
92
-
93
92
  def setup
94
93
  super
95
94
 
@@ -108,14 +107,14 @@ class ViewModel::ActiveRecord::HasManyThroughPolyTest < ActiveSupport::TestCase
108
107
  def test_roundtrip
109
108
  # Objects are serialized to a view and deserialized, and should not be different when complete.
110
109
 
111
- alter_by_view!(ParentView, @parent1, serialize_context: context_with(:tags)) {}
110
+ alter_by_view!(ParentView, @parent1) {}
112
111
  assert_equal('p1', @parent1.name)
113
112
  assert_equal([@tag_a1, @tag_a2, @tag_b1, @tag_b2],
114
113
  @parent1.parents_tags.order(:position).map(&:tag))
115
114
  end
116
115
 
117
116
  def test_loading_batching
118
- context = context_with(:tags)
117
+ context = ParentView.new_serialize_context
119
118
  log_queries do
120
119
  parent_views = ParentView.load(serialize_context: context)
121
120
  serialize(parent_views, serialize_context: context)
@@ -126,7 +125,7 @@ class ViewModel::ActiveRecord::HasManyThroughPolyTest < ActiveSupport::TestCase
126
125
  end
127
126
 
128
127
  def test_eager_includes
129
- includes = ParentView.eager_includes(serialize_context: context_with(:tags))
128
+ includes = ParentView.eager_includes
130
129
  assert_equal(DeepPreloader::Spec.new(
131
130
  'parents_tags' => DeepPreloader::Spec.new(
132
131
  'tag' => DeepPreloader::PolymorphicSpec.new(
@@ -157,8 +156,7 @@ class ViewModel::ActiveRecord::HasManyThroughPolyTest < ActiveSupport::TestCase
157
156
 
158
157
 
159
158
  def test_serialize
160
- view, refs = serialize_with_references(ParentView.new(@parent1),
161
- serialize_context: context_with(:tags))
159
+ view, refs = serialize_with_references(ParentView.new(@parent1))
162
160
 
163
161
  tag_data = view['tags'].map { |hash| refs[hash['_ref']] }
164
162
  assert_equal([{ 'id' => @tag_a1.id, '_type' => 'TagA', '_version' => 1, 'name' => 'tag A1' },
@@ -188,7 +186,7 @@ class ViewModel::ActiveRecord::HasManyThroughPolyTest < ActiveSupport::TestCase
188
186
  end
189
187
 
190
188
  def test_reordering_swap_type
191
- alter_by_view!(ParentView, @parent1, serialize_context: context_with(:tags)) do |view, refs|
189
+ alter_by_view!(ParentView, @parent1) do |view, refs|
192
190
  t1, t2, t3, t4 = view['tags']
193
191
  view['tags'] = [t3, t2, t1, t4]
194
192
  end
@@ -223,8 +221,9 @@ class ViewModel::ActiveRecord::HasManyThroughPolyTest < ActiveSupport::TestCase
223
221
  end
224
222
 
225
223
  define_viewmodel do
224
+ root!
226
225
  attributes :name
227
- association :tags, shared: true, through: :parents_tags, through_order_attr: :position, viewmodels: [TagAView, TagBView], as: :something_else
226
+ association :tags, through: :parents_tags, through_order_attr: :position, viewmodels: [TagAView, TagBView], as: :something_else
228
227
  end
229
228
  end
230
229
 
@@ -244,12 +243,10 @@ class ViewModel::ActiveRecord::HasManyThroughPolyTest < ActiveSupport::TestCase
244
243
  # Compare to non-polymorphic, which will also load the tags
245
244
  deps = root_updates.first.preload_dependencies
246
245
  assert_equal(DeepPreloader::Spec.new('parents_tags' => DeepPreloader::Spec.new('tag' => DeepPreloader::PolymorphicSpec.new)), deps)
247
- assert_equal({ 'something_else' => {} }, root_updates.first.updated_associations)
248
246
  end
249
247
 
250
-
251
248
  def test_renamed_roundtrip
252
- context = ParentView.new_serialize_context(include: :something_else)
249
+ context = ParentView.new_serialize_context
253
250
  alter_by_view!(ParentView, @parent, serialize_context: context) do |view, refs|
254
251
  assert_equal({refs.keys.first => { 'id' => @parent.parents_tags.first.tag.id,
255
252
  '_type' => 'TagA',
@@ -19,8 +19,9 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
19
19
  end
20
20
 
21
21
  define_viewmodel do
22
+ root!
22
23
  attributes :name
23
- association :tags, shared: true, through: :parents_tags, through_order_attr: :position
24
+ association :tags, through: :parents_tags, through_order_attr: :position
24
25
  end
25
26
  end
26
27
  end
@@ -40,6 +41,7 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
40
41
  end
41
42
 
42
43
  define_viewmodel do
44
+ root!
43
45
  attributes :name
44
46
  if use_childtag
45
47
  associations :child_tags
@@ -91,10 +93,6 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
91
93
  self.class.build_join_table_model(self)
92
94
  end
93
95
 
94
- private def context_with(*args)
95
- ParentView.new_serialize_context(include: args)
96
- end
97
-
98
96
  def setup
99
97
  super
100
98
 
@@ -108,7 +106,7 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
108
106
  end
109
107
 
110
108
  def test_loading_batching
111
- context = context_with(:tags)
109
+ context = ParentView.new_serialize_context
112
110
  log_queries do
113
111
  parent_views = ParentView.load(serialize_context: context)
114
112
  serialize(parent_views, serialize_context: context)
@@ -121,13 +119,13 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
121
119
  def test_roundtrip
122
120
  # Objects are serialized to a view and deserialized, and should not be different when complete.
123
121
 
124
- alter_by_view!(ParentView, @parent1, serialize_context: context_with(:tags)) {}
122
+ alter_by_view!(ParentView, @parent1) {}
125
123
  assert_equal('p1', @parent1.name)
126
124
  assert_equal([@tag1, @tag2], @parent1.parents_tags.order(:position).map(&:tag))
127
125
  end
128
126
 
129
127
  def test_eager_includes
130
- includes = ParentView.eager_includes(serialize_context: context_with(:tags))
128
+ includes = ParentView.eager_includes
131
129
  assert_equal(DeepPreloader::Spec.new('parents_tags' => DeepPreloader::Spec.new('tag' => DeepPreloader::Spec.new)), includes)
132
130
  end
133
131
 
@@ -150,21 +148,8 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
150
148
  'mentioning tags and child_tags causes through association loading')
151
149
  end
152
150
 
153
- def test_updated_associations
154
- root_updates, _ref_updates = ViewModel::ActiveRecord::UpdateData.parse_hashes(
155
- [{ '_type' => 'Parent',
156
- 'tags' => [{ '_ref' => 'r1' }] }],
157
- { 'r1' => { '_type' => 'Tag', } })
158
-
159
- assert_equal({ 'tags' => {} },
160
- root_updates.first.updated_associations,
161
- 'mentioning tags causes through association loading')
162
-
163
- end
164
-
165
151
  def test_serialize
166
- view, refs = serialize_with_references(ParentView.new(@parent1),
167
- serialize_context: context_with(:tags))
152
+ view, refs = serialize_with_references(ParentView.new(@parent1))
168
153
 
169
154
  tag_data = view['tags'].map { |hash| refs[hash['_ref']] }
170
155
  assert_equal([{ 'id' => @tag1.id, '_type' => 'Tag', '_version' => 1, 'name' => 'tag1' },
@@ -198,7 +183,7 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
198
183
  end
199
184
 
200
185
  def test_reordering
201
- pv, ctx = alter_by_view!(ParentView, @parent1, serialize_context: context_with(:tags)) do |view, _refs|
186
+ pv, ctx = alter_by_view!(ParentView, @parent1) do |view, _refs|
202
187
  view['tags'].reverse!
203
188
  end
204
189
 
@@ -211,7 +196,7 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
211
196
 
212
197
  def test_child_edit_doesnt_editcheck_parent
213
198
  # editing child doesn't edit check parent
214
- pv, d_context = alter_by_view!(ParentView, @parent1, serialize_context: context_with(:tags)) do |view, refs|
199
+ pv, d_context = alter_by_view!(ParentView, @parent1) do |view, refs|
215
200
  refs[view['tags'][0]["_ref"]]["name"] = "changed"
216
201
  end
217
202
 
@@ -223,7 +208,7 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
223
208
  end
224
209
 
225
210
  def test_child_reordering_editchecks_parent
226
- pv, d_context = alter_by_view!(ParentView, @parent1, serialize_context: context_with(:tags)) do |view, _refs|
211
+ pv, d_context = alter_by_view!(ParentView, @parent1) do |view, _refs|
227
212
  view['tags'].reverse!
228
213
  end
229
214
 
@@ -232,7 +217,7 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
232
217
  end
233
218
 
234
219
  def test_child_deletion_editchecks_parent
235
- pv, d_context = alter_by_view!(ParentView, @parent1, serialize_context: context_with(:tags)) do |view, refs|
220
+ pv, d_context = alter_by_view!(ParentView, @parent1) do |view, refs|
236
221
  removed = view['tags'].pop['_ref']
237
222
  refs.delete(removed)
238
223
  end
@@ -242,7 +227,7 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
242
227
  end
243
228
 
244
229
  def test_child_addition_editchecks_parent
245
- pv, d_context = alter_by_view!(ParentView, @parent1, serialize_context: context_with(:tags)) do |view, refs|
230
+ pv, d_context = alter_by_view!(ParentView, @parent1) do |view, refs|
246
231
  view['tags'] << { '_ref' => 't_new' }
247
232
  refs['t_new'] = { '_type' => 'Tag', 'name' => 'newest tag' }
248
233
  end
@@ -617,8 +602,9 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
617
602
  end
618
603
 
619
604
  define_viewmodel do
605
+ root!
620
606
  attributes :name
621
- association :tags, shared: true, through: :parents_tags, through_order_attr: :position, as: :something_else
607
+ association :tags, through: :parents_tags, through_order_attr: :position, as: :something_else
622
608
  end
623
609
  end
624
610
 
@@ -638,11 +624,10 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
638
624
  root_updates, _ref_updates = ViewModel::ActiveRecord::UpdateData.parse_hashes([{ '_type' => 'Parent', 'something_else' => [] }])
639
625
  assert_equal(DeepPreloader::Spec.new('parents_tags' => DeepPreloader::Spec.new('tag' => DeepPreloader::Spec.new)),
640
626
  root_updates.first.preload_dependencies)
641
- assert_equal({ 'something_else' => {} }, root_updates.first.updated_associations)
642
627
  end
643
628
 
644
629
  def test_renamed_roundtrip
645
- context = ParentView.new_serialize_context(include: :something_else)
630
+ context = ParentView.new_serialize_context
646
631
  alter_by_view!(ParentView, @parent, serialize_context: context) do |view, refs|
647
632
  assert_equal({refs.keys.first => { 'id' => @parent.parents_tags.first.tag.id,
648
633
  '_type' => 'Tag',
@@ -703,34 +688,6 @@ class ViewModel::ActiveRecord::HasManyThroughTest < ActiveSupport::TestCase
703
688
  root_updates.first.preload_dependencies,
704
689
  'mentioning tags and child_tags in functional update value causes through association loading, ' \
705
690
  'excluding shared')
706
-
707
- end
708
-
709
- def test_updated_associations
710
- root_updates, _ref_updates = ViewModel::ActiveRecord::UpdateData.parse_hashes(
711
- [{ '_type' => 'Parent',
712
- 'tags' => [{ '_ref' => 'r1' }] }],
713
- { 'r1' => { '_type' => 'Tag', 'child_tags' => [] } })
714
-
715
- assert_equal({ 'tags' => { } },
716
- root_updates.first.updated_associations,
717
- 'mentioning tags and child_tags causes through association loading, excluding shared')
718
- end
719
-
720
- def test_updated_associations_functional
721
- fupdate = build_fupdate do
722
- append([{ '_ref' => 'r1' }])
723
- end
724
-
725
- root_updates, _ref_updates = ViewModel::ActiveRecord::UpdateData.parse_hashes(
726
- [{ '_type' => 'Parent',
727
- 'tags' => fupdate }],
728
- { 'r1' => { '_type' => 'Tag', 'child_tags' => [] } })
729
-
730
- assert_equal({ 'tags' => { } },
731
- root_updates.first.updated_associations,
732
- 'mentioning tags and child_tags in functional_update causes through association loading, ' \
733
- 'excluding shared')
734
691
  end
735
692
  end
736
693
  end