iknow_view_models 3.1.8 → 3.2.0
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 +5 -0
- data/Appraisals +3 -3
- data/Gemfile +6 -2
- data/gemfiles/{rails_6_0_beta.gemfile → rails_6_0.gemfile} +2 -2
- data/iknow_view_models.gemspec +2 -1
- data/lib/iknow_view_models/version.rb +1 -1
- data/lib/view_model.rb +14 -3
- data/lib/view_model/active_record.rb +1 -1
- data/lib/view_model/active_record/cache.rb +110 -32
- data/lib/view_model/active_record/cache/cacheable_view.rb +2 -2
- data/lib/view_model/active_record/controller.rb +53 -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 +25 -0
- data/lib/view_model/migration/one_way_error.rb +23 -0
- data/lib/view_model/migration/unspecified_version_error.rb +23 -0
- data/lib/view_model/migrator.rb +108 -0
- data/lib/view_model/record.rb +4 -1
- data/lib/view_model/test_helpers/arvm_builder.rb +2 -1
- data/nix/dependencies.nix +5 -0
- data/nix/gem/generate.rb +2 -1
- data/shell.nix +8 -3
- data/test/helpers/controller_test_helpers.rb +14 -0
- data/test/helpers/viewmodel_spec_helpers.rb +67 -2
- data/test/unit/view_model/active_record/cache_test.rb +41 -5
- data/test/unit/view_model/active_record/controller_test.rb +34 -0
- data/test/unit/view_model/active_record/migration_test.rb +161 -0
- metadata +31 -9
- data/.travis.yml +0 -31
- data/appveyor.yml +0 -22
@@ -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::
|
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.
|
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
|
-
|
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.
|
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
|
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
|
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
|
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
|
-
- ".
|
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/
|
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
|
data/.travis.yml
DELETED
@@ -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
|