iknow_view_models 2.10.1 → 3.0.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 +119 -0
- data/.travis.yml +31 -0
- data/Appraisals +6 -16
- data/gemfiles/{rails_7_0.gemfile → rails_6_0_beta.gemfile} +2 -2
- data/iknow_view_models.gemspec +3 -5
- data/lib/iknow_view_models/version.rb +1 -1
- data/lib/view_model/active_record/association_data.rb +206 -92
- data/lib/view_model/active_record/association_manipulation.rb +22 -12
- data/lib/view_model/active_record/cache/cacheable_view.rb +3 -13
- data/lib/view_model/active_record/cache.rb +2 -2
- data/lib/view_model/active_record/cloner.rb +11 -11
- data/lib/view_model/active_record/controller.rb +0 -2
- data/lib/view_model/active_record/update_context.rb +21 -3
- data/lib/view_model/active_record/update_data.rb +43 -45
- data/lib/view_model/active_record/update_operation.rb +265 -153
- data/lib/view_model/active_record/visitor.rb +9 -6
- data/lib/view_model/active_record.rb +94 -74
- data/lib/view_model/after_transaction_runner.rb +3 -18
- data/lib/view_model/callbacks.rb +2 -2
- data/lib/view_model/changes.rb +24 -16
- data/lib/view_model/config.rb +6 -2
- data/lib/view_model/deserialization_error.rb +31 -0
- data/lib/view_model/deserialize_context.rb +2 -6
- data/lib/view_model/error_view.rb +6 -5
- data/lib/view_model/record/attribute_data.rb +11 -6
- data/lib/view_model/record.rb +44 -24
- data/lib/view_model/serialize_context.rb +2 -63
- data/lib/view_model/test_helpers/arvm_builder.rb +2 -4
- data/lib/view_model/traversal_context.rb +2 -2
- data/lib/view_model.rb +21 -13
- data/shell.nix +1 -1
- data/test/helpers/arvm_test_models.rb +4 -12
- data/test/helpers/arvm_test_utilities.rb +6 -0
- data/test/helpers/controller_test_helpers.rb +6 -6
- data/test/helpers/viewmodel_spec_helpers.rb +63 -52
- data/test/unit/view_model/access_control_test.rb +88 -37
- data/test/unit/view_model/active_record/belongs_to_test.rb +110 -178
- data/test/unit/view_model/active_record/cache_test.rb +11 -5
- data/test/unit/view_model/active_record/cloner_test.rb +1 -1
- data/test/unit/view_model/active_record/controller_test.rb +12 -20
- data/test/unit/view_model/active_record/has_many_test.rb +540 -316
- data/test/unit/view_model/active_record/has_many_through_poly_test.rb +12 -15
- data/test/unit/view_model/active_record/has_many_through_test.rb +15 -58
- data/test/unit/view_model/active_record/has_one_test.rb +288 -135
- data/test/unit/view_model/active_record/poly_test.rb +0 -1
- data/test/unit/view_model/active_record/shared_test.rb +21 -39
- data/test/unit/view_model/active_record/version_test.rb +3 -2
- data/test/unit/view_model/active_record_test.rb +5 -63
- data/test/unit/view_model/callbacks_test.rb +1 -0
- data/test/unit/view_model/record_test.rb +0 -32
- data/test/unit/view_model/traversal_context_test.rb +13 -12
- metadata +15 -25
- data/.github/workflows/gem-push.yml +0 -31
- data/.github/workflows/test.yml +0 -65
- data/gemfiles/rails_6_0.gemfile +0 -9
- data/gemfiles/rails_6_1.gemfile +0 -9
- data/test/unit/view_model/active_record/optional_attribute_view_test.rb +0 -58
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative "../../../helpers/arvm_test_utilities.rb"
|
2
2
|
require_relative "../../../helpers/arvm_test_models.rb"
|
3
|
+
require_relative '../../../helpers/viewmodel_spec_helpers.rb'
|
3
4
|
|
4
5
|
require "minitest/autorun"
|
5
6
|
|
@@ -8,80 +9,41 @@ require "view_model/active_record"
|
|
8
9
|
class ViewModel::ActiveRecord::HasOneTest < ActiveSupport::TestCase
|
9
10
|
include ARVMTestUtilities
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
define_schema do |t|
|
14
|
-
t.string :text
|
15
|
-
t.references :parent, foreign_key: true
|
16
|
-
end
|
17
|
-
|
18
|
-
define_model do
|
19
|
-
belongs_to :parent, inverse_of: :target
|
20
|
-
end
|
21
|
-
|
22
|
-
define_viewmodel do
|
23
|
-
attributes :text
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
|
29
|
-
def self.build_parent(arvm_test_case)
|
30
|
-
arvm_test_case.build_viewmodel(:Parent) do
|
31
|
-
define_schema do |t|
|
32
|
-
t.string :name
|
33
|
-
end
|
34
|
-
|
35
|
-
define_model do
|
36
|
-
has_one :target, dependent: :destroy, inverse_of: :parent
|
37
|
-
end
|
38
|
-
|
39
|
-
define_viewmodel do
|
40
|
-
attributes :name
|
41
|
-
associations :target
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def before_all
|
47
|
-
super
|
48
|
-
|
49
|
-
self.class.build_parent(self)
|
50
|
-
self.class.build_target(self)
|
51
|
-
end
|
12
|
+
extend Minitest::Spec::DSL
|
13
|
+
include ViewModelSpecHelpers::ParentAndHasOneChild
|
52
14
|
|
53
15
|
def setup
|
54
16
|
super
|
55
17
|
|
56
|
-
# TODO make a `has_list?` that allows a
|
57
|
-
@
|
58
|
-
|
59
|
-
@
|
18
|
+
# TODO make a `has_list?` that allows a model to set all children as an array
|
19
|
+
@model1 = model_class.new(name: "p1",
|
20
|
+
child: child_model_class.new(name: "p1t"))
|
21
|
+
@model1.save!
|
60
22
|
|
61
|
-
@
|
62
|
-
|
23
|
+
@model2 = model_class.new(name: "p2",
|
24
|
+
child: child_model_class.new(name: "p2t"))
|
63
25
|
|
64
|
-
@
|
26
|
+
@model2.save!
|
65
27
|
|
66
28
|
enable_logging!
|
67
29
|
end
|
68
30
|
|
69
31
|
def test_loading_batching
|
70
32
|
log_queries do
|
71
|
-
serialize(
|
33
|
+
serialize(ModelView.load)
|
72
34
|
end
|
73
|
-
assert_equal(['
|
35
|
+
assert_equal(['Model Load', 'Child Load'],
|
74
36
|
logged_load_queries)
|
75
37
|
end
|
76
38
|
|
77
39
|
def test_create_from_view
|
78
40
|
view = {
|
79
|
-
"_type" => "
|
41
|
+
"_type" => "Model",
|
80
42
|
"name" => "p",
|
81
|
-
"
|
43
|
+
"child" => { "_type" => "Child", "name" => "t" },
|
82
44
|
}
|
83
45
|
|
84
|
-
pv =
|
46
|
+
pv = ModelView.deserialize_from_view(view)
|
85
47
|
p = pv.model
|
86
48
|
|
87
49
|
assert(!p.changed?)
|
@@ -90,192 +52,383 @@ class ViewModel::ActiveRecord::HasOneTest < ActiveSupport::TestCase
|
|
90
52
|
assert_equal("p", p.name)
|
91
53
|
|
92
54
|
|
93
|
-
assert(p.
|
94
|
-
assert_equal("t", p.
|
55
|
+
assert(p.child.present?)
|
56
|
+
assert_equal("t", p.child.name)
|
95
57
|
end
|
96
58
|
|
97
59
|
def test_serialize_view
|
98
|
-
view, _refs = serialize_with_references(
|
99
|
-
assert_equal({ "_type" => "
|
60
|
+
view, _refs = serialize_with_references(ModelView.new(@model1))
|
61
|
+
assert_equal({ "_type" => "Model",
|
100
62
|
"_version" => 1,
|
101
|
-
"id" => @
|
102
|
-
"name" => @
|
103
|
-
"
|
63
|
+
"id" => @model1.id,
|
64
|
+
"name" => @model1.name,
|
65
|
+
"child" => { "_type" => "Child",
|
104
66
|
"_version" => 1,
|
105
|
-
"id" => @
|
106
|
-
"
|
67
|
+
"id" => @model1.child.id,
|
68
|
+
"name" => @model1.child.name } },
|
107
69
|
view)
|
108
70
|
end
|
109
71
|
|
110
72
|
def test_swap_has_one
|
111
|
-
@
|
112
|
-
@
|
73
|
+
@model1.update(child: t1 = Child.new)
|
74
|
+
@model2.update(child: t2 = Child.new)
|
113
75
|
|
114
76
|
deserialize_context = ViewModelBase.new_deserialize_context
|
115
77
|
|
116
|
-
|
117
|
-
[update_hash_for(
|
118
|
-
update_hash_for(
|
78
|
+
ModelView.deserialize_from_view(
|
79
|
+
[update_hash_for(ModelView, @model1) { |p| p['child'] = update_hash_for(ChildView, t2) },
|
80
|
+
update_hash_for(ModelView, @model2) { |p| p['child'] = update_hash_for(ChildView, t1) }],
|
119
81
|
deserialize_context: deserialize_context)
|
120
82
|
|
121
|
-
assert_equal(Set.new([ViewModel::Reference.new(
|
122
|
-
ViewModel::Reference.new(
|
83
|
+
assert_equal(Set.new([ViewModel::Reference.new(ModelView, @model1.id),
|
84
|
+
ViewModel::Reference.new(ModelView, @model2.id)]),
|
123
85
|
deserialize_context.valid_edit_refs.to_set)
|
124
86
|
|
125
|
-
@
|
126
|
-
@
|
87
|
+
@model1.reload
|
88
|
+
@model2.reload
|
127
89
|
|
128
|
-
assert_equal(@
|
129
|
-
assert_equal(@
|
90
|
+
assert_equal(@model1.child, t2)
|
91
|
+
assert_equal(@model2.child, t1)
|
130
92
|
end
|
131
93
|
|
132
94
|
def test_has_one_create_nil
|
133
|
-
view = { '_type' => '
|
134
|
-
pv =
|
135
|
-
assert_nil(pv.model.
|
95
|
+
view = { '_type' => 'Model', 'name' => 'p', 'child' => nil }
|
96
|
+
pv = ModelView.deserialize_from_view(view)
|
97
|
+
assert_nil(pv.model.child)
|
136
98
|
end
|
137
99
|
|
138
100
|
def test_has_one_create
|
139
|
-
@
|
101
|
+
@model1.update(child: nil)
|
140
102
|
|
141
|
-
alter_by_view!(
|
142
|
-
view['
|
103
|
+
alter_by_view!(ModelView, @model1) do |view, refs|
|
104
|
+
view['child'] = { '_type' => 'Child', 'name' => 't' }
|
143
105
|
end
|
144
106
|
|
145
|
-
assert_equal('t', @
|
107
|
+
assert_equal('t', @model1.child.name)
|
146
108
|
end
|
147
109
|
|
148
110
|
def test_has_one_update
|
149
|
-
alter_by_view!(
|
150
|
-
view['
|
111
|
+
alter_by_view!(ModelView, @model1) do |view, refs|
|
112
|
+
view['child']['name'] = "hello"
|
151
113
|
end
|
152
114
|
|
153
|
-
assert_equal('hello', @
|
115
|
+
assert_equal('hello', @model1.child.name)
|
154
116
|
end
|
155
117
|
|
156
118
|
def test_has_one_destroy
|
157
|
-
|
158
|
-
alter_by_view!(
|
159
|
-
view['
|
119
|
+
old_child = @model1.child
|
120
|
+
alter_by_view!(ModelView, @model1) do |view, refs|
|
121
|
+
view['child'] = nil
|
160
122
|
end
|
161
|
-
assert(
|
123
|
+
assert(Child.where(id: old_child.id).blank?)
|
162
124
|
end
|
163
125
|
|
164
126
|
def test_has_one_move_and_replace
|
165
|
-
|
166
|
-
|
127
|
+
old_model1_child = @model1.child
|
128
|
+
old_model2_child = @model2.child
|
167
129
|
|
168
|
-
alter_by_view!(
|
169
|
-
p2['
|
170
|
-
p1['
|
130
|
+
alter_by_view!(ModelView, [@model1, @model2]) do |(p1, p2), refs|
|
131
|
+
p2['child'] = p1['child']
|
132
|
+
p1['child'] = nil
|
171
133
|
end
|
172
134
|
|
173
|
-
assert(@
|
174
|
-
assert_equal(
|
175
|
-
assert(
|
135
|
+
assert(@model1.child.blank?)
|
136
|
+
assert_equal(old_model1_child, @model2.child)
|
137
|
+
assert(Child.where(id: old_model2_child).blank?)
|
176
138
|
end
|
177
139
|
|
178
140
|
def test_has_one_cannot_duplicate_unreleased_child
|
179
|
-
# p2 shouldn't be able to copy p1's
|
141
|
+
# p2 shouldn't be able to copy p1's child
|
180
142
|
assert_raises(ViewModel::DeserializationError::DuplicateNodes) do
|
181
|
-
alter_by_view!(
|
182
|
-
p2['
|
143
|
+
alter_by_view!(ModelView, [@model1, @model2]) do |(p1, p2), _refs|
|
144
|
+
p2['child'] = p1['child'].dup
|
183
145
|
end
|
184
146
|
end
|
185
147
|
end
|
186
148
|
|
187
149
|
def test_has_one_cannot_duplicate_implicitly_unreleased_child
|
188
|
-
# p2 shouldn't be able to copy p1's
|
150
|
+
# p2 shouldn't be able to copy p1's child, even when p1 doesn't explicitly
|
189
151
|
# specify the association
|
190
152
|
assert_raises(ViewModel::DeserializationError::ParentNotFound) do
|
191
|
-
alter_by_view!(
|
192
|
-
p2['
|
193
|
-
p1.delete('
|
153
|
+
alter_by_view!(ModelView, [@model1, @model2]) do |(p1, p2), _refs|
|
154
|
+
p2['child'] = p1['child']
|
155
|
+
p1.delete('child')
|
194
156
|
end
|
195
157
|
end
|
196
158
|
end
|
197
159
|
|
198
160
|
def test_has_one_cannot_take_from_outside_tree
|
199
|
-
t3 =
|
161
|
+
t3 = Model.create(child: Child.new(name: 'hi')).child
|
200
162
|
|
201
163
|
assert_raises(ViewModel::DeserializationError::ParentNotFound) do
|
202
|
-
alter_by_view!(
|
203
|
-
p1['
|
164
|
+
alter_by_view!(ModelView, [@model1]) do |(p1), _refs|
|
165
|
+
p1['child'] = update_hash_for(ChildView, t3)
|
204
166
|
end
|
205
167
|
end
|
206
168
|
end
|
207
169
|
|
208
|
-
def
|
209
|
-
t3 =
|
170
|
+
def test_has_one_cannot_take_unmodeled_from_outside_tree
|
171
|
+
t3 = Child.create(name: 'hi') # no model
|
210
172
|
|
211
173
|
assert_raises(ViewModel::DeserializationError::ParentNotFound) do
|
212
|
-
alter_by_view!(
|
213
|
-
p1['
|
174
|
+
alter_by_view!(ModelView, @model1) do |p1, _refs|
|
175
|
+
p1['child'] = update_hash_for(ChildView, t3)
|
214
176
|
end
|
215
177
|
end
|
216
178
|
end
|
217
179
|
|
218
180
|
def test_bad_single_association
|
219
181
|
view = {
|
220
|
-
"_type" => "
|
221
|
-
"
|
182
|
+
"_type" => "Model",
|
183
|
+
"child" => []
|
222
184
|
}
|
223
185
|
ex = assert_raises(ViewModel::DeserializationError::InvalidSyntax) do
|
224
|
-
|
186
|
+
ModelView.deserialize_from_view(view)
|
225
187
|
end
|
226
188
|
assert_match(/not an object/, ex.message)
|
227
189
|
end
|
228
190
|
|
191
|
+
describe 'owned reference child' do
|
192
|
+
def child_attributes
|
193
|
+
super.merge(viewmodel: ->(v) { root! })
|
194
|
+
end
|
229
195
|
|
230
|
-
|
231
|
-
|
196
|
+
def new_model
|
197
|
+
model_class.new(name: 'm1', child: child_model_class.new(name: 'c1'))
|
198
|
+
end
|
232
199
|
|
233
|
-
|
234
|
-
|
200
|
+
it 'makes a reference association' do
|
201
|
+
assert(subject_association.referenced?)
|
202
|
+
end
|
235
203
|
|
236
|
-
|
237
|
-
|
238
|
-
|
204
|
+
it 'makes an owned association' do
|
205
|
+
assert(subject_association.owned?)
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'loads and batches' do
|
209
|
+
create_model!
|
210
|
+
|
211
|
+
log_queries do
|
212
|
+
serialize(ModelView.load)
|
213
|
+
end
|
214
|
+
|
215
|
+
assert_equal(['Model Load', 'Child Load'], logged_load_queries)
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'serializes' do
|
219
|
+
model = create_model!
|
220
|
+
view, refs = serialize_with_references(ModelView.new(model))
|
221
|
+
child1_ref = refs.detect { |_, v| v['_type'] == 'Child' }.first
|
222
|
+
|
223
|
+
assert_equal({ child1_ref => { '_type' => 'Child',
|
224
|
+
'_version' => 1,
|
225
|
+
'id' => model.child.id,
|
226
|
+
'name' => model.child.name } },
|
227
|
+
refs)
|
228
|
+
|
229
|
+
assert_equal({ '_type' => 'Model',
|
230
|
+
'_version' => 1,
|
231
|
+
'id' => model.id,
|
232
|
+
'name' => model.name,
|
233
|
+
'child' => { '_ref' => child1_ref } },
|
234
|
+
view)
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'creates from view' do
|
238
|
+
view = {
|
239
|
+
'_type' => 'Model',
|
240
|
+
'name' => 'p',
|
241
|
+
'child' => { '_ref' => 'r1' },
|
242
|
+
}
|
243
|
+
|
244
|
+
refs = {
|
245
|
+
'r1' => { '_type' => 'Child', 'name' => 'newkid' },
|
246
|
+
}
|
247
|
+
|
248
|
+
pv = ModelView.deserialize_from_view(view, references: refs)
|
249
|
+
p = pv.model
|
250
|
+
|
251
|
+
assert(!p.changed?)
|
252
|
+
assert(!p.new_record?)
|
253
|
+
|
254
|
+
assert_equal('p', p.name)
|
255
|
+
|
256
|
+
assert(p.child.present?)
|
257
|
+
assert_equal('newkid', p.child.name)
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'updates' do
|
261
|
+
model = create_model!
|
262
|
+
|
263
|
+
alter_by_view!(ModelView, model) do |view, refs|
|
264
|
+
ref = view['child']['_ref']
|
265
|
+
refs[ref]['name'] = 'newchildname'
|
266
|
+
end
|
267
|
+
|
268
|
+
assert_equal('newchildname', model.child.name)
|
269
|
+
end
|
270
|
+
|
271
|
+
describe 'without a child' do
|
272
|
+
let(:new_model) {
|
273
|
+
model_class.new(name: 'm1', child: nil)
|
274
|
+
}
|
275
|
+
|
276
|
+
it 'can add a child' do
|
277
|
+
model = create_model!
|
278
|
+
|
279
|
+
alter_by_view!(ModelView, model) do |view, refs|
|
280
|
+
view['child'] = { '_ref' => 'ref1' }
|
281
|
+
refs['ref1'] = {
|
282
|
+
'_type' => 'Child',
|
283
|
+
'name' => 'newchildname',
|
284
|
+
}
|
239
285
|
end
|
240
286
|
|
241
|
-
|
242
|
-
|
287
|
+
assert(model.child.present?)
|
288
|
+
assert_equal('newchildname', model.child.name)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
it 'replaces a child with a new child' do
|
293
|
+
model = create_model!
|
294
|
+
old_child = model.child
|
295
|
+
|
296
|
+
alter_by_view!(ModelView, model) do |view, refs|
|
297
|
+
ref = view['child']['_ref']
|
298
|
+
refs[ref] = { '_type' => 'Child', 'name' => 'newchildname' }
|
299
|
+
end
|
300
|
+
model.reload
|
301
|
+
|
302
|
+
assert_equal('newchildname', model.child.name)
|
303
|
+
refute_equal(old_child, model.child)
|
304
|
+
assert(Child.where(id: old_child.id).blank?)
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'takes a released child from another parent' do
|
308
|
+
model1 = create_model!
|
309
|
+
model2 = create_model!
|
310
|
+
|
311
|
+
old_child1 = model1.child
|
312
|
+
old_child2 = model2.child
|
313
|
+
|
314
|
+
alter_by_view!(ModelView, [model1, model2]) do |(view1, view2), refs|
|
315
|
+
ref1 = view1['child']['_ref']
|
316
|
+
ref2 = view2['child']['_ref']
|
317
|
+
refs.delete(ref1)
|
318
|
+
view1['child'] = { '_ref' => ref2 }
|
319
|
+
view2['child'] = nil
|
320
|
+
end
|
321
|
+
|
322
|
+
assert_equal(model1.child, old_child2)
|
323
|
+
assert_nil(model2.child)
|
324
|
+
assert(Child.where(id: old_child1.id).blank?)
|
325
|
+
end
|
326
|
+
|
327
|
+
it 'prevents taking an unreleased reference out-of-tree' do
|
328
|
+
model1 = create_model!
|
329
|
+
child2 = Child.create!(name: 'dummy')
|
330
|
+
|
331
|
+
assert_raises(ViewModel::DeserializationError::ParentNotFound) do
|
332
|
+
alter_by_view!(ModelView, model1) do |view, refs|
|
333
|
+
refs.clear
|
334
|
+
view['child']['_ref'] = 'r1'
|
335
|
+
refs['r1'] = { '_type' => 'Child', 'id' => child2.id }
|
243
336
|
end
|
337
|
+
end
|
338
|
+
end
|
244
339
|
|
245
|
-
|
246
|
-
|
247
|
-
|
340
|
+
it 'prevents taking an unreleased reference in-tree' do
|
341
|
+
model1 = create_model!
|
342
|
+
model2 = create_model!
|
343
|
+
|
344
|
+
assert_raises(ViewModel::DeserializationError::DuplicateOwner) do
|
345
|
+
alter_by_view!(ModelView, [model1, model2]) do |(view1, view2), refs|
|
346
|
+
refs.delete(view1['child']['_ref'])
|
347
|
+
view1['child']['_ref'] = view2['child']['_ref']
|
248
348
|
end
|
249
349
|
end
|
350
|
+
end
|
351
|
+
|
352
|
+
it 'prevents two parents taking the same new reference' do
|
353
|
+
model1 = create_model!
|
354
|
+
model2 = create_model!
|
355
|
+
|
356
|
+
assert_raises(ViewModel::DeserializationError::DuplicateOwner) do
|
357
|
+
alter_by_view!(ModelView, [model1, model2]) do |(view1, view2), refs|
|
358
|
+
refs.clear
|
359
|
+
refs['ref1'] = { '_type' => 'Child', 'name' => 'new' }
|
360
|
+
view1['child']['_ref'] = 'ref1'
|
361
|
+
view2['child']['_ref'] = 'ref1'
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
it 'swaps children' do
|
367
|
+
model1 = create_model!
|
368
|
+
model2 = create_model!
|
369
|
+
|
370
|
+
old_child1 = model1.child
|
371
|
+
old_child2 = model2.child
|
372
|
+
|
373
|
+
alter_by_view!(ModelView, [model1, model2]) do |(view1, view2), _refs|
|
374
|
+
ref1 = view1['child']
|
375
|
+
ref2 = view2['child']
|
376
|
+
view1['child'] = ref2
|
377
|
+
view2['child'] = ref1
|
378
|
+
end
|
379
|
+
|
380
|
+
assert_equal(model1.child, old_child2)
|
381
|
+
assert_equal(model2.child, old_child1)
|
382
|
+
end
|
383
|
+
|
384
|
+
it 'deletes a child' do
|
385
|
+
model = create_model!
|
386
|
+
old_child = model.child
|
387
|
+
|
388
|
+
alter_by_view!(ModelView, model) do |view, refs|
|
389
|
+
refs.clear
|
390
|
+
view['child'] = nil
|
391
|
+
end
|
392
|
+
|
393
|
+
assert_nil(model.child)
|
394
|
+
assert(Child.where(id: old_child.id).blank?)
|
395
|
+
end
|
396
|
+
|
397
|
+
it 'eager includes' do
|
398
|
+
includes = viewmodel_class.eager_includes
|
399
|
+
assert_equal(DeepPreloader::Spec.new('child' => DeepPreloader::Spec.new), includes)
|
400
|
+
end
|
401
|
+
end
|
250
402
|
|
251
|
-
|
403
|
+
describe 'renaming associations' do
|
404
|
+
def subject_association_features
|
405
|
+
{ as: :something_else }
|
252
406
|
end
|
253
407
|
|
254
408
|
def setup
|
255
409
|
super
|
256
410
|
|
257
|
-
@
|
411
|
+
@model = model_class.create(child: child_model_class.new(name: 'child name'))
|
258
412
|
|
259
413
|
enable_logging!
|
260
414
|
end
|
261
415
|
|
262
416
|
def test_dependencies
|
263
|
-
root_updates, _ref_updates = ViewModel::ActiveRecord::UpdateData.parse_hashes([{ '_type' => '
|
264
|
-
assert_equal(DeepPreloader::Spec.new('
|
265
|
-
assert_equal({ 'something_else' => {} }, root_updates.first.updated_associations)
|
417
|
+
root_updates, _ref_updates = ViewModel::ActiveRecord::UpdateData.parse_hashes([{ '_type' => 'Model', 'something_else' => nil }])
|
418
|
+
assert_equal(DeepPreloader::Spec.new('child' => DeepPreloader::Spec.new), root_updates.first.preload_dependencies)
|
266
419
|
end
|
267
420
|
|
268
421
|
def test_renamed_roundtrip
|
269
|
-
alter_by_view!(
|
270
|
-
assert_equal({ 'id' => @
|
271
|
-
'_type' => '
|
422
|
+
alter_by_view!(ModelView, @model) do |view, refs|
|
423
|
+
assert_equal({ 'id' => @model.child.id,
|
424
|
+
'_type' => 'Child',
|
272
425
|
'_version' => 1,
|
273
|
-
'
|
426
|
+
'name' => 'child name' },
|
274
427
|
view['something_else'])
|
275
|
-
view['something_else']['
|
428
|
+
view['something_else']['name'] = 'child new name'
|
276
429
|
end
|
277
430
|
|
278
|
-
assert_equal('
|
431
|
+
assert_equal('child new name', @model.child.name)
|
279
432
|
end
|
280
433
|
end
|
281
434
|
|
@@ -299,7 +299,6 @@ module ViewModel::ActiveRecord::PolyTest
|
|
299
299
|
def test_dependencies
|
300
300
|
root_updates, _ref_updates = ViewModel::ActiveRecord::UpdateData.parse_hashes([{ '_type' => 'Parent', 'something_else' => nil }])
|
301
301
|
assert_equal(DeepPreloader::Spec.new('poly' => DeepPreloader::PolymorphicSpec.new), root_updates.first.preload_dependencies)
|
302
|
-
assert_equal({ 'something_else' => {} }, root_updates.first.updated_associations)
|
303
302
|
end
|
304
303
|
|
305
304
|
def test_renamed_roundtrip
|