iknow_view_models 3.2.0 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) 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 +12 -11
  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_base.rb +4 -1
  23. data/lib/view_model/active_record/nested_controller_base.rb +1 -0
  24. data/lib/view_model/active_record/update_context.rb +8 -6
  25. data/lib/view_model/active_record/update_data.rb +32 -30
  26. data/lib/view_model/active_record/update_operation.rb +17 -13
  27. data/lib/view_model/active_record/visitor.rb +0 -1
  28. data/lib/view_model/after_transaction_runner.rb +0 -1
  29. data/lib/view_model/callbacks.rb +3 -1
  30. data/lib/view_model/controller.rb +13 -3
  31. data/lib/view_model/deserialization_error.rb +15 -12
  32. data/lib/view_model/error.rb +12 -10
  33. data/lib/view_model/error_view.rb +3 -1
  34. data/lib/view_model/migration/no_path_error.rb +1 -0
  35. data/lib/view_model/migration/one_way_error.rb +1 -0
  36. data/lib/view_model/migration/unspecified_version_error.rb +1 -0
  37. data/lib/view_model/record.rb +11 -13
  38. data/lib/view_model/reference.rb +3 -1
  39. data/lib/view_model/references.rb +8 -5
  40. data/lib/view_model/registry.rb +1 -1
  41. data/lib/view_model/schemas.rb +9 -4
  42. data/lib/view_model/serialization_error.rb +4 -1
  43. data/lib/view_model/serialize_context.rb +4 -4
  44. data/lib/view_model/test_helpers.rb +8 -3
  45. data/lib/view_model/test_helpers/arvm_builder.rb +19 -14
  46. data/lib/view_model/traversal_context.rb +2 -1
  47. data/test/.rubocop.yml +14 -0
  48. data/test/helpers/arvm_test_models.rb +12 -9
  49. data/test/helpers/arvm_test_utilities.rb +5 -3
  50. data/test/helpers/controller_test_helpers.rb +31 -29
  51. data/test/helpers/match_enumerator.rb +1 -0
  52. data/test/helpers/query_logging.rb +2 -1
  53. data/test/helpers/test_access_control.rb +5 -3
  54. data/test/helpers/viewmodel_spec_helpers.rb +21 -20
  55. data/test/unit/view_model/access_control_test.rb +144 -144
  56. data/test/unit/view_model/active_record/alias_test.rb +15 -13
  57. data/test/unit/view_model/active_record/belongs_to_test.rb +40 -39
  58. data/test/unit/view_model/active_record/cache_test.rb +27 -26
  59. data/test/unit/view_model/active_record/cloner_test.rb +67 -63
  60. data/test/unit/view_model/active_record/controller_test.rb +37 -38
  61. data/test/unit/view_model/active_record/counter_test.rb +10 -9
  62. data/test/unit/view_model/active_record/customization_test.rb +59 -58
  63. data/test/unit/view_model/active_record/has_many_test.rb +112 -111
  64. data/test/unit/view_model/active_record/has_many_through_poly_test.rb +15 -14
  65. data/test/unit/view_model/active_record/has_many_through_test.rb +33 -38
  66. data/test/unit/view_model/active_record/has_one_test.rb +37 -36
  67. data/test/unit/view_model/active_record/migration_test.rb +13 -13
  68. data/test/unit/view_model/active_record/namespacing_test.rb +19 -17
  69. data/test/unit/view_model/active_record/poly_test.rb +44 -45
  70. data/test/unit/view_model/active_record/shared_test.rb +30 -28
  71. data/test/unit/view_model/active_record/version_test.rb +9 -7
  72. data/test/unit/view_model/active_record_test.rb +72 -72
  73. data/test/unit/view_model/callbacks_test.rb +19 -15
  74. data/test/unit/view_model/controller_test.rb +4 -2
  75. data/test/unit/view_model/record_test.rb +92 -97
  76. data/test/unit/view_model/traversal_context_test.rb +4 -5
  77. data/test/unit/view_model_test.rb +18 -16
  78. metadata +7 -5
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ViewModel::AccessControlError < ViewModel::AbstractErrorWithBlame
2
4
  attr_reader :detail
5
+
3
6
  status 403
4
- code "AccessControl.Forbidden"
7
+ code 'AccessControl.Forbidden'
5
8
 
6
9
  def initialize(detail, nodes = [])
7
10
  @detail = detail
@@ -1,23 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support"
4
- require "active_record"
3
+ require 'active_support'
4
+ require 'active_record'
5
5
 
6
- require "view_model"
7
- require "view_model/record"
6
+ require 'view_model'
7
+ require 'view_model/record'
8
8
 
9
- require "lazily"
10
- require "concurrent"
9
+ require 'lazily'
10
+ require 'concurrent'
11
11
 
12
12
  class ViewModel::ActiveRecord < ViewModel::Record
13
13
  # Defined before requiring components so components can refer to them at parse time
14
14
 
15
15
  # for functional updates
16
- FUNCTIONAL_UPDATE_TYPE = "_update"
17
- ACTIONS_ATTRIBUTE = "actions"
18
- VALUES_ATTRIBUTE = "values"
19
- BEFORE_ATTRIBUTE = "before"
20
- AFTER_ATTRIBUTE = "after"
16
+ FUNCTIONAL_UPDATE_TYPE = '_update'
17
+ ACTIONS_ATTRIBUTE = 'actions'
18
+ VALUES_ATTRIBUTE = 'values'
19
+ BEFORE_ATTRIBUTE = 'before'
20
+ AFTER_ATTRIBUTE = 'after'
21
21
 
22
22
  require 'view_model/utils/collections'
23
23
  require 'view_model/active_record/association_data'
@@ -232,6 +232,7 @@ class ViewModel::ActiveRecord < ViewModel::Record
232
232
  _members.each_value do |data|
233
233
  next unless data.is_a?(AssociationData)
234
234
  next unless include_referenced || !data.referenced?
235
+
235
236
  data.viewmodel_classes.each do |vm|
236
237
  vm.dependent_viewmodels(seen, include_referenced: include_referenced)
237
238
  end
@@ -42,7 +42,7 @@ class ViewModel::ActiveRecord::AssociationData
42
42
  @indirect_reflection = load_indirect_reflection(intermediate_model, @indirect_association_name)
43
43
  target_reflection = @indirect_reflection
44
44
  else
45
- target_reflection = @direct_reflection
45
+ target_reflection = @direct_reflection
46
46
  end
47
47
 
48
48
  @viewmodel_classes =
@@ -202,6 +202,7 @@ class ViewModel::ActiveRecord::AssociationData
202
202
 
203
203
  def direct_viewmodel
204
204
  raise ArgumentError.new('not a through association') unless through?
205
+
205
206
  lazy_initialize! unless @initialized
206
207
  @direct_viewmodel
207
208
  end
@@ -11,7 +11,8 @@ module ViewModel::ActiveRecord::AssociationManipulation
11
11
  association_scope = association.scope
12
12
 
13
13
  if association_data.through?
14
- raise ArgumentError.new("Polymorphic through relationships not supported yet") if association_data.polymorphic?
14
+ raise ArgumentError.new('Polymorphic through relationships not supported yet') if association_data.polymorphic?
15
+
15
16
  associated_viewmodel = association_data.viewmodel_class
16
17
  direct_viewmodel = association_data.direct_viewmodel
17
18
  else
@@ -44,6 +45,7 @@ module ViewModel::ActiveRecord::AssociationManipulation
44
45
  if vms.size > 1
45
46
  raise ViewModel::DeserializationError::Internal.new("Internal error: encountered multiple records for single association #{association_name}", self.blame_reference)
46
47
  end
48
+
47
49
  vms.first
48
50
  end
49
51
  end
@@ -112,7 +114,7 @@ module ViewModel::ActiveRecord::AssociationManipulation
112
114
  deserialize_context.run_callback(ViewModel::Callbacks::Hook::BeforeValidate, self)
113
115
 
114
116
  if association_data.through?
115
- raise ArgumentError.new("Polymorphic through relationships not supported yet") if association_data.polymorphic?
117
+ raise ArgumentError.new('Polymorphic through relationships not supported yet') if association_data.polymorphic?
116
118
 
117
119
  direct_viewmodel_class = association_data.direct_viewmodel
118
120
  root_update_data, referenced_update_data = construct_indirect_append_updates(association_data, subtree_hashes, references)
@@ -317,7 +319,7 @@ module ViewModel::ActiveRecord::AssociationManipulation
317
319
  # TODO: this won't handle polymorphic associations! In the case of polymorphism,
318
320
  # need to join on (type, id) pairs instead.
319
321
  if association_data.polymorphic?
320
- raise ArgumentError.new("Internal error: append_association is not yet supported for polymorphic indirect associations")
322
+ raise ArgumentError.new('Internal error: append_association is not yet supported for polymorphic indirect associations')
321
323
  end
322
324
 
323
325
  existing_indirect_associates = indirect_update_data.map { |upd| upd.id unless upd.new? }.compact
@@ -340,7 +342,7 @@ module ViewModel::ActiveRecord::AssociationManipulation
340
342
  ViewModel::ActiveRecord::UpdateData.new(
341
343
  direct_viewmodel_class,
342
344
  metadata,
343
- { indirect_reflection.name.to_s => { ViewModel::REFERENCE_ATTRIBUTE => ref_name }},
345
+ { indirect_reflection.name.to_s => { ViewModel::REFERENCE_ATTRIBUTE => ref_name } },
344
346
  [ref_name])
345
347
  end
346
348
 
@@ -243,14 +243,14 @@ class ViewModel::ActiveRecord::Cache
243
243
  if ids.present?
244
244
  found = viewmodel_class.find(ids,
245
245
  eager_include: false,
246
- lock: "FOR SHARE",
246
+ lock: 'FOR SHARE',
247
247
  serialize_context: serialize_context)
248
248
  viewmodels.concat(found)
249
249
  end
250
250
 
251
251
  ViewModel.preload_for_serialization(viewmodels,
252
252
  include_referenced: false,
253
- lock: "FOR SHARE",
253
+ lock: 'FOR SHARE',
254
254
  serialize_context: serialize_context)
255
255
 
256
256
  viewmodels
@@ -264,6 +264,7 @@ class ViewModel::ActiveRecord::Cache
264
264
  def add_refs_to_worklist(cacheable_references)
265
265
  cacheable_references.each do |ref_name, (type, id)|
266
266
  next if resolved_references.has_key?(ref_name)
267
+
267
268
  (@worklist[type] ||= {})[id] = WorklistEntry.new(ref_name, nil)
268
269
  end
269
270
  end
@@ -271,6 +272,7 @@ class ViewModel::ActiveRecord::Cache
271
272
  def add_viewmodels_to_worklist(referenced_viewmodels)
272
273
  referenced_viewmodels.each do |ref_name, viewmodel|
273
274
  next if resolved_references.has_key?(ref_name)
275
+
274
276
  (@worklist[viewmodel.class.view_name] ||= {})[viewmodel.id] = WorklistEntry.new(ref_name, viewmodel)
275
277
  end
276
278
  end
@@ -47,7 +47,7 @@ module ViewModel::ActiveRecord::CollectionNestedController
47
47
  after = parse_relative_position(:after)
48
48
 
49
49
  if before && after
50
- raise ViewModel::DeserializationError::InvalidSyntax.new("Can not provide both `before` and `after` anchors for a collection append")
50
+ raise ViewModel::DeserializationError::InvalidSyntax.new('Can not provide both `before` and `after` anchors for a collection append')
51
51
  end
52
52
 
53
53
  owner_view = owner_viewmodel.find(owner_viewmodel_id, eager_include: false, serialize_context: serialize_context)
@@ -55,8 +55,8 @@ module ViewModel::ActiveRecord::CollectionNestedController
55
55
  assoc_view = owner_view.append_associated(association_name,
56
56
  update_hash,
57
57
  references: refs,
58
- before: before,
59
- after: after,
58
+ before: before,
59
+ after: after,
60
60
  deserialize_context: deserialize_context)
61
61
  ViewModel::Callbacks.wrap_serialize(owner_view, context: serialize_context) do
62
62
  child_context = owner_view.context_for_child(association_name, context: serialize_context)
@@ -33,7 +33,7 @@ module ViewModel::ActiveRecord::ControllerBase
33
33
  if (match = /(.*)Controller$/.match(self.name))
34
34
  self.viewmodel_name = match[1].singularize
35
35
  else
36
- raise ArgumentError.new("Could not auto-determine ViewModel from Controller name '#{self.name}'") if match.nil?
36
+ raise ArgumentError.new("Could not auto-determine ViewModel from Controller name '#{self.name}'")
37
37
  end
38
38
  end
39
39
  @viewmodel_class
@@ -43,6 +43,7 @@ module ViewModel::ActiveRecord::ControllerBase
43
43
  unless instance_variable_defined?(:@access_control)
44
44
  raise ArgumentError.new("AccessControl instance not set for Controller '#{self.name}'")
45
45
  end
46
+
46
47
  @access_control
47
48
  end
48
49
 
@@ -64,6 +65,7 @@ module ViewModel::ActiveRecord::ControllerBase
64
65
  unless type < ViewModel
65
66
  raise ArgumentError.new("'#{type.inspect}' is not a valid ViewModel")
66
67
  end
68
+
67
69
  @viewmodel_class = type
68
70
  end
69
71
 
@@ -75,6 +77,7 @@ module ViewModel::ActiveRecord::ControllerBase
75
77
  unless access_control.is_a?(Class) && access_control < ViewModel::AccessControl
76
78
  raise ArgumentError.new("'#{access_control.inspect}' is not a valid AccessControl")
77
79
  end
80
+
78
81
  @access_control = access_control
79
82
  end
80
83
  end
@@ -88,6 +88,7 @@ module ViewModel::ActiveRecord::NestedControllerBase
88
88
 
89
89
  self.owner_viewmodel = owner
90
90
  raise ArgumentError.new("Could not find owner ViewModel class '#{owner_name}'") if owner_viewmodel.nil?
91
+
91
92
  self.association_name = as
92
93
  end
93
94
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Assembles an update operation tree from user input. Handles the interlinking
2
4
  # and model of update operations, but does not handle the actual user data nor
3
5
  # the mechanism by which it is applied to models.
@@ -66,7 +68,7 @@ class ViewModel::ActiveRecord
66
68
  .assemble_update_tree
67
69
  end
68
70
 
69
- # TODO an unfortunate abstraction violation. The `append` case constructs an
71
+ # TODO: an unfortunate abstraction violation. The `append` case constructs an
70
72
  # update tree and later injects the context of parent and position.
71
73
  def root_updates
72
74
  @root_update_operations
@@ -143,7 +145,7 @@ class ViewModel::ActiveRecord
143
145
  if ref.nil?
144
146
  @root_update_operations << update_op
145
147
  else
146
- # TODO make sure that referenced subtree hashes are unique and provide a decent error message
148
+ # TODO: make sure that referenced subtree hashes are unique and provide a decent error message
147
149
  # not strictly necessary, but will save confusion
148
150
  @referenced_update_operations[ref] = update_op
149
151
  end
@@ -200,9 +202,9 @@ class ViewModel::ActiveRecord
200
202
  deferred_update.build!(self)
201
203
  end
202
204
 
203
- dangling_references = @referenced_update_operations.reject { |ref, upd| upd.built? }.map { |ref, upd| upd.viewmodel.to_reference }
205
+ dangling_references = @referenced_update_operations.reject { |_ref, upd| upd.built? }.map { |_ref, upd| upd.viewmodel.to_reference }
204
206
  if dangling_references.present?
205
- raise ViewModel::DeserializationError::InvalidStructure.new("References not referred to from roots", dangling_references)
207
+ raise ViewModel::DeserializationError::InvalidStructure.new('References not referred to from roots', dangling_references)
206
208
  end
207
209
 
208
210
  self
@@ -265,8 +267,8 @@ class ViewModel::ActiveRecord
265
267
  # individual node that caused it, without attempting to parse Postgres'
266
268
  # human-readable error details.
267
269
  def check_deferred_constraints!(model_class)
268
- if model_class.connection.adapter_name == "PostgreSQL"
269
- model_class.connection.execute("SET CONSTRAINTS ALL IMMEDIATE")
270
+ if model_class.connection.adapter_name == 'PostgreSQL'
271
+ model_class.connection.execute('SET CONSTRAINTS ALL IMMEDIATE')
270
272
  end
271
273
  rescue ::ActiveRecord::StatementInvalid => ex
272
274
  raise ViewModel::DeserializationError::DatabaseConstraint.from_exception(ex)
@@ -24,6 +24,7 @@ class ViewModel::ActiveRecord
24
24
  NAME = 'append'
25
25
  attr_accessor :before, :after
26
26
  attr_reader :contents
27
+
27
28
  def initialize(contents)
28
29
  @contents = contents
29
30
  end
@@ -32,6 +33,7 @@ class ViewModel::ActiveRecord
32
33
  class Update
33
34
  NAME = 'update'
34
35
  attr_reader :contents
36
+
35
37
  def initialize(contents)
36
38
  @contents = contents
37
39
  end
@@ -56,6 +58,7 @@ class ViewModel::ActiveRecord
56
58
  # associations or reference strings for root.
57
59
  class Replace
58
60
  attr_reader :contents
61
+
59
62
  def initialize(contents)
60
63
  @contents = contents
61
64
  end
@@ -66,6 +69,7 @@ class ViewModel::ActiveRecord
66
69
  # associations.
67
70
  class Functional
68
71
  attr_reader :actions
72
+
69
73
  def initialize(actions)
70
74
  @actions = actions
71
75
  end
@@ -215,23 +219,23 @@ class ViewModel::ActiveRecord
215
219
  # The contents of the actions are determined by the subclasses
216
220
 
217
221
  def functional_update_schema # abstract
218
- raise "abstract"
222
+ raise 'abstract'
219
223
  end
220
224
 
221
225
  def append_action_schema # abstract
222
- raise "abstract"
226
+ raise 'abstract'
223
227
  end
224
228
 
225
229
  def remove_action_schema # abstract
226
- raise "abstract"
230
+ raise 'abstract'
227
231
  end
228
232
 
229
233
  def update_action_schema # abstract
230
- raise "abstract"
234
+ raise 'abstract'
231
235
  end
232
236
 
233
- def parse_contents(values) # abstract
234
- raise "abstract"
237
+ def parse_contents(_values) # abstract
238
+ raise 'abstract'
235
239
  end
236
240
 
237
241
  # Remove values are always anchors
@@ -255,11 +259,11 @@ class ViewModel::ActiveRecord
255
259
  # behaviour, so we parameterise the result type as well.
256
260
 
257
261
  def replace_update_type # abstract
258
- raise "abstract"
262
+ raise 'abstract'
259
263
  end
260
264
 
261
265
  def functional_update_type # abstract
262
- raise "abstract"
266
+ raise 'abstract'
263
267
  end
264
268
  end
265
269
  end
@@ -390,6 +394,7 @@ class ViewModel::ActiveRecord
390
394
 
391
395
  class UpdateData
392
396
  attr_accessor :viewmodel_class, :metadata, :attributes, :associations, :referenced_associations
397
+
393
398
  delegate :id, :view_name, :schema_version, to: :metadata
394
399
 
395
400
  module Schemas
@@ -400,7 +405,7 @@ class ViewModel::ActiveRecord
400
405
  'properties' => { ViewModel::TYPE_ATTRIBUTE => { 'type' => 'string' },
401
406
  ViewModel::ID_ATTRIBUTE => ViewModel::Schemas::ID_SCHEMA },
402
407
  'additionalProperties' => false,
403
- 'required' => [ViewModel::TYPE_ATTRIBUTE, ViewModel::ID_ATTRIBUTE]
408
+ 'required' => [ViewModel::TYPE_ATTRIBUTE, ViewModel::ID_ATTRIBUTE],
404
409
  }
405
410
 
406
411
  VIEWMODEL_REFERENCE_ONLY = JsonSchema.parse!(viewmodel_reference_only)
@@ -409,14 +414,14 @@ class ViewModel::ActiveRecord
409
414
  {
410
415
  'description' => 'functional update',
411
416
  'type' => 'object',
417
+ 'required' => [ViewModel::TYPE_ATTRIBUTE, VALUES_ATTRIBUTE],
412
418
  'properties' => {
413
419
  ViewModel::TYPE_ATTRIBUTE => { 'enum' => [FunctionalUpdate::Append::NAME,
414
420
  FunctionalUpdate::Update::NAME,
415
- FunctionalUpdate::Remove::NAME] },
421
+ FunctionalUpdate::Remove::NAME,] },
416
422
  VALUES_ATTRIBUTE => { 'type' => 'array',
417
- 'items' => value_schema }
423
+ 'items' => value_schema },
418
424
  },
419
- 'required' => [ViewModel::TYPE_ATTRIBUTE, VALUES_ATTRIBUTE]
420
425
  }
421
426
  end
422
427
 
@@ -426,7 +431,7 @@ class ViewModel::ActiveRecord
426
431
  'properties' => {
427
432
  ViewModel::TYPE_ATTRIBUTE => { 'enum' => [FunctionalUpdate::Append::NAME] },
428
433
  BEFORE_ATTRIBUTE => viewmodel_reference_only,
429
- AFTER_ATTRIBUTE => viewmodel_reference_only
434
+ AFTER_ATTRIBUTE => viewmodel_reference_only,
430
435
  },
431
436
  }
432
437
 
@@ -435,7 +440,7 @@ class ViewModel::ActiveRecord
435
440
 
436
441
  fupdate_shared =
437
442
  fupdate_base.({ 'oneOf' => [ViewModel::Schemas::VIEWMODEL_REFERENCE_SCHEMA,
438
- viewmodel_reference_only] })
443
+ viewmodel_reference_only,] })
439
444
 
440
445
  # Referenced updates are special:
441
446
  # - Append requires `_ref` hashes
@@ -450,7 +455,7 @@ class ViewModel::ActiveRecord
450
455
  'description' => 'collection update',
451
456
  'additionalProperties' => false,
452
457
  'properties' => {
453
- ViewModel::TYPE_ATTRIBUTE => { 'enum' => [FunctionalUpdate::Update::NAME] }
458
+ ViewModel::TYPE_ATTRIBUTE => { 'enum' => [FunctionalUpdate::Update::NAME] },
454
459
  },
455
460
  }
456
461
 
@@ -471,7 +476,6 @@ class ViewModel::ActiveRecord
471
476
  REMOVE_ACTION = JsonSchema.parse!(fupdate_owned.deep_merge(remove_mixin))
472
477
  REFERENCED_REMOVE_ACTION = JsonSchema.parse!(fupdate_shared.deep_merge(remove_mixin))
473
478
 
474
-
475
479
  collection_update = ->(base_schema) do
476
480
  {
477
481
  'type' => 'object',
@@ -480,7 +484,7 @@ class ViewModel::ActiveRecord
480
484
  'required' => [ViewModel::TYPE_ATTRIBUTE, ACTIONS_ATTRIBUTE],
481
485
  'properties' => {
482
486
  ViewModel::TYPE_ATTRIBUTE => { 'enum' => [FUNCTIONAL_UPDATE_TYPE] },
483
- ACTIONS_ATTRIBUTE => { 'type' => 'array', 'items' => base_schema }
487
+ ACTIONS_ATTRIBUTE => { 'type' => 'array', 'items' => base_schema },
484
488
  # The ACTIONS_ATTRIBUTE could be accurately expressed as
485
489
  #
486
490
  # { 'oneOf' => [append, update, remove] }
@@ -488,7 +492,7 @@ class ViewModel::ActiveRecord
488
492
  # but this produces completely unusable error messages. Instead we
489
493
  # specify it must be an array, and defer checking to the code that
490
494
  # can determine the schema by inspecting the type field.
491
- }
495
+ },
492
496
  }
493
497
  end
494
498
 
@@ -503,7 +507,7 @@ class ViewModel::ActiveRecord
503
507
  when :_type
504
508
  viewmodel_class.view_name
505
509
  else
506
- attributes.fetch(name) { associations.fetch(name) { referenced_associations.fetch(name) }}
510
+ attributes.fetch(name) { associations.fetch(name) { referenced_associations.fetch(name) } }
507
511
  end
508
512
  end
509
513
 
@@ -538,7 +542,7 @@ class ViewModel::ActiveRecord
538
542
  end
539
543
 
540
544
  # Ensure that no root is referred to more than once
541
- check_duplicate_updates(root_updates, type: "root")
545
+ check_duplicate_updates(root_updates, type: 'root')
542
546
 
543
547
  # Construct reference UpdateData
544
548
  referenced_updates = referenced_subtree_hashes.transform_values do |subtree_hash|
@@ -549,7 +553,7 @@ class ViewModel::ActiveRecord
549
553
  UpdateData.new(viewmodel_class, metadata, subtree_hash, valid_reference_keys)
550
554
  end
551
555
 
552
- check_duplicate_updates(referenced_updates.values, type: "reference")
556
+ check_duplicate_updates(referenced_updates.values, type: 'reference')
553
557
 
554
558
  return root_updates, referenced_updates
555
559
  end
@@ -614,7 +618,7 @@ class ViewModel::ActiveRecord
614
618
  def preload_dependencies
615
619
  deps = {}
616
620
 
617
- (associations.merge(referenced_associations)).each do |assoc_name, reference|
621
+ associations.merge(referenced_associations).each do |assoc_name, reference|
618
622
  association_data = self.viewmodel_class._association_data(assoc_name)
619
623
 
620
624
  preload_specs = build_preload_specs(association_data,
@@ -665,7 +669,7 @@ class ViewModel::ActiveRecord
665
669
 
666
670
  # handle view pre-parsing if defined
667
671
  self.viewmodel_class.pre_parse(viewmodel_reference, metadata, hash_data) if self.viewmodel_class.respond_to?(:pre_parse)
668
- hash_data.keys.each do |key|
672
+ hash_data.keys.each do |key| # rubocop:disable Style/HashEachMethods
669
673
  if self.viewmodel_class.respond_to?(:"pre_parse_#{key}")
670
674
  self.viewmodel_class.public_send("pre_parse_#{key}", viewmodel_reference, metadata, hash_data, hash_data.delete(key))
671
675
  end
@@ -710,19 +714,18 @@ class ViewModel::ActiveRecord
710
714
  referenced_associations[name] = ref
711
715
  end
712
716
  else
713
- if association_data.collection?
714
- associations[name] =
717
+ associations[name] =
718
+ if association_data.collection?
715
719
  OwnedCollectionUpdate::Parser
716
720
  .new(association_data, blame_reference, valid_reference_keys)
717
721
  .parse(value)
718
- else # not a collection
719
- associations[name] =
720
- if value.nil?
722
+ else # not a collection
723
+ if value.nil? # rubocop:disable Style/IfInsideElse
721
724
  nil
722
725
  else
723
726
  self.class.parse_associated(association_data, blame_reference, valid_reference_keys, value)
724
727
  end
725
- end
728
+ end
726
729
  end
727
730
  else
728
731
  raise ViewModel::DeserializationError::UnknownAttribute.new(name, blame_reference)
@@ -730,7 +733,6 @@ class ViewModel::ActiveRecord
730
733
  end
731
734
  end
732
735
 
733
-
734
736
  def blame_reference
735
737
  ViewModel::Reference.new(self.viewmodel_class, self.id)
736
738
  end