iknow_view_models 2.9.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,5 +1,6 @@
1
1
  require_relative "../../../helpers/arvm_test_utilities.rb"
2
2
  require_relative "../../../helpers/arvm_test_models.rb"
3
+ require_relative '../../../helpers/viewmodel_spec_helpers.rb'
3
4
 
4
5
  require "minitest/autorun"
5
6
 
@@ -7,122 +8,57 @@ require "view_model/active_record"
7
8
 
8
9
  class ViewModel::ActiveRecord::BelongsToTest < ActiveSupport::TestCase
9
10
  include ARVMTestUtilities
10
-
11
- module WithLabel
12
- def before_all
13
- super
14
-
15
- build_viewmodel(:Label) do
16
- define_schema do |t|
17
- t.string :text
18
- end
19
-
20
- define_model do
21
- has_one :parent, inverse_of: :label
22
- end
23
-
24
- define_viewmodel do
25
- attributes :text
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 :label, foreign_key: true
39
- end
40
-
41
- define_model do
42
- belongs_to :label, inverse_of: :parent, dependent: :destroy
43
- end
44
-
45
- define_viewmodel do
46
- attributes :name
47
- associations :label
48
- end
49
- end
50
- end
51
- end
52
-
53
- module WithOwner
54
- def before_all
55
- super
56
-
57
- build_viewmodel(:Owner) do
58
- define_schema do |t|
59
- t.integer :deleted_id
60
- t.integer :ignored_id
61
- end
62
-
63
- define_model do
64
- belongs_to :deleted, class_name: Label.name, dependent: :delete
65
- belongs_to :ignored, class_name: Label.name
66
- end
67
-
68
- define_viewmodel do
69
- associations :deleted, :ignored
70
- end
71
- end
72
- end
73
- end
74
-
75
- include WithLabel
76
- include WithParent
11
+ extend Minitest::Spec::DSL
12
+ include ViewModelSpecHelpers::ParentAndBelongsToChild
77
13
 
78
14
  def setup
79
15
  super
80
16
 
81
17
  # TODO make a `has_list?` that allows a parent to set all children as an array
82
- @parent1 = Parent.new(name: "p1",
83
- label: Label.new(text: "p1l"))
84
- @parent1.save!
18
+ @model1 = model_class.new(name: "p1",
19
+ child: child_model_class.new(name: "p1l"))
20
+ @model1.save!
85
21
 
86
- @parent2 = Parent.new(name: "p2",
87
- label: Label.new(text: "p2l"))
22
+ @model2 = model_class.new(name: "p2",
23
+ child: child_model_class.new(name: "p2l"))
88
24
 
89
- @parent2.save!
25
+ @model2.save!
90
26
 
91
27
  enable_logging!
92
28
  end
93
29
 
94
30
  def test_serialize_view
95
- view, _refs = serialize_with_references(ParentView.new(@parent1))
31
+ view, _refs = serialize_with_references(ModelView.new(@model1))
96
32
 
97
- assert_equal({ "_type" => "Parent",
33
+ assert_equal({ "_type" => "Model",
98
34
  "_version" => 1,
99
- "id" => @parent1.id,
100
- "name" => @parent1.name,
101
- "label" => { "_type" => "Label",
35
+ "id" => @model1.id,
36
+ "name" => @model1.name,
37
+ "child" => { "_type" => "Child",
102
38
  "_version" => 1,
103
- "id" => @parent1.label.id,
104
- "text" => @parent1.label.text },
39
+ "id" => @model1.child.id,
40
+ "name" => @model1.child.name },
105
41
  },
106
42
  view)
107
43
  end
108
44
 
109
45
  def test_loading_batching
110
46
  log_queries do
111
- serialize(ParentView.load)
47
+ serialize(ModelView.load)
112
48
  end
113
49
 
114
- assert_equal(['Parent Load', 'Label Load'],
50
+ assert_equal(['Model Load', 'Child Load'],
115
51
  logged_load_queries)
116
52
  end
117
53
 
118
54
  def test_create_from_view
119
55
  view = {
120
- "_type" => "Parent",
56
+ "_type" => "Model",
121
57
  "name" => "p",
122
- "label" => { "_type" => "Label", "text" => "l" },
58
+ "child" => { "_type" => "Child", "name" => "l" },
123
59
  }
124
60
 
125
- pv = ParentView.deserialize_from_view(view)
61
+ pv = ModelView.deserialize_from_view(view)
126
62
  p = pv.model
127
63
 
128
64
  assert(!p.changed?)
@@ -130,185 +66,181 @@ class ViewModel::ActiveRecord::BelongsToTest < ActiveSupport::TestCase
130
66
 
131
67
  assert_equal("p", p.name)
132
68
 
133
- assert(p.label.present?)
134
- assert_equal("l", p.label.text)
69
+ assert(p.child.present?)
70
+ assert_equal("l", p.child.name)
135
71
  end
136
72
 
137
73
  def test_create_belongs_to_nil
138
- view = { '_type' => 'Parent', 'name' => 'p', 'label' => nil }
139
- pv = ParentView.deserialize_from_view(view)
140
- assert_nil(pv.model.label)
74
+ view = { '_type' => 'Model', 'name' => 'p', 'child' => nil }
75
+ pv = ModelView.deserialize_from_view(view)
76
+ assert_nil(pv.model.child)
141
77
  end
142
78
 
143
79
  def test_create_invalid_child_type
144
- view = { '_type' => 'Parent', 'name' => 'p', 'label' => { '_type' => 'Parent', 'name' => 'q' } }
80
+ view = { '_type' => 'Model', 'name' => 'p', 'child' => { '_type' => 'Model', 'name' => 'q' } }
145
81
  assert_raises(ViewModel::DeserializationError::InvalidAssociationType) do
146
- ParentView.deserialize_from_view(view)
82
+ ModelView.deserialize_from_view(view)
147
83
  end
148
84
  end
149
85
 
150
86
  def test_belongs_to_create
151
- @parent1.update(label: nil)
87
+ @model1.update(child: nil)
152
88
 
153
- alter_by_view!(ParentView, @parent1) do |view, refs|
154
- view['label'] = { '_type' => 'Label', 'text' => 'cheese' }
89
+ alter_by_view!(ModelView, @model1) do |view, refs|
90
+ view['child'] = { '_type' => 'Child', 'name' => 'cheese' }
155
91
  end
156
92
 
157
- assert_equal('cheese', @parent1.label.text)
93
+ assert_equal('cheese', @model1.child.name)
158
94
  end
159
95
 
160
96
  def test_belongs_to_replace
161
- old_label = @parent1.label
97
+ old_child = @model1.child
162
98
 
163
- alter_by_view!(ParentView, @parent1) do |view, refs|
164
- view['label'] = { '_type' => 'Label', 'text' => 'cheese' }
99
+ alter_by_view!(ModelView, @model1) do |view, refs|
100
+ view['child'] = { '_type' => 'Child', 'name' => 'cheese' }
165
101
  end
166
102
 
167
- assert_equal('cheese', @parent1.label.text)
168
- assert(Label.where(id: old_label).blank?)
103
+ assert_equal('cheese', @model1.child.name)
104
+ assert(Child.where(id: old_child).blank?)
169
105
  end
170
106
 
171
107
  def test_belongs_to_move_and_replace
172
- old_p1_label = @parent1.label
173
- old_p2_label = @parent2.label
108
+ old_p1_child = @model1.child
109
+ old_p2_child = @model2.child
174
110
 
175
- set_by_view!(ParentView, [@parent1, @parent2]) do |(p1, p2), refs|
176
- p1['label'] = nil
177
- p2['label'] = update_hash_for(LabelView, old_p1_label)
111
+ set_by_view!(ModelView, [@model1, @model2]) do |(p1, p2), refs|
112
+ p1['child'] = nil
113
+ p2['child'] = update_hash_for(ChildView, old_p1_child)
178
114
  end
179
115
 
180
- assert(@parent1.label.blank?, 'l1 label reference removed')
181
- assert_equal(old_p1_label, @parent2.label, 'p2 has label from p1')
182
- assert(Label.where(id: old_p2_label).blank?, 'p2 old label deleted')
116
+ assert(@model1.child.blank?, 'l1 child reference removed')
117
+ assert_equal(old_p1_child, @model2.child, 'p2 has child from p1')
118
+ assert(Child.where(id: old_p2_child).blank?, 'p2 old child deleted')
183
119
  end
184
120
 
185
121
  def test_belongs_to_swap
186
- old_p1_label = @parent1.label
187
- old_p2_label = @parent2.label
122
+ old_p1_child = @model1.child
123
+ old_p2_child = @model2.child
188
124
 
189
- alter_by_view!(ParentView, [@parent1, @parent2]) do |(p1, p2), refs|
190
- p1['label'] = update_hash_for(LabelView, old_p2_label)
191
- p2['label'] = update_hash_for(LabelView, old_p1_label)
125
+ alter_by_view!(ModelView, [@model1, @model2]) do |(p1, p2), refs|
126
+ p1['child'] = update_hash_for(ChildView, old_p2_child)
127
+ p2['child'] = update_hash_for(ChildView, old_p1_child)
192
128
  end
193
129
 
194
- assert_equal(old_p2_label, @parent1.label, 'p1 has label from p2')
195
- assert_equal(old_p1_label, @parent2.label, 'p2 has label from p1')
130
+ assert_equal(old_p2_child, @model1.child, 'p1 has child from p2')
131
+ assert_equal(old_p1_child, @model2.child, 'p2 has child from p1')
196
132
  end
197
133
 
198
134
  def test_moved_child_is_not_delete_checked
199
135
  # move from p1 to p3
200
- d_context = ParentView.new_deserialize_context
136
+ d_context = ModelView.new_deserialize_context
201
137
 
202
- target_label = Label.create
203
- from_parent = Parent.create(name: 'from', label: target_label)
204
- to_parent = Parent.create(name: 'p3')
138
+ target_child = Child.create
139
+ from_model = Model.create(name: 'from', child: target_child)
140
+ to_model = Model.create(name: 'p3')
205
141
 
206
142
  alter_by_view!(
207
- ParentView, [from_parent, to_parent],
143
+ ModelView, [from_model, to_model],
208
144
  deserialize_context: d_context
209
145
  ) do |(from, to), refs|
210
- from['label'] = nil
211
- to['label'] = update_hash_for(LabelView, target_label)
146
+ from['child'] = nil
147
+ to['child'] = update_hash_for(ChildView, target_child)
212
148
  end
213
149
 
214
- assert_equal(target_label, to_parent.label, 'target label moved')
215
- assert_equal([ViewModel::Reference.new(ParentView, from_parent.id),
216
- ViewModel::Reference.new(ParentView, to_parent.id)],
150
+ assert_equal(target_child, to_model.child, 'target child moved')
151
+ assert_equal([ViewModel::Reference.new(ModelView, from_model.id),
152
+ ViewModel::Reference.new(ModelView, to_model.id)],
217
153
  d_context.valid_edit_refs,
218
- "only parents are checked for change; child was not")
154
+ "only models are checked for change; child was not")
219
155
  end
220
156
 
221
157
  def test_implicit_release_invalid_belongs_to
222
- taken_label_ref = update_hash_for(LabelView, @parent1.label)
158
+ taken_child_ref = update_hash_for(ChildView, @model1.child)
223
159
  assert_raises(ViewModel::DeserializationError::ParentNotFound) do
224
- ParentView.deserialize_from_view(
225
- [{ '_type' => 'Parent',
160
+ ModelView.deserialize_from_view(
161
+ [{ '_type' => 'Model',
226
162
  'name' => 'newp',
227
- 'label' => taken_label_ref }])
163
+ 'child' => taken_child_ref }])
228
164
  end
229
165
  end
230
166
 
231
167
  class GCTests < ActiveSupport::TestCase
232
168
  include ARVMTestUtilities
233
- include WithLabel
234
- include WithOwner
235
- include WithParent
169
+ include ViewModelSpecHelpers::ParentAndBelongsToChild
170
+
171
+ def model_attributes
172
+ super.merge(
173
+ schema: ->(t) do
174
+ t.integer :deleted_child_id
175
+ t.integer :ignored_child_id
176
+ end,
177
+ model: ->(m) do
178
+ belongs_to :deleted_child, class_name: Child.name, dependent: :delete
179
+ belongs_to :ignored_child, class_name: Child.name
180
+ end,
181
+ viewmodel: ->(v) do
182
+ associations :deleted_child, :ignored_child
183
+ end)
184
+ end
236
185
 
237
186
  # test belongs_to garbage collection - dependent: delete_all
238
187
  def test_gc_dependent_delete_all
239
- owner = Owner.create(deleted: Label.new(text: 'one'))
240
- old_label = owner.deleted
188
+ model = model_class.create(deleted_child: Child.new(name: 'one'))
189
+ old_child = model.deleted_child
241
190
 
242
- alter_by_view!(OwnerView, owner) do |ov, refs|
243
- ov['deleted'] = { '_type' => 'Label', 'text' => 'two' }
191
+ alter_by_view!(ModelView, model) do |ov, _refs|
192
+ ov['deleted_child'] = { '_type' => 'Child', 'name' => 'two' }
244
193
  end
245
194
 
246
- assert_equal('two', owner.deleted.text)
247
- refute_equal(old_label, owner.deleted)
248
- assert(Label.where(id: old_label.id).blank?)
195
+ assert_equal('two', model.deleted_child.name)
196
+ refute_equal(old_child, model.deleted_child)
197
+ assert(Child.where(id: old_child.id).blank?)
249
198
  end
250
199
 
251
200
  def test_no_gc_dependent_ignore
252
- owner = Owner.create(ignored: Label.new(text: "one"))
253
- old_label = owner.ignored
201
+ model = model_class.create(ignored_child: Child.new(name: "one"))
202
+ old_child = model.ignored_child
254
203
 
255
- alter_by_view!(OwnerView, owner) do |ov, refs|
256
- ov['ignored'] = { '_type' => 'Label', 'text' => 'two' }
204
+ alter_by_view!(ModelView, model) do |ov, _refs|
205
+ ov['ignored_child'] = { '_type' => 'Child', 'name' => 'two' }
257
206
  end
258
- assert_equal('two', owner.ignored.text)
259
- refute_equal(old_label, owner.ignored)
260
- assert_equal(1, Label.where(id: old_label.id).count)
207
+ assert_equal('two', model.ignored_child.name)
208
+ refute_equal(old_child, model.ignored_child)
209
+ assert_equal(1, Child.where(id: old_child.id).count)
261
210
  end
262
211
  end
263
212
 
264
213
  class RenamedTest < ActiveSupport::TestCase
265
214
  include ARVMTestUtilities
266
- include WithLabel
267
-
268
- def before_all
269
- super
270
-
271
- build_viewmodel(:Parent) do
272
- define_schema do |t|
273
- t.string :name
274
- t.references :label, foreign_key: true
275
- end
276
-
277
- define_model do
278
- belongs_to :label, inverse_of: :parent, dependent: :destroy
279
- end
215
+ include ViewModelSpecHelpers::ParentAndBelongsToChild
280
216
 
281
- define_viewmodel do
282
- attributes :name
283
- association :label, as: :something_else
284
- end
285
- end
217
+ def subject_association_features
218
+ { as: :something_else }
286
219
  end
287
220
 
288
221
  def setup
289
222
  super
290
223
 
291
- @parent = Parent.create(name: 'p1', label: Label.new(text: 'l1'))
224
+ @model = model_class.create(name: 'p1', child: child_model_class.new(name: 'l1'))
292
225
 
293
226
  enable_logging!
294
227
  end
295
228
 
296
229
  def test_dependencies
297
- root_updates, _ref_updates = ViewModel::ActiveRecord::UpdateData.parse_hashes([{ '_type' => 'Parent', 'something_else' => nil }])
298
- assert_equal(DeepPreloader::Spec.new('label' => DeepPreloader::Spec.new), root_updates.first.preload_dependencies)
299
- assert_equal({ 'something_else' => {} }, root_updates.first.updated_associations)
230
+ root_updates, _ref_updates = ViewModel::ActiveRecord::UpdateData.parse_hashes([{ '_type' => 'Model', 'something_else' => nil }])
231
+ assert_equal(DeepPreloader::Spec.new('child' => DeepPreloader::Spec.new), root_updates.first.preload_dependencies)
300
232
  end
301
233
 
302
234
  def test_renamed_roundtrip
303
- alter_by_view!(ParentView, @parent) do |view, refs|
304
- assert_equal({ 'id' => @parent.label.id,
305
- '_type' => 'Label',
235
+ alter_by_view!(ModelView, @model) do |view, refs|
236
+ assert_equal({ 'id' => @model.child.id,
237
+ '_type' => 'Child',
306
238
  '_version' => 1,
307
- 'text' => 'l1' },
239
+ 'name' => 'l1' },
308
240
  view['something_else'])
309
- view['something_else']['text'] = 'new l1 text'
241
+ view['something_else']['name'] = 'new l1 name'
310
242
  end
311
- assert_equal('new l1 text', @parent.label.text)
243
+ assert_equal('new l1 name', @model.child.name)
312
244
  end
313
245
  end
314
246
 
@@ -353,7 +285,7 @@ class ViewModel::ActiveRecord::BelongsToTest < ActiveSupport::TestCase
353
285
  end
354
286
 
355
287
 
356
- # Do we support replacing a node in the tree and reparenting its children
288
+ # Do we support replacing a node in the tree and remodeling its children
357
289
  # back to it? In theory we want to, but currently we don't: the child node
358
290
  # is unresolvable.
359
291
 
@@ -41,7 +41,7 @@ class ViewModel::ActiveRecord
41
41
  schema: ->(t) { t.references :shared, foreign_key: true },
42
42
  model: ->(_) { belongs_to :shared, inverse_of: :models },
43
43
  viewmodel: ->(_) {
44
- association :shared, shared: true, optional: false
44
+ association :shared
45
45
  cacheable!
46
46
  }
47
47
  )
@@ -63,6 +63,7 @@ class ViewModel::ActiveRecord
63
63
  end
64
64
 
65
65
  define_viewmodel do
66
+ root!
66
67
  attributes :name
67
68
  cacheable!(cache_group: shared_cache_group)
68
69
  end
@@ -326,7 +327,7 @@ class ViewModel::ActiveRecord
326
327
  end
327
328
 
328
329
  describe "with a non-cacheable shared child" do
329
- include ViewModelSpecHelpers::ParentAndSharedChild
330
+ include ViewModelSpecHelpers::ParentAndSharedBelongsToChild
330
331
  def model_attributes
331
332
  super.merge(viewmodel: ->(_) { cacheable! })
332
333
  end
@@ -173,7 +173,7 @@ class ViewModel::ActiveRecord
173
173
  end
174
174
 
175
175
  describe "as belongs_to shared child" do
176
- include ViewModelSpecHelpers::ParentAndSharedChild
176
+ include ViewModelSpecHelpers::ParentAndSharedBelongsToChild
177
177
  include BehavesLikeConstructingAChild
178
178
  it "can clone the model but not the child" do
179
179
  clone_model = Cloner.new.clone(viewmodel)
@@ -1,15 +1,14 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- require "bundler/setup"
4
- Bundler.require
5
-
6
- require_relative "../../../helpers/callback_tracer.rb"
7
- require_relative "../../../helpers/controller_test_helpers.rb"
8
-
9
- require 'byebug'
10
-
11
3
  require "minitest/autorun"
12
4
  require 'minitest/unit'
5
+ require 'minitest/hooks'
6
+
7
+ require "view_model"
8
+ require "view_model/active_record"
9
+
10
+ require_relative "../../../helpers/controller_test_helpers.rb"
11
+ require_relative "../../../helpers/callback_tracer.rb"
13
12
 
14
13
  class ViewModel::ActiveRecord::ControllerTest < ActiveSupport::TestCase
15
14
  include ARVMTestUtilities
@@ -144,9 +143,7 @@ class ViewModel::ActiveRecord::ControllerTest < ActiveSupport::TestCase
144
143
  p2_view = ParentView.new(p2)
145
144
  assert(p2.present?, 'p2 created')
146
145
 
147
- context = ParentView.new_serialize_context(include: 'children')
148
- assert_equal({ 'data' => p2_view.to_hash(serialize_context: context) },
149
- parentcontroller.hash_response)
146
+ assert_equal({ 'data' => p2_view.to_hash }, parentcontroller.hash_response)
150
147
 
151
148
  assert_all_hooks_nested_inside_parent_hook(parentcontroller.hook_trace)
152
149
  end
@@ -210,9 +207,7 @@ class ViewModel::ActiveRecord::ControllerTest < ActiveSupport::TestCase
210
207
  'detail' => "Couldn't find Parent(s) with id(s)=[9999]",
211
208
  'title' => nil,
212
209
  'code' => "DeserializationError.NotFound",
213
- 'meta' => { 'nodes' => [{ '_type' => "Parent", 'id' => 9999 }]},
214
- 'exception' => nil,
215
- 'causes' => nil }},
210
+ 'meta' => { 'nodes' => [{ '_type' => "Parent", 'id' => 9999 }]}}},
216
211
  parentcontroller.hash_response)
217
212
  end
218
213
 
@@ -234,9 +229,7 @@ class ViewModel::ActiveRecord::ControllerTest < ActiveSupport::TestCase
234
229
  'meta' => { 'nodes' => [{ '_type' => "Child", 'id' => nil }],
235
230
  'attribute' => 'age',
236
231
  'message' => 'must be less than 42',
237
- 'details' => { 'error' => 'less_than', 'value' => 42, 'count' => 42 }},
238
- 'exception' => nil,
239
- 'causes' => nil }},
232
+ 'details' => { 'error' => 'less_than', 'value' => 42, 'count' => 42 }}}},
240
233
  parentcontroller.hash_response)
241
234
  end
242
235
 
@@ -264,9 +257,7 @@ class ViewModel::ActiveRecord::ControllerTest < ActiveSupport::TestCase
264
257
  'detail' => "Couldn't find Parent(s) with id(s)=[9999]",
265
258
  'title' => nil,
266
259
  'code' => "DeserializationError.NotFound",
267
- 'meta' => { "nodes" => [{"_type" => "Parent", "id" => 9999 }] },
268
- 'exception' => nil,
269
- 'causes' => nil } },
260
+ 'meta' => { "nodes" => [{"_type" => "Parent", "id" => 9999}]}} },
270
261
  parentcontroller.hash_response)
271
262
  assert_equal(404, parentcontroller.status)
272
263
  end
@@ -337,6 +328,7 @@ class ViewModel::ActiveRecord::ControllerTest < ActiveSupport::TestCase
337
328
  assert_all_hooks_nested_inside_parent_hook(childcontroller.hook_trace)
338
329
  end
339
330
 
331
+ # FIXME: nested controllers really need to be to other roots; children aren't roots.
340
332
  def test_nested_collection_replace
341
333
  # Parent.children
342
334
  old_children = @parent.children