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::CounterTest < ActiveSupport::TestCase
         | 
| 9 11 | 
             
              include ARVMTestUtilities
         | 
| @@ -40,7 +42,6 @@ class ViewModel::ActiveRecord::CounterTest < ActiveSupport::TestCase | |
| 40 42 | 
             
                    attribute :name
         | 
| 41 43 | 
             
                  end
         | 
| 42 44 | 
             
                end
         | 
| 43 | 
            -
             | 
| 44 45 | 
             
              end
         | 
| 45 46 |  | 
| 46 47 | 
             
              def setup
         | 
| @@ -50,15 +51,15 @@ class ViewModel::ActiveRecord::CounterTest < ActiveSupport::TestCase | |
| 50 51 | 
             
              end
         | 
| 51 52 |  | 
| 52 53 | 
             
              def test_counter_cache_create
         | 
| 53 | 
            -
                alter_by_view!(CategoryView, @category1) do |view,  | 
| 54 | 
            -
                  view['products'] << {'_type' => 'Product'}
         | 
| 54 | 
            +
                alter_by_view!(CategoryView, @category1) do |view, _refs|
         | 
| 55 | 
            +
                  view['products'] << { '_type' => 'Product' }
         | 
| 55 56 | 
             
                end
         | 
| 56 57 | 
             
                assert_equal(2, @category1.products_count)
         | 
| 57 58 | 
             
              end
         | 
| 58 59 |  | 
| 59 60 | 
             
              def test_counter_cache_move
         | 
| 60 61 | 
             
                @category2 = Category.create(name: 'c2')
         | 
| 61 | 
            -
                alter_by_view!(CategoryView, [@category1, @category2]) do |(c1view, c2view),  | 
| 62 | 
            +
                alter_by_view!(CategoryView, [@category1, @category2]) do |(c1view, c2view), _refs|
         | 
| 62 63 | 
             
                  c2view['products'] = c1view['products']
         | 
| 63 64 | 
             
                  c1view['products'] = []
         | 
| 64 65 | 
             
                end
         | 
| @@ -67,7 +68,7 @@ class ViewModel::ActiveRecord::CounterTest < ActiveSupport::TestCase | |
| 67 68 | 
             
              end
         | 
| 68 69 |  | 
| 69 70 | 
             
              def test_counter_cache_delete
         | 
| 70 | 
            -
                alter_by_view!(CategoryView, @category1) do |view,  | 
| 71 | 
            +
                alter_by_view!(CategoryView, @category1) do |view, _refs|
         | 
| 71 72 | 
             
                  view['products'] = []
         | 
| 72 73 | 
             
                end
         | 
| 73 74 | 
             
                assert_equal(0, @category1.products_count)
         | 
| @@ -1,11 +1,11 @@ | |
| 1 | 
            -
            #  | 
| 2 | 
            -
            require_relative "../../../helpers/arvm_test_utilities.rb"
         | 
| 3 | 
            -
            require_relative "../../../helpers/arvm_test_models.rb"
         | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 4 2 |  | 
| 5 | 
            -
             | 
| 3 | 
            +
            require_relative '../../../helpers/arvm_test_utilities'
         | 
| 4 | 
            +
            require_relative '../../../helpers/arvm_test_models'
         | 
| 6 5 |  | 
| 7 | 
            -
            require  | 
| 6 | 
            +
            require 'minitest/autorun'
         | 
| 8 7 |  | 
| 8 | 
            +
            require 'view_model/active_record'
         | 
| 9 9 |  | 
| 10 10 | 
             
            require 'renum'
         | 
| 11 11 |  | 
| @@ -28,15 +28,16 @@ class ViewModel::ActiveRecord::SpecializeAssociationTest < ActiveSupport::TestCa | |
| 28 28 | 
             
                    attributes :text
         | 
| 29 29 | 
             
                    association :translations
         | 
| 30 30 |  | 
| 31 | 
            -
                    def self.pre_parse_translations( | 
| 32 | 
            -
                      raise  | 
| 33 | 
            -
             | 
| 31 | 
            +
                    def self.pre_parse_translations(_viewmodel_reference, _metadata, hash, translations)
         | 
| 32 | 
            +
                      raise 'type check' unless translations.is_a?(Hash) && translations.all? { |k, v| k.is_a?(String) && v.is_a?(String) }
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                      hash['translations'] = translations.map { |lang, text| { '_type' => 'Translation', 'language' => lang, 'translation' => text } }
         | 
| 34 35 | 
             
                    end
         | 
| 35 36 |  | 
| 36 37 | 
             
                    def resolve_translations(update_datas, previous_translation_views)
         | 
| 37 38 | 
             
                      existing = previous_translation_views.index_by { |x| [x.model.language, x.model.translation] }
         | 
| 38 39 | 
             
                      update_datas.map do |update_data|
         | 
| 39 | 
            -
                        existing.fetch([update_data[ | 
| 40 | 
            +
                        existing.fetch([update_data['language'], update_data['translation']]) { TranslationView.for_new_model }
         | 
| 40 41 | 
             
                      end
         | 
| 41 42 | 
             
                    end
         | 
| 42 43 |  | 
| @@ -71,19 +72,19 @@ class ViewModel::ActiveRecord::SpecializeAssociationTest < ActiveSupport::TestCa | |
| 71 72 | 
             
              def setup
         | 
| 72 73 | 
             
                super
         | 
| 73 74 |  | 
| 74 | 
            -
                @text1 = Text.create(text:  | 
| 75 | 
            -
                                translations: [Translation.new(language:  | 
| 76 | 
            -
                                               Translation.new(language:  | 
| 75 | 
            +
                @text1 = Text.create(text: 'dog',
         | 
| 76 | 
            +
                                translations: [Translation.new(language: 'ja', translation: '犬'),
         | 
| 77 | 
            +
                                               Translation.new(language: 'fr', translation: 'chien'),])
         | 
| 77 78 |  | 
| 78 79 | 
             
                @text1_view = {
         | 
| 79 | 
            -
                   | 
| 80 | 
            -
                   | 
| 81 | 
            -
                   | 
| 82 | 
            -
                   | 
| 83 | 
            -
                   | 
| 84 | 
            -
                     | 
| 85 | 
            -
                     | 
| 86 | 
            -
                  }
         | 
| 80 | 
            +
                  'id'           => @text1.id,
         | 
| 81 | 
            +
                  '_type'        => 'Text',
         | 
| 82 | 
            +
                  '_version'     => 1,
         | 
| 83 | 
            +
                  'text'         => 'dog',
         | 
| 84 | 
            +
                  'translations' => {
         | 
| 85 | 
            +
                    'ja' => '犬',
         | 
| 86 | 
            +
                    'fr' => 'chien',
         | 
| 87 | 
            +
                  },
         | 
| 87 88 | 
             
                }
         | 
| 88 89 |  | 
| 89 90 | 
             
                enable_logging!
         | 
| @@ -94,7 +95,7 @@ class ViewModel::ActiveRecord::SpecializeAssociationTest < ActiveSupport::TestCa | |
| 94 95 | 
             
              end
         | 
| 95 96 |  | 
| 96 97 | 
             
              def test_create
         | 
| 97 | 
            -
                create_view = @text1_view.dup.tap {|v| v.delete('id')}
         | 
| 98 | 
            +
                create_view = @text1_view.dup.tap { |v| v.delete('id') }
         | 
| 98 99 | 
             
                new_text_view = TextView.deserialize_from_view(create_view)
         | 
| 99 100 | 
             
                new_text_model = new_text_view.model
         | 
| 100 101 |  | 
| @@ -103,8 +104,8 @@ class ViewModel::ActiveRecord::SpecializeAssociationTest < ActiveSupport::TestCa | |
| 103 104 | 
             
                new_translations = new_text_model.translations.map do |x|
         | 
| 104 105 | 
             
                  [x['language'], x['translation']]
         | 
| 105 106 | 
             
                end
         | 
| 106 | 
            -
                assert_equal([%w | 
| 107 | 
            -
                              %w | 
| 107 | 
            +
                assert_equal([%w[fr chien],
         | 
| 108 | 
            +
                              %w[ja 犬],],
         | 
| 108 109 | 
             
                             new_translations.sort)
         | 
| 109 110 | 
             
              end
         | 
| 110 111 |  | 
| @@ -163,10 +164,11 @@ class ViewModel::ActiveRecord::FlattenAssociationTest < ActiveSupport::TestCase | |
| 163 164 | 
             
                  def construct_hash(members)
         | 
| 164 165 | 
             
                    case self
         | 
| 165 166 | 
             
                    when SectionType::Simple
         | 
| 166 | 
            -
                      raise  | 
| 167 | 
            +
                      raise 'nopes' if members.present?
         | 
| 168 | 
            +
             | 
| 167 169 | 
             
                      nil
         | 
| 168 170 | 
             
                    else
         | 
| 169 | 
            -
                      members.merge( | 
| 171 | 
            +
                      members.merge('_type' => viewmodel.view_name)
         | 
| 170 172 | 
             
                    end
         | 
| 171 173 | 
             
                  end
         | 
| 172 174 |  | 
| @@ -219,7 +221,7 @@ class ViewModel::ActiveRecord::FlattenAssociationTest < ActiveSupport::TestCase | |
| 219 221 | 
             
                      section_type = SectionType.with_name(section_type_name)
         | 
| 220 222 | 
             
                      raise "Invalid section type: #{section_type_name.inspect}" unless section_type
         | 
| 221 223 |  | 
| 222 | 
            -
                      user_data[ | 
| 224 | 
            +
                      user_data['section_data'] = section_type.construct_hash(user_data.slice!(*self._members.keys))
         | 
| 223 225 | 
             
                    end
         | 
| 224 226 |  | 
| 225 227 | 
             
                    def resolve_section_data(update_data, previous_translation_view)
         | 
| @@ -242,39 +244,38 @@ class ViewModel::ActiveRecord::FlattenAssociationTest < ActiveSupport::TestCase | |
| 242 244 | 
             
                    end
         | 
| 243 245 | 
             
                  end
         | 
| 244 246 | 
             
                end
         | 
| 245 | 
            -
             | 
| 246 247 | 
             
              end
         | 
| 247 248 |  | 
| 248 249 | 
             
              def setup
         | 
| 249 250 | 
             
                super
         | 
| 250 251 |  | 
| 251 | 
            -
                @simplesection = Section.create(name:  | 
| 252 | 
            +
                @simplesection = Section.create(name: 'simple1')
         | 
| 252 253 | 
             
                @simplesection_view = {
         | 
| 253 | 
            -
                   | 
| 254 | 
            -
                   | 
| 255 | 
            -
                   | 
| 256 | 
            -
                   | 
| 257 | 
            -
                   | 
| 254 | 
            +
                  'id'           => @simplesection.id,
         | 
| 255 | 
            +
                  '_type'        => 'Section',
         | 
| 256 | 
            +
                  '_version'     => 1,
         | 
| 257 | 
            +
                  'section_type' => 'Simple',
         | 
| 258 | 
            +
                  'name'         => 'simple1',
         | 
| 258 259 | 
             
                }
         | 
| 259 260 |  | 
| 260 | 
            -
                @quizsection = Section.create(name:  | 
| 261 | 
            +
                @quizsection = Section.create(name: 'quiz1', section_data: QuizSection.new(quiz_name: 'qq'))
         | 
| 261 262 | 
             
                @quizsection_view = {
         | 
| 262 | 
            -
                   | 
| 263 | 
            -
                   | 
| 264 | 
            -
                   | 
| 265 | 
            -
                   | 
| 266 | 
            -
                   | 
| 267 | 
            -
                   | 
| 263 | 
            +
                  'id'           => @quizsection.id,
         | 
| 264 | 
            +
                  '_type'        => 'Section',
         | 
| 265 | 
            +
                  '_version'     => 1,
         | 
| 266 | 
            +
                  'section_type' => 'Quiz',
         | 
| 267 | 
            +
                  'name'         => 'quiz1',
         | 
| 268 | 
            +
                  'quiz_name'    => 'qq',
         | 
| 268 269 | 
             
                }
         | 
| 269 270 |  | 
| 270 | 
            -
                @vocabsection = Section.create(name:  | 
| 271 | 
            +
                @vocabsection = Section.create(name: 'vocab1', section_data: VocabSection.new(vocab_word: 'dog'))
         | 
| 271 272 | 
             
                @vocabsection_view = {
         | 
| 272 | 
            -
                   | 
| 273 | 
            -
                   | 
| 274 | 
            -
                   | 
| 275 | 
            -
                   | 
| 276 | 
            -
                   | 
| 277 | 
            -
                   | 
| 273 | 
            +
                  'id'           => @vocabsection.id,
         | 
| 274 | 
            +
                  '_type'        => 'Section',
         | 
| 275 | 
            +
                  '_version'     => 1,
         | 
| 276 | 
            +
                  'section_type' => 'Vocab',
         | 
| 277 | 
            +
                  'name'         => 'vocab1',
         | 
| 278 | 
            +
                  'vocab_word'   => 'dog',
         | 
| 278 279 | 
             
                }
         | 
| 279 280 |  | 
| 280 281 | 
             
                enable_logging!
         | 
| @@ -296,7 +297,7 @@ class ViewModel::ActiveRecord::FlattenAssociationTest < ActiveSupport::TestCase | |
| 296 297 | 
             
              end
         | 
| 297 298 |  | 
| 298 299 | 
             
              def test_create
         | 
| 299 | 
            -
                assert_section = ->(model, name, &check_section){
         | 
| 300 | 
            +
                assert_section = ->(model, name, &check_section) {
         | 
| 300 301 | 
             
                  assert(!model.changed?)
         | 
| 301 302 | 
             
                  assert(!model.new_record?)
         | 
| 302 303 | 
             
                  assert_equal(name, model.name)
         | 
| @@ -313,18 +314,18 @@ class ViewModel::ActiveRecord::FlattenAssociationTest < ActiveSupport::TestCase | |
| 313 314 | 
             
                }
         | 
| 314 315 |  | 
| 315 316 | 
             
                v = SectionView.deserialize_from_view(new_view_like(@simplesection_view))
         | 
| 316 | 
            -
                assert_section.call(v.model,  | 
| 317 | 
            +
                assert_section.call(v.model, 'simple1')
         | 
| 317 318 |  | 
| 318 319 | 
             
                v = SectionView.deserialize_from_view(new_view_like(@quizsection_view))
         | 
| 319 | 
            -
                assert_section.call(v.model,  | 
| 320 | 
            +
                assert_section.call(v.model, 'quiz1') do |m|
         | 
| 320 321 | 
             
                  assert(m.is_a?(QuizSection))
         | 
| 321 | 
            -
                  assert_equal( | 
| 322 | 
            +
                  assert_equal('qq', m.quiz_name)
         | 
| 322 323 | 
             
                end
         | 
| 323 324 |  | 
| 324 325 | 
             
                v = SectionView.deserialize_from_view(new_view_like(@vocabsection_view))
         | 
| 325 | 
            -
                assert_section.call(v.model,  | 
| 326 | 
            +
                assert_section.call(v.model, 'vocab1') do |m|
         | 
| 326 327 | 
             
                  assert(m.is_a?(VocabSection))
         | 
| 327 | 
            -
                  assert_equal( | 
| 328 | 
            +
                  assert_equal('dog', m.vocab_word)
         | 
| 328 329 | 
             
                end
         | 
| 329 330 | 
             
              end
         | 
| 330 331 |  | 
| @@ -363,15 +364,15 @@ class ViewModel::ActiveRecord::FlattenAssociationTest < ActiveSupport::TestCase | |
| 363 364 | 
             
                def setup
         | 
| 364 365 | 
             
                  super
         | 
| 365 366 | 
             
                  sections = [
         | 
| 366 | 
            -
                    Section.new(name:  | 
| 367 | 
            -
                    Section.new(name:  | 
| 368 | 
            -
                    Section.new(name:  | 
| 367 | 
            +
                    Section.new(name: 'simple1'),
         | 
| 368 | 
            +
                    Section.new(name: 'quiz1', section_data: QuizSection.new(quiz_name: 'qq')),
         | 
| 369 | 
            +
                    Section.new(name: 'vocab1', section_data: VocabSection.new(vocab_word: 'dog')),
         | 
| 369 370 | 
             
                  ]
         | 
| 370 371 | 
             
                  @exercise1 = Exercise.create(sections: sections)
         | 
| 371 372 | 
             
                end
         | 
| 372 373 |  | 
| 373 374 | 
             
                def test_functional_update
         | 
| 374 | 
            -
                  alter_by_view!(ExerciseView, @exercise1) do |view,  | 
| 375 | 
            +
                  alter_by_view!(ExerciseView, @exercise1) do |view, _refs|
         | 
| 375 376 | 
             
                    view['sections'] = {
         | 
| 376 377 | 
             
                      '_type'   => '_update',
         | 
| 377 378 | 
             
                      'actions' => [{ '_type'  => 'append',
         | 
| @@ -380,7 +381,7 @@ class ViewModel::ActiveRecord::FlattenAssociationTest < ActiveSupport::TestCase | |
| 380 381 | 
             
                                                     'name'         => 'vocab_new',
         | 
| 381 382 | 
             
                                                     'vocab_word'   => 'cat',
         | 
| 382 383 | 
             
                                                   }],
         | 
| 383 | 
            -
                                    }]
         | 
| 384 | 
            +
                                    }],
         | 
| 384 385 | 
             
                    }
         | 
| 385 386 | 
             
                  end
         | 
| 386 387 | 
             
                end
         | 
| @@ -1,10 +1,12 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            require_relative "../../../helpers/arvm_test_models.rb"
         | 
| 3 | 
            -
            require_relative '../../../helpers/viewmodel_spec_helpers.rb'
         | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 4 2 |  | 
| 5 | 
            -
             | 
| 3 | 
            +
            require_relative '../../../helpers/arvm_test_utilities'
         | 
| 4 | 
            +
            require_relative '../../../helpers/arvm_test_models'
         | 
| 5 | 
            +
            require_relative '../../../helpers/viewmodel_spec_helpers'
         | 
| 6 6 |  | 
| 7 | 
            -
            require  | 
| 7 | 
            +
            require 'minitest/autorun'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            require 'view_model/active_record'
         | 
| 8 10 |  | 
| 9 11 | 
             
            class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase
         | 
| 10 12 | 
             
              include ARVMTestUtilities
         | 
| @@ -15,15 +17,15 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 15 17 | 
             
              def setup
         | 
| 16 18 | 
             
                super
         | 
| 17 19 |  | 
| 18 | 
            -
                @model1 = model_class.new(name:  | 
| 19 | 
            -
                                      children: [child_model_class.new(name:  | 
| 20 | 
            -
                                                 child_model_class.new(name:  | 
| 21 | 
            -
                                                 child_model_class.new(name:  | 
| 20 | 
            +
                @model1 = model_class.new(name: 'p1',
         | 
| 21 | 
            +
                                      children: [child_model_class.new(name: 'p1c1', position: 1),
         | 
| 22 | 
            +
                                                 child_model_class.new(name: 'p1c2', position: 2),
         | 
| 23 | 
            +
                                                 child_model_class.new(name: 'p1c3', position: 3),])
         | 
| 22 24 | 
             
                @model1.save!
         | 
| 23 25 |  | 
| 24 | 
            -
                @model2 = model_class.new(name:  | 
| 25 | 
            -
                                      children: [child_model_class.new(name:  | 
| 26 | 
            -
                                                 child_model_class.new(name:  | 
| 26 | 
            +
                @model2 = model_class.new(name: 'p2',
         | 
| 27 | 
            +
                                      children: [child_model_class.new(name: 'p2c1').tap { |c| c.position = 1 },
         | 
| 28 | 
            +
                                                 child_model_class.new(name: 'p2c2').tap { |c| c.position = 2 },])
         | 
| 27 29 |  | 
| 28 30 | 
             
                @model2.save!
         | 
| 29 31 |  | 
| @@ -35,22 +37,21 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 35 37 |  | 
| 36 38 | 
             
                childviews = parentview.load_associated(:children)
         | 
| 37 39 | 
             
                assert_equal(3, childviews.size)
         | 
| 38 | 
            -
                assert_equal([ | 
| 40 | 
            +
                assert_equal(['p1c1', 'p1c2', 'p1c3'],
         | 
| 39 41 | 
             
                             childviews.map(&:name))
         | 
| 40 42 | 
             
              end
         | 
| 41 43 |  | 
| 42 44 | 
             
              def test_serialize_view
         | 
| 43 45 | 
             
                view, _refs = serialize_with_references(viewmodel_class.new(@model1))
         | 
| 44 46 |  | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
                                | 
| 48 | 
            -
                                | 
| 49 | 
            -
                                | 
| 50 | 
            -
             | 
| 51 | 
            -
                                                                                | 
| 52 | 
            -
                                                                                | 
| 53 | 
            -
                                                                               "name"     => child.name } } },
         | 
| 47 | 
            +
                assert_equal({ '_type'    => 'Model',
         | 
| 48 | 
            +
                               '_version' => 1,
         | 
| 49 | 
            +
                               'id'       => @model1.id,
         | 
| 50 | 
            +
                               'name'     => @model1.name,
         | 
| 51 | 
            +
                               'children' => @model1.children.map { |child| { '_type' => 'Child',
         | 
| 52 | 
            +
                                                                               '_version' => 1,
         | 
| 53 | 
            +
                                                                               'id'       => child.id,
         | 
| 54 | 
            +
                                                                               'name'     => child.name } } },
         | 
| 54 55 | 
             
                             view)
         | 
| 55 56 | 
             
              end
         | 
| 56 57 |  | 
| @@ -64,10 +65,10 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 64 65 |  | 
| 65 66 | 
             
              def test_create_from_view
         | 
| 66 67 | 
             
                view = {
         | 
| 67 | 
            -
                   | 
| 68 | 
            -
                   | 
| 69 | 
            -
                   | 
| 70 | 
            -
                                 {  | 
| 68 | 
            +
                  '_type'    => 'Model',
         | 
| 69 | 
            +
                  'name'     => 'p',
         | 
| 70 | 
            +
                  'children' => [{ '_type' => 'Child', 'name' => 'c1' },
         | 
| 71 | 
            +
                                 { '_type' => 'Child', 'name' => 'c2' },],
         | 
| 71 72 | 
             
                }
         | 
| 72 73 |  | 
| 73 74 | 
             
                pv = viewmodel_class.deserialize_from_view(view)
         | 
| @@ -76,7 +77,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 76 77 | 
             
                assert(!p.changed?)
         | 
| 77 78 | 
             
                assert(!p.new_record?)
         | 
| 78 79 |  | 
| 79 | 
            -
                assert_equal( | 
| 80 | 
            +
                assert_equal('p', p.name)
         | 
| 80 81 |  | 
| 81 82 | 
             
                assert_equal(2, p.children.count)
         | 
| 82 83 | 
             
                p.children.order(:id).each_with_index do |c, i|
         | 
| @@ -91,7 +92,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 91 92 |  | 
| 92 93 | 
             
                assert_raises(ViewModel::AccessControlError) do
         | 
| 93 94 | 
             
                  # append child
         | 
| 94 | 
            -
                  viewmodel_class.new(@model1).append_associated(:children, {  | 
| 95 | 
            +
                  viewmodel_class.new(@model1).append_associated(:children, { '_type' => 'Child', 'name' => 'hi' }, deserialize_context: no_edit_context)
         | 
| 95 96 | 
             
                end
         | 
| 96 97 |  | 
| 97 98 | 
             
                assert_raises(ViewModel::AccessControlError) do
         | 
| @@ -110,7 +111,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 110 111 | 
             
                view = { '_type'    => 'Model',
         | 
| 111 112 | 
             
                         'name'     => 'p',
         | 
| 112 113 | 
             
                         'children' => [{ '_type' => 'Child', 'name' => 'c1' },
         | 
| 113 | 
            -
                                        { '_type' => 'Child', 'name' => 'c2' }] }
         | 
| 114 | 
            +
                                        { '_type' => 'Child', 'name' => 'c2' },] }
         | 
| 114 115 |  | 
| 115 116 | 
             
                context = viewmodel_class.new_deserialize_context
         | 
| 116 117 | 
             
                pv = viewmodel_class.deserialize_from_view(view, deserialize_context: context)
         | 
| @@ -124,8 +125,8 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 124 125 |  | 
| 125 126 | 
             
              def test_nil_multiple_association
         | 
| 126 127 | 
             
                view = {
         | 
| 127 | 
            -
                   | 
| 128 | 
            -
                   | 
| 128 | 
            +
                  '_type' => 'Model',
         | 
| 129 | 
            +
                  'children' => nil,
         | 
| 129 130 | 
             
                }
         | 
| 130 131 | 
             
                ex = assert_raises(ViewModel::DeserializationError::InvalidSyntax) do
         | 
| 131 132 | 
             
                  viewmodel_class.deserialize_from_view(view)
         | 
| @@ -136,8 +137,8 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 136 137 |  | 
| 137 138 | 
             
              def test_non_array_multiple_association
         | 
| 138 139 | 
             
                view = {
         | 
| 139 | 
            -
                   | 
| 140 | 
            -
                   | 
| 140 | 
            +
                  '_type' => 'Model',
         | 
| 141 | 
            +
                  'children' => { '_type' => 'Child', 'name' => 'c1' },
         | 
| 141 142 | 
             
                }
         | 
| 142 143 | 
             
                ex = assert_raises(ViewModel::DeserializationError::InvalidSyntax) do
         | 
| 143 144 | 
             
                  viewmodel_class.deserialize_from_view(view)
         | 
| @@ -149,7 +150,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 149 150 | 
             
              def test_replace_has_many
         | 
| 150 151 | 
             
                old_children = @model1.children
         | 
| 151 152 |  | 
| 152 | 
            -
                alter_by_view!(viewmodel_class, @model1) do |view,  | 
| 153 | 
            +
                alter_by_view!(viewmodel_class, @model1) do |view, _refs|
         | 
| 153 154 | 
             
                  view['children'] = [{ '_type' => 'Child', 'name' => 'new_child' }]
         | 
| 154 155 | 
             
                end
         | 
| 155 156 |  | 
| @@ -169,7 +170,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 169 170 |  | 
| 170 171 | 
             
                expected_edit_checks = [pv.to_reference,
         | 
| 171 172 | 
             
                                        *old_children.map { |x| ViewModel::Reference.new(child_viewmodel_class, x.id) },
         | 
| 172 | 
            -
                                        *nc.map(&:to_reference)]
         | 
| 173 | 
            +
                                        *nc.map(&:to_reference),]
         | 
| 173 174 |  | 
| 174 175 | 
             
                assert_contains_exactly(expected_edit_checks,
         | 
| 175 176 | 
             
                                        context.valid_edit_refs)
         | 
| @@ -200,7 +201,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 200 201 | 
             
                expected_edit_checks = [pv.to_reference,
         | 
| 201 202 | 
             
                                        ViewModel::Reference.new(child_viewmodel_class, new_child.id),
         | 
| 202 203 | 
             
                                        ViewModel::Reference.new(child_viewmodel_class, old_children.first.id),
         | 
| 203 | 
            -
                                        ViewModel::Reference.new(child_viewmodel_class, old_children.last.id)]
         | 
| 204 | 
            +
                                        ViewModel::Reference.new(child_viewmodel_class, old_children.last.id),]
         | 
| 204 205 |  | 
| 205 206 | 
             
                assert_contains_exactly(expected_edit_checks,
         | 
| 206 207 | 
             
                                        context.valid_edit_refs)
         | 
| @@ -215,7 +216,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 215 216 |  | 
| 216 217 | 
             
              def test_remove_has_many
         | 
| 217 218 | 
             
                old_children = @model1.children
         | 
| 218 | 
            -
                _, context = alter_by_view!(viewmodel_class, @model1) do |view,  | 
| 219 | 
            +
                _, context = alter_by_view!(viewmodel_class, @model1) do |view, _refs|
         | 
| 219 220 | 
             
                  view['children'] = []
         | 
| 220 221 | 
             
                end
         | 
| 221 222 |  | 
| @@ -239,7 +240,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 239 240 | 
             
                                     deserialize_context: context)
         | 
| 240 241 |  | 
| 241 242 | 
             
                expected_edit_checks = [ViewModel::Reference.new(viewmodel_class, @model1.id),
         | 
| 242 | 
            -
                                        ViewModel::Reference.new(child_viewmodel_class, | 
| 243 | 
            +
                                        ViewModel::Reference.new(child_viewmodel_class, c1.id),].to_set
         | 
| 243 244 |  | 
| 244 245 | 
             
                assert_equal(expected_edit_checks,
         | 
| 245 246 | 
             
                             context.valid_edit_refs.to_set)
         | 
| @@ -260,8 +261,8 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 260 261 |  | 
| 261 262 | 
             
                assert_contains_exactly(
         | 
| 262 263 | 
             
                  [ViewModel::Reference.new(viewmodel_class, @model1.id),
         | 
| 263 | 
            -
                   ViewModel::Reference.new(child_viewmodel_class,  c1.id), | 
| 264 | 
            -
                   ViewModel::Reference.new(child_viewmodel_class,  nc.id)], # created child
         | 
| 264 | 
            +
                   ViewModel::Reference.new(child_viewmodel_class,  c1.id), # deleted child
         | 
| 265 | 
            +
                   ViewModel::Reference.new(child_viewmodel_class,  nc.id),], # created child
         | 
| 265 266 | 
             
                  context.valid_edit_refs)
         | 
| 266 267 |  | 
| 267 268 | 
             
                assert_equal([c2, c3, child_model_class.find_by_name('new_c')],
         | 
| @@ -314,7 +315,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 314 315 | 
             
                                     deserialize_context: (context = viewmodel_class.new_deserialize_context))
         | 
| 315 316 |  | 
| 316 317 | 
             
                expected_edit_checks = [ViewModel::Reference.new(viewmodel_class, @model1.id),
         | 
| 317 | 
            -
                                        ViewModel::Reference.new(viewmodel_class, @model2.id)]
         | 
| 318 | 
            +
                                        ViewModel::Reference.new(viewmodel_class, @model2.id),]
         | 
| 318 319 |  | 
| 319 320 | 
             
                assert_contains_exactly(expected_edit_checks, context.valid_edit_refs)
         | 
| 320 321 |  | 
| @@ -335,7 +336,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 335 336 | 
             
                n1 = child_model_class.find_by_name('new1')
         | 
| 336 337 |  | 
| 337 338 | 
             
                expected_edit_checks = [ViewModel::Reference.new(viewmodel_class, @model1.id),
         | 
| 338 | 
            -
                                        ViewModel::Reference.new(child_viewmodel_class, n1.id)]
         | 
| 339 | 
            +
                                        ViewModel::Reference.new(child_viewmodel_class, n1.id),]
         | 
| 339 340 |  | 
| 340 341 | 
             
                assert_contains_exactly(expected_edit_checks, context.valid_edit_refs)
         | 
| 341 342 |  | 
| @@ -351,7 +352,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 351 352 | 
             
                n2 = child_model_class.find_by_name('new2')
         | 
| 352 353 |  | 
| 353 354 | 
             
                expected_edit_checks = [ViewModel::Reference.new(viewmodel_class, @model1.id),
         | 
| 354 | 
            -
                                        ViewModel::Reference.new(child_viewmodel_class, n2.id)]
         | 
| 355 | 
            +
                                        ViewModel::Reference.new(child_viewmodel_class, n2.id),]
         | 
| 355 356 |  | 
| 356 357 | 
             
                assert_contains_exactly(expected_edit_checks, context.valid_edit_refs)
         | 
| 357 358 |  | 
| @@ -366,7 +367,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 366 367 | 
             
                n3 = child_model_class.find_by_name('new3')
         | 
| 367 368 |  | 
| 368 369 | 
             
                expected_edit_checks = [ViewModel::Reference.new(viewmodel_class, @model1.id),
         | 
| 369 | 
            -
                                        ViewModel::Reference.new(child_viewmodel_class, n3.id)]
         | 
| 370 | 
            +
                                        ViewModel::Reference.new(child_viewmodel_class, n3.id),]
         | 
| 370 371 |  | 
| 371 372 | 
             
                assert_contains_exactly(expected_edit_checks, context.valid_edit_refs)
         | 
| 372 373 |  | 
| @@ -377,7 +378,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 377 378 | 
             
              def test_edit_implicit_list_position
         | 
| 378 379 | 
             
                c1, c2, c3 = @model1.children.order(:position).to_a
         | 
| 379 380 |  | 
| 380 | 
            -
                alter_by_view!(viewmodel_class, @model1) do |view,  | 
| 381 | 
            +
                alter_by_view!(viewmodel_class, @model1) do |view, _refs|
         | 
| 381 382 | 
             
                  view['children'].reverse!
         | 
| 382 383 | 
             
                  view['children'].insert(1, { '_type' => 'Child', 'name' => 'new_c' })
         | 
| 383 384 | 
             
                end
         | 
| @@ -388,11 +389,11 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 388 389 |  | 
| 389 390 | 
             
              def test_edit_missing_child
         | 
| 390 391 | 
             
                view = {
         | 
| 391 | 
            -
                   | 
| 392 | 
            -
                   | 
| 393 | 
            -
                                    | 
| 394 | 
            -
                                    | 
| 395 | 
            -
                                 }]
         | 
| 392 | 
            +
                  '_type' => 'Model',
         | 
| 393 | 
            +
                  'children' => [{
         | 
| 394 | 
            +
                                   '_type' => 'Child',
         | 
| 395 | 
            +
                                   'id'    => 9999,
         | 
| 396 | 
            +
                                 }],
         | 
| 396 397 | 
             
                }
         | 
| 397 398 |  | 
| 398 399 | 
             
                ex = assert_raises(ViewModel::DeserializationError::NotFound) do
         | 
| @@ -411,7 +412,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 411 412 | 
             
                view = { '_type'    => 'Model',
         | 
| 412 413 | 
             
                         'name'     => 'new_p',
         | 
| 413 414 | 
             
                         'children' => [moved_child_ref,
         | 
| 414 | 
            -
                                        { '_type' => 'Child', 'name' => 'new' }] }
         | 
| 415 | 
            +
                                        { '_type' => 'Child', 'name' => 'new' },] }
         | 
| 415 416 |  | 
| 416 417 | 
             
                retained_children = old_children - [moved_child]
         | 
| 417 418 | 
             
                release_view = { '_type'    => 'Model',
         | 
| @@ -430,7 +431,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 430 431 |  | 
| 431 432 | 
             
                # child should be added to new parent
         | 
| 432 433 | 
             
                new_children = new_parent.children.order(:position)
         | 
| 433 | 
            -
                assert_equal(%w | 
| 434 | 
            +
                assert_equal(%w[p1c2 new], new_children.map(&:name))
         | 
| 434 435 | 
             
                assert_equal(moved_child, new_children.first)
         | 
| 435 436 | 
             
              end
         | 
| 436 437 |  | 
| @@ -470,7 +471,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 470 471 |  | 
| 471 472 | 
             
                retained_children = old_children - [moved_child]
         | 
| 472 473 | 
             
                release_view = { '_type' => 'Model', 'id' => @model1.id,
         | 
| 473 | 
            -
                                 'children' => retained_children.map { |c| update_hash_for(child_viewmodel_class, c) }}
         | 
| 474 | 
            +
                                 'children' => retained_children.map { |c| update_hash_for(child_viewmodel_class, c) } }
         | 
| 474 475 |  | 
| 475 476 | 
             
                viewmodel_class.deserialize_from_view([view, release_view])
         | 
| 476 477 |  | 
| @@ -482,25 +483,25 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 482 483 |  | 
| 483 484 | 
             
                # child should be added to new parent with valid position
         | 
| 484 485 | 
             
                new_children = @model2.children.order(:position)
         | 
| 485 | 
            -
                assert_equal(%w | 
| 486 | 
            +
                assert_equal(%w[p2c1 p2c2 p1c2], new_children.map(&:name))
         | 
| 486 487 | 
             
                assert_equal(moved_child, new_children.last)
         | 
| 487 488 | 
             
              end
         | 
| 488 489 |  | 
| 489 490 | 
             
              def test_has_many_append_child
         | 
| 490 | 
            -
                viewmodel_class.new(@model1).append_associated(:children, {  | 
| 491 | 
            +
                viewmodel_class.new(@model1).append_associated(:children, { '_type' => 'Child', 'name' => 'new' })
         | 
| 491 492 |  | 
| 492 493 | 
             
                @model1.reload
         | 
| 493 494 |  | 
| 494 495 | 
             
                assert_equal(4, @model1.children.size)
         | 
| 495 496 | 
             
                lc = @model1.children.order(:position).last
         | 
| 496 | 
            -
                assert_equal( | 
| 497 | 
            +
                assert_equal('new', lc.name)
         | 
| 497 498 | 
             
              end
         | 
| 498 499 |  | 
| 499 500 | 
             
              def test_has_many_append_and_update_existing_association
         | 
| 500 501 | 
             
                child = @model1.children[1]
         | 
| 501 502 |  | 
| 502 503 | 
             
                cv = child_viewmodel_class.new(child).to_hash
         | 
| 503 | 
            -
                cv[ | 
| 504 | 
            +
                cv['name'] = 'newname'
         | 
| 504 505 |  | 
| 505 506 | 
             
                viewmodel_class.new(@model1).append_associated(:children, cv)
         | 
| 506 507 |  | 
| @@ -509,28 +510,28 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 509 510 | 
             
                # Child should have been moved to the end (and edited)
         | 
| 510 511 | 
             
                assert_equal(3, @model1.children.size)
         | 
| 511 512 | 
             
                c1, c2, c3 = @model1.children.order(:position)
         | 
| 512 | 
            -
                assert_equal( | 
| 513 | 
            -
                assert_equal( | 
| 513 | 
            +
                assert_equal('p1c1', c1.name)
         | 
| 514 | 
            +
                assert_equal('p1c3', c2.name)
         | 
| 514 515 | 
             
                assert_equal(child, c3)
         | 
| 515 | 
            -
                assert_equal( | 
| 516 | 
            +
                assert_equal('newname', c3.name)
         | 
| 516 517 | 
             
              end
         | 
| 517 518 |  | 
| 518 519 | 
             
              def test_has_many_move_existing_association
         | 
| 519 520 | 
             
                p1c2 = @model1.children[1]
         | 
| 520 521 | 
             
                assert_equal(2, p1c2.position)
         | 
| 521 522 |  | 
| 522 | 
            -
                viewmodel_class.new(@model2).append_associated( | 
| 523 | 
            +
                viewmodel_class.new(@model2).append_associated('children', { '_type' => 'Child', 'id' => p1c2.id })
         | 
| 523 524 |  | 
| 524 525 | 
             
                @model1.reload
         | 
| 525 526 | 
             
                @model2.reload
         | 
| 526 527 |  | 
| 527 528 | 
             
                p1c = @model1.children.order(:position)
         | 
| 528 529 | 
             
                assert_equal(2, p1c.size)
         | 
| 529 | 
            -
                assert_equal([ | 
| 530 | 
            +
                assert_equal(['p1c1', 'p1c3'], p1c.map(&:name))
         | 
| 530 531 |  | 
| 531 532 | 
             
                p2c = @model2.children.order(:position)
         | 
| 532 533 | 
             
                assert_equal(3, p2c.size)
         | 
| 533 | 
            -
                assert_equal([ | 
| 534 | 
            +
                assert_equal(['p2c1', 'p2c2', 'p1c2'], p2c.map(&:name))
         | 
| 534 535 | 
             
                assert_equal(p1c2, p2c[2])
         | 
| 535 536 | 
             
                assert_equal(3, p2c[2].position)
         | 
| 536 537 | 
             
              end
         | 
| @@ -545,8 +546,8 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 545 546 | 
             
                # Child should have been removed
         | 
| 546 547 | 
             
                assert_equal(2, @model1.children.size)
         | 
| 547 548 | 
             
                c1, c2 = @model1.children.order(:position)
         | 
| 548 | 
            -
                assert_equal( | 
| 549 | 
            -
                assert_equal( | 
| 549 | 
            +
                assert_equal('p1c1', c1.name)
         | 
| 550 | 
            +
                assert_equal('p1c3', c2.name)
         | 
| 550 551 |  | 
| 551 552 | 
             
                assert_equal(0, child_model_class.where(id: child.id).size)
         | 
| 552 553 | 
             
              end
         | 
| @@ -555,47 +556,47 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 555 556 | 
             
                child = @model1.children[1]
         | 
| 556 557 |  | 
| 557 558 | 
             
                child_view = child_viewmodel_class.new(child).to_hash
         | 
| 558 | 
            -
                child_view[ | 
| 559 | 
            +
                child_view['name'] = 'changed'
         | 
| 559 560 |  | 
| 560 | 
            -
                view = {  | 
| 561 | 
            -
                          | 
| 562 | 
            -
                          | 
| 561 | 
            +
                view = { '_type' => 'Model',
         | 
| 562 | 
            +
                         'name' => 'new_p',
         | 
| 563 | 
            +
                         'children' => [child_view, { '_type' => 'Child', 'name' => 'new' }] }
         | 
| 563 564 |  | 
| 564 | 
            -
                # TODO this is as awkward here as it is in the application
         | 
| 565 | 
            -
                release_view = {  | 
| 566 | 
            -
                                  | 
| 567 | 
            -
                                  | 
| 568 | 
            -
                                                {  | 
| 565 | 
            +
                # TODO: this is as awkward here as it is in the application
         | 
| 566 | 
            +
                release_view = { '_type' => 'Model',
         | 
| 567 | 
            +
                                 'id' => @model1.id,
         | 
| 568 | 
            +
                                 'children' => [{ '_type' => 'Child', 'id' => @model1.children[0].id },
         | 
| 569 | 
            +
                                                { '_type' => 'Child', 'id' => @model1.children[2].id },] }
         | 
| 569 570 |  | 
| 570 571 | 
             
                pv = viewmodel_class.deserialize_from_view([view, release_view])
         | 
| 571 572 | 
             
                new_parent = pv.first.model
         | 
| 572 573 |  | 
| 573 574 | 
             
                # child should be removed from old parent and positions updated
         | 
| 574 575 | 
             
                @model1.reload
         | 
| 575 | 
            -
                assert_equal(2, @model1.children.size,  | 
| 576 | 
            +
                assert_equal(2, @model1.children.size, 'database has 2 children')
         | 
| 576 577 | 
             
                oc1, oc2 = @model1.children.order(:position)
         | 
| 577 | 
            -
                assert_equal( | 
| 578 | 
            -
                assert_equal( | 
| 578 | 
            +
                assert_equal('p1c1', oc1.name, 'database c1 unchanged')
         | 
| 579 | 
            +
                assert_equal('p1c3', oc2.name, 'database c2 unchanged')
         | 
| 579 580 |  | 
| 580 581 | 
             
                # child should be added to new parent with valid position
         | 
| 581 | 
            -
                assert_equal(2, new_parent.children.size,  | 
| 582 | 
            +
                assert_equal(2, new_parent.children.size, 'viewmodel has 2 children')
         | 
| 582 583 | 
             
                nc1, nc2 = new_parent.children.order(:position)
         | 
| 583 584 | 
             
                assert_equal(child, nc1)
         | 
| 584 | 
            -
                assert_equal( | 
| 585 | 
            -
                assert_equal( | 
| 585 | 
            +
                assert_equal('changed', nc1.name)
         | 
| 586 | 
            +
                assert_equal('new', nc2.name)
         | 
| 586 587 | 
             
              end
         | 
| 587 588 |  | 
| 588 589 | 
             
              def test_move_and_edit_child_to_existing
         | 
| 589 590 | 
             
                old_child = @model1.children[1]
         | 
| 590 591 |  | 
| 591 592 | 
             
                old_child_view = child_viewmodel_class.new(old_child).to_hash
         | 
| 592 | 
            -
                old_child_view[ | 
| 593 | 
            +
                old_child_view['name'] = 'changed'
         | 
| 593 594 | 
             
                view = viewmodel_class.new(@model2).to_hash
         | 
| 594 | 
            -
                view[ | 
| 595 | 
            +
                view['children'] << old_child_view
         | 
| 595 596 |  | 
| 596 | 
            -
                release_view = { | 
| 597 | 
            -
                                 | 
| 598 | 
            -
                                               { | 
| 597 | 
            +
                release_view = { '_type' => 'Model', 'id' => @model1.id,
         | 
| 598 | 
            +
                                'children' => [{ '_type' => 'Child', 'id' => @model1.children[0].id },
         | 
| 599 | 
            +
                                               { '_type' => 'Child', 'id' => @model1.children[2].id },] }
         | 
| 599 600 |  | 
| 600 601 | 
             
                viewmodel_class.deserialize_from_view([view, release_view])
         | 
| 601 602 |  | 
| @@ -606,46 +607,46 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 606 607 | 
             
                assert_equal(2, @model1.children.size)
         | 
| 607 608 | 
             
                oc1, oc2 = @model1.children.order(:position)
         | 
| 608 609 |  | 
| 609 | 
            -
                assert_equal( | 
| 610 | 
            -
                assert_equal( | 
| 610 | 
            +
                assert_equal('p1c1', oc1.name)
         | 
| 611 | 
            +
                assert_equal('p1c3', oc2.name)
         | 
| 611 612 |  | 
| 612 613 | 
             
                # child should be added to new parent with valid position
         | 
| 613 614 | 
             
                assert_equal(3, @model2.children.size)
         | 
| 614 615 | 
             
                nc1, _, nc3 = @model2.children.order(:position)
         | 
| 615 | 
            -
                assert_equal( | 
| 616 | 
            +
                assert_equal('p2c1', nc1.name)
         | 
| 616 617 |  | 
| 617 | 
            -
                assert_equal( | 
| 618 | 
            +
                assert_equal('p2c1', nc1.name)
         | 
| 618 619 |  | 
| 619 620 | 
             
                assert_equal(old_child, nc3)
         | 
| 620 | 
            -
                assert_equal( | 
| 621 | 
            +
                assert_equal('changed', nc3.name)
         | 
| 621 622 | 
             
              end
         | 
| 622 623 |  | 
| 623 624 | 
             
              def test_functional_update_append
         | 
| 624 625 | 
             
                children_before = @model1.children.order(:position).pluck(:id)
         | 
| 625 626 | 
             
                fupdate = build_fupdate do
         | 
| 626 627 | 
             
                  append([{ '_type' => 'Child' },
         | 
| 627 | 
            -
                          { '_type' => 'Child' }])
         | 
| 628 | 
            +
                          { '_type' => 'Child' },])
         | 
| 628 629 | 
             
                end
         | 
| 629 630 |  | 
| 630 | 
            -
                append_view | 
| 631 | 
            +
                append_view = { '_type' => 'Model',
         | 
| 631 632 | 
             
                                    'id'       => @model1.id,
         | 
| 632 633 | 
             
                                    'children' => fupdate }
         | 
| 633 634 |  | 
| 634 | 
            -
                result | 
| 635 | 
            +
                result = viewmodel_class.deserialize_from_view(append_view)
         | 
| 635 636 | 
             
                @model1.reload
         | 
| 636 637 |  | 
| 637 | 
            -
                created_children = result.children[-2,2].map(&:id)
         | 
| 638 | 
            +
                created_children = result.children[-2, 2].map(&:id)
         | 
| 638 639 |  | 
| 639 640 | 
             
                assert_equal(children_before + created_children,
         | 
| 640 641 | 
             
                             @model1.children.order(:position).pluck(:id))
         | 
| 641 642 | 
             
              end
         | 
| 642 643 |  | 
| 643 644 | 
             
              def test_functional_update_append_before_mid
         | 
| 644 | 
            -
                c1, c2, c3 | 
| 645 | 
            +
                c1, c2, c3 = @model1.children.order(:position)
         | 
| 645 646 |  | 
| 646 647 | 
             
                fupdate = build_fupdate do
         | 
| 647 648 | 
             
                  append([{ '_type' => 'Child', 'name' => 'new c1' },
         | 
| 648 | 
            -
                          { '_type' => 'Child', 'name' => 'new c2' }],
         | 
| 649 | 
            +
                          { '_type' => 'Child', 'name' => 'new c2' },],
         | 
| 649 650 | 
             
                         before: { '_type' => 'Child', 'id' => c2.id })
         | 
| 650 651 | 
             
                end
         | 
| 651 652 |  | 
| @@ -660,7 +661,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 660 661 | 
             
              end
         | 
| 661 662 |  | 
| 662 663 | 
             
              def test_functional_update_append_before_reorder
         | 
| 663 | 
            -
                c1, c2, c3 | 
| 664 | 
            +
                c1, c2, c3 = @model1.children.order(:position)
         | 
| 664 665 |  | 
| 665 666 | 
             
                fupdate = build_fupdate do
         | 
| 666 667 | 
             
                  append([{ '_type' => 'Child', 'id' => c3.id }],
         | 
| @@ -678,11 +679,11 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 678 679 | 
             
              end
         | 
| 679 680 |  | 
| 680 681 | 
             
              def test_functional_update_append_before_beginning
         | 
| 681 | 
            -
                c1, c2, c3 | 
| 682 | 
            +
                c1, c2, c3 = @model1.children.order(:position)
         | 
| 682 683 |  | 
| 683 684 | 
             
                fupdate = build_fupdate do
         | 
| 684 685 | 
             
                  append([{ '_type' => 'Child', 'name' => 'new c1' },
         | 
| 685 | 
            -
                          { '_type' => 'Child', 'name' => 'new c2' }],
         | 
| 686 | 
            +
                          { '_type' => 'Child', 'name' => 'new c2' },],
         | 
| 686 687 | 
             
                         before: { '_type' => 'Child', 'id' => c1.id })
         | 
| 687 688 | 
             
                end
         | 
| 688 689 |  | 
| @@ -702,7 +703,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 702 703 |  | 
| 703 704 | 
             
                fupdate = build_fupdate do
         | 
| 704 705 | 
             
                  append([{ '_type' => 'Child', 'name' => 'new c1' },
         | 
| 705 | 
            -
                          { '_type' => 'Child', 'name' => 'new c2' }],
         | 
| 706 | 
            +
                          { '_type' => 'Child', 'name' => 'new c2' },],
         | 
| 706 707 | 
             
                         before: { '_type' => 'Child', 'id' => c2.id })
         | 
| 707 708 | 
             
                end
         | 
| 708 709 |  | 
| @@ -715,11 +716,11 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 715 716 | 
             
              end
         | 
| 716 717 |  | 
| 717 718 | 
             
              def test_functional_update_append_after_mid
         | 
| 718 | 
            -
                c1, c2, c3 | 
| 719 | 
            +
                c1, c2, c3 = @model1.children.order(:position)
         | 
| 719 720 |  | 
| 720 721 | 
             
                fupdate = build_fupdate do
         | 
| 721 722 | 
             
                  append([{ '_type' => 'Child', 'name' => 'new c1' },
         | 
| 722 | 
            -
                          { '_type' => 'Child', 'name' => 'new c2' }],
         | 
| 723 | 
            +
                          { '_type' => 'Child', 'name' => 'new c2' },],
         | 
| 723 724 | 
             
                         after: { '_type' => 'Child', 'id' => c2.id })
         | 
| 724 725 | 
             
                end
         | 
| 725 726 |  | 
| @@ -734,12 +735,12 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 734 735 | 
             
              end
         | 
| 735 736 |  | 
| 736 737 | 
             
              def test_functional_update_append_after_end
         | 
| 737 | 
            -
                c1, c2, c3 | 
| 738 | 
            +
                c1, c2, c3 = @model1.children.order(:position)
         | 
| 738 739 |  | 
| 739 740 | 
             
                fupdate = build_fupdate do
         | 
| 740 741 | 
             
                  append([{ '_type' => 'Child', 'name' => 'new c1' },
         | 
| 741 | 
            -
                          { '_type' => 'Child', 'name' => 'new c2' }],
         | 
| 742 | 
            -
                         after: { '_type' => 'Child', 'id' => c3.id | 
| 742 | 
            +
                          { '_type' => 'Child', 'name' => 'new c2' },],
         | 
| 743 | 
            +
                         after: { '_type' => 'Child', 'id' => c3.id })
         | 
| 743 744 | 
             
                end
         | 
| 744 745 |  | 
| 745 746 | 
             
                append_view = { '_type'    => 'Model',
         | 
| @@ -758,9 +759,9 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 758 759 |  | 
| 759 760 | 
             
                fupdate = build_fupdate do
         | 
| 760 761 | 
             
                  append([{ '_type' => 'Child', 'name' => 'new c1' },
         | 
| 761 | 
            -
                          { '_type' => 'Child', 'name' => 'new c2' }],
         | 
| 762 | 
            +
                          { '_type' => 'Child', 'name' => 'new c2' },],
         | 
| 762 763 | 
             
                         after: { '_type' => 'Child', 'id' => c2.id },
         | 
| 763 | 
            -
             | 
| 764 | 
            +
                        )
         | 
| 764 765 | 
             
                end
         | 
| 765 766 |  | 
| 766 767 | 
             
                append_view = { '_type'    => 'Model',
         | 
| @@ -778,7 +779,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 778 779 | 
             
                  remove([{ '_type' => 'Child', 'id' => c2_id }])
         | 
| 779 780 | 
             
                end
         | 
| 780 781 |  | 
| 781 | 
            -
                remove_view | 
| 782 | 
            +
                remove_view = { '_type' => 'Model',
         | 
| 782 783 | 
             
                                        'id'       => @model1.id,
         | 
| 783 784 | 
             
                                        'children' => fupdate }
         | 
| 784 785 | 
             
                viewmodel_class.deserialize_from_view(remove_view)
         | 
| @@ -788,7 +789,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 788 789 | 
             
              end
         | 
| 789 790 |  | 
| 790 791 | 
             
              def test_functional_update_remove_failure
         | 
| 791 | 
            -
                c_id | 
| 792 | 
            +
                c_id = @model1.children.pluck(:id).first
         | 
| 792 793 |  | 
| 793 794 | 
             
                fupdate = build_fupdate do
         | 
| 794 795 | 
             
                  remove([{ '_type' => 'Child',
         | 
| @@ -816,7 +817,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 816 817 | 
             
                            'name'  => 'Functionally Updated Child' }])
         | 
| 817 818 | 
             
                end
         | 
| 818 819 |  | 
| 819 | 
            -
                update_view | 
| 820 | 
            +
                update_view = { '_type' => 'Model',
         | 
| 820 821 | 
             
                                        'id'       => @model1.id,
         | 
| 821 822 | 
             
                                        'children' => fupdate }
         | 
| 822 823 | 
             
                viewmodel_class.deserialize_from_view(update_view)
         | 
| @@ -866,7 +867,7 @@ class ViewModel::ActiveRecord::HasManyTest < ActiveSupport::TestCase | |
| 866 867 |  | 
| 867 868 | 
             
              describe 'owned reference children' do
         | 
| 868 869 | 
             
                def child_attributes
         | 
| 869 | 
            -
                  super.merge(viewmodel: ->( | 
| 870 | 
            +
                  super.merge(viewmodel: ->(_v) { root! })
         | 
| 870 871 | 
             
                end
         | 
| 871 872 |  | 
| 872 873 | 
             
                def new_model
         |