iknow_view_models 2.8.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +115 -0
  3. data/.gitignore +36 -0
  4. data/.travis.yml +31 -0
  5. data/Appraisals +9 -0
  6. data/Gemfile +19 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +19 -0
  9. data/Rakefile +21 -0
  10. data/appveyor.yml +22 -0
  11. data/gemfiles/rails_5_2.gemfile +15 -0
  12. data/gemfiles/rails_6_0_beta.gemfile +15 -0
  13. data/iknow_view_models.gemspec +49 -0
  14. data/lib/iknow_view_models.rb +12 -0
  15. data/lib/iknow_view_models/railtie.rb +8 -0
  16. data/lib/iknow_view_models/version.rb +5 -0
  17. data/lib/view_model.rb +333 -0
  18. data/lib/view_model/access_control.rb +154 -0
  19. data/lib/view_model/access_control/composed.rb +216 -0
  20. data/lib/view_model/access_control/open.rb +13 -0
  21. data/lib/view_model/access_control/read_only.rb +13 -0
  22. data/lib/view_model/access_control/tree.rb +264 -0
  23. data/lib/view_model/access_control_error.rb +10 -0
  24. data/lib/view_model/active_record.rb +383 -0
  25. data/lib/view_model/active_record/association_data.rb +178 -0
  26. data/lib/view_model/active_record/association_manipulation.rb +389 -0
  27. data/lib/view_model/active_record/cache.rb +265 -0
  28. data/lib/view_model/active_record/cache/cacheable_view.rb +51 -0
  29. data/lib/view_model/active_record/cloner.rb +113 -0
  30. data/lib/view_model/active_record/collection_nested_controller.rb +100 -0
  31. data/lib/view_model/active_record/controller.rb +77 -0
  32. data/lib/view_model/active_record/controller_base.rb +185 -0
  33. data/lib/view_model/active_record/nested_controller_base.rb +93 -0
  34. data/lib/view_model/active_record/singular_nested_controller.rb +34 -0
  35. data/lib/view_model/active_record/update_context.rb +252 -0
  36. data/lib/view_model/active_record/update_data.rb +749 -0
  37. data/lib/view_model/active_record/update_operation.rb +810 -0
  38. data/lib/view_model/active_record/visitor.rb +77 -0
  39. data/lib/view_model/after_transaction_runner.rb +29 -0
  40. data/lib/view_model/callbacks.rb +219 -0
  41. data/lib/view_model/changes.rb +62 -0
  42. data/lib/view_model/config.rb +29 -0
  43. data/lib/view_model/controller.rb +142 -0
  44. data/lib/view_model/deserialization_error.rb +437 -0
  45. data/lib/view_model/deserialize_context.rb +16 -0
  46. data/lib/view_model/error.rb +191 -0
  47. data/lib/view_model/error_view.rb +35 -0
  48. data/lib/view_model/record.rb +367 -0
  49. data/lib/view_model/record/attribute_data.rb +48 -0
  50. data/lib/view_model/reference.rb +31 -0
  51. data/lib/view_model/references.rb +48 -0
  52. data/lib/view_model/registry.rb +73 -0
  53. data/lib/view_model/schemas.rb +45 -0
  54. data/lib/view_model/serialization_error.rb +10 -0
  55. data/lib/view_model/serialize_context.rb +118 -0
  56. data/lib/view_model/test_helpers.rb +103 -0
  57. data/lib/view_model/test_helpers/arvm_builder.rb +111 -0
  58. data/lib/view_model/traversal_context.rb +126 -0
  59. data/lib/view_model/utils.rb +24 -0
  60. data/lib/view_model/utils/collections.rb +49 -0
  61. data/test/helpers/arvm_test_models.rb +59 -0
  62. data/test/helpers/arvm_test_utilities.rb +187 -0
  63. data/test/helpers/callback_tracer.rb +27 -0
  64. data/test/helpers/controller_test_helpers.rb +270 -0
  65. data/test/helpers/match_enumerator.rb +58 -0
  66. data/test/helpers/query_logging.rb +71 -0
  67. data/test/helpers/test_access_control.rb +56 -0
  68. data/test/helpers/viewmodel_spec_helpers.rb +326 -0
  69. data/test/unit/view_model/access_control_test.rb +769 -0
  70. data/test/unit/view_model/active_record/alias_test.rb +35 -0
  71. data/test/unit/view_model/active_record/belongs_to_test.rb +376 -0
  72. data/test/unit/view_model/active_record/cache_test.rb +351 -0
  73. data/test/unit/view_model/active_record/cloner_test.rb +313 -0
  74. data/test/unit/view_model/active_record/controller_test.rb +561 -0
  75. data/test/unit/view_model/active_record/counter_test.rb +80 -0
  76. data/test/unit/view_model/active_record/customization_test.rb +388 -0
  77. data/test/unit/view_model/active_record/has_many_test.rb +957 -0
  78. data/test/unit/view_model/active_record/has_many_through_poly_test.rb +269 -0
  79. data/test/unit/view_model/active_record/has_many_through_test.rb +736 -0
  80. data/test/unit/view_model/active_record/has_one_test.rb +334 -0
  81. data/test/unit/view_model/active_record/namespacing_test.rb +75 -0
  82. data/test/unit/view_model/active_record/optional_attribute_view_test.rb +58 -0
  83. data/test/unit/view_model/active_record/poly_test.rb +320 -0
  84. data/test/unit/view_model/active_record/shared_test.rb +285 -0
  85. data/test/unit/view_model/active_record/version_test.rb +121 -0
  86. data/test/unit/view_model/active_record_test.rb +542 -0
  87. data/test/unit/view_model/callbacks_test.rb +582 -0
  88. data/test/unit/view_model/deserialization_error/unique_violation_test.rb +73 -0
  89. data/test/unit/view_model/record_test.rb +524 -0
  90. data/test/unit/view_model/traversal_context_test.rb +371 -0
  91. data/test/unit/view_model_test.rb +62 -0
  92. metadata +490 -0
@@ -0,0 +1,320 @@
1
+ require_relative "../../../helpers/arvm_test_utilities.rb"
2
+ require_relative "../../../helpers/arvm_test_models.rb"
3
+
4
+ require "minitest/autorun"
5
+
6
+ require "view_model/active_record"
7
+
8
+ module ViewModel::ActiveRecord::PolyTest
9
+ ## Polymorphic pointer to parent in child (child may belong to different type parents)
10
+ class PolyParentPointerTest < ActiveSupport::TestCase
11
+ include ARVMTestUtilities
12
+ def before_all
13
+ super
14
+ build_viewmodel(:Child) do
15
+ define_schema do |t|
16
+ t.string :text
17
+ t.string :poly_type
18
+ t.integer :poly_id
19
+ end
20
+ define_model do
21
+ belongs_to :poly, polymorphic: true
22
+ end
23
+ define_viewmodel do
24
+ attributes :text
25
+ end
26
+ end
27
+
28
+ build_viewmodel(:PolyParentOne) do
29
+ define_schema do |t|
30
+ t.string :text
31
+ end
32
+ define_model do
33
+ has_one :grandparent, inverse_of: :poly_parent_one
34
+ has_one :child, as: :poly, dependent: :destroy, inverse_of: :poly
35
+ end
36
+ define_viewmodel do
37
+ attributes :text
38
+ association :child
39
+ end
40
+ end
41
+
42
+ build_viewmodel(:PolyParentTwo) do
43
+ define_schema do |t|
44
+ t.integer :num
45
+ end
46
+ define_model do
47
+ has_one :grandparent, inverse_of: :poly_parent_two
48
+ has_many :children, as: :poly, dependent: :destroy, inverse_of: :poly
49
+ end
50
+ define_viewmodel do
51
+ attributes :num
52
+ association :children
53
+ end
54
+ end
55
+
56
+ build_viewmodel(:Grandparent) do
57
+ define_schema do |t|
58
+ t.integer :poly_parent_one_id
59
+ t.integer :poly_parent_two_id
60
+ end
61
+ define_model do
62
+ belongs_to :poly_parent_one, dependent: :destroy, inverse_of: :grandparent
63
+ belongs_to :poly_parent_two, dependent: :destroy, inverse_of: :grandparent
64
+ end
65
+ define_viewmodel do
66
+ associations :poly_parent_one, :poly_parent_two
67
+ end
68
+ end
69
+ end
70
+
71
+ def setup
72
+ super
73
+ @parent1 = PolyParentOne.create(text: "p1", child: Child.new(text: "c1"))
74
+ @parent2 = PolyParentTwo.create(num: 2, children: [Child.new(text: "c2"), Child.new(text: "c3")])
75
+ @grandparent = Grandparent.create(poly_parent_one: @parent1, poly_parent_two: @parent2)
76
+ enable_logging!
77
+ end
78
+
79
+ def test_create_has_one_from_view
80
+ p1_view = {
81
+ "_type" => "PolyParentOne",
82
+ "text" => "p",
83
+ "child" => { "_type" => "Child", "text" => "c" }
84
+ }
85
+ p1v = PolyParentOneView.deserialize_from_view(p1_view)
86
+ p1 = p1v.model
87
+
88
+ assert(p1.present?)
89
+ assert(p1.child.present?)
90
+ assert_equal(p1, p1.child.poly)
91
+ end
92
+
93
+ def test_create_has_many_from_view
94
+ p2_view = {
95
+ "_type" => "PolyParentTwo",
96
+ "num" => "2",
97
+ "children" => [{ "_type" => "Child", "text" => "c1" }, { "_type" => "Child", "text" => "c2" }]
98
+ }
99
+ p2v = PolyParentTwoView.deserialize_from_view(p2_view)
100
+ p2 = p2v.model
101
+
102
+ assert(p2.present?)
103
+ assert(p2.children.count == 2)
104
+ p2.children.each do |c|
105
+ assert_equal(p2, c.poly)
106
+ end
107
+ end
108
+
109
+ def test_move
110
+ # test that I can move a child from one type to another and the parent pointer/type is correctly updated.
111
+ alter_by_view!(GrandparentView, @grandparent) do |view, refs|
112
+ c1 = view["poly_parent_one"]["child"]
113
+ c2 = view["poly_parent_two"]["children"].pop
114
+ view["poly_parent_one"]["child"] = c2
115
+ view["poly_parent_two"]["children"].push(c1)
116
+ end
117
+ @grandparent.reload
118
+ assert_equal("c3", @grandparent.poly_parent_one.child.text)
119
+ assert_equal(["c1","c2"], @grandparent.poly_parent_two.children.map(&:text).sort)
120
+ end
121
+ end
122
+
123
+ ## Polymorphic pointer to child in parent (multiple types of child)
124
+ class PolyChildPointerTest < ActiveSupport::TestCase
125
+ include ARVMTestUtilities
126
+ def self.build_poly_children(arvm_test_case)
127
+ arvm_test_case.build_viewmodel(:PolyOne) do
128
+ define_schema do |t|
129
+ t.integer :number
130
+ end
131
+
132
+ define_model do
133
+ has_one :parent, as: :poly
134
+ end
135
+
136
+ define_viewmodel do
137
+ attributes :number
138
+ end
139
+ end
140
+
141
+ arvm_test_case.build_viewmodel(:PolyTwo) do
142
+ define_schema do |t|
143
+ t.string :text
144
+ end
145
+
146
+ define_model do
147
+ has_one :parent, as: :poly
148
+ end
149
+
150
+ define_viewmodel do
151
+ attributes :text
152
+ end
153
+ end
154
+ end
155
+
156
+ def self.build_parent(arvm_test_case)
157
+ arvm_test_case.build_viewmodel(:Parent) do
158
+ define_schema do |t|
159
+ t.string :name
160
+ t.string :poly_type
161
+ t.integer :poly_id
162
+ end
163
+
164
+ define_model do
165
+ belongs_to :poly, polymorphic: true, dependent: :destroy, inverse_of: :parent
166
+ end
167
+
168
+ define_viewmodel do
169
+ attributes :name
170
+ association :poly, viewmodels: [PolyOneView, PolyTwoView]
171
+ end
172
+ end
173
+ end
174
+
175
+
176
+ def before_all
177
+ super
178
+ self.class.build_poly_children(self)
179
+ self.class.build_parent(self)
180
+ end
181
+
182
+ def setup
183
+ super
184
+
185
+ @parent1 = Parent.create(name: "p1",
186
+ poly: PolyOne.new(number: 1))
187
+
188
+ @parent2 = Parent.create(name: "p2")
189
+
190
+ enable_logging!
191
+ end
192
+
193
+ def test_loading_batching
194
+ Parent.create(name: "with PolyOne", poly: PolyOne.new)
195
+ Parent.create(name: "with PolyTwo", poly: PolyTwo.new)
196
+
197
+ log_queries do
198
+ serialize(ParentView.load)
199
+ end
200
+ assert_equal(['Parent Load', 'PolyOne Load', 'PolyTwo Load'],
201
+ logged_load_queries.sort)
202
+ end
203
+
204
+ def test_create_from_view
205
+ view = {
206
+ "_type" => "Parent",
207
+ "name" => "p",
208
+ "poly" => { "_type" => "PolyTwo", "text" => "pol" }
209
+ }
210
+
211
+ pv = ParentView.deserialize_from_view(view)
212
+ p = pv.model
213
+
214
+ assert(!p.changed?)
215
+ assert(!p.new_record?)
216
+
217
+ assert_equal("p", p.name)
218
+
219
+ assert(p.poly.present?)
220
+ assert(p.poly.is_a?(PolyTwo))
221
+ assert_equal("pol", p.poly.text)
222
+ end
223
+
224
+
225
+ def test_serialize_view
226
+ view, _refs = serialize_with_references(ParentView.new(@parent1))
227
+
228
+ assert_equal({ "_type" => "Parent",
229
+ "_version" => 1,
230
+ "id" => @parent1.id,
231
+ "name" => @parent1.name,
232
+ "poly" => { "_type" => @parent1.poly_type,
233
+ "_version" => 1,
234
+ "id" => @parent1.poly.id,
235
+ "number" => @parent1.poly.number }
236
+ },
237
+ view)
238
+ end
239
+
240
+ # TODO(review): is this test worth keeping?
241
+ def test_invalid_type_lookup_with_version
242
+ ex = assert_raises do
243
+ ParentView.deserialize_from_view(
244
+ { '_type' => 'Parent',
245
+ '_new' => true,
246
+ 'poly' => {
247
+ '_type' => 'SomethingThatsNotActuallyAType',
248
+ '_version' => 1,
249
+ } })
250
+ end
251
+ assert_match(/\binvalid\b.+\bviewmodel type\b/i, ex.message)
252
+ end
253
+
254
+ def test_change_polymorphic_type
255
+ old_poly = @parent1.poly
256
+
257
+ alter_by_view!(ParentView, @parent1) do |view, refs|
258
+ view['poly'] = { '_type' => 'PolyTwo', 'text' => 'hi' }
259
+ end
260
+
261
+ assert_instance_of(PolyTwo, @parent1.poly)
262
+ assert_equal(false, PolyOne.exists?(old_poly.id))
263
+ end
264
+
265
+ class RenameTest < ActiveSupport::TestCase
266
+ include ARVMTestUtilities
267
+
268
+ def before_all
269
+ super
270
+
271
+ ViewModel::ActiveRecord::PolyTest::PolyChildPointerTest.build_poly_children(self)
272
+
273
+ build_viewmodel(:Parent) do
274
+ define_schema do |t|
275
+ t.string :name
276
+ t.string :poly_type
277
+ t.integer :poly_id
278
+ end
279
+
280
+ define_model do
281
+ belongs_to :poly, polymorphic: true, dependent: :destroy, inverse_of: :parent
282
+ end
283
+
284
+ define_viewmodel do
285
+ attributes :name
286
+ association :poly, viewmodels: [PolyOneView, PolyTwoView], as: :something_else
287
+ end
288
+ end
289
+ end
290
+
291
+ def setup
292
+ super
293
+
294
+ @parent = Parent.create(poly: PolyOne.create(number: 42))
295
+
296
+ enable_logging!
297
+ end
298
+
299
+ def test_dependencies
300
+ root_updates, _ref_updates = ViewModel::ActiveRecord::UpdateData.parse_hashes([{ '_type' => 'Parent', 'something_else' => nil }])
301
+ assert_equal(DeepPreloader::Spec.new('poly' => DeepPreloader::PolymorphicSpec.new), root_updates.first.preload_dependencies)
302
+ assert_equal({ 'something_else' => {} }, root_updates.first.updated_associations)
303
+ end
304
+
305
+ def test_renamed_roundtrip
306
+ alter_by_view!(ParentView, @parent) do |view, refs|
307
+ assert_equal({ 'id' => @parent.id,
308
+ '_type' => 'PolyOne',
309
+ '_version' => 1,
310
+ 'number' => 42 },
311
+ view['something_else'])
312
+ view['something_else'] = {'_type' => 'PolyTwo', 'text' => 'hi'}
313
+ end
314
+
315
+ assert_equal('hi', @parent.poly.text)
316
+ end
317
+ end
318
+
319
+ end
320
+ end
@@ -0,0 +1,285 @@
1
+ require_relative "../../../helpers/arvm_test_utilities.rb"
2
+ require_relative "../../../helpers/arvm_test_models.rb"
3
+
4
+ require "minitest/autorun"
5
+
6
+ require "view_model/active_record"
7
+
8
+ class ViewModel::ActiveRecord::SharedTest < ActiveSupport::TestCase
9
+ include ARVMTestUtilities
10
+
11
+ module WithCategory
12
+ def before_all
13
+ super
14
+
15
+ build_viewmodel(:Category) do
16
+ define_schema do |t|
17
+ t.string :name
18
+ end
19
+
20
+ define_model do
21
+ has_many :parents
22
+ end
23
+
24
+ define_viewmodel do
25
+ attributes :name
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ module WithParent
32
+ def before_all
33
+ super
34
+
35
+ build_viewmodel(:Parent) do
36
+ define_schema do |t|
37
+ t.string :name
38
+ t.references :category, foreign_key: true # shared reference
39
+ end
40
+
41
+ define_model do
42
+ belongs_to :category
43
+ end
44
+
45
+ define_viewmodel do
46
+ attributes :name
47
+ association :category, shared: true, optional: true
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ include WithCategory
54
+ include WithParent
55
+
56
+ def setup
57
+ super
58
+
59
+ @parent1 = Parent.create(name: "p1",
60
+ category: Category.new(name: "p1cat"))
61
+
62
+ @parent2 = Parent.create(name: "p2")
63
+
64
+ @category1 = Category.create(name: "Cat1")
65
+
66
+ enable_logging!
67
+ end
68
+
69
+ def serialize_context
70
+ ParentView.new_serialize_context(include: :category)
71
+ end
72
+
73
+ def test_loading_batching
74
+ Parent.create(category: Category.new)
75
+
76
+ log_queries do
77
+ serialize(ParentView.load(serialize_context: serialize_context),
78
+ serialize_context: serialize_context)
79
+ end
80
+ assert_equal(['Parent Load', 'Category Load'],
81
+ logged_load_queries)
82
+ end
83
+
84
+ def test_create_from_view
85
+ view = {
86
+ "_type" => "Parent",
87
+ "name" => "p",
88
+ "category" => { "_ref" => "r1" },
89
+ }
90
+ refs = {
91
+ "r1" => { "_type" => "Category", "name" => "newcat"}
92
+ }
93
+
94
+ pv = ParentView.deserialize_from_view(view, references: refs)
95
+ p = pv.model
96
+
97
+ assert(!p.changed?)
98
+ assert(!p.new_record?)
99
+
100
+ assert_equal("p", p.name)
101
+
102
+ assert(p.category.present?)
103
+ assert_equal("newcat", p.category.name)
104
+ end
105
+
106
+ def test_serialize_view
107
+ view, refs = serialize_with_references(ParentView.new(@parent1), serialize_context: serialize_context)
108
+ cat1_ref = refs.detect { |_, v| v['_type'] == 'Category' }.first
109
+
110
+ assert_equal({cat1_ref => { '_type' => "Category",
111
+ "_version" => 1,
112
+ 'id' => @parent1.category.id,
113
+ 'name' => @parent1.category.name }},
114
+ refs)
115
+
116
+ assert_equal({ "_type" => "Parent",
117
+ "_version" => 1,
118
+ "id" => @parent1.id,
119
+ "name" => @parent1.name,
120
+ "category" => { "_ref" => cat1_ref } },
121
+ view)
122
+ end
123
+
124
+ def test_shared_eager_include
125
+ parent_includes = ParentView.eager_includes
126
+
127
+ assert_equal(DeepPreloader::Spec.new, parent_includes)
128
+
129
+ extra_includes = ParentView.eager_includes(serialize_context: ParentView.new_serialize_context(include: :category))
130
+
131
+ assert_equal(DeepPreloader::Spec.new('category' => DeepPreloader::Spec.new), extra_includes)
132
+ end
133
+
134
+ def test_shared_serialize_interning
135
+ @parent2.update(category: @parent1.category)
136
+ view, refs = serialize_with_references([ParentView.new(@parent1),
137
+ ParentView.new(@parent2)],
138
+ serialize_context: ParentView.new_serialize_context(include: :category))
139
+
140
+ category_ref = view.first['category']['_ref']
141
+
142
+ assert_equal([category_ref], refs.keys,
143
+ 'category referenced twice generates a single reference')
144
+ end
145
+
146
+ def test_shared_add_reference
147
+ alter_by_view!(ParentView, @parent2, serialize_context: serialize_context) do |p2view, refs|
148
+ p2view['category'] = { '_ref' => 'myref' }
149
+ refs['myref'] = update_hash_for(CategoryView, @category1)
150
+ end
151
+
152
+ assert_equal(@category1, @parent2.category)
153
+ end
154
+
155
+ def test_shared_add_multiple_references
156
+ alter_by_view!(ParentView, [@parent1, @parent2], serialize_context: serialize_context) do |(p1view, p2view), refs|
157
+ refs.delete(p1view['category']['_ref'])
158
+ refs['myref'] = update_hash_for(CategoryView, @category1)
159
+
160
+ p1view['category'] = { '_ref' => 'myref' }
161
+ p2view['category'] = { '_ref' => 'myref' }
162
+ end
163
+
164
+ assert_equal(@category1, @parent1.category)
165
+ assert_equal(@category1, @parent2.category)
166
+ end
167
+
168
+ def test_shared_requires_all_references
169
+ ex = assert_raises(ViewModel::DeserializationError::InvalidStructure) do
170
+ alter_by_view!(ParentView, @parent2, serialize_context: serialize_context) do |p2view, refs|
171
+ refs['spurious_ref'] = { '_type' => 'Parent', 'id' => @parent1.id }
172
+ end
173
+ end
174
+ assert_match(/References not referred to from roots/, ex.message)
175
+ end
176
+
177
+ def test_shared_requires_valid_references
178
+ assert_raises(ViewModel::DeserializationError::InvalidSharedReference) do
179
+ serialize_context = ParentView.new_serialize_context(include: :category)
180
+ alter_by_view!(ParentView, @parent1, serialize_context: serialize_context) do |p1view, refs|
181
+ refs.clear # remove the expected serialized refs
182
+ end
183
+ end
184
+ end
185
+
186
+ def test_shared_requires_assignable_type
187
+ ex = assert_raises(ViewModel::DeserializationError::InvalidAssociationType) do
188
+ serialize_context = ParentView.new_serialize_context(include: :category)
189
+ alter_by_view!(ParentView, @parent1, serialize_context: serialize_context) do |p1view, refs|
190
+ p1view['category'] = { '_ref' => 'p2' }
191
+ refs['p2'] = update_hash_for(ParentView, @parent2)
192
+ end
193
+ end
194
+ assert_equal("category", ex.association)
195
+ end
196
+
197
+ def test_shared_requires_unique_references
198
+ serialize_context = ParentView.new_serialize_context(include: :category)
199
+ c1_ref = update_hash_for(CategoryView, @category1)
200
+ assert_raises(ViewModel::DeserializationError::DuplicateNodes) do
201
+ alter_by_view!(ParentView, [@parent1, @parent2], serialize_context: serialize_context) do |(p1view, p2view), refs|
202
+ refs['c_a'] = c1_ref.dup
203
+ refs['c_b'] = c1_ref.dup
204
+ p1view['category'] = { '_ref' => 'c_a' }
205
+ p2view['category'] = { '_ref' => 'c_b' }
206
+ end
207
+ end
208
+ end
209
+
210
+ def test_shared_updates_shared_data
211
+ serialize_context = ParentView.new_serialize_context(include: :category)
212
+ alter_by_view!(ParentView, @parent1, serialize_context: serialize_context) do |p1view, refs|
213
+ category_ref = p1view['category']['_ref']
214
+ refs[category_ref]['name'] = 'newcatname'
215
+ end
216
+ assert_equal('newcatname', @parent1.category.name)
217
+ end
218
+
219
+ def test_shared_delete_reference
220
+ serialize_context = ParentView.new_serialize_context(include: :category)
221
+ alter_by_view!(ParentView, @parent1, serialize_context: serialize_context) do |p1view, refs|
222
+ category_ref = p1view['category']['_ref']
223
+ refs.delete(category_ref)
224
+ p1view['category'] = nil
225
+ end
226
+ assert_nil(@parent1.category)
227
+ assert(Category.where(id: @category1.id).present?)
228
+ end
229
+
230
+ def test_child_edit_doesnt_editcheck_parent
231
+ serialize_context = ParentView.new_serialize_context(include: :category)
232
+ d_context = ParentView.new_deserialize_context
233
+
234
+ alter_by_view!(ParentView, @parent1, serialize_context: serialize_context, deserialize_context: d_context) do |view, refs|
235
+ refs[view['category']["_ref"]]["name"] = "changed"
236
+ end
237
+
238
+ assert(d_context.valid_edit_refs.include?(ViewModel::Reference.new(CategoryView, @parent1.category.id)))
239
+ refute(d_context.valid_edit_refs.include?(ViewModel::Reference.new(ParentView, @parent1.id)))
240
+ end
241
+
242
+ def test_child_change_editchecks_parent
243
+ s_context = ParentView.new_serialize_context(include: :category)
244
+
245
+ nv, d_context = alter_by_view!(ParentView, @parent1, serialize_context: s_context) do |view, refs|
246
+ refs.delete(view['category']['_ref'])
247
+ view['category']['_ref'] = 'new_cat'
248
+ refs['new_cat'] = { '_type' => 'Category', 'name' => 'new category' }
249
+ end
250
+
251
+ assert(d_context.valid_edit_refs.include?(nv.to_reference))
252
+ assert(d_context.valid_edit_refs.include?(nv.category.to_reference))
253
+ end
254
+
255
+ def test_child_delete_editchecks_parent
256
+ serialize_context = ParentView.new_serialize_context(include: :category)
257
+ d_context = ParentView.new_deserialize_context
258
+
259
+ alter_by_view!(ParentView, @parent1, serialize_context: serialize_context, deserialize_context: d_context) do |view, refs|
260
+ refs.delete(view['category']['_ref'])
261
+ view['category'] = nil
262
+ end
263
+
264
+ assert(d_context.valid_edit_refs.include?(ViewModel::Reference.new(ParentView, @parent1.id)))
265
+ end
266
+
267
+ def test_dependent_viewmodels
268
+ deps = ParentView.dependent_viewmodels
269
+ assert_equal([ParentView, CategoryView].to_set, deps)
270
+
271
+ deps = ParentView.dependent_viewmodels(include_shared: false)
272
+ assert_equal([ParentView].to_set, deps)
273
+ end
274
+
275
+ def test_deep_schema_version
276
+ vers = ParentView.deep_schema_version
277
+ assert_equal({ ParentView.view_name => ParentView.schema_version,
278
+ CategoryView.view_name => CategoryView.schema_version },
279
+ vers)
280
+
281
+ vers = ParentView.deep_schema_version(include_shared: false)
282
+ assert_equal({ ParentView.view_name => ParentView.schema_version },
283
+ vers)
284
+ end
285
+ end