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,181 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Lutaml::Model::Schema::XmlCompiler::SimpleType do
4
+ let(:class_name) { "TestType" }
5
+ let(:base_class) { "string" }
6
+ let(:unions) { [] }
7
+ let(:simple_type) { described_class.new(class_name, unions) }
8
+
9
+ describe "#initialize" do
10
+ it "sets class_name and unions" do
11
+ expect(simple_type.class_name).to eq(class_name)
12
+ expect(simple_type.unions).to eq([])
13
+ end
14
+
15
+ it "allows unions to be set" do
16
+ st = described_class.new("UnionType", ["foo", "bar"])
17
+ expect(st.unions).to eq(["foo", "bar"])
18
+ end
19
+ end
20
+
21
+ describe "attribute accessors" do
22
+ it "allows reading and writing base_class and instance" do
23
+ simple_type.base_class = "integer"
24
+ expect(simple_type.base_class).to eq("integer")
25
+ simple_type.instance = :foo
26
+ expect(simple_type.instance).to eq(:foo)
27
+ end
28
+ end
29
+
30
+ describe "#to_class" do
31
+ before do
32
+ simple_type.base_class = base_class
33
+ simple_type.instance = Lutaml::Model::Schema::XmlCompiler::Restriction.new
34
+ end
35
+
36
+ it "renders the instance model template when no unions" do
37
+ result = simple_type.to_class
38
+ expect(result).to include("class TestType")
39
+ expect(result).to include("def self.cast")
40
+ expect(result).to include("register_class_with_id")
41
+ end
42
+
43
+ it "renders the union model template when unions are present" do
44
+ st = described_class.new("UnionType", ["foo", "bar"])
45
+ st.base_class = base_class
46
+ st.instance = Lutaml::Model::Schema::XmlCompiler::Restriction.new
47
+ result = st.to_class
48
+ expect(result).to include("class UnionType < Lutaml::Model::Type::Value")
49
+ expect(result).to include("def self.cast")
50
+ expect(result).to include("register_class_with_id")
51
+ end
52
+
53
+ it "respects the :indent option" do
54
+ result = simple_type.to_class(options: { indent: 4 })
55
+ expect(result).to include(" def self.cast") # 4 spaces
56
+ end
57
+ end
58
+
59
+ describe "#required_files" do
60
+ it "returns nil if instance is nil" do
61
+ expect(simple_type.required_files).to be_empty
62
+ end
63
+
64
+ it "returns required files from instance and parent if needed" do
65
+ restriction = Lutaml::Model::Schema::XmlCompiler::Restriction.new
66
+ allow(restriction).to receive(:required_files).and_return(["require 'foo'"])
67
+ simple_type.instance = restriction
68
+ allow(simple_type).to receive_messages(require_parent?: true, parent_class: "ParentClass")
69
+ expect(simple_type.required_files).to include("require 'foo'")
70
+ expect(simple_type.required_files).to include("require_relative \"parent_class\"")
71
+ end
72
+ end
73
+
74
+ describe "private methods" do
75
+ it "klass_name returns camel case" do
76
+ expect(simple_type.send(:klass_name)).to eq("TestType")
77
+ end
78
+
79
+ it "parent_class returns correct class for skippable and non-skippable" do
80
+ simple_type.base_class = "string"
81
+ expect(simple_type.send(:parent_class)).to eq("Lutaml::Model::Type::String")
82
+ simple_type.base_class = "nonNegativeInteger"
83
+ expect(simple_type.send(:parent_class)).to eq("NonNegativeInteger")
84
+ simple_type.base_class = nil
85
+ expect(simple_type.send(:parent_class)).to eq("Lutaml::Model::Type::Value")
86
+ end
87
+
88
+ it "require_parent? returns correct boolean" do
89
+ simple_type.base_class = "string"
90
+ expect(simple_type.send(:require_parent?)).to be false
91
+ simple_type.base_class = "unknownType"
92
+ expect(simple_type.send(:require_parent?)).to be true
93
+ end
94
+
95
+ it "union_class_method_body returns correct code" do
96
+ st = described_class.new("UnionType", ["foo:Bar", "baz:Qux"])
97
+ expect(st.send(:union_class_method_body)).to include("register.get_class(:bar).cast(value, options)")
98
+ expect(st.send(:union_class_method_body)).to include("register.get_class(:qux).cast(value, options)")
99
+ end
100
+
101
+ it "union_required_files returns correct require lines" do
102
+ st = described_class.new("UnionType", ["foo:Bar", "baz:string"])
103
+ expect(st.send(:union_required_files)).to include("require_relative \"bar\"")
104
+ expect(st.send(:union_required_files)).not_to include("string")
105
+ end
106
+ end
107
+
108
+ describe ".setup_supported_types" do
109
+ it "returns a hash of supported types with correct instances" do
110
+ types = described_class.setup_supported_types
111
+ expect(types).to be_a(Hash)
112
+ expect(types.keys).to include("nonNegativeInteger")
113
+ expect(types["nonNegativeInteger"]).to be_a(described_class)
114
+ expect(types["nonNegativeInteger"].base_class).to eq("string")
115
+ end
116
+ end
117
+
118
+ describe ".setup_restriction" do
119
+ it "returns nil if validations is nil" do
120
+ expect(described_class.setup_restriction("string", nil)).to be_nil
121
+ end
122
+
123
+ it "returns a Restriction with correct values" do
124
+ validations = { min_inclusive: 1, max_inclusive: 10, pattern: /foo/, transform: "bar" }
125
+ restriction = described_class.setup_restriction("string", validations)
126
+ expect(restriction).to be_a(Lutaml::Model::Schema::XmlCompiler::Restriction)
127
+ expect(restriction.base_class).to eq("string")
128
+ expect(restriction.min_inclusive).to eq(1)
129
+ expect(restriction.max_inclusive).to eq(10)
130
+ expect(restriction.pattern).to eq(/foo/)
131
+ expect(restriction.transform).to eq("bar")
132
+ end
133
+ end
134
+
135
+ describe ".skippable?" do
136
+ it "returns true for skippable types" do
137
+ expect(described_class.skippable?("string")).to be true
138
+ expect(described_class.skippable?("int")).to be true
139
+ end
140
+
141
+ it "returns false for non-skippable types" do
142
+ expect(described_class.skippable?("nonNegativeInteger")).to be false
143
+ expect(described_class.skippable?("id")).to be false
144
+ end
145
+ end
146
+
147
+ describe "edge cases" do
148
+ it "handles unknown base_class gracefully" do
149
+ simple_type.base_class = nil
150
+ expect { simple_type.send(:parent_class) }.not_to raise_error
151
+ expect(simple_type.send(:parent_class)).to eq("Lutaml::Model::Type::Value")
152
+ end
153
+
154
+ it "handles unions with skippable and non-skippable types" do
155
+ st = described_class.new("UnionType", ["foo:string", "bar:CustomType"])
156
+ expect(st.send(:union_required_files)).to include("require_relative \"custom_type\"")
157
+ expect(st.send(:union_required_files)).not_to include("string")
158
+ end
159
+ end
160
+
161
+ describe "integration" do
162
+ it "generates valid Ruby code for a simple type" do
163
+ simple_type.base_class = "string"
164
+ simple_type.instance = Lutaml::Model::Schema::XmlCompiler::Restriction.new
165
+ code = simple_type.to_class
166
+ expect(code).to include("class TestType")
167
+ expect(code).to include("def self.cast")
168
+ expect(code).to include("register_class_with_id")
169
+ end
170
+
171
+ it "generates valid Ruby code for a union type" do
172
+ st = described_class.new("UnionType", ["foo:Bar", "baz:Qux"])
173
+ st.base_class = "string"
174
+ st.instance = Lutaml::Model::Schema::XmlCompiler::Restriction.new
175
+ code = st.to_class
176
+ expect(code).to include("class UnionType < Lutaml::Model::Type::Value")
177
+ expect(code).to include("def self.cast")
178
+ expect(code).to include("register_class_with_id")
179
+ end
180
+ end
181
+ end