lutaml-model 0.3.24 → 0.3.26

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +36 -17
  3. data/README.adoc +396 -30
  4. data/lib/lutaml/model/attribute.rb +52 -27
  5. data/lib/lutaml/model/error/pattern_not_matched_error.rb +17 -0
  6. data/lib/lutaml/model/error/type_error.rb +9 -0
  7. data/lib/lutaml/model/error/unknown_type_error.rb +9 -0
  8. data/lib/lutaml/model/error/validation_error.rb +0 -1
  9. data/lib/lutaml/model/error.rb +3 -0
  10. data/lib/lutaml/model/mapping_hash.rb +8 -0
  11. data/lib/lutaml/model/serialize.rb +10 -5
  12. data/lib/lutaml/model/type/boolean.rb +38 -0
  13. data/lib/lutaml/model/type/date.rb +35 -0
  14. data/lib/lutaml/model/type/date_time.rb +32 -4
  15. data/lib/lutaml/model/type/decimal.rb +42 -0
  16. data/lib/lutaml/model/type/float.rb +37 -0
  17. data/lib/lutaml/model/type/hash.rb +62 -0
  18. data/lib/lutaml/model/type/integer.rb +41 -0
  19. data/lib/lutaml/model/type/string.rb +49 -0
  20. data/lib/lutaml/model/type/time.rb +49 -0
  21. data/lib/lutaml/model/type/time_without_date.rb +37 -5
  22. data/lib/lutaml/model/type/value.rb +52 -0
  23. data/lib/lutaml/model/type.rb +50 -114
  24. data/lib/lutaml/model/validation.rb +2 -1
  25. data/lib/lutaml/model/version.rb +1 -1
  26. data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +12 -3
  27. data/lib/lutaml/model/xml_adapter/builder/ox.rb +7 -1
  28. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +3 -1
  29. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +12 -8
  30. data/lib/lutaml/model/xml_adapter/xml_document.rb +6 -8
  31. data/lib/lutaml/model/xml_mapping.rb +6 -2
  32. data/lib/lutaml/model/xml_mapping_rule.rb +7 -1
  33. data/lutaml-model.gemspec +1 -1
  34. data/spec/address_spec.rb +170 -0
  35. data/spec/fixtures/address.rb +33 -0
  36. data/spec/fixtures/person.rb +73 -0
  37. data/spec/fixtures/sample_model.rb +40 -0
  38. data/spec/fixtures/vase.rb +38 -0
  39. data/spec/fixtures/xml/special_char.xml +13 -0
  40. data/spec/lutaml/model/attribute_spec.rb +139 -0
  41. data/spec/lutaml/model/cdata_spec.rb +520 -0
  42. data/spec/lutaml/model/collection_spec.rb +299 -0
  43. data/spec/lutaml/model/comparable_model_spec.rb +106 -0
  44. data/spec/lutaml/model/custom_model_spec.rb +410 -0
  45. data/spec/lutaml/model/custom_serialization_spec.rb +170 -0
  46. data/spec/lutaml/model/defaults_spec.rb +221 -0
  47. data/spec/lutaml/model/delegation_spec.rb +340 -0
  48. data/spec/lutaml/model/inheritance_spec.rb +92 -0
  49. data/spec/lutaml/model/json_adapter_spec.rb +37 -0
  50. data/spec/lutaml/model/key_value_mapping_spec.rb +86 -0
  51. data/spec/lutaml/model/map_content_spec.rb +118 -0
  52. data/spec/lutaml/model/mixed_content_spec.rb +625 -0
  53. data/spec/lutaml/model/namespace_spec.rb +57 -0
  54. data/spec/lutaml/model/ordered_content_spec.rb +83 -0
  55. data/spec/lutaml/model/render_nil_spec.rb +138 -0
  56. data/spec/lutaml/model/schema/json_schema_spec.rb +79 -0
  57. data/spec/lutaml/model/schema/relaxng_schema_spec.rb +60 -0
  58. data/spec/lutaml/model/schema/xsd_schema_spec.rb +55 -0
  59. data/spec/lutaml/model/schema/yaml_schema_spec.rb +47 -0
  60. data/spec/lutaml/model/serializable_spec.rb +297 -0
  61. data/spec/lutaml/model/serializable_validation_spec.rb +90 -0
  62. data/spec/lutaml/model/simple_model_spec.rb +314 -0
  63. data/spec/lutaml/model/toml_adapter_spec.rb +39 -0
  64. data/spec/lutaml/model/type/boolean_spec.rb +54 -0
  65. data/spec/lutaml/model/type/date_spec.rb +118 -0
  66. data/spec/lutaml/model/type/date_time_spec.rb +127 -0
  67. data/spec/lutaml/model/type/decimal_spec.rb +125 -0
  68. data/spec/lutaml/model/type/float_spec.rb +191 -0
  69. data/spec/lutaml/model/type/hash_spec.rb +63 -0
  70. data/spec/lutaml/model/type/integer_spec.rb +145 -0
  71. data/spec/lutaml/model/type/string_spec.rb +150 -0
  72. data/spec/lutaml/model/type/time_spec.rb +142 -0
  73. data/spec/lutaml/model/type/time_without_date_spec.rb +125 -0
  74. data/spec/lutaml/model/type_spec.rb +276 -0
  75. data/spec/lutaml/model/utils_spec.rb +79 -0
  76. data/spec/lutaml/model/validation_spec.rb +83 -0
  77. data/spec/lutaml/model/with_child_mapping_spec.rb +174 -0
  78. data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +56 -0
  79. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +56 -0
  80. data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +61 -0
  81. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +251 -0
  82. data/spec/lutaml/model/xml_adapter_spec.rb +178 -0
  83. data/spec/lutaml/model/xml_mapping_spec.rb +863 -0
  84. data/spec/lutaml/model/yaml_adapter_spec.rb +30 -0
  85. data/spec/lutaml/model_spec.rb +1 -0
  86. data/spec/person_spec.rb +161 -0
  87. data/spec/spec_helper.rb +33 -0
  88. metadata +68 -2
@@ -0,0 +1,56 @@
1
+ require "spec_helper"
2
+ require "oga"
3
+ require_relative "../../../../lib/lutaml/model/xml_adapter/oga_adapter"
4
+
5
+ RSpec.xdescribe Lutaml::Model::XmlAdapter::OgaAdapter do
6
+ let(:xml_string) do
7
+ <<~XML
8
+ <root xmlns="http://example.com/default" xmlns:prefix="http://example.com/prefixed">
9
+ <prefix:child attr="value" prefix:attr="prefixed_value">Text</prefix:child>
10
+ </root>
11
+ XML
12
+ end
13
+
14
+ let(:document) { described_class.parse(xml_string) }
15
+
16
+ context "parsing XML with namespaces" do
17
+ it "parses the root element with default namespace" do
18
+ expect(document.root.name).to eq("root")
19
+ expect(document.root.namespace).to eq("http://example.com/default")
20
+ expect(document.root.namespace_prefix).to be_nil
21
+ end
22
+
23
+ it "parses child element with prefixed namespace" do
24
+ child = document.root.children.first
25
+ expect(child.name).to eq("child")
26
+ expect(child.namespace).to eq("http://example.com/prefixed")
27
+ expect(child.namespace_prefix).to eq("prefix")
28
+ end
29
+
30
+ it "parses attributes with and without namespaces" do
31
+ child = document.root.children.first
32
+ expect(child.attributes["attr"].value).to eq("value")
33
+ expect(child.attributes["attr"].namespace).to be_nil
34
+ expect(child.attributes["prefix:attr"].value).to eq("prefixed_value")
35
+ expect(child.attributes["prefix:attr"].namespace).to eq("http://example.com/prefixed")
36
+ expect(child.attributes["prefix:attr"].namespace_prefix).to eq("prefix")
37
+ end
38
+ end
39
+
40
+ context "generating XML with namespaces" do
41
+ it "generates XML with namespaces correctly" do
42
+ xml_output = document.to_xml
43
+ parsed_output = Oga.parse_xml(xml_output)
44
+
45
+ root = parsed_output.root
46
+ expect(root.name).to eq("root")
47
+ expect(root.namespace.uri).to eq("http://example.com/default")
48
+
49
+ child = root.children.first
50
+ expect(child.name).to eq("child")
51
+ expect(child.namespace.uri).to eq("http://example.com/prefixed")
52
+ expect(child.get("attr")).to eq("value")
53
+ expect(child.get("prefix:attr")).to eq("prefixed_value")
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,61 @@
1
+ require "spec_helper"
2
+ require "ox"
3
+ require_relative "../../../../lib/lutaml/model/xml_adapter/ox_adapter"
4
+
5
+ RSpec.describe Lutaml::Model::XmlAdapter::OxAdapter do
6
+ let(:xml_string) do
7
+ <<-XML
8
+ <root xmlns="http://example.com/default" xmlns:prefix="http://example.com/prefixed">
9
+ <prefix:child attr="value" prefix:attr="prefixed_value">Text</prefix:child>
10
+ <!-- just-a-comment -->
11
+ </root>
12
+ XML
13
+ end
14
+
15
+ let(:document) { described_class.parse(xml_string) }
16
+
17
+ context "parsing XML with namespaces" do
18
+ it "parses the root element with default namespace" do
19
+ expect(document.root.name).to eq("root")
20
+ expect(document.root.namespace.uri).to eq("http://example.com/default")
21
+ expect(document.root.namespace.prefix).to be_nil
22
+ end
23
+
24
+ it "parses child element with prefixed namespace" do
25
+ child = document.root.children.first
26
+ expect(child.name).to eq("prefix:child")
27
+ expect(child.namespace.uri).to eq("http://example.com/prefixed")
28
+ expect(child.namespace.prefix).to eq("prefix")
29
+ end
30
+
31
+ it "parses comment" do
32
+ expect(document.root.nodes.last.text).to eq("just-a-comment")
33
+ end
34
+
35
+ it "parses attributes with and without namespaces" do
36
+ child = document.root.children.first
37
+ expect(child.attributes["attr"].value).to eq("value")
38
+ expect(child.attributes["attr"].namespace).to be_nil
39
+ expect(child.attributes["prefix:attr"].value).to eq("prefixed_value")
40
+ expect(child.attributes["prefix:attr"].namespace).to eq("http://example.com/prefixed")
41
+ expect(child.attributes["prefix:attr"].namespace_prefix).to eq("prefix")
42
+ end
43
+ end
44
+
45
+ context "generating XML with namespaces" do
46
+ it "generates XML with namespaces correctly" do
47
+ xml_output = document.to_xml
48
+ parsed_output = Ox.parse(xml_output)
49
+
50
+ root = parsed_output
51
+ expect(root.name).to eq("root")
52
+ expect(root.attributes[:xmlns]).to eq("http://example.com/default")
53
+ expect(root.attributes[:"xmlns:prefix"]).to eq("http://example.com/prefixed")
54
+
55
+ child = root.nodes.first
56
+ expect(child.name).to eq("prefix:child")
57
+ expect(child.attributes[:attr]).to eq("value")
58
+ expect(child.attributes[:"prefix:attr"]).to eq("prefixed_value")
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,251 @@
1
+ require "spec_helper"
2
+ require "lutaml/model/xml_adapter/nokogiri_adapter"
3
+ require "lutaml/model/xml_adapter/ox_adapter"
4
+ require "lutaml/model/xml_adapter/oga_adapter"
5
+ require "lutaml/model"
6
+
7
+ RSpec.shared_context "XML namespace models" do
8
+ class TestModelNoPrefix < Lutaml::Model::Serializable
9
+ attribute :name, :string
10
+
11
+ xml do
12
+ root "test"
13
+ namespace "http://example.com/test", "test"
14
+ map_element "name", to: :name
15
+ end
16
+ end
17
+
18
+ class TestModelWithPrefix < Lutaml::Model::Serializable
19
+ attribute :name, :string
20
+
21
+ xml do
22
+ root "test"
23
+ namespace "http://example.com/test", "test"
24
+ map_element "name", to: :name
25
+ end
26
+ end
27
+
28
+ class SamplePrefixedNamespacedModel < Lutaml::Model::Serializable
29
+ attribute :id, :string
30
+ attribute :lang, :string
31
+ attribute :name, :string, default: -> { "Anonymous" }
32
+ attribute :age, :integer, default: -> { 18 }
33
+
34
+ xml do
35
+ root "SamplePrefixedNamespacedModel"
36
+ namespace "http://example.com/foo", "foo"
37
+
38
+ map_attribute "id", to: :id
39
+ map_attribute "lang", to: :lang,
40
+ prefix: "xml",
41
+ namespace: "http://example.com/xml"
42
+
43
+ map_element "Name", to: :name, prefix: "bar", namespace: "http://example.com/bar"
44
+ map_element "Age", to: :age, prefix: "baz", namespace: "http://example.com/baz"
45
+ end
46
+ end
47
+
48
+ class NamespaceNilPrefixedNamespaced < Lutaml::Model::Serializable
49
+ attribute :namespace_model, SamplePrefixedNamespacedModel
50
+
51
+ xml do
52
+ root "NamespaceNil"
53
+ map_element "SamplePrefixedNamespacedModel", to: :namespace_model,
54
+ namespace: nil,
55
+ prefix: nil
56
+ end
57
+ end
58
+
59
+ class SampleDefaultNamespacedModel < Lutaml::Model::Serializable
60
+ attribute :id, :string
61
+ attribute :lang, :string
62
+ attribute :name, :string, default: -> { "Anonymous" }
63
+ attribute :age, :integer, default: -> { 18 }
64
+
65
+ xml do
66
+ root "SampleDefaultNamespacedModel"
67
+ namespace "http://example.com/foo"
68
+
69
+ map_attribute "id", to: :id
70
+ map_attribute "lang", to: :lang,
71
+ prefix: "xml",
72
+ namespace: "http://example.com/xml"
73
+
74
+ map_element "Name", to: :name, prefix: "bar", namespace: "http://example.com/bar"
75
+ map_element "Age", to: :age, prefix: "baz", namespace: "http://example.com/baz"
76
+ end
77
+ end
78
+
79
+ class NamespaceNilDefaultNamespaced < Lutaml::Model::Serializable
80
+ attribute :namespace_model, SampleDefaultNamespacedModel
81
+
82
+ xml do
83
+ root "NamespaceNil"
84
+ map_element "SampleDefaultNamespacedModel", to: :namespace_model,
85
+ namespace: nil,
86
+ prefix: nil
87
+ end
88
+ end
89
+ end
90
+
91
+ RSpec.shared_examples "XML serialization with namespace" do |model_class, xml_string|
92
+ it "serializes to XML" do
93
+ model = model_class.new(name: "Test Name")
94
+ expect(model.to_xml).to be_equivalent_to(xml_string)
95
+ end
96
+
97
+ it "deserializes from XML" do
98
+ model = model_class.from_xml(xml_string)
99
+ expect(model.name).to eq("Test Name")
100
+ end
101
+ end
102
+
103
+ RSpec.shared_examples "an XML namespace parser" do |adapter_class|
104
+ include_context "XML namespace models"
105
+
106
+ around do |example|
107
+ old_adapter = Lutaml::Model::Config.xml_adapter
108
+ Lutaml::Model::Config.xml_adapter = adapter_class
109
+ example.run
110
+ ensure
111
+ Lutaml::Model::Config.xml_adapter = old_adapter
112
+ end
113
+
114
+ context "with no prefix" do
115
+ include_examples "XML serialization with namespace",
116
+ TestModelNoPrefix,
117
+ '<test xmlns="http://example.com/test"><name>Test Name</name></test>'
118
+ end
119
+
120
+ context "with prefix" do
121
+ include_examples "XML serialization with namespace",
122
+ TestModelWithPrefix,
123
+ '<test:test xmlns:test="http://example.com/test"><test:name>Test Name</test:name></test:test>'
124
+ end
125
+
126
+ context "with prefixed namespace" do
127
+ let(:attributes) { { name: "John Doe", age: 30 } }
128
+ let(:model) { SamplePrefixedNamespacedModel.new(attributes) }
129
+
130
+ it "serializes to XML" do
131
+ expected_xml = <<~XML
132
+ <foo:SamplePrefixedNamespacedModel xmlns:foo="http://example.com/foo" xmlns:bar="http://example.com/bar" xmlns:baz="http://example.com/baz">
133
+ <bar:Name>John Doe</bar:Name>
134
+ <baz:Age>30</baz:Age>
135
+ </foo:SamplePrefixedNamespacedModel>
136
+ XML
137
+
138
+ expect(model.to_xml).to be_equivalent_to(expected_xml)
139
+ end
140
+
141
+ it "deserializes from XML" do
142
+ xml = <<~XML
143
+ <foo:SamplePrefixedNamespacedModel xmlns:foo="http://example.com/foo" xmlns:bar="http://example.com/bar" xmlns:baz="http://example.com/baz">
144
+ <bar:Name>John Doe</bar:Name>
145
+ <baz:Age>30</baz:Age>
146
+ </foo:SamplePrefixedNamespacedModel>
147
+ XML
148
+
149
+ new_model = SamplePrefixedNamespacedModel.from_xml(xml)
150
+ expect(new_model.name).to eq("John Doe")
151
+ expect(new_model.age).to eq(30)
152
+ end
153
+
154
+ it "round-trips if namespace is set" do
155
+ xml = <<~XML
156
+ <foo:SamplePrefixedNamespacedModel xml:lang="en" xmlns:foo="http://example.com/foo" xmlns:bar="http://example.com/bar" xmlns:baz="http://example.com/baz">
157
+ <bar:Name>John Doe</bar:Name>
158
+ <baz:Age>30</baz:Age>
159
+ </foo:SamplePrefixedNamespacedModel>
160
+ XML
161
+
162
+ doc = SamplePrefixedNamespacedModel.from_xml(xml)
163
+ generated_xml = doc.to_xml
164
+ expect(generated_xml).to be_equivalent_to(xml)
165
+ end
166
+
167
+ it "round-trips if namespace is set to nil in parent" do
168
+ xml = <<~XML
169
+ <NamespaceNil xmlns:foo="http://example.com/foo" xmlns:bar="http://example.com/bar" xmlns:baz="http://example.com/baz">
170
+ <SamplePrefixedNamespacedModel xml:lang="en">
171
+ <bar:Name>John Doe</bar:Name>
172
+ <baz:Age>30</baz:Age>
173
+ </SamplePrefixedNamespacedModel>
174
+ </NamespaceNil>
175
+ XML
176
+
177
+ doc = NamespaceNilPrefixedNamespaced.from_xml(xml)
178
+ generated_xml = doc.to_xml
179
+ expect(generated_xml).to be_equivalent_to(xml)
180
+ end
181
+ end
182
+
183
+ context "with default namespace" do
184
+ let(:attributes) { { name: "Jane Smith", age: 25 } }
185
+ let(:model) { SampleDefaultNamespacedModel.new(attributes) }
186
+
187
+ it "serializes to XML" do
188
+ expected_xml = <<~XML
189
+ <SampleDefaultNamespacedModel xmlns="http://example.com/foo" xmlns:bar="http://example.com/bar" xmlns:baz="http://example.com/baz">
190
+ <bar:Name>Jane Smith</bar:Name>
191
+ <baz:Age>25</baz:Age>
192
+ </SampleDefaultNamespacedModel>
193
+ XML
194
+
195
+ expect(model.to_xml).to be_equivalent_to(expected_xml)
196
+ end
197
+
198
+ it "deserializes from XML" do
199
+ xml = <<~XML
200
+ <SampleDefaultNamespacedModel xmlns="http://example.com/foo" xmlns:bar="http://example.com/bar" xmlns:baz="http://example.com/baz">
201
+ <bar:Name>Jane Smith</bar:Name>
202
+ <baz:Age>25</baz:Age>
203
+ </SampleDefaultNamespacedModel>
204
+ XML
205
+
206
+ new_model = SampleDefaultNamespacedModel.from_xml(xml)
207
+ expect(new_model.name).to eq("Jane Smith")
208
+ expect(new_model.age).to eq(25)
209
+ end
210
+
211
+ it "round-trips if namespace is set" do
212
+ xml = <<~XML
213
+ <SampleDefaultNamespacedModel xml:lang="en" xmlns="http://example.com/foo" xmlns:bar="http://example.com/bar" xmlns:baz="http://example.com/baz">
214
+ <bar:Name>Jane Smith</bar:Name>
215
+ <baz:Age>25</baz:Age>
216
+ </SampleDefaultNamespacedModel>
217
+ XML
218
+
219
+ doc = SampleDefaultNamespacedModel.from_xml(xml)
220
+ generated_xml = doc.to_xml
221
+ expect(generated_xml).to be_equivalent_to(xml)
222
+ end
223
+
224
+ it "round-trips if namespace is set to nil in parent" do
225
+ xml = <<~XML
226
+ <NamespaceNil xmlns:bar="http://example.com/bar" xmlns:baz="http://example.com/baz">
227
+ <SampleDefaultNamespacedModel xml:lang="en">
228
+ <bar:Name>Jane Smith</bar:Name>
229
+ <baz:Age>25</baz:Age>
230
+ </SampleDefaultNamespacedModel>
231
+ </NamespaceNil>
232
+ XML
233
+
234
+ doc = NamespaceNilDefaultNamespaced.from_xml(xml)
235
+ generated_xml = doc.to_xml
236
+ expect(generated_xml).to be_equivalent_to(xml)
237
+ end
238
+ end
239
+ end
240
+
241
+ RSpec.describe Lutaml::Model::XmlAdapter::NokogiriAdapter do
242
+ it_behaves_like "an XML namespace parser", described_class
243
+ end
244
+
245
+ RSpec.describe Lutaml::Model::XmlAdapter::OxAdapter do
246
+ it_behaves_like "an XML namespace parser", described_class
247
+ end
248
+
249
+ RSpec.xdescribe Lutaml::Model::XmlAdapter::OgaAdapter do
250
+ it_behaves_like "an XML namespace parser", described_class
251
+ end
@@ -0,0 +1,178 @@
1
+ require "spec_helper"
2
+ require "lutaml/model/xml_adapter/nokogiri_adapter"
3
+ require "lutaml/model/xml_adapter/ox_adapter"
4
+ require "lutaml/model/xml_adapter/oga_adapter"
5
+ require_relative "../../fixtures/sample_model"
6
+
7
+ module XmlAdapterSpec
8
+ class Mstyle < Lutaml::Model::Serializable
9
+ attribute :displaystyle, :string, default: -> { "true" }
10
+ attribute :color, :string
11
+ attribute :finish, :string, default: -> { "yes" }
12
+
13
+ xml do
14
+ root "mstyle"
15
+
16
+ map_attribute :displaystyle, to: :displaystyle, render_default: true
17
+ end
18
+ end
19
+
20
+ class Maths < Lutaml::Model::Serializable
21
+ attribute :display, :string, default: -> { "block" }
22
+ attribute :style, Mstyle, default: -> { Mstyle.new }
23
+
24
+ xml do
25
+ root "math"
26
+
27
+ map_attribute :display, to: :display
28
+ map_attribute "color", to: :color, delegate: :style
29
+ map_attribute "finish", to: :finish, delegate: :style,
30
+ render_default: true
31
+ map_element :mstyle, to: :style, render_default: true
32
+ end
33
+ end
34
+ end
35
+
36
+ RSpec.shared_examples "an XML adapter" do |adapter_class|
37
+ around do |example|
38
+ old_adapter = Lutaml::Model::Config.xml_adapter
39
+ Lutaml::Model::Config.xml_adapter = adapter_class
40
+
41
+ example.run
42
+ ensure
43
+ Lutaml::Model::Config.xml_adapter = old_adapter
44
+ end
45
+
46
+ before do
47
+ # Ensure BigDecimal is loaded because it is being used in sample model
48
+ require "bigdecimal"
49
+ end
50
+
51
+ let(:attributes) { { name: "John Doe", age: 30 } }
52
+ let(:model) { SampleModel.new(attributes) }
53
+
54
+ context "with default value" do
55
+ it "set display attribute correctly, other attributes default" do
56
+ xml = "<math display='true'></math>"
57
+
58
+ parsed = XmlAdapterSpec::Maths.from_xml(xml)
59
+ expect(parsed.display).to eq("true")
60
+ expect(parsed.style.class).to eq(XmlAdapterSpec::Mstyle)
61
+ expect(parsed.style.displaystyle).to eq("true")
62
+ end
63
+
64
+ it "set style attribute correctly, other attributes default" do
65
+ xml = "<math> <mstyle displaystyle='false'> </mstyle> </math>"
66
+
67
+ parsed = XmlAdapterSpec::Maths.from_xml(xml)
68
+ expect(parsed.display).to eq("block")
69
+ expect(parsed.style.class).to eq(XmlAdapterSpec::Mstyle)
70
+ expect(parsed.style.displaystyle).to eq("false")
71
+ end
72
+
73
+ it "sets attributes default values" do
74
+ xml = "<math> </math>"
75
+
76
+ parsed = XmlAdapterSpec::Maths.from_xml(xml)
77
+ expect(parsed.display).to eq("block")
78
+ expect(parsed.style.class).to eq(XmlAdapterSpec::Mstyle)
79
+ expect(parsed.style.displaystyle).to eq("true")
80
+ end
81
+
82
+ it "delegate attributes with value" do
83
+ xml = "<math color='blue' finish='no'></math>"
84
+
85
+ parsed = XmlAdapterSpec::Maths.from_xml(xml)
86
+ expect(parsed.style.color).to eq("blue")
87
+ expect(parsed.style.finish).to eq("no")
88
+ expect(parsed.style.displaystyle).to eq("true")
89
+ end
90
+
91
+ it "delegate attributes with one value" do
92
+ xml = "<math color='blue'></math>"
93
+
94
+ parsed = XmlAdapterSpec::Maths.from_xml(xml)
95
+ expect(parsed.style.color).to eq("blue")
96
+ expect(parsed.style.finish).to eq("yes")
97
+ expect(parsed.style.displaystyle).to eq("true")
98
+ end
99
+
100
+ it "delegate attributes with no value" do
101
+ xml = "<math></math>"
102
+
103
+ parsed = XmlAdapterSpec::Maths.from_xml(xml)
104
+ expect(parsed.style.color).to be_nil
105
+ expect(parsed.style.finish).to eq("yes")
106
+ expect(parsed.style.displaystyle).to eq("true")
107
+ end
108
+
109
+ it "round-trips XML and add default values" do
110
+ input_xml = <<~XML
111
+ <math display='true'></math>
112
+ XML
113
+
114
+ output_xml = <<~XML
115
+ <math display="true" finish="yes">
116
+ <mstyle displaystyle="true"/>
117
+ </math>
118
+ XML
119
+
120
+ parsed = XmlAdapterSpec::Maths.from_xml(input_xml)
121
+
122
+ expect(parsed.to_xml.strip).to eq(output_xml.strip)
123
+ end
124
+ end
125
+
126
+ it "serializes to XML" do
127
+ expected_xml = <<~XML
128
+ <SampleModel>
129
+ <Name>John Doe</Name>
130
+ <Age>30</Age>
131
+ </SampleModel>
132
+ XML
133
+
134
+ doc = adapter_class.parse(expected_xml)
135
+ xml = doc.to_xml
136
+ expect(xml).to be_equivalent_to(expected_xml)
137
+ end
138
+
139
+ it "serializes to XML with only content" do
140
+ expected_xml = <<~XML
141
+ <Tag>
142
+ Bug
143
+ </Tag>
144
+ XML
145
+
146
+ doc = SampleModelTag.from_xml(expected_xml)
147
+ xml = doc.to_xml
148
+ expect(xml).to be_equivalent_to(expected_xml)
149
+ end
150
+
151
+ it "deserializes from XML" do
152
+ xml = <<~XML
153
+ <SampleModel>
154
+ <Name>John Doe</Name>
155
+ <Age>30</Age>
156
+ </SampleModel>
157
+ XML
158
+
159
+ doc = adapter_class.parse(xml)
160
+ new_model = SampleModel.new(doc.root.children.to_h do |child|
161
+ [child.name.downcase.to_sym, child.text]
162
+ end)
163
+ expect(new_model.name).to eq("John Doe")
164
+ expect(new_model.age).to eq(30)
165
+ end
166
+ end
167
+
168
+ RSpec.describe Lutaml::Model::XmlAdapter::NokogiriAdapter do
169
+ it_behaves_like "an XML adapter", described_class
170
+ end
171
+
172
+ RSpec.describe Lutaml::Model::XmlAdapter::OxAdapter do
173
+ it_behaves_like "an XML adapter", described_class
174
+ end
175
+
176
+ RSpec.xdescribe Lutaml::Model::XmlAdapter::OgaAdapter do
177
+ it_behaves_like "an XML adapter", described_class
178
+ end