iknow_view_models 2.8.4

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 (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