lutaml-model 0.3.24 → 0.3.25
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/.rubocop_todo.yml +35 -16
- data/README.adoc +274 -28
- data/lib/lutaml/model/attribute.rb +18 -8
- data/lib/lutaml/model/error/type_error.rb +9 -0
- data/lib/lutaml/model/error/unknown_type_error.rb +9 -0
- data/lib/lutaml/model/error/validation_error.rb +0 -1
- data/lib/lutaml/model/error.rb +2 -0
- data/lib/lutaml/model/serialize.rb +6 -1
- data/lib/lutaml/model/type/boolean.rb +38 -0
- data/lib/lutaml/model/type/date.rb +35 -0
- data/lib/lutaml/model/type/date_time.rb +32 -4
- data/lib/lutaml/model/type/decimal.rb +42 -0
- data/lib/lutaml/model/type/float.rb +37 -0
- data/lib/lutaml/model/type/hash.rb +62 -0
- data/lib/lutaml/model/type/integer.rb +41 -0
- data/lib/lutaml/model/type/string.rb +49 -0
- data/lib/lutaml/model/type/time.rb +49 -0
- data/lib/lutaml/model/type/time_without_date.rb +37 -5
- data/lib/lutaml/model/type/value.rb +52 -0
- data/lib/lutaml/model/type.rb +50 -114
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +5 -2
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +2 -1
- data/lib/lutaml/model/xml_adapter/xml_document.rb +0 -2
- data/lutaml-model.gemspec +1 -1
- data/spec/address_spec.rb +170 -0
- data/spec/fixtures/address.rb +33 -0
- data/spec/fixtures/person.rb +73 -0
- data/spec/fixtures/sample_model.rb +40 -0
- data/spec/fixtures/vase.rb +38 -0
- data/spec/fixtures/xml/special_char.xml +13 -0
- data/spec/lutaml/model/attribute_spec.rb +112 -0
- data/spec/lutaml/model/collection_spec.rb +299 -0
- data/spec/lutaml/model/comparable_model_spec.rb +106 -0
- data/spec/lutaml/model/custom_model_spec.rb +410 -0
- data/spec/lutaml/model/custom_serialization_spec.rb +170 -0
- data/spec/lutaml/model/defaults_spec.rb +221 -0
- data/spec/lutaml/model/delegation_spec.rb +340 -0
- data/spec/lutaml/model/inheritance_spec.rb +92 -0
- data/spec/lutaml/model/json_adapter_spec.rb +37 -0
- data/spec/lutaml/model/key_value_mapping_spec.rb +86 -0
- data/spec/lutaml/model/map_content_spec.rb +118 -0
- data/spec/lutaml/model/mixed_content_spec.rb +625 -0
- data/spec/lutaml/model/namespace_spec.rb +57 -0
- data/spec/lutaml/model/ordered_content_spec.rb +83 -0
- data/spec/lutaml/model/render_nil_spec.rb +138 -0
- data/spec/lutaml/model/schema/json_schema_spec.rb +79 -0
- data/spec/lutaml/model/schema/relaxng_schema_spec.rb +60 -0
- data/spec/lutaml/model/schema/xsd_schema_spec.rb +55 -0
- data/spec/lutaml/model/schema/yaml_schema_spec.rb +47 -0
- data/spec/lutaml/model/serializable_spec.rb +297 -0
- data/spec/lutaml/model/serializable_validation_spec.rb +85 -0
- data/spec/lutaml/model/simple_model_spec.rb +314 -0
- data/spec/lutaml/model/toml_adapter_spec.rb +39 -0
- data/spec/lutaml/model/type/boolean_spec.rb +54 -0
- data/spec/lutaml/model/type/date_spec.rb +118 -0
- data/spec/lutaml/model/type/date_time_spec.rb +127 -0
- data/spec/lutaml/model/type/decimal_spec.rb +125 -0
- data/spec/lutaml/model/type/float_spec.rb +191 -0
- data/spec/lutaml/model/type/hash_spec.rb +63 -0
- data/spec/lutaml/model/type/integer_spec.rb +145 -0
- data/spec/lutaml/model/type/string_spec.rb +150 -0
- data/spec/lutaml/model/type/time_spec.rb +142 -0
- data/spec/lutaml/model/type/time_without_date_spec.rb +125 -0
- data/spec/lutaml/model/type_spec.rb +276 -0
- data/spec/lutaml/model/utils_spec.rb +79 -0
- data/spec/lutaml/model/validation_spec.rb +83 -0
- data/spec/lutaml/model/with_child_mapping_spec.rb +174 -0
- data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +56 -0
- data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +56 -0
- data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +61 -0
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +251 -0
- data/spec/lutaml/model/xml_adapter_spec.rb +178 -0
- data/spec/lutaml/model/xml_mapping_spec.rb +863 -0
- data/spec/lutaml/model/yaml_adapter_spec.rb +30 -0
- data/spec/lutaml/model_spec.rb +1 -0
- data/spec/person_spec.rb +161 -0
- data/spec/spec_helper.rb +33 -0
- metadata +66 -2
@@ -0,0 +1,625 @@
|
|
1
|
+
# spec/lutaml/model/mixed_content_spec.rb
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "lutaml/model"
|
5
|
+
require "lutaml/model/xml_adapter/nokogiri_adapter"
|
6
|
+
require "lutaml/model/xml_adapter/ox_adapter"
|
7
|
+
require "lutaml/model/xml_adapter/oga_adapter"
|
8
|
+
require_relative "../../fixtures/sample_model"
|
9
|
+
|
10
|
+
module MixedContentSpec
|
11
|
+
class PlanetaryBody < Lutaml::Model::Serializable
|
12
|
+
attribute :name, :string
|
13
|
+
attribute :distance_from_earth, :integer
|
14
|
+
xml do
|
15
|
+
root "PlanetaryBody"
|
16
|
+
map_element "Name", to: :name
|
17
|
+
map_element "DistanceFromEarth", to: :distance_from_earth
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Source < Lutaml::Model::Serializable
|
22
|
+
attribute :content, :string
|
23
|
+
|
24
|
+
xml do
|
25
|
+
root "source"
|
26
|
+
map_content to: :content
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class ElementCitation < Lutaml::Model::Serializable
|
31
|
+
attribute :source, Source
|
32
|
+
|
33
|
+
xml do
|
34
|
+
root "element-citation"
|
35
|
+
map_element "source", to: :source
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Ref < Lutaml::Model::Serializable
|
40
|
+
attribute :element_citation, ElementCitation
|
41
|
+
|
42
|
+
xml do
|
43
|
+
root "ref"
|
44
|
+
map_element "element-citation", to: :element_citation
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class RefList < Lutaml::Model::Serializable
|
49
|
+
attribute :ref, Ref
|
50
|
+
|
51
|
+
xml do
|
52
|
+
root "ref-list"
|
53
|
+
map_element "ref", to: :ref
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Back < Lutaml::Model::Serializable
|
58
|
+
attribute :ref_list, RefList
|
59
|
+
|
60
|
+
xml do
|
61
|
+
root "back"
|
62
|
+
map_element "ref-list", to: :ref_list
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Article < Lutaml::Model::Serializable
|
67
|
+
attribute :back, Back
|
68
|
+
|
69
|
+
xml do
|
70
|
+
root "article"
|
71
|
+
map_element "back", to: :back
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class SpecialCharContentWithMixedTrue < Lutaml::Model::Serializable
|
76
|
+
attribute :content, :string
|
77
|
+
|
78
|
+
xml do
|
79
|
+
root "SpecialCharContentWithMixedTrue", mixed: true
|
80
|
+
map_content to: :content
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class SpecialCharContentWithRawAndMixedOption < Lutaml::Model::Serializable
|
85
|
+
attribute :special, :string, raw: true
|
86
|
+
|
87
|
+
xml do
|
88
|
+
root "SpecialCharContentWithRawOptionAndMixedOption", mixed: true
|
89
|
+
map_element :special, to: :special
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class RootMixedContent < Lutaml::Model::Serializable
|
94
|
+
attribute :id, :string
|
95
|
+
attribute :bold, :string, collection: true
|
96
|
+
attribute :italic, :string, collection: true
|
97
|
+
attribute :underline, :string
|
98
|
+
attribute :content, :string
|
99
|
+
|
100
|
+
xml do
|
101
|
+
root "RootMixedContent", mixed: true
|
102
|
+
map_attribute :id, to: :id
|
103
|
+
map_element :bold, to: :bold
|
104
|
+
map_element :italic, to: :italic
|
105
|
+
map_element :underline, to: :underline
|
106
|
+
map_content to: :content
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class RootMixedContentWithModel < Lutaml::Model::Serializable
|
111
|
+
attribute :id, :string
|
112
|
+
attribute :bold, :string, collection: true
|
113
|
+
attribute :italic, :string, collection: true
|
114
|
+
attribute :underline, :string
|
115
|
+
attribute :planetary_body, PlanetaryBody
|
116
|
+
attribute :content, :string
|
117
|
+
|
118
|
+
xml do
|
119
|
+
root "RootMixedContentWithModel", mixed: true
|
120
|
+
map_content to: :content
|
121
|
+
map_attribute :id, to: :id
|
122
|
+
map_element :bold, to: :bold
|
123
|
+
map_element :italic, to: :italic
|
124
|
+
map_element :underline, to: :underline
|
125
|
+
map_element "PlanetaryBody", to: :planetary_body
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class RootMixedContentNested < Lutaml::Model::Serializable
|
130
|
+
attribute :id, :string
|
131
|
+
attribute :text, :string
|
132
|
+
attribute :content, RootMixedContent
|
133
|
+
attribute :sup, :string, collection: true
|
134
|
+
attribute :sub, :string, collection: true
|
135
|
+
|
136
|
+
xml do
|
137
|
+
root "RootMixedContentNested", mixed: true
|
138
|
+
map_content to: :text
|
139
|
+
map_attribute :id, to: :id
|
140
|
+
map_element :sup, to: :sup
|
141
|
+
map_element :sub, to: :sub
|
142
|
+
map_element "MixedContent", to: :content
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class RootMixedContentNestedWithModel < Lutaml::Model::Serializable
|
147
|
+
attribute :id, :string
|
148
|
+
attribute :text, :string
|
149
|
+
attribute :content, RootMixedContentWithModel
|
150
|
+
attribute :sup, :string, collection: true
|
151
|
+
attribute :sub, :string, collection: true
|
152
|
+
|
153
|
+
xml do
|
154
|
+
root "RootMixedContentNestedWithModel", mixed: true
|
155
|
+
|
156
|
+
map_content to: :text
|
157
|
+
map_attribute :id, to: :id
|
158
|
+
map_element :sup, to: :sup
|
159
|
+
map_element :sub, to: :sub
|
160
|
+
map_element "MixedContentWithModel", to: :content
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class TextualSupport < Lutaml::Model::Serializable
|
165
|
+
attribute :value, :string
|
166
|
+
|
167
|
+
xml do
|
168
|
+
root "TextualSupport"
|
169
|
+
|
170
|
+
map_element :value, to: :value
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
RSpec.describe "MixedContent" do
|
176
|
+
shared_examples "mixed content behavior" do |adapter_class|
|
177
|
+
around do |example|
|
178
|
+
old_adapter = Lutaml::Model::Config.xml_adapter
|
179
|
+
Lutaml::Model::Config.xml_adapter = adapter_class
|
180
|
+
example.run
|
181
|
+
ensure
|
182
|
+
Lutaml::Model::Config.xml_adapter = old_adapter
|
183
|
+
end
|
184
|
+
|
185
|
+
context "when mixed: true is set at root" do
|
186
|
+
let(:xml) do
|
187
|
+
<<~XML
|
188
|
+
<RootMixedContent id="123">
|
189
|
+
The Earth's Moon rings like a <bold>bell</bold> when struck by
|
190
|
+
meteroids. Distanced from the Earth by <italic>384,400 km</italic>,
|
191
|
+
its surface is covered in <underline>craters</underline>.
|
192
|
+
Ain't that <bold>cool</bold>?
|
193
|
+
</RootMixedContent>
|
194
|
+
XML
|
195
|
+
end
|
196
|
+
|
197
|
+
it "deserializes and serializes mixed content correctly" do
|
198
|
+
parsed = MixedContentSpec::RootMixedContent.from_xml(xml)
|
199
|
+
|
200
|
+
expected_content = [
|
201
|
+
"\n The Earth's Moon rings like a ",
|
202
|
+
" when struck by\n meteroids. Distanced from the Earth by ",
|
203
|
+
",\n its surface is covered in ",
|
204
|
+
".\n Ain't that ",
|
205
|
+
"?\n",
|
206
|
+
]
|
207
|
+
|
208
|
+
expect(parsed.id).to eq("123")
|
209
|
+
expect(parsed.bold).to eq(["bell", "cool"])
|
210
|
+
expect(parsed.italic).to eq(["384,400 km"])
|
211
|
+
expect(parsed.underline).to eq("craters")
|
212
|
+
|
213
|
+
parsed.content.each_with_index do |content, index|
|
214
|
+
expected_output = expected_content[index]
|
215
|
+
|
216
|
+
# due to the difference in capturing
|
217
|
+
# newlines in ox and nokogiri adapters
|
218
|
+
if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
|
219
|
+
expected_output = expected_output.gsub(/\n\s*/, " ")
|
220
|
+
end
|
221
|
+
|
222
|
+
expect(content).to eq(expected_output)
|
223
|
+
end
|
224
|
+
|
225
|
+
serialized = parsed.to_xml
|
226
|
+
expect(serialized).to be_equivalent_to(xml)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
context "when mixed: true is set at root with nested model" do
|
231
|
+
let(:xml) do
|
232
|
+
<<~XML
|
233
|
+
<RootMixedContentWithModel id="123">
|
234
|
+
The Earth's Moon rings like a <bold>bell</bold> when struck by
|
235
|
+
meteroids. Distanced from the Earth by <italic>384,400 km</italic>,
|
236
|
+
its surface is covered in <underline>craters</underline>.
|
237
|
+
Ain't that <bold>cool</bold>?
|
238
|
+
<PlanetaryBody>
|
239
|
+
<Name>Moon</Name>
|
240
|
+
<DistanceFromEarth>384400</DistanceFromEarth>
|
241
|
+
</PlanetaryBody>
|
242
|
+
NOTE: The above model content is to be formatted as a table.
|
243
|
+
</RootMixedContentWithModel>
|
244
|
+
XML
|
245
|
+
end
|
246
|
+
|
247
|
+
it "deserializes and serializes mixed content correctly" do
|
248
|
+
parsed = MixedContentSpec::RootMixedContentWithModel.from_xml(xml)
|
249
|
+
|
250
|
+
expected_content = [
|
251
|
+
"\n The Earth's Moon rings like a ",
|
252
|
+
" when struck by\n meteroids. Distanced from the Earth by ",
|
253
|
+
",\n its surface is covered in ",
|
254
|
+
".\n Ain't that ",
|
255
|
+
"?\n ",
|
256
|
+
"\n NOTE: The above model content is to be formatted as a table.\n",
|
257
|
+
]
|
258
|
+
|
259
|
+
expect(parsed.id).to eq("123")
|
260
|
+
expect(parsed.bold).to eq(["bell", "cool"])
|
261
|
+
expect(parsed.italic).to eq(["384,400 km"])
|
262
|
+
expect(parsed.underline).to eq("craters")
|
263
|
+
|
264
|
+
parsed.content.each_with_index do |content, index|
|
265
|
+
expected_output = expected_content[index]
|
266
|
+
|
267
|
+
# due to the difference in capturing
|
268
|
+
# newlines in ox and nokogiri adapters
|
269
|
+
if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
|
270
|
+
expected_output = expected_output.gsub(/\n\s*/, " ")
|
271
|
+
end
|
272
|
+
|
273
|
+
expect(content).to eq(expected_output)
|
274
|
+
end
|
275
|
+
|
276
|
+
expect(parsed.planetary_body.name).to eq("Moon")
|
277
|
+
expect(parsed.planetary_body.distance_from_earth).to eq(384400)
|
278
|
+
|
279
|
+
serialized = parsed.to_xml
|
280
|
+
expect(serialized).to be_equivalent_to(xml)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
context "when mixed: true is set for nested content" do
|
285
|
+
let(:xml) do
|
286
|
+
<<~XML
|
287
|
+
<RootMixedContentNested id="outer123">
|
288
|
+
The following text is about the Moon.
|
289
|
+
<MixedContent id="inner456">
|
290
|
+
The Earth's Moon rings like a <bold>bell</bold> when struck by
|
291
|
+
meteroids. Distanced from the Earth by <italic>384,400 km</italic>,
|
292
|
+
its surface is covered in <underline>craters</underline>.
|
293
|
+
Ain't that <bold>cool</bold>?
|
294
|
+
</MixedContent>
|
295
|
+
<sup>1</sup>: The Moon is not a planet.
|
296
|
+
<sup>2</sup>: The Moon's atmosphere is mainly composed of helium in the form of He<sub>2</sub>.
|
297
|
+
</RootMixedContentNested>
|
298
|
+
XML
|
299
|
+
end
|
300
|
+
|
301
|
+
it "deserializes and serializes mixed content correctly" do
|
302
|
+
parsed = MixedContentSpec::RootMixedContentNested.from_xml(xml)
|
303
|
+
|
304
|
+
expected_content = [
|
305
|
+
"\n The Earth's Moon rings like a ",
|
306
|
+
" when struck by\n meteroids. Distanced from the Earth by ",
|
307
|
+
",\n its surface is covered in ",
|
308
|
+
".\n Ain't that ",
|
309
|
+
"?\n ",
|
310
|
+
]
|
311
|
+
|
312
|
+
expect(parsed.id).to eq("outer123")
|
313
|
+
expect(parsed.sup).to eq(["1", "2"])
|
314
|
+
expect(parsed.sub).to eq(["2"])
|
315
|
+
expect(parsed.content.id).to eq("inner456")
|
316
|
+
expect(parsed.content.bold).to eq(["bell", "cool"])
|
317
|
+
expect(parsed.content.italic).to eq(["384,400 km"])
|
318
|
+
expect(parsed.content.underline).to eq("craters")
|
319
|
+
|
320
|
+
parsed.content.content.each_with_index do |content, index|
|
321
|
+
expected_output = expected_content[index]
|
322
|
+
|
323
|
+
# due to the difference in capturing
|
324
|
+
# newlines in ox and nokogiri adapters
|
325
|
+
if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
|
326
|
+
expected_output = expected_output.gsub(/\n\s*/, " ")
|
327
|
+
end
|
328
|
+
|
329
|
+
expect(content).to eq(expected_output)
|
330
|
+
end
|
331
|
+
|
332
|
+
serialized = parsed.to_xml
|
333
|
+
expect(serialized).to be_equivalent_to(xml)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
context "when mixed: true is set for nested content with model" do
|
338
|
+
let(:xml) do
|
339
|
+
<<~XML
|
340
|
+
<RootMixedContentNestedWithModel id="outer123">
|
341
|
+
The following text is about the Moon.
|
342
|
+
<MixedContentWithModel id="inner456">
|
343
|
+
The Earth's Moon rings like a <bold>bell</bold> when struck by
|
344
|
+
meteroids. Distanced from the Earth by <italic>384,400 km</italic>,
|
345
|
+
its surface is covered in <underline>craters</underline>.
|
346
|
+
Ain't that <bold>cool</bold>?
|
347
|
+
<PlanetaryBody>
|
348
|
+
<Name>Moon</Name>
|
349
|
+
<DistanceFromEarth>384400</DistanceFromEarth>
|
350
|
+
</PlanetaryBody>
|
351
|
+
NOTE: The above model content is to be formatted as a table.
|
352
|
+
</MixedContentWithModel>
|
353
|
+
<sup>1</sup>: The Moon is not a planet.
|
354
|
+
<sup>2</sup>: The Moon's atmosphere is mainly composed of helium in the form of He<sub>2</sub>.
|
355
|
+
</RootMixedContentNestedWithModel>
|
356
|
+
XML
|
357
|
+
end
|
358
|
+
|
359
|
+
it "deserializes and serializes mixed content correctly" do
|
360
|
+
parsed = MixedContentSpec::RootMixedContentNestedWithModel.from_xml(xml)
|
361
|
+
|
362
|
+
expected_content = [
|
363
|
+
"\n The Earth's Moon rings like a ",
|
364
|
+
" when struck by\n meteroids. Distanced from the Earth by ",
|
365
|
+
",\n its surface is covered in ",
|
366
|
+
".\n Ain't that ",
|
367
|
+
"?\n ",
|
368
|
+
"\n NOTE: The above model content is to be formatted as a table.\n ",
|
369
|
+
]
|
370
|
+
|
371
|
+
expect(parsed.id).to eq("outer123")
|
372
|
+
expect(parsed.sup).to eq(["1", "2"])
|
373
|
+
expect(parsed.sub).to eq(["2"])
|
374
|
+
expect(parsed.content.id).to eq("inner456")
|
375
|
+
expect(parsed.content.bold).to eq(["bell", "cool"])
|
376
|
+
expect(parsed.content.italic).to eq(["384,400 km"])
|
377
|
+
expect(parsed.content.underline).to eq("craters")
|
378
|
+
|
379
|
+
parsed.content.content.each_with_index do |content, index|
|
380
|
+
expected_output = expected_content[index]
|
381
|
+
|
382
|
+
# due to the difference in capturing
|
383
|
+
# newlines in ox and nokogiri adapters
|
384
|
+
if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
|
385
|
+
expected_output = expected_output.gsub(/\n\s*/, " ")
|
386
|
+
end
|
387
|
+
|
388
|
+
expect(content).to eq(expected_output)
|
389
|
+
end
|
390
|
+
|
391
|
+
expect(parsed.content.planetary_body.name).to eq("Moon")
|
392
|
+
expect(parsed.content.planetary_body.distance_from_earth).to eq(384400)
|
393
|
+
|
394
|
+
serialized = parsed.to_xml
|
395
|
+
expect(serialized).to be_equivalent_to(xml)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
context "when mixed: true is used with map_element" do
|
400
|
+
it "raises an error" do
|
401
|
+
expect do
|
402
|
+
Class.new(Lutaml::Model::Serializable) do
|
403
|
+
attribute :id, :string
|
404
|
+
|
405
|
+
xml do
|
406
|
+
root "Invalid"
|
407
|
+
map_element :id, to: :id, mixed: true
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end.to raise_error(ArgumentError, /unknown keyword: :mixed/)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
context "when mixed: true is used with map_attribute" do
|
415
|
+
it "raises an error" do
|
416
|
+
expect do
|
417
|
+
Class.new(Lutaml::Model::Serializable) do
|
418
|
+
attribute :id, :string
|
419
|
+
|
420
|
+
xml do
|
421
|
+
root "Invalid"
|
422
|
+
map_attribute :id, to: :id, mixed: true
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end.to raise_error(ArgumentError, /unknown keyword: :mixed/)
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
context "when special char used in content with mixed true" do
|
430
|
+
let(:xml) do
|
431
|
+
<<~XML
|
432
|
+
<SpecialCharContentWithMixedTrue>
|
433
|
+
Moon&Mars Distanced©its — surface covered & processed
|
434
|
+
</SpecialCharContentWithMixedTrue>
|
435
|
+
XML
|
436
|
+
end
|
437
|
+
|
438
|
+
describe ".from_xml" do
|
439
|
+
let(:expected_content) { "Moon&Mars Distanced©its — surface covered & processed" }
|
440
|
+
|
441
|
+
it "deserializes special char mixed content correctly" do
|
442
|
+
parsed = MixedContentSpec::SpecialCharContentWithMixedTrue.from_xml(xml)
|
443
|
+
expect(parsed.content.strip).to eq(expected_content)
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
describe ".to_xml" do
|
448
|
+
let(:expected_xml) { "Moon&Mars Distanced©its — surface covered & processed" }
|
449
|
+
|
450
|
+
it "serializes special char mixed content correctly" do
|
451
|
+
parsed = MixedContentSpec::SpecialCharContentWithMixedTrue.from_xml(xml)
|
452
|
+
serialized = parsed.to_xml
|
453
|
+
|
454
|
+
expect(serialized).to include(expected_xml)
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
context "when special char used in content read from xml file" do
|
460
|
+
let(:fixture) { File.read(fixture_path("xml/special_char.xml")) }
|
461
|
+
|
462
|
+
describe ".from_xml" do
|
463
|
+
it "deserializes special char mixed content correctly" do
|
464
|
+
parsed = MixedContentSpec::Article.from_xml(fixture)
|
465
|
+
expect(parsed.back.ref_list.ref.element_citation.source.content).to include("R&D")
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
describe ".to_xml" do
|
470
|
+
it "serializes special char mixed content correctly" do
|
471
|
+
parsed = MixedContentSpec::Article.from_xml(fixture)
|
472
|
+
serialized = parsed.to_xml
|
473
|
+
|
474
|
+
expect(serialized).to include("R&D")
|
475
|
+
end
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
context "when special char entities used with raw true" do
|
480
|
+
let(:xml) do
|
481
|
+
<<~XML
|
482
|
+
<SpecialCharContentWithRawAndMixedOption>
|
483
|
+
<special>
|
484
|
+
B <p>R&C</p>
|
485
|
+
C <p>J—C</p>
|
486
|
+
O <p>A & B </p>
|
487
|
+
F <p>Z ©S</p>
|
488
|
+
</special>
|
489
|
+
</SpecialCharContentWithRawAndMixedOption>
|
490
|
+
XML
|
491
|
+
end
|
492
|
+
|
493
|
+
describe ".from_xml" do
|
494
|
+
let(:expected_nokogiri_content) { "B <p>R&C</p>\n C <p>J—C</p>\n O <p>A & B </p>\n F <p>Z ©S</p>" }
|
495
|
+
let(:expected_ox_content) { "B <p>R&C</p>\n C <p>J—C</p>\n O <p>A & B </p>\n F <p>Z ©S</p>" }
|
496
|
+
|
497
|
+
it "deserializes special char mixed content correctly" do
|
498
|
+
parsed = MixedContentSpec::SpecialCharContentWithRawAndMixedOption.from_xml(xml)
|
499
|
+
expected_content = expected_nokogiri_content
|
500
|
+
|
501
|
+
if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
|
502
|
+
expected_content = expected_ox_content
|
503
|
+
parsed.special.force_encoding("UTF-8")
|
504
|
+
end
|
505
|
+
|
506
|
+
expect(parsed.special.strip).to eq(expected_content)
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
describe ".to_xml" do
|
511
|
+
let(:expected_nokogiri_xml) do
|
512
|
+
<<~XML
|
513
|
+
<SpecialCharContentWithRawOptionAndMixedOption><special>
|
514
|
+
B <p>R&amp;C</p>
|
515
|
+
C <p>J&#x2014;C</p>
|
516
|
+
O <p>A &amp; B </p>
|
517
|
+
F <p>Z &#xA9;S</p>
|
518
|
+
</special></SpecialCharContentWithRawOptionAndMixedOption>
|
519
|
+
XML
|
520
|
+
end
|
521
|
+
|
522
|
+
let(:expected_ox_xml) do
|
523
|
+
<<~XML
|
524
|
+
<SpecialCharContentWithRawOptionAndMixedOption>
|
525
|
+
<special> B <p>R&amp;C</p>
|
526
|
+
C <p>J—C</p>
|
527
|
+
O <p>A &amp; B </p>
|
528
|
+
F <p>Z ©S</p>
|
529
|
+
</special>
|
530
|
+
</SpecialCharContentWithRawOptionAndMixedOption>
|
531
|
+
XML
|
532
|
+
end
|
533
|
+
|
534
|
+
it "serializes special char mixed content correctly" do
|
535
|
+
parsed = MixedContentSpec::SpecialCharContentWithRawAndMixedOption.from_xml(xml)
|
536
|
+
serialized = parsed.to_xml
|
537
|
+
|
538
|
+
expected_output = adapter_class == Lutaml::Model::XmlAdapter::OxAdapter ? expected_ox_xml : expected_nokogiri_xml
|
539
|
+
expect(serialized).to be_equivalent_to(expected_output)
|
540
|
+
end
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
context "when special char used with raw true, remove & if entity not provided" do
|
545
|
+
let(:xml) do
|
546
|
+
<<~XML
|
547
|
+
<SpecialCharContentWithRawAndMixedOption>
|
548
|
+
<special>
|
549
|
+
B <p>R&C</p>
|
550
|
+
</special>
|
551
|
+
</SpecialCharContentWithRawAndMixedOption>
|
552
|
+
XML
|
553
|
+
end
|
554
|
+
|
555
|
+
describe ".from_xml" do
|
556
|
+
let(:expected_nokogiri_content) { "B <p>R</p>" }
|
557
|
+
let(:expected_ox_content) { "B <p>R&C</p>" }
|
558
|
+
|
559
|
+
it "deserializes special char mixed content correctly" do
|
560
|
+
parsed = MixedContentSpec::SpecialCharContentWithRawAndMixedOption.from_xml(xml)
|
561
|
+
|
562
|
+
expected_output = adapter_class == Lutaml::Model::XmlAdapter::OxAdapter ? expected_ox_content : expected_nokogiri_content
|
563
|
+
expect(parsed.special.strip).to eq(expected_output)
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
describe ".to_xml" do
|
568
|
+
let(:expected_nokogiri_xml) { "B <p>R</p>" }
|
569
|
+
let(:expected_ox_xml) { "B <p>R&amp;C</p>" }
|
570
|
+
|
571
|
+
it "serializes special char mixed content correctly" do
|
572
|
+
parsed = MixedContentSpec::SpecialCharContentWithRawAndMixedOption.from_xml(xml)
|
573
|
+
serialized = parsed.to_xml
|
574
|
+
|
575
|
+
expected_output = adapter_class == Lutaml::Model::XmlAdapter::OxAdapter ? expected_ox_xml : expected_nokogiri_xml
|
576
|
+
expect(serialized).to include(expected_output)
|
577
|
+
end
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
context "when special char used as full entities" do
|
582
|
+
let(:xml) do
|
583
|
+
<<~XML
|
584
|
+
<TextualSupport>
|
585
|
+
<value><computer security> type of operation specified by an access right</value>
|
586
|
+
</TextualSupport>
|
587
|
+
XML
|
588
|
+
end
|
589
|
+
|
590
|
+
describe ".from_xml" do
|
591
|
+
let(:expected_content) { "<computer security> type of operation specified by an access right" }
|
592
|
+
|
593
|
+
it "deserializes special char mixed content correctly" do
|
594
|
+
parsed = MixedContentSpec::TextualSupport.from_xml(xml)
|
595
|
+
|
596
|
+
expect(parsed.value).to eq(expected_content)
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
describe ".to_xml" do
|
601
|
+
let(:expected_xml) { "<TextualSupport>\n <value><computer security> type of operation specified by an access right</value>\n</TextualSupport>" }
|
602
|
+
|
603
|
+
it "serializes special char mixed content correctly" do
|
604
|
+
parsed = MixedContentSpec::TextualSupport.from_xml(xml)
|
605
|
+
serialized = parsed.to_xml
|
606
|
+
|
607
|
+
expect(serialized.strip).to include(expected_xml)
|
608
|
+
end
|
609
|
+
end
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
describe Lutaml::Model::XmlAdapter::NokogiriAdapter do
|
614
|
+
it_behaves_like "mixed content behavior", described_class
|
615
|
+
end
|
616
|
+
|
617
|
+
describe Lutaml::Model::XmlAdapter::OxAdapter do
|
618
|
+
it_behaves_like "mixed content behavior", described_class
|
619
|
+
end
|
620
|
+
|
621
|
+
# Not implemented yet
|
622
|
+
xdescribe Lutaml::Model::XmlAdapter::OgaAdapter do
|
623
|
+
it_behaves_like "mixed content behavior", described_class
|
624
|
+
end
|
625
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module NamespaceSpec
|
4
|
+
class NestedChild < Lutaml::Model::Serializable
|
5
|
+
attribute :name, :string
|
6
|
+
|
7
|
+
xml do
|
8
|
+
root "NestedChild"
|
9
|
+
|
10
|
+
map_element :name, to: :name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Child < Lutaml::Model::Serializable
|
15
|
+
attribute :nested_child, NestedChild
|
16
|
+
|
17
|
+
xml do
|
18
|
+
root "NestedChild"
|
19
|
+
|
20
|
+
map_element :NestedChild, to: :nested_child
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Parent < Lutaml::Model::Serializable
|
25
|
+
attribute :child, Child
|
26
|
+
|
27
|
+
xml do
|
28
|
+
root "Parent"
|
29
|
+
namespace "https://abc.com"
|
30
|
+
|
31
|
+
map_element :Child, to: :child
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
RSpec.describe "NamespaceSpec" do
|
37
|
+
let(:parsed) { NamespaceSpec::Parent.from_xml(xml) }
|
38
|
+
let(:xml) do
|
39
|
+
<<~XML
|
40
|
+
<Parent xmlns="https://abc.com">
|
41
|
+
<Child>
|
42
|
+
<NestedChild>
|
43
|
+
<name>Rogger moore</name>
|
44
|
+
</NestedChild>
|
45
|
+
</Child>
|
46
|
+
</Parent>
|
47
|
+
XML
|
48
|
+
end
|
49
|
+
|
50
|
+
it "parses nested child using root namespace" do
|
51
|
+
expect(parsed.child.nested_child.name).to eq("Rogger moore")
|
52
|
+
end
|
53
|
+
|
54
|
+
it "round-trips xml" do
|
55
|
+
expect(parsed.to_xml).to be_equivalent_to(xml)
|
56
|
+
end
|
57
|
+
end
|