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
| @@ -21,6 +21,7 @@ class ViewModel::TraversalContext | |
| 21 21 | 
             
              end
         | 
| 22 22 |  | 
| 23 23 | 
             
              attr_reader :shared_context
         | 
| 24 | 
            +
             | 
| 24 25 | 
             
              delegate :access_control, :callbacks, to: :shared_context
         | 
| 25 26 |  | 
| 26 27 | 
             
              def self.new_child(*args)
         | 
| @@ -118,7 +119,7 @@ class ViewModel::TraversalContext | |
| 118 119 |  | 
| 119 120 | 
             
              def nearest_root_viewmodel
         | 
| 120 121 | 
             
                if root?
         | 
| 121 | 
            -
                  raise RuntimeError.new( | 
| 122 | 
            +
                  raise RuntimeError.new('Attempted to find nearest root from a root context. This is probably not what you wanted.')
         | 
| 122 123 | 
             
                elsif parent_context.root?
         | 
| 123 124 | 
             
                  parent_viewmodel
         | 
| 124 125 | 
             
                else
         | 
    
        data/nix/gem/generate.rb
    CHANGED
    
    | @@ -7,6 +7,7 @@ | |
| 7 7 | 
             
            # This workaround is from https://github.com/manveru/bundix/issues/10#issuecomment-405879379
         | 
| 8 8 |  | 
| 9 9 | 
             
            require 'shellwords'
         | 
| 10 | 
            +
            require 'uri'
         | 
| 10 11 |  | 
| 11 12 | 
             
            def sh(*args)
         | 
| 12 13 | 
             
              warn args.shelljoin
         | 
| @@ -20,7 +21,7 @@ require 'bundler' | |
| 20 21 |  | 
| 21 22 | 
             
            lockfile = Bundler::LockfileParser.new(File.read('Gemfile.lock'))
         | 
| 22 23 | 
             
            gems = lockfile.specs.select { |spec| spec.source.is_a?(Bundler::Source::Rubygems) }
         | 
| 23 | 
            -
            sources =  | 
| 24 | 
            +
            sources = gems.map(&:source).flat_map(&:remotes).uniq
         | 
| 24 25 |  | 
| 25 26 | 
             
            FileUtils.mkdir_p 'nix/gem'
         | 
| 26 27 | 
             
            Dir.chdir 'nix/gem' do
         | 
    
        data/shell.nix
    CHANGED
    
    | @@ -1,10 +1,15 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            { pkgs ? import <nixpkgs> {} }:
         | 
| 2 2 |  | 
| 3 | 
            +
            with pkgs;
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            let
         | 
| 6 | 
            +
              dependencies = import ./nix/dependencies.nix { inherit pkgs; };
         | 
| 7 | 
            +
            in
         | 
| 3 8 | 
             
            (bundlerEnv {
         | 
| 4 9 | 
             
              name = "iknow-view-models-shell";
         | 
| 5 10 | 
             
              gemdir = ./nix/gem;
         | 
| 6 11 |  | 
| 7 | 
            -
              gemConfig = (defaultGemConfig.override {  | 
| 12 | 
            +
              gemConfig = (defaultGemConfig.override { inherit (dependencies) postgresql; });
         | 
| 8 13 |  | 
| 9 | 
            -
               | 
| 14 | 
            +
              inherit (dependencies) ruby;
         | 
| 10 15 | 
             
            }).env
         | 
    
        data/test/.rubocop.yml
    ADDED
    
    | @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            inherit_from: ../.rubocop.yml
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Lint/NestedMethodDefinition:
         | 
| 4 | 
            +
              Enabled: false
         | 
| 5 | 
            +
            Lint/ConstantDefinitionInBlock:
         | 
| 6 | 
            +
              Enabled: false
         | 
| 7 | 
            +
            Layout/MultilineBlockLayout:
         | 
| 8 | 
            +
              Enabled: false
         | 
| 9 | 
            +
            Layout/HashAlignment:
         | 
| 10 | 
            +
              Enabled: false
         | 
| 11 | 
            +
            Layout/BlockEndNewline:
         | 
| 12 | 
            +
              Enabled: false
         | 
| 13 | 
            +
            Style/Semicolon:
         | 
| 14 | 
            +
              Enabled: false
         | 
| @@ -1,19 +1,22 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
            require "view_model/active_record"
         | 
| 5 | 
            -
            require "view_model/active_record/controller"
         | 
| 3 | 
            +
            require_relative 'test_access_control'
         | 
| 6 4 |  | 
| 7 | 
            -
            require  | 
| 5 | 
            +
            require 'iknow_view_models'
         | 
| 6 | 
            +
            require 'view_model/active_record'
         | 
| 7 | 
            +
            require 'view_model/active_record/controller'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            require 'acts_as_manual_list'
         | 
| 8 10 |  | 
| 9 11 | 
             
            db_config_path = File.join(File.dirname(__FILE__), '../config/database.yml')
         | 
| 10 | 
            -
            db_config = YAML. | 
| 11 | 
            -
            raise  | 
| 12 | 
            -
             | 
| 12 | 
            +
            db_config = YAML.safe_load(File.open(db_config_path))
         | 
| 13 | 
            +
            raise 'Test database configuration missing' unless db_config['test']
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            ActiveRecord::Base.establish_connection(db_config['test'])
         | 
| 13 16 |  | 
| 14 17 | 
             
            # Remove test tables if any exist
         | 
| 15 18 | 
             
            %w[labels parents children targets poly_ones poly_twos owners
         | 
| 16 | 
            -
             | 
| 19 | 
            +
               grand_parents categories tags parents_tags].each do |t|
         | 
| 17 20 | 
             
              ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{t} CASCADE")
         | 
| 18 21 | 
             
            end
         | 
| 19 22 |  | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'active_support'
         | 
| 2 4 | 
             
            require 'minitest/hooks'
         | 
| 3 5 |  | 
| @@ -10,7 +12,7 @@ unless ViewModel::Config.configured? | |
| 10 12 | 
             
              end
         | 
| 11 13 | 
             
            end
         | 
| 12 14 |  | 
| 13 | 
            -
            require_relative 'query_logging | 
| 15 | 
            +
            require_relative 'query_logging'
         | 
| 14 16 |  | 
| 15 17 | 
             
            ActiveSupport::TestCase.include(Minitest::Hooks)
         | 
| 16 18 |  | 
| @@ -91,7 +93,7 @@ module ARVMTestUtilities | |
| 91 93 |  | 
| 92 94 | 
             
              def enable_logging!
         | 
| 93 95 | 
             
                if ENV['DEBUG']
         | 
| 94 | 
            -
                  ActiveRecord::Base.logger = Logger.new( | 
| 96 | 
            +
                  ActiveRecord::Base.logger = Logger.new($stderr)
         | 
| 95 97 | 
             
                end
         | 
| 96 98 | 
             
              end
         | 
| 97 99 |  | 
| @@ -170,7 +172,7 @@ module ARVMTestUtilities | |
| 170 172 | 
             
                    {
         | 
| 171 173 | 
             
                      ViewModel::ActiveRecord::TYPE_ATTRIBUTE   => type::NAME,
         | 
| 172 174 | 
             
                      ViewModel::ActiveRecord::VALUES_ATTRIBUTE => values,
         | 
| 173 | 
            -
                    }.merge(rest.transform_keys(&:to_s))
         | 
| 175 | 
            +
                    }.merge(rest.transform_keys(&:to_s)),
         | 
| 174 176 | 
             
                  )
         | 
| 175 177 | 
             
                end
         | 
| 176 178 |  | 
| @@ -1,11 +1,15 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            require "view_model/active_record"
         | 
| 3 | 
            -
            require "view_model/active_record/controller"
         | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 4 2 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 3 | 
            +
            require 'iknow_view_models'
         | 
| 4 | 
            +
            require 'view_model/active_record'
         | 
| 5 | 
            +
            require 'view_model/active_record/controller'
         | 
| 7 6 |  | 
| 8 | 
            -
             | 
| 7 | 
            +
            require_relative '../helpers/arvm_test_utilities'
         | 
| 8 | 
            +
            require_relative '../helpers/arvm_test_models'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            require 'action_controller'
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            require 'acts_as_manual_list'
         | 
| 9 13 |  | 
| 10 14 | 
             
            # models for ARVM controller test
         | 
| 11 15 | 
             
            module ControllerTestModels
         | 
| @@ -79,11 +83,25 @@ module ControllerTestModels | |
| 79 83 | 
             
                  end
         | 
| 80 84 | 
             
                  define_viewmodel do
         | 
| 81 85 | 
             
                    root!
         | 
| 86 | 
            +
                    self.schema_version = 2
         | 
| 87 | 
            +
             | 
| 82 88 | 
             
                    attributes   :name
         | 
| 83 89 | 
             
                    associations :label, :target
         | 
| 84 90 | 
             
                    association  :children
         | 
| 85 91 | 
             
                    association  :poly, viewmodels: [:PolyOne, :PolyTwo]
         | 
| 86 92 | 
             
                    association  :category, external: true
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    migrates from: 1, to: 2 do
         | 
| 95 | 
            +
                      up do |view, _refs|
         | 
| 96 | 
            +
                        if view.has_key?('old_name')
         | 
| 97 | 
            +
                          view['name'] = view.delete('old_name')
         | 
| 98 | 
            +
                        end
         | 
| 99 | 
            +
                      end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                      down do |view, _refs|
         | 
| 102 | 
            +
                        view['old_name'] = view.delete('name')
         | 
| 103 | 
            +
                      end
         | 
| 104 | 
            +
                    end
         | 
| 87 105 | 
             
                  end
         | 
| 88 106 | 
             
                end
         | 
| 89 107 |  | 
| @@ -129,27 +147,25 @@ end | |
| 129 147 |  | 
| 130 148 | 
             
            ## Dummy Rails Controllers
         | 
| 131 149 | 
             
            class DummyController
         | 
| 132 | 
            -
              attr_reader :params, :status
         | 
| 150 | 
            +
              attr_reader :params, :headers, :status
         | 
| 133 151 |  | 
| 134 | 
            -
              def initialize( | 
| 135 | 
            -
                 | 
| 136 | 
            -
                @ | 
| 152 | 
            +
              def initialize(headers: {}, params: {})
         | 
| 153 | 
            +
                @params = ActionController::Parameters.new(params)
         | 
| 154 | 
            +
                @headers = ActionDispatch::Http::Headers.from_hash({}).merge!(headers)
         | 
| 137 155 | 
             
                @status = 200
         | 
| 138 156 | 
             
              end
         | 
| 139 157 |  | 
| 140 158 | 
             
              def invoke(method)
         | 
| 141 | 
            -
                 | 
| 142 | 
            -
             | 
| 143 | 
            -
                 | 
| 144 | 
            -
             | 
| 145 | 
            -
             | 
| 146 | 
            -
                   | 
| 147 | 
            -
             | 
| 148 | 
            -
                   | 
| 149 | 
            -
             | 
| 150 | 
            -
                   | 
| 151 | 
            -
                    self.instance_exec(ex, &handler)
         | 
| 152 | 
            -
                  end
         | 
| 159 | 
            +
                self.public_send(method)
         | 
| 160 | 
            +
              rescue StandardError => ex
         | 
| 161 | 
            +
                handler = self.class.rescue_block(ex.class)
         | 
| 162 | 
            +
                case handler
         | 
| 163 | 
            +
                when nil
         | 
| 164 | 
            +
                  raise
         | 
| 165 | 
            +
                when Symbol
         | 
| 166 | 
            +
                  self.send(handler, ex)
         | 
| 167 | 
            +
                when Proc
         | 
| 168 | 
            +
                  self.instance_exec(ex, &handler)
         | 
| 153 169 | 
             
                end
         | 
| 154 170 | 
             
              end
         | 
| 155 171 |  | 
| @@ -169,7 +185,8 @@ class DummyController | |
| 169 185 | 
             
              end
         | 
| 170 186 |  | 
| 171 187 | 
             
              def json_response
         | 
| 172 | 
            -
                raise  | 
| 188 | 
            +
                raise 'Not a JSON response' unless @content_type == 'application/json'
         | 
| 189 | 
            +
             | 
| 173 190 | 
             
                @response_body
         | 
| 174 191 | 
             
              end
         | 
| 175 192 |  | 
| @@ -177,9 +194,15 @@ class DummyController | |
| 177 194 | 
             
                JSON.parse(json_response)
         | 
| 178 195 | 
             
              end
         | 
| 179 196 |  | 
| 197 | 
            +
              # for request.params and request.headers
         | 
| 198 | 
            +
              def request
         | 
| 199 | 
            +
                self
         | 
| 200 | 
            +
              end
         | 
| 201 | 
            +
             | 
| 180 202 | 
             
              class << self
         | 
| 181 203 | 
             
                def inherited(subclass)
         | 
| 182 204 | 
             
                  subclass.initialize_rescue_blocks
         | 
| 205 | 
            +
                  super
         | 
| 183 206 | 
             
                end
         | 
| 184 207 |  | 
| 185 208 | 
             
                def initialize_rescue_blocks
         | 
| @@ -191,11 +214,10 @@ class DummyController | |
| 191 214 | 
             
                end
         | 
| 192 215 |  | 
| 193 216 | 
             
                def rescue_block(type)
         | 
| 194 | 
            -
                  @rescue_blocks.to_a.reverse.detect { |btype,  | 
| 217 | 
            +
                  @rescue_blocks.to_a.reverse.detect { |btype, _h| type <= btype }.try(&:last)
         | 
| 195 218 | 
             
                end
         | 
| 196 219 |  | 
| 197 | 
            -
                def etag(*)
         | 
| 198 | 
            -
                end
         | 
| 220 | 
            +
                def etag(*); end
         | 
| 199 221 | 
             
              end
         | 
| 200 222 | 
             
            end
         | 
| 201 223 |  | 
| @@ -216,14 +238,15 @@ end | |
| 216 238 |  | 
| 217 239 | 
             
            module CallbackTracing
         | 
| 218 240 | 
             
              attr_reader :callback_tracer
         | 
| 241 | 
            +
             | 
| 219 242 | 
             
              delegate :hook_trace, to: :callback_tracer
         | 
| 220 243 |  | 
| 221 | 
            -
              def new_deserialize_context(** | 
| 244 | 
            +
              def new_deserialize_context(**_args)
         | 
| 222 245 | 
             
                @callback_tracer ||= CallbackTracer.new
         | 
| 223 246 | 
             
                super(callbacks: [@callback_tracer])
         | 
| 224 247 | 
             
              end
         | 
| 225 248 |  | 
| 226 | 
            -
              def new_serialize_context(** | 
| 249 | 
            +
              def new_serialize_context(**_args)
         | 
| 227 250 | 
             
                @callback_tracer ||= CallbackTracer.new
         | 
| 228 251 | 
             
                super(callbacks: [@callback_tracer])
         | 
| 229 252 | 
             
              end
         | 
| @@ -233,14 +256,14 @@ module ControllerTestControllers | |
| 233 256 | 
             
              def before_all
         | 
| 234 257 | 
             
                super
         | 
| 235 258 |  | 
| 236 | 
            -
                Class.new(DummyController) do | | 
| 259 | 
            +
                Class.new(DummyController) do |_c|
         | 
| 237 260 | 
             
                  Object.const_set(:ParentController, self)
         | 
| 238 261 | 
             
                  include ViewModel::ActiveRecord::Controller
         | 
| 239 262 | 
             
                  include CallbackTracing
         | 
| 240 263 | 
             
                  self.access_control = ViewModel::AccessControl::Open
         | 
| 241 264 | 
             
                end
         | 
| 242 265 |  | 
| 243 | 
            -
                Class.new(DummyController) do | | 
| 266 | 
            +
                Class.new(DummyController) do |_c|
         | 
| 244 267 | 
             
                  Object.const_set(:ChildController, self)
         | 
| 245 268 | 
             
                  include ViewModel::ActiveRecord::Controller
         | 
| 246 269 | 
             
                  include CallbackTracing
         | 
| @@ -248,7 +271,7 @@ module ControllerTestControllers | |
| 248 271 | 
             
                  nested_in :parent, as: :children
         | 
| 249 272 | 
             
                end
         | 
| 250 273 |  | 
| 251 | 
            -
                Class.new(DummyController) do | | 
| 274 | 
            +
                Class.new(DummyController) do |_c|
         | 
| 252 275 | 
             
                  Object.const_set(:LabelController, self)
         | 
| 253 276 | 
             
                  include ViewModel::ActiveRecord::Controller
         | 
| 254 277 | 
             
                  include CallbackTracing
         | 
| @@ -256,7 +279,7 @@ module ControllerTestControllers | |
| 256 279 | 
             
                  nested_in :parent, as: :label
         | 
| 257 280 | 
             
                end
         | 
| 258 281 |  | 
| 259 | 
            -
                Class.new(DummyController) do | | 
| 282 | 
            +
                Class.new(DummyController) do |_c|
         | 
| 260 283 | 
             
                  Object.const_set(:TargetController, self)
         | 
| 261 284 | 
             
                  include ViewModel::ActiveRecord::Controller
         | 
| 262 285 | 
             
                  include CallbackTracing
         | 
| @@ -14,6 +14,7 @@ module MiniTest::Assertions | |
| 14 14 |  | 
| 15 15 | 
             
                def result
         | 
| 16 16 | 
             
                  return false unless @actual.respond_to? :to_a
         | 
| 17 | 
            +
             | 
| 17 18 | 
             
                  @extra_items = difference_between_enumerators(@actual, @expected)
         | 
| 18 19 | 
             
                  @missing_items = difference_between_enumerators(@expected, @actual)
         | 
| 19 20 | 
             
                  @extra_items.empty? & @missing_items.empty?
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            # Test mixin that allows queries executed in a block to be introspected.
         | 
| 2 4 | 
             
            #
         | 
| 3 5 | 
             
            # Code run within a `log_queries` block will collect data. Collected data is
         | 
| @@ -9,7 +11,6 @@ | |
| 9 11 | 
             
            require 'active_support/subscriber'
         | 
| 10 12 |  | 
| 11 13 | 
             
            module QueryLogging
         | 
| 12 | 
            -
             | 
| 13 14 | 
             
              # ActiveRecord integration
         | 
| 14 15 | 
             
              class QueryLogger < ActiveSupport::Subscriber
         | 
| 15 16 | 
             
                @log              = false
         | 
| @@ -1,5 +1,6 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require 'iknow_view_models'
         | 
| 3 4 |  | 
| 4 5 | 
             
            class TestAccessControl < ViewModel::AccessControl
         | 
| 5 6 | 
             
              attr_accessor :editable_checks, :visible_checks
         | 
| @@ -41,13 +42,14 @@ class TestAccessControl < ViewModel::AccessControl | |
| 41 42 | 
             
              def valid_edit_changes(ref)
         | 
| 42 43 | 
             
                all = all_valid_edit_changes(ref)
         | 
| 43 44 | 
             
                raise "Expected single change for ref '#{ref}'; found #{all}" unless all.size == 1
         | 
| 45 | 
            +
             | 
| 44 46 | 
             
                all.first
         | 
| 45 47 | 
             
              end
         | 
| 46 48 |  | 
| 47 49 | 
             
              def all_valid_edit_changes(ref)
         | 
| 48 50 | 
             
                @valid_edit_checks
         | 
| 49 | 
            -
                  .select { | | 
| 50 | 
            -
                  .map    { |_cref, | 
| 51 | 
            +
                  .select { |cref, _changes| cref == ref }
         | 
| 52 | 
            +
                  .map    { |_cref, changes| changes }
         | 
| 51 53 | 
             
              end
         | 
| 52 54 |  | 
| 53 55 | 
             
              def was_edited?(ref)
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'view_model'
         | 
| 2 4 | 
             
            require 'view_model/test_helpers'
         | 
| 3 5 |  | 
| @@ -77,7 +79,7 @@ module ViewModelSpecHelpers | |
| 77 79 | 
             
                  ViewModel::TestHelpers::ARVMBuilder::Spec.new(
         | 
| 78 80 | 
             
                    schema:    ->(t) { t.string :name },
         | 
| 79 81 | 
             
                    model:     ->(m) {},
         | 
| 80 | 
            -
                    viewmodel: ->( | 
| 82 | 
            +
                    viewmodel: ->(_v) { root!; attribute :name },
         | 
| 81 83 | 
             
                  )
         | 
| 82 84 | 
             
                end
         | 
| 83 85 |  | 
| @@ -85,7 +87,7 @@ module ViewModelSpecHelpers | |
| 85 87 | 
             
                  ViewModel::TestHelpers::ARVMBuilder::Spec.new(
         | 
| 86 88 | 
             
                    schema:    ->(t) { t.string :name },
         | 
| 87 89 | 
             
                    model:     ->(m) {},
         | 
| 88 | 
            -
                    viewmodel: ->( | 
| 90 | 
            +
                    viewmodel: ->(_v) { attribute :name },
         | 
| 89 91 | 
             
                  )
         | 
| 90 92 | 
             
                end
         | 
| 91 93 |  | 
| @@ -120,12 +122,12 @@ module ViewModelSpecHelpers | |
| 120 122 | 
             
                def model_attributes
         | 
| 121 123 | 
             
                  f = subject_association_features
         | 
| 122 124 | 
             
                  super.merge(schema:    ->(t) { t.references :child, foreign_key: true },
         | 
| 123 | 
            -
                              model:     ->( | 
| 124 | 
            -
                              viewmodel: ->( | 
| 125 | 
            +
                              model:     ->(_m) { belongs_to :child, inverse_of: :model, dependent: :destroy },
         | 
| 126 | 
            +
                              viewmodel: ->(_v) { association :child, **f })
         | 
| 125 127 | 
             
                end
         | 
| 126 128 |  | 
| 127 129 | 
             
                def child_attributes
         | 
| 128 | 
            -
                  super.merge(model: ->( | 
| 130 | 
            +
                  super.merge(model: ->(_m) { has_one :model, inverse_of: :child })
         | 
| 129 131 | 
             
                end
         | 
| 130 132 |  | 
| 131 133 | 
             
                # parent depends on child, ensure it's touched first
         | 
| @@ -139,11 +141,76 @@ module ViewModelSpecHelpers | |
| 139 141 | 
             
                end
         | 
| 140 142 | 
             
              end
         | 
| 141 143 |  | 
| 144 | 
            +
              module ParentAndBelongsToChildWithMigration
         | 
| 145 | 
            +
                extend ActiveSupport::Concern
         | 
| 146 | 
            +
                include ViewModelSpecHelpers::ParentAndBelongsToChild
         | 
| 147 | 
            +
                def model_attributes
         | 
| 148 | 
            +
                  super.merge(
         | 
| 149 | 
            +
                    schema: ->(t) { t.integer :new_field, default: 1, null: false },
         | 
| 150 | 
            +
                    viewmodel: ->(_v) {
         | 
| 151 | 
            +
                      self.schema_version = 4
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                      attribute :new_field
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                      # add: old_field (one-way)
         | 
| 156 | 
            +
                      migrates from: 1, to: 2 do
         | 
| 157 | 
            +
                        down do |view, _refs|
         | 
| 158 | 
            +
                          view.delete('old_field')
         | 
| 159 | 
            +
                        end
         | 
| 160 | 
            +
                      end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                      # rename: old_field -> mid_field
         | 
| 163 | 
            +
                      migrates from: 2, to: 3 do
         | 
| 164 | 
            +
                        up do |view, _refs|
         | 
| 165 | 
            +
                          if view.has_key?('old_field')
         | 
| 166 | 
            +
                            view['mid_field'] = view.delete('old_field') + 1
         | 
| 167 | 
            +
                          end
         | 
| 168 | 
            +
                        end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                        down do |view, _refs|
         | 
| 171 | 
            +
                          view['old_field'] = view.delete('mid_field') - 1
         | 
| 172 | 
            +
                        end
         | 
| 173 | 
            +
                      end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                      # rename: mid_field -> new_field
         | 
| 176 | 
            +
                      migrates from: 3, to: 4 do
         | 
| 177 | 
            +
                        up do |view, _refs|
         | 
| 178 | 
            +
                          if view.has_key?('mid_field')
         | 
| 179 | 
            +
                            view['new_field'] = view.delete('mid_field') + 1
         | 
| 180 | 
            +
                          end
         | 
| 181 | 
            +
                        end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                        down do |view, _refs|
         | 
| 184 | 
            +
                          view['mid_field'] = view.delete('new_field') - 1
         | 
| 185 | 
            +
                        end
         | 
| 186 | 
            +
                      end
         | 
| 187 | 
            +
                    })
         | 
| 188 | 
            +
                end
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                def child_attributes
         | 
| 191 | 
            +
                  super.merge(
         | 
| 192 | 
            +
                    viewmodel: ->(_v) {
         | 
| 193 | 
            +
                      self.schema_version = 3
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                      # delete: former_field
         | 
| 196 | 
            +
                      migrates from: 2, to: 3 do
         | 
| 197 | 
            +
                        up do |view, _refs|
         | 
| 198 | 
            +
                          view.delete('former_field')
         | 
| 199 | 
            +
                        end
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                        down do |view, _refs|
         | 
| 202 | 
            +
                          view['former_field'] = 'reconstructed'
         | 
| 203 | 
            +
                        end
         | 
| 204 | 
            +
                      end
         | 
| 205 | 
            +
                    })
         | 
| 206 | 
            +
                end
         | 
| 207 | 
            +
              end
         | 
| 208 | 
            +
             | 
| 142 209 | 
             
              module ParentAndSharedBelongsToChild
         | 
| 143 210 | 
             
                extend ActiveSupport::Concern
         | 
| 144 211 | 
             
                include ViewModelSpecHelpers::ParentAndBelongsToChild
         | 
| 145 212 | 
             
                def child_attributes
         | 
| 146 | 
            -
                  super.merge(viewmodel: ->( | 
| 213 | 
            +
                  super.merge(viewmodel: ->(_v) { root! })
         | 
| 147 214 | 
             
                end
         | 
| 148 215 | 
             
              end
         | 
| 149 216 |  | 
| @@ -157,11 +224,11 @@ module ViewModelSpecHelpers | |
| 157 224 | 
             
                      t.string :name
         | 
| 158 225 | 
             
                      t.integer :next_id
         | 
| 159 226 | 
             
                    },
         | 
| 160 | 
            -
                    model:     ->( | 
| 227 | 
            +
                    model:     ->(_m) {
         | 
| 161 228 | 
             
                      belongs_to :next, class_name: self.name, inverse_of: :previous, dependent: :destroy
         | 
| 162 229 | 
             
                      has_one :previous, class_name: self.name, foreign_key: :next_id, inverse_of: :next
         | 
| 163 230 | 
             
                    },
         | 
| 164 | 
            -
                    viewmodel: ->( | 
| 231 | 
            +
                    viewmodel: ->(_v) {
         | 
| 165 232 | 
             
                      # Not a root
         | 
| 166 233 | 
             
                      association :next
         | 
| 167 234 | 
             
                      attribute :name
         | 
| @@ -180,15 +247,15 @@ module ViewModelSpecHelpers | |
| 180 247 | 
             
                def model_attributes
         | 
| 181 248 | 
             
                  f = subject_association_features
         | 
| 182 249 | 
             
                  super.merge(
         | 
| 183 | 
            -
                    model:     ->( | 
| 184 | 
            -
                    viewmodel: ->( | 
| 250 | 
            +
                    model:     ->(_m) { has_one :child, inverse_of: :model, dependent: :destroy },
         | 
| 251 | 
            +
                    viewmodel: ->(_v) { association :child, **f },
         | 
| 185 252 | 
             
                  )
         | 
| 186 253 | 
             
                end
         | 
| 187 254 |  | 
| 188 255 | 
             
                def child_attributes
         | 
| 189 256 | 
             
                  super.merge(
         | 
| 190 257 | 
             
                    schema: ->(t) { t.references :model, foreign_key: true },
         | 
| 191 | 
            -
                    model:  ->( | 
| 258 | 
            +
                    model:  ->(_m) { belongs_to :model, inverse_of: :child },
         | 
| 192 259 | 
             
                  )
         | 
| 193 260 | 
             
                end
         | 
| 194 261 |  | 
| @@ -207,7 +274,7 @@ module ViewModelSpecHelpers | |
| 207 274 | 
             
                extend ActiveSupport::Concern
         | 
| 208 275 | 
             
                include ViewModelSpecHelpers::ParentAndHasOneChild
         | 
| 209 276 | 
             
                def child_attributes
         | 
| 210 | 
            -
                  super.merge(viewmodel: ->( | 
| 277 | 
            +
                  super.merge(viewmodel: ->(_v) { root! })
         | 
| 211 278 | 
             
                end
         | 
| 212 279 | 
             
              end
         | 
| 213 280 |  | 
| @@ -218,15 +285,15 @@ module ViewModelSpecHelpers | |
| 218 285 | 
             
                def model_attributes
         | 
| 219 286 | 
             
                  f = subject_association_features
         | 
| 220 287 | 
             
                  super.merge(
         | 
| 221 | 
            -
                    model:     ->( | 
| 222 | 
            -
                    viewmodel: ->( | 
| 288 | 
            +
                    model:     ->(_m) { has_many :children, inverse_of: :model, dependent: :destroy },
         | 
| 289 | 
            +
                    viewmodel: ->(_v) { association :children, **f },
         | 
| 223 290 | 
             
                  )
         | 
| 224 291 | 
             
                end
         | 
| 225 292 |  | 
| 226 293 | 
             
                def child_attributes
         | 
| 227 294 | 
             
                  super.merge(
         | 
| 228 295 | 
             
                    schema: ->(t) { t.references :model, foreign_key: true },
         | 
| 229 | 
            -
                    model:  ->( | 
| 296 | 
            +
                    model:  ->(_m) { belongs_to :model, inverse_of: :children },
         | 
| 230 297 | 
             
                  )
         | 
| 231 298 | 
             
                end
         | 
| 232 299 |  | 
| @@ -245,7 +312,7 @@ module ViewModelSpecHelpers | |
| 245 312 | 
             
                extend ActiveSupport::Concern
         | 
| 246 313 | 
             
                include ViewModelSpecHelpers::ParentAndHasManyChildren
         | 
| 247 314 | 
             
                def child_attributes
         | 
| 248 | 
            -
                  super.merge(viewmodel: ->( | 
| 315 | 
            +
                  super.merge(viewmodel: ->(_v) { root! })
         | 
| 249 316 | 
             
                end
         | 
| 250 317 | 
             
              end
         | 
| 251 318 |  | 
| @@ -271,12 +338,11 @@ module ViewModelSpecHelpers | |
| 271 338 | 
             
                    table = model.table_name
         | 
| 272 339 | 
             
                    model.connection.execute <<-SQL
         | 
| 273 340 | 
             
                        ALTER TABLE #{table} ADD CONSTRAINT #{table}_unique_on_model_and_position UNIQUE(model_id, position) DEFERRABLE INITIALLY DEFERRED
         | 
| 274 | 
            -
             | 
| 341 | 
            +
                    SQL
         | 
| 275 342 | 
             
                  end
         | 
| 276 343 | 
             
                end
         | 
| 277 344 | 
             
              end
         | 
| 278 345 |  | 
| 279 | 
            -
             | 
| 280 346 | 
             
              module ParentAndExternalSharedChild
         | 
| 281 347 | 
             
                extend ActiveSupport::Concern
         | 
| 282 348 | 
             
                include ViewModelSpecHelpers::ParentAndSharedBelongsToChild
         | 
| @@ -293,15 +359,15 @@ module ViewModelSpecHelpers | |
| 293 359 | 
             
                def model_attributes
         | 
| 294 360 | 
             
                  f = subject_association_features
         | 
| 295 361 | 
             
                  super.merge(
         | 
| 296 | 
            -
                    model:     ->( | 
| 297 | 
            -
                    viewmodel: ->( | 
| 362 | 
            +
                    model:     ->(_m) { has_many :model_children, inverse_of: :model, dependent: :destroy },
         | 
| 363 | 
            +
                    viewmodel: ->(_v) { association :children, through: :model_children, through_order_attr: :position, **f },
         | 
| 298 364 | 
             
                  )
         | 
| 299 365 | 
             
                end
         | 
| 300 366 |  | 
| 301 367 | 
             
                def child_attributes
         | 
| 302 368 | 
             
                  super.merge(
         | 
| 303 | 
            -
                    model: ->( | 
| 304 | 
            -
                    viewmodel: ->( | 
| 369 | 
            +
                    model: ->(_m) { has_many :model_children, inverse_of: :child, dependent: :destroy },
         | 
| 370 | 
            +
                    viewmodel: ->(_v) { root! },
         | 
| 305 371 | 
             
                  )
         | 
| 306 372 | 
             
                end
         | 
| 307 373 |  |