lutaml-model 0.5.0 → 0.5.1
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/.rubocop_todo.yml +4 -4
- data/README.adoc +837 -184
- data/lib/lutaml/model/attribute.rb +6 -2
- data/lib/lutaml/model/error/collection_true_missing_error.rb +16 -0
- data/lib/lutaml/model/error/multiple_mappings_error.rb +6 -0
- data/lib/lutaml/model/error.rb +2 -0
- data/lib/lutaml/model/key_value_mapping.rb +26 -4
- data/lib/lutaml/model/key_value_mapping_rule.rb +15 -4
- data/lib/lutaml/model/loggable.rb +15 -0
- data/lib/lutaml/model/mapping_rule.rb +2 -2
- data/lib/lutaml/model/serialize.rb +50 -15
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/xml_document.rb +3 -3
- data/lib/lutaml/model/xml_mapping.rb +4 -0
- data/lib/lutaml/model/xml_mapping_rule.rb +2 -5
- data/lib/lutaml/model.rb +1 -0
- data/spec/lutaml/model/root_mappings_spec.rb +297 -0
- data/spec/lutaml/model/serializable_spec.rb +41 -6
- data/spec/lutaml/model/with_child_mapping_spec.rb +182 -0
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +66 -0
- data/spec/lutaml/model/xml_mapping_spec.rb +8 -0
- metadata +6 -2
@@ -142,7 +142,7 @@ module Lutaml
|
|
142
142
|
# Use the default value if the value is nil
|
143
143
|
value = default if value.nil?
|
144
144
|
|
145
|
-
valid_value!(value) && valid_collection!(value) && valid_pattern!(value)
|
145
|
+
valid_value!(value) && valid_collection!(value, self) && valid_pattern!(value)
|
146
146
|
end
|
147
147
|
|
148
148
|
def validate_collection_range
|
@@ -169,7 +169,9 @@ module Lutaml
|
|
169
169
|
end
|
170
170
|
end
|
171
171
|
|
172
|
-
def valid_collection!(value)
|
172
|
+
def valid_collection!(value, caller)
|
173
|
+
raise Lutaml::Model::CollectionTrueMissingError.new(name, caller) if value.is_a?(Array) && !collection?
|
174
|
+
|
173
175
|
return true unless collection?
|
174
176
|
|
175
177
|
# Allow nil values for collections during initialization
|
@@ -207,6 +209,8 @@ module Lutaml
|
|
207
209
|
end
|
208
210
|
|
209
211
|
def serialize(value, format, options = {})
|
212
|
+
return if value.nil?
|
213
|
+
|
210
214
|
if value.is_a?(Array)
|
211
215
|
value.map do |v|
|
212
216
|
serialize(v, format, options)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Lutaml
|
2
|
+
module Model
|
3
|
+
class CollectionTrueMissingError < Error
|
4
|
+
def initialize(attr_name, caller_class)
|
5
|
+
@attr_name = attr_name
|
6
|
+
@caller = caller_class
|
7
|
+
|
8
|
+
super()
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
"May be `collection: true` is missing for `#{@attr_name}` in #{@caller}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/lutaml/model/error.rb
CHANGED
@@ -14,3 +14,5 @@ require_relative "error/validation_error"
|
|
14
14
|
require_relative "error/type_not_enabled_error"
|
15
15
|
require_relative "error/type_error"
|
16
16
|
require_relative "error/unknown_type_error"
|
17
|
+
require_relative "error/multiple_mappings_error"
|
18
|
+
require_relative "error/collection_true_missing_error"
|
@@ -10,28 +10,38 @@ module Lutaml
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def map(
|
13
|
-
name,
|
13
|
+
name = nil,
|
14
14
|
to: nil,
|
15
15
|
render_nil: false,
|
16
16
|
render_default: false,
|
17
17
|
with: {},
|
18
18
|
delegate: nil,
|
19
|
-
child_mappings: nil
|
19
|
+
child_mappings: nil,
|
20
|
+
root_mappings: nil
|
20
21
|
)
|
21
|
-
|
22
|
+
mapping_name = name_for_mapping(root_mappings, name)
|
23
|
+
validate!(mapping_name, to, with)
|
24
|
+
|
22
25
|
@mappings << KeyValueMappingRule.new(
|
23
|
-
|
26
|
+
mapping_name,
|
24
27
|
to: to,
|
25
28
|
render_nil: render_nil,
|
26
29
|
render_default: render_default,
|
27
30
|
with: with,
|
28
31
|
delegate: delegate,
|
29
32
|
child_mappings: child_mappings,
|
33
|
+
root_mappings: root_mappings,
|
30
34
|
)
|
31
35
|
end
|
32
36
|
|
33
37
|
alias map_element map
|
34
38
|
|
39
|
+
def name_for_mapping(root_mappings, name)
|
40
|
+
return "root_mapping" if root_mappings
|
41
|
+
|
42
|
+
name
|
43
|
+
end
|
44
|
+
|
35
45
|
def validate!(key, to, with)
|
36
46
|
if to.nil? && with.empty?
|
37
47
|
msg = ":to or :with argument is required for mapping '#{key}'"
|
@@ -42,6 +52,14 @@ module Lutaml
|
|
42
52
|
msg = ":with argument for mapping '#{key}' requires :to and :from keys"
|
43
53
|
raise IncorrectMappingArgumentsError.new(msg)
|
44
54
|
end
|
55
|
+
|
56
|
+
validate_mappings(key)
|
57
|
+
end
|
58
|
+
|
59
|
+
def validate_mappings(name)
|
60
|
+
if @mappings.any?(&:root_mapping?) || (name == "root_mapping" && @mappings.any?)
|
61
|
+
raise MultipleMappingsError.new("root_mappings cannot be used with other mappings")
|
62
|
+
end
|
45
63
|
end
|
46
64
|
|
47
65
|
def deep_dup
|
@@ -53,6 +71,10 @@ module Lutaml
|
|
53
71
|
def duplicate_mappings
|
54
72
|
@mappings.map(&:deep_dup)
|
55
73
|
end
|
74
|
+
|
75
|
+
def find_by_to(to)
|
76
|
+
@mappings.find { |m| m.to.to_s == to.to_s }
|
77
|
+
end
|
56
78
|
end
|
57
79
|
end
|
58
80
|
end
|
@@ -3,7 +3,8 @@ require_relative "mapping_rule"
|
|
3
3
|
module Lutaml
|
4
4
|
module Model
|
5
5
|
class KeyValueMappingRule < MappingRule
|
6
|
-
attr_reader :child_mappings
|
6
|
+
attr_reader :child_mappings,
|
7
|
+
:root_mappings
|
7
8
|
|
8
9
|
def initialize(
|
9
10
|
name,
|
@@ -13,7 +14,7 @@ module Lutaml
|
|
13
14
|
with: {},
|
14
15
|
delegate: nil,
|
15
16
|
child_mappings: nil,
|
16
|
-
|
17
|
+
root_mappings: nil
|
17
18
|
)
|
18
19
|
super(
|
19
20
|
name,
|
@@ -21,11 +22,17 @@ module Lutaml
|
|
21
22
|
render_nil: render_nil,
|
22
23
|
render_default: render_default,
|
23
24
|
with: with,
|
24
|
-
delegate: delegate
|
25
|
-
id: id
|
25
|
+
delegate: delegate
|
26
26
|
)
|
27
27
|
|
28
28
|
@child_mappings = child_mappings
|
29
|
+
@root_mappings = root_mappings
|
30
|
+
end
|
31
|
+
|
32
|
+
def hash_mappings
|
33
|
+
return @root_mappings if @root_mappings
|
34
|
+
|
35
|
+
@child_mappings
|
29
36
|
end
|
30
37
|
|
31
38
|
def deep_dup
|
@@ -38,6 +45,10 @@ module Lutaml
|
|
38
45
|
child_mappings: Utils.deep_dup(child_mappings),
|
39
46
|
)
|
40
47
|
end
|
48
|
+
|
49
|
+
def root_mapping?
|
50
|
+
name == "root_mapping"
|
51
|
+
end
|
41
52
|
end
|
42
53
|
end
|
43
54
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Lutaml
|
2
|
+
module Model
|
3
|
+
module Loggable
|
4
|
+
def self.included(base)
|
5
|
+
base.define_method :warn_auto_handling do |name|
|
6
|
+
caller_file = File.basename(caller_locations(2, 1)[0].path)
|
7
|
+
caller_line = caller_locations(2, 1)[0].lineno
|
8
|
+
|
9
|
+
str = "[Lutaml::Model] WARN: `#{name}` is handled by default. No need to explecitly define at `#{caller_file}:#{caller_line}`"
|
10
|
+
warn(str)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -17,7 +17,7 @@ module Lutaml
|
|
17
17
|
with: {},
|
18
18
|
attribute: false,
|
19
19
|
delegate: nil,
|
20
|
-
|
20
|
+
root_mappings: nil
|
21
21
|
)
|
22
22
|
@name = name
|
23
23
|
@to = to
|
@@ -26,7 +26,7 @@ module Lutaml
|
|
26
26
|
@custom_methods = with
|
27
27
|
@attribute = attribute
|
28
28
|
@delegate = delegate
|
29
|
-
@
|
29
|
+
@root_mappings = root_mappings
|
30
30
|
end
|
31
31
|
|
32
32
|
alias from name
|
@@ -271,8 +271,10 @@ module Lutaml
|
|
271
271
|
|
272
272
|
attribute = attributes[name]
|
273
273
|
|
274
|
+
next hash.merge!(generate_hash_from_child_mappings(value, format, rule.root_mappings)) if rule.root_mapping?
|
275
|
+
|
274
276
|
value = if rule.child_mappings
|
275
|
-
generate_hash_from_child_mappings(value, rule.child_mappings)
|
277
|
+
generate_hash_from_child_mappings(value, format, rule.child_mappings)
|
276
278
|
else
|
277
279
|
attribute.serialize(value, format, options)
|
278
280
|
end
|
@@ -313,11 +315,11 @@ module Lutaml
|
|
313
315
|
end
|
314
316
|
end
|
315
317
|
|
316
|
-
def
|
318
|
+
def translate_mappings(hash, child_mappings, attr, format)
|
317
319
|
return hash unless child_mappings
|
318
320
|
|
319
321
|
hash.map do |key, value|
|
320
|
-
child_mappings.to_h do |attr_name, path|
|
322
|
+
child_hash = child_mappings.to_h do |attr_name, path|
|
321
323
|
attr_value = if path == :key
|
322
324
|
key
|
323
325
|
elsif path == :value
|
@@ -327,32 +329,59 @@ module Lutaml
|
|
327
329
|
value.dig(*path.map(&:to_s))
|
328
330
|
end
|
329
331
|
|
330
|
-
|
332
|
+
attr_rule = attr.type.mappings_for(format).find_by_to(attr_name)
|
333
|
+
[attr_rule.from.to_s, attr_value]
|
334
|
+
end
|
335
|
+
|
336
|
+
if child_mappings.values == [:key] && hash.values.all?(Hash)
|
337
|
+
child_hash.merge!(value)
|
331
338
|
end
|
339
|
+
|
340
|
+
attr.type.apply_hash_mapping(
|
341
|
+
child_hash,
|
342
|
+
attr.type.model.new,
|
343
|
+
format,
|
344
|
+
{ mappings: attr.type.mappings_for(format).mappings },
|
345
|
+
)
|
332
346
|
end
|
333
347
|
end
|
334
348
|
|
335
|
-
def generate_hash_from_child_mappings(value, child_mappings)
|
349
|
+
def generate_hash_from_child_mappings(value, format, child_mappings)
|
336
350
|
return value unless child_mappings
|
337
351
|
|
338
352
|
hash = {}
|
339
353
|
|
354
|
+
if child_mappings.values == [:key]
|
355
|
+
klass = value.first.class
|
356
|
+
mappings = klass.mappings_for(format)
|
357
|
+
|
358
|
+
klass.attributes.each_key do |name|
|
359
|
+
next if child_mappings.key?(name.to_sym) || child_mappings.key?(name.to_s)
|
360
|
+
|
361
|
+
child_mappings[name.to_sym] = mappings.find_by_to(name)&.name.to_s || name.to_s
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
340
365
|
value.each do |child_obj|
|
341
366
|
map_key = nil
|
342
367
|
map_value = {}
|
343
368
|
child_mappings.each do |attr_name, path|
|
369
|
+
attr_value = child_obj.send(attr_name)
|
370
|
+
attr_value = attr_value.to_yaml_hash if attr_value.is_a?(Lutaml::Model::Serialize)
|
371
|
+
|
344
372
|
if path == :key
|
345
|
-
map_key =
|
373
|
+
map_key = attr_value
|
346
374
|
elsif path == :value
|
347
|
-
map_value =
|
375
|
+
map_value = attr_value
|
348
376
|
else
|
349
377
|
path = [path] unless path.is_a?(Array)
|
350
378
|
path[0...-1].inject(map_value) do |acc, k|
|
351
379
|
acc[k.to_s] ||= {}
|
352
|
-
end.public_send(:[]=, path.last.to_s,
|
380
|
+
end.public_send(:[]=, path.last.to_s, attr_value)
|
353
381
|
end
|
354
382
|
end
|
355
383
|
|
384
|
+
map_value = nil if map_value.empty?
|
356
385
|
hash[map_key] = map_value
|
357
386
|
end
|
358
387
|
|
@@ -380,12 +409,15 @@ module Lutaml
|
|
380
409
|
def apply_mappings(doc, format, options = {})
|
381
410
|
instance = options[:instance] || model.new
|
382
411
|
return instance if Utils.blank?(doc)
|
412
|
+
|
413
|
+
options[:mappings] = mappings_for(format).mappings
|
383
414
|
return apply_xml_mapping(doc, instance, options) if format == :xml
|
384
415
|
|
385
416
|
apply_hash_mapping(doc, instance, format, options)
|
386
417
|
end
|
387
418
|
|
388
419
|
def apply_xml_mapping(doc, instance, options = {})
|
420
|
+
options = Utils.deep_dup(options)
|
389
421
|
instance.encoding = options[:encoding]
|
390
422
|
return instance unless doc
|
391
423
|
|
@@ -393,10 +425,10 @@ module Lutaml
|
|
393
425
|
options[:default_namespace] =
|
394
426
|
mappings_for(:xml)&.namespace_uri
|
395
427
|
end
|
396
|
-
mappings = mappings_for(:xml).mappings
|
428
|
+
mappings = options[:mappings] || mappings_for(:xml).mappings
|
397
429
|
|
398
430
|
if doc.is_a?(Array)
|
399
|
-
raise
|
431
|
+
raise Lutaml::Model::CollectionTrueMissingError(self, option[:caller_class])
|
400
432
|
end
|
401
433
|
|
402
434
|
if instance.respond_to?(:ordered=) && doc.is_a?(Lutaml::Model::MappingHash)
|
@@ -443,8 +475,8 @@ module Lutaml
|
|
443
475
|
instance
|
444
476
|
end
|
445
477
|
|
446
|
-
def apply_hash_mapping(doc, instance, format,
|
447
|
-
mappings = mappings_for(format).mappings
|
478
|
+
def apply_hash_mapping(doc, instance, format, options = {})
|
479
|
+
mappings = options[:mappings] || mappings_for(format).mappings
|
448
480
|
mappings.each do |rule|
|
449
481
|
raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
|
450
482
|
|
@@ -453,7 +485,9 @@ module Lutaml
|
|
453
485
|
names = rule.multiple_mappings? ? rule.name : [rule.name]
|
454
486
|
|
455
487
|
value = names.collect do |rule_name|
|
456
|
-
if
|
488
|
+
if rule.root_mapping?
|
489
|
+
doc
|
490
|
+
elsif doc.key?(rule_name.to_s)
|
457
491
|
doc[rule_name.to_s]
|
458
492
|
elsif doc.key?(rule_name.to_sym)
|
459
493
|
doc[rule_name.to_sym]
|
@@ -470,8 +504,9 @@ module Lutaml
|
|
470
504
|
next
|
471
505
|
end
|
472
506
|
|
473
|
-
value =
|
474
|
-
value = attr.cast(value, format)
|
507
|
+
value = translate_mappings(value, rule.hash_mappings, attr, format)
|
508
|
+
value = attr.cast(value, format) unless rule.hash_mappings
|
509
|
+
attr.valid_collection!(value, self)
|
475
510
|
|
476
511
|
rule.deserialize(instance, value, attributes, self)
|
477
512
|
end
|
data/lib/lutaml/model/version.rb
CHANGED
@@ -109,9 +109,9 @@ module Lutaml
|
|
109
109
|
prefix: attr.namespace_prefix,
|
110
110
|
schema_location: attr.value,
|
111
111
|
}
|
112
|
-
else
|
113
|
-
result[attr.namespaced_name] = attr.value
|
114
112
|
end
|
113
|
+
|
114
|
+
result[attr.namespaced_name] = attr.value
|
115
115
|
end
|
116
116
|
|
117
117
|
result
|
@@ -328,7 +328,7 @@ module Lutaml
|
|
328
328
|
{}
|
329
329
|
end
|
330
330
|
|
331
|
-
if element.respond_to?(:schema_location) && element.schema_location && !options[:except]&.include?(:schema_location)
|
331
|
+
if element.respond_to?(:schema_location) && element.schema_location.is_a?(Lutaml::Model::SchemaLocation) && !options[:except]&.include?(:schema_location)
|
332
332
|
attrs.merge!(element.schema_location.to_xml_attributes)
|
333
333
|
end
|
334
334
|
|
@@ -3,6 +3,8 @@ require_relative "xml_mapping_rule"
|
|
3
3
|
module Lutaml
|
4
4
|
module Model
|
5
5
|
class XmlMapping
|
6
|
+
include Lutaml::Model::Loggable
|
7
|
+
|
6
8
|
TYPES = {
|
7
9
|
attribute: :map_attribute,
|
8
10
|
element: :map_element,
|
@@ -92,6 +94,8 @@ module Lutaml
|
|
92
94
|
nil)
|
93
95
|
)
|
94
96
|
validate!(name, to, with, type: TYPES[:attribute])
|
97
|
+
warn_auto_handling(name) if name == "schemaLocation"
|
98
|
+
|
95
99
|
rule = XmlMappingRule.new(
|
96
100
|
name,
|
97
101
|
to: to,
|
@@ -19,8 +19,7 @@ module Lutaml
|
|
19
19
|
namespace_set: false,
|
20
20
|
prefix_set: false,
|
21
21
|
attribute: false,
|
22
|
-
default_namespace: nil
|
23
|
-
id: nil
|
22
|
+
default_namespace: nil
|
24
23
|
)
|
25
24
|
super(
|
26
25
|
name,
|
@@ -29,8 +28,7 @@ module Lutaml
|
|
29
28
|
render_default: render_default,
|
30
29
|
with: with,
|
31
30
|
delegate: delegate,
|
32
|
-
attribute: attribute
|
33
|
-
id: id
|
31
|
+
attribute: attribute
|
34
32
|
)
|
35
33
|
|
36
34
|
@namespace = if namespace.to_s == "inherit"
|
@@ -47,7 +45,6 @@ module Lutaml
|
|
47
45
|
|
48
46
|
@namespace_set = namespace_set
|
49
47
|
@prefix_set = prefix_set
|
50
|
-
@id = id
|
51
48
|
end
|
52
49
|
|
53
50
|
def namespace_set?
|