lutaml-model 0.6.7 → 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.
- checksums.yaml +4 -4
- data/.github/workflows/dependent-repos-todo.json +7 -0
- data/.github/workflows/dependent-repos.json +17 -9
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +51 -65
- data/Gemfile +4 -1
- data/README.adoc +5083 -2612
- data/RELEASE_NOTES.adoc +346 -0
- data/docs/custom_adapters.adoc +144 -0
- data/lib/lutaml/model/attribute.rb +101 -16
- data/lib/lutaml/model/choice.rb +7 -0
- data/lib/lutaml/model/comparable_model.rb +48 -9
- data/lib/lutaml/model/config.rb +48 -42
- data/lib/lutaml/model/error/collection_count_out_of_range_error.rb +1 -1
- data/lib/lutaml/model/error/polymorphic_error.rb +14 -0
- data/lib/lutaml/model/error.rb +1 -0
- data/lib/lutaml/model/format_registry.rb +41 -0
- data/lib/lutaml/model/hash/document.rb +11 -0
- data/lib/lutaml/model/hash/mapping.rb +19 -0
- data/lib/lutaml/model/hash/mapping_rule.rb +9 -0
- data/lib/lutaml/model/hash/standard_adapter.rb +17 -0
- data/lib/lutaml/model/hash/transform.rb +8 -0
- data/lib/lutaml/model/hash.rb +21 -0
- data/lib/lutaml/model/json/document.rb +11 -0
- data/lib/lutaml/model/json/mapping.rb +19 -0
- data/lib/lutaml/model/json/mapping_rule.rb +9 -0
- data/lib/lutaml/model/{json_adapter → json}/multi_json_adapter.rb +4 -5
- data/lib/lutaml/model/{json_adapter/standard_json_adapter.rb → json/standard_adapter.rb} +5 -3
- data/lib/lutaml/model/json/transform.rb +8 -0
- data/lib/lutaml/model/json.rb +21 -0
- data/lib/lutaml/model/key_value_document.rb +27 -0
- data/lib/lutaml/model/{key_value_mapping.rb → mapping/key_value_mapping.rb} +64 -16
- data/lib/lutaml/model/{key_value_mapping_rule.rb → mapping/key_value_mapping_rule.rb} +18 -2
- data/lib/lutaml/model/mapping/mapping.rb +13 -0
- data/lib/lutaml/model/mapping/mapping_rule.rb +300 -0
- data/lib/lutaml/model/schema/xml_compiler.rb +15 -15
- data/lib/lutaml/model/sequence.rb +2 -2
- data/lib/lutaml/model/serialization_adapter.rb +22 -0
- data/lib/lutaml/model/serialize.rb +219 -444
- data/lib/lutaml/model/services/logger.rb +54 -0
- data/lib/lutaml/model/services/transformer.rb +48 -0
- data/lib/lutaml/model/services.rb +2 -0
- data/lib/lutaml/model/toml/document.rb +11 -0
- data/lib/lutaml/model/toml/mapping.rb +27 -0
- data/lib/lutaml/model/toml/mapping_rule.rb +9 -0
- data/lib/lutaml/model/{toml_adapter → toml}/toml_rb_adapter.rb +3 -3
- data/lib/lutaml/model/toml/tomlib_adapter.rb +19 -0
- data/lib/lutaml/model/toml/transform.rb +8 -0
- data/lib/lutaml/model/toml.rb +30 -0
- data/lib/lutaml/model/transform/key_value_transform.rb +291 -0
- data/lib/lutaml/model/transform/xml_transform.rb +239 -0
- data/lib/lutaml/model/transform.rb +78 -0
- data/lib/lutaml/model/type/date.rb +1 -1
- data/lib/lutaml/model/type/date_time.rb +2 -2
- data/lib/lutaml/model/type/hash.rb +1 -1
- data/lib/lutaml/model/type/time.rb +2 -2
- data/lib/lutaml/model/type/time_without_date.rb +2 -2
- data/lib/lutaml/model/type/value.rb +6 -9
- data/lib/lutaml/model/uninitialized_class.rb +64 -0
- data/lib/lutaml/model/utils.rb +44 -0
- data/lib/lutaml/model/validation.rb +1 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/{xml_adapter → xml}/builder/nokogiri.rb +2 -2
- data/lib/lutaml/model/{xml_adapter → xml}/builder/oga.rb +10 -10
- data/lib/lutaml/model/{xml_adapter → xml}/builder/ox.rb +1 -1
- data/lib/lutaml/model/{xml_adapter/xml_document.rb → xml/document.rb} +41 -21
- data/lib/lutaml/model/xml/element.rb +32 -0
- data/lib/lutaml/model/xml/mapping.rb +410 -0
- data/lib/lutaml/model/xml/mapping_rule.rb +141 -0
- data/lib/lutaml/model/xml/nokogiri_adapter.rb +232 -0
- data/lib/lutaml/model/{xml_adapter → xml}/oga/document.rb +1 -1
- data/lib/lutaml/model/{xml_adapter → xml}/oga/element.rb +3 -1
- data/lib/lutaml/model/xml/oga_adapter.rb +171 -0
- data/lib/lutaml/model/xml/ox_adapter.rb +215 -0
- data/lib/lutaml/model/xml/transform.rb +8 -0
- data/lib/lutaml/model/{xml_adapter → xml}/xml_attribute.rb +1 -1
- data/lib/lutaml/model/{xml_adapter → xml}/xml_element.rb +23 -10
- data/lib/lutaml/model/{xml_adapter → xml}/xml_namespace.rb +1 -1
- data/lib/lutaml/model/xml.rb +31 -0
- data/lib/lutaml/model/xml_adapter/element.rb +11 -25
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +6 -223
- data/lib/lutaml/model/xml_adapter/oga_adapter.rb +13 -163
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +10 -207
- data/lib/lutaml/model/yaml/document.rb +10 -0
- data/lib/lutaml/model/yaml/mapping.rb +19 -0
- data/lib/lutaml/model/yaml/mapping_rule.rb +9 -0
- data/lib/lutaml/model/{yaml_adapter/standard_yaml_adapter.rb → yaml/standard_adapter.rb} +4 -3
- data/lib/lutaml/model/yaml/transform.rb +8 -0
- data/lib/lutaml/model/yaml.rb +21 -0
- data/lib/lutaml/model.rb +40 -4
- data/lutaml-model.gemspec +0 -4
- data/spec/benchmarks/xml_parsing_benchmark_spec.rb +7 -7
- data/spec/fixtures/person.rb +5 -5
- data/spec/lutaml/model/attribute_spec.rb +37 -1
- data/spec/lutaml/model/cdata_spec.rb +9 -9
- data/spec/lutaml/model/collection_spec.rb +50 -2
- data/spec/lutaml/model/comparable_model_spec.rb +92 -27
- data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +598 -0
- data/spec/lutaml/model/custom_vobject_adapter_spec.rb +1226 -0
- data/spec/lutaml/model/defaults_spec.rb +1 -1
- data/spec/lutaml/model/enum_spec.rb +1 -1
- data/spec/lutaml/model/group_spec.rb +333 -20
- data/spec/lutaml/model/hash/adapter_spec.rb +255 -0
- data/spec/lutaml/model/json_adapter_spec.rb +6 -6
- data/spec/lutaml/model/key_value_mapping_spec.rb +65 -3
- data/spec/lutaml/model/mixed_content_spec.rb +24 -24
- data/spec/lutaml/model/multiple_mapping_spec.rb +5 -5
- data/spec/lutaml/model/ordered_content_spec.rb +6 -6
- data/spec/lutaml/model/polymorphic_spec.rb +526 -0
- data/spec/lutaml/model/render_empty_spec.rb +194 -0
- data/spec/lutaml/model/render_nil_spec.rb +206 -22
- data/spec/lutaml/model/root_mappings_spec.rb +3 -3
- data/spec/lutaml/model/schema/xml_compiler_spec.rb +6 -6
- data/spec/lutaml/model/serializable_spec.rb +179 -103
- data/spec/lutaml/model/simple_model_spec.rb +9 -9
- data/spec/lutaml/model/toml_adapter_spec.rb +6 -6
- data/spec/lutaml/model/toml_spec.rb +51 -0
- data/spec/lutaml/model/transformation_spec.rb +72 -15
- data/spec/lutaml/model/uninitialized_class_spec.rb +96 -0
- data/spec/lutaml/model/value_map_spec.rb +240 -0
- data/spec/lutaml/model/xml/namespace/nested_with_explicit_namespace_spec.rb +85 -0
- data/spec/lutaml/model/xml/namespace_spec.rb +57 -0
- data/spec/lutaml/model/xml/xml_element_spec.rb +93 -0
- data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
- data/spec/lutaml/model/xml_adapter_spec.rb +6 -6
- data/spec/lutaml/model/xml_mapping_rule_spec.rb +105 -5
- data/spec/lutaml/model/xml_mapping_spec.rb +70 -16
- data/spec/lutaml/model/xml_spec.rb +63 -0
- data/spec/lutaml/model/yaml_adapter_spec.rb +3 -5
- data/spec/sample_model_spec.rb +3 -3
- data/spec/spec_helper.rb +3 -3
- metadata +76 -59
- data/lib/lutaml/model/json_adapter/json_document.rb +0 -20
- data/lib/lutaml/model/json_adapter/json_object.rb +0 -28
- data/lib/lutaml/model/loggable.rb +0 -15
- data/lib/lutaml/model/mapping_rule.rb +0 -109
- data/lib/lutaml/model/toml_adapter/toml_document.rb +0 -20
- data/lib/lutaml/model/toml_adapter/toml_object.rb +0 -28
- data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +0 -20
- data/lib/lutaml/model/toml_adapter.rb +0 -6
- data/lib/lutaml/model/xml_mapping.rb +0 -307
- data/lib/lutaml/model/xml_mapping_rule.rb +0 -122
- data/lib/lutaml/model/yaml_adapter/yaml_document.rb +0 -20
- data/lib/lutaml/model/yaml_adapter/yaml_object.rb +0 -28
- data/lib/lutaml/model/yaml_adapter.rb +0 -8
@@ -1,12 +1,9 @@
|
|
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
|
-
require_relative "mapping_rule"
|
7
5
|
require_relative "mapping_hash"
|
8
|
-
require_relative "
|
9
|
-
require_relative "key_value_mapping"
|
6
|
+
# require_relative "mapping"
|
10
7
|
require_relative "json_adapter"
|
11
8
|
require_relative "comparable_model"
|
12
9
|
require_relative "schema_location"
|
@@ -15,6 +12,7 @@ require_relative "error"
|
|
15
12
|
require_relative "choice"
|
16
13
|
require_relative "sequence"
|
17
14
|
require_relative "liquefiable"
|
15
|
+
require_relative "transform"
|
18
16
|
|
19
17
|
module Lutaml
|
20
18
|
module Model
|
@@ -94,22 +92,15 @@ module Lutaml
|
|
94
92
|
end
|
95
93
|
end
|
96
94
|
|
97
|
-
|
98
|
-
|
99
|
-
if type.is_a?(Hash)
|
100
|
-
options[:method_name] = type[:method]
|
101
|
-
type = nil
|
102
|
-
end
|
103
|
-
|
104
|
-
attr = Attribute.new(name, type, options)
|
105
|
-
attributes[name] = attr
|
95
|
+
def define_attribute_methods(attr)
|
96
|
+
name = attr.name
|
106
97
|
|
107
98
|
if attr.enum?
|
108
99
|
add_enum_methods_to_model(
|
109
100
|
model,
|
110
101
|
name,
|
111
|
-
options[:values],
|
112
|
-
collection: options[:collection],
|
102
|
+
attr.options[:values],
|
103
|
+
collection: attr.options[:collection],
|
113
104
|
)
|
114
105
|
elsif attr.derived? && name != attr.method_name
|
115
106
|
define_method(name) do
|
@@ -119,35 +110,75 @@ module Lutaml
|
|
119
110
|
define_method(name) do
|
120
111
|
instance_variable_get(:"@#{name}")
|
121
112
|
end
|
122
|
-
|
123
113
|
define_method(:"#{name}=") do |value|
|
124
114
|
value_set_for(name)
|
125
115
|
instance_variable_set(:"@#{name}", attr.cast_value(value))
|
126
116
|
end
|
127
117
|
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Define an attribute for the model
|
121
|
+
def attribute(name, type, options = {})
|
122
|
+
if type.is_a?(Hash)
|
123
|
+
options[:method_name] = type[:method]
|
124
|
+
type = nil
|
125
|
+
end
|
126
|
+
|
127
|
+
attr = Attribute.new(name, type, options)
|
128
|
+
attributes[name] = attr
|
129
|
+
define_attribute_methods(attr)
|
128
130
|
|
129
131
|
attr
|
130
132
|
end
|
131
133
|
|
132
134
|
def root?
|
133
|
-
mappings_for(:xml)
|
135
|
+
mappings_for(:xml)&.root?
|
136
|
+
end
|
137
|
+
|
138
|
+
def import_model_with_root_error(model)
|
139
|
+
return unless model.mappings.key?(:xml) && model.root?
|
140
|
+
|
141
|
+
raise Lutaml::Model::ImportModelWithRootError.new(model)
|
134
142
|
end
|
135
143
|
|
136
144
|
def import_model_attributes(model)
|
137
|
-
|
145
|
+
model.attributes.each_value do |attr|
|
146
|
+
define_attribute_methods(attr)
|
147
|
+
end
|
138
148
|
|
139
|
-
@
|
149
|
+
@choice_attributes.concat(Utils.deep_dup(model.choice_attributes))
|
150
|
+
@attributes.merge!(Utils.deep_dup(model.attributes))
|
140
151
|
end
|
141
152
|
|
142
153
|
def import_model_mappings(model)
|
143
|
-
|
154
|
+
import_model_with_root_error(model)
|
155
|
+
|
156
|
+
Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
|
157
|
+
next unless model.mappings.key?(format)
|
158
|
+
|
159
|
+
mapping = model.mappings_for(format)
|
160
|
+
mapping = Utils.deep_dup(mapping)
|
161
|
+
|
162
|
+
klass = ::Lutaml::Model::Config.mappings_class_for(format)
|
163
|
+
@mappings[format] ||= klass.new
|
144
164
|
|
145
|
-
|
165
|
+
if format == :xml
|
166
|
+
@mappings[format].merge_mapping_attributes(mapping)
|
167
|
+
@mappings[format].merge_mapping_elements(mapping)
|
168
|
+
@mappings[format].merge_elements_sequence(mapping)
|
169
|
+
else
|
170
|
+
@mappings[format].mappings.concat(mapping.mappings)
|
171
|
+
end
|
172
|
+
end
|
146
173
|
end
|
147
174
|
|
148
|
-
def
|
149
|
-
|
175
|
+
def handle_key_value_mappings(mapping, format)
|
176
|
+
@mappings[format] ||= KeyValueMapping.new
|
177
|
+
@mappings[format].mappings.concat(mapping.mappings)
|
178
|
+
end
|
150
179
|
|
180
|
+
def import_model(model)
|
181
|
+
import_model_with_root_error(model)
|
151
182
|
import_model_attributes(model)
|
152
183
|
import_model_mappings(model)
|
153
184
|
end
|
@@ -213,7 +244,8 @@ module Lutaml
|
|
213
244
|
|
214
245
|
def add_enum_setter_if_not_defined(klass, enum_name, _values, collection)
|
215
246
|
Utils.add_method_if_not_defined(klass, "#{enum_name}=") do |value|
|
216
|
-
value = [
|
247
|
+
value = [] if value.nil?
|
248
|
+
value = [value] if !value.is_a?(Array)
|
217
249
|
|
218
250
|
value_set_for(enum_name)
|
219
251
|
|
@@ -231,140 +263,86 @@ module Lutaml
|
|
231
263
|
attributes.select { |_, attr| attr.enum? }
|
232
264
|
end
|
233
265
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
mappings[format].instance_eval(&block)
|
266
|
+
def process_mapping(format, &block)
|
267
|
+
# klass = Lutaml::Model.const_get("#{format.to_s.capitalize}Mapping")
|
268
|
+
# mappings[format] ||= klass.new
|
269
|
+
# mappings[format].instance_eval(&block)
|
239
270
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
271
|
+
# handle_root_assignment(mappings, format)
|
272
|
+
|
273
|
+
klass = ::Lutaml::Model::Config.mappings_class_for(format)
|
274
|
+
mappings[format] ||= klass.new
|
244
275
|
|
245
|
-
|
246
|
-
adapter = Lutaml::Model::Config.send(:"#{format}_adapter")
|
276
|
+
mappings[format].instance_eval(&block)
|
247
277
|
|
248
|
-
|
249
|
-
|
278
|
+
if mappings[format].respond_to?(:finalize)
|
279
|
+
mappings[format].finalize(self)
|
250
280
|
end
|
281
|
+
end
|
251
282
|
|
252
|
-
|
253
|
-
|
254
|
-
return doc.map { |item| send(:"of_#{format}", item) }
|
255
|
-
end
|
283
|
+
def from(format, data, options = {})
|
284
|
+
return data if Utils.uninitialized?(data)
|
256
285
|
|
257
|
-
|
258
|
-
raise Lutaml::Model::NoRootMappingError.new(self) unless root?
|
286
|
+
adapter = Lutaml::Model::Config.adapter_for(format)
|
259
287
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
288
|
+
doc = adapter.parse(data, options)
|
289
|
+
public_send(:"of_#{format}", doc, options)
|
290
|
+
end
|
291
|
+
|
292
|
+
def of(format, doc, options = {})
|
293
|
+
if doc.is_a?(Array)
|
294
|
+
return doc.map { |item| send(:"of_#{format}", item) }
|
265
295
|
end
|
266
296
|
|
267
|
-
|
268
|
-
|
269
|
-
adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
|
297
|
+
if format == :xml
|
298
|
+
raise Lutaml::Model::NoRootMappingError.new(self) unless root?
|
270
299
|
|
271
|
-
|
272
|
-
xml_options = { mapper_class: self }
|
273
|
-
adapter.new(value).public_send(:"to_#{format}", xml_options)
|
274
|
-
else
|
275
|
-
adapter.new(value).public_send(:"to_#{format}")
|
276
|
-
end
|
300
|
+
options[:encoding] = doc.encoding
|
277
301
|
end
|
278
302
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
end
|
303
|
+
transformer = Lutaml::Model::Config.transformer_for(format)
|
304
|
+
transformer.data_to_model(self, doc, format, options)
|
305
|
+
end
|
283
306
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
end
|
307
|
+
def to(format, instance, options = {})
|
308
|
+
value = public_send(:"as_#{format}", instance, options)
|
309
|
+
adapter = Lutaml::Model::Config.adapter_for(format)
|
288
310
|
|
289
|
-
|
311
|
+
options[:mapper_class] = self if format == :xml
|
290
312
|
|
291
|
-
|
292
|
-
end
|
313
|
+
adapter.new(value).public_send(:"to_#{format}", options)
|
293
314
|
end
|
294
315
|
|
295
316
|
def as(format, instance, options = {})
|
296
|
-
|
317
|
+
if instance.is_a?(Array)
|
318
|
+
return instance.map { |item| public_send(:"as_#{format}", item) }
|
319
|
+
end
|
320
|
+
|
321
|
+
unless instance.is_a?(model)
|
322
|
+
msg = "argument is a '#{instance.class}' but should be a '#{model}'"
|
323
|
+
raise Lutaml::Model::IncorrectModelError, msg
|
324
|
+
end
|
325
|
+
|
326
|
+
transformer = Lutaml::Model::Config.transformer_for(format)
|
327
|
+
transformer.model_to_data(self, instance, format, options)
|
297
328
|
end
|
298
329
|
|
299
330
|
def key_value(&block)
|
300
331
|
Lutaml::Model::Config::KEY_VALUE_FORMATS.each do |format|
|
301
|
-
mappings[format] ||= KeyValueMapping.new
|
332
|
+
mappings[format] ||= KeyValueMapping.new(format)
|
302
333
|
mappings[format].instance_eval(&block)
|
303
334
|
end
|
304
335
|
end
|
305
336
|
|
306
|
-
def hash_representation(instance, format, options = {})
|
307
|
-
only = options[:only]
|
308
|
-
except = options[:except]
|
309
|
-
mappings = mappings_for(format).mappings
|
310
|
-
|
311
|
-
mappings.each_with_object({}) do |rule, hash|
|
312
|
-
name = rule.to
|
313
|
-
next if except&.include?(name) || (only && !only.include?(name))
|
314
|
-
next if !rule.custom_methods[:to] && (!rule.render_default? && instance.using_default?(rule.to))
|
315
|
-
|
316
|
-
next handle_delegate(instance, rule, hash, format) if rule.delegate
|
317
|
-
|
318
|
-
if rule.custom_methods[:to]
|
319
|
-
next instance.send(rule.custom_methods[:to], instance, hash)
|
320
|
-
end
|
321
|
-
|
322
|
-
value = instance.send(name)
|
323
|
-
|
324
|
-
if rule.raw_mapping?
|
325
|
-
adapter = Lutaml::Model::Config.send(:"#{format}_adapter")
|
326
|
-
return adapter.parse(value, options)
|
327
|
-
end
|
328
|
-
|
329
|
-
attribute = attributes[name]
|
330
|
-
|
331
|
-
if export_method = rule.transform[:export] || attribute.transform_export_method
|
332
|
-
value = export_method.call(value)
|
333
|
-
end
|
334
|
-
|
335
|
-
next hash.merge!(generate_hash_from_child_mappings(attribute, value, format, rule.root_mappings)) if rule.root_mapping?
|
336
|
-
|
337
|
-
value = if rule.child_mappings
|
338
|
-
generate_hash_from_child_mappings(attribute, value, format, rule.child_mappings)
|
339
|
-
else
|
340
|
-
attribute.serialize(value, format, options)
|
341
|
-
end
|
342
|
-
|
343
|
-
next unless rule.render?(value)
|
344
|
-
|
345
|
-
rule_from_name = rule.multiple_mappings? ? rule.from.first.to_s : rule.from.to_s
|
346
|
-
hash[rule_from_name] = value
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
def handle_delegate(instance, rule, hash, format)
|
351
|
-
name = rule.to
|
352
|
-
value = instance.send(rule.delegate).send(name)
|
353
|
-
return if value.nil? && !rule.render_nil
|
354
|
-
|
355
|
-
attribute = instance.send(rule.delegate).class.attributes[name]
|
356
|
-
rule_from_name = rule.multiple_mappings? ? rule.from.first.to_s : rule.from.to_s
|
357
|
-
hash[rule_from_name] = attribute.serialize(value, format)
|
358
|
-
end
|
359
|
-
|
360
337
|
def mappings_for(format)
|
361
338
|
mappings[format] || default_mappings(format)
|
362
339
|
end
|
363
340
|
|
364
341
|
def default_mappings(format)
|
365
|
-
klass = format
|
342
|
+
klass = ::Lutaml::Model::Config.mappings_class_for(format)
|
343
|
+
mappings = klass.new
|
366
344
|
|
367
|
-
|
345
|
+
mappings.tap do |mapping|
|
368
346
|
attributes&.each_key do |name|
|
369
347
|
mapping.map_element(
|
370
348
|
name.to_s,
|
@@ -376,288 +354,55 @@ module Lutaml
|
|
376
354
|
end
|
377
355
|
end
|
378
356
|
|
379
|
-
def translate_mappings(hash, child_mappings, attr, format)
|
380
|
-
return hash unless child_mappings
|
381
|
-
|
382
|
-
hash.map do |key, value|
|
383
|
-
child_hash = child_mappings.to_h do |attr_name, path|
|
384
|
-
attr_value = if path == :key
|
385
|
-
key
|
386
|
-
elsif path == :value
|
387
|
-
value
|
388
|
-
else
|
389
|
-
path = [path] unless path.is_a?(Array)
|
390
|
-
value.dig(*path.map(&:to_s))
|
391
|
-
end
|
392
|
-
|
393
|
-
attr_rule = attr.type.mappings_for(format).find_by_to(attr_name)
|
394
|
-
[attr_rule.from.to_s, attr_value]
|
395
|
-
end
|
396
|
-
|
397
|
-
if child_mappings.values == [:key] && hash.values.all?(Hash)
|
398
|
-
child_hash.merge!(value)
|
399
|
-
end
|
400
|
-
|
401
|
-
attr.type.apply_hash_mapping(
|
402
|
-
child_hash,
|
403
|
-
attr.type.model.new,
|
404
|
-
format,
|
405
|
-
{ mappings: attr.type.mappings_for(format).mappings },
|
406
|
-
)
|
407
|
-
end
|
408
|
-
end
|
409
|
-
|
410
|
-
def generate_hash_from_child_mappings(attr, value, format, child_mappings)
|
411
|
-
return value unless child_mappings
|
412
|
-
|
413
|
-
hash = {}
|
414
|
-
|
415
|
-
if child_mappings.values == [:key]
|
416
|
-
klass = value.first.class
|
417
|
-
mappings = klass.mappings_for(format)
|
418
|
-
|
419
|
-
klass.attributes.each_key do |name|
|
420
|
-
next if child_mappings.key?(name.to_sym) || child_mappings.key?(name.to_s)
|
421
|
-
|
422
|
-
child_mappings[name.to_sym] = mappings.find_by_to(name)&.name.to_s || name.to_s
|
423
|
-
end
|
424
|
-
end
|
425
|
-
|
426
|
-
value.each do |child_obj|
|
427
|
-
map_key = nil
|
428
|
-
map_value = {}
|
429
|
-
mapping_rules = attr.type.mappings_for(format)
|
430
|
-
|
431
|
-
child_mappings.each do |attr_name, path|
|
432
|
-
mapping_rule = mapping_rules.find_by_to(attr_name)
|
433
|
-
|
434
|
-
attr_value = child_obj.send(attr_name)
|
435
|
-
|
436
|
-
attr_value = if attr_value.is_a?(Lutaml::Model::Serialize)
|
437
|
-
attr_value.to_yaml_hash
|
438
|
-
elsif attr_value.is_a?(Array) && attr_value.first.is_a?(Lutaml::Model::Serialize)
|
439
|
-
attr_value.map(&:to_yaml_hash)
|
440
|
-
else
|
441
|
-
attr_value
|
442
|
-
end
|
443
|
-
|
444
|
-
next unless mapping_rule&.render?(attr_value)
|
445
|
-
|
446
|
-
if path == :key
|
447
|
-
map_key = attr_value
|
448
|
-
elsif path == :value
|
449
|
-
map_value = attr_value
|
450
|
-
else
|
451
|
-
path = [path] unless path.is_a?(Array)
|
452
|
-
path[0...-1].inject(map_value) do |acc, k|
|
453
|
-
acc[k.to_s] ||= {}
|
454
|
-
end.public_send(:[]=, path.last.to_s, attr_value)
|
455
|
-
end
|
456
|
-
end
|
457
|
-
|
458
|
-
map_value = nil if map_value.empty?
|
459
|
-
hash[map_key] = map_value
|
460
|
-
end
|
461
|
-
|
462
|
-
hash
|
463
|
-
end
|
464
|
-
|
465
|
-
def valid_rule?(rule)
|
466
|
-
attribute = attribute_for_rule(rule)
|
467
|
-
|
468
|
-
!!attribute || rule.custom_methods[:from]
|
469
|
-
end
|
470
|
-
|
471
|
-
def attribute_for_rule(rule)
|
472
|
-
return attributes[rule.to] unless rule.delegate
|
473
|
-
|
474
|
-
attributes[rule.delegate].type.attributes[rule.to]
|
475
|
-
end
|
476
|
-
|
477
|
-
def attribute_for_child(child_name, format)
|
478
|
-
mapping_rule = mappings_for(format).find_by_name(child_name)
|
479
|
-
|
480
|
-
attribute_for_rule(mapping_rule) if mapping_rule
|
481
|
-
end
|
482
|
-
|
483
357
|
def apply_mappings(doc, format, options = {})
|
484
358
|
instance = options[:instance] || model.new
|
485
359
|
return instance if Utils.blank?(doc)
|
486
360
|
|
487
|
-
|
488
|
-
|
489
|
-
return apply_xml_mapping(doc, instance, options) if format == :xml
|
490
|
-
|
491
|
-
apply_hash_mapping(doc, instance, format, options)
|
492
|
-
end
|
493
|
-
|
494
|
-
def apply_xml_mapping(doc, instance, options = {})
|
495
|
-
options = Utils.deep_dup(options)
|
496
|
-
instance.encoding = options[:encoding]
|
497
|
-
return instance unless doc
|
498
|
-
|
499
|
-
if options[:default_namespace].nil?
|
500
|
-
options[:default_namespace] = mappings_for(:xml)&.namespace_uri
|
501
|
-
end
|
502
|
-
mappings = options[:mappings] || mappings_for(:xml).mappings
|
503
|
-
|
504
|
-
raise Lutaml::Model::CollectionTrueMissingError(self, option[:caller_class]) if doc.is_a?(Array)
|
361
|
+
mappings = mappings_for(format)
|
505
362
|
|
506
|
-
|
507
|
-
|
508
|
-
instance.element_order = doc_order
|
509
|
-
instance.ordered = mappings_for(:xml).ordered? || options[:ordered]
|
510
|
-
instance.mixed = mappings_for(:xml).mixed_content? || options[:mixed_content]
|
363
|
+
if mappings.polymorphic_mapping
|
364
|
+
return resolve_polymorphic(doc, format, mappings, instance, options)
|
511
365
|
end
|
512
366
|
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
if !schema_location.nil?
|
518
|
-
instance.schema_location = Lutaml::Model::SchemaLocation.new(
|
519
|
-
schema_location: schema_location.value,
|
520
|
-
prefix: schema_location.namespace_prefix,
|
521
|
-
namespace: schema_location.namespace,
|
522
|
-
)
|
523
|
-
end
|
524
|
-
|
525
|
-
defaults_used = []
|
526
|
-
validate_sequence!(doc_order)
|
527
|
-
|
528
|
-
mappings.each do |rule|
|
529
|
-
raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
|
530
|
-
|
531
|
-
attr = attribute_for_rule(rule)
|
532
|
-
next if attr&.derived?
|
533
|
-
|
534
|
-
value = if rule.raw_mapping?
|
535
|
-
doc.root.inner_xml
|
536
|
-
elsif rule.content_mapping?
|
537
|
-
rule.cdata ? doc.cdata : doc.text
|
538
|
-
elsif val = value_for_rule(doc, rule, options, instance)
|
539
|
-
val
|
540
|
-
elsif instance.using_default?(rule.to) || rule.render_default
|
541
|
-
defaults_used << rule.to
|
542
|
-
attr&.default || rule.to_value_for(instance)
|
543
|
-
end
|
544
|
-
|
545
|
-
value = normalize_xml_value(value, rule, attr, options)
|
546
|
-
rule.deserialize(instance, value, attributes, self)
|
547
|
-
end
|
548
|
-
|
549
|
-
defaults_used.each { |attr_name| instance.using_default_for(attr_name) }
|
550
|
-
|
551
|
-
instance
|
367
|
+
# options[:mappings] = mappings.mappings
|
368
|
+
transformer = Lutaml::Model::Config.transformer_for(format)
|
369
|
+
transformer.data_to_model(self, doc, format, options)
|
552
370
|
end
|
553
371
|
|
554
|
-
def
|
555
|
-
|
556
|
-
|
557
|
-
if rule.attribute?
|
558
|
-
doc.root.find_attribute_value(rule_names)
|
559
|
-
else
|
560
|
-
attr = attribute_for_rule(rule)
|
561
|
-
children = doc.children.select do |child|
|
562
|
-
rule_names.include?(child.namespaced_name) && !child.text?
|
563
|
-
end
|
564
|
-
|
565
|
-
if rule.using_custom_methods? || attr.type == Lutaml::Model::Type::Hash
|
566
|
-
return_child = attr.type == Lutaml::Model::Type::Hash || !attr.collection? if attr
|
567
|
-
return return_child ? children.first : children
|
568
|
-
end
|
372
|
+
def resolve_polymorphic(doc, format, mappings, instance, options = {})
|
373
|
+
polymorphic_mapping = mappings.polymorphic_mapping
|
374
|
+
return instance if polymorphic_mapping.polymorphic_map.empty?
|
569
375
|
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
if rule.cdata
|
575
|
-
values = children.map do |child|
|
576
|
-
child.cdata_children&.map(&:text)
|
577
|
-
end.flatten
|
578
|
-
return children.count > 1 ? values : values.first
|
579
|
-
end
|
376
|
+
klass_key = doc[polymorphic_mapping.name]
|
377
|
+
klass_name = polymorphic_mapping.polymorphic_map[klass_key]
|
378
|
+
klass = Object.const_get(klass_name)
|
580
379
|
|
581
|
-
|
582
|
-
if !rule.using_custom_methods? && attr.type <= Serialize
|
583
|
-
attr.cast(child, :xml, options.except(:mappings))
|
584
|
-
elsif attr.raw?
|
585
|
-
inner_xml_of(child)
|
586
|
-
else
|
587
|
-
child&.children&.first&.text
|
588
|
-
end
|
589
|
-
end
|
590
|
-
attr&.collection? ? values : values.first
|
591
|
-
end
|
380
|
+
klass.apply_mappings(doc, format, options)
|
592
381
|
end
|
593
382
|
|
594
|
-
def
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
value = names.collect do |rule_name|
|
604
|
-
if rule.root_mapping?
|
605
|
-
doc
|
606
|
-
elsif rule.raw_mapping?
|
607
|
-
adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
|
608
|
-
adapter.new(doc).public_send(:"to_#{format}")
|
609
|
-
elsif doc.key?(rule_name.to_s)
|
610
|
-
doc[rule_name.to_s]
|
611
|
-
elsif doc.key?(rule_name.to_sym)
|
612
|
-
doc[rule_name.to_sym]
|
613
|
-
else
|
614
|
-
attr&.default
|
615
|
-
end
|
616
|
-
end.compact.first
|
617
|
-
|
618
|
-
if rule.using_custom_methods?
|
619
|
-
if Utils.present?(value)
|
620
|
-
value = new.send(rule.custom_methods[:from], instance, value)
|
621
|
-
end
|
622
|
-
|
623
|
-
next
|
624
|
-
end
|
625
|
-
|
626
|
-
value = translate_mappings(value, rule.hash_mappings, attr, format)
|
627
|
-
value = attr.cast(value, format) unless rule.hash_mappings
|
628
|
-
attr.valid_collection!(value, self)
|
629
|
-
|
630
|
-
rule.deserialize(instance, value, attributes, self)
|
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
|
631
392
|
end
|
632
|
-
|
633
|
-
instance
|
634
393
|
end
|
635
394
|
|
636
|
-
def
|
637
|
-
|
638
|
-
|
639
|
-
return value unless cast_value?(attr, rule)
|
640
|
-
|
641
|
-
options.merge(caller_class: self, mixed_content: rule.mixed_content)
|
642
|
-
attr.cast(
|
643
|
-
value,
|
644
|
-
:xml,
|
645
|
-
options,
|
646
|
-
)
|
647
|
-
end
|
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
|
648
398
|
|
649
|
-
|
650
|
-
attr &&
|
651
|
-
!rule.raw_mapping? &&
|
652
|
-
!rule.content_mapping? &&
|
653
|
-
!rule.custom_methods[:from]
|
399
|
+
Lutaml::Model::UninitializedClass.instance
|
654
400
|
end
|
655
401
|
|
656
|
-
def
|
657
|
-
return
|
658
|
-
return value.one? && value.text? unless attr
|
402
|
+
def empty_object(attr)
|
403
|
+
return [] if attr.collection?
|
659
404
|
|
660
|
-
|
405
|
+
""
|
661
406
|
end
|
662
407
|
|
663
408
|
def ensure_utf8(value)
|
@@ -677,64 +422,61 @@ module Lutaml
|
|
677
422
|
value
|
678
423
|
end
|
679
424
|
end
|
425
|
+
end
|
680
426
|
|
681
|
-
|
682
|
-
|
683
|
-
current_order = element_order.filter_map(&:element_tag)
|
427
|
+
def self.register_format_mapping_method(format)
|
428
|
+
method_name = format == :hash ? :hsh : format
|
684
429
|
|
685
|
-
|
686
|
-
|
687
|
-
end
|
430
|
+
::Lutaml::Model::Serialize::ClassMethods.define_method(method_name) do |&block|
|
431
|
+
process_mapping(format, &block)
|
688
432
|
end
|
433
|
+
end
|
689
434
|
|
690
|
-
|
435
|
+
def self.register_from_format_method(format)
|
436
|
+
ClassMethods.define_method(:"from_#{format}") do |data, options = {}|
|
437
|
+
from(format, data, options)
|
438
|
+
end
|
691
439
|
|
692
|
-
|
693
|
-
|
694
|
-
when XmlAdapter::XmlElement
|
695
|
-
node.inner_xml
|
696
|
-
else
|
697
|
-
node.children.map(&:to_xml).join
|
698
|
-
end
|
440
|
+
ClassMethods.define_method(:"of_#{format}") do |doc, options = {}|
|
441
|
+
of(format, doc, options)
|
699
442
|
end
|
700
443
|
end
|
701
444
|
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
@using_default = {}
|
707
|
-
|
708
|
-
return unless self.class.attributes
|
445
|
+
def self.register_to_format_method(format)
|
446
|
+
ClassMethods.define_method(:"to_#{format}") do |instance, options = {}|
|
447
|
+
to(format, instance, options)
|
448
|
+
end
|
709
449
|
|
710
|
-
|
711
|
-
|
712
|
-
@element_order = attrs.item_order
|
450
|
+
ClassMethods.define_method(:"as_#{format}") do |instance, options = {}|
|
451
|
+
as(format, instance, options)
|
713
452
|
end
|
714
453
|
|
715
|
-
|
716
|
-
self.
|
454
|
+
define_method(:"to_#{format}") do |options = {}|
|
455
|
+
raise Lutaml::Model::NoRootMappingError.new(self.class) if format == :xml && !self.class.root?
|
456
|
+
|
457
|
+
options[:parse_encoding] = encoding if encoding
|
458
|
+
self.class.to(format, self, options)
|
717
459
|
end
|
460
|
+
end
|
718
461
|
|
719
|
-
|
720
|
-
|
462
|
+
attr_accessor :element_order, :schema_location, :encoding
|
463
|
+
attr_writer :ordered, :mixed
|
721
464
|
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
using_default_for(name)
|
726
|
-
attr.default
|
727
|
-
end
|
465
|
+
def initialize(attrs = {}, options = {})
|
466
|
+
@using_default = {}
|
467
|
+
return unless self.class.attributes
|
728
468
|
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
469
|
+
set_ordering(attrs)
|
470
|
+
set_schema_location(attrs)
|
471
|
+
initialize_attributes(attrs, options)
|
472
|
+
end
|
733
473
|
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
474
|
+
def value_map(options)
|
475
|
+
{
|
476
|
+
omitted: options[:omitted] || :nil,
|
477
|
+
nil: options[:nil] || :nil,
|
478
|
+
empty: options[:empty] || :empty,
|
479
|
+
}
|
738
480
|
end
|
739
481
|
|
740
482
|
def attr_value(attrs, name, attr_rule)
|
@@ -747,7 +489,7 @@ module Lutaml
|
|
747
489
|
end
|
748
490
|
|
749
491
|
if attr_rule.collection? || value.is_a?(Array)
|
750
|
-
|
492
|
+
value&.map do |v|
|
751
493
|
if v.is_a?(Hash)
|
752
494
|
attr_rule.type.new(v)
|
753
495
|
else
|
@@ -776,7 +518,7 @@ module Lutaml
|
|
776
518
|
end
|
777
519
|
|
778
520
|
def method_missing(method_name, *args)
|
779
|
-
if method_name.to_s.end_with?("=") &&
|
521
|
+
if method_name.to_s.end_with?("=") && attribute_exist?(method_name)
|
780
522
|
define_singleton_method(method_name) do |value|
|
781
523
|
instance_variable_set(:"@#{method_name.to_s.chomp('=')}", value)
|
782
524
|
end
|
@@ -786,6 +528,17 @@ module Lutaml
|
|
786
528
|
end
|
787
529
|
end
|
788
530
|
|
531
|
+
def respond_to_missing?(method_name, include_private = false)
|
532
|
+
(method_name.to_s.end_with?("=") && attribute_exist?(method_name)) ||
|
533
|
+
super
|
534
|
+
end
|
535
|
+
|
536
|
+
def attribute_exist?(name)
|
537
|
+
name = name.to_s.chomp("=").to_sym if name.end_with?("=")
|
538
|
+
|
539
|
+
self.class.attributes.key?(name)
|
540
|
+
end
|
541
|
+
|
789
542
|
def validate_attribute!(attr_name)
|
790
543
|
attr = self.class.attributes[attr_name]
|
791
544
|
value = instance_variable_get(:"@#{attr_name}")
|
@@ -816,20 +569,42 @@ module Lutaml
|
|
816
569
|
self.class.as_yaml(self)
|
817
570
|
end
|
818
571
|
|
819
|
-
|
820
|
-
define_method(:"to_#{format}") do |options = {}|
|
821
|
-
adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
|
822
|
-
raise Lutaml::Model::NoRootMappingError.new(self.class) unless self.class.root?
|
572
|
+
private
|
823
573
|
|
824
|
-
|
825
|
-
|
826
|
-
else
|
827
|
-
self.class.hash_representation(self, format,
|
828
|
-
options)
|
829
|
-
end
|
574
|
+
def set_ordering(attrs)
|
575
|
+
return unless attrs.respond_to?(:ordered?)
|
830
576
|
|
831
|
-
|
832
|
-
|
577
|
+
@ordered = attrs.ordered?
|
578
|
+
@element_order = attrs.item_order
|
579
|
+
end
|
580
|
+
|
581
|
+
def set_schema_location(attrs)
|
582
|
+
return unless attrs.key?(:schema_location)
|
583
|
+
|
584
|
+
self.schema_location = attrs[:schema_location]
|
585
|
+
end
|
586
|
+
|
587
|
+
def initialize_attributes(attrs, options = {})
|
588
|
+
self.class.attributes.each do |name, attr|
|
589
|
+
next if attr.derived?
|
590
|
+
|
591
|
+
value = determine_value(attrs, name, attr)
|
592
|
+
|
593
|
+
default = using_default?(name)
|
594
|
+
value = self.class.apply_value_map(value, value_map(options), attr)
|
595
|
+
public_send(:"#{name}=", self.class.ensure_utf8(value))
|
596
|
+
using_default_for(name) if default
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
def determine_value(attrs, name, attr)
|
601
|
+
if attrs.key?(name) || attrs.key?(name.to_s)
|
602
|
+
attr_value(attrs, name, attr)
|
603
|
+
elsif attr.default_set?
|
604
|
+
using_default_for(name)
|
605
|
+
attr.default
|
606
|
+
else
|
607
|
+
Lutaml::Model::UninitializedClass.instance
|
833
608
|
end
|
834
609
|
end
|
835
610
|
end
|