lutaml-model 0.6.7 → 0.7.2

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 (148) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-repos-todo.json +7 -0
  3. data/.github/workflows/dependent-repos.json +17 -9
  4. data/.rubocop.yml +1 -1
  5. data/.rubocop_todo.yml +51 -65
  6. data/Gemfile +4 -1
  7. data/README.adoc +5083 -2612
  8. data/RELEASE_NOTES.adoc +346 -0
  9. data/docs/custom_adapters.adoc +144 -0
  10. data/lib/lutaml/model/attribute.rb +101 -16
  11. data/lib/lutaml/model/choice.rb +7 -0
  12. data/lib/lutaml/model/comparable_model.rb +48 -9
  13. data/lib/lutaml/model/config.rb +48 -42
  14. data/lib/lutaml/model/error/collection_count_out_of_range_error.rb +1 -1
  15. data/lib/lutaml/model/error/polymorphic_error.rb +14 -0
  16. data/lib/lutaml/model/error.rb +1 -0
  17. data/lib/lutaml/model/format_registry.rb +41 -0
  18. data/lib/lutaml/model/hash/document.rb +11 -0
  19. data/lib/lutaml/model/hash/mapping.rb +19 -0
  20. data/lib/lutaml/model/hash/mapping_rule.rb +9 -0
  21. data/lib/lutaml/model/hash/standard_adapter.rb +17 -0
  22. data/lib/lutaml/model/hash/transform.rb +8 -0
  23. data/lib/lutaml/model/hash.rb +21 -0
  24. data/lib/lutaml/model/json/document.rb +11 -0
  25. data/lib/lutaml/model/json/mapping.rb +19 -0
  26. data/lib/lutaml/model/json/mapping_rule.rb +9 -0
  27. data/lib/lutaml/model/{json_adapter → json}/multi_json_adapter.rb +4 -5
  28. data/lib/lutaml/model/{json_adapter/standard_json_adapter.rb → json/standard_adapter.rb} +5 -3
  29. data/lib/lutaml/model/json/transform.rb +8 -0
  30. data/lib/lutaml/model/json.rb +21 -0
  31. data/lib/lutaml/model/key_value_document.rb +27 -0
  32. data/lib/lutaml/model/{key_value_mapping.rb → mapping/key_value_mapping.rb} +64 -16
  33. data/lib/lutaml/model/{key_value_mapping_rule.rb → mapping/key_value_mapping_rule.rb} +18 -2
  34. data/lib/lutaml/model/mapping/mapping.rb +13 -0
  35. data/lib/lutaml/model/mapping/mapping_rule.rb +300 -0
  36. data/lib/lutaml/model/schema/xml_compiler.rb +15 -15
  37. data/lib/lutaml/model/sequence.rb +2 -2
  38. data/lib/lutaml/model/serialization_adapter.rb +22 -0
  39. data/lib/lutaml/model/serialize.rb +219 -444
  40. data/lib/lutaml/model/services/logger.rb +54 -0
  41. data/lib/lutaml/model/services/transformer.rb +48 -0
  42. data/lib/lutaml/model/services.rb +2 -0
  43. data/lib/lutaml/model/toml/document.rb +11 -0
  44. data/lib/lutaml/model/toml/mapping.rb +27 -0
  45. data/lib/lutaml/model/toml/mapping_rule.rb +9 -0
  46. data/lib/lutaml/model/{toml_adapter → toml}/toml_rb_adapter.rb +3 -3
  47. data/lib/lutaml/model/toml/tomlib_adapter.rb +19 -0
  48. data/lib/lutaml/model/toml/transform.rb +8 -0
  49. data/lib/lutaml/model/toml.rb +30 -0
  50. data/lib/lutaml/model/transform/key_value_transform.rb +291 -0
  51. data/lib/lutaml/model/transform/xml_transform.rb +239 -0
  52. data/lib/lutaml/model/transform.rb +78 -0
  53. data/lib/lutaml/model/type/date.rb +1 -1
  54. data/lib/lutaml/model/type/date_time.rb +2 -2
  55. data/lib/lutaml/model/type/hash.rb +1 -1
  56. data/lib/lutaml/model/type/time.rb +2 -2
  57. data/lib/lutaml/model/type/time_without_date.rb +2 -2
  58. data/lib/lutaml/model/type/value.rb +6 -9
  59. data/lib/lutaml/model/uninitialized_class.rb +64 -0
  60. data/lib/lutaml/model/utils.rb +44 -0
  61. data/lib/lutaml/model/validation.rb +1 -0
  62. data/lib/lutaml/model/version.rb +1 -1
  63. data/lib/lutaml/model/{xml_adapter → xml}/builder/nokogiri.rb +2 -2
  64. data/lib/lutaml/model/{xml_adapter → xml}/builder/oga.rb +10 -10
  65. data/lib/lutaml/model/{xml_adapter → xml}/builder/ox.rb +1 -1
  66. data/lib/lutaml/model/{xml_adapter/xml_document.rb → xml/document.rb} +41 -21
  67. data/lib/lutaml/model/xml/element.rb +32 -0
  68. data/lib/lutaml/model/xml/mapping.rb +410 -0
  69. data/lib/lutaml/model/xml/mapping_rule.rb +141 -0
  70. data/lib/lutaml/model/xml/nokogiri_adapter.rb +232 -0
  71. data/lib/lutaml/model/{xml_adapter → xml}/oga/document.rb +1 -1
  72. data/lib/lutaml/model/{xml_adapter → xml}/oga/element.rb +3 -1
  73. data/lib/lutaml/model/xml/oga_adapter.rb +171 -0
  74. data/lib/lutaml/model/xml/ox_adapter.rb +215 -0
  75. data/lib/lutaml/model/xml/transform.rb +8 -0
  76. data/lib/lutaml/model/{xml_adapter → xml}/xml_attribute.rb +1 -1
  77. data/lib/lutaml/model/{xml_adapter → xml}/xml_element.rb +23 -10
  78. data/lib/lutaml/model/{xml_adapter → xml}/xml_namespace.rb +1 -1
  79. data/lib/lutaml/model/xml.rb +31 -0
  80. data/lib/lutaml/model/xml_adapter/element.rb +11 -25
  81. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +6 -223
  82. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +13 -163
  83. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +10 -207
  84. data/lib/lutaml/model/yaml/document.rb +10 -0
  85. data/lib/lutaml/model/yaml/mapping.rb +19 -0
  86. data/lib/lutaml/model/yaml/mapping_rule.rb +9 -0
  87. data/lib/lutaml/model/{yaml_adapter/standard_yaml_adapter.rb → yaml/standard_adapter.rb} +4 -3
  88. data/lib/lutaml/model/yaml/transform.rb +8 -0
  89. data/lib/lutaml/model/yaml.rb +21 -0
  90. data/lib/lutaml/model.rb +40 -4
  91. data/lutaml-model.gemspec +0 -4
  92. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +7 -7
  93. data/spec/fixtures/person.rb +5 -5
  94. data/spec/lutaml/model/attribute_spec.rb +37 -1
  95. data/spec/lutaml/model/cdata_spec.rb +9 -9
  96. data/spec/lutaml/model/collection_spec.rb +50 -2
  97. data/spec/lutaml/model/comparable_model_spec.rb +92 -27
  98. data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +598 -0
  99. data/spec/lutaml/model/custom_vobject_adapter_spec.rb +1226 -0
  100. data/spec/lutaml/model/defaults_spec.rb +1 -1
  101. data/spec/lutaml/model/enum_spec.rb +1 -1
  102. data/spec/lutaml/model/group_spec.rb +333 -20
  103. data/spec/lutaml/model/hash/adapter_spec.rb +255 -0
  104. data/spec/lutaml/model/json_adapter_spec.rb +6 -6
  105. data/spec/lutaml/model/key_value_mapping_spec.rb +65 -3
  106. data/spec/lutaml/model/mixed_content_spec.rb +24 -24
  107. data/spec/lutaml/model/multiple_mapping_spec.rb +5 -5
  108. data/spec/lutaml/model/ordered_content_spec.rb +6 -6
  109. data/spec/lutaml/model/polymorphic_spec.rb +526 -0
  110. data/spec/lutaml/model/render_empty_spec.rb +194 -0
  111. data/spec/lutaml/model/render_nil_spec.rb +206 -22
  112. data/spec/lutaml/model/root_mappings_spec.rb +3 -3
  113. data/spec/lutaml/model/schema/xml_compiler_spec.rb +6 -6
  114. data/spec/lutaml/model/serializable_spec.rb +179 -103
  115. data/spec/lutaml/model/simple_model_spec.rb +9 -9
  116. data/spec/lutaml/model/toml_adapter_spec.rb +6 -6
  117. data/spec/lutaml/model/toml_spec.rb +51 -0
  118. data/spec/lutaml/model/transformation_spec.rb +72 -15
  119. data/spec/lutaml/model/uninitialized_class_spec.rb +96 -0
  120. data/spec/lutaml/model/value_map_spec.rb +240 -0
  121. data/spec/lutaml/model/xml/namespace/nested_with_explicit_namespace_spec.rb +85 -0
  122. data/spec/lutaml/model/xml/namespace_spec.rb +57 -0
  123. data/spec/lutaml/model/xml/xml_element_spec.rb +93 -0
  124. data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +2 -2
  125. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +2 -2
  126. data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +2 -2
  127. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
  128. data/spec/lutaml/model/xml_adapter_spec.rb +6 -6
  129. data/spec/lutaml/model/xml_mapping_rule_spec.rb +105 -5
  130. data/spec/lutaml/model/xml_mapping_spec.rb +70 -16
  131. data/spec/lutaml/model/xml_spec.rb +63 -0
  132. data/spec/lutaml/model/yaml_adapter_spec.rb +3 -5
  133. data/spec/sample_model_spec.rb +3 -3
  134. data/spec/spec_helper.rb +3 -3
  135. metadata +76 -59
  136. data/lib/lutaml/model/json_adapter/json_document.rb +0 -20
  137. data/lib/lutaml/model/json_adapter/json_object.rb +0 -28
  138. data/lib/lutaml/model/loggable.rb +0 -15
  139. data/lib/lutaml/model/mapping_rule.rb +0 -109
  140. data/lib/lutaml/model/toml_adapter/toml_document.rb +0 -20
  141. data/lib/lutaml/model/toml_adapter/toml_object.rb +0 -28
  142. data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +0 -20
  143. data/lib/lutaml/model/toml_adapter.rb +0 -6
  144. data/lib/lutaml/model/xml_mapping.rb +0 -307
  145. data/lib/lutaml/model/xml_mapping_rule.rb +0 -122
  146. data/lib/lutaml/model/yaml_adapter/yaml_document.rb +0 -20
  147. data/lib/lutaml/model/yaml_adapter/yaml_object.rb +0 -28
  148. data/lib/lutaml/model/yaml_adapter.rb +0 -8
@@ -21,35 +21,80 @@ class RenderNil < Lutaml::Model::Serializable
21
21
  attribute :render_nil_nested, RenderNilNested
22
22
 
23
23
  json do
24
- map "name", to: :name, render_nil: true
25
- map "clay_type", to: :clay_type, render_nil: true
26
- map "glaze", to: :glaze, render_nil: true
27
- map "dimensions", to: :dimensions, render_nil: false
24
+ map "name", to: :name, render_nil: true, render_empty: true
25
+ map "clay_type", to: :clay_type, render_nil: true, render_empty: true
26
+ map "glaze", to: :glaze, render_nil: true, render_empty: true
27
+ map "dimensions", to: :dimensions, render_empty: false
28
28
  end
29
29
 
30
30
  xml do
31
31
  root "render_nil"
32
- map_element "name", to: :name, render_nil: true
33
- map_element "clay_type", to: :clay_type, render_nil: false
34
- map_element "glaze", to: :glaze, render_nil: true
32
+ map_element "name", to: :name, render_nil: true, render_empty: true
33
+ map_element "clay_type", to: :clay_type, render_nil: false, render_empty: true
34
+ map_element "glaze", to: :glaze, render_nil: true, render_empty: true
35
35
  map_element "render_nil_nested", to: :render_nil_nested, render_nil: true,
36
36
  render_default: true
37
- map_element "dimensions", to: :dimensions, render_nil: false
37
+ map_element "dimensions", to: :dimensions, render_empty: false
38
38
  end
39
39
 
40
40
  yaml do
41
- map "name", to: :name, render_nil: true
42
- map "clay_type", to: :clay_type, render_nil: false
43
- map "glaze", to: :glaze, render_nil: true
44
- map "dimensions", to: :dimensions, render_nil: false
41
+ map "name", to: :name, render_nil: true, render_empty: true
42
+ map "clay_type", to: :clay_type, render_nil: false, render_empty: true
43
+ map "glaze", to: :glaze, render_nil: true, render_empty: true
44
+ map "dimensions", to: :dimensions, render_empty: false
45
45
  map "render_nil_nested", to: :render_nil_nested, render_nil: false
46
46
  end
47
47
 
48
48
  toml do
49
- map "name", to: :name, render_nil: true
50
- map "clay_type", to: :clay_type, render_nil: false
51
- map "glaze", to: :glaze, render_nil: true
52
- map "dimensions", to: :dimensions, render_nil: false
49
+ map "name", to: :name, render_nil: :empty, render_empty: true
50
+ map "clay_type", to: :clay_type, render_nil: false, render_empty: true
51
+ map "glaze", to: :glaze, render_nil: false, render_empty: true
52
+ map "dimensions", to: :dimensions, render_empty: false
53
+ end
54
+ end
55
+
56
+ module RenderNilSpec
57
+ class OmitNilModel < Lutaml::Model::Serializable
58
+ attribute :items, :string, collection: true
59
+
60
+ xml do
61
+ root "omit-nil-model"
62
+ map_element "items", to: :items, render_nil: :omit
63
+ end
64
+
65
+ key_value do
66
+ map "items", to: :items, render_nil: :omit
67
+ end
68
+ end
69
+
70
+ class ExplicitNilModel < Lutaml::Model::Serializable
71
+ attribute :items, :string, collection: true
72
+
73
+ xml do
74
+ root "explicit-nil-model"
75
+ map_element "items", to: :items, render_nil: :as_nil
76
+ end
77
+
78
+ yaml do
79
+ map "items", to: :items, render_nil: :as_nil
80
+ end
81
+ end
82
+
83
+ class AsBlankNilModel < Lutaml::Model::Serializable
84
+ attribute :items, :string, collection: true
85
+
86
+ xml do
87
+ root "omit-nil-model"
88
+ map_element "items", to: :items, render_nil: :as_blank
89
+ end
90
+ end
91
+
92
+ class AsEmptyNilModel < Lutaml::Model::Serializable
93
+ attribute :items, :string, collection: true
94
+
95
+ yaml do
96
+ map "items", to: :items, render_nil: :as_empty
97
+ end
53
98
  end
54
99
  end
55
100
 
@@ -82,14 +127,14 @@ RSpec.describe RenderNil do
82
127
  expect(pottery.name).to be_nil
83
128
  expect(pottery.clay_type).to be_nil
84
129
  expect(pottery.glaze).to be_nil
85
- expect(pottery.dimensions).to eq([])
130
+ expect(pottery.dimensions).to be_nil
86
131
  end
87
132
 
88
133
  it "serializes to XML with render_nil option" do
89
134
  expected_xml = <<~XML
90
135
  <render_nil>
91
- <name/>
92
- <glaze/>
136
+ <name xsi:nil="true"/>
137
+ <glaze xsi:nil="true"/>
93
138
  <render_nil_nested/>
94
139
  </render_nil>
95
140
  XML
@@ -100,8 +145,8 @@ RSpec.describe RenderNil do
100
145
  it "deserializes from XML with render_nil option" do
101
146
  xml = <<~XML
102
147
  <render_nil>
103
- <name/>
104
- <glaze/>
148
+ <name xsi:nil="true" />
149
+ <glaze xsi:nil="true" />
105
150
  </render_nil>
106
151
  XML
107
152
 
@@ -148,7 +193,7 @@ RSpec.describe RenderNil do
148
193
  }
149
194
  end
150
195
 
151
- it "does not tread empty string as nil" do
196
+ it "does not treat empty string as nil" do
152
197
  expected_yaml = <<~YAML
153
198
  ---
154
199
  name: ''
@@ -165,4 +210,143 @@ RSpec.describe RenderNil do
165
210
  expect(generated_yaml).to eq(expected_yaml.strip)
166
211
  end
167
212
  end
213
+
214
+ describe "render_nil option" do
215
+ context "when :omit" do
216
+ let(:model) { RenderNilSpec::OmitNilModel.new(items: nil) }
217
+
218
+ describe "YAML" do
219
+ let(:parsed) do
220
+ RenderNilSpec::OmitNilModel.from_yaml(yaml)
221
+ end
222
+
223
+ let(:yaml) do
224
+ <<~YAML
225
+ ---
226
+ items:
227
+ YAML
228
+ end
229
+
230
+ it "omits nil collections while deserialize" do
231
+ expect(model.items).to be_nil
232
+ end
233
+
234
+ it "omits nil collections" do
235
+ expect(parsed.to_yaml.strip).to eq("--- {}")
236
+ end
237
+ end
238
+
239
+ describe "XML" do
240
+ let(:parsed) { RenderNilSpec::OmitNilModel.from_xml(xml) }
241
+
242
+ let(:xml) do
243
+ <<~XML
244
+ <omit-nil-model/>
245
+ XML
246
+ end
247
+
248
+ it "omits nil collection" do
249
+ expect(model.to_xml).not_to include("<items")
250
+ end
251
+
252
+ it "omits nil collections while deserialize" do
253
+ expect(parsed.items).to be_nil
254
+ end
255
+ end
256
+ end
257
+
258
+ context "when :as_nil" do
259
+ let(:model) { RenderNilSpec::ExplicitNilModel.new(items: nil) }
260
+
261
+ describe "YAML" do
262
+ let(:yaml) do
263
+ <<~YAML
264
+ ---
265
+ items:
266
+ YAML
267
+ end
268
+
269
+ let(:parsed) do
270
+ RenderNilSpec::ExplicitNilModel.from_yaml(yaml)
271
+ end
272
+
273
+ it "renders explicit nil" do
274
+ expect(model.to_yaml).to include("items:")
275
+ end
276
+
277
+ it "sets nil values while deserialize" do
278
+ expect(parsed.items).to be_nil
279
+ end
280
+ end
281
+
282
+ describe "XML" do
283
+ let(:xml) do
284
+ <<~XML
285
+ <explicit-nil-model>
286
+ <items xsi:nil="true"/>
287
+ </explicit-nil-model>
288
+ XML
289
+ end
290
+
291
+ let(:parsed) do
292
+ RenderNilSpec::ExplicitNilModel.from_xml(xml)
293
+ end
294
+
295
+ it "renders explicit nil" do
296
+ expect(model.to_xml).to include('<items xsi:nil="true"/>')
297
+ end
298
+
299
+ it "sets nil values while deserialize" do
300
+ expect(parsed.items).to be_nil
301
+ end
302
+ end
303
+ end
304
+
305
+ context "when :as_blank" do
306
+ let(:model) { RenderNilSpec::AsBlankNilModel.new(items: nil) }
307
+
308
+ let(:parsed) do
309
+ RenderNilSpec::AsBlankNilModel.from_xml(xml)
310
+ end
311
+
312
+ let(:xml) do
313
+ <<~XML
314
+ <explicit-nil-model>
315
+ <items/>
316
+ </explicit-nil-model>
317
+ XML
318
+ end
319
+
320
+ it "creates blank element from nil collections" do
321
+ expect(model.to_xml).to include("<items/>")
322
+ end
323
+
324
+ it "sets blank values while deserialize" do
325
+ expect(parsed.items).to eq([])
326
+ end
327
+ end
328
+
329
+ context "when :as_empty" do
330
+ let(:model) { RenderNilSpec::AsEmptyNilModel.new(items: nil) }
331
+
332
+ let(:parsed) do
333
+ RenderNilSpec::AsEmptyNilModel.from_yaml(yaml)
334
+ end
335
+
336
+ let(:yaml) do
337
+ <<~YAML
338
+ ---
339
+ items:
340
+ YAML
341
+ end
342
+
343
+ it "creates key and empty collection" do
344
+ expect(model.to_yaml).to include("items: []")
345
+ end
346
+
347
+ it "sets nil value while deserialize" do
348
+ expect(parsed.items).to be_nil
349
+ end
350
+ end
351
+ end
168
352
  end
@@ -283,15 +283,15 @@ RSpec.describe "RootMapping" do
283
283
  end
284
284
  end
285
285
 
286
- describe Lutaml::Model::YamlAdapter::StandardYamlAdapter do
286
+ describe Lutaml::Model::Yaml::StandardAdapter do
287
287
  it_behaves_like "having root mappings", :yaml
288
288
  end
289
289
 
290
- describe Lutaml::Model::JsonAdapter::StandardJsonAdapter do
290
+ describe Lutaml::Model::Json::StandardAdapter do
291
291
  it_behaves_like "having root mappings", :json
292
292
  end
293
293
 
294
- describe Lutaml::Model::TomlAdapter::TomlRbAdapter do
294
+ describe Lutaml::Model::Toml::TomlRbAdapter do
295
295
  it_behaves_like "having root mappings", :toml
296
296
  end
297
297
  end
@@ -471,7 +471,7 @@ RSpec.describe Lutaml::Model::Schema::XmlCompiler do
471
471
  Lutaml::Xsd::SimpleContent.new.tap do |ct|
472
472
  ct.extension = Lutaml::Xsd::ExtensionSimpleContent.new(base: "test_extension")
473
473
  ct.element_order = [
474
- Lutaml::Model::XmlAdapter::Element.new("Element", "extension"),
474
+ Lutaml::Model::Xml::Element.new("Element", "extension"),
475
475
  ]
476
476
  end
477
477
  end
@@ -488,7 +488,7 @@ RSpec.describe Lutaml::Model::Schema::XmlCompiler do
488
488
  Lutaml::Xsd::SimpleContent.new.tap do |ct|
489
489
  ct.restriction = Lutaml::Xsd::RestrictionSimpleContent.new(base: "test_restriction")
490
490
  ct.element_order = [
491
- Lutaml::Model::XmlAdapter::Element.new("Element", "restriction"),
491
+ Lutaml::Model::Xml::Element.new("Element", "restriction"),
492
492
  ]
493
493
  end
494
494
  end
@@ -870,7 +870,7 @@ RSpec.describe Lutaml::Model::Schema::XmlCompiler do
870
870
  element.min_occurs = 0
871
871
  element.max_occurs = 1
872
872
  element.complex_type = Lutaml::Xsd::ComplexType.new(name: "test_complex_type")
873
- element.element_order = [Lutaml::Model::XmlAdapter::Element.new("Element", "complex_type")]
873
+ element.element_order = [Lutaml::Model::Xml::Element.new("Element", "complex_type")]
874
874
  end
875
875
  end
876
876
 
@@ -910,7 +910,7 @@ RSpec.describe Lutaml::Model::Schema::XmlCompiler do
910
910
  element.type = "test_type"
911
911
  element.name = "test_name"
912
912
  element.complex_type = Lutaml::Xsd::ComplexType.new(name: "test_complex_type")
913
- element.element_order = [Lutaml::Model::XmlAdapter::Element.new("Element", "complex_type")]
913
+ element.element_order = [Lutaml::Model::Xml::Element.new("Element", "complex_type")]
914
914
  end
915
915
  end
916
916
 
@@ -1143,7 +1143,7 @@ RSpec.describe Lutaml::Model::Schema::XmlCompiler do
1143
1143
  end
1144
1144
 
1145
1145
  it "raises an error when element_order contains elements that isn't an attribute of the instance" do
1146
- element.element_order << Lutaml::Model::XmlAdapter::Element.new("Element", "test_element")
1146
+ element.element_order << Lutaml::Model::Xml::Element.new("Element", "test_element")
1147
1147
  expect { resolved_element_order }.to raise_error(NoMethodError)
1148
1148
  end
1149
1149
 
@@ -1915,7 +1915,7 @@ RSpec.describe Lutaml::Model::Schema::XmlCompiler do
1915
1915
  end
1916
1916
 
1917
1917
  def create_pattern_mapping(array)
1918
- array.map { |type, text| Lutaml::Model::XmlAdapter::Element.new(type, text) }
1918
+ array.map { |type, text| Lutaml::Model::Xml::Element.new(type, text) }
1919
1919
  end
1920
1920
 
1921
1921
  def to_mapping_hash(content)
@@ -96,6 +96,48 @@ module SerializeableSpec
96
96
  }
97
97
  end
98
98
  end
99
+
100
+ ### Single option serialization
101
+
102
+ class SingleOptionModel < Lutaml::Model::Serializable
103
+ attribute :name, :string
104
+ attribute :age, :integer
105
+ attribute :phone, :string
106
+ attribute :address, :string
107
+
108
+ json do
109
+ map "name", to: :name
110
+ map "age", to: :age
111
+ map "phone", to: :phone, with: { to: :phone_to_json }
112
+ map "address", to: :address, with: { from: :address_from_json }
113
+ end
114
+
115
+ xml do
116
+ root "person"
117
+ map_element "name", to: :name
118
+ map_element "age", to: :age
119
+ map_element "phone", to: :phone, with: { to: :phone_to_xml }
120
+ map_element "address", to: :address, with: { from: :address_from_xml }
121
+ end
122
+
123
+ def phone_to_json(model, doc)
124
+ doc["phone"] = "+1-#{model.phone}"
125
+ end
126
+
127
+ def address_from_json(model, value)
128
+ model.address = value.sub(/^Address: /, "")
129
+ end
130
+
131
+ def phone_to_xml(model, parent, doc)
132
+ el = doc.create_element("phone")
133
+ doc.add_text(el, "+1-#{model.phone}")
134
+ doc.add_element(parent, el)
135
+ end
136
+
137
+ def address_from_xml(model, value)
138
+ model.address = value.text.sub(/^Address: /, "")
139
+ end
140
+ end
99
141
  end
100
142
 
101
143
  RSpec.describe Lutaml::Model::Serializable do
@@ -151,48 +193,6 @@ RSpec.describe Lutaml::Model::Serializable do
151
193
  end
152
194
  end
153
195
 
154
- describe ".hash_representation" do
155
- context "when model is separate" do
156
- let(:instance) do
157
- SerializeableSpec::TestModel.new(name: "John", age: 18)
158
- end
159
-
160
- let(:expected_hash) do
161
- {
162
- "name" => "John",
163
- "age" => "18",
164
- }
165
- end
166
-
167
- it "return hash representation" do
168
- generate_hash = SerializeableSpec::TestModelMapper.hash_representation(
169
- instance, :yaml
170
- )
171
- expect(generate_hash).to eq(expected_hash)
172
- end
173
- end
174
-
175
- context "when model is self" do
176
- let(:instance) do
177
- SerializeableSpec::TestMapper.new(name: "John", age: 18)
178
- end
179
-
180
- let(:expected_hash) do
181
- {
182
- "na" => "John",
183
- "ag" => "18",
184
- }
185
- end
186
-
187
- it "return hash representation" do
188
- generate_hash = SerializeableSpec::TestMapper.hash_representation(
189
- instance, :yaml
190
- )
191
- expect(generate_hash).to eq(expected_hash)
192
- end
193
- end
194
- end
195
-
196
196
  describe ".mappings_for" do
197
197
  context "when mapping is defined" do
198
198
  it "returns the defined mapping" do
@@ -221,67 +221,68 @@ RSpec.describe Lutaml::Model::Serializable do
221
221
  end
222
222
  end
223
223
 
224
- describe ".translate_mappings" do
225
- let(:child_mappings) do
226
- {
227
- id: :key,
228
- path: %i[path link],
229
- name: %i[path name],
230
- }
231
- end
232
-
233
- let(:hash) do
234
- {
235
- "foo" => {
236
- "path" => {
237
- "link" => "link one",
238
- "name" => "one",
239
- },
240
- },
241
- "abc" => {
242
- "path" => {
243
- "link" => "link two",
244
- "name" => "two",
245
- },
246
- },
247
- "hello" => {
248
- "path" => {
249
- "link" => "link three",
250
- "name" => "three",
251
- },
252
- },
253
- }
254
- end
255
-
256
- let(:attr) { SerializeableSpec::TranslateMappings.attributes[:translate] }
257
-
258
- let(:expected_value) do
259
- [
260
- SerializeableSpec::TranslateHelper.new({
261
- "id" => "foo",
262
- "name" => "one",
263
- "path" => "link one",
264
- }),
265
- SerializeableSpec::TranslateHelper.new({
266
- "id" => "abc",
267
- "name" => "two",
268
- "path" => "link two",
269
- }),
270
- SerializeableSpec::TranslateHelper.new({
271
- "id" => "hello",
272
- "name" => "three",
273
- "path" => "link three",
274
- }),
275
- ]
276
- end
277
-
278
- it "generates hash based on child_mappings" do
279
- actual_value = described_class.translate_mappings(hash, child_mappings, attr, :yaml)
280
-
281
- expect(actual_value.map { |obj| [obj.id, obj.name, obj.path] })
282
- .to eq(expected_value.map { |obj| [obj.id, obj.name, obj.path] })
283
- end
284
- end
224
+ # TODO: Move to key_value_transform specs
225
+ # describe ".translate_mappings" do
226
+ # let(:child_mappings) do
227
+ # {
228
+ # id: :key,
229
+ # path: %i[path link],
230
+ # name: %i[path name],
231
+ # }
232
+ # end
233
+
234
+ # let(:hash) do
235
+ # {
236
+ # "foo" => {
237
+ # "path" => {
238
+ # "link" => "link one",
239
+ # "name" => "one",
240
+ # },
241
+ # },
242
+ # "abc" => {
243
+ # "path" => {
244
+ # "link" => "link two",
245
+ # "name" => "two",
246
+ # },
247
+ # },
248
+ # "hello" => {
249
+ # "path" => {
250
+ # "link" => "link three",
251
+ # "name" => "three",
252
+ # },
253
+ # },
254
+ # }
255
+ # end
256
+
257
+ # let(:attr) { SerializeableSpec::TranslateMappings.attributes[:translate] }
258
+
259
+ # let(:expected_value) do
260
+ # [
261
+ # SerializeableSpec::TranslateHelper.new({
262
+ # "id" => "foo",
263
+ # "name" => "one",
264
+ # "path" => "link one",
265
+ # }),
266
+ # SerializeableSpec::TranslateHelper.new({
267
+ # "id" => "abc",
268
+ # "name" => "two",
269
+ # "path" => "link two",
270
+ # }),
271
+ # SerializeableSpec::TranslateHelper.new({
272
+ # "id" => "hello",
273
+ # "name" => "three",
274
+ # "path" => "link three",
275
+ # }),
276
+ # ]
277
+ # end
278
+
279
+ # it "generates hash based on child_mappings" do
280
+ # actual_value = described_class.translate_mappings(hash, child_mappings, attr, :yaml)
281
+
282
+ # expect(actual_value.map { |obj| [obj.id, obj.name, obj.path] })
283
+ # .to eq(expected_value.map { |obj| [obj.id, obj.name, obj.path] })
284
+ # end
285
+ # end
285
286
 
286
287
  describe "#key_value" do
287
288
  let(:model) { SerializeableSpec::KeyValueMapper }
@@ -386,4 +387,79 @@ RSpec.describe Lutaml::Model::Serializable do
386
387
  end
387
388
  end
388
389
  end
390
+
391
+ describe "Single option serialization" do
392
+ let(:attributes) do
393
+ {
394
+ name: "John Doe",
395
+ age: 30,
396
+ phone: "123-456-7890",
397
+ address: "123 Main St",
398
+ }
399
+ end
400
+
401
+ let(:model) { SerializeableSpec::SingleOptionModel.new(attributes) }
402
+
403
+ describe "JSON serialization" do
404
+ let(:expected_json) do
405
+ {
406
+ name: "John Doe",
407
+ age: 30,
408
+ phone: "+1-123-456-7890",
409
+ address: "123 Main St",
410
+ }.to_json
411
+ end
412
+
413
+ let(:parsed) do
414
+ SerializeableSpec::SingleOptionModel.from_json(
415
+ {
416
+ name: "John Doe",
417
+ age: 30,
418
+ phone: "123-456-7890",
419
+ address: "Address: 123 Main St",
420
+ }.to_json,
421
+ )
422
+ end
423
+
424
+ it "serializes to JSON with custom name transformation" do
425
+ expect(model.to_json).to eq(expected_json)
426
+ end
427
+
428
+ it "deserializes from JSON with custom name transformation" do
429
+ expect(parsed).to eq(model)
430
+ end
431
+ end
432
+
433
+ describe "XML serialization" do
434
+ let(:expected_xml) do
435
+ <<~XML
436
+ <person>
437
+ <name>John Doe</name>
438
+ <age>30</age>
439
+ <phone>+1-123-456-7890</phone>
440
+ <address>123 Main St</address>
441
+ </person>
442
+ XML
443
+ end
444
+
445
+ let(:parsed) do
446
+ SerializeableSpec::SingleOptionModel.from_xml <<~XML
447
+ <person>
448
+ <name>John Doe</name>
449
+ <age>30</age>
450
+ <phone>123-456-7890</phone>
451
+ <address>Address: 123 Main St</address>
452
+ </person>
453
+ XML
454
+ end
455
+
456
+ it "serializes to XML with custom name transformation" do
457
+ expect(model.to_xml).to be_equivalent_to(expected_xml)
458
+ end
459
+
460
+ it "deserializes from XML with custom name transformation" do
461
+ expect(parsed).to eq(model)
462
+ end
463
+ end
464
+ end
389
465
  end