iknow_view_models 3.1.8 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -34,7 +34,7 @@ class ViewModel::ActiveRecord
34
34
  # Defines a cacheable parent Model with a owned Child and a cachable shared Shared.
35
35
  module CacheableParentAndChildren
36
36
  extend ActiveSupport::Concern
37
- include ViewModelSpecHelpers::ParentAndBelongsToChild
37
+ include ViewModelSpecHelpers::ParentAndBelongsToChildWithMigration
38
38
 
39
39
  def model_attributes
40
40
  super.merge(
@@ -43,7 +43,7 @@ class ViewModel::ActiveRecord
43
43
  viewmodel: ->(_) {
44
44
  association :shared
45
45
  cacheable!
46
- }
46
+ },
47
47
  )
48
48
  end
49
49
 
@@ -64,8 +64,18 @@ class ViewModel::ActiveRecord
64
64
 
65
65
  define_viewmodel do
66
66
  root!
67
+ self.schema_version = 2
67
68
  attributes :name
68
69
  cacheable!(cache_group: shared_cache_group)
70
+ migrates from: 1, to: 2 do
71
+ up do |view, _references|
72
+ view['name'] = view.delete('old_name')
73
+ end
74
+
75
+ down do |view, _references|
76
+ view['old_name'] = view.delete('name')
77
+ end
78
+ end
69
79
  end
70
80
  end
71
81
  end
@@ -91,10 +101,17 @@ class ViewModel::ActiveRecord
91
101
  DUMMY_RAILS_CACHE.clear
92
102
  end
93
103
 
104
+ # Request serializations to be migrated to the specified versions
105
+ let(:migration_versions) { {} }
106
+
94
107
  # Extract the iKnowCaches to verify their contents
95
108
  def read_cache(viewmodel_class, id)
96
109
  vm_cache = viewmodel_class.viewmodel_cache
97
- vm_cache.send(:cache).read(vm_cache.key_for(id))
110
+ cache_migration_version = vm_cache.migrated_cache_version(migration_versions)
111
+
112
+ key = vm_cache.key_for(id, cache_migration_version)
113
+ iknow_cache = vm_cache.cache_for(cache_migration_version)
114
+ iknow_cache.read(key)
98
115
  end
99
116
 
100
117
  def serialize_from_database
@@ -102,6 +119,12 @@ class ViewModel::ActiveRecord
102
119
  context = viewmodel_class.new_serialize_context
103
120
  data = ViewModel.serialize_to_hash([view], serialize_context: context)
104
121
  refs = context.serialize_references_to_hash
122
+
123
+ if migration_versions.present?
124
+ migrator = ViewModel::DownMigrator.new(migration_versions)
125
+ migrator.migrate!([data, refs], references: refs)
126
+ end
127
+
105
128
  [data, refs]
106
129
  end
107
130
 
@@ -113,7 +136,7 @@ class ViewModel::ActiveRecord
113
136
  end
114
137
 
115
138
  def fetch_with_cache
116
- viewmodel_class.viewmodel_cache.fetch([root.id])
139
+ viewmodel_class.viewmodel_cache.fetch([root.id], migration_versions: migration_versions)
117
140
  end
118
141
 
119
142
  def serialize_with_cache
@@ -179,7 +202,20 @@ class ViewModel::ActiveRecord
179
202
 
180
203
  describe 'with owned and shared children' do
181
204
  include CacheableParentAndChildren
182
- include BehavesLikeACache
205
+
206
+ describe 'without migrations' do
207
+ include BehavesLikeACache
208
+ end
209
+
210
+ describe 'with migrations' do
211
+ let(:migration_versions) { { viewmodel_class => 1, child_viewmodel_class => 2 } }
212
+ include BehavesLikeACache
213
+ end
214
+
215
+ describe 'with shared migrations' do
216
+ let(:migration_versions) { { shared_viewmodel_class => 1 } }
217
+ include BehavesLikeACache
218
+ end
183
219
 
184
220
  describe 'with a record in the cache' do
185
221
  # Fetch the root record to ensure it's in the cache
@@ -109,6 +109,24 @@ class ViewModel::ActiveRecord::ControllerTest < ActiveSupport::TestCase
109
109
  assert_all_hooks_nested_inside_parent_hook(parentcontroller.hook_trace)
110
110
  end
111
111
 
112
+ def test_migrated_show
113
+ parentcontroller = ParentController.new(id: @parent.id, versions: { ParentView.view_name => 1 })
114
+ parentcontroller.invoke(:show)
115
+
116
+ expected_view = @parent_view.to_hash
117
+ .except('name')
118
+ .merge('old_name' => @parent.name,
119
+ ViewModel::VERSION_ATTRIBUTE => 1,
120
+ ViewModel::MIGRATED_ATTRIBUTE => true)
121
+
122
+ assert_equal({ 'data' => expected_view },
123
+ parentcontroller.hash_response)
124
+
125
+ assert_equal(200, parentcontroller.status)
126
+
127
+ assert_all_hooks_nested_inside_parent_hook(parentcontroller.hook_trace)
128
+ end
129
+
112
130
  def test_index
113
131
  p2 = Parent.create(name: "p2")
114
132
  p2_view = ParentView.new(p2)
@@ -148,6 +166,22 @@ class ViewModel::ActiveRecord::ControllerTest < ActiveSupport::TestCase
148
166
  assert_all_hooks_nested_inside_parent_hook(parentcontroller.hook_trace)
149
167
  end
150
168
 
169
+ def test_migrated_create
170
+ data = {
171
+ '_type' => 'Parent',
172
+ '_version' => 1,
173
+ 'old_name' => 'p2',
174
+ }
175
+
176
+ parentcontroller = ParentController.new(data: data, versions: { ParentView.view_name => 1 })
177
+ parentcontroller.invoke(:create)
178
+
179
+ assert_equal(200, parentcontroller.status)
180
+
181
+ p2 = Parent.where(name: 'p2').first
182
+ assert(p2.present?, 'p2 created')
183
+ end
184
+
151
185
  def test_create_empty
152
186
  parentcontroller = ParentController.new(data: [])
153
187
  parentcontroller.invoke(:create)
@@ -0,0 +1,161 @@
1
+ require_relative "../../../helpers/arvm_test_utilities.rb"
2
+ require_relative "../../../helpers/arvm_test_models.rb"
3
+ require_relative "../../../helpers/viewmodel_spec_helpers.rb"
4
+
5
+ require "minitest/autorun"
6
+
7
+ require "view_model/active_record"
8
+
9
+ class ViewModel::ActiveRecord::Migration < ActiveSupport::TestCase
10
+ include ARVMTestUtilities
11
+ extend Minitest::Spec::DSL
12
+
13
+ include ViewModelSpecHelpers::ParentAndBelongsToChildWithMigration
14
+
15
+ def new_model
16
+ model_class.new(name: 'm1', child: child_model_class.new(name: 'c1'))
17
+ end
18
+
19
+ let(:viewmodel) { create_viewmodel! }
20
+
21
+ let(:current_serialization) { ViewModel.serialize_to_hash(viewmodel) }
22
+
23
+ let(:v2_serialization) do
24
+ {
25
+ ViewModel::TYPE_ATTRIBUTE => viewmodel_class.view_name,
26
+ ViewModel::VERSION_ATTRIBUTE => 2,
27
+ ViewModel::ID_ATTRIBUTE => viewmodel.id,
28
+ 'name' => viewmodel.name,
29
+ 'old_field' => 1,
30
+ 'child' => {
31
+ ViewModel::TYPE_ATTRIBUTE => child_viewmodel_class.view_name,
32
+ ViewModel::VERSION_ATTRIBUTE => 2,
33
+ ViewModel::ID_ATTRIBUTE => viewmodel.child.id,
34
+ 'name' => viewmodel.child.name,
35
+ 'former_field' => 'former_value',
36
+ }
37
+ }
38
+ end
39
+
40
+ let(:migration_versions) { { viewmodel_class => 2, child_viewmodel_class => 2 } }
41
+
42
+ let(:down_migrator) { ViewModel::DownMigrator.new(migration_versions) }
43
+ let(:up_migrator) { ViewModel::UpMigrator.new(migration_versions) }
44
+
45
+ def migrate!
46
+ migrator.migrate!(subject, references: {})
47
+ end
48
+
49
+
50
+ describe 'downwards' do
51
+ let(:migrator) { down_migrator }
52
+ let(:subject) { current_serialization.deep_dup }
53
+
54
+ let(:expected_result) do
55
+ v2_serialization.deep_merge(
56
+ {
57
+ ViewModel::MIGRATED_ATTRIBUTE => true,
58
+ 'old_field' => -1,
59
+ 'child' => {
60
+ ViewModel::MIGRATED_ATTRIBUTE => true,
61
+ 'former_field' => 'reconstructed'
62
+ }
63
+ }
64
+ )
65
+ end
66
+
67
+ it 'migrates' do
68
+ migrate!
69
+
70
+ assert_equal(expected_result, subject)
71
+ end
72
+
73
+
74
+ describe 'to an unreachable version' do
75
+ let(:migration_versions) { { viewmodel_class => 2, child_viewmodel_class => 1 } }
76
+
77
+ it 'raises' do
78
+ assert_raises(ViewModel::Migration::NoPathError) do
79
+ migrate!
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ describe 'upwards' do
86
+ let(:migrator) { up_migrator }
87
+ let(:subject) { v2_serialization.deep_dup }
88
+
89
+ let(:expected_result) do
90
+ current_serialization.deep_merge(
91
+ ViewModel::MIGRATED_ATTRIBUTE => true,
92
+ 'new_field' => 3,
93
+ 'child' => {
94
+ ViewModel::MIGRATED_ATTRIBUTE => true,
95
+ }
96
+ )
97
+ end
98
+
99
+ it 'migrates' do
100
+ migrate!
101
+
102
+ assert_equal(expected_result, subject)
103
+ end
104
+
105
+ describe 'with version unspecified' do
106
+ let(:subject) do
107
+ v2_serialization
108
+ .except(ViewModel::VERSION_ATTRIBUTE)
109
+ end
110
+
111
+ it 'treats it as the requested version' do
112
+ migrate!
113
+ assert_equal(expected_result, subject)
114
+ end
115
+ end
116
+
117
+ describe 'with a version not in the specification' do
118
+ let(:subject) do
119
+ v2_serialization
120
+ .except('old_field')
121
+ .deep_merge(ViewModel::VERSION_ATTRIBUTE => 3, 'mid_field' => 1)
122
+ end
123
+
124
+ it 'rejects it' do
125
+ assert_raises(ViewModel::Migration::UnspecifiedVersionError) do
126
+ migrate!
127
+ end
128
+ end
129
+ end
130
+
131
+ describe 'from an unreachable version' do
132
+ let(:migration_versions) { { viewmodel_class => 2, child_viewmodel_class => 1 } }
133
+
134
+ let(:subject) do
135
+ v2_serialization.deep_merge(
136
+ 'child' => { ViewModel::VERSION_ATTRIBUTE => 1 }
137
+ )
138
+ end
139
+
140
+ it 'raises' do
141
+ assert_raises(ViewModel::Migration::NoPathError) do
142
+ migrate!
143
+ end
144
+ end
145
+ end
146
+
147
+ describe 'in an undefined direction' do
148
+ let(:migration_versions) { { viewmodel_class => 1, child_viewmodel_class => 2 } }
149
+
150
+ let(:subject) do
151
+ v2_serialization.except('old_field').merge(ViewModel::VERSION_ATTRIBUTE => 1)
152
+ end
153
+
154
+ it 'raises' do
155
+ assert_raises(ViewModel::Migration::OneWayError) do
156
+ migrate!
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iknow_view_models
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.8
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - iKnow Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-26 00:00:00.000000000 Z
11
+ date: 2020-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -206,6 +206,20 @@ dependencies:
206
206
  - - ">="
207
207
  - !ruby/object:Gem::Version
208
208
  version: '0'
209
+ - !ruby/object:Gem::Dependency
210
+ name: rgl
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :runtime
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
209
223
  - !ruby/object:Gem::Dependency
210
224
  name: appraisal
211
225
  requirement: !ruby/object:Gem::Requirement
@@ -280,16 +294,16 @@ dependencies:
280
294
  name: pg
281
295
  requirement: !ruby/object:Gem::Requirement
282
296
  requirements:
283
- - - "~>"
297
+ - - ">="
284
298
  - !ruby/object:Gem::Version
285
- version: '0.18'
299
+ version: '0'
286
300
  type: :development
287
301
  prerelease: false
288
302
  version_requirements: !ruby/object:Gem::Requirement
289
303
  requirements:
290
- - - "~>"
304
+ - - ">="
291
305
  - !ruby/object:Gem::Version
292
- version: '0.18'
306
+ version: '0'
293
307
  - !ruby/object:Gem::Dependency
294
308
  name: pry
295
309
  requirement: !ruby/object:Gem::Requirement
@@ -357,15 +371,14 @@ files:
357
371
  - ".envrc"
358
372
  - ".gitignore"
359
373
  - ".idea/codeStyleSettings.xml"
360
- - ".travis.yml"
374
+ - ".rubocop.yml"
361
375
  - Appraisals
362
376
  - Gemfile
363
377
  - LICENSE.txt
364
378
  - README.md
365
379
  - Rakefile
366
- - appveyor.yml
367
380
  - gemfiles/rails_5_2.gemfile
368
- - gemfiles/rails_6_0_beta.gemfile
381
+ - gemfiles/rails_6_0.gemfile
369
382
  - iknow_view_models.gemspec
370
383
  - lib/iknow_view_models.rb
371
384
  - lib/iknow_view_models/railtie.rb
@@ -401,6 +414,12 @@ files:
401
414
  - lib/view_model/deserialize_context.rb
402
415
  - lib/view_model/error.rb
403
416
  - lib/view_model/error_view.rb
417
+ - lib/view_model/migratable_view.rb
418
+ - lib/view_model/migration.rb
419
+ - lib/view_model/migration/no_path_error.rb
420
+ - lib/view_model/migration/one_way_error.rb
421
+ - lib/view_model/migration/unspecified_version_error.rb
422
+ - lib/view_model/migrator.rb
404
423
  - lib/view_model/record.rb
405
424
  - lib/view_model/record/attribute_data.rb
406
425
  - lib/view_model/reference.rb
@@ -414,6 +433,7 @@ files:
414
433
  - lib/view_model/traversal_context.rb
415
434
  - lib/view_model/utils.rb
416
435
  - lib/view_model/utils/collections.rb
436
+ - nix/dependencies.nix
417
437
  - nix/gem/generate.rb
418
438
  - shell.nix
419
439
  - test/config/database.yml
@@ -437,6 +457,7 @@ files:
437
457
  - test/unit/view_model/active_record/has_many_through_poly_test.rb
438
458
  - test/unit/view_model/active_record/has_many_through_test.rb
439
459
  - test/unit/view_model/active_record/has_one_test.rb
460
+ - test/unit/view_model/active_record/migration_test.rb
440
461
  - test/unit/view_model/active_record/namespacing_test.rb
441
462
  - test/unit/view_model/active_record/poly_test.rb
442
463
  - test/unit/view_model/active_record/shared_test.rb
@@ -494,6 +515,7 @@ test_files:
494
515
  - test/unit/view_model/active_record/has_many_through_poly_test.rb
495
516
  - test/unit/view_model/active_record/has_many_through_test.rb
496
517
  - test/unit/view_model/active_record/has_one_test.rb
518
+ - test/unit/view_model/active_record/migration_test.rb
497
519
  - test/unit/view_model/active_record/namespacing_test.rb
498
520
  - test/unit/view_model/active_record/poly_test.rb
499
521
  - test/unit/view_model/active_record/shared_test.rb
@@ -1,31 +0,0 @@
1
- dist: trusty
2
- sudo: false
3
- language: ruby
4
- cache: bundler
5
-
6
- rvm:
7
- - 2.5
8
-
9
- gemfile:
10
- - gemfiles/rails_5_2.gemfile
11
-
12
- addons:
13
- postgresql: "10"
14
- apt:
15
- packages:
16
- - postgresql-10
17
- - postgresql-client-10
18
- - postgresql-server-dev-10
19
- env:
20
- global:
21
- - PGPORT=5433
22
-
23
- before_install:
24
- - gem update --system
25
- - gem install bundler
26
-
27
- before_script:
28
- - psql -c 'CREATE DATABASE iknow_view_models;'
29
-
30
- notifications:
31
- email: false