iknow_view_models 2.10.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +119 -0
  3. data/.travis.yml +31 -0
  4. data/Appraisals +6 -16
  5. data/gemfiles/{rails_7_0.gemfile → rails_6_0_beta.gemfile} +2 -2
  6. data/iknow_view_models.gemspec +3 -5
  7. data/lib/iknow_view_models/version.rb +1 -1
  8. data/lib/view_model/active_record/association_data.rb +206 -92
  9. data/lib/view_model/active_record/association_manipulation.rb +22 -12
  10. data/lib/view_model/active_record/cache/cacheable_view.rb +3 -13
  11. data/lib/view_model/active_record/cache.rb +2 -2
  12. data/lib/view_model/active_record/cloner.rb +11 -11
  13. data/lib/view_model/active_record/controller.rb +0 -2
  14. data/lib/view_model/active_record/update_context.rb +21 -3
  15. data/lib/view_model/active_record/update_data.rb +43 -45
  16. data/lib/view_model/active_record/update_operation.rb +265 -153
  17. data/lib/view_model/active_record/visitor.rb +9 -6
  18. data/lib/view_model/active_record.rb +94 -74
  19. data/lib/view_model/after_transaction_runner.rb +3 -18
  20. data/lib/view_model/callbacks.rb +2 -2
  21. data/lib/view_model/changes.rb +24 -16
  22. data/lib/view_model/config.rb +6 -2
  23. data/lib/view_model/deserialization_error.rb +31 -0
  24. data/lib/view_model/deserialize_context.rb +2 -6
  25. data/lib/view_model/error_view.rb +6 -5
  26. data/lib/view_model/record/attribute_data.rb +11 -6
  27. data/lib/view_model/record.rb +44 -24
  28. data/lib/view_model/serialize_context.rb +2 -63
  29. data/lib/view_model/test_helpers/arvm_builder.rb +2 -4
  30. data/lib/view_model/traversal_context.rb +2 -2
  31. data/lib/view_model.rb +21 -13
  32. data/shell.nix +1 -1
  33. data/test/helpers/arvm_test_models.rb +4 -12
  34. data/test/helpers/arvm_test_utilities.rb +6 -0
  35. data/test/helpers/controller_test_helpers.rb +6 -6
  36. data/test/helpers/viewmodel_spec_helpers.rb +63 -52
  37. data/test/unit/view_model/access_control_test.rb +88 -37
  38. data/test/unit/view_model/active_record/belongs_to_test.rb +110 -178
  39. data/test/unit/view_model/active_record/cache_test.rb +11 -5
  40. data/test/unit/view_model/active_record/cloner_test.rb +1 -1
  41. data/test/unit/view_model/active_record/controller_test.rb +12 -20
  42. data/test/unit/view_model/active_record/has_many_test.rb +540 -316
  43. data/test/unit/view_model/active_record/has_many_through_poly_test.rb +12 -15
  44. data/test/unit/view_model/active_record/has_many_through_test.rb +15 -58
  45. data/test/unit/view_model/active_record/has_one_test.rb +288 -135
  46. data/test/unit/view_model/active_record/poly_test.rb +0 -1
  47. data/test/unit/view_model/active_record/shared_test.rb +21 -39
  48. data/test/unit/view_model/active_record/version_test.rb +3 -2
  49. data/test/unit/view_model/active_record_test.rb +5 -63
  50. data/test/unit/view_model/callbacks_test.rb +1 -0
  51. data/test/unit/view_model/record_test.rb +0 -32
  52. data/test/unit/view_model/traversal_context_test.rb +13 -12
  53. metadata +15 -25
  54. data/.github/workflows/gem-push.yml +0 -31
  55. data/.github/workflows/test.yml +0 -65
  56. data/gemfiles/rails_6_0.gemfile +0 -9
  57. data/gemfiles/rails_6_1.gemfile +0 -9
  58. 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