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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +35 -16
  3. data/README.adoc +274 -28
  4. data/lib/lutaml/model/attribute.rb +18 -8
  5. data/lib/lutaml/model/error/type_error.rb +9 -0
  6. data/lib/lutaml/model/error/unknown_type_error.rb +9 -0
  7. data/lib/lutaml/model/error/validation_error.rb +0 -1
  8. data/lib/lutaml/model/error.rb +2 -0
  9. data/lib/lutaml/model/serialize.rb +6 -1
  10. data/lib/lutaml/model/type/boolean.rb +38 -0
  11. data/lib/lutaml/model/type/date.rb +35 -0
  12. data/lib/lutaml/model/type/date_time.rb +32 -4
  13. data/lib/lutaml/model/type/decimal.rb +42 -0
  14. data/lib/lutaml/model/type/float.rb +37 -0
  15. data/lib/lutaml/model/type/hash.rb +62 -0
  16. data/lib/lutaml/model/type/integer.rb +41 -0
  17. data/lib/lutaml/model/type/string.rb +49 -0
  18. data/lib/lutaml/model/type/time.rb +49 -0
  19. data/lib/lutaml/model/type/time_without_date.rb +37 -5
  20. data/lib/lutaml/model/type/value.rb +52 -0
  21. data/lib/lutaml/model/type.rb +50 -114
  22. data/lib/lutaml/model/version.rb +1 -1
  23. data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +5 -2
  24. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +2 -1
  25. data/lib/lutaml/model/xml_adapter/xml_document.rb +0 -2
  26. data/lutaml-model.gemspec +1 -1
  27. data/spec/address_spec.rb +170 -0
  28. data/spec/fixtures/address.rb +33 -0
  29. data/spec/fixtures/person.rb +73 -0
  30. data/spec/fixtures/sample_model.rb +40 -0
  31. data/spec/fixtures/vase.rb +38 -0
  32. data/spec/fixtures/xml/special_char.xml +13 -0
  33. data/spec/lutaml/model/attribute_spec.rb +112 -0
  34. data/spec/lutaml/model/collection_spec.rb +299 -0
  35. data/spec/lutaml/model/comparable_model_spec.rb +106 -0
  36. data/spec/lutaml/model/custom_model_spec.rb +410 -0
  37. data/spec/lutaml/model/custom_serialization_spec.rb +170 -0
  38. data/spec/lutaml/model/defaults_spec.rb +221 -0
  39. data/spec/lutaml/model/delegation_spec.rb +340 -0
  40. data/spec/lutaml/model/inheritance_spec.rb +92 -0
  41. data/spec/lutaml/model/json_adapter_spec.rb +37 -0
  42. data/spec/lutaml/model/key_value_mapping_spec.rb +86 -0
  43. data/spec/lutaml/model/map_content_spec.rb +118 -0
  44. data/spec/lutaml/model/mixed_content_spec.rb +625 -0
  45. data/spec/lutaml/model/namespace_spec.rb +57 -0
  46. data/spec/lutaml/model/ordered_content_spec.rb +83 -0
  47. data/spec/lutaml/model/render_nil_spec.rb +138 -0
  48. data/spec/lutaml/model/schema/json_schema_spec.rb +79 -0
  49. data/spec/lutaml/model/schema/relaxng_schema_spec.rb +60 -0
  50. data/spec/lutaml/model/schema/xsd_schema_spec.rb +55 -0
  51. data/spec/lutaml/model/schema/yaml_schema_spec.rb +47 -0
  52. data/spec/lutaml/model/serializable_spec.rb +297 -0
  53. data/spec/lutaml/model/serializable_validation_spec.rb +85 -0
  54. data/spec/lutaml/model/simple_model_spec.rb +314 -0
  55. data/spec/lutaml/model/toml_adapter_spec.rb +39 -0
  56. data/spec/lutaml/model/type/boolean_spec.rb +54 -0
  57. data/spec/lutaml/model/type/date_spec.rb +118 -0
  58. data/spec/lutaml/model/type/date_time_spec.rb +127 -0
  59. data/spec/lutaml/model/type/decimal_spec.rb +125 -0
  60. data/spec/lutaml/model/type/float_spec.rb +191 -0
  61. data/spec/lutaml/model/type/hash_spec.rb +63 -0
  62. data/spec/lutaml/model/type/integer_spec.rb +145 -0
  63. data/spec/lutaml/model/type/string_spec.rb +150 -0
  64. data/spec/lutaml/model/type/time_spec.rb +142 -0
  65. data/spec/lutaml/model/type/time_without_date_spec.rb +125 -0
  66. data/spec/lutaml/model/type_spec.rb +276 -0
  67. data/spec/lutaml/model/utils_spec.rb +79 -0
  68. data/spec/lutaml/model/validation_spec.rb +83 -0
  69. data/spec/lutaml/model/with_child_mapping_spec.rb +174 -0
  70. data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +56 -0
  71. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +56 -0
  72. data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +61 -0
  73. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +251 -0
  74. data/spec/lutaml/model/xml_adapter_spec.rb +178 -0
  75. data/spec/lutaml/model/xml_mapping_spec.rb +863 -0
  76. data/spec/lutaml/model/yaml_adapter_spec.rb +30 -0
  77. data/spec/lutaml/model_spec.rb +1 -0
  78. data/spec/person_spec.rb +161 -0
  79. data/spec/spec_helper.rb +33 -0
  80. metadata +66 -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