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
@@ -2,31 +2,34 @@ module Lutaml
2
2
  module Model
3
3
  module Type
4
4
  class Integer < Value
5
- def self.cast(value)
5
+ def self.cast(value, options = {})
6
6
  return nil if value.nil?
7
7
  return 1 if value === true
8
8
  return 0 if value === false
9
9
 
10
- case value
11
- when ::String
12
- if value.match?(/^0[0-7]+$/) # Octal
13
- value.to_i(8)
14
- elsif value.match?(/^-?\d+(\.\d+)?(e-?\d+)?$/i) # Float/exponential
15
- Float(value).to_i
16
- else
17
- begin
18
- Integer(value, 10)
19
- rescue StandardError
20
- nil
21
- end
22
- end
23
- else
24
- begin
25
- Integer(value)
26
- rescue StandardError
27
- nil
28
- end
29
- end
10
+ value = case value
11
+ when ::String
12
+ if value.match?(/^0[0-7]+$/) # Octal
13
+ value.to_i(8)
14
+ elsif value.match?(/^-?\d+(\.\d+)?(e-?\d+)?$/i) # Float/exponential
15
+ Float(value).to_i
16
+ else
17
+ begin
18
+ Integer(value, 10)
19
+ rescue StandardError
20
+ nil
21
+ end
22
+ end
23
+ else
24
+ begin
25
+ Integer(value)
26
+ rescue StandardError
27
+ nil
28
+ end
29
+ end
30
+
31
+ Model::Services::Type::Validator::Number.validate!(value, options)
32
+ value
30
33
  end
31
34
 
32
35
  # Override serialize to return Integer instead of String
@@ -2,10 +2,12 @@ module Lutaml
2
2
  module Model
3
3
  module Type
4
4
  class String < Value
5
- def self.cast(value)
5
+ def self.cast(value, options = {})
6
6
  return nil if value.nil?
7
7
 
8
- value.to_s
8
+ value = value.to_s
9
+ Model::Services::Type::Validator::String.validate!(value, options)
10
+ value
9
11
  end
10
12
 
11
13
  # xs:string format
@@ -4,7 +4,7 @@ module Lutaml
4
4
  module Model
5
5
  module Type
6
6
  class Time < Value
7
- def self.cast(value)
7
+ def self.cast(value, _options = {})
8
8
  return value if value.nil? || Utils.uninitialized?(value)
9
9
 
10
10
  case value
@@ -10,7 +10,7 @@ module Lutaml
10
10
  # time = ::Time.parse(value.to_s)
11
11
  # ::Time.new(1, 1, 1, time.hour, time.min, time.sec)
12
12
 
13
- def self.cast(value)
13
+ def self.cast(value, _options = {})
14
14
  return value if value.nil? || Utils.uninitialized?(value)
15
15
 
16
16
  case value
@@ -11,7 +11,11 @@ module Lutaml
11
11
  @value = self.class.cast(value)
12
12
  end
13
13
 
14
- def self.cast(value)
14
+ def initialized?
15
+ true
16
+ end
17
+
18
+ def self.cast(value, _options = {})
15
19
  return nil if value.nil?
16
20
 
17
21
  value
@@ -28,20 +32,17 @@ module Lutaml
28
32
  value.to_s
29
33
  end
30
34
 
31
- # Format-specific instance methods
32
- ::Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
33
- define_method(:"to_#{format}") do
34
- value
35
- end
36
- end
37
-
38
35
  # Class-level format conversion
39
36
  def self.from_format(value, format)
40
37
  new(send(:"from_#{format}", value))
41
38
  end
42
39
 
43
- # Default format-specific class methods that can be overridden
44
- ::Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
40
+ # called from config when a new format is added
41
+ def self.register_format_to_from_methods(format)
42
+ define_method(:"to_#{format}") do
43
+ value
44
+ end
45
+
45
46
  define_singleton_method(:"from_#{format}") do |value|
46
47
  cast(value)
47
48
  end
@@ -5,6 +5,7 @@ module Lutaml
5
5
  string: "Lutaml::Model::Type::String",
6
6
  integer: "Lutaml::Model::Type::Integer",
7
7
  float: "Lutaml::Model::Type::Float",
8
+ double: "Lutaml::Model::Type::Float",
8
9
  decimal: "Lutaml::Model::Type::Decimal",
9
10
  date: "Lutaml::Model::Type::Date",
10
11
  time: "Lutaml::Model::Type::Time",
@@ -32,6 +33,8 @@ module Lutaml
32
33
  end
33
34
 
34
35
  def lookup(type_name)
36
+ return type_name if type_name.is_a?(Class)
37
+
35
38
  @registry ||= {}
36
39
  klass = @registry[type_name.to_sym]
37
40
 
@@ -43,13 +46,13 @@ module Lutaml
43
46
  def cast(value, type)
44
47
  return nil if value.nil?
45
48
 
46
- type.cast(value)
49
+ lookup(type).cast(value)
47
50
  end
48
51
 
49
52
  def serialize(value, type)
50
53
  return nil if value.nil?
51
54
 
52
- type.serialize(value)
55
+ lookup(type).serialize(value)
53
56
  end
54
57
  end
55
58
  end
@@ -12,7 +12,7 @@ module Lutaml
12
12
  end
13
13
 
14
14
  def inspect
15
- "unititialized"
15
+ "uninitialized"
16
16
  end
17
17
 
18
18
  def uninitialized?
@@ -4,6 +4,8 @@ module Lutaml
4
4
  module Model
5
5
  module Utils
6
6
  class << self
7
+ UNINITIALIZED = Lutaml::Model::UninitializedClass.instance
8
+
7
9
  # Convert string to camel case
8
10
  def camel_case(str)
9
11
  return "" if str.nil? || str.empty?
@@ -34,14 +36,22 @@ module Lutaml
34
36
  .downcase
35
37
  end
36
38
 
37
- def initialized?(value)
38
- return true unless value.respond_to?(:initialized?)
39
+ # Extract the base name of the class
40
+ def base_class_name(klass)
41
+ klass.to_s.split("::").last
42
+ end
39
43
 
40
- value.initialized?
44
+ # Convert the extracted base class to snake case format
45
+ def base_class_snake_case(klass)
46
+ snake_case(base_class_name(klass))
47
+ end
48
+
49
+ def initialized?(value)
50
+ !value.equal?(UNINITIALIZED)
41
51
  end
42
52
 
43
53
  def uninitialized?(value)
44
- !initialized?(value)
54
+ value.equal?(UNINITIALIZED)
45
55
  end
46
56
 
47
57
  def present?(value)
@@ -63,6 +73,48 @@ module Lutaml
63
73
  value.respond_to?(:empty?) ? value.empty? : false
64
74
  end
65
75
 
76
+ def add_if_present(hash, key, value)
77
+ hash[key] = value if value
78
+ end
79
+
80
+ # Check if the hash contains the given key in string or symbol format
81
+ # @param hash [Hash] the hash to check
82
+ # @param key [String, Symbol] the key to check
83
+ # @return [Boolean] true if the hash contains the key, false otherwise
84
+ # @example
85
+ # hash = { "key" => "value" }
86
+ # string_or_symbol_key?(hash, "key") # => true
87
+ # string_or_symbol_key?(hash, :key) # => true
88
+ # string_or_symbol_key?(hash, "invalid_key") # => false
89
+ def string_or_symbol_key?(hash, key)
90
+ hash.key?(key.to_s) || hash.key?(key.to_sym)
91
+ end
92
+
93
+ # Fetch the value from the hash using the key in string or symbol format
94
+ # @param hash [Hash] the hash to fetch the value from
95
+ # @param key [String, Symbol] the key to fetch the value for
96
+ # @return [Object] the value associated with the key
97
+ # @example
98
+ # hash = { "key" => "value" }
99
+ # fetch_str_or_sym(hash, "key") # => "value"
100
+ # fetch_str_or_sym(hash, :key) # => "value"
101
+ # fetch_str_or_sym(hash, "invalid_key") # => nil
102
+ def fetch_str_or_sym(hash, key, default = nil)
103
+ if hash.key?(key.to_s)
104
+ hash[key.to_s]
105
+ elsif hash.key?(key.to_sym)
106
+ hash[key.to_sym]
107
+ else
108
+ default
109
+ end
110
+ end
111
+
112
+ def add_singleton_method_if_not_defined(instance, method_name, &block)
113
+ return if instance.respond_to?(method_name)
114
+
115
+ instance.define_singleton_method(method_name, &block)
116
+ end
117
+
66
118
  def add_method_if_not_defined(klass, method_name, &block)
67
119
  unless klass.method_defined?(method_name)
68
120
  klass.class_eval do
@@ -1,20 +1,22 @@
1
1
  module Lutaml
2
2
  module Model
3
3
  module Validation
4
- def validate
4
+ def validate(register: Lutaml::Model::Config.default_register)
5
5
  errors = []
6
+
6
7
  self.class.attributes.each do |name, attr|
7
8
  value = public_send(:"#{name}")
8
9
  begin
9
10
  if value.respond_to?(:validate)
10
11
  errors.concat(value.validate)
11
12
  else
12
- attr.validate_value!(value)
13
+ attr.validate_value!(value, register)
13
14
  end
14
15
  rescue Lutaml::Model::InvalidValueError,
15
16
  Lutaml::Model::CollectionCountOutOfRangeError,
16
17
  Lutaml::Model::CollectionTrueMissingError,
17
18
  Lutaml::Model::PolymorphicError,
19
+ Lutaml::Model::ValidationFailedError,
18
20
  PatternNotMatchedError => e
19
21
  errors << e
20
22
  end
@@ -23,8 +25,8 @@ module Lutaml
23
25
  validate_helper(errors)
24
26
  end
25
27
 
26
- def validate!
27
- errors = validate
28
+ def validate!(register: Lutaml::Model::Config.default_register)
29
+ errors = validate(register: register)
28
30
  raise Lutaml::Model::ValidationError.new(errors) if errors.any?
29
31
  end
30
32
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.7.1"
5
+ VERSION = "0.7.7"
6
6
  end
7
7
  end
@@ -1,6 +1,6 @@
1
1
  module Lutaml
2
2
  module Model
3
- module XmlAdapter
3
+ module Xml
4
4
  module Builder
5
5
  class Nokogiri
6
6
  def self.build(options = {})
@@ -84,7 +84,7 @@ module Lutaml
84
84
  end
85
85
 
86
86
  def method_missing(method_name, *args, &block)
87
- if block_given?
87
+ if block
88
88
  xml.public_send(method_name, *args, &block)
89
89
  else
90
90
  xml.public_send(method_name, *args)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- module XmlAdapter
5
+ module Xml
6
6
  module Builder
7
7
  class Oga
8
8
  def self.build(options = {}, &block)
@@ -12,7 +12,7 @@ module Lutaml
12
12
  attr_reader :document, :current_node, :encoding
13
13
 
14
14
  def initialize(options = {})
15
- @document = XmlAdapter::Oga::Document.new
15
+ @document = Xml::Oga::Document.new
16
16
  @current_node = @document
17
17
  @encoding = options[:encoding]
18
18
  yield(self) if block_given?
@@ -23,7 +23,7 @@ module Lutaml
23
23
  name = "#{@current_namespace}:#{name}"
24
24
  end
25
25
 
26
- if block_given?
26
+ if block
27
27
  element(name, attributes, &block)
28
28
  else
29
29
  element(name, attributes)
@@ -48,9 +48,9 @@ module Lutaml
48
48
 
49
49
  def add_element(oga_element, child)
50
50
  if child.is_a?(String)
51
- current_element = oga_element.is_a?(XmlAdapter::Oga::Document) ? current_node : oga_element
51
+ current_element = oga_element.is_a?(Xml::Oga::Document) ? current_node : oga_element
52
52
  add_xml_fragment(current_element, child)
53
- elsif oga_element.is_a?(XmlAdapter::Oga::Document)
53
+ elsif oga_element.is_a?(Xml::Oga::Document)
54
54
  oga_element.children.last.children << child
55
55
  else
56
56
  oga_element.children << child
@@ -62,7 +62,7 @@ module Lutaml
62
62
  name: name,
63
63
  value: value.to_s,
64
64
  )
65
- if element.is_a?(XmlAdapter::Oga::Document)
65
+ if element.is_a?(Xml::Oga::Document)
66
66
  element.children.last.attributes << attribute
67
67
  else
68
68
  element.attributes << attribute
@@ -85,7 +85,7 @@ module Lutaml
85
85
  element_name
86
86
  end
87
87
 
88
- if block_given?
88
+ if block
89
89
  element(prefixed_name, attributes, &block)
90
90
  else
91
91
  element(prefixed_name, attributes)
@@ -100,7 +100,7 @@ module Lutaml
100
100
  fragment = "<fragment>#{content}</fragment>"
101
101
  parsed_fragment = ::Oga.parse_xml(fragment)
102
102
  parsed_children = parsed_fragment.children.first.children
103
- if element.is_a?(XmlAdapter::Oga::Document)
103
+ if element.is_a?(Xml::Oga::Document)
104
104
  element.children.last.children += parsed_children
105
105
  else
106
106
  element.children += parsed_children
@@ -116,7 +116,7 @@ module Lutaml
116
116
  end
117
117
 
118
118
  def append_text_node(element, oga_text)
119
- if element.is_a?(XmlAdapter::Oga::Document)
119
+ if element.is_a?(Xml::Oga::Document)
120
120
  children = element.children
121
121
  children.empty? ? children << oga_text : children.last.children << oga_text
122
122
  else
@@ -126,7 +126,7 @@ module Lutaml
126
126
 
127
127
  def add_cdata(element, value)
128
128
  oga_cdata = ::Oga::XML::CData.new(text: value.to_s)
129
- if element.is_a?(XmlAdapter::Oga::Document)
129
+ if element.is_a?(Xml::Oga::Document)
130
130
  element.children.last.children << oga_cdata
131
131
  else
132
132
  element.children << oga_cdata
@@ -1,6 +1,6 @@
1
1
  module Lutaml
2
2
  module Model
3
- module XmlAdapter
3
+ module Xml
4
4
  module Builder
5
5
  class Ox
6
6
  def self.build(options = {})
@@ -6,13 +6,14 @@ require_relative "element"
6
6
 
7
7
  module Lutaml
8
8
  module Model
9
- module XmlAdapter
10
- class XmlDocument
11
- attr_reader :root, :encoding
9
+ module Xml
10
+ class Document
11
+ attr_reader :root, :encoding, :register
12
12
 
13
- def initialize(root, encoding = nil)
13
+ def initialize(root, encoding = nil, register: nil)
14
14
  @root = root
15
15
  @encoding = encoding
16
+ @register = setup_register(register)
16
17
  end
17
18
 
18
19
  def self.parse(xml, _options = {})
@@ -79,7 +80,7 @@ module Lutaml
79
80
  options[:mixed_content] = rule.mixed_content
80
81
  options[:tag_name] = rule.name
81
82
 
82
- options[:mapper_class] = attribute&.type if attribute
83
+ options[:mapper_class] = attribute&.type(register) if attribute
83
84
  options[:set_namespace] = set_namespace?(rule)
84
85
 
85
86
  options
@@ -107,7 +108,7 @@ module Lutaml
107
108
  result["elements"] ||= Lutaml::Model::MappingHash.new
108
109
  result["elements"].assign_or_append_value(
109
110
  self.class.namespaced_name_of(child),
110
- parse_element(child, attr&.type || klass, format),
111
+ parse_element(child, attr&.type(register) || klass, format),
111
112
  )
112
113
  end
113
114
 
@@ -127,9 +128,9 @@ module Lutaml
127
128
  prefix: attr.namespace_prefix,
128
129
  schema_location: attr.value,
129
130
  }
131
+ else
132
+ result[attr.namespaced_name] = attr.value
130
133
  end
131
-
132
- result[attr.namespaced_name] = attr.value
133
134
  end
134
135
 
135
136
  result
@@ -148,17 +149,16 @@ module Lutaml
148
149
  rule = options[:rule]
149
150
 
150
151
  if rule.custom_methods[:to]
151
- options[:mapper_class].new.send(rule.custom_methods[:to], element,
152
- xml.parent, xml)
152
+ options[:mapper_class].new.send(rule.custom_methods[:to], element, xml.parent, xml)
153
153
  return
154
154
  end
155
155
 
156
156
  # Only transform when recursion is not called
157
- if (!attribute.collection? || value.is_a?(Array)) && transform_method = rule.transform[:export] || attribute.transform_export_method
158
- value = transform_method.call(value)
157
+ if !attribute.collection? || attribute.collection_instance?(value)
158
+ value = ExportTransformer.call(value, rule, attribute)
159
159
  end
160
160
 
161
- if value.is_a?(Array) && !Utils.empty_collection?(value)
161
+ if attribute.collection_instance?(value) && !Utils.empty_collection?(value)
162
162
  value.each do |item|
163
163
  add_to_xml(xml, element, prefix, item, options)
164
164
  end
@@ -170,7 +170,7 @@ module Lutaml
170
170
 
171
171
  value = rule.render_value_for(value)
172
172
 
173
- if value && (attribute&.type&.<= Lutaml::Model::Serialize)
173
+ if value && (attribute&.type(register)&.<= Lutaml::Model::Serialize)
174
174
  handle_nested_elements(
175
175
  xml,
176
176
  value,
@@ -195,10 +195,10 @@ module Lutaml
195
195
 
196
196
  def add_value(xml, value, attribute, cdata: false)
197
197
  if !value.nil?
198
- serialized_value = attribute.serialize(value, :xml)
198
+ serialized_value = attribute.serialize(value, :xml, register)
199
199
  if attribute.raw?
200
200
  xml.add_xml_fragment(xml, value)
201
- elsif attribute.type == Lutaml::Model::Type::Hash
201
+ elsif attribute.type(register) == Lutaml::Model::Type::Hash
202
202
  serialized_value.each do |key, val|
203
203
  xml.create_and_add_element(key) do |element|
204
204
  element.text(val)
@@ -233,7 +233,6 @@ module Lutaml
233
233
  end
234
234
 
235
235
  mappings = xml_mapping.elements + [xml_mapping.raw_mapping].compact
236
-
237
236
  mappings.each do |element_rule|
238
237
  attribute_def = attribute_definition_for(element, element_rule,
239
238
  mapper_class: mapper_class)
@@ -243,7 +242,7 @@ module Lutaml
243
242
 
244
243
  next if !element_rule.render?(value, element)
245
244
 
246
- value = [value] if attribute_def.collection? && !value.is_a?(Array)
245
+ value = attribute_def.build_collection(value) if attribute_def.collection? && !attribute_def.collection_instance?(value)
247
246
  end
248
247
 
249
248
  add_to_xml(
@@ -265,14 +264,16 @@ module Lutaml
265
264
  return unless content_rule
266
265
 
267
266
  if content_rule.custom_methods[:to]
268
- mapper_class.new.send(content_rule.custom_methods[:to], element,
269
- xml.parent, xml)
267
+ mapper_class.new.send(
268
+ content_rule.custom_methods[:to],
269
+ element,
270
+ xml.parent,
271
+ xml,
272
+ )
270
273
  else
271
274
  text = content_rule.serialize(element)
272
275
  text = text.join if text.is_a?(Array)
273
276
 
274
- return unless content_rule.render?(text, element)
275
-
276
277
  xml.add_text(xml, text, cdata: content_rule.cdata)
277
278
  end
278
279
  end
@@ -323,9 +324,10 @@ module Lutaml
323
324
  processed[klass][mapping_rule.name] = true
324
325
 
325
326
  type = if mapping_rule.delegate
326
- attributes[mapping_rule.delegate].type.attributes[mapping_rule.to].type
327
+ attributes[mapping_rule.delegate].type(register)
328
+ .attributes[mapping_rule.to].type(register)
327
329
  else
328
- attributes[mapping_rule.to]&.type
330
+ attributes[mapping_rule.to]&.type(register)
329
331
  end
330
332
 
331
333
  next unless type
@@ -366,7 +368,9 @@ module Lutaml
366
368
 
367
369
  value = mapping_rule.to_value_for(element)
368
370
  attr = attribute_definition_for(element, mapping_rule, mapper_class: options[:mapper_class])
369
- value = attr.serialize(value, :xml) if attr
371
+ value = attr.serialize(value, :xml, register) if attr
372
+
373
+ value = ExportTransformer.call(value, mapping_rule, attr)
370
374
 
371
375
  if render_element?(mapping_rule, element, value)
372
376
  hash[mapping_rule.prefixed_name] = value ? value.to_s : value
@@ -434,6 +438,19 @@ module Lutaml
434
438
 
435
439
  private
436
440
 
441
+ def setup_register(register)
442
+ return register if register.is_a?(Symbol)
443
+
444
+ return_register = if register.is_a?(Lutaml::Model::Register)
445
+ register.id
446
+ elsif @root.respond_to?(:__register)
447
+ @root.__register
448
+ elsif @root.instance_variable_defined?(:@__register)
449
+ @root.instance_variable_get(:@__register)
450
+ end
451
+ return_register || Lutaml::Model::Config.default_register
452
+ end
453
+
437
454
  def determine_mapper_class(element, options)
438
455
  if options[:mapper_class] && element.is_a?(options[:mapper_class])
439
456
  element.class
@@ -0,0 +1,32 @@
1
+ module Lutaml
2
+ module Model
3
+ module Xml
4
+ class Element
5
+ attr_reader :type, :name
6
+
7
+ def initialize(type, name)
8
+ @type = type
9
+ @name = name
10
+ end
11
+
12
+ def text?
13
+ @type == "Text" && @name != "#cdata-section"
14
+ end
15
+
16
+ def element_tag
17
+ @name unless text?
18
+ end
19
+
20
+ def eql?(other)
21
+ return false unless other.is_a?(self.class)
22
+
23
+ instance_variables.all? do |var|
24
+ instance_variable_get(var) == other.instance_variable_get(var)
25
+ end
26
+ end
27
+
28
+ alias == eql?
29
+ end
30
+ end
31
+ end
32
+ end