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,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Schema
6
+ module XmlCompiler
7
+ class AttributeGroup
8
+ attr_accessor :name, :ref, :instances
9
+
10
+ def initialize(name: nil, ref: nil)
11
+ @name = name
12
+ @ref = ref
13
+ @instances = []
14
+ end
15
+
16
+ def <<(instance)
17
+ return if instance.nil?
18
+
19
+ @instances << instance
20
+ end
21
+
22
+ def to_attributes(indent)
23
+ resolved_instances.map { |instance| instance.to_attributes(indent) }
24
+ end
25
+
26
+ def to_xml_mapping(indent)
27
+ resolved_instances.map { |instance| instance.to_xml_mapping(indent) }
28
+ end
29
+
30
+ def required_files
31
+ resolved_instances.map(&:required_files)
32
+ end
33
+
34
+ private
35
+
36
+ def resolved_instances
37
+ return @instances unless Utils.present?(@ref)
38
+
39
+ XmlCompiler.attribute_groups[@ref]&.instances
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Schema
6
+ module XmlCompiler
7
+ class Choice
8
+ attr_accessor :instances, :min_occurs, :max_occurs
9
+
10
+ INDENT = " "
11
+
12
+ TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
13
+ <%= indent %>choice<%= block_options %> do
14
+ <%= instances.map { |instance| instance.to_attributes(indent + INDENT) }.join -%>
15
+ <%= indent %>end
16
+ TEMPLATE
17
+
18
+ def initialize
19
+ @instances = []
20
+ end
21
+
22
+ def <<(instance)
23
+ return if instance.nil?
24
+
25
+ @instances << instance
26
+ end
27
+
28
+ def to_attributes(indent = INDENT)
29
+ TEMPLATE.result(binding)
30
+ end
31
+
32
+ def to_xml_mapping(indent = INDENT)
33
+ instances.filter_map { |instance| instance.to_xml_mapping(indent) }.join
34
+ end
35
+
36
+ def required_files
37
+ @instances.map(&:required_files).flatten.compact
38
+ end
39
+
40
+ private
41
+
42
+ def min_option
43
+ "min: #{min_occurs.nil? ? 1 : min_occurs.to_i}"
44
+ end
45
+
46
+ def max_option
47
+ value = case max_occurs
48
+ when "unbounded"
49
+ "Float::INFINITY"
50
+ when NilClass
51
+ 1
52
+ else
53
+ max_occurs.to_i
54
+ end
55
+ ", max: #{value}"
56
+ end
57
+
58
+ def block_options
59
+ ["(", min_option, max_option, ")"].compact.join
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,27 @@
1
+ module Lutaml
2
+ module Model
3
+ module Schema
4
+ module XmlCompiler
5
+ class ComplexContent
6
+ attr_accessor :restriction
7
+
8
+ def initialize(restriction = nil)
9
+ @restriction = restriction
10
+ end
11
+
12
+ def to_attributes(indent)
13
+ restriction&.to_attributes(indent)
14
+ end
15
+
16
+ def to_xml_mapping(indent)
17
+ restriction&.to_xml_mapping(indent)
18
+ end
19
+
20
+ def required_files
21
+ restriction&.required_files
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,34 @@
1
+ module Lutaml
2
+ module Model
3
+ module Schema
4
+ module XmlCompiler
5
+ class ComplexContentRestriction
6
+ attr_accessor :base, :instances
7
+
8
+ def initialize(base: nil, instances: [])
9
+ @base = base
10
+ @instances = instances
11
+ end
12
+
13
+ def <<(instance)
14
+ return if instance.nil?
15
+
16
+ @instances << instance
17
+ end
18
+
19
+ def to_attributes(indent)
20
+ instances.map { |instance| instance.to_attributes(indent) }
21
+ end
22
+
23
+ def to_xml_mapping(indent)
24
+ instances.map { |instance| instance.to_xml_mapping(indent) }
25
+ end
26
+
27
+ def required_files
28
+ instances.map(&:required_files)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,136 @@
1
+ module Lutaml
2
+ module Model
3
+ module Schema
4
+ module XmlCompiler
5
+ class ComplexType
6
+ attr_accessor :id,
7
+ :name,
8
+ :mixed,
9
+ :instances,
10
+ :base_class,
11
+ :simple_content
12
+
13
+ SERIALIZABLE_BASE_CLASS = "Lutaml::Model::Serializable".freeze
14
+
15
+ SIMPLE_CONTENT_ATTRIBUTE_TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
16
+ <%= @indent %>attribute :content, :<%= simple_content_type %>
17
+ <%= simple_content.to_attributes(@indent) if simple_content? -%>
18
+ TEMPLATE
19
+
20
+ TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
21
+ # frozen_string_literal: true
22
+
23
+ <%= required_files.uniq.join("\n") + "\n" -%>
24
+ class <%= Utils.camel_case(name) %> < <%= base_class_name %>
25
+ <%= instances.flat_map { |instance| instance.to_attributes(@indent) }.join + "\n" -%>
26
+ <%= simple_content_attribute -%>
27
+ <%= @indent %>xml do
28
+ <%= extended_indent %>root "<%= name %>"<%= root_options %>
29
+ <%= namespace_and_prefix %>
30
+ <%= "\#{extended_indent}map_content to: :content" if simple_content? || mixed %>
31
+ <%= instances.flat_map { |instance| instance.to_xml_mapping(extended_indent) }.join -%>
32
+ <%= simple_content.to_xml_mapping(extended_indent) if simple_content? -%>
33
+ <%= @indent %>end
34
+
35
+ <%= @indent %>def self.register
36
+ <%= extended_indent %>Lutaml::Model::GlobalRegister.lookup(Lutaml::Model::Config.default_register)
37
+ <%= @indent %>end
38
+
39
+ <%= @indent %>def self.register_class_with_id
40
+ <%= extended_indent %>register.register_model(self, id: :<%= Utils.snake_case(name) %>)
41
+ <%= @indent %>end
42
+ end
43
+
44
+ <%= Utils.camel_case(name) %>.register_class_with_id
45
+ TEMPLATE
46
+
47
+ def initialize(base_class: SERIALIZABLE_BASE_CLASS)
48
+ @base_class = base_class
49
+ @instances = []
50
+ end
51
+
52
+ def <<(instance)
53
+ return if instance.nil?
54
+
55
+ @instances << instance
56
+ end
57
+
58
+ def simple_content?
59
+ Utils.present?(@simple_content)
60
+ end
61
+
62
+ def to_class(options: {})
63
+ setup_options(options)
64
+ TEMPLATE.result(binding)
65
+ end
66
+
67
+ def required_files
68
+ files = [base_class_require]
69
+ files.concat(@instances.map(&:required_files).flatten.compact.uniq)
70
+ files.concat(simple_content.required_files) if simple_content?
71
+ files
72
+ end
73
+
74
+ private
75
+
76
+ def setup_options(options)
77
+ @namespace, @prefix = options.values_at(:namespace, :prefix)
78
+ @indent = " " * options&.fetch(:indent, 2)
79
+ end
80
+
81
+ def simple_content_type
82
+ return "string" unless simple_content?
83
+
84
+ Utils.snake_case(last_of_split(simple_content.base_class))
85
+ end
86
+
87
+ def simple_content_attribute
88
+ SIMPLE_CONTENT_ATTRIBUTE_TEMPLATE.result(binding) if simple_content? || mixed
89
+ end
90
+
91
+ def root_options
92
+ return "" unless mixed
93
+
94
+ ", mixed: true"
95
+ end
96
+
97
+ def namespace_and_prefix
98
+ return "" if Utils.blank?(@namespace) && Utils.blank?(@prefix)
99
+
100
+ [namespace_option, @prefix&.inspect].compact.join(", ")
101
+ end
102
+
103
+ def extended_indent
104
+ @indent * 2
105
+ end
106
+
107
+ def namespace_option
108
+ "#{extended_indent}namespace #{@namespace.inspect}"
109
+ end
110
+
111
+ def base_class_name
112
+ case base_class
113
+ when SERIALIZABLE_BASE_CLASS
114
+ SERIALIZABLE_BASE_CLASS
115
+ else
116
+ Utils.camel_case(last_of_split(base_class))
117
+ end
118
+ end
119
+
120
+ def base_class_require
121
+ case base_class
122
+ when SERIALIZABLE_BASE_CLASS
123
+ "require \"lutaml/model\""
124
+ else
125
+ "require_relative \"#{Utils.snake_case(last_of_split(base_class))}\""
126
+ end
127
+ end
128
+
129
+ def last_of_split(field)
130
+ field&.split(":")&.last
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Schema
6
+ module XmlCompiler
7
+ class Element
8
+ attr_accessor :id,
9
+ :ref,
10
+ :name,
11
+ :type,
12
+ :fixed,
13
+ :default,
14
+ :max_occurs,
15
+ :min_occurs
16
+
17
+ def initialize(name: nil, ref: nil)
18
+ @name = name
19
+ @ref = ref
20
+ end
21
+
22
+ def to_attributes(indent)
23
+ return if skippable?
24
+ return unless resolved_type
25
+
26
+ "#{indent}attribute :#{resolved_name}, :#{resolved_type}#{attribute_options}\n"
27
+ end
28
+
29
+ def to_xml_mapping(indent)
30
+ return if skippable?
31
+ return unless resolved_type
32
+
33
+ "#{indent}map_element :#{resolved_name(change_case: false)}, to: :#{resolved_name}#{render_default_option}\n"
34
+ end
35
+
36
+ def required_files
37
+ return if skippable?
38
+
39
+ element_type = resolved_type(change_case: false)
40
+ return "require \"bigdecimal\"" if element_type == "decimal"
41
+ return if SimpleType.skippable?(element_type)
42
+
43
+ "require_relative \"#{Utils.snake_case(element_type)}\""
44
+ end
45
+
46
+ private
47
+
48
+ def resolved_instance
49
+ @resolved_instance ||= XmlCompiler.instance_variable_get(:@elements)[last_of_split]
50
+ end
51
+
52
+ def resolved_type(change_case: true)
53
+ @current_type ||= type || resolved_instance&.type
54
+ klass_name = last_of_split(@current_type)
55
+ change_case ? Utils.snake_case(klass_name) : klass_name
56
+ end
57
+
58
+ def resolved_name(change_case: true)
59
+ @current_name ||= name || resolved_instance&.name
60
+ change_case ? Utils.snake_case(@current_name) : @current_name
61
+ end
62
+
63
+ def collection_option
64
+ return if min_occurs.nil? && max_occurs.nil?
65
+
66
+ min_value = min_occurs.nil? ? 1 : min_occurs.to_i
67
+ ", collection: #{min_value}..#{max_value}"
68
+ end
69
+
70
+ def max_value
71
+ return "Float::INFINITY" if max_occurs == "unbounded"
72
+ return 1 if max_occurs.nil?
73
+
74
+ max_occurs.to_i
75
+ end
76
+
77
+ def default_option
78
+ return if default.nil?
79
+
80
+ ", default: -> { #{default.inspect} }"
81
+ end
82
+
83
+ def render_default_option
84
+ return if default.nil?
85
+
86
+ ", render_default: true"
87
+ end
88
+
89
+ def attribute_options
90
+ [collection_option, default_option].compact.join
91
+ end
92
+
93
+ def last_of_split(field = ref)
94
+ field&.split(":")&.last
95
+ end
96
+
97
+ def skippable?
98
+ resolved_name == "schema_location"
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Schema
6
+ module XmlCompiler
7
+ class Group
8
+ attr_accessor :name, :ref, :instance
9
+
10
+ GROUP_TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
11
+ # frozen_string_literal: true
12
+
13
+ <%= "\n" + required_files.uniq.join("\n") -%>
14
+
15
+ class <%= Utils.camel_case(base_name) %> < Lutaml::Model::Serializable
16
+ <%= definitions_content %>
17
+ <%= xml_mapping_block -%>
18
+
19
+ <%= @indent %>def self.register
20
+ <%= extended_indent %>@register ||= Lutaml::Model::GlobalRegister.lookup(Lutaml::Model::Config.default_register)
21
+ <%= @indent %>end
22
+
23
+ <%= @indent %>def self.register_class_with_id
24
+ <%= extended_indent %>register.register_model(self, id: :<%= Utils.snake_case(base_name) %>)
25
+ <%= @indent %>end
26
+ end
27
+
28
+ <%= Utils.camel_case(base_name) %>.register_class_with_id
29
+ TEMPLATE
30
+
31
+ XML_MAPPING_TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
32
+ <%= @indent %>xml do
33
+ <%= extended_indent %>no_root
34
+ <%= instance&.to_xml_mapping(extended_indent) -%>
35
+ <%= @indent %>end
36
+ TEMPLATE
37
+
38
+ def initialize(name = nil, ref = nil)
39
+ @name = name
40
+ @ref = ref
41
+ end
42
+
43
+ def to_xml_mapping(indent = @indent)
44
+ if Utils.present?(@ref)
45
+ "#{indent}import_model_mappings :#{Utils.snake_case(base_name)}\n"
46
+ else
47
+ @instance.to_xml_mapping(indent * 2)
48
+ end
49
+ end
50
+
51
+ def to_class(options: {})
52
+ setup_options(options)
53
+ GROUP_TEMPLATE.result(binding)
54
+ end
55
+
56
+ def required_files
57
+ if Utils.blank?(name) && Utils.present?(ref)
58
+ "require_relative \"#{Utils.snake_case(ref.split(':').last)}\""
59
+ else
60
+ @instance&.required_files
61
+ end
62
+ end
63
+
64
+ def to_attributes(indent = @indent)
65
+ if Utils.present?(@ref)
66
+ "#{indent}import_model_attributes :#{Utils.snake_case(base_name)}\n"
67
+ else
68
+ @instance.to_attributes(indent)
69
+ end
70
+ end
71
+
72
+ def base_name
73
+ (name || ref)&.split(":")&.last
74
+ end
75
+
76
+ private
77
+
78
+ def setup_options(options)
79
+ @indent = " " * options&.fetch(:indent, 2)
80
+ end
81
+
82
+ def definitions_content
83
+ @definitions_content ||= instance.to_attributes(@indent)
84
+ end
85
+
86
+ def extended_indent
87
+ @indent * 2
88
+ end
89
+
90
+ def xml_mapping_block
91
+ XML_MAPPING_TEMPLATE.result(binding)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Schema
6
+ module XmlCompiler
7
+ class Restriction
8
+ # transform is not a 'xsd' type, but it is used for internal purposes.
9
+ attr_accessor :min_inclusive,
10
+ :max_inclusive,
11
+ :min_exclusive,
12
+ :max_exclusive,
13
+ :enumerations,
14
+ :max_length,
15
+ :min_length,
16
+ :base_class,
17
+ :pattern,
18
+ :length,
19
+ :transform
20
+
21
+ MIN_MAX_BOUNDS = ERB.new(<<~TEMPLATE, trim_mode: "-")
22
+ <%= "\#{indent}options[:max] = \#{max_inclusive || max_exclusive}" if max_bound_exist? %>
23
+ <%= "\#{indent}options[:min] = \#{min_inclusive || min_exclusive}" if min_bound_exist? %>
24
+ TEMPLATE
25
+
26
+ PATTERN = ERB.new(<<~TEMPLATE, trim_mode: "-")
27
+ <%= "\#{indent}options[:pattern] = %r{\#{pattern}}" %>
28
+ TEMPLATE
29
+
30
+ ENUMERATIONS = ERB.new(<<~TEMPLATE, trim_mode: "-")
31
+ <%= "\#{indent}options[:values] = [\#{casted_enumerations}]" %>
32
+ TEMPLATE
33
+
34
+ TRANSFORM = ERB.new(<<~TEMPLATE, trim_mode: "-")
35
+ <%= "\#{indent}value = \#{transform}" %>
36
+ TEMPLATE
37
+
38
+ def to_method_body(indent = nil)
39
+ [
40
+ value_for(ENUMERATIONS, type: :enumerations, indent: indent),
41
+ value_for(MIN_MAX_BOUNDS, type: :min_max_bounds, indent: indent),
42
+ value_for(PATTERN, type: :pattern, indent: indent),
43
+ value_for(TRANSFORM, type: :transform, indent: indent),
44
+ ].compact.join
45
+ end
46
+
47
+ def required_files
48
+ if base_class_name == :decimal
49
+ "require \"bigdecimal\""
50
+ elsif !SimpleType::SUPPORTED_DATA_TYPES.dig(base_class_name, :skippable)
51
+ "require_relative \"#{Utils.snake_case(base_class_name)}\""
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def value_for(constant, type:, indent:)
58
+ return unless send("#{type}_exist?")
59
+
60
+ indent ||= " "
61
+ constant.result(binding)
62
+ end
63
+
64
+ def min_max_bounds_exist?
65
+ min_bound_exist? || max_bound_exist?
66
+ end
67
+
68
+ def min_bound_exist?
69
+ Utils.present?(min_inclusive) || Utils.present?(min_exclusive)
70
+ end
71
+
72
+ def max_bound_exist?
73
+ Utils.present?(max_inclusive) || Utils.present?(max_exclusive)
74
+ end
75
+
76
+ def pattern_exist?
77
+ Utils.present?(pattern)
78
+ end
79
+
80
+ def enumerations_exist?
81
+ Utils.present?(enumerations)
82
+ end
83
+
84
+ def transform_exist?
85
+ Utils.present?(transform)
86
+ end
87
+
88
+ def base_class_name
89
+ return if Utils.blank?(base_class)
90
+
91
+ base_class.split(":").last.to_sym
92
+ end
93
+
94
+ def casted_enumerations
95
+ enumerations.map { |enumeration| "super(#{enumeration.inspect})" }.join(", ")
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Schema
6
+ module XmlCompiler
7
+ class Sequence
8
+ attr_accessor :instances
9
+
10
+ XML_MAPPING_TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
11
+ <%= indent %>sequence do
12
+ <%= content -%>
13
+ <%= indent %>end
14
+ TEMPLATE
15
+
16
+ def initialize
17
+ @instances = []
18
+ end
19
+
20
+ def <<(instance)
21
+ return if instance.nil?
22
+
23
+ @instances << instance
24
+ end
25
+
26
+ def to_attributes(indent)
27
+ instances.filter_map { |instance| instance.to_attributes(indent) }.join
28
+ end
29
+
30
+ def to_xml_mapping(indent)
31
+ content = xml_block_content(indent)
32
+ return "" if content.empty?
33
+
34
+ XML_MAPPING_TEMPLATE.result(binding)
35
+ end
36
+
37
+ def required_files
38
+ @instances.map(&:required_files)
39
+ end
40
+
41
+ private
42
+
43
+ def xml_block_content(indent)
44
+ instances.filter_map { |instance| instance.to_xml_mapping(indent * 2) }.join
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Schema
6
+ module XmlCompiler
7
+ class SimpleContent
8
+ attr_accessor :instances, :base_class
9
+
10
+ def initialize
11
+ @instances = []
12
+ @base_class = nil
13
+ end
14
+
15
+ def <<(instance)
16
+ return if instance.nil?
17
+
18
+ @instances << instance
19
+ end
20
+
21
+ def to_attributes(indent = nil)
22
+ instances.filter_map { |instance| instance.to_attributes(indent) }.join("\n")
23
+ end
24
+
25
+ def to_xml_mapping(indent = nil)
26
+ instances.filter_map { |instance| instance.to_xml_mapping(indent) }.join("\n")
27
+ end
28
+
29
+ def required_files
30
+ instances.map(&:required_files).flatten.compact.uniq
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end