lutaml-model 0.7.3 → 0.7.5

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 (184) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +1 -0
  3. data/.github/workflows/release.yml +3 -0
  4. data/.gitignore +6 -1
  5. data/.irbrc +1 -0
  6. data/.pryrc +1 -0
  7. data/.rubocop_todo.yml +25 -52
  8. data/README.adoc +2177 -192
  9. data/docs/custom_registers.adoc +228 -0
  10. data/docs/schema_generation.adoc +898 -0
  11. data/docs/schema_import.adoc +364 -0
  12. data/flake.lock +114 -0
  13. data/flake.nix +103 -0
  14. data/lib/lutaml/model/attribute.rb +230 -94
  15. data/lib/lutaml/model/choice.rb +30 -0
  16. data/lib/lutaml/model/collection.rb +194 -0
  17. data/lib/lutaml/model/comparable_model.rb +3 -3
  18. data/lib/lutaml/model/config.rb +26 -3
  19. data/lib/lutaml/model/constants.rb +2 -0
  20. data/lib/lutaml/model/error/element_count_out_of_range_error.rb +29 -0
  21. data/lib/lutaml/model/error/invalid_attribute_name_error.rb +15 -0
  22. data/lib/lutaml/model/error/invalid_attribute_options_error.rb +16 -0
  23. data/lib/lutaml/model/error/invalid_choice_range_error.rb +3 -5
  24. data/lib/lutaml/model/error/register/not_registrable_class_error.rb +11 -0
  25. data/lib/lutaml/model/error/type/invalid_value_error.rb +5 -3
  26. data/lib/lutaml/model/error/type/max_bound_error.rb +20 -0
  27. data/lib/lutaml/model/error/type/max_length_error.rb +20 -0
  28. data/lib/lutaml/model/error/type/min_bound_error.rb +20 -0
  29. data/lib/lutaml/model/error/type/min_length_error.rb +20 -0
  30. data/lib/lutaml/model/error/type/pattern_not_matched_error.rb +18 -0
  31. data/lib/lutaml/model/error/validation_failed_error.rb +9 -0
  32. data/lib/lutaml/model/error.rb +10 -0
  33. data/lib/lutaml/model/errors.rb +36 -0
  34. data/lib/lutaml/model/format_registry.rb +5 -2
  35. data/lib/lutaml/model/global_register.rb +41 -0
  36. data/lib/lutaml/model/{hash.rb → hash_adapter.rb} +5 -5
  37. data/lib/lutaml/model/jsonl/document.rb +14 -0
  38. data/lib/lutaml/model/jsonl/mapping.rb +19 -0
  39. data/lib/lutaml/model/jsonl/mapping_rule.rb +9 -0
  40. data/lib/lutaml/model/jsonl/standard_adapter.rb +33 -0
  41. data/lib/lutaml/model/jsonl/transform.rb +19 -0
  42. data/lib/lutaml/model/jsonl.rb +21 -0
  43. data/lib/lutaml/model/key_value_document.rb +3 -2
  44. data/lib/lutaml/model/mapping/key_value_mapping.rb +64 -4
  45. data/lib/lutaml/model/mapping/key_value_mapping_rule.rb +4 -0
  46. data/lib/lutaml/model/mapping/mapping_rule.rb +8 -3
  47. data/lib/lutaml/model/register.rb +105 -0
  48. data/lib/lutaml/model/registrable.rb +6 -0
  49. data/lib/lutaml/model/schema/base_schema.rb +64 -0
  50. data/lib/lutaml/model/schema/decorators/attribute.rb +114 -0
  51. data/lib/lutaml/model/schema/decorators/choices.rb +31 -0
  52. data/lib/lutaml/model/schema/decorators/class_definition.rb +85 -0
  53. data/lib/lutaml/model/schema/decorators/definition_collection.rb +97 -0
  54. data/lib/lutaml/model/schema/generator/definition.rb +53 -0
  55. data/lib/lutaml/model/schema/generator/definitions_collection.rb +81 -0
  56. data/lib/lutaml/model/schema/generator/properties_collection.rb +63 -0
  57. data/lib/lutaml/model/schema/generator/property.rb +110 -0
  58. data/lib/lutaml/model/schema/generator/ref.rb +24 -0
  59. data/lib/lutaml/model/schema/helpers/template_helper.rb +49 -0
  60. data/lib/lutaml/model/schema/json_schema.rb +42 -49
  61. data/lib/lutaml/model/schema/relaxng_schema.rb +14 -10
  62. data/lib/lutaml/model/schema/renderer.rb +36 -0
  63. data/lib/lutaml/model/schema/shared_methods.rb +24 -0
  64. data/lib/lutaml/model/schema/templates/model.erb +9 -0
  65. data/lib/lutaml/model/schema/xml_compiler/attribute.rb +85 -0
  66. data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +45 -0
  67. data/lib/lutaml/model/schema/xml_compiler/choice.rb +65 -0
  68. data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +27 -0
  69. data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +34 -0
  70. data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +136 -0
  71. data/lib/lutaml/model/schema/xml_compiler/element.rb +104 -0
  72. data/lib/lutaml/model/schema/xml_compiler/group.rb +97 -0
  73. data/lib/lutaml/model/schema/xml_compiler/restriction.rb +101 -0
  74. data/lib/lutaml/model/schema/xml_compiler/sequence.rb +50 -0
  75. data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +36 -0
  76. data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +189 -0
  77. data/lib/lutaml/model/schema/xml_compiler.rb +231 -587
  78. data/lib/lutaml/model/schema/xsd_schema.rb +12 -8
  79. data/lib/lutaml/model/schema/yaml_schema.rb +41 -35
  80. data/lib/lutaml/model/schema.rb +1 -0
  81. data/lib/lutaml/model/sequence.rb +60 -30
  82. data/lib/lutaml/model/serialize.rb +175 -53
  83. data/lib/lutaml/model/services/base.rb +11 -0
  84. data/lib/lutaml/model/services/logger.rb +2 -2
  85. data/lib/lutaml/model/services/rule_value_extractor.rb +92 -0
  86. data/lib/lutaml/model/services/type/validator/number.rb +25 -0
  87. data/lib/lutaml/model/services/type/validator/string.rb +52 -0
  88. data/lib/lutaml/model/services/type/validator.rb +43 -0
  89. data/lib/lutaml/model/services/validator.rb +145 -0
  90. data/lib/lutaml/model/services.rb +3 -0
  91. data/lib/lutaml/model/transform/key_value_transform.rb +60 -50
  92. data/lib/lutaml/model/transform/xml_transform.rb +46 -57
  93. data/lib/lutaml/model/transform.rb +22 -8
  94. data/lib/lutaml/model/type/boolean.rb +1 -1
  95. data/lib/lutaml/model/type/date.rb +1 -1
  96. data/lib/lutaml/model/type/date_time.rb +1 -1
  97. data/lib/lutaml/model/type/decimal.rb +11 -9
  98. data/lib/lutaml/model/type/float.rb +2 -1
  99. data/lib/lutaml/model/type/integer.rb +24 -21
  100. data/lib/lutaml/model/type/string.rb +4 -2
  101. data/lib/lutaml/model/type/time.rb +1 -1
  102. data/lib/lutaml/model/type/time_without_date.rb +1 -1
  103. data/lib/lutaml/model/type/value.rb +5 -1
  104. data/lib/lutaml/model/type.rb +5 -2
  105. data/lib/lutaml/model/utils.rb +30 -8
  106. data/lib/lutaml/model/validation.rb +6 -4
  107. data/lib/lutaml/model/version.rb +1 -1
  108. data/lib/lutaml/model/xml/document.rb +37 -19
  109. data/lib/lutaml/model/xml/mapping.rb +74 -13
  110. data/lib/lutaml/model/xml/mapping_rule.rb +10 -2
  111. data/lib/lutaml/model/xml/nokogiri_adapter.rb +5 -3
  112. data/lib/lutaml/model/xml/oga/element.rb +4 -1
  113. data/lib/lutaml/model/xml/oga_adapter.rb +4 -3
  114. data/lib/lutaml/model/xml/ox_adapter.rb +20 -6
  115. data/lib/lutaml/model/xml/xml_element.rb +3 -28
  116. data/lib/lutaml/model/xml_adapter/element.rb +1 -1
  117. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +1 -1
  118. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +1 -1
  119. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +1 -1
  120. data/lib/lutaml/model/yamls/document.rb +14 -0
  121. data/lib/lutaml/model/yamls/mapping.rb +19 -0
  122. data/lib/lutaml/model/yamls/mapping_rule.rb +9 -0
  123. data/lib/lutaml/model/yamls/standard_adapter.rb +34 -0
  124. data/lib/lutaml/model/yamls/transform.rb +19 -0
  125. data/lib/lutaml/model/yamls.rb +21 -0
  126. data/lib/lutaml/model.rb +7 -31
  127. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +4 -5
  128. data/spec/fixtures/xml/advanced_test_schema.xsd +134 -0
  129. data/spec/fixtures/xml/examples/nested_categories.xml +55 -0
  130. data/spec/fixtures/xml/examples/valid_catalog.xml +43 -0
  131. data/spec/fixtures/xml/product_catalog.xsd +151 -0
  132. data/spec/fixtures/xml/specifications_schema.xsd +38 -0
  133. data/spec/lutaml/model/attribute_collection_spec.rb +101 -0
  134. data/spec/lutaml/model/attribute_spec.rb +41 -44
  135. data/spec/lutaml/model/choice_spec.rb +44 -0
  136. data/spec/lutaml/model/custom_collection_spec.rb +830 -0
  137. data/spec/lutaml/model/custom_model_spec.rb +15 -3
  138. data/spec/lutaml/model/defaults_spec.rb +5 -1
  139. data/spec/lutaml/model/global_register_spec.rb +108 -0
  140. data/spec/lutaml/model/group_spec.rb +9 -3
  141. data/spec/lutaml/model/jsonl/standard_adapter_spec.rb +91 -0
  142. data/spec/lutaml/model/jsonl_spec.rb +229 -0
  143. data/spec/lutaml/model/multiple_mapping_spec.rb +1 -1
  144. data/spec/lutaml/model/register/key_value_spec.rb +275 -0
  145. data/spec/lutaml/model/register/xml_spec.rb +187 -0
  146. data/spec/lutaml/model/register_spec.rb +147 -0
  147. data/spec/lutaml/model/rule_value_extractor_spec.rb +162 -0
  148. data/spec/lutaml/model/schema/generator/definitions_collection_spec.rb +120 -0
  149. data/spec/lutaml/model/schema/json_schema_spec.rb +412 -51
  150. data/spec/lutaml/model/schema/json_schema_to_models_spec.rb +383 -0
  151. data/spec/lutaml/model/schema/xml_compiler/attribute_group_spec.rb +65 -0
  152. data/spec/lutaml/model/schema/xml_compiler/attribute_spec.rb +63 -0
  153. data/spec/lutaml/model/schema/xml_compiler/choice_spec.rb +71 -0
  154. data/spec/lutaml/model/schema/xml_compiler/complex_content_restriction_spec.rb +55 -0
  155. data/spec/lutaml/model/schema/xml_compiler/complex_content_spec.rb +37 -0
  156. data/spec/lutaml/model/schema/xml_compiler/complex_type_spec.rb +173 -0
  157. data/spec/lutaml/model/schema/xml_compiler/element_spec.rb +63 -0
  158. data/spec/lutaml/model/schema/xml_compiler/group_spec.rb +86 -0
  159. data/spec/lutaml/model/schema/xml_compiler/restriction_spec.rb +76 -0
  160. data/spec/lutaml/model/schema/xml_compiler/sequence_spec.rb +59 -0
  161. data/spec/lutaml/model/schema/xml_compiler/simple_content_spec.rb +55 -0
  162. data/spec/lutaml/model/schema/xml_compiler/simple_type_spec.rb +181 -0
  163. data/spec/lutaml/model/schema/xml_compiler_spec.rb +503 -1804
  164. data/spec/lutaml/model/schema/yaml_schema_spec.rb +249 -26
  165. data/spec/lutaml/model/sequence_spec.rb +36 -0
  166. data/spec/lutaml/model/serializable_spec.rb +31 -0
  167. data/spec/lutaml/model/type_spec.rb +8 -4
  168. data/spec/lutaml/model/utils_spec.rb +3 -3
  169. data/spec/lutaml/model/xml/derived_attributes_spec.rb +1 -1
  170. data/spec/lutaml/model/xml/xml_element_spec.rb +7 -1
  171. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
  172. data/spec/lutaml/model/xml_adapter_spec.rb +24 -0
  173. data/spec/lutaml/model/xml_mapping_rule_spec.rb +11 -4
  174. data/spec/lutaml/model/xml_mapping_spec.rb +1 -1
  175. data/spec/lutaml/model/yamls/standard_adapter_spec.rb +183 -0
  176. data/spec/lutaml/model/yamls_spec.rb +294 -0
  177. data/spec/spec_helper.rb +1 -0
  178. metadata +105 -9
  179. data/lib/lutaml/model/schema/templates/simple_type.rb +0 -247
  180. /data/lib/lutaml/model/{hash → hash_adapter}/document.rb +0 -0
  181. /data/lib/lutaml/model/{hash → hash_adapter}/mapping.rb +0 -0
  182. /data/lib/lutaml/model/{hash → hash_adapter}/mapping_rule.rb +0 -0
  183. /data/lib/lutaml/model/{hash → hash_adapter}/standard_adapter.rb +0 -0
  184. /data/lib/lutaml/model/{hash → hash_adapter}/transform.rb +0 -0
@@ -0,0 +1,183 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Lutaml::Model::Yamls::StandardAdapter do
4
+ let(:valid_yamls_content) do
5
+ <<~YAMLS
6
+ name: John
7
+ age: 30
8
+ ---
9
+ name: Jane
10
+ age: 25
11
+ ---
12
+ name: Bob
13
+ age: 35
14
+ YAMLS
15
+ end
16
+
17
+ let(:invalid_yamls_content) do
18
+ <<~YAMLS
19
+ name: John
20
+ age: 30
21
+ ---
22
+ invalid yaml: [
23
+ ---
24
+ name: Bob
25
+ age: 35
26
+ YAMLS
27
+ end
28
+
29
+ describe ".parse" do
30
+ context "with valid YAMLS content" do
31
+ it "parses each YAML document as a separate object" do
32
+ results = described_class.parse(valid_yamls_content)
33
+ expect(results).to be_an(Array)
34
+ expect(results.length).to eq(3)
35
+ expect(results.first).to eq({ "name" => "John", "age" => 30 })
36
+ expect(results.last).to eq({ "name" => "Bob", "age" => 35 })
37
+ end
38
+ end
39
+
40
+ context "with invalid YAMLS content" do
41
+ it "skips invalid YAML documents and continues parsing" do
42
+ results = described_class.parse(invalid_yamls_content)
43
+ expect(results).to be_an(Array)
44
+ expect(results.length).to eq(2)
45
+ expect(results.first).to eq({ "name" => "John", "age" => 30 })
46
+ expect(results.last).to eq({ "name" => "Bob", "age" => 35 })
47
+ end
48
+ end
49
+
50
+ context "with empty documents" do
51
+ let(:yamls_with_empty_docs) do
52
+ <<~YAMLS
53
+ name: John
54
+ ---
55
+
56
+ ---
57
+ name: Jane
58
+ YAMLS
59
+ end
60
+
61
+ it "skips empty documents" do
62
+ results = described_class.parse(yamls_with_empty_docs)
63
+ expect(results).to be_an(Array)
64
+ expect(results.length).to eq(2)
65
+ end
66
+ end
67
+
68
+ context "with single document" do
69
+ let(:single_yaml_doc) do
70
+ <<~YAMLS
71
+ name: John
72
+ age: 30
73
+ YAMLS
74
+ end
75
+
76
+ it "parses single document correctly" do
77
+ results = described_class.parse(single_yaml_doc)
78
+ expect(results).to be_an(Array)
79
+ expect(results.length).to eq(1)
80
+ expect(results.first).to eq({ "name" => "John", "age" => 30 })
81
+ end
82
+ end
83
+
84
+ context "with complex YAML structures" do
85
+ let(:complex_yamls_content) do
86
+ <<~YAMLS
87
+ name: John
88
+ address:
89
+ street: 123 Main St
90
+ city: New York
91
+ hobbies:
92
+ - reading
93
+ - swimming
94
+ ---
95
+ name: Jane
96
+ address:
97
+ street: 456 Oak Ave
98
+ city: Los Angeles
99
+ hobbies:
100
+ - hiking
101
+ - cooking
102
+ YAMLS
103
+ end
104
+
105
+ it "parses complex YAML structures" do
106
+ results = described_class.parse(complex_yamls_content)
107
+ expect(results).to be_an(Array)
108
+ expect(results.length).to eq(2)
109
+
110
+ expect(results.first).to eq({
111
+ "name" => "John",
112
+ "address" => {
113
+ "street" => "123 Main St",
114
+ "city" => "New York",
115
+ },
116
+ "hobbies" => ["reading", "swimming"],
117
+ })
118
+
119
+ expect(results.last).to eq({
120
+ "name" => "Jane",
121
+ "address" => {
122
+ "street" => "456 Oak Ave",
123
+ "city" => "Los Angeles",
124
+ },
125
+ "hobbies" => ["hiking", "cooking"],
126
+ })
127
+ end
128
+ end
129
+ end
130
+
131
+ describe "#to_yamls" do
132
+ let(:john) { { "name" => "John", "age" => 30 } }
133
+ let(:adapter) { described_class.new([john]) }
134
+
135
+ it "generates YAMLS format" do
136
+ expect(adapter.to_yamls.strip).to eq(john.to_yaml.strip)
137
+ end
138
+
139
+ context "with multiple objects" do
140
+ let(:jane) { { "name" => "Jane", "age" => 25 } }
141
+ let(:adapter) { described_class.new([john, jane]) }
142
+
143
+ let(:expected_yamls) do
144
+ <<~YAMLS.strip
145
+ #{john.to_yaml.strip}
146
+ #{jane.to_yaml.strip}
147
+ YAMLS
148
+ end
149
+
150
+ it "generates multiple YAML documents" do
151
+ expect(adapter.to_yamls.strip).to eq(expected_yamls.strip)
152
+ end
153
+ end
154
+
155
+ context "with complex data structures" do
156
+ let(:adapter) do
157
+ described_class.new([
158
+ {
159
+ "name" => "John",
160
+ "address" => {
161
+ "street" => "123 Main St",
162
+ "city" => "New York",
163
+ },
164
+ "hobbies" => ["reading", "swimming"],
165
+ },
166
+ ])
167
+ end
168
+
169
+ it "generates complex YAML structures" do
170
+ result = adapter.to_yamls
171
+ expect(result).to include("name")
172
+ expect(result).to include("address")
173
+ expect(result).to include("hobbies")
174
+ end
175
+ end
176
+ end
177
+
178
+ describe "FORMAT_SYMBOL" do
179
+ it "returns :yaml" do
180
+ expect(described_class::FORMAT_SYMBOL).to eq(:yaml)
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,294 @@
1
+ require "spec_helper"
2
+
3
+ module YamlsSpec
4
+ class Address < Lutaml::Model::Serializable
5
+ attribute :city, :string
6
+
7
+ yaml do
8
+ map "city", to: :city
9
+ end
10
+ end
11
+
12
+ class Person < Lutaml::Model::Serializable
13
+ attribute :name, :string
14
+ attribute :age, :integer
15
+ attribute :address, Address
16
+
17
+ yaml do
18
+ map "full_name", to: :name
19
+ map "age", to: :age
20
+ map "address", to: :address
21
+ end
22
+ end
23
+
24
+ class Directory < Lutaml::Model::Collection
25
+ instances :persons, Person
26
+
27
+ yamls do
28
+ map_instances to: :persons
29
+ end
30
+ end
31
+ end
32
+
33
+ RSpec.describe "Yamls" do
34
+ let(:john) do
35
+ YamlsSpec::Person.new(
36
+ {
37
+ name: "John",
38
+ age: "30",
39
+ address: YamlsSpec::Address.new({ city: "New York" }),
40
+ },
41
+ )
42
+ end
43
+
44
+ let(:jane) do
45
+ YamlsSpec::Person.new(
46
+ {
47
+ name: "Jane",
48
+ age: "25",
49
+ address: YamlsSpec::Address.new({ city: "London" }),
50
+ },
51
+ )
52
+ end
53
+
54
+ let(:bob) do
55
+ YamlsSpec::Person.new(
56
+ {
57
+ name: "Bob",
58
+ age: "35",
59
+ address: YamlsSpec::Address.new({ city: "Paris" }),
60
+ },
61
+ )
62
+ end
63
+
64
+ let(:valid_yamls) do
65
+ <<~YAMLS.strip
66
+ ---
67
+ full_name: John
68
+ age: 30
69
+ address:
70
+ city: New York
71
+ ---
72
+ full_name: Jane
73
+ age: 25
74
+ address:
75
+ city: London
76
+ ---
77
+ full_name: Bob
78
+ age: 35
79
+ address:
80
+ city: Paris
81
+ YAMLS
82
+ end
83
+
84
+ let(:valid_yamls_with_empty_docs) do
85
+ <<~YAMLS
86
+ ---
87
+ full_name: John
88
+ age: 30
89
+ address:
90
+ city: New York
91
+ ---
92
+
93
+ ---
94
+ full_name: Jane
95
+ age: 25
96
+ address:
97
+ city: London
98
+ ---
99
+
100
+
101
+ ---
102
+ full_name: Bob
103
+ age: 35
104
+ address:
105
+ city: Paris
106
+ ---
107
+
108
+
109
+ YAMLS
110
+ end
111
+
112
+ let(:invalid_yamls_content) do
113
+ <<~YAMLS
114
+ ---
115
+ full_name: John
116
+ age: 30
117
+ address:
118
+ city: New York
119
+ ---
120
+ invalid yaml: [
121
+ ---
122
+ full_name: Bob
123
+ age: 35
124
+ address:
125
+ city: Paris
126
+ YAMLS
127
+ end
128
+
129
+ let(:parsed) { YamlsSpec::Directory.from_yamls(valid_yamls) }
130
+
131
+ it "parses all the yaml documents" do
132
+ expect(parsed.persons).to eq([john, jane, bob])
133
+ end
134
+
135
+ it "handles empty documents" do
136
+ expect(
137
+ YamlsSpec::Directory.from_yamls(valid_yamls_with_empty_docs).persons,
138
+ ).to eq([john, jane, bob])
139
+ end
140
+
141
+ it "round trips valid yamls correctly" do
142
+ expect(parsed.to_yamls).to eq(valid_yamls)
143
+ end
144
+
145
+ it "removes empty documents when round triping valid yamls" do
146
+ expect(
147
+ YamlsSpec::Directory.from_yamls(valid_yamls_with_empty_docs).to_yamls,
148
+ ).to eq(valid_yamls)
149
+ end
150
+
151
+ it "skips invalid documents and show warning" do
152
+ expect do
153
+ YamlsSpec::Directory.from_yamls(invalid_yamls_content)
154
+ end.to output(/Skipping invalid yaml: /).to_stderr
155
+ end
156
+
157
+ describe "parsing" do
158
+ it "handles empty documents" do
159
+ yamls_with_empty_docs = <<~YAMLS
160
+ ---
161
+ full_name: John
162
+ age: 30
163
+ address:
164
+ city: New York
165
+ ---
166
+
167
+ ---
168
+ full_name: Jane
169
+ age: 25
170
+ address:
171
+ city: London
172
+ YAMLS
173
+ result = YamlsSpec::Directory.from_yamls(yamls_with_empty_docs)
174
+ expect(result.persons).to be_an(Array)
175
+ expect(result.persons.length).to eq(2)
176
+ end
177
+ end
178
+
179
+ describe "roundtrip" do
180
+ it "maintains data integrity with special characters" do
181
+ special_chars_yamls = <<~YAMLS
182
+ ---
183
+ full_name: John Doe
184
+ age: 30
185
+ address:
186
+ city: New York
187
+ ---
188
+ full_name: Jane "Smith"
189
+ age: 25
190
+ address:
191
+ city: London
192
+ YAMLS
193
+
194
+ # Parse the original YAMLS
195
+ directory = YamlsSpec::Directory.from_yamls(special_chars_yamls)
196
+
197
+ # Serialize back to YAMLS
198
+ serialized = directory.to_yamls
199
+
200
+ # Parse the serialized data again
201
+ roundtrip_directory = YamlsSpec::Directory.from_yamls(serialized)
202
+
203
+ # Compare the results
204
+ expect(roundtrip_directory.persons.length).to eq(directory.persons.length)
205
+ roundtrip_directory.persons.each_with_index do |person, index|
206
+ original = directory.persons[index]
207
+ expect(person.name).to eq(original.name)
208
+ expect(person.age).to eq(original.age)
209
+ expect(person.address.city).to eq(original.address.city)
210
+ end
211
+ end
212
+ end
213
+
214
+ describe "format conversion" do
215
+ let(:valid_yamls_content) do
216
+ <<~YAMLS.strip
217
+ ---
218
+ full_name: John
219
+ age: 30
220
+ address:
221
+ city: New York
222
+ ---
223
+ full_name: Jane
224
+ age: 25
225
+ address:
226
+ city: London
227
+ ---
228
+ full_name: Bob
229
+ age: 35
230
+ address:
231
+ city: Paris
232
+ YAMLS
233
+ end
234
+
235
+ it "converts between YAMLS and YAML" do
236
+ # Parse YAMLS to Directory
237
+ directory = YamlsSpec::Directory.from_yamls(valid_yamls_content)
238
+
239
+ # Convert to YAML
240
+ yaml_string = directory.to_yaml
241
+
242
+ # Parse YAML back to Directory
243
+ yaml_directory = YamlsSpec::Directory.from_yaml(yaml_string)
244
+
245
+ # Convert back to YAMLS
246
+ yamls_output = yaml_directory.to_yamls
247
+
248
+ # Final parse to verify
249
+ final_directory = YamlsSpec::Directory.from_yamls(yamls_output)
250
+
251
+ # Compare the results
252
+ expect(final_directory.persons.length).to eq(directory.persons.length)
253
+ final_directory.persons.each_with_index do |person, index|
254
+ original = directory.persons[index]
255
+ expect(person.name).to eq(original.name)
256
+ expect(person.age).to eq(original.age)
257
+ expect(person.address.city).to eq(original.address.city)
258
+ end
259
+ end
260
+
261
+ it "handles collections of objects" do
262
+ # Create a directory with people
263
+ directory = YamlsSpec::Directory.new(
264
+ [
265
+ YamlsSpec::Person.new(
266
+ name: "John",
267
+ age: 30,
268
+ address: YamlsSpec::Address.new(city: "New York"),
269
+ ),
270
+ YamlsSpec::Person.new(
271
+ name: "Jane",
272
+ age: 25,
273
+ address: YamlsSpec::Address.new(city: "London"),
274
+ ),
275
+ ],
276
+ )
277
+
278
+ # Convert to YAMLS
279
+ yamls_output = directory.to_yamls
280
+
281
+ # Parse back to verify
282
+ parsed_directory = YamlsSpec::Directory.from_yamls(yamls_output)
283
+
284
+ # Compare the results
285
+ expect(parsed_directory.persons.length).to eq(directory.persons.length)
286
+ parsed_directory.persons.each_with_index do |person, index|
287
+ original = directory.persons[index]
288
+ expect(person.name).to eq(original.name)
289
+ expect(person.age).to eq(original.age)
290
+ expect(person.address.city).to eq(original.address.city)
291
+ end
292
+ end
293
+ end
294
+ end
data/spec/spec_helper.rb CHANGED
@@ -28,6 +28,7 @@ require_relative "../lib/lutaml/model"
28
28
 
29
29
  Lutaml::Model::Config.configure do |config|
30
30
  config.xml_adapter_type = :nokogiri
31
+ config.hash_adapter_type = :standard_hash
31
32
  config.json_adapter_type = :standard_json
32
33
  config.yaml_adapter_type = :standard_yaml
33
34
  config.toml_adapter_type = :toml_rb