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
@@ -1,10 +1,8 @@
1
- require_relative "yaml_adapter"
2
1
  require_relative "xml_adapter"
3
2
  require_relative "config"
4
3
  require_relative "type"
5
4
  require_relative "attribute"
6
5
  require_relative "mapping_hash"
7
- require_relative "mapping"
8
6
  require_relative "json_adapter"
9
7
  require_relative "comparable_model"
10
8
  require_relative "schema_location"
@@ -13,6 +11,8 @@ require_relative "error"
13
11
  require_relative "choice"
14
12
  require_relative "sequence"
15
13
  require_relative "liquefiable"
14
+ require_relative "transform"
15
+ require_relative "registrable"
16
16
 
17
17
  module Lutaml
18
18
  module Model
@@ -20,6 +20,7 @@ module Lutaml
20
20
  include ComparableModel
21
21
  include Validation
22
22
  include Lutaml::Model::Liquefiable
23
+ include Lutaml::Model::Registrable
23
24
 
24
25
  def self.included(base)
25
26
  base.extend(ClassMethods)
@@ -29,7 +30,7 @@ module Lutaml
29
30
  module ClassMethods
30
31
  include Lutaml::Model::Liquefiable::ClassMethods
31
32
 
32
- attr_accessor :attributes, :mappings, :choice_attributes
33
+ attr_accessor :choice_attributes, :mappings
33
34
 
34
35
  def inherited(subclass)
35
36
  super
@@ -48,6 +49,16 @@ module Lutaml
48
49
  instance_variable_set(:@model, self)
49
50
  end
50
51
 
52
+ def attributes(register = nil)
53
+ ensure_imports!(register) if finalized?
54
+ @attributes
55
+ end
56
+
57
+ def ensure_imports!(register = nil)
58
+ ensure_model_imports!(register)
59
+ ensure_choice_imports!(register)
60
+ end
61
+
51
62
  def model(klass = nil)
52
63
  if klass
53
64
  @model = klass
@@ -112,7 +123,7 @@ module Lutaml
112
123
  end
113
124
  define_method(:"#{name}=") do |value|
114
125
  value_set_for(name)
115
- instance_variable_set(:"@#{name}", attr.cast_value(value))
126
+ instance_variable_set(:"@#{name}", attr.cast_value(value, __register))
116
127
  end
117
128
  end
118
129
  end
@@ -131,6 +142,25 @@ module Lutaml
131
142
  attr
132
143
  end
133
144
 
145
+ def restrict(name, options = {})
146
+ validate_attribute_options!(name, options)
147
+ attr = attributes[name]
148
+ attr.options.merge!(options)
149
+ attr.process_options!
150
+ name
151
+ end
152
+
153
+ def validate_attribute_options!(name, options)
154
+ invalid_opts = options.keys - Attribute::ALLOWED_OPTIONS
155
+ return if invalid_opts.empty?
156
+
157
+ raise Lutaml::Model::InvalidAttributeOptionsError.new(name, invalid_opts)
158
+ end
159
+
160
+ def register(name)
161
+ name&.to_sym
162
+ end
163
+
134
164
  def root?
135
165
  mappings_for(:xml)&.root?
136
166
  end
@@ -142,6 +172,14 @@ module Lutaml
142
172
  end
143
173
 
144
174
  def import_model_attributes(model)
175
+ if model.is_a?(Symbol) || model.is_a?(String)
176
+ importable_models[:import_model_attributes] << model.to_sym
177
+ @models_imported = false
178
+ @choices_imported = false
179
+ setup_trace_point
180
+ return
181
+ end
182
+
145
183
  model.attributes.each_value do |attr|
146
184
  define_attribute_methods(attr)
147
185
  end
@@ -151,37 +189,60 @@ module Lutaml
151
189
  end
152
190
 
153
191
  def import_model_mappings(model)
154
- import_model_with_root_error(model)
192
+ if model.is_a?(Symbol) || model.is_a?(String)
193
+ importable_models[:import_model_mappings] << model.to_sym
194
+ @models_imported = false
195
+ setup_trace_point
196
+ return
197
+ end
155
198
 
199
+ import_model_with_root_error(model)
156
200
  Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
157
201
  next unless model.mappings.key?(format)
158
202
 
159
203
  mapping = model.mappings_for(format)
160
204
  mapping = Utils.deep_dup(mapping)
161
205
 
162
- @mappings[format] ||= format == :xml ? XmlMapping.new : KeyValueMapping.new
206
+ klass = ::Lutaml::Model::Config.mappings_class_for(format)
207
+ @mappings[format] ||= klass.new
163
208
 
164
209
  if format == :xml
165
210
  @mappings[format].merge_mapping_attributes(mapping)
166
211
  @mappings[format].merge_mapping_elements(mapping)
167
212
  @mappings[format].merge_elements_sequence(mapping)
168
213
  else
169
- @mappings[format].mappings.concat(mapping.mappings)
214
+ @mappings[format].mappings_hash.merge!(mapping.mappings_hash)
170
215
  end
171
216
  end
172
217
  end
173
218
 
174
219
  def handle_key_value_mappings(mapping, format)
175
220
  @mappings[format] ||= KeyValueMapping.new
176
- @mappings[format].mappings.concat(mapping.mappings)
221
+ @mappings[format].mappings_hash.merge!(mapping.mappings_hash)
177
222
  end
178
223
 
179
224
  def import_model(model)
225
+ if model.is_a?(Symbol) || model.is_a?(String)
226
+ importable_models[:import_model] << model.to_sym
227
+ @models_imported = false
228
+ @choices_imported = false
229
+ setup_trace_point
230
+ return
231
+ end
232
+
180
233
  import_model_with_root_error(model)
181
234
  import_model_attributes(model)
182
235
  import_model_mappings(model)
183
236
  end
184
237
 
238
+ def importable_models
239
+ @importable_models ||= MappingHash.new { |h, k| h[k] = [] }
240
+ end
241
+
242
+ def importable_choices
243
+ @importable_choices ||= MappingHash.new { |h, k| h[k] = MappingHash.new { |h1, k1| h1[k1] = [] } }
244
+ end
245
+
185
246
  def add_enum_methods_to_model(klass, enum_name, values, collection: false)
186
247
  add_enum_getter_if_not_defined(klass, enum_name, collection)
187
248
  add_enum_setter_if_not_defined(klass, enum_name, values, collection)
@@ -262,71 +323,60 @@ module Lutaml
262
323
  attributes.select { |_, attr| attr.enum? }
263
324
  end
264
325
 
265
- Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
266
- define_method(format) do |&block|
267
- mapping_class = const_get("Lutaml::Model::#{format.to_s.capitalize}Mapping")
268
- mappings[format] ||= mapping_class.new
269
- mappings[format].instance_eval(&block)
326
+ def process_mapping(format, &block)
327
+ klass = ::Lutaml::Model::Config.mappings_class_for(format)
328
+ mappings[format] ||= klass.new
329
+ mappings[format].instance_eval(&block)
270
330
 
271
- if format == :xml && !mappings[format].root_element && !mappings[format].no_root?
272
- mappings[format].root(model.to_s)
273
- end
331
+ if mappings[format].respond_to?(:finalize)
332
+ mappings[format].finalize(self)
274
333
  end
334
+ end
275
335
 
276
- define_method(:"from_#{format}") do |data, options = {}|
277
- return data if Utils.uninitialized?(data)
336
+ def from(format, data, options = {})
337
+ adapter = Lutaml::Model::Config.adapter_for(format)
278
338
 
279
- adapter = Lutaml::Model::Config.send(:"#{format}_adapter")
339
+ doc = adapter.parse(data, options)
340
+ send("of_#{format}", doc, options)
341
+ end
280
342
 
281
- doc = adapter.parse(data, options)
282
- public_send(:"of_#{format}", doc, options)
343
+ def of(format, doc, options = {})
344
+ if doc.is_a?(Array) && format != :jsonl
345
+ return doc.map { |item| send(:"of_#{format}", item) }
283
346
  end
284
347
 
285
- define_method(:"of_#{format}") do |doc, options = {}|
286
- if doc.is_a?(Array)
287
- return doc.map { |item| send(:"of_#{format}", item) }
288
- end
289
-
290
- if format == :xml
291
- raise Lutaml::Model::NoRootMappingError.new(self) unless root?
348
+ if format == :xml
349
+ valid = root? || options[:from_collection]
350
+ raise Lutaml::Model::NoRootMappingError.new(self) unless valid
292
351
 
293
- options[:encoding] = doc.encoding
294
- apply_mappings(doc, format, options)
295
- else
296
- apply_mappings(doc.to_h, format, options)
297
- end
352
+ options[:encoding] = doc.encoding
298
353
  end
354
+ options[:register] = extract_register_id(options[:register])
299
355
 
300
- define_method(:"to_#{format}") do |instance|
301
- value = public_send(:"as_#{format}", instance)
302
- adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
303
-
304
- if format == :xml
305
- xml_options = { mapper_class: self }
306
- adapter.new(value).public_send(:"to_#{format}", xml_options)
307
- else
308
- adapter.new(value).public_send(:"to_#{format}")
309
- end
310
- end
356
+ transformer = Lutaml::Model::Config.transformer_for(format)
357
+ transformer.data_to_model(self, doc, format, options)
358
+ end
311
359
 
312
- define_method(:"as_#{format}") do |instance, options = {}|
313
- if instance.is_a?(Array)
314
- return instance.map { |item| public_send(:"as_#{format}", item) }
315
- end
360
+ def to(format, instance, options = {})
361
+ value = public_send(:"as_#{format}", instance, options)
362
+ adapter = Lutaml::Model::Config.adapter_for(format)
316
363
 
317
- unless instance.is_a?(model)
318
- msg = "argument is a '#{instance.class}' but should be a '#{model}'"
319
- raise Lutaml::Model::IncorrectModelError, msg
320
- end
364
+ options[:mapper_class] = self if format == :xml
365
+ adapter.new(value).public_send(:"to_#{format}", options)
366
+ end
321
367
 
322
- return instance if format == :xml
368
+ def as(format, instance, options = {})
369
+ if instance.is_a?(Array)
370
+ return instance.map { |item| public_send(:"as_#{format}", item) }
371
+ end
323
372
 
324
- hash_representation(instance, format, options)
373
+ unless instance.is_a?(model)
374
+ msg = "argument is a '#{instance.class}' but should be a '#{model}'"
375
+ raise Lutaml::Model::IncorrectModelError, msg
325
376
  end
326
- end
327
377
 
328
- def as(format, instance, options = {})
329
- public_send(:"as_#{format}", instance, options)
378
+ transformer = Lutaml::Model::Config.transformer_for(format)
379
+ transformer.model_to_data(self, instance, format, options)
330
380
  end
331
381
 
332
382
  def key_value(&block)
@@ -336,93 +386,16 @@ module Lutaml
336
386
  end
337
387
  end
338
388
 
339
- def hash_representation(instance, format, options = {})
340
- only = options[:only]
341
- except = options[:except]
342
- mappings = mappings_for(format).mappings
343
-
344
- mappings.each_with_object({}) do |rule, hash|
345
- name = rule.to
346
- attr = attributes[name]
347
-
348
- next if except&.include?(name) || (only && !only.include?(name))
349
- next handle_delegate(instance, rule, hash, format) if rule.delegate
350
-
351
- if rule.custom_methods[:to]
352
- next instance.send(rule.custom_methods[:to], instance, hash)
353
- end
354
-
355
- value = rule.serialize(instance)
356
-
357
- if rule.raw_mapping?
358
- adapter = Lutaml::Model::Config.send(:"#{format}_adapter")
359
- return adapter.parse(value, options)
360
- end
361
-
362
- if export_method = rule.transform[:export] || attr.transform_export_method
363
- value = export_method.call(value)
364
- end
365
-
366
- next hash.merge!(generate_hash_from_child_mappings(attr, value, format, rule.root_mappings)) if rule.root_mapping?
367
-
368
- value = if rule.child_mappings
369
- generate_hash_from_child_mappings(attr, value, format, rule.child_mappings)
370
- else
371
- attr.serialize(value, format, options)
372
- end
373
-
374
- next if !rule.render?(value, instance, options)
375
-
376
- value = apply_value_map(value, rule.value_map(:to, options), attr)
377
-
378
- rule_from_name = rule.multiple_mappings? ? rule.from.first.to_s : rule.from.to_s
379
- hash[rule_from_name] = value
380
- end
381
- end
382
-
383
- def apply_value_map(value, value_map, attr)
384
- if value.nil?
385
- value_for_option(value_map[:nil], attr)
386
- elsif Utils.empty?(value)
387
- value_for_option(value_map[:empty], attr, value)
388
- elsif Utils.uninitialized?(value)
389
- value_for_option(value_map[:omitted], attr)
390
- else
391
- value
392
- end
393
- end
394
-
395
- def value_for_option(option, attr, empty_value = nil)
396
- return nil if option == :nil
397
- return empty_value || empty_object(attr) if option == :empty
398
-
399
- Lutaml::Model::UninitializedClass.instance
400
- end
401
-
402
- def empty_object(attr)
403
- return [] if attr.collection?
404
-
405
- ""
406
- end
407
-
408
- def handle_delegate(instance, rule, hash, format)
409
- name = rule.to
410
- value = instance.send(rule.delegate).send(name)
411
- return if value.nil? && !rule.render_nil
412
-
413
- attribute = instance.send(rule.delegate).class.attributes[name]
414
- rule_from_name = rule.multiple_mappings? ? rule.from.first.to_s : rule.from.to_s
415
- hash[rule_from_name] = attribute.serialize(value, format)
416
- end
417
-
418
389
  def mappings_for(format)
390
+ @mappings[:xml]&.ensure_mappings_imported! if @mappings&.dig(:xml)&.finalized?
419
391
  mappings[format] || default_mappings(format)
420
392
  end
421
393
 
422
394
  def default_mappings(format)
423
- _mappings = format == :xml ? XmlMapping.new : KeyValueMapping.new(format)
395
+ klass = ::Lutaml::Model::Config.mappings_class_for(format)
396
+ mappings = klass.new
424
397
 
425
- _mappings.tap do |mapping|
398
+ mappings.tap do |mapping|
426
399
  attributes&.each_key do |name|
427
400
  mapping.map_element(
428
401
  name.to_s,
@@ -430,116 +403,21 @@ module Lutaml
430
403
  )
431
404
  end
432
405
 
433
- mapping.root(to_s.split("::").last) if format == :xml
434
- end
435
- end
436
-
437
- def translate_mappings(hash, child_mappings, attr, format)
438
- return hash unless child_mappings
439
-
440
- hash.map do |key, value|
441
- child_hash = child_mappings.to_h do |attr_name, path|
442
- attr_value = if path == :key
443
- key
444
- elsif path == :value
445
- value
446
- else
447
- path = [path] unless path.is_a?(Array)
448
- value.dig(*path.map(&:to_s))
449
- end
450
-
451
- attr_rule = attr.type.mappings_for(format).find_by_to(attr_name)
452
- [attr_rule.from.to_s, attr_value]
453
- end
454
-
455
- if child_mappings.values == [:key] && hash.values.all?(Hash)
456
- child_hash.merge!(value)
457
- end
458
-
459
- attr.type.apply_hash_mapping(
460
- child_hash,
461
- attr.type.model.new,
462
- format,
463
- { mappings: attr.type.mappings_for(format).mappings },
464
- )
465
- end
466
- end
467
-
468
- def generate_hash_from_child_mappings(attr, value, format, child_mappings)
469
- return value unless child_mappings
470
-
471
- hash = {}
472
-
473
- if child_mappings.values == [:key]
474
- klass = value.first.class
475
- mappings = klass.mappings_for(format)
476
-
477
- klass.attributes.each_key do |name|
478
- next if child_mappings.key?(name.to_sym) || child_mappings.key?(name.to_s)
479
-
480
- child_mappings[name.to_sym] = mappings.find_by_to(name)&.name.to_s || name.to_s
481
- end
482
- end
483
-
484
- value.each do |child_obj|
485
- map_key = nil
486
- map_value = {}
487
- mapping_rules = attr.type.mappings_for(format)
488
-
489
- child_mappings.each do |attr_name, path|
490
- mapping_rule = mapping_rules.find_by_to(attr_name)
491
-
492
- attr_value = child_obj.send(attr_name)
493
-
494
- attr_value = if attr_value.is_a?(Lutaml::Model::Serialize)
495
- attr_value.to_yaml_hash
496
- elsif attr_value.is_a?(Array) && attr_value.first.is_a?(Lutaml::Model::Serialize)
497
- attr_value.map(&:to_yaml_hash)
498
- else
499
- attr_value
500
- end
501
-
502
- next unless mapping_rule&.render?(attr_value, nil)
503
-
504
- if path == :key
505
- map_key = attr_value
506
- elsif path == :value
507
- map_value = attr_value
508
- else
509
- path = [path] unless path.is_a?(Array)
510
- path[0...-1].inject(map_value) do |acc, k|
511
- acc[k.to_s] ||= {}
512
- end.public_send(:[]=, path.last.to_s, attr_value)
513
- end
514
- end
515
-
516
- map_value = nil if map_value.empty?
517
- hash[map_key] = map_value
406
+ mapping.root(Utils.base_class_name(self)) if format == :xml
518
407
  end
519
-
520
- hash
521
- end
522
-
523
- def valid_rule?(rule)
524
- attribute = attribute_for_rule(rule)
525
-
526
- !!attribute || rule.custom_methods[:from]
527
- end
528
-
529
- def attribute_for_rule(rule)
530
- return attributes[rule.to] unless rule.delegate
531
-
532
- attributes[rule.delegate].type.attributes[rule.to]
533
- end
534
-
535
- def attribute_for_child(child_name, format)
536
- mapping_rule = mappings_for(format).find_by_name(child_name)
537
-
538
- attribute_for_rule(mapping_rule) if mapping_rule
539
408
  end
540
409
 
541
410
  def apply_mappings(doc, format, options = {})
542
- instance = options[:instance] || model.new
411
+ register = options[:register] || Lutaml::Model::Config.default_register
412
+ instance = if options.key?(:instance)
413
+ options[:instance]
414
+ elsif model.include?(Lutaml::Model::Serialize)
415
+ model.new({ __register: register })
416
+ else
417
+ object = model.new
418
+ register_accessor_methods_for(object, register)
419
+ object
420
+ end
543
421
  return instance if Utils.blank?(doc)
544
422
 
545
423
  mappings = mappings_for(format)
@@ -548,10 +426,8 @@ module Lutaml
548
426
  return resolve_polymorphic(doc, format, mappings, instance, options)
549
427
  end
550
428
 
551
- options[:mappings] = mappings.mappings
552
- return apply_xml_mapping(doc, instance, options) if format == :xml
553
-
554
- apply_hash_mapping(doc, instance, format, options)
429
+ transformer = Lutaml::Model::Config.transformer_for(format)
430
+ transformer.data_to_model(self, doc, format, options)
555
431
  end
556
432
 
557
433
  def resolve_polymorphic(doc, format, mappings, instance, options = {})
@@ -562,321 +438,167 @@ module Lutaml
562
438
  klass_name = polymorphic_mapping.polymorphic_map[klass_key]
563
439
  klass = Object.const_get(klass_name)
564
440
 
565
- klass.apply_mappings(doc, format, options)
441
+ klass.apply_mappings(doc, format, options.merge(register: instance.__register))
566
442
  end
567
443
 
568
- def apply_xml_mapping(doc, instance, options = {})
569
- options = prepare_options(options)
570
- instance.encoding = options[:encoding]
571
- return instance unless doc
572
-
573
- mappings = options[:mappings] || mappings_for(:xml).mappings
574
-
575
- validate_document!(doc, options)
576
-
577
- set_instance_ordering(instance, doc, options)
578
- set_schema_location(instance, doc)
579
-
580
- defaults_used = []
581
- validate_sequence!(doc.root.order)
582
-
583
- mappings.each do |rule|
584
- raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
585
-
586
- attr = attribute_for_rule(rule)
587
- next if attr&.derived?
588
-
589
- new_opts = options.dup
590
- if rule.namespace_set?
591
- new_opts[:default_namespace] = rule.namespace
592
- end
593
-
594
- value = if rule.raw_mapping?
595
- doc.root.inner_xml
596
- elsif rule.content_mapping?
597
- rule.cdata ? doc.cdata : doc.text
598
- else
599
- val = value_for_rule(doc, rule, new_opts, instance)
600
-
601
- if (Utils.uninitialized?(val) || val.nil?) && (instance.using_default?(rule.to) || rule.render_default)
602
- defaults_used << rule.to
603
- attr&.default || rule.to_value_for(instance)
604
- else
605
- val
606
- end
607
- end
608
-
609
- value = apply_value_map(value, rule.value_map(:from, new_opts), attr)
610
- value = normalize_xml_value(value, rule, attr, new_opts)
611
- rule.deserialize(instance, value, attributes, self)
612
- end
613
-
614
- defaults_used.each do |attr_name|
615
- instance.using_default_for(attr_name)
444
+ def apply_value_map(value, value_map, attr)
445
+ if value.nil?
446
+ value_for_option(value_map[:nil], attr)
447
+ elsif Utils.empty?(value)
448
+ value_for_option(value_map[:empty], attr, value)
449
+ elsif Utils.uninitialized?(value)
450
+ value_for_option(value_map[:omitted], attr)
451
+ else
452
+ value
616
453
  end
617
-
618
- instance
619
454
  end
620
455
 
621
- def prepare_options(options)
622
- opts = Utils.deep_dup(options)
623
- opts[:default_namespace] ||= mappings_for(:xml)&.namespace_uri
456
+ def value_for_option(option, attr, empty_value = nil)
457
+ return nil if option == :nil
458
+ return empty_value || empty_object(attr) if option == :empty
624
459
 
625
- opts
460
+ Lutaml::Model::UninitializedClass.instance
626
461
  end
627
462
 
628
- def validate_document!(doc, options)
629
- return unless doc.is_a?(Array)
463
+ def empty_object(attr)
464
+ return attr.build_collection if attr.collection?
630
465
 
631
- raise Lutaml::Model::CollectionTrueMissingError(
632
- self,
633
- options[:caller_class],
634
- )
466
+ ""
635
467
  end
636
468
 
637
- def set_instance_ordering(instance, doc, options)
638
- return unless instance.respond_to?(:ordered=)
639
-
640
- instance.element_order = doc.root.order
641
- instance.ordered = mappings_for(:xml).ordered? || options[:ordered]
642
- instance.mixed = mappings_for(:xml).mixed_content? || options[:mixed_content]
469
+ def ensure_utf8(value)
470
+ case value
471
+ when String
472
+ value.encode("UTF-8", invalid: :replace, undef: :replace,
473
+ replace: "")
474
+ when Array
475
+ value.map { |v| ensure_utf8(v) }
476
+ when Hash
477
+ value.transform_keys do |k|
478
+ ensure_utf8(k)
479
+ end.transform_values do |v|
480
+ ensure_utf8(v)
481
+ end
482
+ else
483
+ value
484
+ end
643
485
  end
644
486
 
645
- def set_schema_location(instance, doc)
646
- schema_location = doc.attributes.values.find do |a|
647
- a.unprefixed_name == "schemaLocation"
487
+ def register_accessor_methods_for(object, register)
488
+ Utils.add_singleton_method_if_not_defined(object, :__register) do
489
+ @__register
648
490
  end
649
-
650
- return if schema_location.nil?
651
-
652
- instance.schema_location = Lutaml::Model::SchemaLocation.new(
653
- schema_location: schema_location.value,
654
- prefix: schema_location.namespace_prefix,
655
- namespace: schema_location.namespace,
656
- )
491
+ Utils.add_singleton_method_if_not_defined(object, :__register=) do |value|
492
+ @__register = value
493
+ end
494
+ object.__register = register
657
495
  end
658
496
 
659
- def value_for_rule(doc, rule, options, instance)
660
- rule_names = rule.namespaced_names(options[:default_namespace])
661
-
662
- if rule.attribute?
663
- doc.root.find_attribute_value(rule_names)
497
+ def extract_register_id(register)
498
+ if register
499
+ register.is_a?(Lutaml::Model::Register) ? register.id : register
500
+ elsif class_variable_defined?(:@@__register)
501
+ class_variable_get(:@@__register)
664
502
  else
665
- attr = attribute_for_rule(rule)
666
- children = doc.children.select do |child|
667
- rule_names.include?(child.namespaced_name) && !child.text?
668
- end
669
-
670
- if rule.using_custom_methods? || attr.type == Lutaml::Model::Type::Hash
671
- return_child = attr.type == Lutaml::Model::Type::Hash || !attr.collection? if attr
672
- return return_child ? children.first : children
673
- end
503
+ Lutaml::Model::Config.default_register
504
+ end
505
+ end
674
506
 
675
- return handle_cdata(children) if rule.cdata
507
+ def ensure_model_imports!(register_id = nil)
508
+ return if @models_imported
676
509
 
677
- values = []
510
+ register_id ||= Lutaml::Model::Config.default_register
511
+ register = Lutaml::Model::GlobalRegister.lookup(register_id)
512
+ importable_models.each do |method, models|
513
+ models.uniq.each do |model|
514
+ model_class = register.get_class_without_register(model)
515
+ import_model_with_root_error(model_class)
678
516
 
679
- if Utils.present?(children)
680
- instance.value_set_for(attr.name)
681
- else
682
- children = nil
683
- values = Lutaml::Model::UninitializedClass.instance
517
+ @model.public_send(method, model_class)
684
518
  end
519
+ end
685
520
 
686
- children&.each do |child|
687
- if !rule.using_custom_methods? && attr.type <= Serialize
688
- cast_options = options.except(:mappings)
689
- cast_options[:polymorphic] = rule.polymorphic if rule.polymorphic
521
+ @models_imported = true
522
+ end
690
523
 
691
- values << attr.cast(child, :xml, cast_options)
692
- elsif attr.raw?
693
- values << inner_xml_of(child)
694
- else
695
- return nil if rule.render_nil_as_nil? && child.nil_element?
524
+ def ensure_choice_imports!(register_id = nil)
525
+ return if @choices_imported
696
526
 
697
- text = child.nil_element? ? nil : (child&.text&.+ child&.cdata)
698
- values << text
699
- end
527
+ register_id ||= Lutaml::Model::Config.default_register
528
+ register = Lutaml::Model::GlobalRegister.lookup(register_id)
529
+ importable_choices.each do |choice, choice_imports|
530
+ choice_imports.each do |method, models|
531
+ models.uniq!
532
+ choice.public_send(method, register.get_class_without_register(models.shift)) until models.empty?
700
533
  end
701
-
702
- normalized_value_for_attr(values, attr)
703
534
  end
704
- end
705
-
706
- def handle_cdata(children)
707
- values = children.map do |child|
708
- child.cdata_children&.map(&:text)
709
- end.flatten
710
-
711
- children.count > 1 ? values : values.first
712
- end
713
-
714
- def normalized_value_for_attr(values, attr)
715
- # for xml collection true cases like
716
- # <store><items /></store>
717
- # <store><items xsi:nil="true"/></store>
718
- # <store><items></items></store>
719
- #
720
- # these are considered empty collection
721
- return [] if attr&.collection? && [[nil], [""]].include?(values)
722
- return values if attr&.collection?
723
535
 
724
- values.is_a?(Array) ? values.first : values
536
+ @choices_imported = true
725
537
  end
726
538
 
727
- def apply_hash_mapping(doc, instance, format, options = {})
728
- mappings = options[:mappings] || mappings_for(format).mappings
729
- mappings.each do |rule|
730
- raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
731
-
732
- attr = attribute_for_rule(rule)
733
- next if attr&.derived?
734
-
735
- names = rule.multiple_mappings? ? rule.name : [rule.name]
736
-
737
- values = names.collect do |rule_name|
738
- if rule.root_mapping?
739
- doc
740
- elsif rule.raw_mapping?
741
- adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
742
- adapter.new(doc).public_send(:"to_#{format}")
743
- elsif doc.key?(rule_name.to_s)
744
- doc[rule_name.to_s]
745
- elsif doc.key?(rule_name.to_sym)
746
- doc[rule_name.to_sym]
747
- elsif attr&.default_set?
748
- attr&.default
749
- else
750
- Lutaml::Model::UninitializedClass.instance
751
- end
752
- end.compact
753
-
754
- value = values.find { |v| Utils.initialized?(v) } || values.first
755
-
756
- value = apply_value_map(value, rule.value_map(:from, options), attr)
757
-
758
- if rule.using_custom_methods?
759
- if Utils.present?(value)
760
- value = new.send(rule.custom_methods[:from], instance, value)
761
- end
762
-
763
- next
539
+ def setup_trace_point
540
+ @trace ||= TracePoint.new(:end) do |_tp|
541
+ if include?(Lutaml::Model::Serialize)
542
+ @finalized = true
543
+ @trace.disable
764
544
  end
765
-
766
- value = translate_mappings(value, rule.hash_mappings, attr, format)
767
-
768
- cast_options = {}
769
- cast_options[:polymorphic] = rule.polymorphic if rule.polymorphic
770
-
771
- value = attr.cast(value, format, cast_options) unless rule.hash_mappings
772
- attr.valid_collection!(value, self)
773
-
774
- rule.deserialize(instance, value, attributes, self)
775
545
  end
776
-
777
- instance
546
+ @trace.enable unless @trace.enabled?
778
547
  end
779
548
 
780
- def normalize_xml_value(value, rule, attr, options = {})
781
- value = [value].compact if attr&.collection? && !value.is_a?(Array) && !value.nil?
549
+ def finalized?
550
+ @finalized
551
+ end
552
+ end
782
553
 
783
- return value unless cast_value?(attr, rule)
554
+ def self.register_format_mapping_method(format)
555
+ method_name = format == :hash ? :hsh : format
784
556
 
785
- options.merge(caller_class: self, mixed_content: rule.mixed_content)
786
- attr.cast(
787
- value,
788
- :xml,
789
- options,
790
- )
557
+ ::Lutaml::Model::Serialize::ClassMethods.define_method(method_name) do |&block|
558
+ process_mapping(format, &block)
791
559
  end
560
+ end
792
561
 
793
- def cast_value?(attr, rule)
794
- attr &&
795
- !rule.raw_mapping? &&
796
- !rule.content_mapping? &&
797
- !rule.custom_methods[:from]
562
+ def self.register_from_format_method(format)
563
+ ClassMethods.define_method(:"from_#{format}") do |data, options = {}|
564
+ from(format, data, options)
798
565
  end
799
566
 
800
- def text_hash?(attr, value)
801
- return false unless value.is_a?(Hash)
802
- return value.one? && value.text? unless attr
803
-
804
- !(attr.type <= Serialize) && attr.type != Lutaml::Model::Type::Hash
567
+ ClassMethods.define_method(:"of_#{format}") do |doc, options = {}|
568
+ of(format, doc, options)
805
569
  end
570
+ end
806
571
 
807
- def ensure_utf8(value)
808
- case value
809
- when String
810
- value.encode("UTF-8", invalid: :replace, undef: :replace,
811
- replace: "")
812
- when Array
813
- value.map { |v| ensure_utf8(v) }
814
- when Hash
815
- value.transform_keys do |k|
816
- ensure_utf8(k)
817
- end.transform_values do |v|
818
- ensure_utf8(v)
819
- end
820
- else
821
- value
822
- end
572
+ def self.register_to_format_method(format)
573
+ ClassMethods.define_method(:"to_#{format}") do |instance, options = {}|
574
+ to(format, instance, options)
823
575
  end
824
576
 
825
- def validate_sequence!(element_order)
826
- mapping_sequence = mappings_for(:xml).element_sequence
827
- current_order = element_order.filter_map(&:element_tag)
828
-
829
- mapping_sequence.each do |mapping|
830
- mapping.validate_content!(current_order)
831
- end
577
+ ClassMethods.define_method(:"as_#{format}") do |instance, options = {}|
578
+ as(format, instance, options)
832
579
  end
833
580
 
834
- private
835
-
836
- def inner_xml_of(node)
837
- case node
838
- when XmlAdapter::XmlElement
839
- node.inner_xml
840
- else
841
- node.children.map(&:to_xml).join
842
- end
581
+ define_method(:"to_#{format}") do |options = {}|
582
+ to_format(format, options)
843
583
  end
844
584
  end
845
585
 
846
- attr_accessor :element_order, :schema_location, :encoding
586
+ attr_accessor :element_order, :schema_location, :encoding, :__register
847
587
  attr_writer :ordered, :mixed
848
588
 
849
589
  def initialize(attrs = {}, options = {})
850
590
  @using_default = {}
851
-
852
591
  return unless self.class.attributes
853
592
 
854
- if attrs.is_a?(Lutaml::Model::MappingHash)
855
- @ordered = attrs.ordered?
856
- @element_order = attrs.item_order
857
- end
858
-
859
- if attrs.key?(:schema_location)
860
- self.schema_location = attrs[:schema_location]
861
- end
862
-
863
- self.class.attributes.each do |name, attr|
864
- next if attr.derived?
865
-
866
- value = if attrs.key?(name) || attrs.key?(name.to_s)
867
- attr_value(attrs, name, attr)
868
- elsif attr.default_set?
869
- using_default_for(name)
870
- attr.default
871
- else
872
- Lutaml::Model::UninitializedClass.instance
873
- end
593
+ @__register = extract_register_id(attrs, options)
594
+ set_ordering(attrs)
595
+ set_schema_location(attrs)
596
+ initialize_attributes(attrs, options)
597
+ end
874
598
 
875
- default = using_default?(name)
876
- value = self.class.apply_value_map(value, value_map(options), attr)
877
- public_send(:"#{name}=", self.class.ensure_utf8(value))
878
- using_default_for(name) if default
879
- end
599
+ def extract_register_id(attrs, options)
600
+ register = attrs&.dig(:__register) || options&.dig(:register)
601
+ self.class.extract_register_id(register)
880
602
  end
881
603
 
882
604
  def value_map(options)
@@ -887,30 +609,9 @@ module Lutaml
887
609
  }
888
610
  end
889
611
 
890
- def attr_value(attrs, name, attr_rule)
891
- value = if attrs.key?(name.to_sym)
892
- attrs[name.to_sym]
893
- elsif attrs.key?(name.to_s)
894
- attrs[name.to_s]
895
- else
896
- attr_rule.default
897
- end
898
-
899
- if attr_rule.collection? || value.is_a?(Array)
900
- value&.map do |v|
901
- if v.is_a?(Hash)
902
- attr_rule.type.new(v)
903
- else
904
- # TODO: This code is problematic because Type.cast does not know
905
- # about all the types.
906
- Lutaml::Model::Type.cast(v, attr_rule.type)
907
- end
908
- end
909
- else
910
- # TODO: This code is problematic because Type.cast does not know
911
- # about all the types.
912
- Lutaml::Model::Type.cast(value, attr_rule.type)
913
- end
612
+ def attr_value(attrs, name, attribute)
613
+ value = Utils.fetch_str_or_sym(attrs, name, attribute.default(__register))
614
+ attribute.cast_value(value, __register)
914
615
  end
915
616
 
916
617
  def using_default_for(attribute_name)
@@ -926,7 +627,7 @@ module Lutaml
926
627
  end
927
628
 
928
629
  def method_missing(method_name, *args)
929
- if method_name.to_s.end_with?("=") && self.class.attributes.key?(method_name.to_s.chomp("=").to_sym)
630
+ if method_name.to_s.end_with?("=") && attribute_exist?(method_name)
930
631
  define_singleton_method(method_name) do |value|
931
632
  instance_variable_set(:"@#{method_name.to_s.chomp('=')}", value)
932
633
  end
@@ -936,6 +637,17 @@ module Lutaml
936
637
  end
937
638
  end
938
639
 
640
+ def respond_to_missing?(method_name, include_private = false)
641
+ (method_name.to_s.end_with?("=") && attribute_exist?(method_name)) ||
642
+ super
643
+ end
644
+
645
+ def attribute_exist?(name)
646
+ name = name.to_s.chomp("=").to_sym if name.end_with?("=")
647
+
648
+ self.class.attributes.key?(name)
649
+ end
650
+
939
651
  def validate_attribute!(attr_name)
940
652
  attr = self.class.attributes[attr_name]
941
653
  value = instance_variable_get(:"@#{attr_name}")
@@ -959,27 +671,63 @@ module Lutaml
959
671
  end
960
672
 
961
673
  def pretty_print_instance_variables
962
- (instance_variables - %i[@using_default]).sort
674
+ (instance_variables - %i[@using_default @__register]).sort
963
675
  end
964
676
 
965
677
  def to_yaml_hash
966
678
  self.class.as_yaml(self)
967
679
  end
968
680
 
969
- Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
970
- define_method(:"to_#{format}") do |options = {}|
971
- adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
972
- raise Lutaml::Model::NoRootMappingError.new(self.class) unless self.class.root?
973
-
974
- representation = if format == :xml
975
- self
976
- else
977
- self.class.hash_representation(self, format,
978
- options)
979
- end
980
-
981
- options[:parse_encoding] = encoding if encoding
982
- adapter.new(representation).public_send(:"to_#{format}", options)
681
+ def to_format(format, options = {})
682
+ validate_root_mapping!(format, options)
683
+
684
+ options[:parse_encoding] = encoding if encoding
685
+ self.class.to(format, self, options)
686
+ end
687
+
688
+ private
689
+
690
+ def validate_root_mapping!(format, options)
691
+ return if format != :xml
692
+ return if options[:collection] || self.class.root?
693
+
694
+ raise Lutaml::Model::NoRootMappingError.new(self.class)
695
+ end
696
+
697
+ def set_ordering(attrs)
698
+ return unless attrs.respond_to?(:ordered?)
699
+
700
+ @ordered = attrs.ordered?
701
+ @element_order = attrs.item_order
702
+ end
703
+
704
+ def set_schema_location(attrs)
705
+ return unless attrs.key?(:schema_location)
706
+
707
+ self.schema_location = attrs[:schema_location]
708
+ end
709
+
710
+ def initialize_attributes(attrs, options = {})
711
+ self.class.attributes.each do |name, attr|
712
+ next if attr.derived?
713
+
714
+ value = determine_value(attrs, name, attr)
715
+
716
+ default = using_default?(name)
717
+ value = self.class.apply_value_map(value, value_map(options), attr)
718
+ public_send(:"#{name}=", self.class.ensure_utf8(value))
719
+ using_default_for(name) if default
720
+ end
721
+ end
722
+
723
+ def determine_value(attrs, name, attr)
724
+ if attrs.key?(name) || attrs.key?(name.to_s)
725
+ attr_value(attrs, name, attr)
726
+ elsif attr.default_set?(__register)
727
+ using_default_for(name)
728
+ attr.default(__register)
729
+ else
730
+ Lutaml::Model::UninitializedClass.instance
983
731
  end
984
732
  end
985
733
  end