lutaml-model 0.3.25 → 0.3.27
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 +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
|