iknow_view_models 2.8.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +115 -0
- data/.gitignore +36 -0
- data/.travis.yml +31 -0
- data/Appraisals +9 -0
- data/Gemfile +19 -0
- data/LICENSE.txt +22 -0
- data/README.md +19 -0
- data/Rakefile +21 -0
- data/appveyor.yml +22 -0
- data/gemfiles/rails_5_2.gemfile +15 -0
- data/gemfiles/rails_6_0_beta.gemfile +15 -0
- data/iknow_view_models.gemspec +49 -0
- data/lib/iknow_view_models.rb +12 -0
- data/lib/iknow_view_models/railtie.rb +8 -0
- data/lib/iknow_view_models/version.rb +5 -0
- data/lib/view_model.rb +333 -0
- data/lib/view_model/access_control.rb +154 -0
- data/lib/view_model/access_control/composed.rb +216 -0
- data/lib/view_model/access_control/open.rb +13 -0
- data/lib/view_model/access_control/read_only.rb +13 -0
- data/lib/view_model/access_control/tree.rb +264 -0
- data/lib/view_model/access_control_error.rb +10 -0
- data/lib/view_model/active_record.rb +383 -0
- data/lib/view_model/active_record/association_data.rb +178 -0
- data/lib/view_model/active_record/association_manipulation.rb +389 -0
- data/lib/view_model/active_record/cache.rb +265 -0
- data/lib/view_model/active_record/cache/cacheable_view.rb +51 -0
- data/lib/view_model/active_record/cloner.rb +113 -0
- data/lib/view_model/active_record/collection_nested_controller.rb +100 -0
- data/lib/view_model/active_record/controller.rb +77 -0
- data/lib/view_model/active_record/controller_base.rb +185 -0
- data/lib/view_model/active_record/nested_controller_base.rb +93 -0
- data/lib/view_model/active_record/singular_nested_controller.rb +34 -0
- data/lib/view_model/active_record/update_context.rb +252 -0
- data/lib/view_model/active_record/update_data.rb +749 -0
- data/lib/view_model/active_record/update_operation.rb +810 -0
- data/lib/view_model/active_record/visitor.rb +77 -0
- data/lib/view_model/after_transaction_runner.rb +29 -0
- data/lib/view_model/callbacks.rb +219 -0
- data/lib/view_model/changes.rb +62 -0
- data/lib/view_model/config.rb +29 -0
- data/lib/view_model/controller.rb +142 -0
- data/lib/view_model/deserialization_error.rb +437 -0
- data/lib/view_model/deserialize_context.rb +16 -0
- data/lib/view_model/error.rb +191 -0
- data/lib/view_model/error_view.rb +35 -0
- data/lib/view_model/record.rb +367 -0
- data/lib/view_model/record/attribute_data.rb +48 -0
- data/lib/view_model/reference.rb +31 -0
- data/lib/view_model/references.rb +48 -0
- data/lib/view_model/registry.rb +73 -0
- data/lib/view_model/schemas.rb +45 -0
- data/lib/view_model/serialization_error.rb +10 -0
- data/lib/view_model/serialize_context.rb +118 -0
- data/lib/view_model/test_helpers.rb +103 -0
- data/lib/view_model/test_helpers/arvm_builder.rb +111 -0
- data/lib/view_model/traversal_context.rb +126 -0
- data/lib/view_model/utils.rb +24 -0
- data/lib/view_model/utils/collections.rb +49 -0
- data/test/helpers/arvm_test_models.rb +59 -0
- data/test/helpers/arvm_test_utilities.rb +187 -0
- data/test/helpers/callback_tracer.rb +27 -0
- data/test/helpers/controller_test_helpers.rb +270 -0
- data/test/helpers/match_enumerator.rb +58 -0
- data/test/helpers/query_logging.rb +71 -0
- data/test/helpers/test_access_control.rb +56 -0
- data/test/helpers/viewmodel_spec_helpers.rb +326 -0
- data/test/unit/view_model/access_control_test.rb +769 -0
- data/test/unit/view_model/active_record/alias_test.rb +35 -0
- data/test/unit/view_model/active_record/belongs_to_test.rb +376 -0
- data/test/unit/view_model/active_record/cache_test.rb +351 -0
- data/test/unit/view_model/active_record/cloner_test.rb +313 -0
- data/test/unit/view_model/active_record/controller_test.rb +561 -0
- data/test/unit/view_model/active_record/counter_test.rb +80 -0
- data/test/unit/view_model/active_record/customization_test.rb +388 -0
- data/test/unit/view_model/active_record/has_many_test.rb +957 -0
- data/test/unit/view_model/active_record/has_many_through_poly_test.rb +269 -0
- data/test/unit/view_model/active_record/has_many_through_test.rb +736 -0
- data/test/unit/view_model/active_record/has_one_test.rb +334 -0
- data/test/unit/view_model/active_record/namespacing_test.rb +75 -0
- data/test/unit/view_model/active_record/optional_attribute_view_test.rb +58 -0
- data/test/unit/view_model/active_record/poly_test.rb +320 -0
- data/test/unit/view_model/active_record/shared_test.rb +285 -0
- data/test/unit/view_model/active_record/version_test.rb +121 -0
- data/test/unit/view_model/active_record_test.rb +542 -0
- data/test/unit/view_model/callbacks_test.rb +582 -0
- data/test/unit/view_model/deserialization_error/unique_violation_test.rb +73 -0
- data/test/unit/view_model/record_test.rb +524 -0
- data/test/unit/view_model/traversal_context_test.rb +371 -0
- data/test/unit/view_model_test.rb +62 -0
- metadata +490 -0
@@ -0,0 +1,320 @@
|
|
1
|
+
require_relative "../../../helpers/arvm_test_utilities.rb"
|
2
|
+
require_relative "../../../helpers/arvm_test_models.rb"
|
3
|
+
|
4
|
+
require "minitest/autorun"
|
5
|
+
|
6
|
+
require "view_model/active_record"
|
7
|
+
|
8
|
+
module ViewModel::ActiveRecord::PolyTest
|
9
|
+
## Polymorphic pointer to parent in child (child may belong to different type parents)
|
10
|
+
class PolyParentPointerTest < ActiveSupport::TestCase
|
11
|
+
include ARVMTestUtilities
|
12
|
+
def before_all
|
13
|
+
super
|
14
|
+
build_viewmodel(:Child) do
|
15
|
+
define_schema do |t|
|
16
|
+
t.string :text
|
17
|
+
t.string :poly_type
|
18
|
+
t.integer :poly_id
|
19
|
+
end
|
20
|
+
define_model do
|
21
|
+
belongs_to :poly, polymorphic: true
|
22
|
+
end
|
23
|
+
define_viewmodel do
|
24
|
+
attributes :text
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
build_viewmodel(:PolyParentOne) do
|
29
|
+
define_schema do |t|
|
30
|
+
t.string :text
|
31
|
+
end
|
32
|
+
define_model do
|
33
|
+
has_one :grandparent, inverse_of: :poly_parent_one
|
34
|
+
has_one :child, as: :poly, dependent: :destroy, inverse_of: :poly
|
35
|
+
end
|
36
|
+
define_viewmodel do
|
37
|
+
attributes :text
|
38
|
+
association :child
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
build_viewmodel(:PolyParentTwo) do
|
43
|
+
define_schema do |t|
|
44
|
+
t.integer :num
|
45
|
+
end
|
46
|
+
define_model do
|
47
|
+
has_one :grandparent, inverse_of: :poly_parent_two
|
48
|
+
has_many :children, as: :poly, dependent: :destroy, inverse_of: :poly
|
49
|
+
end
|
50
|
+
define_viewmodel do
|
51
|
+
attributes :num
|
52
|
+
association :children
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
build_viewmodel(:Grandparent) do
|
57
|
+
define_schema do |t|
|
58
|
+
t.integer :poly_parent_one_id
|
59
|
+
t.integer :poly_parent_two_id
|
60
|
+
end
|
61
|
+
define_model do
|
62
|
+
belongs_to :poly_parent_one, dependent: :destroy, inverse_of: :grandparent
|
63
|
+
belongs_to :poly_parent_two, dependent: :destroy, inverse_of: :grandparent
|
64
|
+
end
|
65
|
+
define_viewmodel do
|
66
|
+
associations :poly_parent_one, :poly_parent_two
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def setup
|
72
|
+
super
|
73
|
+
@parent1 = PolyParentOne.create(text: "p1", child: Child.new(text: "c1"))
|
74
|
+
@parent2 = PolyParentTwo.create(num: 2, children: [Child.new(text: "c2"), Child.new(text: "c3")])
|
75
|
+
@grandparent = Grandparent.create(poly_parent_one: @parent1, poly_parent_two: @parent2)
|
76
|
+
enable_logging!
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_create_has_one_from_view
|
80
|
+
p1_view = {
|
81
|
+
"_type" => "PolyParentOne",
|
82
|
+
"text" => "p",
|
83
|
+
"child" => { "_type" => "Child", "text" => "c" }
|
84
|
+
}
|
85
|
+
p1v = PolyParentOneView.deserialize_from_view(p1_view)
|
86
|
+
p1 = p1v.model
|
87
|
+
|
88
|
+
assert(p1.present?)
|
89
|
+
assert(p1.child.present?)
|
90
|
+
assert_equal(p1, p1.child.poly)
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_create_has_many_from_view
|
94
|
+
p2_view = {
|
95
|
+
"_type" => "PolyParentTwo",
|
96
|
+
"num" => "2",
|
97
|
+
"children" => [{ "_type" => "Child", "text" => "c1" }, { "_type" => "Child", "text" => "c2" }]
|
98
|
+
}
|
99
|
+
p2v = PolyParentTwoView.deserialize_from_view(p2_view)
|
100
|
+
p2 = p2v.model
|
101
|
+
|
102
|
+
assert(p2.present?)
|
103
|
+
assert(p2.children.count == 2)
|
104
|
+
p2.children.each do |c|
|
105
|
+
assert_equal(p2, c.poly)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_move
|
110
|
+
# test that I can move a child from one type to another and the parent pointer/type is correctly updated.
|
111
|
+
alter_by_view!(GrandparentView, @grandparent) do |view, refs|
|
112
|
+
c1 = view["poly_parent_one"]["child"]
|
113
|
+
c2 = view["poly_parent_two"]["children"].pop
|
114
|
+
view["poly_parent_one"]["child"] = c2
|
115
|
+
view["poly_parent_two"]["children"].push(c1)
|
116
|
+
end
|
117
|
+
@grandparent.reload
|
118
|
+
assert_equal("c3", @grandparent.poly_parent_one.child.text)
|
119
|
+
assert_equal(["c1","c2"], @grandparent.poly_parent_two.children.map(&:text).sort)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
## Polymorphic pointer to child in parent (multiple types of child)
|
124
|
+
class PolyChildPointerTest < ActiveSupport::TestCase
|
125
|
+
include ARVMTestUtilities
|
126
|
+
def self.build_poly_children(arvm_test_case)
|
127
|
+
arvm_test_case.build_viewmodel(:PolyOne) do
|
128
|
+
define_schema do |t|
|
129
|
+
t.integer :number
|
130
|
+
end
|
131
|
+
|
132
|
+
define_model do
|
133
|
+
has_one :parent, as: :poly
|
134
|
+
end
|
135
|
+
|
136
|
+
define_viewmodel do
|
137
|
+
attributes :number
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
arvm_test_case.build_viewmodel(:PolyTwo) do
|
142
|
+
define_schema do |t|
|
143
|
+
t.string :text
|
144
|
+
end
|
145
|
+
|
146
|
+
define_model do
|
147
|
+
has_one :parent, as: :poly
|
148
|
+
end
|
149
|
+
|
150
|
+
define_viewmodel do
|
151
|
+
attributes :text
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.build_parent(arvm_test_case)
|
157
|
+
arvm_test_case.build_viewmodel(:Parent) do
|
158
|
+
define_schema do |t|
|
159
|
+
t.string :name
|
160
|
+
t.string :poly_type
|
161
|
+
t.integer :poly_id
|
162
|
+
end
|
163
|
+
|
164
|
+
define_model do
|
165
|
+
belongs_to :poly, polymorphic: true, dependent: :destroy, inverse_of: :parent
|
166
|
+
end
|
167
|
+
|
168
|
+
define_viewmodel do
|
169
|
+
attributes :name
|
170
|
+
association :poly, viewmodels: [PolyOneView, PolyTwoView]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
def before_all
|
177
|
+
super
|
178
|
+
self.class.build_poly_children(self)
|
179
|
+
self.class.build_parent(self)
|
180
|
+
end
|
181
|
+
|
182
|
+
def setup
|
183
|
+
super
|
184
|
+
|
185
|
+
@parent1 = Parent.create(name: "p1",
|
186
|
+
poly: PolyOne.new(number: 1))
|
187
|
+
|
188
|
+
@parent2 = Parent.create(name: "p2")
|
189
|
+
|
190
|
+
enable_logging!
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_loading_batching
|
194
|
+
Parent.create(name: "with PolyOne", poly: PolyOne.new)
|
195
|
+
Parent.create(name: "with PolyTwo", poly: PolyTwo.new)
|
196
|
+
|
197
|
+
log_queries do
|
198
|
+
serialize(ParentView.load)
|
199
|
+
end
|
200
|
+
assert_equal(['Parent Load', 'PolyOne Load', 'PolyTwo Load'],
|
201
|
+
logged_load_queries.sort)
|
202
|
+
end
|
203
|
+
|
204
|
+
def test_create_from_view
|
205
|
+
view = {
|
206
|
+
"_type" => "Parent",
|
207
|
+
"name" => "p",
|
208
|
+
"poly" => { "_type" => "PolyTwo", "text" => "pol" }
|
209
|
+
}
|
210
|
+
|
211
|
+
pv = ParentView.deserialize_from_view(view)
|
212
|
+
p = pv.model
|
213
|
+
|
214
|
+
assert(!p.changed?)
|
215
|
+
assert(!p.new_record?)
|
216
|
+
|
217
|
+
assert_equal("p", p.name)
|
218
|
+
|
219
|
+
assert(p.poly.present?)
|
220
|
+
assert(p.poly.is_a?(PolyTwo))
|
221
|
+
assert_equal("pol", p.poly.text)
|
222
|
+
end
|
223
|
+
|
224
|
+
|
225
|
+
def test_serialize_view
|
226
|
+
view, _refs = serialize_with_references(ParentView.new(@parent1))
|
227
|
+
|
228
|
+
assert_equal({ "_type" => "Parent",
|
229
|
+
"_version" => 1,
|
230
|
+
"id" => @parent1.id,
|
231
|
+
"name" => @parent1.name,
|
232
|
+
"poly" => { "_type" => @parent1.poly_type,
|
233
|
+
"_version" => 1,
|
234
|
+
"id" => @parent1.poly.id,
|
235
|
+
"number" => @parent1.poly.number }
|
236
|
+
},
|
237
|
+
view)
|
238
|
+
end
|
239
|
+
|
240
|
+
# TODO(review): is this test worth keeping?
|
241
|
+
def test_invalid_type_lookup_with_version
|
242
|
+
ex = assert_raises do
|
243
|
+
ParentView.deserialize_from_view(
|
244
|
+
{ '_type' => 'Parent',
|
245
|
+
'_new' => true,
|
246
|
+
'poly' => {
|
247
|
+
'_type' => 'SomethingThatsNotActuallyAType',
|
248
|
+
'_version' => 1,
|
249
|
+
} })
|
250
|
+
end
|
251
|
+
assert_match(/\binvalid\b.+\bviewmodel type\b/i, ex.message)
|
252
|
+
end
|
253
|
+
|
254
|
+
def test_change_polymorphic_type
|
255
|
+
old_poly = @parent1.poly
|
256
|
+
|
257
|
+
alter_by_view!(ParentView, @parent1) do |view, refs|
|
258
|
+
view['poly'] = { '_type' => 'PolyTwo', 'text' => 'hi' }
|
259
|
+
end
|
260
|
+
|
261
|
+
assert_instance_of(PolyTwo, @parent1.poly)
|
262
|
+
assert_equal(false, PolyOne.exists?(old_poly.id))
|
263
|
+
end
|
264
|
+
|
265
|
+
class RenameTest < ActiveSupport::TestCase
|
266
|
+
include ARVMTestUtilities
|
267
|
+
|
268
|
+
def before_all
|
269
|
+
super
|
270
|
+
|
271
|
+
ViewModel::ActiveRecord::PolyTest::PolyChildPointerTest.build_poly_children(self)
|
272
|
+
|
273
|
+
build_viewmodel(:Parent) do
|
274
|
+
define_schema do |t|
|
275
|
+
t.string :name
|
276
|
+
t.string :poly_type
|
277
|
+
t.integer :poly_id
|
278
|
+
end
|
279
|
+
|
280
|
+
define_model do
|
281
|
+
belongs_to :poly, polymorphic: true, dependent: :destroy, inverse_of: :parent
|
282
|
+
end
|
283
|
+
|
284
|
+
define_viewmodel do
|
285
|
+
attributes :name
|
286
|
+
association :poly, viewmodels: [PolyOneView, PolyTwoView], as: :something_else
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def setup
|
292
|
+
super
|
293
|
+
|
294
|
+
@parent = Parent.create(poly: PolyOne.create(number: 42))
|
295
|
+
|
296
|
+
enable_logging!
|
297
|
+
end
|
298
|
+
|
299
|
+
def test_dependencies
|
300
|
+
root_updates, _ref_updates = ViewModel::ActiveRecord::UpdateData.parse_hashes([{ '_type' => 'Parent', 'something_else' => nil }])
|
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
|
+
end
|
304
|
+
|
305
|
+
def test_renamed_roundtrip
|
306
|
+
alter_by_view!(ParentView, @parent) do |view, refs|
|
307
|
+
assert_equal({ 'id' => @parent.id,
|
308
|
+
'_type' => 'PolyOne',
|
309
|
+
'_version' => 1,
|
310
|
+
'number' => 42 },
|
311
|
+
view['something_else'])
|
312
|
+
view['something_else'] = {'_type' => 'PolyTwo', 'text' => 'hi'}
|
313
|
+
end
|
314
|
+
|
315
|
+
assert_equal('hi', @parent.poly.text)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
end
|
@@ -0,0 +1,285 @@
|
|
1
|
+
require_relative "../../../helpers/arvm_test_utilities.rb"
|
2
|
+
require_relative "../../../helpers/arvm_test_models.rb"
|
3
|
+
|
4
|
+
require "minitest/autorun"
|
5
|
+
|
6
|
+
require "view_model/active_record"
|
7
|
+
|
8
|
+
class ViewModel::ActiveRecord::SharedTest < ActiveSupport::TestCase
|
9
|
+
include ARVMTestUtilities
|
10
|
+
|
11
|
+
module WithCategory
|
12
|
+
def before_all
|
13
|
+
super
|
14
|
+
|
15
|
+
build_viewmodel(:Category) do
|
16
|
+
define_schema do |t|
|
17
|
+
t.string :name
|
18
|
+
end
|
19
|
+
|
20
|
+
define_model do
|
21
|
+
has_many :parents
|
22
|
+
end
|
23
|
+
|
24
|
+
define_viewmodel do
|
25
|
+
attributes :name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module WithParent
|
32
|
+
def before_all
|
33
|
+
super
|
34
|
+
|
35
|
+
build_viewmodel(:Parent) do
|
36
|
+
define_schema do |t|
|
37
|
+
t.string :name
|
38
|
+
t.references :category, foreign_key: true # shared reference
|
39
|
+
end
|
40
|
+
|
41
|
+
define_model do
|
42
|
+
belongs_to :category
|
43
|
+
end
|
44
|
+
|
45
|
+
define_viewmodel do
|
46
|
+
attributes :name
|
47
|
+
association :category, shared: true, optional: true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
include WithCategory
|
54
|
+
include WithParent
|
55
|
+
|
56
|
+
def setup
|
57
|
+
super
|
58
|
+
|
59
|
+
@parent1 = Parent.create(name: "p1",
|
60
|
+
category: Category.new(name: "p1cat"))
|
61
|
+
|
62
|
+
@parent2 = Parent.create(name: "p2")
|
63
|
+
|
64
|
+
@category1 = Category.create(name: "Cat1")
|
65
|
+
|
66
|
+
enable_logging!
|
67
|
+
end
|
68
|
+
|
69
|
+
def serialize_context
|
70
|
+
ParentView.new_serialize_context(include: :category)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_loading_batching
|
74
|
+
Parent.create(category: Category.new)
|
75
|
+
|
76
|
+
log_queries do
|
77
|
+
serialize(ParentView.load(serialize_context: serialize_context),
|
78
|
+
serialize_context: serialize_context)
|
79
|
+
end
|
80
|
+
assert_equal(['Parent Load', 'Category Load'],
|
81
|
+
logged_load_queries)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_create_from_view
|
85
|
+
view = {
|
86
|
+
"_type" => "Parent",
|
87
|
+
"name" => "p",
|
88
|
+
"category" => { "_ref" => "r1" },
|
89
|
+
}
|
90
|
+
refs = {
|
91
|
+
"r1" => { "_type" => "Category", "name" => "newcat"}
|
92
|
+
}
|
93
|
+
|
94
|
+
pv = ParentView.deserialize_from_view(view, references: refs)
|
95
|
+
p = pv.model
|
96
|
+
|
97
|
+
assert(!p.changed?)
|
98
|
+
assert(!p.new_record?)
|
99
|
+
|
100
|
+
assert_equal("p", p.name)
|
101
|
+
|
102
|
+
assert(p.category.present?)
|
103
|
+
assert_equal("newcat", p.category.name)
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_serialize_view
|
107
|
+
view, refs = serialize_with_references(ParentView.new(@parent1), serialize_context: serialize_context)
|
108
|
+
cat1_ref = refs.detect { |_, v| v['_type'] == 'Category' }.first
|
109
|
+
|
110
|
+
assert_equal({cat1_ref => { '_type' => "Category",
|
111
|
+
"_version" => 1,
|
112
|
+
'id' => @parent1.category.id,
|
113
|
+
'name' => @parent1.category.name }},
|
114
|
+
refs)
|
115
|
+
|
116
|
+
assert_equal({ "_type" => "Parent",
|
117
|
+
"_version" => 1,
|
118
|
+
"id" => @parent1.id,
|
119
|
+
"name" => @parent1.name,
|
120
|
+
"category" => { "_ref" => cat1_ref } },
|
121
|
+
view)
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_shared_eager_include
|
125
|
+
parent_includes = ParentView.eager_includes
|
126
|
+
|
127
|
+
assert_equal(DeepPreloader::Spec.new, parent_includes)
|
128
|
+
|
129
|
+
extra_includes = ParentView.eager_includes(serialize_context: ParentView.new_serialize_context(include: :category))
|
130
|
+
|
131
|
+
assert_equal(DeepPreloader::Spec.new('category' => DeepPreloader::Spec.new), extra_includes)
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_shared_serialize_interning
|
135
|
+
@parent2.update(category: @parent1.category)
|
136
|
+
view, refs = serialize_with_references([ParentView.new(@parent1),
|
137
|
+
ParentView.new(@parent2)],
|
138
|
+
serialize_context: ParentView.new_serialize_context(include: :category))
|
139
|
+
|
140
|
+
category_ref = view.first['category']['_ref']
|
141
|
+
|
142
|
+
assert_equal([category_ref], refs.keys,
|
143
|
+
'category referenced twice generates a single reference')
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_shared_add_reference
|
147
|
+
alter_by_view!(ParentView, @parent2, serialize_context: serialize_context) do |p2view, refs|
|
148
|
+
p2view['category'] = { '_ref' => 'myref' }
|
149
|
+
refs['myref'] = update_hash_for(CategoryView, @category1)
|
150
|
+
end
|
151
|
+
|
152
|
+
assert_equal(@category1, @parent2.category)
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_shared_add_multiple_references
|
156
|
+
alter_by_view!(ParentView, [@parent1, @parent2], serialize_context: serialize_context) do |(p1view, p2view), refs|
|
157
|
+
refs.delete(p1view['category']['_ref'])
|
158
|
+
refs['myref'] = update_hash_for(CategoryView, @category1)
|
159
|
+
|
160
|
+
p1view['category'] = { '_ref' => 'myref' }
|
161
|
+
p2view['category'] = { '_ref' => 'myref' }
|
162
|
+
end
|
163
|
+
|
164
|
+
assert_equal(@category1, @parent1.category)
|
165
|
+
assert_equal(@category1, @parent2.category)
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_shared_requires_all_references
|
169
|
+
ex = assert_raises(ViewModel::DeserializationError::InvalidStructure) do
|
170
|
+
alter_by_view!(ParentView, @parent2, serialize_context: serialize_context) do |p2view, refs|
|
171
|
+
refs['spurious_ref'] = { '_type' => 'Parent', 'id' => @parent1.id }
|
172
|
+
end
|
173
|
+
end
|
174
|
+
assert_match(/References not referred to from roots/, ex.message)
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_shared_requires_valid_references
|
178
|
+
assert_raises(ViewModel::DeserializationError::InvalidSharedReference) do
|
179
|
+
serialize_context = ParentView.new_serialize_context(include: :category)
|
180
|
+
alter_by_view!(ParentView, @parent1, serialize_context: serialize_context) do |p1view, refs|
|
181
|
+
refs.clear # remove the expected serialized refs
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def test_shared_requires_assignable_type
|
187
|
+
ex = assert_raises(ViewModel::DeserializationError::InvalidAssociationType) do
|
188
|
+
serialize_context = ParentView.new_serialize_context(include: :category)
|
189
|
+
alter_by_view!(ParentView, @parent1, serialize_context: serialize_context) do |p1view, refs|
|
190
|
+
p1view['category'] = { '_ref' => 'p2' }
|
191
|
+
refs['p2'] = update_hash_for(ParentView, @parent2)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
assert_equal("category", ex.association)
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_shared_requires_unique_references
|
198
|
+
serialize_context = ParentView.new_serialize_context(include: :category)
|
199
|
+
c1_ref = update_hash_for(CategoryView, @category1)
|
200
|
+
assert_raises(ViewModel::DeserializationError::DuplicateNodes) do
|
201
|
+
alter_by_view!(ParentView, [@parent1, @parent2], serialize_context: serialize_context) do |(p1view, p2view), refs|
|
202
|
+
refs['c_a'] = c1_ref.dup
|
203
|
+
refs['c_b'] = c1_ref.dup
|
204
|
+
p1view['category'] = { '_ref' => 'c_a' }
|
205
|
+
p2view['category'] = { '_ref' => 'c_b' }
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def test_shared_updates_shared_data
|
211
|
+
serialize_context = ParentView.new_serialize_context(include: :category)
|
212
|
+
alter_by_view!(ParentView, @parent1, serialize_context: serialize_context) do |p1view, refs|
|
213
|
+
category_ref = p1view['category']['_ref']
|
214
|
+
refs[category_ref]['name'] = 'newcatname'
|
215
|
+
end
|
216
|
+
assert_equal('newcatname', @parent1.category.name)
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_shared_delete_reference
|
220
|
+
serialize_context = ParentView.new_serialize_context(include: :category)
|
221
|
+
alter_by_view!(ParentView, @parent1, serialize_context: serialize_context) do |p1view, refs|
|
222
|
+
category_ref = p1view['category']['_ref']
|
223
|
+
refs.delete(category_ref)
|
224
|
+
p1view['category'] = nil
|
225
|
+
end
|
226
|
+
assert_nil(@parent1.category)
|
227
|
+
assert(Category.where(id: @category1.id).present?)
|
228
|
+
end
|
229
|
+
|
230
|
+
def test_child_edit_doesnt_editcheck_parent
|
231
|
+
serialize_context = ParentView.new_serialize_context(include: :category)
|
232
|
+
d_context = ParentView.new_deserialize_context
|
233
|
+
|
234
|
+
alter_by_view!(ParentView, @parent1, serialize_context: serialize_context, deserialize_context: d_context) do |view, refs|
|
235
|
+
refs[view['category']["_ref"]]["name"] = "changed"
|
236
|
+
end
|
237
|
+
|
238
|
+
assert(d_context.valid_edit_refs.include?(ViewModel::Reference.new(CategoryView, @parent1.category.id)))
|
239
|
+
refute(d_context.valid_edit_refs.include?(ViewModel::Reference.new(ParentView, @parent1.id)))
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_child_change_editchecks_parent
|
243
|
+
s_context = ParentView.new_serialize_context(include: :category)
|
244
|
+
|
245
|
+
nv, d_context = alter_by_view!(ParentView, @parent1, serialize_context: s_context) do |view, refs|
|
246
|
+
refs.delete(view['category']['_ref'])
|
247
|
+
view['category']['_ref'] = 'new_cat'
|
248
|
+
refs['new_cat'] = { '_type' => 'Category', 'name' => 'new category' }
|
249
|
+
end
|
250
|
+
|
251
|
+
assert(d_context.valid_edit_refs.include?(nv.to_reference))
|
252
|
+
assert(d_context.valid_edit_refs.include?(nv.category.to_reference))
|
253
|
+
end
|
254
|
+
|
255
|
+
def test_child_delete_editchecks_parent
|
256
|
+
serialize_context = ParentView.new_serialize_context(include: :category)
|
257
|
+
d_context = ParentView.new_deserialize_context
|
258
|
+
|
259
|
+
alter_by_view!(ParentView, @parent1, serialize_context: serialize_context, deserialize_context: d_context) do |view, refs|
|
260
|
+
refs.delete(view['category']['_ref'])
|
261
|
+
view['category'] = nil
|
262
|
+
end
|
263
|
+
|
264
|
+
assert(d_context.valid_edit_refs.include?(ViewModel::Reference.new(ParentView, @parent1.id)))
|
265
|
+
end
|
266
|
+
|
267
|
+
def test_dependent_viewmodels
|
268
|
+
deps = ParentView.dependent_viewmodels
|
269
|
+
assert_equal([ParentView, CategoryView].to_set, deps)
|
270
|
+
|
271
|
+
deps = ParentView.dependent_viewmodels(include_shared: false)
|
272
|
+
assert_equal([ParentView].to_set, deps)
|
273
|
+
end
|
274
|
+
|
275
|
+
def test_deep_schema_version
|
276
|
+
vers = ParentView.deep_schema_version
|
277
|
+
assert_equal({ ParentView.view_name => ParentView.schema_version,
|
278
|
+
CategoryView.view_name => CategoryView.schema_version },
|
279
|
+
vers)
|
280
|
+
|
281
|
+
vers = ParentView.deep_schema_version(include_shared: false)
|
282
|
+
assert_equal({ ParentView.view_name => ParentView.schema_version },
|
283
|
+
vers)
|
284
|
+
end
|
285
|
+
end
|