lutaml-model 0.5.2 → 0.5.4
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/.github/workflows/dependent-tests.yml +2 -0
- data/.rubocop_todo.yml +39 -13
- data/Gemfile +1 -0
- data/README.adoc +430 -52
- data/lib/lutaml/model/constants.rb +7 -0
- data/lib/lutaml/model/error/type/invalid_value_error.rb +19 -0
- data/lib/lutaml/model/error.rb +1 -0
- data/lib/lutaml/model/key_value_mapping.rb +31 -2
- data/lib/lutaml/model/mapping_hash.rb +8 -0
- data/lib/lutaml/model/mapping_rule.rb +8 -0
- data/lib/lutaml/model/schema/templates/simple_type.rb +247 -0
- data/lib/lutaml/model/schema/xml_compiler.rb +720 -0
- data/lib/lutaml/model/schema.rb +5 -0
- data/lib/lutaml/model/serialize.rb +33 -13
- data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +1 -2
- data/lib/lutaml/model/type/hash.rb +11 -11
- data/lib/lutaml/model/utils.rb +7 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +5 -1
- data/lib/lutaml/model/xml_adapter/xml_document.rb +11 -15
- data/lib/lutaml/model/xml_mapping.rb +4 -2
- data/lib/lutaml/model/xml_mapping_rule.rb +1 -4
- data/lib/lutaml/model.rb +1 -0
- data/spec/fixtures/xml/invalid_math_document.xml +4 -0
- data/spec/fixtures/xml/math_document_schema.xsd +56 -0
- data/spec/fixtures/xml/test_schema.xsd +53 -0
- data/spec/fixtures/xml/valid_math_document.xml +4 -0
- data/spec/lutaml/model/cdata_spec.rb +2 -2
- data/spec/lutaml/model/custom_model_spec.rb +7 -20
- data/spec/lutaml/model/key_value_mapping_spec.rb +27 -0
- data/spec/lutaml/model/map_all_spec.rb +188 -0
- data/spec/lutaml/model/mixed_content_spec.rb +15 -15
- data/spec/lutaml/model/render_nil_spec.rb +29 -0
- data/spec/lutaml/model/schema/xml_compiler_spec.rb +1431 -0
- data/spec/lutaml/model/with_child_mapping_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +52 -0
- data/spec/lutaml/model/xml_mapping_spec.rb +108 -1
- metadata +12 -2
@@ -0,0 +1,188 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module MapAllSpec
|
4
|
+
class Document < Lutaml::Model::Serializable
|
5
|
+
attribute :content, :string
|
6
|
+
|
7
|
+
xml do
|
8
|
+
root "document"
|
9
|
+
map_all to: :content
|
10
|
+
end
|
11
|
+
|
12
|
+
json do
|
13
|
+
map_all to: :content
|
14
|
+
end
|
15
|
+
|
16
|
+
yaml do
|
17
|
+
map_all to: :content
|
18
|
+
end
|
19
|
+
|
20
|
+
toml do
|
21
|
+
map_all to: :content
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class InvalidDocument < Lutaml::Model::Serializable
|
26
|
+
attribute :content, :string
|
27
|
+
attribute :title, :string
|
28
|
+
|
29
|
+
json do
|
30
|
+
map_all to: :content
|
31
|
+
end
|
32
|
+
|
33
|
+
yaml do
|
34
|
+
map_element "title", to: :title
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
RSpec.describe "MapAll" do
|
39
|
+
describe "XML serialization" do
|
40
|
+
let(:xml_content) do
|
41
|
+
<<~XML
|
42
|
+
<document>
|
43
|
+
Content with <b>tags</b> and <i>formatting</i>.
|
44
|
+
<metadata>
|
45
|
+
<author>John Doe</author>
|
46
|
+
<date>2024-01-15</date>
|
47
|
+
</metadata>
|
48
|
+
</document>
|
49
|
+
XML
|
50
|
+
end
|
51
|
+
|
52
|
+
let(:sub_xml_content) do
|
53
|
+
<<~XML
|
54
|
+
Content with <b>tags</b> and <i>formatting</i>.
|
55
|
+
<metadata>
|
56
|
+
<author>John Doe</author>
|
57
|
+
<date>2024-01-15</date>
|
58
|
+
</metadata>
|
59
|
+
XML
|
60
|
+
end
|
61
|
+
|
62
|
+
it "captures all XML content" do
|
63
|
+
doc = Document.from_xml(xml_content)
|
64
|
+
expect(doc.content).to be_equivalent_to(sub_xml_content)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "preserves XML content through round trip" do
|
68
|
+
doc = Document.from_xml(xml_content)
|
69
|
+
regenerated = doc.to_xml
|
70
|
+
expect(regenerated).to be_equivalent_to(xml_content)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "JSON serialization" do
|
75
|
+
let(:json_content) do
|
76
|
+
{
|
77
|
+
"sections" => [
|
78
|
+
{ "title" => "Introduction", "text" => "Chapter 1" },
|
79
|
+
{ "title" => "Conclusion", "text" => "Final chapter" },
|
80
|
+
],
|
81
|
+
"metadata" => {
|
82
|
+
"author" => "John Doe",
|
83
|
+
"date" => "2024-01-15",
|
84
|
+
},
|
85
|
+
}.to_json
|
86
|
+
end
|
87
|
+
|
88
|
+
it "captures all JSON content" do
|
89
|
+
doc = Document.from_json(json_content)
|
90
|
+
parsed = JSON.parse(doc.content)
|
91
|
+
expect(parsed["sections"].first["title"]).to eq("Introduction")
|
92
|
+
expect(parsed["metadata"]["author"]).to eq("John Doe")
|
93
|
+
end
|
94
|
+
|
95
|
+
it "preserves JSON content through round trip" do
|
96
|
+
doc = Document.from_json(json_content)
|
97
|
+
regenerated = doc.to_json
|
98
|
+
expect(JSON.parse(regenerated)).to eq(JSON.parse(json_content))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "YAML serialization" do
|
103
|
+
let(:yaml_content) do
|
104
|
+
<<~YAML
|
105
|
+
sections:
|
106
|
+
- title: Introduction
|
107
|
+
text: Chapter 1
|
108
|
+
- title: Conclusion
|
109
|
+
text: Final chapter
|
110
|
+
metadata:
|
111
|
+
author: John Doe
|
112
|
+
date: 2024-01-15
|
113
|
+
YAML
|
114
|
+
end
|
115
|
+
|
116
|
+
it "captures all YAML content" do
|
117
|
+
doc = Document.from_yaml(yaml_content)
|
118
|
+
parsed = YAML.safe_load(doc.content, permitted_classes: [Date])
|
119
|
+
expect(parsed["sections"].first["title"]).to eq("Introduction")
|
120
|
+
expect(parsed["metadata"]["author"]).to eq("John Doe")
|
121
|
+
end
|
122
|
+
|
123
|
+
it "preserves YAML content through round trip" do
|
124
|
+
doc = Document.from_yaml(yaml_content)
|
125
|
+
regenerated = doc.to_yaml
|
126
|
+
expect(YAML.safe_load(regenerated, permitted_classes: [Date])).to eq(YAML.safe_load(yaml_content, permitted_classes: [Date]))
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "TOML serialization" do
|
131
|
+
let(:toml_content) do
|
132
|
+
<<~TOML
|
133
|
+
title = "Document Title"
|
134
|
+
|
135
|
+
[metadata]
|
136
|
+
author = "John Doe"
|
137
|
+
date = "2024-01-15"
|
138
|
+
|
139
|
+
[[sections]]
|
140
|
+
title = "Introduction"
|
141
|
+
text = "Chapter 1"
|
142
|
+
|
143
|
+
[[sections]]
|
144
|
+
title = "Conclusion"
|
145
|
+
text = "Final chapter"
|
146
|
+
TOML
|
147
|
+
end
|
148
|
+
|
149
|
+
it "captures all TOML content" do
|
150
|
+
doc = Document.from_toml(toml_content)
|
151
|
+
parsed = TomlRB.parse(doc.content)
|
152
|
+
expect(parsed["sections"].first["title"]).to eq("Introduction")
|
153
|
+
expect(parsed["metadata"]["author"]).to eq("John Doe")
|
154
|
+
end
|
155
|
+
|
156
|
+
it "preserves TOML content through round trip" do
|
157
|
+
doc = Document.from_toml(toml_content)
|
158
|
+
regenerated = doc.to_toml
|
159
|
+
expect(TomlRB.parse(regenerated)).to eq(TomlRB.parse(toml_content))
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe "invalid mapping combinations" do
|
164
|
+
it "raises error when combining map_all with other mappings" do
|
165
|
+
expect do
|
166
|
+
InvalidDocument.json do
|
167
|
+
map_element "title", to: :title
|
168
|
+
end
|
169
|
+
end.to raise_error(
|
170
|
+
StandardError,
|
171
|
+
"map_all is not allowed with other mappings",
|
172
|
+
)
|
173
|
+
end
|
174
|
+
|
175
|
+
it "raises error when combining other mappings are used with map_all" do
|
176
|
+
expect do
|
177
|
+
InvalidDocument.yaml do
|
178
|
+
map_element "title", to: :title
|
179
|
+
map_all to: :content
|
180
|
+
end
|
181
|
+
end.to raise_error(
|
182
|
+
StandardError,
|
183
|
+
"map_all is not allowed with other mappings",
|
184
|
+
)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -539,10 +539,10 @@ RSpec.describe "MixedContent" do
|
|
539
539
|
let(:expected_nokogiri_xml) do
|
540
540
|
<<~XML
|
541
541
|
<SpecialCharContentWithRawOptionAndMixedOption><special>
|
542
|
-
B
|
543
|
-
C
|
544
|
-
O
|
545
|
-
F
|
542
|
+
B <p>R&C</p>
|
543
|
+
C <p>J—C</p>
|
544
|
+
O <p>A & B </p>
|
545
|
+
F <p>Z ©S</p>
|
546
546
|
</special></SpecialCharContentWithRawOptionAndMixedOption>
|
547
547
|
XML
|
548
548
|
end
|
@@ -550,10 +550,10 @@ RSpec.describe "MixedContent" do
|
|
550
550
|
let(:expected_ox_xml) do
|
551
551
|
<<~XML
|
552
552
|
<SpecialCharContentWithRawOptionAndMixedOption>
|
553
|
-
<special> B
|
554
|
-
C
|
555
|
-
O
|
556
|
-
F
|
553
|
+
<special> B <p>R&C</p>
|
554
|
+
C <p>J—C</p>
|
555
|
+
O <p>A & B </p>
|
556
|
+
F <p>Z ©S</p>
|
557
557
|
</special>
|
558
558
|
</SpecialCharContentWithRawOptionAndMixedOption>
|
559
559
|
XML
|
@@ -562,10 +562,10 @@ RSpec.describe "MixedContent" do
|
|
562
562
|
let(:expected_oga_xml) do
|
563
563
|
<<~XML
|
564
564
|
<SpecialCharContentWithRawOptionAndMixedOption>
|
565
|
-
<special> B
|
566
|
-
C
|
567
|
-
O
|
568
|
-
F
|
565
|
+
<special> B <p>R&C</p>
|
566
|
+
C <p>J—C</p>
|
567
|
+
O <p>A & B </p>
|
568
|
+
F <p>Z ©S</p>
|
569
569
|
</special>
|
570
570
|
</SpecialCharContentWithRawOptionAndMixedOption>
|
571
571
|
XML
|
@@ -604,9 +604,9 @@ RSpec.describe "MixedContent" do
|
|
604
604
|
end
|
605
605
|
|
606
606
|
describe ".to_xml" do
|
607
|
-
let(:expected_nokogiri_xml) { "B
|
608
|
-
let(:expected_oga_xml) { "B
|
609
|
-
let(:expected_ox_xml) { "B
|
607
|
+
let(:expected_nokogiri_xml) { "B <p>R</p>" }
|
608
|
+
let(:expected_oga_xml) { "B <p>R&C</p>" }
|
609
|
+
let(:expected_ox_xml) { "B <p>R&C</p>" }
|
610
610
|
|
611
611
|
it "serializes special char mixed content correctly" do
|
612
612
|
parsed = MixedContentSpec::SpecialCharContentWithRawAndMixedOption.from_xml(xml)
|
@@ -136,4 +136,33 @@ RSpec.describe RenderNil do
|
|
136
136
|
expect(pottery.name).to eq("Unnamed Pottery")
|
137
137
|
expect(pottery.glaze).to be_nil
|
138
138
|
end
|
139
|
+
|
140
|
+
context "with empty string as values for attributes" do
|
141
|
+
let(:attributes) do
|
142
|
+
{
|
143
|
+
name: "",
|
144
|
+
clay_type: "",
|
145
|
+
glaze: "",
|
146
|
+
dimensions: [],
|
147
|
+
render_nil_nested: RenderNilNested.new,
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
it "does not tread empty string as nil" do
|
152
|
+
expected_yaml = <<~YAML
|
153
|
+
---
|
154
|
+
name: ''
|
155
|
+
clay_type: ''
|
156
|
+
glaze: ''
|
157
|
+
YAML
|
158
|
+
|
159
|
+
generated_yaml = model.to_yaml.strip
|
160
|
+
|
161
|
+
# Removing empty spaces from the end of the line because of and issue in
|
162
|
+
# libyaml -> https://github.com/yaml/libyaml/issues/46
|
163
|
+
generated_yaml = generated_yaml.gsub(": \n", ":\n")
|
164
|
+
|
165
|
+
expect(generated_yaml).to eq(expected_yaml.strip)
|
166
|
+
end
|
167
|
+
end
|
139
168
|
end
|