lutaml-model 0.7.1 → 0.7.7

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 (259) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +1 -0
  3. data/.github/workflows/dependent-tests.yml +4 -0
  4. data/.github/workflows/rake.yml +12 -0
  5. data/.github/workflows/release.yml +3 -0
  6. data/.gitignore +6 -1
  7. data/.irbrc +1 -0
  8. data/.pryrc +1 -0
  9. data/.rubocop.yml +1 -1
  10. data/.rubocop_todo.yml +58 -84
  11. data/Gemfile +4 -1
  12. data/README.adoc +3115 -365
  13. data/RELEASE_NOTES.adoc +346 -0
  14. data/docs/custom_adapters.adoc +144 -0
  15. data/docs/custom_registers.adoc +228 -0
  16. data/docs/schema_generation.adoc +898 -0
  17. data/docs/schema_import.adoc +364 -0
  18. data/flake.lock +114 -0
  19. data/flake.nix +103 -0
  20. data/lib/lutaml/model/attribute.rb +242 -100
  21. data/lib/lutaml/model/choice.rb +30 -0
  22. data/lib/lutaml/model/collection.rb +212 -0
  23. data/lib/lutaml/model/comparable_model.rb +3 -3
  24. data/lib/lutaml/model/config.rb +71 -42
  25. data/lib/lutaml/model/constants.rb +2 -0
  26. data/lib/lutaml/model/error/element_count_out_of_range_error.rb +29 -0
  27. data/lib/lutaml/model/error/invalid_attribute_name_error.rb +15 -0
  28. data/lib/lutaml/model/error/invalid_attribute_options_error.rb +16 -0
  29. data/lib/lutaml/model/error/invalid_choice_range_error.rb +3 -5
  30. data/lib/lutaml/model/error/polymorphic_error.rb +7 -2
  31. data/lib/lutaml/model/error/register/not_registrable_class_error.rb +11 -0
  32. data/lib/lutaml/model/error/type/invalid_value_error.rb +5 -3
  33. data/lib/lutaml/model/error/type/max_bound_error.rb +20 -0
  34. data/lib/lutaml/model/error/type/max_length_error.rb +20 -0
  35. data/lib/lutaml/model/error/type/min_bound_error.rb +20 -0
  36. data/lib/lutaml/model/error/type/min_length_error.rb +20 -0
  37. data/lib/lutaml/model/error/type/pattern_not_matched_error.rb +18 -0
  38. data/lib/lutaml/model/error/validation_failed_error.rb +9 -0
  39. data/lib/lutaml/model/error.rb +10 -0
  40. data/lib/lutaml/model/errors.rb +36 -0
  41. data/lib/lutaml/model/format_registry.rb +44 -0
  42. data/lib/lutaml/model/global_register.rb +41 -0
  43. data/lib/lutaml/model/hash_adapter/document.rb +11 -0
  44. data/lib/lutaml/model/hash_adapter/mapping.rb +19 -0
  45. data/lib/lutaml/model/hash_adapter/mapping_rule.rb +9 -0
  46. data/lib/lutaml/model/hash_adapter/standard_adapter.rb +17 -0
  47. data/lib/lutaml/model/hash_adapter/transform.rb +8 -0
  48. data/lib/lutaml/model/hash_adapter.rb +21 -0
  49. data/lib/lutaml/model/json/document.rb +11 -0
  50. data/lib/lutaml/model/json/mapping.rb +19 -0
  51. data/lib/lutaml/model/json/mapping_rule.rb +9 -0
  52. data/lib/lutaml/model/{json_adapter → json}/multi_json_adapter.rb +4 -5
  53. data/lib/lutaml/model/{json_adapter/standard_json_adapter.rb → json/standard_adapter.rb} +5 -3
  54. data/lib/lutaml/model/json/transform.rb +8 -0
  55. data/lib/lutaml/model/json.rb +21 -0
  56. data/lib/lutaml/model/jsonl/document.rb +14 -0
  57. data/lib/lutaml/model/jsonl/mapping.rb +19 -0
  58. data/lib/lutaml/model/jsonl/mapping_rule.rb +9 -0
  59. data/lib/lutaml/model/jsonl/standard_adapter.rb +33 -0
  60. data/lib/lutaml/model/jsonl/transform.rb +19 -0
  61. data/lib/lutaml/model/jsonl.rb +21 -0
  62. data/lib/lutaml/model/key_value_document.rb +28 -0
  63. data/lib/lutaml/model/mapping/key_value_mapping.rb +85 -13
  64. data/lib/lutaml/model/mapping/key_value_mapping_rule.rb +5 -0
  65. data/lib/lutaml/model/mapping/mapping.rb +13 -0
  66. data/lib/lutaml/model/mapping/mapping_rule.rb +15 -9
  67. data/lib/lutaml/model/register.rb +105 -0
  68. data/lib/lutaml/model/{toml_adapter.rb → registrable.rb} +1 -1
  69. data/lib/lutaml/model/schema/base_schema.rb +64 -0
  70. data/lib/lutaml/model/schema/decorators/attribute.rb +114 -0
  71. data/lib/lutaml/model/schema/decorators/choices.rb +31 -0
  72. data/lib/lutaml/model/schema/decorators/class_definition.rb +85 -0
  73. data/lib/lutaml/model/schema/decorators/definition_collection.rb +97 -0
  74. data/lib/lutaml/model/schema/generator/definition.rb +53 -0
  75. data/lib/lutaml/model/schema/generator/definitions_collection.rb +81 -0
  76. data/lib/lutaml/model/schema/generator/properties_collection.rb +63 -0
  77. data/lib/lutaml/model/schema/generator/property.rb +110 -0
  78. data/lib/lutaml/model/schema/generator/ref.rb +24 -0
  79. data/lib/lutaml/model/schema/helpers/template_helper.rb +49 -0
  80. data/lib/lutaml/model/schema/json_schema.rb +42 -49
  81. data/lib/lutaml/model/schema/relaxng_schema.rb +14 -10
  82. data/lib/lutaml/model/schema/renderer.rb +36 -0
  83. data/lib/lutaml/model/schema/shared_methods.rb +24 -0
  84. data/lib/lutaml/model/schema/templates/model.erb +9 -0
  85. data/lib/lutaml/model/schema/xml_compiler/attribute.rb +85 -0
  86. data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +45 -0
  87. data/lib/lutaml/model/schema/xml_compiler/choice.rb +65 -0
  88. data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +27 -0
  89. data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +34 -0
  90. data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +136 -0
  91. data/lib/lutaml/model/schema/xml_compiler/element.rb +104 -0
  92. data/lib/lutaml/model/schema/xml_compiler/group.rb +97 -0
  93. data/lib/lutaml/model/schema/xml_compiler/restriction.rb +101 -0
  94. data/lib/lutaml/model/schema/xml_compiler/sequence.rb +50 -0
  95. data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +36 -0
  96. data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +189 -0
  97. data/lib/lutaml/model/schema/xml_compiler.rb +231 -587
  98. data/lib/lutaml/model/schema/xsd_schema.rb +12 -8
  99. data/lib/lutaml/model/schema/yaml_schema.rb +41 -35
  100. data/lib/lutaml/model/schema.rb +1 -0
  101. data/lib/lutaml/model/sequence.rb +60 -30
  102. data/lib/lutaml/model/serialization_adapter.rb +22 -0
  103. data/lib/lutaml/model/serialize.rb +296 -548
  104. data/lib/lutaml/model/services/base.rb +11 -0
  105. data/lib/lutaml/model/services/logger.rb +54 -0
  106. data/lib/lutaml/model/services/rule_value_extractor.rb +92 -0
  107. data/lib/lutaml/model/services/transformer.rb +48 -0
  108. data/lib/lutaml/model/services/type/validator/number.rb +25 -0
  109. data/lib/lutaml/model/services/type/validator/string.rb +52 -0
  110. data/lib/lutaml/model/services/type/validator.rb +43 -0
  111. data/lib/lutaml/model/services/validator.rb +145 -0
  112. data/lib/lutaml/model/services.rb +5 -0
  113. data/lib/lutaml/model/toml/document.rb +11 -0
  114. data/lib/lutaml/model/toml/mapping.rb +27 -0
  115. data/lib/lutaml/model/toml/mapping_rule.rb +9 -0
  116. data/lib/lutaml/model/{toml_adapter → toml}/toml_rb_adapter.rb +3 -3
  117. data/lib/lutaml/model/toml/tomlib_adapter.rb +19 -0
  118. data/lib/lutaml/model/toml/transform.rb +8 -0
  119. data/lib/lutaml/model/toml.rb +30 -0
  120. data/lib/lutaml/model/transform/key_value_transform.rb +297 -0
  121. data/lib/lutaml/model/transform/xml_transform.rb +228 -0
  122. data/lib/lutaml/model/transform.rb +93 -0
  123. data/lib/lutaml/model/type/boolean.rb +1 -1
  124. data/lib/lutaml/model/type/date.rb +1 -1
  125. data/lib/lutaml/model/type/date_time.rb +1 -1
  126. data/lib/lutaml/model/type/decimal.rb +11 -9
  127. data/lib/lutaml/model/type/float.rb +2 -1
  128. data/lib/lutaml/model/type/integer.rb +24 -21
  129. data/lib/lutaml/model/type/string.rb +4 -2
  130. data/lib/lutaml/model/type/time.rb +1 -1
  131. data/lib/lutaml/model/type/time_without_date.rb +1 -1
  132. data/lib/lutaml/model/type/value.rb +11 -10
  133. data/lib/lutaml/model/type.rb +5 -2
  134. data/lib/lutaml/model/uninitialized_class.rb +1 -1
  135. data/lib/lutaml/model/utils.rb +56 -4
  136. data/lib/lutaml/model/validation.rb +6 -4
  137. data/lib/lutaml/model/version.rb +1 -1
  138. data/lib/lutaml/model/{xml_adapter → xml}/builder/nokogiri.rb +2 -2
  139. data/lib/lutaml/model/{xml_adapter → xml}/builder/oga.rb +10 -10
  140. data/lib/lutaml/model/{xml_adapter → xml}/builder/ox.rb +1 -1
  141. data/lib/lutaml/model/{xml_adapter/xml_document.rb → xml/document.rb} +42 -25
  142. data/lib/lutaml/model/xml/element.rb +32 -0
  143. data/lib/lutaml/model/xml/mapping.rb +471 -0
  144. data/lib/lutaml/model/xml/mapping_rule.rb +149 -0
  145. data/lib/lutaml/model/xml/nokogiri_adapter.rb +234 -0
  146. data/lib/lutaml/model/{xml_adapter → xml}/oga/document.rb +1 -1
  147. data/lib/lutaml/model/{xml_adapter → xml}/oga/element.rb +7 -2
  148. data/lib/lutaml/model/xml/oga_adapter.rb +172 -0
  149. data/lib/lutaml/model/xml/ox_adapter.rb +229 -0
  150. data/lib/lutaml/model/xml/transform.rb +8 -0
  151. data/lib/lutaml/model/{xml_adapter → xml}/xml_attribute.rb +1 -1
  152. data/lib/lutaml/model/{xml_adapter → xml}/xml_element.rb +9 -31
  153. data/lib/lutaml/model/{xml_adapter → xml}/xml_namespace.rb +1 -1
  154. data/lib/lutaml/model/xml.rb +31 -0
  155. data/lib/lutaml/model/xml_adapter/element.rb +11 -25
  156. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +6 -223
  157. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +13 -163
  158. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +10 -207
  159. data/lib/lutaml/model/yaml/document.rb +10 -0
  160. data/lib/lutaml/model/yaml/mapping.rb +19 -0
  161. data/lib/lutaml/model/yaml/mapping_rule.rb +9 -0
  162. data/lib/lutaml/model/{yaml_adapter/standard_yaml_adapter.rb → yaml/standard_adapter.rb} +4 -3
  163. data/lib/lutaml/model/yaml/transform.rb +8 -0
  164. data/lib/lutaml/model/yaml.rb +21 -0
  165. data/lib/lutaml/model/yamls/document.rb +14 -0
  166. data/lib/lutaml/model/yamls/mapping.rb +19 -0
  167. data/lib/lutaml/model/yamls/mapping_rule.rb +9 -0
  168. data/lib/lutaml/model/yamls/standard_adapter.rb +34 -0
  169. data/lib/lutaml/model/yamls/transform.rb +19 -0
  170. data/lib/lutaml/model/yamls.rb +21 -0
  171. data/lib/lutaml/model.rb +15 -4
  172. data/lutaml-model.gemspec +0 -4
  173. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +5 -6
  174. data/spec/fixtures/xml/advanced_test_schema.xsd +134 -0
  175. data/spec/fixtures/xml/examples/nested_categories.xml +55 -0
  176. data/spec/fixtures/xml/examples/valid_catalog.xml +43 -0
  177. data/spec/fixtures/xml/product_catalog.xsd +151 -0
  178. data/spec/fixtures/xml/specifications_schema.xsd +38 -0
  179. data/spec/lutaml/model/attribute_collection_spec.rb +101 -0
  180. data/spec/lutaml/model/attribute_spec.rb +41 -44
  181. data/spec/lutaml/model/cdata_spec.rb +7 -7
  182. data/spec/lutaml/model/choice_spec.rb +44 -0
  183. data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +598 -0
  184. data/spec/lutaml/model/custom_collection_spec.rb +830 -0
  185. data/spec/lutaml/model/custom_vobject_adapter_spec.rb +1226 -0
  186. data/spec/lutaml/model/global_register_spec.rb +108 -0
  187. data/spec/lutaml/model/group_spec.rb +27 -10
  188. data/spec/lutaml/model/hash/adapter_spec.rb +255 -0
  189. data/spec/lutaml/model/json_adapter_spec.rb +6 -6
  190. data/spec/lutaml/model/jsonl/standard_adapter_spec.rb +91 -0
  191. data/spec/lutaml/model/jsonl_spec.rb +229 -0
  192. data/spec/lutaml/model/key_value_mapping_spec.rb +25 -1
  193. data/spec/lutaml/model/mixed_content_spec.rb +24 -24
  194. data/spec/lutaml/model/multiple_mapping_spec.rb +6 -6
  195. data/spec/lutaml/model/ordered_content_spec.rb +6 -6
  196. data/spec/lutaml/model/polymorphic_spec.rb +178 -0
  197. data/spec/lutaml/model/register/key_value_spec.rb +275 -0
  198. data/spec/lutaml/model/register/xml_spec.rb +185 -0
  199. data/spec/lutaml/model/register_spec.rb +147 -0
  200. data/spec/lutaml/model/root_mappings_spec.rb +3 -3
  201. data/spec/lutaml/model/rule_value_extractor_spec.rb +162 -0
  202. data/spec/lutaml/model/schema/generator/definitions_collection_spec.rb +120 -0
  203. data/spec/lutaml/model/schema/json_schema_spec.rb +412 -51
  204. data/spec/lutaml/model/schema/json_schema_to_models_spec.rb +383 -0
  205. data/spec/lutaml/model/schema/xml_compiler/attribute_group_spec.rb +65 -0
  206. data/spec/lutaml/model/schema/xml_compiler/attribute_spec.rb +63 -0
  207. data/spec/lutaml/model/schema/xml_compiler/choice_spec.rb +71 -0
  208. data/spec/lutaml/model/schema/xml_compiler/complex_content_restriction_spec.rb +55 -0
  209. data/spec/lutaml/model/schema/xml_compiler/complex_content_spec.rb +37 -0
  210. data/spec/lutaml/model/schema/xml_compiler/complex_type_spec.rb +173 -0
  211. data/spec/lutaml/model/schema/xml_compiler/element_spec.rb +63 -0
  212. data/spec/lutaml/model/schema/xml_compiler/group_spec.rb +86 -0
  213. data/spec/lutaml/model/schema/xml_compiler/restriction_spec.rb +76 -0
  214. data/spec/lutaml/model/schema/xml_compiler/sequence_spec.rb +59 -0
  215. data/spec/lutaml/model/schema/xml_compiler/simple_content_spec.rb +55 -0
  216. data/spec/lutaml/model/schema/xml_compiler/simple_type_spec.rb +181 -0
  217. data/spec/lutaml/model/schema/xml_compiler_spec.rb +503 -1804
  218. data/spec/lutaml/model/schema/yaml_schema_spec.rb +249 -26
  219. data/spec/lutaml/model/sequence_spec.rb +36 -0
  220. data/spec/lutaml/model/serializable_spec.rb +201 -94
  221. data/spec/lutaml/model/toml_adapter_spec.rb +6 -6
  222. data/spec/lutaml/model/toml_spec.rb +51 -0
  223. data/spec/lutaml/model/transformation_spec.rb +72 -15
  224. data/spec/lutaml/model/type_spec.rb +8 -4
  225. data/spec/lutaml/model/uninitialized_class_spec.rb +96 -0
  226. data/spec/lutaml/model/utils_spec.rb +3 -3
  227. data/spec/lutaml/model/xml/derived_attributes_spec.rb +1 -1
  228. data/spec/lutaml/model/xml/namespace_spec.rb +57 -0
  229. data/spec/lutaml/model/xml/root_mappings/nested_child_mappings_spec.rb +217 -0
  230. data/spec/lutaml/model/xml/xml_element_spec.rb +7 -1
  231. data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +2 -2
  232. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +2 -2
  233. data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +2 -2
  234. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +12 -12
  235. data/spec/lutaml/model/xml_adapter_spec.rb +30 -6
  236. data/spec/lutaml/model/xml_mapping_rule_spec.rb +14 -7
  237. data/spec/lutaml/model/xml_mapping_spec.rb +27 -15
  238. data/spec/lutaml/model/xml_spec.rb +63 -0
  239. data/spec/lutaml/model/yaml_adapter_spec.rb +3 -5
  240. data/spec/lutaml/model/yamls/standard_adapter_spec.rb +183 -0
  241. data/spec/lutaml/model/yamls_spec.rb +294 -0
  242. data/spec/spec_helper.rb +4 -3
  243. metadata +162 -60
  244. data/lib/lutaml/model/json_adapter/json_document.rb +0 -20
  245. data/lib/lutaml/model/json_adapter/json_object.rb +0 -28
  246. data/lib/lutaml/model/loggable.rb +0 -15
  247. data/lib/lutaml/model/mapping/json_mapping.rb +0 -17
  248. data/lib/lutaml/model/mapping/toml_mapping.rb +0 -25
  249. data/lib/lutaml/model/mapping/xml_mapping.rb +0 -389
  250. data/lib/lutaml/model/mapping/xml_mapping_rule.rb +0 -139
  251. data/lib/lutaml/model/mapping/yaml_mapping.rb +0 -17
  252. data/lib/lutaml/model/mapping.rb +0 -14
  253. data/lib/lutaml/model/schema/templates/simple_type.rb +0 -247
  254. data/lib/lutaml/model/toml_adapter/toml_document.rb +0 -20
  255. data/lib/lutaml/model/toml_adapter/toml_object.rb +0 -28
  256. data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +0 -20
  257. data/lib/lutaml/model/yaml_adapter/yaml_document.rb +0 -20
  258. data/lib/lutaml/model/yaml_adapter/yaml_object.rb +0 -28
  259. data/lib/lutaml/model/yaml_adapter.rb +0 -8
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe Lutaml::Model::GlobalRegister do
6
+ describe "#initialize" do
7
+ it "initializes with a default register" do
8
+ expect(described_class.instance.instance_variable_get(:@registers)).to include(default: described_class.lookup(:default))
9
+ end
10
+ end
11
+
12
+ describe "#register" do
13
+ it "adds register to the internal hash" do
14
+ register = Lutaml::Model::Register.new(:test_register)
15
+ described_class.register(register)
16
+
17
+ registers = described_class.instance.instance_variable_get(:@registers)
18
+ expect(registers[:test_register]).to eq(register)
19
+ end
20
+ end
21
+
22
+ describe ".register" do
23
+ it "calls instance method to register" do
24
+ register = Lutaml::Model::Register.new(:temp_register)
25
+ expect { described_class.register(register) }.to(
26
+ change { described_class.instance.instance_variable_get(:@registers).count }.by(1),
27
+ )
28
+ end
29
+ end
30
+
31
+ describe "#lookup" do
32
+ let(:register_v_one) { Lutaml::Model::Register.new(:v1) }
33
+ let(:register_v_two) { Lutaml::Model::Register.new(:v2) }
34
+
35
+ before do
36
+ described_class.instance.register(register_v_one)
37
+ described_class.instance.register(register_v_two)
38
+ end
39
+
40
+ it "returns the correct register for a given id" do
41
+ expect(described_class.instance.lookup(:v1)).to eq(register_v_one)
42
+ expect(described_class.instance.lookup(:v2)).to eq(register_v_two)
43
+ end
44
+
45
+ it "returns nil for non-existent id" do
46
+ expect(described_class.instance.lookup(:non_existent)).to be_nil
47
+ end
48
+
49
+ it "converts string id to symbol" do
50
+ expect(described_class.instance.lookup("v1")).to eq(register_v_one)
51
+ end
52
+ end
53
+
54
+ describe ".lookup" do
55
+ let(:register_v_one) { Lutaml::Model::Register.new(:v1) }
56
+
57
+ it "calls instance method to lookup" do
58
+ allow(described_class.instance).to receive(:lookup).with(:v1).and_return(register_v_one)
59
+
60
+ expect(described_class.lookup(:v1)).to eq(register_v_one)
61
+ end
62
+ end
63
+
64
+ describe "singleton behavior" do
65
+ it "returns the same instance on multiple calls" do
66
+ instance1 = described_class.instance
67
+ instance2 = described_class.instance
68
+
69
+ expect(instance1).to be(instance2)
70
+ end
71
+
72
+ it "shares state between method calls" do
73
+ register = Lutaml::Model::Register.new(:shared_test)
74
+ described_class.register(register)
75
+ expect(described_class.lookup(:shared_test)).to eq(register)
76
+ end
77
+ end
78
+
79
+ describe "#remove" do
80
+ let(:register_v_one) { Lutaml::Model::Register.new(:v1) }
81
+ let(:register_v_two) { Lutaml::Model::Register.new(:v2) }
82
+
83
+ before do
84
+ described_class.register(register_v_one)
85
+ described_class.register(register_v_two)
86
+ end
87
+
88
+ it "removes the specified register" do
89
+ register = described_class.instance
90
+ expect(register.instance_variable_get(:@registers).values).to include(register_v_one)
91
+ expect { described_class.remove(:v1) }.to(
92
+ change { register.instance_variable_get(:@registers).values.count }.by(-1),
93
+ )
94
+ expect(register.instance_variable_get(:@registers).values).not_to include(register_v_one)
95
+ end
96
+
97
+ it "does not remove other registers" do
98
+ described_class.remove(:v1)
99
+ registers = described_class.instance.instance_variable_get(:@registers)
100
+ expect(registers.values).to include(register_v_two)
101
+ end
102
+
103
+ it "does nothing when the specified register does not exist" do
104
+ registers = described_class.instance.instance_variable_get(:@registers)
105
+ expect { described_class.remove(:non_existent) }.not_to(change { registers.values.count })
106
+ end
107
+ end
108
+ end
@@ -60,12 +60,12 @@ module GroupSpec
60
60
  map_attribute "tag", to: :tag
61
61
  map_content to: :content
62
62
  map_element :group, to: :group
63
- import_model_mappings GroupOfItems
63
+ import_model_mappings :group_of_items
64
64
  end
65
65
  end
66
66
 
67
67
  class SimpleType < Lutaml::Model::Serializable
68
- import_model GroupOfItems
68
+ import_model :group_of_items
69
69
  end
70
70
 
71
71
  class GenericType < Lutaml::Model::Serializable
@@ -228,13 +228,24 @@ RSpec.describe "Group" do
228
228
  )
229
229
  end
230
230
 
231
- it "raises error if root_less class used for deserializing" do
232
- ceramic = GroupSpec::Ceramic.new(type: "Data", name: "Starc")
231
+ context "deserializing XML" do
232
+ let(:ceramic) { GroupSpec::Ceramic.new(type: "Data", name: "Starc") }
233
233
 
234
- expect { ceramic.to_xml }.to raise_error(
235
- Lutaml::Model::NoRootMappingError,
236
- "GroupSpec::Ceramic has `no_root`, it allowed only for reusable models",
237
- )
234
+ it "raises error for root_less class" do
235
+ expect { ceramic.to_xml }.to raise_error(
236
+ Lutaml::Model::NoRootMappingError,
237
+ "GroupSpec::Ceramic has `no_root`, it allowed only for reusable models",
238
+ )
239
+ end
240
+ end
241
+
242
+ context "deserializing key-value formats" do
243
+ let(:ceramic) { GroupSpec::Ceramic.new(type: "Data", name: "Starc") }
244
+
245
+ it "does not raises error for root_less class" do
246
+ expect { ceramic.to_yaml }
247
+ .not_to raise_error(Lutaml::Model::NoRootMappingError)
248
+ end
238
249
  end
239
250
 
240
251
  it "correctly get the element of root-less class" do
@@ -251,6 +262,12 @@ RSpec.describe "Group" do
251
262
  end
252
263
 
253
264
  context "with model" do
265
+ before do
266
+ Lutaml::Model::GlobalRegister
267
+ .lookup(Lutaml::Model::Config.default_register)
268
+ .register_model(GroupSpec::GroupOfItems, id: :group_of_items)
269
+ end
270
+
254
271
  shared_examples "imports attributes from" do |source_class, target_class|
255
272
  it "#{source_class.name} correctly" do
256
273
  source_attributes = source_class.attributes
@@ -328,7 +345,7 @@ RSpec.describe "Group" do
328
345
  context "when update the imported attribute" do
329
346
  it "updates the attribute `mstyle` only in `Mrow`" do
330
347
  GroupSpec::Mrow.attributes[:mstyle].instance_variable_set(:@type, :integer)
331
- expect(GroupSpec::Mrow.attributes[:mstyle].type).to eq(:integer)
348
+ expect(GroupSpec::Mrow.attributes[:mstyle].type).to eq(Lutaml::Model::Type::Integer)
332
349
  end
333
350
 
334
351
  it "maintains original type for the attribute `mstyle` in `Mfrac`" do
@@ -412,7 +429,7 @@ RSpec.describe "Group" do
412
429
  context "with sequence elements" do
413
430
  it "updates sequence elements only in `Mrow`" do
414
431
  sequence = GroupSpec::Mrow.mappings_for(:xml).element_sequence.first
415
- sequence.attributes << Lutaml::Model::XmlMappingRule.new(
432
+ sequence.attributes << Lutaml::Model::Xml::MappingRule.new(
416
433
  "new_element",
417
434
  to: :new_element,
418
435
  namespace: "http://example.com",
@@ -0,0 +1,255 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "HashAdapter" do
4
+ before do
5
+ stub_const("HashMappingSpec", Module.new)
6
+
7
+ simple_class = Class.new(Lutaml::Model::Serializable) do
8
+ attribute :name, :string
9
+ attribute :age, :integer
10
+ attribute :active, :boolean
11
+
12
+ hsh do
13
+ map "name", to: :name
14
+ map "age", to: :age
15
+ map "is_active", to: :active
16
+ end
17
+ end
18
+
19
+ stub_const("HashMappingSpec::SimpleClass", simple_class)
20
+
21
+ nested_class = Class.new(Lutaml::Model::Serializable) do
22
+ attribute :title, :string
23
+ attribute :simple, HashMappingSpec::SimpleClass
24
+ attribute :items, :string, collection: true
25
+
26
+ hsh do
27
+ map "title", to: :title
28
+ map "simple", to: :simple
29
+ map "items", to: :items
30
+ end
31
+ end
32
+
33
+ stub_const("HashMappingSpec::NestedClass", nested_class)
34
+
35
+ collection_class = Class.new(Lutaml::Model::Serializable) do
36
+ attribute :name, :string
37
+ attribute :simples, HashMappingSpec::SimpleClass, collection: true
38
+
39
+ hsh do
40
+ map "name", to: :name
41
+ map "simples", to: :simples
42
+ end
43
+ end
44
+
45
+ stub_const("HashMappingSpec::CollectionClass", collection_class)
46
+ end
47
+
48
+ describe "from_hash" do
49
+ context "with simple attributes" do
50
+ let(:parsed) do
51
+ HashMappingSpec::SimpleClass.from_hash(hash_data)
52
+ end
53
+
54
+ let(:hash_data) do
55
+ {
56
+ "name" => "John Doe",
57
+ "age" => 30,
58
+ "is_active" => true,
59
+ }
60
+ end
61
+
62
+ let(:expected) do
63
+ HashMappingSpec::SimpleClass.new(
64
+ name: "John Doe",
65
+ age: 30,
66
+ active: true,
67
+ )
68
+ end
69
+
70
+ it "deserializes hash to object" do
71
+ expect(parsed).to eq(expected)
72
+ end
73
+ end
74
+
75
+ context "with nested objects" do
76
+ let(:parsed) do
77
+ HashMappingSpec::NestedClass.from_hash(hash_data)
78
+ end
79
+
80
+ let(:hash_data) do
81
+ {
82
+ "title" => "Test Title",
83
+ "simple" => {
84
+ "name" => "Jane Doe",
85
+ "age" => 25,
86
+ "is_active" => false,
87
+ },
88
+ "items" => ["item1", "item2", "item3"],
89
+ }
90
+ end
91
+
92
+ let(:expected) do
93
+ HashMappingSpec::NestedClass.new(
94
+ title: "Test Title",
95
+ simple: HashMappingSpec::SimpleClass.new(
96
+ name: "Jane Doe",
97
+ age: 25,
98
+ active: false,
99
+ ),
100
+ items: ["item1", "item2", "item3"],
101
+ )
102
+ end
103
+
104
+ it "deserializes nested objects" do
105
+ expect(parsed).to eq(expected)
106
+ end
107
+ end
108
+
109
+ context "with collections of objects" do
110
+ let(:parsed) do
111
+ HashMappingSpec::CollectionClass.from_hash(hash_data)
112
+ end
113
+
114
+ let(:hash_data) do
115
+ {
116
+ "name" => "Collection",
117
+ "simples" => [
118
+ { "name" => "First", "age" => 10, "is_active" => true },
119
+ { "name" => "Second", "age" => 20, "is_active" => false },
120
+ ],
121
+ }
122
+ end
123
+
124
+ let(:expected) do
125
+ HashMappingSpec::CollectionClass.new(
126
+ name: "Collection",
127
+ simples: [
128
+ HashMappingSpec::SimpleClass.new(
129
+ name: "First",
130
+ age: 10,
131
+ active: true,
132
+ ),
133
+ HashMappingSpec::SimpleClass.new(
134
+ name: "Second",
135
+ age: 20,
136
+ active: false,
137
+ ),
138
+ ],
139
+ )
140
+ end
141
+
142
+ it "deserializes collections of objects" do
143
+ expect(parsed).to eq(expected)
144
+ end
145
+ end
146
+ end
147
+
148
+ describe "to_hash" do
149
+ context "with simple attributes" do
150
+ let(:instance) do
151
+ HashMappingSpec::SimpleClass.new(
152
+ name: "John Doe",
153
+ age: 30,
154
+ active: true,
155
+ )
156
+ end
157
+
158
+ let(:expected_hash) do
159
+ {
160
+ "name" => "John Doe",
161
+ "age" => 30,
162
+ "is_active" => true,
163
+ }
164
+ end
165
+
166
+ it "serializes object to hash" do
167
+ expect(instance.to_hash).to eq(expected_hash)
168
+ end
169
+ end
170
+
171
+ context "with nested objects" do
172
+ let(:simple) do
173
+ HashMappingSpec::SimpleClass.new(
174
+ name: "Jane Doe",
175
+ age: 25,
176
+ active: false,
177
+ )
178
+ end
179
+
180
+ let(:instance) do
181
+ HashMappingSpec::NestedClass.new(
182
+ title: "Test Title",
183
+ simple: simple,
184
+ items: ["item1", "item2", "item3"],
185
+ )
186
+ end
187
+
188
+ let(:expected_hash) do
189
+ {
190
+ "title" => "Test Title",
191
+ "simple" => {
192
+ "name" => "Jane Doe",
193
+ "age" => 25,
194
+ "is_active" => false,
195
+ },
196
+ "items" => ["item1", "item2", "item3"],
197
+ }
198
+ end
199
+
200
+ it "serializes nested objects to hash" do
201
+ expect(instance.to_hash).to eq(expected_hash)
202
+ end
203
+ end
204
+
205
+ context "with collections of objects" do
206
+ let(:simples) do
207
+ [
208
+ HashMappingSpec::SimpleClass.new(name: "First", age: 10, active: true),
209
+ HashMappingSpec::SimpleClass.new(name: "Second", age: 20, active: false),
210
+ ]
211
+ end
212
+
213
+ let(:instance) do
214
+ HashMappingSpec::CollectionClass.new(
215
+ name: "Collection",
216
+ simples: simples,
217
+ )
218
+ end
219
+
220
+ let(:expected_hash) do
221
+ {
222
+ "name" => "Collection",
223
+ "simples" => [
224
+ { "name" => "First", "age" => 10, "is_active" => true },
225
+ { "name" => "Second", "age" => 20, "is_active" => false },
226
+ ],
227
+ }
228
+ end
229
+
230
+ it "serializes collections of objects to hash" do
231
+ expect(instance.to_hash).to eq(expected_hash)
232
+ end
233
+ end
234
+ end
235
+
236
+ describe "round-trip serialization" do
237
+ let(:original_hash) do
238
+ {
239
+ "name" => "Collection",
240
+ "simples" => [
241
+ { "name" => "First", "age" => 10, "is_active" => true },
242
+ { "name" => "Second", "age" => 20, "is_active" => false },
243
+ ],
244
+ }
245
+ end
246
+
247
+ let(:parsed) do
248
+ HashMappingSpec::CollectionClass.from_hash(original_hash)
249
+ end
250
+
251
+ it "maintains data integrity through serialization and deserialization" do
252
+ expect(parsed.to_hash).to eq(original_hash)
253
+ end
254
+ end
255
+ end
@@ -1,6 +1,6 @@
1
1
  require "spec_helper"
2
- require "lutaml/model/json_adapter/standard_json_adapter"
3
- require "lutaml/model/json_adapter/multi_json_adapter"
2
+ require "lutaml/model/json/standard_adapter"
3
+ require "lutaml/model/json/multi_json_adapter"
4
4
  require_relative "../../fixtures/sample_model"
5
5
 
6
6
  RSpec.describe "JsonAdapter" do
@@ -9,9 +9,9 @@ RSpec.describe "JsonAdapter" do
9
9
  let(:model) { SampleModel.new(attributes) }
10
10
 
11
11
  let(:expected_json) do
12
- if adapter_class == Lutaml::Model::JsonAdapter::StandardJsonAdapter
12
+ if adapter_class == Lutaml::Model::Json::StandardAdapter
13
13
  JSON.generate(attributes)
14
- elsif adapter_class == Lutaml::Model::JsonAdapter::MultiJsonAdapter
14
+ elsif adapter_class == Lutaml::Model::Json::MultiJsonAdapter
15
15
  MultiJson.dump(attributes)
16
16
  end
17
17
  end
@@ -29,11 +29,11 @@ RSpec.describe "JsonAdapter" do
29
29
  end
30
30
  end
31
31
 
32
- describe Lutaml::Model::JsonAdapter::StandardJsonAdapter do
32
+ describe Lutaml::Model::Json::StandardAdapter do
33
33
  it_behaves_like "a JSON adapter", described_class
34
34
  end
35
35
 
36
- describe Lutaml::Model::JsonAdapter::MultiJsonAdapter do
36
+ describe Lutaml::Model::Json::MultiJsonAdapter do
37
37
  it_behaves_like "a JSON adapter", described_class
38
38
  end
39
39
  end
@@ -0,0 +1,91 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Lutaml::Model::Jsonl::StandardAdapter do
4
+ let(:valid_jsonl_content) do
5
+ <<~JSONL
6
+ {"name": "John", "age": 30}
7
+ {"name": "Jane", "age": 25}
8
+ {"name": "Bob", "age": 35}
9
+ JSONL
10
+ end
11
+
12
+ let(:invalid_jsonl_content) do
13
+ <<~JSONL
14
+ {"name": "John", "age": 30}
15
+ invalid json
16
+ {"name": "Bob", "age": 35}
17
+ JSONL
18
+ end
19
+
20
+ describe ".parse" do
21
+ context "with valid JSONL content" do
22
+ it "parses each line as a JSON object" do
23
+ results = described_class.parse(valid_jsonl_content)
24
+ expect(results).to be_an(Array)
25
+ expect(results.length).to eq(3)
26
+ expect(results.first).to eq({ "name" => "John", "age" => 30 })
27
+ expect(results.last).to eq({ "name" => "Bob", "age" => 35 })
28
+ end
29
+ end
30
+
31
+ context "with invalid JSONL content" do
32
+ it "skips invalid lines and continues parsing" do
33
+ results = described_class.parse(invalid_jsonl_content)
34
+ expect(results).to be_an(Array)
35
+ expect(results.length).to eq(2)
36
+ expect(results.first).to eq({ "name" => "John", "age" => 30 })
37
+ expect(results.last).to eq({ "name" => "Bob", "age" => 35 })
38
+ end
39
+ end
40
+
41
+ context "with empty lines" do
42
+ let(:jsonl_with_empty_lines) do
43
+ <<~JSONL
44
+ {"name": "John"}
45
+
46
+ {"name": "Jane"}
47
+ JSONL
48
+ end
49
+
50
+ it "skips empty lines" do
51
+ results = described_class.parse(jsonl_with_empty_lines)
52
+ expect(results).to be_an(Array)
53
+ expect(results.length).to eq(2)
54
+ end
55
+ end
56
+ end
57
+
58
+ describe "#to_jsonl" do
59
+ let(:adapter) { described_class.new([{ "name" => "John", "age" => 30 }]) }
60
+
61
+ it "generates JSONL format" do
62
+ expect(adapter.to_jsonl).to eq('{"name":"John","age":30}')
63
+ end
64
+
65
+ context "with multiple objects" do
66
+ let(:adapter) do
67
+ described_class.new([
68
+ { "name" => "John", "age" => 30 },
69
+ { "name" => "Jane", "age" => 25 },
70
+ ])
71
+ end
72
+
73
+ let(:expected_jsonl) do
74
+ <<~JSONL.strip
75
+ {"name":"John","age":30}
76
+ {"name":"Jane","age":25}
77
+ JSONL
78
+ end
79
+
80
+ it "generates multiple lines" do
81
+ expect(adapter.to_jsonl).to eq(expected_jsonl)
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "FORMAT_SYMBOL" do
87
+ it "returns :jsonl" do
88
+ expect(described_class::FORMAT_SYMBOL).to eq(:jsonl)
89
+ end
90
+ end
91
+ end