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,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ module RegisterSpec
5
+ class CustomString < Lutaml::Model::Type::String; end
6
+ class CustomInteger < Lutaml::Model::Type::Integer; end
7
+
8
+ Lutaml::Model::Type.register(:custom_string, CustomString)
9
+
10
+ class Address < Lutaml::Model::Serializable
11
+ attribute :location, :string
12
+ attribute :postal_code, :custom_string
13
+ end
14
+ end
15
+
16
+ RSpec.describe Lutaml::Model::Register do
17
+ describe "#initialize" do
18
+ it "initializes with id" do
19
+ register = described_class.new(:v1)
20
+ expect(register.id).to eq(:v1)
21
+ expect(register.models).to eq({})
22
+ end
23
+ end
24
+
25
+ describe "#register_model" do
26
+ let(:v1_register) { described_class.new(:v1) }
27
+
28
+ before do
29
+ v1_register.register_model(RegisterSpec::CustomString, id: :custom_string)
30
+ v1_register.register_model(RegisterSpec::CustomInteger, id: :custom_integer)
31
+ end
32
+
33
+ it "registers model with explicit id" do
34
+ expect(v1_register.models[:custom_string]).to be_nil
35
+ end
36
+
37
+ it "allows overriding an existing type" do
38
+ v1_register.register_model(Lutaml::Model::Type::String, id: :custom_string)
39
+ expect(v1_register.models[:custom_string]).to be_nil
40
+ end
41
+
42
+ it "registers serializable class" do
43
+ v1_register.register_model(RegisterSpec::Address, id: :address)
44
+ expect(v1_register.models[:address]).to eq(RegisterSpec::Address)
45
+ end
46
+
47
+ it "registers model without explicit id" do
48
+ stub_const("TestModel", Class.new(Lutaml::Model::Serializable))
49
+ v1_register.register_model(TestModel)
50
+ expect(v1_register.models[:test_model]).to eq(TestModel)
51
+ end
52
+ end
53
+
54
+ describe "#resolve" do
55
+ let(:v1_register) { described_class.new(:v1) }
56
+
57
+ before do
58
+ v1_register.register_model(RegisterSpec::Address, id: :address)
59
+ end
60
+
61
+ it "finds registered class by string representation" do
62
+ expect(v1_register.resolve("RegisterSpec::Address")).to eq(RegisterSpec::Address)
63
+ end
64
+
65
+ it "returns nil for unregistered class" do
66
+ expect(v1_register.resolve("UnknownClass")).to be_nil
67
+ end
68
+ end
69
+
70
+ describe "#get_class" do
71
+ let(:v1_register) { described_class.new(:v1) }
72
+
73
+ before do
74
+ v1_register.register_model(Lutaml::Model::Type::String, id: :custom_type)
75
+ end
76
+
77
+ it "returns registered class by key" do
78
+ expect(v1_register.get_class(:custom_type)).to eq(Lutaml::Model::Type::String)
79
+ end
80
+
81
+ it "returns class by string using constant lookup" do
82
+ expect(v1_register.get_class("String")).to eq(Lutaml::Model::Type::String)
83
+ end
84
+
85
+ it "returns class by symbol using Type.lookup" do
86
+ allow(Lutaml::Model::Type).to receive(:lookup).with(:String).and_return(Lutaml::Model::Type::String)
87
+ expect(v1_register.get_class(:String)).to eq(Lutaml::Model::Type::String)
88
+ end
89
+
90
+ it "returns class directly if class is provided" do
91
+ expect(v1_register.get_class(Lutaml::Model::Type::String)).to eq(Lutaml::Model::Type::String)
92
+ end
93
+
94
+ it "raises error for unsupported type" do
95
+ expect { v1_register.get_class(123) }.to raise_error(Lutaml::Model::UnknownTypeError)
96
+ end
97
+ end
98
+
99
+ describe "#register_model_tree" do
100
+ let(:v1_register) { described_class.new(:v1) }
101
+
102
+ context "when registering a valid model" do
103
+ let(:model_class) do
104
+ Class.new(Lutaml::Model::Serializable) do
105
+ attribute :nested_address, RegisterSpec::Address
106
+ end
107
+ end
108
+
109
+ it "registers the model and its nested attributes" do
110
+ v1_register.register_model_tree(model_class)
111
+ expect(v1_register.models.values).to include(model_class)
112
+ expect(v1_register.models.values).to include(RegisterSpec::Address)
113
+ end
114
+ end
115
+ end
116
+
117
+ describe "#register_global_type_substitution" do
118
+ let(:v1_register) { described_class.new(:v1) }
119
+
120
+ it "registers a global type substitution" do
121
+ v1_register.register_global_type_substitution(from_type: :string, to_type: :text)
122
+ expect(v1_register.instance_variable_get(:@global_substitutions)).to include(string: :text)
123
+ end
124
+ end
125
+
126
+ describe "#register_attributes" do
127
+ let(:v1_register) { described_class.new(:v1) }
128
+ let(:model_class) do
129
+ Class.new(Lutaml::Model::Serializable) do
130
+ attribute :nested_address, RegisterSpec::Address
131
+ attribute :string_attr, :string
132
+ end
133
+ end
134
+
135
+ it "registers non-builtin type attributes" do
136
+ attributes = model_class.attributes
137
+ v1_register.register_attributes(attributes)
138
+ expect(v1_register.models.values).to include(RegisterSpec::Address)
139
+ end
140
+
141
+ it "doesn't register built-in types" do
142
+ attributes = model_class.attributes
143
+ v1_register.register_attributes(attributes)
144
+ expect(v1_register.models.keys).not_to include(:string)
145
+ end
146
+ end
147
+ 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
@@ -0,0 +1,162 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Lutaml::Model::RuleValueExtractor do
4
+ subject(:extractor) do
5
+ described_class.new(rule, doc, format, attr, register, options)
6
+ end
7
+
8
+ let(:rule) { instance_double(Lutaml::Model::Jsonl::MappingRule) }
9
+ let(:doc) { { "name" => "Test", "value" => 123 } }
10
+ let(:format) { :json }
11
+ let(:attr) { instance_double(Lutaml::Model::Attribute) }
12
+ let(:register) { instance_double(Lutaml::Model::Register) }
13
+ let(:options) { {} }
14
+
15
+ describe "#call" do
16
+ context "when rule has single mapping" do
17
+ before do
18
+ allow(rule).to receive_messages(
19
+ multiple_mappings?: false,
20
+ name: "name",
21
+ root_mapping?: false,
22
+ raw_mapping?: false,
23
+ )
24
+ end
25
+
26
+ it "returns value for the rule name" do
27
+ expect(extractor.call).to eq("Test")
28
+ end
29
+ end
30
+
31
+ context "when rule has multiple mappings" do
32
+ before do
33
+ allow(rule).to receive_messages(
34
+ multiple_mappings?: true,
35
+ name: ["name", "value"],
36
+ root_mapping?: false,
37
+ raw_mapping?: false,
38
+ )
39
+ end
40
+
41
+ it "returns first initialized value" do
42
+ expect(extractor.call).to eq("Test")
43
+ end
44
+ end
45
+
46
+ context "when rule is root mapping" do
47
+ before do
48
+ allow(rule).to receive_messages(
49
+ multiple_mappings?: false,
50
+ name: "name",
51
+ root_mapping?: true,
52
+ )
53
+ end
54
+
55
+ it "returns the entire document" do
56
+ expect(extractor.call).to eq(doc)
57
+ end
58
+ end
59
+
60
+ context "when rule is raw mapping" do
61
+ before do
62
+ allow(rule).to receive_messages(
63
+ multiple_mappings?: false,
64
+ name: "name",
65
+ root_mapping?: false,
66
+ raw_mapping?: true,
67
+ )
68
+ end
69
+
70
+ it "converts document to specified format" do
71
+ expect(extractor.call).to eq('{"name":"Test","value":123}')
72
+ end
73
+ end
74
+
75
+ context "when value needs transformation" do
76
+ let(:options) do
77
+ {
78
+ key_mappings: instance_double(Lutaml::Model::KeyValueMappingRule, to_instance: :key),
79
+ value_mappings: instance_double(Lutaml::Model::KeyValueMappingRule, as_attribute: :value),
80
+ }
81
+ end
82
+
83
+ before do
84
+ allow(rule).to receive_messages(
85
+ multiple_mappings?: false,
86
+ name: "data",
87
+ root_mapping?: false,
88
+ raw_mapping?: false,
89
+ )
90
+ end
91
+
92
+ it "transforms hash values" do
93
+ doc = { "data" => { "test" => { "nested" => "value" } } }
94
+ extractor = described_class.new(rule, doc, format, attr, register, options)
95
+
96
+ expected = [{
97
+ "key" => "test",
98
+ "nested" => "value",
99
+ }]
100
+ expect(extractor.call).to eq(expected)
101
+ end
102
+
103
+ it "transforms simple values" do
104
+ doc = { "data" => { "test" => "value" } }
105
+ extractor = described_class.new(rule, doc, format, attr, register, options)
106
+
107
+ expected = [{
108
+ "key" => "test",
109
+ "value" => "value",
110
+ }]
111
+ expect(extractor.call).to eq(expected)
112
+ end
113
+ end
114
+
115
+ context "when value is not found" do
116
+ before do
117
+ allow(rule).to receive_messages(
118
+ multiple_mappings?: false,
119
+ name: "nonexistent",
120
+ root_mapping?: false,
121
+ raw_mapping?: false,
122
+ )
123
+ allow(attr).to receive(:default_set?).with(register).and_return(false)
124
+ end
125
+
126
+ it "returns uninitialized value" do
127
+ expect(extractor.call).to be_a(Lutaml::Model::UninitializedClass)
128
+ end
129
+ end
130
+
131
+ context "when attribute has default value" do
132
+ before do
133
+ allow(rule).to receive_messages(
134
+ multiple_mappings?: false,
135
+ name: "nonexistent",
136
+ root_mapping?: false,
137
+ raw_mapping?: false,
138
+ )
139
+ allow(attr).to receive(:default_set?).with(register).and_return(true)
140
+ allow(attr).to receive(:default).with(register).and_return("default_value")
141
+ end
142
+
143
+ it "returns default value" do
144
+ expect(extractor.call).to eq("default_value")
145
+ end
146
+ end
147
+ end
148
+
149
+ describe "#uninitialized_value" do
150
+ it "returns an instance of UninitializedClass" do
151
+ expect(
152
+ extractor.send(:uninitialized_value),
153
+ ).to be_a(Lutaml::Model::UninitializedClass)
154
+ end
155
+
156
+ it "returns the same instance for multiple calls" do
157
+ first_call = extractor.send(:uninitialized_value)
158
+ second_call = extractor.send(:uninitialized_value)
159
+ expect(first_call).to equal(second_call)
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "lutaml/model/schema"
5
+
6
+ module DefinitionsCollectionSpec
7
+ class Address < Lutaml::Model::Serializable
8
+ attribute :street, Lutaml::Model::Type::String
9
+ end
10
+
11
+ class HomeAddress < Address
12
+ attribute :is_home, Lutaml::Model::Type::Boolean
13
+ end
14
+
15
+ class WorkAddress < Address
16
+ attribute :company, Lutaml::Model::Type::String
17
+ end
18
+
19
+ class Person < Lutaml::Model::Serializable
20
+ attribute :name, Lutaml::Model::Type::String
21
+ attribute :age, Lutaml::Model::Type::Integer
22
+ attribute :address, Address, polymorphic: [HomeAddress, WorkAddress]
23
+ end
24
+ end
25
+
26
+ RSpec.describe Lutaml::Model::Schema::Generator::DefinitionsCollection do
27
+ let(:klass) { DefinitionsCollectionSpec::Person }
28
+ let(:register) { :def_collection }
29
+
30
+ before do
31
+ collection_register = Lutaml::Model::Register.new(register)
32
+ Lutaml::Model::GlobalRegister.register(collection_register)
33
+ end
34
+
35
+ describe ".from_class" do
36
+ subject(:collection) { described_class.from_class(klass) }
37
+
38
+ let(:all_classes) do
39
+ [
40
+ klass,
41
+ DefinitionsCollectionSpec::Address,
42
+ DefinitionsCollectionSpec::HomeAddress,
43
+ DefinitionsCollectionSpec::WorkAddress,
44
+ ]
45
+ end
46
+
47
+ it "creates a new DefinitionsCollection" do
48
+ expect(collection).to be_a(described_class)
49
+ end
50
+
51
+ it "generated definition" do
52
+ definition_class = Lutaml::Model::Schema::Generator::Definition
53
+ expect(collection.definitions.first).to be_a(definition_class)
54
+ end
55
+
56
+ it "initializes with the main class definition" do
57
+ expect(collection.definitions.size).to eq(all_classes.size)
58
+ end
59
+
60
+ it "includes definitions for main class and and all custom classes" do
61
+ definition_types = collection.definitions.map(&:type)
62
+ expect(definition_types).to include(*all_classes)
63
+ end
64
+ end
65
+
66
+ describe "#to_schema" do
67
+ subject(:schema) { collection.to_schema }
68
+
69
+ let(:collection) { described_class.from_class(klass) }
70
+
71
+ it "returns a hash" do
72
+ expect(schema).to be_a(Hash)
73
+ end
74
+
75
+ it "includes schema for all definitions" do
76
+ expect(schema.keys.size).to eq(collection.definitions.size)
77
+ end
78
+ end
79
+
80
+ describe "#add_definition" do
81
+ let(:collection) { described_class.new }
82
+ let(:definition) do
83
+ Lutaml::Model::Schema::Generator::Definition.new(String)
84
+ end
85
+
86
+ it "adds a definition to the collection" do
87
+ expect { collection.add_definition(definition) }
88
+ .to change { collection.definitions.size }.by(1)
89
+ end
90
+
91
+ it "accepts both Definition objects and classes" do
92
+ expect { collection.add_definition(String) }
93
+ .to change { collection.definitions.size }.by(1)
94
+ end
95
+ end
96
+
97
+ describe "#merge" do
98
+ let(:first_collection) do
99
+ described_class.new(
100
+ [
101
+ Lutaml::Model::Type::String,
102
+ Lutaml::Model::Type::Integer,
103
+ ],
104
+ )
105
+ end
106
+ let(:second_collection) do
107
+ described_class.new(
108
+ [
109
+ Lutaml::Model::Type::Float,
110
+ Lutaml::Model::Type::Boolean,
111
+ ],
112
+ )
113
+ end
114
+
115
+ it "merges two collections" do
116
+ first_collection.merge(second_collection)
117
+ expect(first_collection.definitions.size).to eq(4)
118
+ end
119
+ end
120
+ end