lutaml-model 0.3.25 → 0.3.27
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +1 -1
- data/README.adoc +185 -2
- data/lib/lutaml/model/attribute.rb +34 -19
- data/lib/lutaml/model/error/pattern_not_matched_error.rb +17 -0
- data/lib/lutaml/model/error.rb +1 -0
- data/lib/lutaml/model/mapping_hash.rb +8 -0
- data/lib/lutaml/model/serialize.rb +4 -4
- data/lib/lutaml/model/validation.rb +2 -1
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +7 -1
- data/lib/lutaml/model/xml_adapter/builder/ox.rb +7 -1
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +14 -5
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +18 -8
- data/lib/lutaml/model/xml_adapter/xml_document.rb +10 -9
- data/lib/lutaml/model/xml_mapping.rb +6 -2
- data/lib/lutaml/model/xml_mapping_rule.rb +7 -1
- data/spec/lutaml/model/attribute_spec.rb +27 -0
- data/spec/lutaml/model/cdata_spec.rb +520 -0
- data/spec/lutaml/model/delegation_spec.rb +2 -2
- data/spec/lutaml/model/mixed_content_spec.rb +68 -1
- data/spec/lutaml/model/serializable_validation_spec.rb +9 -4
- data/spec/lutaml/model/xml_mapping_spec.rb +68 -0
- metadata +4 -2
@@ -47,6 +47,7 @@ module Lutaml
|
|
47
47
|
render_default: false,
|
48
48
|
with: {},
|
49
49
|
delegate: nil,
|
50
|
+
cdata: false,
|
50
51
|
namespace: (namespace_set = false
|
51
52
|
nil),
|
52
53
|
prefix: (prefix_set = false
|
@@ -61,6 +62,7 @@ module Lutaml
|
|
61
62
|
render_default: render_default,
|
62
63
|
with: with,
|
63
64
|
delegate: delegate,
|
65
|
+
cdata: cdata,
|
64
66
|
namespace: namespace,
|
65
67
|
default_namespace: namespace_uri,
|
66
68
|
prefix: prefix,
|
@@ -109,7 +111,8 @@ module Lutaml
|
|
109
111
|
render_default: false,
|
110
112
|
with: {},
|
111
113
|
delegate: nil,
|
112
|
-
mixed: false
|
114
|
+
mixed: false,
|
115
|
+
cdata: false
|
113
116
|
)
|
114
117
|
validate!("content", to, with)
|
115
118
|
|
@@ -121,6 +124,7 @@ module Lutaml
|
|
121
124
|
with: with,
|
122
125
|
delegate: delegate,
|
123
126
|
mixed_content: mixed,
|
127
|
+
cdata: cdata,
|
124
128
|
)
|
125
129
|
end
|
126
130
|
|
@@ -211,7 +215,7 @@ module Lutaml
|
|
211
215
|
end
|
212
216
|
|
213
217
|
def find_by_name(name)
|
214
|
-
if name.to_s
|
218
|
+
if ["text", "#cdata-section"].include?(name.to_s)
|
215
219
|
content_mapping
|
216
220
|
else
|
217
221
|
mappings.detect do |rule|
|
@@ -3,7 +3,7 @@ require_relative "mapping_rule"
|
|
3
3
|
module Lutaml
|
4
4
|
module Model
|
5
5
|
class XmlMappingRule < MappingRule
|
6
|
-
attr_reader :namespace, :prefix, :mixed_content, :default_namespace
|
6
|
+
attr_reader :namespace, :prefix, :mixed_content, :default_namespace, :cdata
|
7
7
|
|
8
8
|
def initialize(
|
9
9
|
name,
|
@@ -15,6 +15,7 @@ module Lutaml
|
|
15
15
|
namespace: nil,
|
16
16
|
prefix: nil,
|
17
17
|
mixed_content: false,
|
18
|
+
cdata: false,
|
18
19
|
namespace_set: false,
|
19
20
|
prefix_set: false,
|
20
21
|
attribute: false,
|
@@ -38,6 +39,7 @@ module Lutaml
|
|
38
39
|
end
|
39
40
|
@prefix = prefix
|
40
41
|
@mixed_content = mixed_content
|
42
|
+
@cdata = cdata
|
41
43
|
|
42
44
|
@default_namespace = default_namespace
|
43
45
|
|
@@ -61,6 +63,10 @@ module Lutaml
|
|
61
63
|
name == "__raw_mapping"
|
62
64
|
end
|
63
65
|
|
66
|
+
def content_key
|
67
|
+
cdata ? "#cdata-section" : "text"
|
68
|
+
end
|
69
|
+
|
64
70
|
def mixed_content?
|
65
71
|
!!@mixed_content
|
66
72
|
end
|
@@ -33,6 +33,33 @@ RSpec.describe Lutaml::Model::Attribute do
|
|
33
33
|
.to("avatar.png")
|
34
34
|
end
|
35
35
|
|
36
|
+
describe "#validate_options!" do
|
37
|
+
let(:validate_options) { name_attr.method(:validate_options!) }
|
38
|
+
|
39
|
+
Lutaml::Model::Attribute::ALLOWED_OPTIONS.each do |option|
|
40
|
+
it "return true if option is `#{option}`" do
|
41
|
+
expect(validate_options.call({ option => "value" })).to be(true)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "raise exception if option is not allowed" do
|
46
|
+
expect do
|
47
|
+
validate_options.call({ foo: "bar" })
|
48
|
+
end.to raise_error(StandardError, "Invalid options given for `name` [:foo]")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "raise exception if pattern is given with non string type" do
|
52
|
+
age_attr = described_class.new("age", :integer)
|
53
|
+
|
54
|
+
expect do
|
55
|
+
age_attr.send(:validate_options!, { pattern: /[A-Za-z ]/ })
|
56
|
+
end.to raise_error(
|
57
|
+
StandardError,
|
58
|
+
"Invalid option `pattern` given for `age`, `pattern` is only allowed for :string type",
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
36
63
|
describe "#validate_type!" do
|
37
64
|
let(:validate_type) { name_attr.method(:validate_type!) }
|
38
65
|
|
@@ -0,0 +1,520 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "lutaml/model"
|
3
|
+
require "lutaml/model/xml_adapter/nokogiri_adapter"
|
4
|
+
require "lutaml/model/xml_adapter/ox_adapter"
|
5
|
+
|
6
|
+
module CDATA
|
7
|
+
class Beta < Lutaml::Model::Serializable
|
8
|
+
attribute :element1, :string
|
9
|
+
|
10
|
+
xml do
|
11
|
+
root "beta"
|
12
|
+
map_content to: :element1, cdata: true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Alpha < Lutaml::Model::Serializable
|
17
|
+
attribute :element1, :string
|
18
|
+
attribute :element2, :string
|
19
|
+
attribute :element3, :string
|
20
|
+
attribute :beta, Beta
|
21
|
+
|
22
|
+
xml do
|
23
|
+
root "alpha"
|
24
|
+
|
25
|
+
map_element "element1", to: :element1, cdata: false
|
26
|
+
map_element "element2", to: :element2, cdata: true
|
27
|
+
map_element "element3", to: :element3, cdata: false
|
28
|
+
map_element "beta", to: :beta
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Address < Lutaml::Model::Serializable
|
33
|
+
attribute :street, :string
|
34
|
+
attribute :city, :string
|
35
|
+
attribute :house, :string
|
36
|
+
attribute :address, Address
|
37
|
+
|
38
|
+
xml do
|
39
|
+
root "address"
|
40
|
+
map_element "street", to: :street
|
41
|
+
map_element "city", with: { from: :city_from_xml, to: :city_to_xml }, cdata: true
|
42
|
+
map_element "house", with: { from: :house_from_xml, to: :house_to_xml }, cdata: false
|
43
|
+
map_element "address", to: :address
|
44
|
+
end
|
45
|
+
|
46
|
+
def house_from_xml(model, node)
|
47
|
+
model.house = node
|
48
|
+
end
|
49
|
+
|
50
|
+
def house_to_xml(model, _parent, doc)
|
51
|
+
doc.create_and_add_element("house") do |element|
|
52
|
+
element.add_text(element, model.house, cdata: false)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def city_from_xml(model, node)
|
57
|
+
model.city = node
|
58
|
+
end
|
59
|
+
|
60
|
+
def city_to_xml(model, _parent, doc)
|
61
|
+
doc.create_and_add_element("city") do |element|
|
62
|
+
element.add_text(element, model.city, cdata: true)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class CustomModelChild
|
68
|
+
attr_accessor :street, :city
|
69
|
+
end
|
70
|
+
|
71
|
+
class CustomModelParent
|
72
|
+
attr_accessor :first_name, :middle_name, :last_name, :child_mapper
|
73
|
+
|
74
|
+
def name
|
75
|
+
"#{first_name} #{last_name}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class CustomModelChildMapper < Lutaml::Model::Serializable
|
80
|
+
model CustomModelChild
|
81
|
+
|
82
|
+
attribute :street, Lutaml::Model::Type::String
|
83
|
+
attribute :city, Lutaml::Model::Type::String
|
84
|
+
|
85
|
+
xml do
|
86
|
+
map_element :street, to: :street, cdata: true
|
87
|
+
map_element :city, to: :city, cdata: true
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class CustomModelParentMapper < Lutaml::Model::Serializable
|
92
|
+
model CustomModelParent
|
93
|
+
|
94
|
+
attribute :first_name, Lutaml::Model::Type::String
|
95
|
+
attribute :middle_name, Lutaml::Model::Type::String
|
96
|
+
attribute :last_name, Lutaml::Model::Type::String
|
97
|
+
attribute :child_mapper, CustomModelChildMapper
|
98
|
+
|
99
|
+
xml do
|
100
|
+
root "CustomModelParent"
|
101
|
+
map_element :first_name, to: :first_name, cdata: true
|
102
|
+
map_element :middle_name, to: :middle_name, cdata: true
|
103
|
+
map_element :last_name, to: :last_name, cdata: false
|
104
|
+
map_element :CustomModelChild, with: { to: :child_to_xml, from: :child_from_xml }, cdata: true
|
105
|
+
end
|
106
|
+
|
107
|
+
def child_to_xml(model, _parent, doc)
|
108
|
+
doc.create_and_add_element("CustomModelChild") do |child_el|
|
109
|
+
child_el.create_and_add_element("street") do |street_el|
|
110
|
+
street_el.add_text(street_el, model.child_mapper.street, cdata: true)
|
111
|
+
end
|
112
|
+
child_el.create_and_add_element("city") do |city_el|
|
113
|
+
city_el.add_text(city_el, model.child_mapper.city, cdata: true)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def child_from_xml(model, value)
|
119
|
+
model.child_mapper ||= CustomModelChild.new
|
120
|
+
|
121
|
+
model.child_mapper.street = value["street"].text
|
122
|
+
model.child_mapper.city = value["city"].text
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class RootMixedContent < Lutaml::Model::Serializable
|
127
|
+
attribute :id, :string
|
128
|
+
attribute :bold, :string, collection: true
|
129
|
+
attribute :italic, :string, collection: true
|
130
|
+
attribute :underline, :string
|
131
|
+
attribute :content, :string
|
132
|
+
|
133
|
+
xml do
|
134
|
+
root "RootMixedContent", mixed: true
|
135
|
+
map_attribute :id, to: :id
|
136
|
+
map_element :bold, to: :bold, cdata: true
|
137
|
+
map_element :italic, to: :italic, cdata: true
|
138
|
+
map_element :underline, to: :underline, cdata: true
|
139
|
+
map_content to: :content, cdata: true
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class RootMixedContentNested < Lutaml::Model::Serializable
|
144
|
+
attribute :id, :string
|
145
|
+
attribute :data, :string
|
146
|
+
attribute :content, RootMixedContent
|
147
|
+
attribute :sup, :string, collection: true
|
148
|
+
attribute :sub, :string, collection: true
|
149
|
+
|
150
|
+
xml do
|
151
|
+
root "RootMixedContentNested", mixed: true
|
152
|
+
map_content to: :data, cdata: true
|
153
|
+
map_attribute :id, to: :id
|
154
|
+
map_element :sup, to: :sup, cdata: true
|
155
|
+
map_element :sub, to: :sub, cdata: false
|
156
|
+
map_element "MixedContent", to: :content
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
class DefaultValue < Lutaml::Model::Serializable
|
161
|
+
attribute :name, :string, default: -> { "Default Value" }
|
162
|
+
attribute :temperature, :integer, default: -> { 1050 }
|
163
|
+
attribute :opacity, :string, default: -> { "Opaque" }
|
164
|
+
attribute :content, :string, default: -> { " " }
|
165
|
+
|
166
|
+
xml do
|
167
|
+
root "DefaultValue"
|
168
|
+
map_element "name", to: :name, render_default: true, cdata: true
|
169
|
+
map_element "temperature", to: :temperature, render_default: true, cdata: true
|
170
|
+
map_element "opacity", to: :opacity, cdata: false, render_default: true
|
171
|
+
map_content to: :content, cdata: true, render_default: true
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
RSpec.describe "CDATA" do
|
177
|
+
let(:parent_mapper) { CDATA::CustomModelParentMapper }
|
178
|
+
let(:child_mapper) { CDATA::CustomModelChildMapper }
|
179
|
+
let(:parent_model) { CDATA::CustomModelParent }
|
180
|
+
let(:child_model) { CDATA::CustomModelChild }
|
181
|
+
|
182
|
+
shared_examples "cdata behavior" do |adapter_class|
|
183
|
+
around do |example|
|
184
|
+
old_adapter = Lutaml::Model::Config.xml_adapter
|
185
|
+
Lutaml::Model::Config.xml_adapter = adapter_class
|
186
|
+
example.run
|
187
|
+
ensure
|
188
|
+
Lutaml::Model::Config.xml_adapter = old_adapter
|
189
|
+
end
|
190
|
+
|
191
|
+
context "with CDATA option" do
|
192
|
+
let(:xml) do
|
193
|
+
<<~XML.strip
|
194
|
+
<alpha>
|
195
|
+
<element1><![CDATA[foo]]></element1>
|
196
|
+
<element2><![CDATA[one]]></element2>
|
197
|
+
<element2><![CDATA[two]]></element2>
|
198
|
+
<element2><![CDATA[three]]></element2>
|
199
|
+
<element3>bar</element3>
|
200
|
+
<beta><![CDATA[child]]></beta>
|
201
|
+
</alpha>
|
202
|
+
XML
|
203
|
+
end
|
204
|
+
|
205
|
+
let(:expected_xml) do
|
206
|
+
<<~XML.strip
|
207
|
+
<alpha>
|
208
|
+
<element1>foo</element1>
|
209
|
+
<element2>
|
210
|
+
<![CDATA[one]]>
|
211
|
+
</element2>
|
212
|
+
<element2>
|
213
|
+
<![CDATA[two]]>
|
214
|
+
</element2>
|
215
|
+
<element2>
|
216
|
+
<![CDATA[three]]>
|
217
|
+
</element2>
|
218
|
+
<element3>bar</element3>
|
219
|
+
<beta>
|
220
|
+
<![CDATA[child]]>
|
221
|
+
</beta>
|
222
|
+
</alpha>
|
223
|
+
XML
|
224
|
+
end
|
225
|
+
|
226
|
+
it "maps xml to object" do
|
227
|
+
instance = CDATA::Alpha.from_xml(xml)
|
228
|
+
|
229
|
+
expect(instance.element1).to eq("foo")
|
230
|
+
expect(instance.element2).to eq(%w[one two three])
|
231
|
+
expect(instance.element3).to eq("bar")
|
232
|
+
expect(instance.beta.element1).to eq("child")
|
233
|
+
end
|
234
|
+
|
235
|
+
it "converts objects to xml" do
|
236
|
+
instance = CDATA::Alpha.new(
|
237
|
+
element1: "foo",
|
238
|
+
element2: %w[one two three],
|
239
|
+
element3: "bar",
|
240
|
+
beta: CDATA::Beta.new(element1: "child"),
|
241
|
+
)
|
242
|
+
|
243
|
+
expect(instance.to_xml).to be_equivalent_to(expected_xml)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
context "with custom methods" do
|
248
|
+
let(:xml) do
|
249
|
+
<<~XML
|
250
|
+
<address>
|
251
|
+
<street>A</street>
|
252
|
+
<city><![CDATA[B]]></city>
|
253
|
+
<house><![CDATA[H]]></house>
|
254
|
+
<address>
|
255
|
+
<street>C</street>
|
256
|
+
<city><![CDATA[D]]></city>
|
257
|
+
<house><![CDATA[G]]></house>
|
258
|
+
</address>
|
259
|
+
</address>
|
260
|
+
XML
|
261
|
+
end
|
262
|
+
|
263
|
+
let(:expected_xml) do
|
264
|
+
<<~XML
|
265
|
+
<address>
|
266
|
+
<street>A</street>
|
267
|
+
<city>
|
268
|
+
<![CDATA[B]]>
|
269
|
+
</city>
|
270
|
+
<house>H</house>
|
271
|
+
<address>
|
272
|
+
<street>C</street>
|
273
|
+
<city>
|
274
|
+
<![CDATA[D]]>
|
275
|
+
</city>
|
276
|
+
<house>G</house>
|
277
|
+
</address>
|
278
|
+
</address>
|
279
|
+
XML
|
280
|
+
end
|
281
|
+
|
282
|
+
it "round-trips XML" do
|
283
|
+
model = CDATA::Address.from_xml(xml)
|
284
|
+
expect(model.to_xml).to be_equivalent_to(expected_xml)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
context "with custom models" do
|
289
|
+
let(:input_xml) do
|
290
|
+
<<~XML
|
291
|
+
<CustomModelParent>
|
292
|
+
<first_name><![CDATA[John]]></first_name>
|
293
|
+
<last_name><![CDATA[Doe]]></last_name>
|
294
|
+
<CustomModelChild>
|
295
|
+
<street><![CDATA[Oxford Street]]></street>
|
296
|
+
<city><![CDATA[London]]></city>
|
297
|
+
</CustomModelChild>
|
298
|
+
</CustomModelParent>
|
299
|
+
XML
|
300
|
+
end
|
301
|
+
|
302
|
+
let(:expected_nokogiri_xml) do
|
303
|
+
<<~XML
|
304
|
+
<CustomModelParent>
|
305
|
+
<first_name><![CDATA[John]]></first_name>
|
306
|
+
<last_name>Doe</last_name>
|
307
|
+
<CustomModelChild>
|
308
|
+
<street><![CDATA[Oxford Street]]></street>
|
309
|
+
<city><![CDATA[London]]></city>
|
310
|
+
</CustomModelChild>
|
311
|
+
</CustomModelParent>
|
312
|
+
XML
|
313
|
+
end
|
314
|
+
|
315
|
+
let(:expected_ox_xml) do
|
316
|
+
<<~XML
|
317
|
+
<CustomModelParent>
|
318
|
+
<first_name>
|
319
|
+
<![CDATA[John]]>
|
320
|
+
</first_name>
|
321
|
+
<last_name>Doe</last_name>
|
322
|
+
<CustomModelChild>
|
323
|
+
<street>
|
324
|
+
<![CDATA[Oxford Street]]>
|
325
|
+
</street>
|
326
|
+
<city>
|
327
|
+
<![CDATA[London]]>
|
328
|
+
</city>
|
329
|
+
</CustomModelChild>
|
330
|
+
</CustomModelParent>
|
331
|
+
XML
|
332
|
+
end
|
333
|
+
|
334
|
+
describe ".from_xml" do
|
335
|
+
it "maps XML content to custom model using custom methods" do
|
336
|
+
instance = parent_mapper.from_xml(input_xml)
|
337
|
+
|
338
|
+
expect(instance.class).to eq(parent_model)
|
339
|
+
expect(instance.first_name).to eq("John")
|
340
|
+
expect(instance.last_name).to eq("Doe")
|
341
|
+
expect(instance.name).to eq("John Doe")
|
342
|
+
|
343
|
+
expect(instance.child_mapper.class).to eq(child_model)
|
344
|
+
expect(instance.child_mapper.street).to eq("Oxford Street")
|
345
|
+
expect(instance.child_mapper.city).to eq("London")
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
describe ".to_xml" do
|
350
|
+
it "with correct model converts objects to xml using custom methods" do
|
351
|
+
instance = parent_mapper.from_xml(input_xml)
|
352
|
+
result_xml = parent_mapper.to_xml(instance)
|
353
|
+
|
354
|
+
expected_output = adapter_class == Lutaml::Model::XmlAdapter::OxAdapter ? expected_ox_xml : expected_nokogiri_xml
|
355
|
+
|
356
|
+
expect(result_xml.strip).to eq(expected_output.strip)
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
context "when mixed: true is set for nested content" do
|
362
|
+
let(:xml) do
|
363
|
+
<<~XML
|
364
|
+
<RootMixedContentNested id="outer123">
|
365
|
+
<![CDATA[The following text is about the Moon.]]>
|
366
|
+
<MixedContent id="inner456">
|
367
|
+
<![CDATA[The Earth's Moon rings like a ]]>
|
368
|
+
<bold><![CDATA[bell]]></bold>
|
369
|
+
<![CDATA[ when struck by meteroids. Distanced from the Earth by ]]>
|
370
|
+
<italic><![CDATA[384,400 km]]></italic>,
|
371
|
+
<![CDATA[ ,its surface is covered in ]]>
|
372
|
+
<underline><![CDATA[craters]]></underline>.
|
373
|
+
<![CDATA[ .Ain't that ]]>
|
374
|
+
<bold><![CDATA[cool]]></bold>
|
375
|
+
<![CDATA[ ? ]]>
|
376
|
+
</MixedContent>
|
377
|
+
<sup><![CDATA[1]]></sup>: <![CDATA[The Moon is not a planet.]]>
|
378
|
+
<sup><![CDATA[2]]></sup>: <![CDATA[The Moon's atmosphere is mainly composed of helium in the form of He]]><sub><![CDATA[2]]></sub>.
|
379
|
+
</RootMixedContentNested>
|
380
|
+
XML
|
381
|
+
end
|
382
|
+
|
383
|
+
expected_xml = "<RootMixedContentNested id=\"outer123\"><![CDATA[The following text is about the Moon.]]><MixedContent id=\"inner456\"><![CDATA[The Earth's Moon rings like a ]]><bold><![CDATA[bell]]></bold><![CDATA[ when struck by meteroids. Distanced from the Earth by ]]><italic><![CDATA[384,400 km]]></italic><![CDATA[ ,its surface is covered in ]]><underline><![CDATA[craters]]></underline><![CDATA[ .Ain't that ]]><bold><![CDATA[cool]]></bold><![CDATA[ ? ]]></MixedContent><sup><![CDATA[1]]></sup><![CDATA[The Moon is not a planet.]]><sup><![CDATA[2]]></sup><![CDATA[The Moon's atmosphere is mainly composed of helium in the form of He]]><sub>2</sub></RootMixedContentNested>"
|
384
|
+
|
385
|
+
expected_ox_xml = <<~XML
|
386
|
+
<RootMixedContentNested id="outer123">
|
387
|
+
<![CDATA[The following text is about the Moon.]]>
|
388
|
+
<MixedContent id="inner456">
|
389
|
+
<![CDATA[The Earth's Moon rings like a ]]>
|
390
|
+
<bold>
|
391
|
+
<![CDATA[bell]]>
|
392
|
+
</bold>
|
393
|
+
<![CDATA[ when struck by meteroids. Distanced from the Earth by ]]>
|
394
|
+
<italic>
|
395
|
+
<![CDATA[384,400 km]]>
|
396
|
+
</italic>
|
397
|
+
<![CDATA[ ,its surface is covered in ]]>
|
398
|
+
<underline>
|
399
|
+
<![CDATA[craters]]>
|
400
|
+
</underline>
|
401
|
+
<![CDATA[ .Ain't that ]]>
|
402
|
+
<bold>
|
403
|
+
<![CDATA[cool]]>
|
404
|
+
</bold>
|
405
|
+
<![CDATA[ ? ]]>
|
406
|
+
</MixedContent>
|
407
|
+
<sup>
|
408
|
+
<![CDATA[1]]>
|
409
|
+
</sup>
|
410
|
+
<![CDATA[The Moon is not a planet.]]>
|
411
|
+
<sup>
|
412
|
+
<![CDATA[2]]>
|
413
|
+
</sup>
|
414
|
+
<![CDATA[The Moon's atmosphere is mainly composed of helium in the form of He]]>
|
415
|
+
<sub>2</sub>
|
416
|
+
</RootMixedContentNested>
|
417
|
+
XML
|
418
|
+
|
419
|
+
it "deserializes and serializes mixed content correctly" do
|
420
|
+
parsed = CDATA::RootMixedContentNested.from_xml(xml)
|
421
|
+
|
422
|
+
expected_content = [
|
423
|
+
"The Earth's Moon rings like a ",
|
424
|
+
" when struck by meteroids. Distanced from the Earth by ",
|
425
|
+
" ,its surface is covered in ",
|
426
|
+
" .Ain't that ",
|
427
|
+
" ? ",
|
428
|
+
]
|
429
|
+
|
430
|
+
expect(parsed.id).to eq("outer123")
|
431
|
+
expect(parsed.sup).to eq(["1", "2"])
|
432
|
+
expect(parsed.sub).to eq(["2"])
|
433
|
+
expect(parsed.content.id).to eq("inner456")
|
434
|
+
expect(parsed.content.bold).to eq(["bell", "cool"])
|
435
|
+
expect(parsed.content.italic).to eq(["384,400 km"])
|
436
|
+
expect(parsed.content.underline).to eq("craters")
|
437
|
+
|
438
|
+
parsed.content.content.each_with_index do |content, index|
|
439
|
+
expected_output = expected_content[index]
|
440
|
+
|
441
|
+
# due to the difference in capturing
|
442
|
+
# newlines in ox and nokogiri adapters
|
443
|
+
if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
|
444
|
+
expected_xml = expected_ox_xml
|
445
|
+
end
|
446
|
+
|
447
|
+
expect(content).to eq(expected_output)
|
448
|
+
end
|
449
|
+
|
450
|
+
serialized = parsed.to_xml
|
451
|
+
expect(serialized).to eq(expected_xml)
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
context "when defualt: true is set for attributes default values" do
|
456
|
+
let(:xml) do
|
457
|
+
<<~XML
|
458
|
+
<DefaultValue>
|
459
|
+
<![CDATA[The following text is about the Moon]]>
|
460
|
+
<temperature>
|
461
|
+
<![CDATA[500]]>
|
462
|
+
</temperature>
|
463
|
+
<![CDATA[The Moon's atmosphere is mainly composed of helium in the form]]>
|
464
|
+
</DefaultValue>
|
465
|
+
XML
|
466
|
+
end
|
467
|
+
|
468
|
+
expected_xml = "<DefaultValue><name><![CDATA[Default Value]]></name><temperature><![CDATA[500]]></temperature><opacity>Opaque</opacity><![CDATA[The following text is about the MoonThe Moon's atmosphere is mainly composed of helium in the form]]></DefaultValue>"
|
469
|
+
|
470
|
+
expected_ox_xml = <<~XML
|
471
|
+
<DefaultValue>
|
472
|
+
<name>
|
473
|
+
<![CDATA[Default Value]]>
|
474
|
+
</name>
|
475
|
+
<temperature>
|
476
|
+
<![CDATA[500]]>
|
477
|
+
</temperature>
|
478
|
+
<opacity>Opaque</opacity>
|
479
|
+
<![CDATA[The following text is about the MoonThe Moon's atmosphere is mainly composed of helium in the form]]>
|
480
|
+
</DefaultValue>
|
481
|
+
XML
|
482
|
+
|
483
|
+
it "deserializes and serializes mixed content correctly" do
|
484
|
+
parsed = CDATA::DefaultValue.from_xml(xml)
|
485
|
+
|
486
|
+
expected_content = [
|
487
|
+
"The following text is about the Moon",
|
488
|
+
"The Moon's atmosphere is mainly composed of helium in the form",
|
489
|
+
]
|
490
|
+
|
491
|
+
expect(parsed.name).to eq("Default Value")
|
492
|
+
expect(parsed.opacity).to eq("Opaque")
|
493
|
+
expect(parsed.temperature).to eq(500)
|
494
|
+
|
495
|
+
parsed.content.each_with_index do |content, index|
|
496
|
+
expected_output = expected_content[index]
|
497
|
+
|
498
|
+
# due to the difference in capturing
|
499
|
+
# newlines in ox and nokogiri adapters
|
500
|
+
if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
|
501
|
+
expected_xml = expected_ox_xml
|
502
|
+
end
|
503
|
+
|
504
|
+
expect(content).to eq(expected_output)
|
505
|
+
end
|
506
|
+
|
507
|
+
serialized = parsed.to_xml
|
508
|
+
expect(serialized).to eq(expected_xml)
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
describe Lutaml::Model::XmlAdapter::NokogiriAdapter do
|
514
|
+
it_behaves_like "cdata behavior", described_class
|
515
|
+
end
|
516
|
+
|
517
|
+
describe Lutaml::Model::XmlAdapter::OxAdapter do
|
518
|
+
it_behaves_like "cdata behavior", described_class
|
519
|
+
end
|
520
|
+
end
|
@@ -148,11 +148,11 @@ RSpec.describe Delegation do
|
|
148
148
|
end
|
149
149
|
|
150
150
|
it "provides XML declaration with UTF-8 encoding" \
|
151
|
-
"if encoding:
|
151
|
+
"if encoding: 'UTF-8' option provided" do
|
152
152
|
xml_data = delegation.to_xml(
|
153
153
|
pretty: true,
|
154
154
|
declaration: true,
|
155
|
-
encoding:
|
155
|
+
encoding: "UTF-8",
|
156
156
|
)
|
157
157
|
expect(xml_data).to include('<?xml version="1.0" encoding="UTF-8"?>')
|
158
158
|
end
|