iknow_view_models 3.1.8 → 3.2.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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +6 -6
- data/.rubocop.yml +18 -0
- data/Appraisals +6 -6
- data/Gemfile +6 -2
- data/Rakefile +5 -5
- data/gemfiles/rails_5_2.gemfile +5 -5
- data/gemfiles/rails_6_0.gemfile +9 -0
- data/iknow_view_models.gemspec +40 -38
- data/lib/iknow_view_models.rb +9 -7
- data/lib/iknow_view_models/version.rb +1 -1
- data/lib/view_model.rb +31 -17
- data/lib/view_model/access_control.rb +5 -2
- data/lib/view_model/access_control/composed.rb +10 -9
- data/lib/view_model/access_control/open.rb +2 -0
- data/lib/view_model/access_control/read_only.rb +2 -0
- data/lib/view_model/access_control/tree.rb +11 -6
- data/lib/view_model/access_control_error.rb +4 -1
- data/lib/view_model/active_record.rb +17 -15
- data/lib/view_model/active_record/association_data.rb +2 -1
- data/lib/view_model/active_record/association_manipulation.rb +6 -4
- data/lib/view_model/active_record/cache.rb +114 -34
- data/lib/view_model/active_record/cache/cacheable_view.rb +2 -2
- data/lib/view_model/active_record/collection_nested_controller.rb +3 -3
- data/lib/view_model/active_record/controller.rb +68 -1
- data/lib/view_model/active_record/controller_base.rb +4 -1
- data/lib/view_model/active_record/nested_controller_base.rb +1 -0
- data/lib/view_model/active_record/update_context.rb +8 -6
- data/lib/view_model/active_record/update_data.rb +32 -30
- data/lib/view_model/active_record/update_operation.rb +17 -13
- data/lib/view_model/active_record/visitor.rb +0 -1
- data/lib/view_model/after_transaction_runner.rb +0 -1
- data/lib/view_model/callbacks.rb +3 -1
- data/lib/view_model/controller.rb +13 -3
- data/lib/view_model/deserialization_error.rb +15 -12
- data/lib/view_model/error.rb +12 -10
- data/lib/view_model/error_view.rb +3 -1
- data/lib/view_model/migratable_view.rb +78 -0
- data/lib/view_model/migration.rb +48 -0
- data/lib/view_model/migration/no_path_error.rb +26 -0
- data/lib/view_model/migration/one_way_error.rb +24 -0
- data/lib/view_model/migration/unspecified_version_error.rb +24 -0
- data/lib/view_model/migrator.rb +108 -0
- data/lib/view_model/record.rb +15 -14
- data/lib/view_model/reference.rb +3 -1
- data/lib/view_model/references.rb +8 -5
- data/lib/view_model/registry.rb +14 -2
- data/lib/view_model/schemas.rb +9 -4
- data/lib/view_model/serialization_error.rb +4 -1
- data/lib/view_model/serialize_context.rb +4 -4
- data/lib/view_model/test_helpers.rb +8 -3
- data/lib/view_model/test_helpers/arvm_builder.rb +21 -15
- data/lib/view_model/traversal_context.rb +2 -1
- data/nix/dependencies.nix +5 -0
- data/nix/gem/generate.rb +2 -1
- data/shell.nix +8 -3
- data/test/.rubocop.yml +14 -0
- data/test/helpers/arvm_test_models.rb +12 -9
- data/test/helpers/arvm_test_utilities.rb +5 -3
- data/test/helpers/controller_test_helpers.rb +55 -32
- data/test/helpers/match_enumerator.rb +1 -0
- data/test/helpers/query_logging.rb +2 -1
- data/test/helpers/test_access_control.rb +5 -3
- data/test/helpers/viewmodel_spec_helpers.rb +88 -22
- data/test/unit/view_model/access_control_test.rb +144 -144
- data/test/unit/view_model/active_record/alias_test.rb +15 -13
- data/test/unit/view_model/active_record/belongs_to_test.rb +40 -39
- data/test/unit/view_model/active_record/cache_test.rb +68 -31
- data/test/unit/view_model/active_record/cloner_test.rb +67 -63
- data/test/unit/view_model/active_record/controller_test.rb +113 -65
- data/test/unit/view_model/active_record/counter_test.rb +10 -9
- data/test/unit/view_model/active_record/customization_test.rb +59 -58
- data/test/unit/view_model/active_record/has_many_test.rb +112 -111
- data/test/unit/view_model/active_record/has_many_through_poly_test.rb +15 -14
- data/test/unit/view_model/active_record/has_many_through_test.rb +33 -38
- data/test/unit/view_model/active_record/has_one_test.rb +37 -36
- data/test/unit/view_model/active_record/migration_test.rb +161 -0
- data/test/unit/view_model/active_record/namespacing_test.rb +19 -17
- data/test/unit/view_model/active_record/poly_test.rb +44 -45
- data/test/unit/view_model/active_record/shared_test.rb +30 -28
- data/test/unit/view_model/active_record/version_test.rb +9 -7
- data/test/unit/view_model/active_record_test.rb +72 -72
- data/test/unit/view_model/callbacks_test.rb +19 -15
- data/test/unit/view_model/controller_test.rb +4 -2
- data/test/unit/view_model/record_test.rb +158 -145
- data/test/unit/view_model/registry_test.rb +38 -0
- data/test/unit/view_model/traversal_context_test.rb +4 -5
- data/test/unit/view_model_test.rb +18 -16
- metadata +38 -12
- data/.travis.yml +0 -31
- data/appveyor.yml +0 -22
- data/gemfiles/rails_6_0_beta.gemfile +0 -9
| @@ -1,9 +1,11 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            require_relative "../../../helpers/arvm_test_models.rb"
         | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 3 2 |  | 
| 4 | 
            -
             | 
| 3 | 
            +
            require_relative '../../../helpers/arvm_test_utilities'
         | 
| 4 | 
            +
            require_relative '../../../helpers/arvm_test_models'
         | 
| 5 5 |  | 
| 6 | 
            -
            require  | 
| 6 | 
            +
            require 'minitest/autorun'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            require 'view_model/active_record'
         | 
| 7 9 |  | 
| 8 10 | 
             
            class ViewModel::ActiveRecord::VersionTest < ActiveSupport::TestCase
         | 
| 9 11 | 
             
              include ARVMTestUtilities
         | 
| @@ -69,14 +71,14 @@ class ViewModel::ActiveRecord::VersionTest < ActiveSupport::TestCase | |
| 69 71 | 
             
                                 'id'       => @parent_with_a.child.id,
         | 
| 70 72 | 
             
                                 '_version' => 10,
         | 
| 71 73 | 
             
                               },
         | 
| 72 | 
            -
                               'target' | 
| 74 | 
            +
                               'target' => { '_ref' => target_ref } },
         | 
| 73 75 | 
             
                             data)
         | 
| 74 76 |  | 
| 75 77 | 
             
                assert_equal({ target_ref =>
         | 
| 76 78 | 
             
                                 {
         | 
| 77 79 | 
             
                                   '_type'    => 'Target',
         | 
| 78 80 | 
             
                                   'id'       => @parent_with_a.target.id,
         | 
| 79 | 
            -
                                   '_version' => 20
         | 
| 81 | 
            +
                                   '_version' => 20,
         | 
| 80 82 | 
             
                                 } },
         | 
| 81 83 | 
             
                             refs)
         | 
| 82 84 | 
             
              end
         | 
| @@ -86,7 +88,7 @@ class ViewModel::ActiveRecord::VersionTest < ActiveSupport::TestCase | |
| 86 88 | 
             
                  ParentView.deserialize_from_view(
         | 
| 87 89 | 
             
                    { '_type'    => 'Parent',
         | 
| 88 90 | 
             
                      '_new'     => true,
         | 
| 89 | 
            -
                      '_version' => 99 } | 
| 91 | 
            +
                      '_version' => 99 })
         | 
| 90 92 | 
             
                end
         | 
| 91 93 | 
             
                assert_match(/schema version/, ex.message)
         | 
| 92 94 | 
             
              end
         | 
| @@ -1,12 +1,12 @@ | |
| 1 | 
            -
            #  | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require_relative  | 
| 4 | 
            -
            require_relative  | 
| 3 | 
            +
            require_relative '../../helpers/arvm_test_utilities'
         | 
| 4 | 
            +
            require_relative '../../helpers/arvm_test_models'
         | 
| 5 5 |  | 
| 6 | 
            -
            require  | 
| 6 | 
            +
            require 'minitest/autorun'
         | 
| 7 7 | 
             
            require 'minitest/unit'
         | 
| 8 8 |  | 
| 9 | 
            -
            require  | 
| 9 | 
            +
            require 'view_model/active_record'
         | 
| 10 10 |  | 
| 11 11 | 
             
            class ViewModel::ActiveRecordTest < ActiveSupport::TestCase
         | 
| 12 12 | 
             
              include ARVMTestUtilities
         | 
| @@ -43,8 +43,8 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 43 43 | 
             
              end
         | 
| 44 44 |  | 
| 45 45 | 
             
              def setup
         | 
| 46 | 
            -
                @parent1 = Parent.create(name:  | 
| 47 | 
            -
                @parent2 = Parent.create(name:  | 
| 46 | 
            +
                @parent1 = Parent.create(name: 'p1')
         | 
| 47 | 
            +
                @parent2 = Parent.create(name: 'p2')
         | 
| 48 48 |  | 
| 49 49 | 
             
                super
         | 
| 50 50 | 
             
              end
         | 
| @@ -80,8 +80,8 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 80 80 |  | 
| 81 81 | 
             
              def test_create_from_view
         | 
| 82 82 | 
             
                view = {
         | 
| 83 | 
            -
                   | 
| 84 | 
            -
                   | 
| 83 | 
            +
                  '_type'    => 'Parent',
         | 
| 84 | 
            +
                  'name'     => 'p',
         | 
| 85 85 | 
             
                }
         | 
| 86 86 |  | 
| 87 87 | 
             
                pv = ParentView.deserialize_from_view(view)
         | 
| @@ -90,7 +90,7 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 90 90 | 
             
                assert(!p.changed?)
         | 
| 91 91 | 
             
                assert(!p.new_record?)
         | 
| 92 92 |  | 
| 93 | 
            -
                assert_equal( | 
| 93 | 
            +
                assert_equal('p', p.name)
         | 
| 94 94 | 
             
              end
         | 
| 95 95 |  | 
| 96 96 | 
             
              def test_create_from_empty_view
         | 
| @@ -101,10 +101,10 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 101 101 |  | 
| 102 102 | 
             
              def test_create_from_view_with_explicit_id
         | 
| 103 103 | 
             
                view = {
         | 
| 104 | 
            -
                   | 
| 105 | 
            -
                   | 
| 106 | 
            -
                   | 
| 107 | 
            -
                   | 
| 104 | 
            +
                  '_type' => 'Parent',
         | 
| 105 | 
            +
                  'id'    => 9999,
         | 
| 106 | 
            +
                  'name'  => 'p',
         | 
| 107 | 
            +
                  '_new'  => true,
         | 
| 108 108 | 
             
                }
         | 
| 109 109 | 
             
                pv = ParentView.deserialize_from_view(view)
         | 
| 110 110 | 
             
                p = pv.model
         | 
| @@ -116,9 +116,9 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 116 116 |  | 
| 117 117 | 
             
              def test_create_explicit_id_raises_with_id
         | 
| 118 118 | 
             
                view = {
         | 
| 119 | 
            -
                   | 
| 120 | 
            -
                   | 
| 121 | 
            -
                   | 
| 119 | 
            +
                  '_type' => 'Parent',
         | 
| 120 | 
            +
                  'id'    => 9999,
         | 
| 121 | 
            +
                  '_new'  => true,
         | 
| 122 122 | 
             
                }
         | 
| 123 123 | 
             
                ex = assert_raises(ViewModel::DeserializationError::DatabaseConstraint) do
         | 
| 124 124 | 
             
                  ParentView.deserialize_from_view(view)
         | 
| @@ -129,15 +129,15 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 129 129 |  | 
| 130 130 | 
             
              def test_read_only_raises_with_id
         | 
| 131 131 | 
             
                view = {
         | 
| 132 | 
            -
                   | 
| 133 | 
            -
                   | 
| 134 | 
            -
                   | 
| 135 | 
            -
                   | 
| 132 | 
            +
                  '_type' => 'Parent',
         | 
| 133 | 
            +
                  'one'   => 2,
         | 
| 134 | 
            +
                  'id'    => 9999,
         | 
| 135 | 
            +
                  '_new'  => true,
         | 
| 136 136 | 
             
                }
         | 
| 137 137 | 
             
                ex = assert_raises(ViewModel::DeserializationError::ReadOnlyAttribute) do
         | 
| 138 138 | 
             
                  ParentView.deserialize_from_view(view)
         | 
| 139 139 | 
             
                end
         | 
| 140 | 
            -
                assert_match( | 
| 140 | 
            +
                assert_match('one', ex.attribute)
         | 
| 141 141 | 
             
                assert_equal([ViewModel::Reference.new(ParentView, 9999)], ex.nodes)
         | 
| 142 142 | 
             
              end
         | 
| 143 143 |  | 
| @@ -151,7 +151,7 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 151 151 |  | 
| 152 152 | 
             
                assert_raises(ViewModel::AccessControlError) do
         | 
| 153 153 | 
             
                  no_view_context = ViewModelBase.new_deserialize_context(can_view: false)
         | 
| 154 | 
            -
                  ParentView.deserialize_from_view({'_type' => 'Parent', 'name' => 'p'},
         | 
| 154 | 
            +
                  ParentView.deserialize_from_view({ '_type' => 'Parent', 'name' => 'p' },
         | 
| 155 155 | 
             
                                                   deserialize_context: no_view_context)
         | 
| 156 156 | 
             
                end
         | 
| 157 157 | 
             
              end
         | 
| @@ -167,7 +167,7 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 167 167 |  | 
| 168 168 | 
             
              def test_editability_checks_create_on_empty_record
         | 
| 169 169 | 
             
                context = ViewModelBase.new_deserialize_context
         | 
| 170 | 
            -
                view = TrivialView.deserialize_from_view({'_type' => 'Trivial' },
         | 
| 170 | 
            +
                view = TrivialView.deserialize_from_view({ '_type' => 'Trivial' },
         | 
| 171 171 | 
             
                                                         deserialize_context: context)
         | 
| 172 172 |  | 
| 173 173 | 
             
                ref = view.to_reference
         | 
| @@ -185,13 +185,13 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 185 185 |  | 
| 186 186 | 
             
                ex = assert_raises(ViewModel::AccessControlError) do
         | 
| 187 187 | 
             
                  # create
         | 
| 188 | 
            -
                  ParentView.deserialize_from_view({  | 
| 188 | 
            +
                  ParentView.deserialize_from_view({ '_type' => 'Parent', 'name' => 'p' }, deserialize_context: no_edit_context)
         | 
| 189 189 | 
             
                end
         | 
| 190 190 | 
             
                assert_match(/Illegal edit/, ex.message)
         | 
| 191 191 |  | 
| 192 192 | 
             
                ex = assert_raises(ViewModel::AccessControlError) do
         | 
| 193 193 | 
             
                  # edit
         | 
| 194 | 
            -
                  v = ParentView.new(@parent1).to_hash.merge( | 
| 194 | 
            +
                  v = ParentView.new(@parent1).to_hash.merge('name' => 'p2')
         | 
| 195 195 | 
             
                  ParentView.deserialize_from_view(v, deserialize_context: no_edit_context)
         | 
| 196 196 | 
             
                end
         | 
| 197 197 | 
             
                assert_match(/Illegal edit/, ex.message)
         | 
| @@ -208,13 +208,13 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 208 208 |  | 
| 209 209 | 
             
                ex = assert_raises(ViewModel::AccessControlError) do
         | 
| 210 210 | 
             
                  # create
         | 
| 211 | 
            -
                  ParentView.deserialize_from_view({  | 
| 211 | 
            +
                  ParentView.deserialize_from_view({ '_type' => 'Parent', 'name' => 'p' }, deserialize_context: no_edit_context)
         | 
| 212 212 | 
             
                end
         | 
| 213 213 | 
             
                assert_match(/Illegal edit/, ex.message)
         | 
| 214 214 |  | 
| 215 215 | 
             
                ex = assert_raises(ViewModel::AccessControlError) do
         | 
| 216 216 | 
             
                  # edit
         | 
| 217 | 
            -
                  v = ParentView.new(@parent1).to_hash.merge( | 
| 217 | 
            +
                  v = ParentView.new(@parent1).to_hash.merge('name' => 'p2')
         | 
| 218 218 | 
             
                  ParentView.deserialize_from_view(v, deserialize_context: no_edit_context)
         | 
| 219 219 | 
             
                end
         | 
| 220 220 | 
             
                assert_match(/Illegal edit/, ex.message)
         | 
| @@ -227,20 +227,20 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 227 227 | 
             
              end
         | 
| 228 228 |  | 
| 229 229 | 
             
              def test_create_multiple
         | 
| 230 | 
            -
                view = [{'_type' => 'Parent', 'name' => 'newp1'},
         | 
| 231 | 
            -
                        {'_type' => 'Parent', 'name' => 'newp2'}]
         | 
| 230 | 
            +
                view = [{ '_type' => 'Parent', 'name' => 'newp1' },
         | 
| 231 | 
            +
                        { '_type' => 'Parent', 'name' => 'newp2' },]
         | 
| 232 232 |  | 
| 233 233 | 
             
                result = ParentView.deserialize_from_view(view)
         | 
| 234 234 |  | 
| 235 | 
            -
                new_parents = Parent.where(id: result.map{|x| x.model.id})
         | 
| 235 | 
            +
                new_parents = Parent.where(id: result.map { |x| x.model.id })
         | 
| 236 236 |  | 
| 237 | 
            -
                assert_equal(%w | 
| 237 | 
            +
                assert_equal(%w[newp1 newp2], new_parents.pluck(:name).sort)
         | 
| 238 238 | 
             
              end
         | 
| 239 239 |  | 
| 240 240 | 
             
              def test_update_duplicate_specification
         | 
| 241 241 | 
             
                view = [
         | 
| 242 | 
            -
                  {'_type' => 'Parent', 'id' => @parent1.id},
         | 
| 243 | 
            -
                  {'_type' => 'Parent', 'id' => @parent1.id},
         | 
| 242 | 
            +
                  { '_type' => 'Parent', 'id' => @parent1.id },
         | 
| 243 | 
            +
                  { '_type' => 'Parent', 'id' => @parent1.id },
         | 
| 244 244 | 
             
                ]
         | 
| 245 245 | 
             
                assert_raises(ViewModel::DeserializationError::DuplicateNodes) do
         | 
| 246 246 | 
             
                  ParentView.deserialize_from_view(view)
         | 
| @@ -248,28 +248,28 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 248 248 | 
             
              end
         | 
| 249 249 |  | 
| 250 250 | 
             
              def test_create_invalid_type
         | 
| 251 | 
            -
             | 
| 251 | 
            +
                build_viewmodel(:Invalid) do
         | 
| 252 252 | 
             
                  define_schema { |t| }
         | 
| 253 253 | 
             
                  define_model {}
         | 
| 254 254 | 
             
                  define_viewmodel {}
         | 
| 255 255 | 
             
                end
         | 
| 256 256 |  | 
| 257 257 | 
             
                ex = assert_raises(ViewModel::DeserializationError::InvalidSyntax) do
         | 
| 258 | 
            -
                  ParentView.deserialize_from_view({  | 
| 258 | 
            +
                  ParentView.deserialize_from_view({ 'target' => [] })
         | 
| 259 259 | 
             
                end
         | 
| 260 260 | 
             
                assert_match(/"_type" wasn't supplied/, ex.message)
         | 
| 261 261 |  | 
| 262 | 
            -
                 | 
| 263 | 
            -
                  ParentView.deserialize_from_view({  | 
| 262 | 
            +
                assert_raises(ViewModel::DeserializationError::InvalidViewType) do
         | 
| 263 | 
            +
                  ParentView.deserialize_from_view({ '_type' => 'Invalid' })
         | 
| 264 264 | 
             
                end
         | 
| 265 265 |  | 
| 266 | 
            -
                 | 
| 267 | 
            -
                  ParentView.deserialize_from_view({  | 
| 266 | 
            +
                assert_raises(ViewModel::DeserializationError::UnknownView) do
         | 
| 267 | 
            +
                  ParentView.deserialize_from_view({ '_type' => 'NotAViewmodelType' })
         | 
| 268 268 | 
             
                end
         | 
| 269 269 | 
             
              end
         | 
| 270 270 |  | 
| 271 271 | 
             
              def test_edit_attribute_from_view
         | 
| 272 | 
            -
                alter_by_view!(ParentView, @parent1) do |view,  | 
| 272 | 
            +
                alter_by_view!(ParentView, @parent1) do |view, _refs|
         | 
| 273 273 | 
             
                  view['name'] = 'renamed'
         | 
| 274 274 | 
             
                end
         | 
| 275 275 | 
             
                assert_equal('renamed', @parent1.name)
         | 
| @@ -278,28 +278,28 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 278 278 | 
             
              def test_edit_attribute_validation_failure
         | 
| 279 279 | 
             
                old_name = @parent1.name
         | 
| 280 280 | 
             
                ex = assert_raises(ViewModel::DeserializationError::Validation) do
         | 
| 281 | 
            -
                  alter_by_view!(ParentView, @parent1) do |view,  | 
| 281 | 
            +
                  alter_by_view!(ParentView, @parent1) do |view, _refs|
         | 
| 282 282 | 
             
                    view['name'] = 'invalid'
         | 
| 283 283 | 
             
                  end
         | 
| 284 284 | 
             
                end
         | 
| 285 285 | 
             
                assert_equal(old_name, @parent1.name, 'validation failure causes rollback')
         | 
| 286 | 
            -
                assert_equal(ex.attribute,  | 
| 287 | 
            -
                assert_equal(ex.reason,  | 
| 286 | 
            +
                assert_equal(ex.attribute, 'name')
         | 
| 287 | 
            +
                assert_equal(ex.reason, 'invalid due to matching test sentinel')
         | 
| 288 288 | 
             
              end
         | 
| 289 289 |  | 
| 290 290 | 
             
              def test_edit_readonly_attribute
         | 
| 291 291 | 
             
                assert_raises(ViewModel::DeserializationError::ReadOnlyAttribute) do
         | 
| 292 | 
            -
                  ex = alter_by_view!(ParentView, @parent1) do |view,  | 
| 292 | 
            +
                  ex = alter_by_view!(ParentView, @parent1) do |view, _refs|
         | 
| 293 293 | 
             
                    view['one'] = 2
         | 
| 294 294 | 
             
                  end
         | 
| 295 | 
            -
                  assert_equal( | 
| 295 | 
            +
                  assert_equal('one', ex.attribute)
         | 
| 296 296 | 
             
                end
         | 
| 297 297 | 
             
              end
         | 
| 298 298 |  | 
| 299 299 | 
             
              def test_edit_missing_root
         | 
| 300 300 | 
             
                view = {
         | 
| 301 | 
            -
                   | 
| 302 | 
            -
                   | 
| 301 | 
            +
                  '_type' => 'Parent',
         | 
| 302 | 
            +
                  'id'    => 9999,
         | 
| 303 303 | 
             
                }
         | 
| 304 304 |  | 
| 305 305 | 
             
                ex = assert_raises(ViewModel::DeserializationError::NotFound) do
         | 
| @@ -310,7 +310,7 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 310 310 | 
             
              end
         | 
| 311 311 |  | 
| 312 312 | 
             
              def test_optimistic_locking
         | 
| 313 | 
            -
                @parent1.name =  | 
| 313 | 
            +
                @parent1.name = 'changed'
         | 
| 314 314 | 
             
                @parent1.save!
         | 
| 315 315 |  | 
| 316 316 | 
             
                assert_raises(ViewModel::DeserializationError::LockFailure) do
         | 
| @@ -320,7 +320,6 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 320 320 | 
             
                end
         | 
| 321 321 | 
             
              end
         | 
| 322 322 |  | 
| 323 | 
            -
             | 
| 324 323 | 
             
              # Tests for overriding the serialization of attributes using custom viewmodels
         | 
| 325 324 | 
             
              class CustomAttributeViewsTests < ActiveSupport::TestCase
         | 
| 326 325 | 
             
                include ARVMTestUtilities
         | 
| @@ -334,7 +333,7 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 334 333 | 
             
                  end
         | 
| 335 334 |  | 
| 336 335 | 
             
                  def self.deserialize_from_view(hash_data, references: {}, deserialize_context:)
         | 
| 337 | 
            -
                    array = [hash_data[ | 
| 336 | 
            +
                    array = [hash_data['a'], hash_data['b']]
         | 
| 338 337 | 
             
                    self.new(array)
         | 
| 339 338 | 
             
                  end
         | 
| 340 339 | 
             
                end
         | 
| @@ -343,7 +342,7 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 343 342 | 
             
                  super
         | 
| 344 343 | 
             
                  build_viewmodel(:Pair) do
         | 
| 345 344 | 
             
                    define_schema do |t|
         | 
| 346 | 
            -
                      t.column :pair,  | 
| 345 | 
            +
                      t.column :pair, 'integer[]'
         | 
| 347 346 | 
             
                    end
         | 
| 348 347 |  | 
| 349 348 | 
             
                    define_model do
         | 
| @@ -357,23 +356,23 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 357 356 |  | 
| 358 357 | 
             
                def setup
         | 
| 359 358 | 
             
                  super
         | 
| 360 | 
            -
                  @pair = Pair.create!(pair: [1,2])
         | 
| 359 | 
            +
                  @pair = Pair.create!(pair: [1, 2])
         | 
| 361 360 | 
             
                end
         | 
| 362 361 |  | 
| 363 362 | 
             
                def test_serialize_view
         | 
| 364 363 | 
             
                  view, _refs = serialize_with_references(PairView.new(@pair))
         | 
| 365 364 |  | 
| 366 | 
            -
                  assert_equal({  | 
| 367 | 
            -
                                  | 
| 368 | 
            -
                                  | 
| 369 | 
            -
                                  | 
| 365 | 
            +
                  assert_equal({ '_type'    => 'Pair',
         | 
| 366 | 
            +
                                 '_version' => 1,
         | 
| 367 | 
            +
                                 'id'       => @pair.id,
         | 
| 368 | 
            +
                                 'pair'     => { 'a' => 1, 'b' => 2 } },
         | 
| 370 369 | 
             
                               view)
         | 
| 371 370 | 
             
                end
         | 
| 372 371 |  | 
| 373 372 | 
             
                def test_create
         | 
| 374 | 
            -
                  view = {  | 
| 373 | 
            +
                  view = { '_type' => 'Pair', 'pair' => { 'a' => 3, 'b' => 4 } }
         | 
| 375 374 | 
             
                  pv = PairView.deserialize_from_view(view)
         | 
| 376 | 
            -
                  assert_equal([3,4], pv.model.pair)
         | 
| 375 | 
            +
                  assert_equal([3, 4], pv.model.pair)
         | 
| 377 376 | 
             
                end
         | 
| 378 377 | 
             
              end
         | 
| 379 378 |  | 
| @@ -383,8 +382,9 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 383 382 |  | 
| 384 383 | 
             
                class RefError < RuntimeError
         | 
| 385 384 | 
             
                  attr_reader :ref
         | 
| 385 | 
            +
             | 
| 386 386 | 
             
                  def initialize(ref)
         | 
| 387 | 
            -
                    super( | 
| 387 | 
            +
                    super('Boom')
         | 
| 388 388 | 
             
                    @ref = ref
         | 
| 389 389 | 
             
                  end
         | 
| 390 390 | 
             
                end
         | 
| @@ -405,7 +405,7 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 405 405 | 
             
                      association :child
         | 
| 406 406 | 
             
                      attribute :explode
         | 
| 407 407 | 
             
                      # Escape deserialization with the parent context
         | 
| 408 | 
            -
                      define_method(:deserialize_explode) do |val, references:, deserialize_context | 
| 408 | 
            +
                      define_method(:deserialize_explode) do |val, references:, deserialize_context:|
         | 
| 409 409 | 
             
                        raise RefError.new(deserialize_context.parent_ref) if val
         | 
| 410 410 | 
             
                      end
         | 
| 411 411 | 
             
                    end
         | 
| @@ -418,22 +418,22 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 418 418 |  | 
| 419 419 | 
             
                def test_deserialize_context
         | 
| 420 420 | 
             
                  view = {
         | 
| 421 | 
            -
                     | 
| 422 | 
            -
                     | 
| 423 | 
            -
                     | 
| 424 | 
            -
                     | 
| 425 | 
            -
                       | 
| 426 | 
            -
                    }}
         | 
| 421 | 
            +
                    '_type' => 'List',
         | 
| 422 | 
            +
                    'id'    => 1000,
         | 
| 423 | 
            +
                    '_new'  => true,
         | 
| 424 | 
            +
                    'child' => {
         | 
| 425 | 
            +
                      '_type' => 'List',
         | 
| 426 | 
            +
                    } }
         | 
| 427 427 |  | 
| 428 428 | 
             
                  ref_error = assert_raises(RefError) do
         | 
| 429 | 
            -
                    ListView.deserialize_from_view(view.deep_merge( | 
| 429 | 
            +
                    ListView.deserialize_from_view(view.deep_merge('child' => { 'explode' => true }))
         | 
| 430 430 | 
             
                  end
         | 
| 431 431 |  | 
| 432 432 | 
             
                  assert_equal(ListView, ref_error.ref.viewmodel_class)
         | 
| 433 433 | 
             
                  assert_equal(1000, ref_error.ref.model_id)
         | 
| 434 434 |  | 
| 435 435 | 
             
                  ref_error = assert_raises(RefError) do
         | 
| 436 | 
            -
                    ListView.deserialize_from_view(view.deep_merge( | 
| 436 | 
            +
                    ListView.deserialize_from_view(view.deep_merge('explode' => true))
         | 
| 437 437 | 
             
                  end
         | 
| 438 438 |  | 
| 439 439 | 
             
                  assert_nil(ref_error.ref)
         | 
| @@ -461,7 +461,7 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 461 461 | 
             
                      association :child
         | 
| 462 462 | 
             
                    end
         | 
| 463 463 | 
             
                  end
         | 
| 464 | 
            -
                  List.connection.execute( | 
| 464 | 
            +
                  List.connection.execute('ALTER TABLE lists ADD CONSTRAINT unique_child UNIQUE (child_id) DEFERRABLE INITIALLY DEFERRED')
         | 
| 465 465 | 
             
                end
         | 
| 466 466 |  | 
| 467 467 | 
             
                def test_deferred_constraint_violation
         | 
| @@ -470,8 +470,8 @@ class ViewModel::ActiveRecordTest < ActiveSupport::TestCase | |
| 470 470 |  | 
| 471 471 | 
             
                  ex = assert_raises(ViewModel::DeserializationError::UniqueViolation) do
         | 
| 472 472 | 
             
                    alter_by_view!(ListView, l2) do |view, refs|
         | 
| 473 | 
            -
                      view['child'] = {  | 
| 474 | 
            -
                      refs[ | 
| 473 | 
            +
                      view['child'] = { '_ref' => 'r1' }
         | 
| 474 | 
            +
                      refs['r1'] = { '_type' => 'List', 'id' => l1.child.id }
         | 
| 475 475 | 
             
                    end
         | 
| 476 476 | 
             
                  end
         | 
| 477 477 |  | 
| @@ -1,9 +1,9 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require_relative '../../helpers/arvm_test_utilities | 
| 4 | 
            -
            require_relative '../../helpers/arvm_test_models | 
| 5 | 
            -
            require_relative '../../helpers/callback_tracer | 
| 6 | 
            -
            require_relative '../../helpers/viewmodel_spec_helpers | 
| 3 | 
            +
            require_relative '../../helpers/arvm_test_utilities'
         | 
| 4 | 
            +
            require_relative '../../helpers/arvm_test_models'
         | 
| 5 | 
            +
            require_relative '../../helpers/callback_tracer'
         | 
| 6 | 
            +
            require_relative '../../helpers/viewmodel_spec_helpers'
         | 
| 7 7 |  | 
| 8 8 | 
             
            require 'minitest/autorun'
         | 
| 9 9 |  | 
| @@ -62,7 +62,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 62 62 | 
             
                      [visit(ViewModel::Callbacks::Hook::BeforeVisit, vm),
         | 
| 63 63 | 
             
                       visit(ViewModel::Callbacks::Hook::BeforeVisit, vm.child),
         | 
| 64 64 | 
             
                       visit(ViewModel::Callbacks::Hook::AfterVisit,  vm.child),
         | 
| 65 | 
            -
                       visit(ViewModel::Callbacks::Hook::AfterVisit,  vm)])
         | 
| 65 | 
            +
                       visit(ViewModel::Callbacks::Hook::AfterVisit,  vm),])
         | 
| 66 66 | 
             
                  end
         | 
| 67 67 |  | 
| 68 68 | 
             
                  it 'visits in correct order when deserializing' do
         | 
| @@ -79,7 +79,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 79 79 |  | 
| 80 80 | 
             
                       visit(ViewModel::Callbacks::Hook::BeforeValidate,    vm),
         | 
| 81 81 | 
             
                       visit(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
         | 
| 82 | 
            -
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm)])
         | 
| 82 | 
            +
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm),])
         | 
| 83 83 | 
             
                  end
         | 
| 84 84 |  | 
| 85 85 | 
             
                  it 'calls edit hook when updating' do
         | 
| @@ -99,7 +99,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 99 99 | 
             
                       visit(ViewModel::Callbacks::Hook::BeforeValidate,    vm),
         | 
| 100 100 | 
             
                       visit(ViewModel::Callbacks::Hook::OnChange,          vm),
         | 
| 101 101 | 
             
                       visit(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
         | 
| 102 | 
            -
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm)])
         | 
| 102 | 
            +
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm),])
         | 
| 103 103 | 
             
                  end
         | 
| 104 104 |  | 
| 105 105 | 
             
                  it 'calls edit hook when deleting' do
         | 
| @@ -122,7 +122,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 122 122 | 
             
                       visit(ViewModel::Callbacks::Hook::OnChange,          vm),
         | 
| 123 123 |  | 
| 124 124 | 
             
                       visit(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
         | 
| 125 | 
            -
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm)])
         | 
| 125 | 
            +
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm),])
         | 
| 126 126 | 
             
                  end
         | 
| 127 127 |  | 
| 128 128 | 
             
                  it 'calls hooks on old and new when replacing' do
         | 
| @@ -153,7 +153,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 153 153 | 
             
                       visit(ViewModel::Callbacks::Hook::OnChange,          vm),
         | 
| 154 154 |  | 
| 155 155 | 
             
                       visit(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
         | 
| 156 | 
            -
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm)])
         | 
| 156 | 
            +
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm),])
         | 
| 157 157 | 
             
                  end
         | 
| 158 158 |  | 
| 159 159 | 
             
                  it 'calls hooks on old and new when moving' do
         | 
| @@ -184,7 +184,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 184 184 | 
             
                       visit(ViewModel::Callbacks::Hook::BeforeValidate,    vm2),
         | 
| 185 185 | 
             
                       visit(ViewModel::Callbacks::Hook::OnChange,          vm2),
         | 
| 186 186 | 
             
                       visit(ViewModel::Callbacks::Hook::AfterDeserialize,  vm2),
         | 
| 187 | 
            -
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm2)])
         | 
| 187 | 
            +
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm2),])
         | 
| 188 188 | 
             
                  end
         | 
| 189 189 |  | 
| 190 190 | 
             
                  it 'calls hooks on delete' do
         | 
| @@ -195,7 +195,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 195 195 | 
             
                       visit(ViewModel::Callbacks::Hook::BeforeDeserialize, vm),
         | 
| 196 196 | 
             
                       visit(ViewModel::Callbacks::Hook::OnChange,          vm),
         | 
| 197 197 | 
             
                       visit(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
         | 
| 198 | 
            -
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm)])
         | 
| 198 | 
            +
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm),])
         | 
| 199 199 | 
             
                    ## At present, children aren't visited on delete.
         | 
| 200 200 | 
             
                    # visit(ViewModel::Callbacks::Hook::BeforeVisit,       old_child),
         | 
| 201 201 | 
             
                    # visit(ViewModel::Callbacks::Hook::BeforeDeserialize, old_child),
         | 
| @@ -283,7 +283,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 283 283 |  | 
| 284 284 | 
             
                       visit(ViewModel::Callbacks::Hook::OnChange,          vm),
         | 
| 285 285 | 
             
                       visit(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
         | 
| 286 | 
            -
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm)])
         | 
| 286 | 
            +
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm),])
         | 
| 287 287 | 
             
                  end
         | 
| 288 288 |  | 
| 289 289 | 
             
                  it 'calls hooks on append_associated' do
         | 
| @@ -306,7 +306,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 306 306 |  | 
| 307 307 | 
             
                       visit(ViewModel::Callbacks::Hook::OnChange,          vm),
         | 
| 308 308 | 
             
                       visit(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
         | 
| 309 | 
            -
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm)])
         | 
| 309 | 
            +
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm),])
         | 
| 310 310 | 
             
                  end
         | 
| 311 311 |  | 
| 312 312 | 
             
                  it 'calls hooks on delete_associated' do
         | 
| @@ -330,7 +330,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 330 330 |  | 
| 331 331 | 
             
                       visit(ViewModel::Callbacks::Hook::OnChange,          vm),
         | 
| 332 332 | 
             
                       visit(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
         | 
| 333 | 
            -
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm)])
         | 
| 333 | 
            +
                       visit(ViewModel::Callbacks::Hook::AfterVisit,        vm),])
         | 
| 334 334 | 
             
                  end
         | 
| 335 335 | 
             
                end
         | 
| 336 336 |  | 
| @@ -386,6 +386,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 386 386 | 
             
                  class ViewSpecificCallback
         | 
| 387 387 | 
             
                    include ViewModel::Callbacks
         | 
| 388 388 | 
             
                    attr_reader :models, :children
         | 
| 389 | 
            +
             | 
| 389 390 | 
             
                    def initialize
         | 
| 390 391 | 
             
                      @models = []
         | 
| 391 392 | 
             
                      @children = []
         | 
| @@ -422,6 +423,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 422 423 | 
             
                    include ViewModel::Callbacks
         | 
| 423 424 |  | 
| 424 425 | 
             
                    attr_reader :events
         | 
| 426 | 
            +
             | 
| 425 427 | 
             
                    def initialize
         | 
| 426 428 | 
             
                      @events = []
         | 
| 427 429 | 
             
                    end
         | 
| @@ -443,6 +445,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 443 445 | 
             
                    include ViewModel::Callbacks
         | 
| 444 446 |  | 
| 445 447 | 
             
                    attr_reader :a
         | 
| 448 | 
            +
             | 
| 446 449 | 
             
                    def initialize
         | 
| 447 450 | 
             
                      @a = 0
         | 
| 448 451 | 
             
                    end
         | 
| @@ -456,6 +459,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 456 459 |  | 
| 457 460 | 
             
                  class ChildCallback < ParentCallback
         | 
| 458 461 | 
             
                    attr_reader :b
         | 
| 462 | 
            +
             | 
| 459 463 | 
             
                    def initialize
         | 
| 460 464 | 
             
                      super
         | 
| 461 465 | 
             
                      @b = 0
         | 
| @@ -541,7 +545,7 @@ class ViewModel::CallbacksTest < ActiveSupport::TestCase | |
| 541 545 | 
             
                    [RecordingCallback.new(events, :a),
         | 
| 542 546 | 
             
                     UpdatingCallback.new(events, :b),
         | 
| 543 547 | 
             
                     RecordingCallback.new(events, :c),
         | 
| 544 | 
            -
                     UpdatingCallback.new(events, :d)]
         | 
| 548 | 
            +
                     UpdatingCallback.new(events, :d),]
         | 
| 545 549 | 
             
                  end
         | 
| 546 550 |  | 
| 547 551 | 
             
                  it 'calls callbacks in order specified partitioned by update' do
         |