lutaml-model 0.7.1 → 0.7.2

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 (124) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.rubocop_todo.yml +49 -48
  4. data/Gemfile +4 -1
  5. data/README.adoc +791 -143
  6. data/RELEASE_NOTES.adoc +346 -0
  7. data/docs/custom_adapters.adoc +144 -0
  8. data/lib/lutaml/model/attribute.rb +17 -11
  9. data/lib/lutaml/model/config.rb +48 -42
  10. data/lib/lutaml/model/error/polymorphic_error.rb +7 -2
  11. data/lib/lutaml/model/format_registry.rb +41 -0
  12. data/lib/lutaml/model/hash/document.rb +11 -0
  13. data/lib/lutaml/model/hash/mapping.rb +19 -0
  14. data/lib/lutaml/model/hash/mapping_rule.rb +9 -0
  15. data/lib/lutaml/model/hash/standard_adapter.rb +17 -0
  16. data/lib/lutaml/model/hash/transform.rb +8 -0
  17. data/lib/lutaml/model/hash.rb +21 -0
  18. data/lib/lutaml/model/json/document.rb +11 -0
  19. data/lib/lutaml/model/json/mapping.rb +19 -0
  20. data/lib/lutaml/model/json/mapping_rule.rb +9 -0
  21. data/lib/lutaml/model/{json_adapter → json}/multi_json_adapter.rb +4 -5
  22. data/lib/lutaml/model/{json_adapter/standard_json_adapter.rb → json/standard_adapter.rb} +5 -3
  23. data/lib/lutaml/model/json/transform.rb +8 -0
  24. data/lib/lutaml/model/json.rb +21 -0
  25. data/lib/lutaml/model/key_value_document.rb +27 -0
  26. data/lib/lutaml/model/mapping/key_value_mapping.rb +8 -4
  27. data/lib/lutaml/model/mapping/mapping.rb +13 -0
  28. data/lib/lutaml/model/mapping/mapping_rule.rb +7 -6
  29. data/lib/lutaml/model/serialization_adapter.rb +22 -0
  30. data/lib/lutaml/model/serialize.rb +146 -521
  31. data/lib/lutaml/model/services/logger.rb +54 -0
  32. data/lib/lutaml/model/services/transformer.rb +48 -0
  33. data/lib/lutaml/model/services.rb +2 -0
  34. data/lib/lutaml/model/toml/document.rb +11 -0
  35. data/lib/lutaml/model/toml/mapping.rb +27 -0
  36. data/lib/lutaml/model/toml/mapping_rule.rb +9 -0
  37. data/lib/lutaml/model/{toml_adapter → toml}/toml_rb_adapter.rb +3 -3
  38. data/lib/lutaml/model/toml/tomlib_adapter.rb +19 -0
  39. data/lib/lutaml/model/toml/transform.rb +8 -0
  40. data/lib/lutaml/model/toml.rb +30 -0
  41. data/lib/lutaml/model/transform/key_value_transform.rb +291 -0
  42. data/lib/lutaml/model/transform/xml_transform.rb +239 -0
  43. data/lib/lutaml/model/transform.rb +78 -0
  44. data/lib/lutaml/model/type/value.rb +6 -9
  45. data/lib/lutaml/model/uninitialized_class.rb +1 -1
  46. data/lib/lutaml/model/utils.rb +30 -0
  47. data/lib/lutaml/model/version.rb +1 -1
  48. data/lib/lutaml/model/{xml_adapter → xml}/builder/nokogiri.rb +2 -2
  49. data/lib/lutaml/model/{xml_adapter → xml}/builder/oga.rb +10 -10
  50. data/lib/lutaml/model/{xml_adapter → xml}/builder/ox.rb +1 -1
  51. data/lib/lutaml/model/{xml_adapter/xml_document.rb → xml/document.rb} +6 -7
  52. data/lib/lutaml/model/xml/element.rb +32 -0
  53. data/lib/lutaml/model/xml/mapping.rb +410 -0
  54. data/lib/lutaml/model/xml/mapping_rule.rb +141 -0
  55. data/lib/lutaml/model/xml/nokogiri_adapter.rb +232 -0
  56. data/lib/lutaml/model/{xml_adapter → xml}/oga/document.rb +1 -1
  57. data/lib/lutaml/model/{xml_adapter → xml}/oga/element.rb +3 -1
  58. data/lib/lutaml/model/xml/oga_adapter.rb +171 -0
  59. data/lib/lutaml/model/xml/ox_adapter.rb +215 -0
  60. data/lib/lutaml/model/xml/transform.rb +8 -0
  61. data/lib/lutaml/model/{xml_adapter → xml}/xml_attribute.rb +1 -1
  62. data/lib/lutaml/model/{xml_adapter → xml}/xml_element.rb +6 -3
  63. data/lib/lutaml/model/{xml_adapter → xml}/xml_namespace.rb +1 -1
  64. data/lib/lutaml/model/xml.rb +31 -0
  65. data/lib/lutaml/model/xml_adapter/element.rb +11 -25
  66. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +6 -223
  67. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +13 -163
  68. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +10 -207
  69. data/lib/lutaml/model/yaml/document.rb +10 -0
  70. data/lib/lutaml/model/yaml/mapping.rb +19 -0
  71. data/lib/lutaml/model/yaml/mapping_rule.rb +9 -0
  72. data/lib/lutaml/model/{yaml_adapter/standard_yaml_adapter.rb → yaml/standard_adapter.rb} +4 -3
  73. data/lib/lutaml/model/yaml/transform.rb +8 -0
  74. data/lib/lutaml/model/yaml.rb +21 -0
  75. data/lib/lutaml/model.rb +39 -4
  76. data/lutaml-model.gemspec +0 -4
  77. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +4 -4
  78. data/spec/lutaml/model/cdata_spec.rb +7 -7
  79. data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +598 -0
  80. data/spec/lutaml/model/custom_vobject_adapter_spec.rb +1226 -0
  81. data/spec/lutaml/model/group_spec.rb +18 -7
  82. data/spec/lutaml/model/hash/adapter_spec.rb +255 -0
  83. data/spec/lutaml/model/json_adapter_spec.rb +6 -6
  84. data/spec/lutaml/model/key_value_mapping_spec.rb +25 -1
  85. data/spec/lutaml/model/mixed_content_spec.rb +24 -24
  86. data/spec/lutaml/model/multiple_mapping_spec.rb +5 -5
  87. data/spec/lutaml/model/ordered_content_spec.rb +6 -6
  88. data/spec/lutaml/model/polymorphic_spec.rb +178 -0
  89. data/spec/lutaml/model/root_mappings_spec.rb +3 -3
  90. data/spec/lutaml/model/schema/xml_compiler_spec.rb +6 -6
  91. data/spec/lutaml/model/serializable_spec.rb +179 -103
  92. data/spec/lutaml/model/toml_adapter_spec.rb +6 -6
  93. data/spec/lutaml/model/toml_spec.rb +51 -0
  94. data/spec/lutaml/model/transformation_spec.rb +72 -15
  95. data/spec/lutaml/model/uninitialized_class_spec.rb +96 -0
  96. data/spec/lutaml/model/xml/namespace_spec.rb +57 -0
  97. data/spec/lutaml/model/xml/xml_element_spec.rb +1 -1
  98. data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +2 -2
  99. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +2 -2
  100. data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +2 -2
  101. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
  102. data/spec/lutaml/model/xml_adapter_spec.rb +6 -6
  103. data/spec/lutaml/model/xml_mapping_rule_spec.rb +3 -3
  104. data/spec/lutaml/model/xml_mapping_spec.rb +26 -14
  105. data/spec/lutaml/model/xml_spec.rb +63 -0
  106. data/spec/lutaml/model/yaml_adapter_spec.rb +3 -5
  107. data/spec/spec_helper.rb +3 -3
  108. metadata +64 -59
  109. data/lib/lutaml/model/json_adapter/json_document.rb +0 -20
  110. data/lib/lutaml/model/json_adapter/json_object.rb +0 -28
  111. data/lib/lutaml/model/loggable.rb +0 -15
  112. data/lib/lutaml/model/mapping/json_mapping.rb +0 -17
  113. data/lib/lutaml/model/mapping/toml_mapping.rb +0 -25
  114. data/lib/lutaml/model/mapping/xml_mapping.rb +0 -389
  115. data/lib/lutaml/model/mapping/xml_mapping_rule.rb +0 -139
  116. data/lib/lutaml/model/mapping/yaml_mapping.rb +0 -17
  117. data/lib/lutaml/model/mapping.rb +0 -14
  118. data/lib/lutaml/model/toml_adapter/toml_document.rb +0 -20
  119. data/lib/lutaml/model/toml_adapter/toml_object.rb +0 -28
  120. data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +0 -20
  121. data/lib/lutaml/model/toml_adapter.rb +0 -6
  122. data/lib/lutaml/model/yaml_adapter/yaml_document.rb +0 -20
  123. data/lib/lutaml/model/yaml_adapter/yaml_object.rb +0 -28
  124. data/lib/lutaml/model/yaml_adapter.rb +0 -8
@@ -0,0 +1,54 @@
1
+ module Lutaml
2
+ module Model
3
+ class Logger
4
+ def self.warn(message)
5
+ new.call(message, :warn)
6
+ end
7
+
8
+ # @param [String] old: The name of the deprecated class or method
9
+ # @param [String] replacement: The name of the replacement class or method
10
+ #
11
+ # Outputs a warning message that
12
+ # Usage of `old` name is deprecated will be removed in the next major
13
+ # release. Please use the `replacement`` instead.
14
+ def self.warn_future_deprication(old:, replacement:)
15
+ warn("Usage of `#{old}` is deprecated and will be removed in the next major release. Please use `#{replacement}` instead.")
16
+ end
17
+
18
+ # @param [String] name
19
+ # @param [String] caller_file
20
+ # @param [Integer] caller_line
21
+ #
22
+ # Outputs a warning message that
23
+ # `<name>` is handled by default. No need to explicitly
24
+ # define at `<caller_file>:<caller_line>`.
25
+ def self.warn_auto_handling(name:, caller_file:, caller_line:)
26
+ warn("`#{name}` is handled by default. No need to explecitly define at `#{caller_file}:#{caller_line}`")
27
+ end
28
+
29
+ def call(message, type)
30
+ Warning.warn format_message(message, type)
31
+ end
32
+
33
+ private
34
+
35
+ def colorize(message, type)
36
+ type_color = {
37
+ error: 31, # Red: 31
38
+ success: 32, # Green: 32
39
+ warn: 33, # Yellow: 33
40
+ }
41
+
42
+ io = type == :warn ? $stderr : $stdout
43
+ return message unless io.tty?
44
+
45
+ color = type_color[type]
46
+ "\e[#{color}m#{message}\e[0m"
47
+ end
48
+
49
+ def format_message(message, type)
50
+ colorize("\n[Lutaml::Model] #{type.upcase}: #{message}\n", type)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,48 @@
1
+ module Lutaml
2
+ module Model
3
+ class Transformer
4
+ class << self
5
+ def call(value, rule, attribute)
6
+ new(rule, attribute).call(value)
7
+ end
8
+ end
9
+
10
+ attr_reader :rule, :attribute
11
+
12
+ def initialize(rule, attribute)
13
+ @rule = rule
14
+ @attribute = attribute
15
+ end
16
+
17
+ def call(value)
18
+ transformation_methods.reduce(value) do |transformed_value, method|
19
+ method.call(transformed_value)
20
+ end
21
+ end
22
+ end
23
+
24
+ class ImportTransformer < Transformer
25
+ # Precedene of transformations:
26
+ # 1. Rule transform
27
+ # 2. Attribute transform
28
+ def transformation_methods
29
+ [
30
+ rule.transform[:import],
31
+ attribute&.transform&.[](:import),
32
+ ].compact
33
+ end
34
+ end
35
+
36
+ class ExportTransformer < Transformer
37
+ # Precedene of transformations (reverse order):
38
+ # 1. Attribute transform
39
+ # 2. Rule transform
40
+ def transformation_methods
41
+ [
42
+ attribute&.transform&.[](:export),
43
+ rule.transform[:export],
44
+ ].compact
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,2 @@
1
+ require_relative "services/logger"
2
+ require_relative "services/transformer"
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Toml
6
+ # Base class for TOML documents
7
+ class Document < Lutaml::Model::KeyValueDocument
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,27 @@
1
+ require_relative "../mapping/key_value_mapping"
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Toml
6
+ class Mapping < Lutaml::Model::KeyValueMapping
7
+ def initialize
8
+ super(:toml)
9
+ end
10
+
11
+ def deep_dup
12
+ self.class.new.tap do |new_mapping|
13
+ new_mapping.instance_variable_set(:@mappings, duplicate_mappings)
14
+ end
15
+ end
16
+
17
+ def validate!(key, to, with, render_nil, render_empty)
18
+ super
19
+
20
+ if [true, :as_nil].include?(render_nil) || render_empty == :as_nil
21
+ raise IncorrectMappingArgumentsError, "nil values are not supported in toml format"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "../mapping/key_value_mapping_rule"
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Toml
6
+ class MappingRule < Lutaml::Model::KeyValueMappingRule; end
7
+ end
8
+ end
9
+ end
@@ -1,10 +1,10 @@
1
1
  require "toml-rb"
2
- require_relative "toml_document"
2
+ require_relative "document"
3
3
 
4
4
  module Lutaml
5
5
  module Model
6
- module TomlAdapter
7
- class TomlRbAdapter < TomlDocument
6
+ module Toml
7
+ class TomlRbAdapter < Document
8
8
  def self.parse(toml, _options = {})
9
9
  TomlRB.parse(toml)
10
10
  end
@@ -0,0 +1,19 @@
1
+ require "tomlib"
2
+ require_relative "document"
3
+
4
+ module Lutaml
5
+ module Model
6
+ module Toml
7
+ class TomlibAdapter < Document
8
+ def self.parse(toml, _options = {})
9
+ Tomlib.load(toml)
10
+ end
11
+
12
+ def to_toml(*)
13
+ Tomlib.dump(to_h)
14
+ # Tomlib::Generator.new(to_h).toml_str
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ module Lutaml
2
+ module Model
3
+ module Toml
4
+ class Transform < Lutaml::Model::KeyValueTransform
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Toml
6
+ def self.detect_toml_adapter
7
+ return :tomlib if Object.const_defined?(:Tomlib)
8
+ return :toml_rb if Object.const_defined?(:TomlRb)
9
+
10
+ nil
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ require_relative "toml/document"
17
+ require_relative "toml/mapping"
18
+ require_relative "toml/mapping_rule"
19
+ require_relative "toml/transform"
20
+
21
+ Lutaml::Model::FormatRegistry.register(
22
+ :toml,
23
+ mapping_class: Lutaml::Model::Toml::Mapping,
24
+ adapter_class: nil,
25
+ transformer: Lutaml::Model::Toml::Transform,
26
+ )
27
+
28
+ if (adapter = Lutaml::Model::Toml.detect_toml_adapter)
29
+ Lutaml::Model::Config.toml_adapter_type = adapter
30
+ end
@@ -0,0 +1,291 @@
1
+ module Lutaml
2
+ module Model
3
+ class KeyValueTransform < Lutaml::Model::Transform
4
+ def data_to_model(data, format, options = {})
5
+ instance = model_class.new
6
+ mappings = extract_mappings(options, format)
7
+
8
+ mappings.each do |rule|
9
+ process_mapping_rule(data, instance, format, rule, options)
10
+ end
11
+
12
+ instance
13
+ end
14
+
15
+ def model_to_data(instance, format, options = {})
16
+ mappings = mappings_for(format).mappings
17
+
18
+ mappings.each_with_object({}) do |rule, hash|
19
+ next unless valid_mapping?(rule, options)
20
+ next handle_delegate(instance, rule, hash, format) if rule.delegate
21
+
22
+ process_mapping_for_instance(instance, hash, format, rule, options)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def process_mapping_for_instance(instance, hash, format, rule, options)
29
+ if rule.custom_methods[:to]
30
+ return instance.send(rule.custom_methods[:to], instance, hash)
31
+ end
32
+
33
+ attribute = attributes[rule.to]
34
+ value = rule.serialize(instance)
35
+
36
+ return handle_raw_mapping(hash, value, format, options) if rule.raw_mapping?
37
+ return handle_root_mappings(hash, value, format, rule, attribute) if rule.root_mapping?
38
+
39
+ value = ExportTransformer.call(value, rule, attribute)
40
+
41
+ value = serialize_value(value, rule, attribute, format, options)
42
+
43
+ return unless rule.render?(value, instance)
44
+
45
+ value = apply_value_map(value, rule.value_map(:to, options), attribute)
46
+
47
+ hash[rule_from_name(rule)] = value
48
+ end
49
+
50
+ def valid_mapping?(rule, options)
51
+ only = options[:only]
52
+ except = options[:except]
53
+ name = rule.to
54
+
55
+ (except.nil? || !except.include?(name)) &&
56
+ (only.nil? || only.include?(name))
57
+ end
58
+
59
+ def handle_raw_mapping(hash, value, format, options)
60
+ result = Lutaml::Model::Config.adapter_for(format).parse(value, options)
61
+
62
+ hash.merge!(result)
63
+ end
64
+
65
+ def handle_root_mappings(hash, value, format, rule, attr)
66
+ hash.merge!(
67
+ generate_hash_from_child_mappings(
68
+ attr,
69
+ value,
70
+ format,
71
+ rule.root_mappings,
72
+ ),
73
+ )
74
+ end
75
+
76
+ def serialize_value(value, rule, attr, format, options)
77
+ return attr.serialize(value, format, options) unless rule.child_mappings
78
+
79
+ generate_hash_from_child_mappings(attr, value, format, rule.child_mappings)
80
+ end
81
+
82
+ def rule_from_name(rule)
83
+ rule.multiple_mappings? ? rule.from.first.to_s : rule.from.to_s
84
+ end
85
+
86
+ def generate_hash_from_child_mappings(attr, value, format, child_mappings)
87
+ return value unless child_mappings
88
+
89
+ hash = {}
90
+
91
+ generate_remaining_mappings_for_value(child_mappings, value, format)
92
+
93
+ value.each do |child_obj|
94
+ rules = attr.type.mappings_for(format)
95
+
96
+ hash.merge!(
97
+ extract_hash_for_child_mapping(child_mappings, child_obj, rules),
98
+ )
99
+ end
100
+
101
+ hash
102
+ end
103
+
104
+ # Generates remaining child mappings for all attributes when only
105
+ # the :key mapping (e.g., { name: :key }) is provided.
106
+ # If any additional mappings (e.g., { name: :key, id: :identifier })
107
+ # are specified, no additional child mappings will be generated.
108
+ def generate_remaining_mappings_for_value(child_mappings, value, format)
109
+ return if child_mappings.values != [:key]
110
+
111
+ klass = value.first.class
112
+ mappings = klass.mappings_for(format)
113
+
114
+ klass.attributes.each_key do |name|
115
+ next if Utils.string_or_symbol_key?(child_mappings, name)
116
+
117
+ child_mappings[name.to_sym] = child_mapping_for(name, mappings)
118
+ end
119
+ end
120
+
121
+ def child_mapping_for(name, mappings)
122
+ mappings.find_by_to(name)&.name.to_s || name.to_s
123
+ end
124
+
125
+ def extract_hash_for_child_mapping(child_mappings, child_obj, rules)
126
+ key = nil
127
+ value = {}
128
+
129
+ child_mappings.each do |attr_name, path|
130
+ rule = rules.find_by_to(attr_name)
131
+
132
+ attr_value = normalize_attribute_value(child_obj.send(attr_name))
133
+
134
+ next unless rule&.render?(attr_value, nil)
135
+ next key = attr_value if path == :key
136
+
137
+ value = extract_hash_value_for_child_mapping(path, attr_value, value)
138
+ end
139
+
140
+ value = nil if value.empty?
141
+ { key => value }
142
+ end
143
+
144
+ def normalize_attribute_value(value)
145
+ if value.is_a?(Lutaml::Model::Serialize)
146
+ value.to_hash
147
+ elsif value.is_a?(Array) && value.first.is_a?(Lutaml::Model::Serialize)
148
+ value.map(&:to_hash)
149
+ else
150
+ value
151
+ end
152
+ end
153
+
154
+ def extract_hash_value_for_child_mapping(path, value, map_value)
155
+ return value if path == :value
156
+
157
+ path = [path] unless path.is_a?(Array)
158
+ path[0...-1].inject(map_value) do |acc, k|
159
+ acc[k.to_s] ||= {}
160
+ end.public_send(:[]=, path.last.to_s, value)
161
+
162
+ map_value
163
+ end
164
+
165
+ def handle_delegate(instance, rule, hash, format)
166
+ value = extract_value_for_delegate(instance, rule)
167
+ return if value.nil? && !rule.render_nil
168
+
169
+ attribute = instance.send(rule.delegate).class.attributes[rule.to]
170
+ hash[rule_from_name(rule)] = attribute.serialize(value, format)
171
+ end
172
+
173
+ def extract_value_for_delegate(instance, rule)
174
+ instance.send(rule.delegate).send(rule.to)
175
+ end
176
+
177
+ def extract_mappings(options, format)
178
+ options[:mappings] || mappings_for(format).mappings
179
+ end
180
+
181
+ def process_mapping_rule(doc, instance, format, rule, options = {})
182
+ raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
183
+
184
+ attr = attribute_for_rule(rule)
185
+ return if attr&.derived?
186
+
187
+ value = extract_rule_value(doc, rule, format, attr)
188
+ value = apply_value_map(value, rule.value_map(:from, options), attr)
189
+
190
+ return process_custom_method(rule, instance, value) if rule.has_custom_method_for_deserialization?
191
+
192
+ value = translate_mappings(value, rule.hash_mappings, attr, format)
193
+ value = cast_value(value, attr, format, rule) unless rule.hash_mappings
194
+
195
+ attr.valid_collection!(value, context)
196
+ rule.deserialize(instance, value, attributes, self)
197
+ end
198
+
199
+ def extract_rule_value(doc, rule, format, attr)
200
+ rule_names = rule.multiple_mappings? ? rule.name : [rule.name]
201
+
202
+ rule_names.each do |rule_name|
203
+ value = rule_value_for(rule_name, doc, rule, format, attr)
204
+
205
+ return value if Utils.initialized?(value)
206
+ end
207
+
208
+ Lutaml::Model::UninitializedClass.instance
209
+ end
210
+
211
+ def rule_value_for(name, doc, rule, format, attr)
212
+ if rule.root_mapping?
213
+ doc
214
+ elsif rule.raw_mapping?
215
+ convert_to_format(doc, format)
216
+ elsif Utils.string_or_symbol_key?(doc, name)
217
+ Utils.fetch_with_string_or_symbol_key(doc, name)
218
+ elsif attr&.default_set?
219
+ attr.default
220
+ else
221
+ Lutaml::Model::UninitializedClass.instance
222
+ end
223
+ end
224
+
225
+ def convert_to_format(doc, format)
226
+ adapter = Lutaml::Model::Config.adapter_for(format)
227
+ adapter.new(doc).public_send(:"to_#{format}")
228
+ end
229
+
230
+ def process_custom_method(rule, instance, value)
231
+ return unless Utils.present?(value)
232
+
233
+ model_class.new.send(rule.custom_methods[:from], instance, value)
234
+ end
235
+
236
+ def cast_value(value, attr, format, rule)
237
+ cast_options = rule.polymorphic ? { polymorphic: rule.polymorphic } : {}
238
+ attr.cast(value, format, cast_options)
239
+ end
240
+
241
+ def translate_mappings(hash, child_mappings, attr, format)
242
+ return hash unless child_mappings
243
+
244
+ hash.map do |key, value|
245
+ process_child_mapping(key, value, child_mappings, attr, format, hash)
246
+ end
247
+ end
248
+
249
+ def process_child_mapping(key, value, child_mappings, attr, format, hash)
250
+ child_hash = build_child_hash(key, value, child_mappings, attr, format)
251
+
252
+ if only_keys_mapped?(child_mappings, hash)
253
+ child_hash.merge!(value)
254
+ end
255
+
256
+ map_child_data(child_hash, attr, format)
257
+ end
258
+
259
+ def build_child_hash(key, value, child_mappings, attr, format)
260
+ child_mappings.to_h do |attr_name, path|
261
+ attr_value = extract_attr_value(path, key, value)
262
+ attr_rule = attr.type.mappings_for(format).find_by_to(attr_name)
263
+ [attr_rule.from.to_s, attr_value]
264
+ end
265
+ end
266
+
267
+ def extract_attr_value(path, key, value)
268
+ case path
269
+ when :key then key
270
+ when :value then value
271
+ else
272
+ path = Array(path)
273
+ value.dig(*path.map(&:to_s))
274
+ end
275
+ end
276
+
277
+ def only_keys_mapped?(child_mappings, hash)
278
+ child_mappings.values == [:key] && hash.values.all?(Hash)
279
+ end
280
+
281
+ def map_child_data(child_hash, attr, format)
282
+ self.class.data_to_model(
283
+ attr.type,
284
+ child_hash,
285
+ format,
286
+ { mappings: attr.type.mappings_for(format).mappings },
287
+ )
288
+ end
289
+ end
290
+ end
291
+ end