iknow_view_models 3.2.0 → 3.2.5

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +13 -0
  3. data/Appraisals +6 -6
  4. data/Rakefile +5 -5
  5. data/gemfiles/rails_5_2.gemfile +5 -5
  6. data/gemfiles/rails_6_0.gemfile +5 -5
  7. data/iknow_view_models.gemspec +40 -39
  8. data/lib/iknow_view_models.rb +9 -7
  9. data/lib/iknow_view_models/version.rb +1 -1
  10. data/lib/view_model.rb +17 -14
  11. data/lib/view_model/access_control.rb +5 -2
  12. data/lib/view_model/access_control/composed.rb +10 -9
  13. data/lib/view_model/access_control/open.rb +2 -0
  14. data/lib/view_model/access_control/read_only.rb +2 -0
  15. data/lib/view_model/access_control/tree.rb +11 -6
  16. data/lib/view_model/access_control_error.rb +4 -1
  17. data/lib/view_model/active_record.rb +17 -15
  18. data/lib/view_model/active_record/association_data.rb +2 -1
  19. data/lib/view_model/active_record/association_manipulation.rb +6 -4
  20. data/lib/view_model/active_record/cache.rb +4 -2
  21. data/lib/view_model/active_record/collection_nested_controller.rb +3 -3
  22. data/lib/view_model/active_record/controller.rb +20 -5
  23. data/lib/view_model/active_record/controller_base.rb +4 -1
  24. data/lib/view_model/active_record/nested_controller_base.rb +1 -0
  25. data/lib/view_model/active_record/update_context.rb +8 -6
  26. data/lib/view_model/active_record/update_data.rb +32 -30
  27. data/lib/view_model/active_record/update_operation.rb +17 -13
  28. data/lib/view_model/active_record/visitor.rb +0 -1
  29. data/lib/view_model/after_transaction_runner.rb +0 -1
  30. data/lib/view_model/callbacks.rb +3 -1
  31. data/lib/view_model/controller.rb +13 -3
  32. data/lib/view_model/deserialization_error.rb +15 -12
  33. data/lib/view_model/error.rb +12 -10
  34. data/lib/view_model/error_view.rb +3 -1
  35. data/lib/view_model/migration/no_path_error.rb +1 -0
  36. data/lib/view_model/migration/one_way_error.rb +1 -0
  37. data/lib/view_model/migration/unspecified_version_error.rb +1 -0
  38. data/lib/view_model/record.rb +11 -13
  39. data/lib/view_model/reference.rb +3 -1
  40. data/lib/view_model/references.rb +8 -5
  41. data/lib/view_model/registry.rb +14 -2
  42. data/lib/view_model/schemas.rb +9 -4
  43. data/lib/view_model/serialization_error.rb +4 -1
  44. data/lib/view_model/serialize_context.rb +4 -4
  45. data/lib/view_model/test_helpers.rb +8 -3
  46. data/lib/view_model/test_helpers/arvm_builder.rb +19 -14
  47. data/lib/view_model/traversal_context.rb +2 -1
  48. data/test/.rubocop.yml +14 -0
  49. data/test/helpers/arvm_test_models.rb +12 -9
  50. data/test/helpers/arvm_test_utilities.rb +5 -3
  51. data/test/helpers/controller_test_helpers.rb +42 -33
  52. data/test/helpers/match_enumerator.rb +1 -0
  53. data/test/helpers/query_logging.rb +2 -1
  54. data/test/helpers/test_access_control.rb +5 -3
  55. data/test/helpers/viewmodel_spec_helpers.rb +21 -20
  56. data/test/unit/view_model/access_control_test.rb +144 -144
  57. data/test/unit/view_model/active_record/alias_test.rb +15 -13
  58. data/test/unit/view_model/active_record/belongs_to_test.rb +40 -39
  59. data/test/unit/view_model/active_record/cache_test.rb +27 -26
  60. data/test/unit/view_model/active_record/cloner_test.rb +67 -63
  61. data/test/unit/view_model/active_record/controller_test.rb +81 -67
  62. data/test/unit/view_model/active_record/counter_test.rb +10 -9
  63. data/test/unit/view_model/active_record/customization_test.rb +59 -58
  64. data/test/unit/view_model/active_record/has_many_test.rb +112 -111
  65. data/test/unit/view_model/active_record/has_many_through_poly_test.rb +15 -14
  66. data/test/unit/view_model/active_record/has_many_through_test.rb +33 -38
  67. data/test/unit/view_model/active_record/has_one_test.rb +37 -36
  68. data/test/unit/view_model/active_record/migration_test.rb +13 -13
  69. data/test/unit/view_model/active_record/namespacing_test.rb +19 -17
  70. data/test/unit/view_model/active_record/poly_test.rb +44 -45
  71. data/test/unit/view_model/active_record/shared_test.rb +30 -28
  72. data/test/unit/view_model/active_record/version_test.rb +9 -7
  73. data/test/unit/view_model/active_record_test.rb +72 -72
  74. data/test/unit/view_model/callbacks_test.rb +19 -15
  75. data/test/unit/view_model/controller_test.rb +4 -2
  76. data/test/unit/view_model/record_test.rb +158 -145
  77. data/test/unit/view_model/registry_test.rb +38 -0
  78. data/test/unit/view_model/traversal_context_test.rb +4 -5
  79. data/test/unit/view_model_test.rb +18 -16
  80. metadata +10 -6
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'minitest/autorun'
2
4
  require 'minitest/unit'
3
5
  require 'minitest/hooks'
4
6
 
5
- require_relative '../../helpers/arvm_test_utilities.rb'
6
- require_relative '../../helpers/viewmodel_spec_helpers.rb'
7
+ require_relative '../../helpers/arvm_test_utilities'
8
+ require_relative '../../helpers/viewmodel_spec_helpers'
7
9
 
8
10
  require 'view_model'
9
11
  require 'view_model/controller'
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../helpers/test_access_control.rb"
3
+ require_relative '../../helpers/test_access_control'
4
4
 
5
- require "minitest/autorun"
5
+ require 'minitest/autorun'
6
6
  require 'minitest/unit'
7
7
 
8
- require "view_model"
9
- require "view_model/record"
8
+ require 'view_model'
9
+ require 'view_model/record'
10
10
 
11
11
  class ViewModel::RecordTest < ActiveSupport::TestCase
12
12
  using ViewModel::Utils::Collections
@@ -15,6 +15,7 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
15
15
  class TestDeserializeContext < ViewModel::DeserializeContext
16
16
  class SharedContext < ViewModel::DeserializeContext::SharedContext
17
17
  attr_reader :targets
18
+
18
19
  def initialize(targets: [], **rest)
19
20
  super(**rest)
20
21
  @targets = targets
@@ -26,16 +27,9 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
26
27
  end
27
28
 
28
29
  delegate :targets, to: :shared_context
29
-
30
- def initialize(**rest)
31
- super(**rest)
32
- end
33
30
  end
34
31
 
35
32
  class TestSerializeContext < ViewModel::SerializeContext
36
- def initialize(**rest)
37
- super(**rest)
38
- end
39
33
  end
40
34
 
41
35
  class TestViewModel < ViewModel::Record
@@ -79,7 +73,7 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
79
73
  # associations.
80
74
  self.unregistered = true
81
75
 
82
- self.view_name = "Model"
76
+ self.view_name = 'Model'
83
77
  self.model_class = mc
84
78
 
85
79
  attrs.each { |a, opts| attribute(a, **opts) }
@@ -90,8 +84,8 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
90
84
 
91
85
  let(:view_base) do
92
86
  {
93
- "_type" => "Model",
94
- "_version" => 1,
87
+ '_type' => 'Model',
88
+ '_version' => 1,
95
89
  }
96
90
  end
97
91
 
@@ -144,7 +138,7 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
144
138
  module CanDeserializeToNew
145
139
  def self.included(base)
146
140
  base.instance_eval do
147
- it "can deserialize to a new model" do
141
+ it 'can deserialize to a new model' do
148
142
  vm = viewmodel_class.deserialize_from_view(default_view, deserialize_context: create_context)
149
143
  assert_equal(default_model, vm.model)
150
144
  refute(default_model.equal?(vm.model))
@@ -159,7 +153,7 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
159
153
  module CanDeserializeToExisting
160
154
  def self.included(base)
161
155
  base.instance_eval do
162
- it "can deserialize to existing model with no changes" do
156
+ it 'can deserialize to existing model with no changes' do
163
157
  vm = viewmodel_class.deserialize_from_view(default_view, deserialize_context: update_context)
164
158
  assert(default_model.equal?(vm.model))
165
159
 
@@ -172,7 +166,7 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
172
166
  module CanSerialize
173
167
  def self.included(base)
174
168
  base.instance_eval do
175
- it "can serialize to the expected view" do
169
+ it 'can serialize to the expected view' do
176
170
  h = viewmodel_class.new(default_model).to_hash
177
171
  assert_equal(default_view, h)
178
172
  end
@@ -180,44 +174,44 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
180
174
  end
181
175
  end
182
176
 
183
- describe "with simple attribute" do
177
+ describe 'with simple attribute' do
184
178
  let(:attributes) { { simple: {} } }
185
179
  include CanSerialize
186
180
  include CanDeserializeToNew
187
181
  include CanDeserializeToExisting
188
182
 
189
- it "can be updated" do
190
- new_view = default_view.merge("simple" => "changed")
183
+ it 'can be updated' do
184
+ new_view = default_view.merge('simple' => 'changed')
191
185
 
192
186
  vm = viewmodel_class.deserialize_from_view(new_view, deserialize_context: update_context)
193
187
 
194
- assert(default_model.equal?(vm.model), "returned model was not the same")
195
- assert_equal("changed", default_model.simple)
188
+ assert(default_model.equal?(vm.model), 'returned model was not the same')
189
+ assert_equal('changed', default_model.simple)
196
190
  assert_edited(vm, changed_attributes: [:simple])
197
191
  end
198
192
 
199
- it "rejects unknown attributes" do
200
- view = default_view.merge("unknown" => "illegal")
193
+ it 'rejects unknown attributes' do
194
+ view = default_view.merge('unknown' => 'illegal')
201
195
  ex = assert_raises(ViewModel::DeserializationError::UnknownAttribute) do
202
196
  viewmodel_class.deserialize_from_view(view, deserialize_context: create_context)
203
197
  end
204
- assert_equal("unknown", ex.attribute)
198
+ assert_equal('unknown', ex.attribute)
205
199
  end
206
200
 
207
- it "edit checks when creating empty" do
201
+ it 'edit checks when creating empty' do
208
202
  vm = viewmodel_class.deserialize_from_view(view_base, deserialize_context: create_context)
209
- refute(default_model.equal?(vm.model), "returned model was the same")
203
+ refute(default_model.equal?(vm.model), 'returned model was the same')
210
204
  assert_edited(vm, new: true)
211
205
  end
212
206
  end
213
207
 
214
- describe "with validated simple attribute" do
208
+ describe 'with validated simple attribute' do
215
209
  let(:attributes) { { validated: {} } }
216
210
  let(:viewmodel_body) do
217
211
  ->(_x) do
218
212
  def validate!
219
- if validated == "naughty"
220
- raise ViewModel::DeserializationError::Validation.new("validated", "was naughty", nil, self.blame_reference)
213
+ if validated == 'naughty'
214
+ raise ViewModel::DeserializationError::Validation.new('validated', 'was naughty', nil, self.blame_reference)
221
215
  end
222
216
  end
223
217
  end
@@ -227,34 +221,34 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
227
221
  include CanDeserializeToNew
228
222
  include CanDeserializeToExisting
229
223
 
230
- it "rejects update when validation fails" do
231
- new_view = default_view.merge("validated" => "naughty")
224
+ it 'rejects update when validation fails' do
225
+ new_view = default_view.merge('validated' => 'naughty')
232
226
 
233
227
  ex = assert_raises(ViewModel::DeserializationError::Validation) do
234
228
  viewmodel_class.deserialize_from_view(new_view, deserialize_context: update_context)
235
229
  end
236
- assert_equal("validated", ex.attribute)
237
- assert_equal("was naughty", ex.reason)
230
+ assert_equal('validated', ex.attribute)
231
+ assert_equal('was naughty', ex.reason)
238
232
  end
239
233
  end
240
234
 
241
- describe "with renamed attribute" do
235
+ describe 'with renamed attribute' do
242
236
  let(:attributes) { { modelname: { as: :viewname } } }
243
- let(:default_model_values) { { modelname: "value" } }
244
- let(:default_view_values) { { viewname: "value" } }
237
+ let(:default_model_values) { { modelname: 'value' } }
238
+ let(:default_view_values) { { viewname: 'value' } }
245
239
 
246
240
  include CanSerialize
247
241
  include CanDeserializeToNew
248
242
  include CanDeserializeToExisting
249
243
 
250
- it "makes attributes available on their new names" do
251
- value(default_model.modelname).must_equal("value")
244
+ it 'makes attributes available on their new names' do
245
+ value(default_model.modelname).must_equal('value')
252
246
  vm = viewmodel_class.new(default_model)
253
- value(vm.viewname).must_equal("value")
247
+ value(vm.viewname).must_equal('value')
254
248
  end
255
249
  end
256
250
 
257
- describe "with formatted attribute" do
251
+ describe 'with formatted attribute' do
258
252
  let(:attributes) { { moment: { format: IknowParams::Serializer::Time } } }
259
253
  let(:moment) { 1.week.ago.change(usec: 0) }
260
254
  let(:default_model_values) { { moment: moment } }
@@ -264,8 +258,8 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
264
258
  include CanDeserializeToNew
265
259
  include CanDeserializeToExisting
266
260
 
267
- it "raises correctly on an unparseable value" do
268
- bad_view = default_view.tap { |v| v["moment"] = "not a timestamp" }
261
+ it 'raises correctly on an unparseable value' do
262
+ bad_view = default_view.tap { |v| v['moment'] = 'not a timestamp' }
269
263
  ex = assert_raises(ViewModel::DeserializationError::Validation) do
270
264
  viewmodel_class.deserialize_from_view(bad_view, deserialize_context: create_context)
271
265
  end
@@ -273,7 +267,7 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
273
267
  assert_match(/could not be deserialized because.*Time/, ex.detail)
274
268
  end
275
269
 
276
- it "raises correctly on an undeserializable value" do
270
+ it 'raises correctly on an undeserializable value' do
277
271
  bad_model = default_model.tap { |m| m.moment = 2.7 }
278
272
  ex = assert_raises(ViewModel::SerializationError) do
279
273
  viewmodel_class.new(bad_model).to_hash
@@ -282,40 +276,40 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
282
276
  end
283
277
  end
284
278
 
285
- describe "with read-only attribute" do
279
+ describe 'with read-only attribute' do
286
280
  let(:attributes) { { read_only: { read_only: true } } }
287
281
 
288
282
  include CanSerialize
289
283
  include CanDeserializeToExisting
290
284
 
291
- it "deserializes to new without the attribute" do
292
- new_view = default_view.tap { |v| v.delete("read_only") }
285
+ it 'deserializes to new without the attribute' do
286
+ new_view = default_view.tap { |v| v.delete('read_only') }
293
287
  vm = viewmodel_class.deserialize_from_view(new_view, deserialize_context: create_context)
294
288
  refute(default_model.equal?(vm.model))
295
289
  assert_nil(vm.model.read_only)
296
290
  assert_edited(vm, new: true)
297
291
  end
298
292
 
299
- it "rejects deserialize from new" do
293
+ it 'rejects deserialize from new' do
300
294
  ex = assert_raises(ViewModel::DeserializationError::ReadOnlyAttribute) do
301
295
  viewmodel_class.deserialize_from_view(default_view, deserialize_context: create_context)
302
296
  end
303
- assert_equal("read_only", ex.attribute)
297
+ assert_equal('read_only', ex.attribute)
304
298
  end
305
299
 
306
- it "rejects update if changed" do
307
- new_view = default_view.merge("read_only" => "written")
300
+ it 'rejects update if changed' do
301
+ new_view = default_view.merge('read_only' => 'written')
308
302
  ex = assert_raises(ViewModel::DeserializationError::ReadOnlyAttribute) do
309
303
  viewmodel_class.deserialize_from_view(new_view, deserialize_context: update_context)
310
304
  end
311
- assert_equal("read_only", ex.attribute)
305
+ assert_equal('read_only', ex.attribute)
312
306
  end
313
307
  end
314
308
 
315
- describe "with read-only write-once attribute" do
309
+ describe 'with read-only write-once attribute' do
316
310
  let(:attributes) { { write_once: { read_only: true, write_once: true } } }
317
311
  let(:model_body) do
318
- ->(x) do
312
+ ->(_x) do
319
313
  # For the purposes of testing, we assume a record is new and can be
320
314
  # written once to if write_once is nil. We will never write a nil.
321
315
  def new_record?
@@ -328,21 +322,21 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
328
322
  include CanDeserializeToNew
329
323
  include CanDeserializeToExisting
330
324
 
331
- it "rejects change to attribute" do
332
- new_view = default_view.merge("write_once" => "written")
325
+ it 'rejects change to attribute' do
326
+ new_view = default_view.merge('write_once' => 'written')
333
327
  ex = assert_raises(ViewModel::DeserializationError::ReadOnlyAttribute) do
334
328
  viewmodel_class.deserialize_from_view(new_view, deserialize_context: update_context)
335
329
  end
336
- assert_equal("write_once", ex.attribute)
330
+ assert_equal('write_once', ex.attribute)
337
331
  end
338
332
  end
339
333
 
340
- describe "with custom serialization" do
334
+ describe 'with custom serialization' do
341
335
  let(:attributes) { { overridden: {} } }
342
336
  let(:default_view_values) { { overridden: 10 } }
343
337
  let(:default_model_values) { { overridden: 5 } }
344
338
  let(:viewmodel_body) do
345
- ->(x) do
339
+ ->(_x) do
346
340
  def serialize_overridden(json, serialize_context:)
347
341
  json.overridden model.overridden.try { |o| o * 2 }
348
342
  end
@@ -359,134 +353,153 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
359
353
  include CanDeserializeToNew
360
354
  include CanDeserializeToExisting
361
355
 
362
- it "can be updated" do
363
- new_view = default_view.merge("overridden" => "20")
356
+ it 'can be updated' do
357
+ new_view = default_view.merge('overridden' => '20')
364
358
 
365
359
  vm = viewmodel_class.deserialize_from_view(new_view, deserialize_context: update_context)
366
360
 
367
- assert(default_model.equal?(vm.model), "returned model was not the same")
361
+ assert(default_model.equal?(vm.model), 'returned model was not the same')
368
362
  assert_equal(10, default_model.overridden)
369
363
 
370
364
  assert_edited(vm, changed_attributes: [:overridden])
371
365
  end
372
366
  end
373
367
 
374
- Nested = Struct.new(:member)
368
+ describe 'nesting' do
369
+ let(:nested_model_class) do
370
+ klass = Struct.new(:member)
371
+ Object.const_set(:Nested, klass)
372
+ klass
373
+ end
375
374
 
376
- class NestedView < TestViewModel
377
- self.view_name = "Nested"
378
- self.model_class = Nested
379
- attribute :member
380
- end
375
+ let(:nested_viewmodel_class) do
376
+ mc = nested_model_class
377
+ klass = Class.new(TestViewModel) do
378
+ self.view_name = 'Nested'
379
+ self.model_class = mc
380
+ attribute :member
381
+ end
382
+ Object.const_set(:NestedView, klass)
383
+ klass
384
+ end
381
385
 
382
- describe "with nested viewmodel" do
383
- let(:default_nested_model) { Nested.new("member") }
384
- let(:default_nested_view) { view_base.merge("_type" => "Nested", "member" => "member") }
386
+ def teardown
387
+ Object.send(:remove_const, :Nested)
388
+ Object.send(:remove_const, :NestedView)
389
+ ActiveSupport::Dependencies::Reference.clear!
390
+ super
391
+ end
385
392
 
386
- let(:attributes) {{ simple: {}, nested: { using: NestedView } }}
393
+ describe 'with nested viewmodel' do
394
+ let(:default_nested_model) { nested_model_class.new('member') }
395
+ let(:default_nested_view) { view_base.merge('_type' => 'Nested', 'member' => 'member') }
387
396
 
388
- let(:default_view_values) { { nested: default_nested_view } }
389
- let(:default_model_values) { { nested: default_nested_model } }
397
+ let(:attributes) { { simple: {}, nested: { using: nested_viewmodel_class } } }
390
398
 
391
- let(:update_context) { TestDeserializeContext.new(targets: [default_model, default_nested_model],
392
- access_control: access_control) }
399
+ let(:default_view_values) { { nested: default_nested_view } }
400
+ let(:default_model_values) { { nested: default_nested_model } }
393
401
 
394
- include CanSerialize
395
- include CanDeserializeToNew
396
- include CanDeserializeToExisting
402
+ let(:update_context) do
403
+ TestDeserializeContext.new(targets: [default_model, default_nested_model],
404
+ access_control: access_control)
405
+ end
397
406
 
398
- it "can update the nested value" do
399
- new_view = default_view.merge("nested" => default_nested_view.merge("member" => "changed"))
407
+ include CanSerialize
408
+ include CanDeserializeToNew
409
+ include CanDeserializeToExisting
400
410
 
401
- vm = viewmodel_class.deserialize_from_view(new_view, deserialize_context: update_context)
411
+ it 'can update the nested value' do
412
+ new_view = default_view.merge('nested' => default_nested_view.merge('member' => 'changed'))
402
413
 
403
- assert(default_model.equal?(vm.model), "returned model was not the same")
404
- assert(default_nested_model.equal?(vm.model.nested), "returned nested model was not the same")
414
+ vm = viewmodel_class.deserialize_from_view(new_view, deserialize_context: update_context)
405
415
 
406
- assert_equal("changed", default_model.nested.member)
416
+ assert(default_model.equal?(vm.model), 'returned model was not the same')
417
+ assert(default_nested_model.equal?(vm.model.nested), 'returned nested model was not the same')
407
418
 
408
- assert_unchanged(vm)
409
- assert_edited(vm.nested, changed_attributes: [:member])
410
- end
419
+ assert_equal('changed', default_model.nested.member)
411
420
 
412
- it "can replace the nested value" do
413
- # The value will be unified if it is different after deserialization
414
- new_view = default_view.merge("nested" => default_nested_view.merge("member" => "changed"))
421
+ assert_unchanged(vm)
422
+ assert_edited(vm.nested, changed_attributes: [:member])
423
+ end
424
+
425
+ it 'can replace the nested value' do
426
+ # The value will be unified if it is different after deserialization
427
+ new_view = default_view.merge('nested' => default_nested_view.merge('member' => 'changed'))
415
428
 
416
- partial_update_context = TestDeserializeContext.new(targets: [default_model],
417
- access_control: access_control)
429
+ partial_update_context = TestDeserializeContext.new(targets: [default_model],
430
+ access_control: access_control)
418
431
 
419
- vm = viewmodel_class.deserialize_from_view(new_view, deserialize_context: partial_update_context)
432
+ vm = viewmodel_class.deserialize_from_view(new_view, deserialize_context: partial_update_context)
420
433
 
421
- assert(default_model.equal?(vm.model), "returned model was not the same")
422
- refute(default_nested_model.equal?(vm.model.nested), "returned nested model was the same")
434
+ assert(default_model.equal?(vm.model), 'returned model was not the same')
435
+ refute(default_nested_model.equal?(vm.model.nested), 'returned nested model was the same')
423
436
 
424
- assert_edited(vm, new: false, changed_attributes: [:nested])
425
- assert_edited(vm.nested, new: true, changed_attributes: [:member])
437
+ assert_edited(vm, new: false, changed_attributes: [:nested])
438
+ assert_edited(vm.nested, new: true, changed_attributes: [:member])
439
+ end
426
440
  end
427
- end
428
441
 
429
- describe "with array of nested viewmodel" do
430
- let(:default_nested_model_1) { Nested.new("member1") }
431
- let(:default_nested_view_1) { view_base.merge("_type" => "Nested", "member" => "member1") }
442
+ describe 'with array of nested viewmodel' do
443
+ let(:default_nested_model_1) { nested_model_class.new('member1') }
444
+ let(:default_nested_view_1) { view_base.merge('_type' => 'Nested', 'member' => 'member1') }
432
445
 
433
- let(:default_nested_model_2) { Nested.new("member2") }
434
- let(:default_nested_view_2) { view_base.merge("_type" => "Nested", "member" => "member2") }
446
+ let(:default_nested_model_2) { nested_model_class.new('member2') }
447
+ let(:default_nested_view_2) { view_base.merge('_type' => 'Nested', 'member' => 'member2') }
435
448
 
436
- let(:attributes) {{ simple: {}, nested: { using: NestedView, array: true } }}
449
+ let(:attributes) { { simple: {}, nested: { using: nested_viewmodel_class, array: true } } }
437
450
 
438
- let(:default_view_values) { { nested: [default_nested_view_1, default_nested_view_2] } }
439
- let(:default_model_values) { { nested: [default_nested_model_1, default_nested_model_2] } }
451
+ let(:default_view_values) { { nested: [default_nested_view_1, default_nested_view_2] } }
452
+ let(:default_model_values) { { nested: [default_nested_model_1, default_nested_model_2] } }
440
453
 
441
- let(:update_context) {
442
- TestDeserializeContext.new(targets: [default_model, default_nested_model_1, default_nested_model_2],
443
- access_control: access_control)
444
- }
454
+ let(:update_context) {
455
+ TestDeserializeContext.new(targets: [default_model, default_nested_model_1, default_nested_model_2],
456
+ access_control: access_control)
457
+ }
445
458
 
446
- include CanSerialize
447
- include CanDeserializeToNew
448
- include CanDeserializeToExisting
459
+ include CanSerialize
460
+ include CanDeserializeToNew
461
+ include CanDeserializeToExisting
449
462
 
450
- it "rejects change to attribute" do
451
- new_view = default_view.merge("nested" => "terrible")
452
- ex = assert_raises(ViewModel::DeserializationError::InvalidAttributeType) do
453
- viewmodel_class.deserialize_from_view(new_view, deserialize_context: update_context)
463
+ it 'rejects change to attribute' do
464
+ new_view = default_view.merge('nested' => 'terrible')
465
+ ex = assert_raises(ViewModel::DeserializationError::InvalidAttributeType) do
466
+ viewmodel_class.deserialize_from_view(new_view, deserialize_context: update_context)
467
+ end
468
+ assert_equal('nested', ex.attribute)
469
+ assert_equal('Array', ex.expected_type)
470
+ assert_equal('String', ex.provided_type)
454
471
  end
455
- assert_equal("nested", ex.attribute)
456
- assert_equal("Array", ex.expected_type)
457
- assert_equal("String", ex.provided_type)
458
- end
459
472
 
460
- it "can edit a nested value" do
461
- default_view["nested"][0]["member"] = "changed"
462
- vm = viewmodel_class.deserialize_from_view(default_view, deserialize_context: update_context)
463
- assert(default_model.equal?(vm.model), "returned model was not the same")
464
- assert_equal(2, vm.model.nested.size)
465
- assert(default_nested_model_1.equal?(vm.model.nested[0]))
466
- assert(default_nested_model_2.equal?(vm.model.nested[1]))
473
+ it 'can edit a nested value' do
474
+ default_view['nested'][0]['member'] = 'changed'
475
+ vm = viewmodel_class.deserialize_from_view(default_view, deserialize_context: update_context)
476
+ assert(default_model.equal?(vm.model), 'returned model was not the same')
477
+ assert_equal(2, vm.model.nested.size)
478
+ assert(default_nested_model_1.equal?(vm.model.nested[0]))
479
+ assert(default_nested_model_2.equal?(vm.model.nested[1]))
467
480
 
468
- assert_unchanged(vm)
469
- assert_edited(vm.nested[0], changed_attributes: [:member])
470
- end
481
+ assert_unchanged(vm)
482
+ assert_edited(vm.nested[0], changed_attributes: [:member])
483
+ end
471
484
 
472
- it "can append a nested value" do
473
- default_view["nested"] << view_base.merge("_type" => "Nested", "member" => "member3")
485
+ it 'can append a nested value' do
486
+ default_view['nested'] << view_base.merge('_type' => 'Nested', 'member' => 'member3')
474
487
 
475
- vm = viewmodel_class.deserialize_from_view(default_view, deserialize_context: update_context)
488
+ vm = viewmodel_class.deserialize_from_view(default_view, deserialize_context: update_context)
476
489
 
477
- assert(default_model.equal?(vm.model), "returned model was not the same")
478
- assert_equal(3, vm.model.nested.size)
479
- assert(default_nested_model_1.equal?(vm.model.nested[0]))
480
- assert(default_nested_model_2.equal?(vm.model.nested[1]))
490
+ assert(default_model.equal?(vm.model), 'returned model was not the same')
491
+ assert_equal(3, vm.model.nested.size)
492
+ assert(default_nested_model_1.equal?(vm.model.nested[0]))
493
+ assert(default_nested_model_2.equal?(vm.model.nested[1]))
481
494
 
482
- vm.model.nested.each_with_index do |nvm, i|
483
- assert_equal("member#{i+1}", nvm.member)
484
- end
495
+ vm.model.nested.each_with_index do |nvm, i|
496
+ assert_equal("member#{i + 1}", nvm.member)
497
+ end
485
498
 
486
- assert_edited(vm, changed_attributes: [:nested])
487
- assert_edited(vm.nested[2], new: true, changed_attributes: [:member])
499
+ assert_edited(vm, changed_attributes: [:nested])
500
+ assert_edited(vm.nested[2], new: true, changed_attributes: [:member])
501
+ end
488
502
  end
489
503
  end
490
504
  end
491
-
492
505
  end