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
@@ -2,7 +2,12 @@ module Lutaml
2
2
  module Model
3
3
  class XmlTransform < Lutaml::Model::Transform
4
4
  def data_to_model(data, _format, options = {})
5
- instance = model_class.new
5
+ if model_class.include?(Lutaml::Model::Serialize)
6
+ instance = model_class.new({}, register: register)
7
+ else
8
+ instance = model_class.new
9
+ register_accessor_methods_for(instance, register)
10
+ end
6
11
  apply_xml_mapping(data, instance, options)
7
12
  end
8
13
 
@@ -29,11 +34,11 @@ module Lutaml
29
34
  validate_sequence!(doc.root.order)
30
35
 
31
36
  mappings.each do |rule|
32
- raise "Attribute '#{rule.to}' not found in #{context}" unless valid_rule?(rule)
33
-
34
37
  attr = attribute_for_rule(rule)
35
38
  next if attr&.derived?
36
39
 
40
+ raise "Attribute '#{rule.to}' not found in #{context}" unless valid_rule?(rule, attr)
41
+
37
42
  new_opts = options.dup
38
43
  if rule.namespace_set?
39
44
  new_opts[:default_namespace] = rule.namespace
@@ -46,9 +51,9 @@ module Lutaml
46
51
  else
47
52
  val = value_for_rule(doc, rule, new_opts, instance)
48
53
 
49
- if (Utils.uninitialized?(val) || val.nil?) && (instance.using_default?(rule.to) || rule.render_default)
54
+ if (val.nil? || Utils.uninitialized?(val)) && (instance.using_default?(rule.to) || rule.render_default)
50
55
  defaults_used << rule.to
51
- attr&.default || rule.to_value_for(instance)
56
+ attr&.default(register) || rule.to_value_for(instance)
52
57
  else
53
58
  val
54
59
  end
@@ -106,49 +111,45 @@ module Lutaml
106
111
 
107
112
  def value_for_rule(doc, rule, options, instance)
108
113
  rule_names = rule.namespaced_names(options[:default_namespace])
114
+ return doc.root.find_attribute_value(rule_names) if rule.attribute?
109
115
 
110
- if rule.attribute?
111
- doc.root.find_attribute_value(rule_names)
112
- else
113
- attr = attribute_for_rule(rule)
114
- children = doc.children.select do |child|
115
- rule_names.include?(child.namespaced_name) && !child.text?
116
- end
116
+ attr = attribute_for_rule(rule)
117
+ attr_type = attr&.type(register)
117
118
 
118
- if rule.has_custom_method_for_deserialization? || attr.type == Lutaml::Model::Type::Hash
119
- return_child = attr.type == Lutaml::Model::Type::Hash || !attr.collection? if attr
120
- return return_child ? children.first : children
121
- end
119
+ children = doc.children.select do |child|
120
+ rule_names.include?(child.namespaced_name) && !child.text?
121
+ end
122
122
 
123
- return handle_cdata(children) if rule.cdata
123
+ if rule.has_custom_method_for_deserialization? || attr_type == Lutaml::Model::Type::Hash
124
+ return_child = attr_type == Lutaml::Model::Type::Hash || !attr.collection? if attr
125
+ return return_child ? children.first : children
126
+ end
124
127
 
125
- values = []
128
+ return handle_cdata(children) if rule.cdata
129
+ return Lutaml::Model::UninitializedClass.instance if children.empty?
126
130
 
127
- if Utils.present?(children)
128
- instance.value_set_for(attr.name)
129
- else
130
- children = nil
131
- values = Lutaml::Model::UninitializedClass.instance
132
- end
131
+ values = attr.build_collection
133
132
 
134
- children&.each do |child|
135
- if !rule.has_custom_method_for_deserialization? && attr.type <= Serialize
136
- cast_options = options.except(:mappings)
137
- cast_options[:polymorphic] = rule.polymorphic if rule.polymorphic
133
+ instance.value_set_for(attr.name)
138
134
 
139
- values << attr.cast(child, :xml, cast_options)
140
- elsif attr.raw?
141
- values << inner_xml_of(child)
142
- else
143
- return nil if rule.render_nil_as_nil? && child.nil_element?
135
+ children.each do |child|
136
+ if !rule.has_custom_method_for_deserialization? && attr_type <= Serialize
137
+ cast_options = options.except(:mappings)
138
+ cast_options[:polymorphic] = rule.polymorphic if rule.polymorphic
139
+ cast_options[:register] = register
144
140
 
145
- text = child.nil_element? ? nil : (child&.text&.+ child&.cdata)
146
- values << text
147
- end
148
- end
141
+ values << attr.cast(child, :xml, register, cast_options)
142
+ elsif attr.raw?
143
+ values << inner_xml_of(child)
144
+ else
145
+ return nil if rule.render_nil_as_nil? && child.nil_element?
149
146
 
150
- normalized_value_for_attr(values, attr)
147
+ text = child.nil_element? ? nil : (child&.text&.+ child&.cdata)
148
+ values << text
149
+ end
151
150
  end
151
+
152
+ normalized_value_for_attr(values, attr)
152
153
  end
153
154
 
154
155
  def handle_cdata(children)
@@ -173,30 +174,16 @@ module Lutaml
173
174
  end
174
175
 
175
176
  def normalize_xml_value(value, rule, attr, options = {})
176
- value = [value].compact if attr&.collection? && !value.is_a?(Array) && !value.nil?
177
+ collection_class = attr&.collection_class || Array
178
+ value = [value].compact if !value.nil? && attr&.collection? && !value.is_a?(collection_class)
177
179
 
178
180
  return value unless cast_value?(attr, rule)
179
181
 
180
- options.merge(caller_class: self, mixed_content: rule.mixed_content)
181
- attr.cast(
182
- value,
183
- :xml,
184
- options,
185
- )
182
+ attr.cast(value, :xml, register, options)
186
183
  end
187
184
 
188
185
  def cast_value?(attr, rule)
189
- attr &&
190
- !rule.raw_mapping? &&
191
- !rule.content_mapping? &&
192
- !rule.custom_methods[:from]
193
- end
194
-
195
- def text_hash?(attr, value)
196
- return false unless value.is_a?(Hash)
197
- return value.one? && value.text? unless attr
198
-
199
- !(attr.type <= Serialize) && attr.type != Lutaml::Model::Type::Hash
186
+ attr && rule.castable?
200
187
  end
201
188
 
202
189
  def ensure_utf8(value)
@@ -228,10 +215,12 @@ module Lutaml
228
215
 
229
216
  def validate_sequence!(element_order)
230
217
  mapping_sequence = mappings_for(:xml).element_sequence
218
+ return if mapping_sequence.empty?
219
+
231
220
  current_order = element_order.filter_map(&:element_tag)
232
221
 
233
222
  mapping_sequence.each do |mapping|
234
- mapping.validate_content!(current_order)
223
+ mapping.validate_content!(current_order, context)
235
224
  end
236
225
  end
237
226
  end
@@ -2,18 +2,19 @@ module Lutaml
2
2
  module Model
3
3
  class Transform
4
4
  def self.data_to_model(context, data, format, options = {})
5
- new(context).data_to_model(data, format, options)
5
+ new(context, options[:register]).data_to_model(data, format, options)
6
6
  end
7
7
 
8
8
  def self.model_to_data(context, model, format, options = {})
9
- new(context).model_to_data(model, format, options)
9
+ new(context, model.register).model_to_data(model, format, options)
10
10
  end
11
11
 
12
- attr_reader :context, :attributes
12
+ attr_reader :context, :attributes, :register
13
13
 
14
- def initialize(context)
14
+ def initialize(context, register = nil)
15
15
  @context = context
16
16
  @attributes = context.attributes
17
+ @register = register || Lutaml::Model::Config.default_register
17
18
  end
18
19
 
19
20
  def model_class
@@ -59,16 +60,29 @@ module Lutaml
59
60
  context.mappings_for(format)
60
61
  end
61
62
 
62
- def valid_rule?(rule)
63
- attribute = attribute_for_rule(rule)
63
+ def defined_mappings_for(format)
64
+ context.mappings[format]
65
+ end
64
66
 
65
- !!attribute || rule.custom_methods[:from]
67
+ def valid_rule?(rule, attribute)
68
+ attribute || rule.custom_methods[:from]
66
69
  end
67
70
 
68
71
  def attribute_for_rule(rule)
69
72
  return attributes[rule.to] unless rule.delegate
70
73
 
71
- attributes[rule.delegate].type.attributes[rule.to]
74
+ attributes[rule.delegate].type(register).attributes[rule.to]
75
+ end
76
+
77
+ def register_accessor_methods_for(object, register)
78
+ klass = object.class
79
+ Utils.add_method_if_not_defined(klass, :register) do
80
+ @register
81
+ end
82
+ Utils.add_method_if_not_defined(klass, :register=) do |value|
83
+ @register = value
84
+ end
85
+ object.register = register
72
86
  end
73
87
  end
74
88
  end
@@ -2,7 +2,7 @@ module Lutaml
2
2
  module Model
3
3
  module Type
4
4
  class Boolean < Value
5
- def self.cast(value)
5
+ def self.cast(value, _options = {})
6
6
  return nil if value.nil?
7
7
  return true if value == true || value.to_s.match?(/^(true|t|yes|y|1)$/i)
8
8
  return false if value == false || value.to_s.match?(/^(false|f|no|n|0)$/i)
@@ -2,7 +2,7 @@ module Lutaml
2
2
  module Model
3
3
  module Type
4
4
  class Date < Value
5
- def self.cast(value)
5
+ def self.cast(value, _options = {})
6
6
  return value if value.nil? || Utils.uninitialized?(value)
7
7
 
8
8
  case value
@@ -5,7 +5,7 @@ module Lutaml
5
5
  module Type
6
6
  # Date and time representation
7
7
  class DateTime < Value
8
- def self.cast(value)
8
+ def self.cast(value, _options = {})
9
9
  return value if value.nil? || Utils.uninitialized?(value)
10
10
 
11
11
  case value
@@ -2,18 +2,20 @@ module Lutaml
2
2
  module Model
3
3
  module Type
4
4
  class Decimal < Value
5
- def self.cast(value)
5
+ def self.cast(value, options = {})
6
6
  return nil if value.nil?
7
7
 
8
8
  check_dependencies!(value)
9
- case value
10
- when BigDecimal
11
- # If already a BigDecimal, return as-is
12
- value
13
- else
14
- # Convert to string first to handle various input types
15
- BigDecimal(value.to_s)
16
- end
9
+ value = case value
10
+ when BigDecimal
11
+ # If already a BigDecimal, return as-is
12
+ value
13
+ else
14
+ # Convert to string first to handle various input types
15
+ BigDecimal(value.to_s)
16
+ end
17
+ Model::Services::Type::Validator::Number.validate!(value, options)
18
+ value
17
19
  rescue ArgumentError
18
20
  nil
19
21
  end
@@ -2,9 +2,10 @@ module Lutaml
2
2
  module Model
3
3
  module Type
4
4
  class Float < Value
5
- def self.cast(value)
5
+ def self.cast(value, options = {})
6
6
  return nil if value.nil?
7
7
 
8
+ Model::Services::Type::Validator::Number.validate!(value, options)
8
9
  value.to_f
9
10
  end
10
11
 
@@ -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
@@ -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
@@ -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,10 @@ 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
+
66
80
  # Check if the hash contains the given key in string or symbol format
67
81
  # @param hash [Hash] the hash to check
68
82
  # @param key [String, Symbol] the key to check
@@ -82,17 +96,25 @@ module Lutaml
82
96
  # @return [Object] the value associated with the key
83
97
  # @example
84
98
  # hash = { "key" => "value" }
85
- # fetch_with_string_or_symbol_key(hash, "key") # => "value"
86
- # fetch_with_string_or_symbol_key(hash, :key) # => "value"
87
- # fetch_with_string_or_symbol_key(hash, "invalid_key") # => nil
88
- def fetch_with_string_or_symbol_key(hash, key)
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)
89
103
  if hash.key?(key.to_s)
90
104
  hash[key.to_s]
91
105
  elsif hash.key?(key.to_sym)
92
106
  hash[key.to_sym]
107
+ else
108
+ default
93
109
  end
94
110
  end
95
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
+
96
118
  def add_method_if_not_defined(klass, method_name, &block)
97
119
  unless klass.method_defined?(method_name)
98
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.3"
5
+ VERSION = "0.7.5"
6
6
  end
7
7
  end