lutaml-model 0.3.11 → 0.3.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +13 -11
- data/README.adoc +78 -2
- data/lib/lutaml/model/attribute.rb +12 -1
- data/lib/lutaml/model/key_value_mapping.rb +10 -0
- data/lib/lutaml/model/key_value_mapping_rule.rb +31 -1
- data/lib/lutaml/model/mapping_hash.rb +8 -0
- data/lib/lutaml/model/mapping_rule.rb +15 -33
- data/lib/lutaml/model/serialize.rb +32 -9
- data/lib/lutaml/model/type.rb +2 -0
- data/lib/lutaml/model/utils.rb +18 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +2 -4
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +18 -8
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +8 -1
- data/lib/lutaml/model/xml_adapter/xml_attribute.rb +10 -0
- data/lib/lutaml/model/xml_adapter/xml_document.rb +45 -14
- data/lib/lutaml/model/xml_adapter/xml_element.rb +13 -3
- data/lib/lutaml/model/xml_mapping.rb +33 -4
- data/lib/lutaml/model/xml_mapping_rule.rb +63 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b36d07a850d4736c0634735a198f05dd34c49b906cd1265b3dd49266931eb22
|
4
|
+
data.tar.gz: cefd0559ad4010ecd2ab0a547ab87493b4958a480f6cb19ab317c248836b8f75
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83fac7632d0eec4f5bd4339132b36d267877932cf34839eac6dbdefd0cd0e551988b73b3cd379b856af0df351e42694c157342efd8baba67e283e71d34b88171
|
7
|
+
data.tar.gz: 6befe6bfce13fb6175a04f8e0ac81794304d3c56c51feea343334c501346cba517db1f89ab3387ea3300e546d160d24af264048e9762c85debc4063300d57025
|
data/.rubocop_todo.yml
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2024-
|
3
|
+
# on 2024-10-23 12:15:48 UTC using RuboCop version 1.66.1.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
8
8
|
|
9
|
-
# Offense count:
|
9
|
+
# Offense count: 139
|
10
10
|
# This cop supports safe autocorrection (--autocorrect).
|
11
11
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
|
12
12
|
# URISchemes: http, https
|
@@ -30,7 +30,7 @@ Lint/DuplicateMethods:
|
|
30
30
|
Exclude:
|
31
31
|
- 'lib/lutaml/model/attribute.rb'
|
32
32
|
|
33
|
-
# Offense count:
|
33
|
+
# Offense count: 37
|
34
34
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
35
35
|
Metrics/AbcSize:
|
36
36
|
Exclude:
|
@@ -44,14 +44,15 @@ Metrics/AbcSize:
|
|
44
44
|
- 'lib/lutaml/model/xml_adapter/nokogiri_adapter.rb'
|
45
45
|
- 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
|
46
46
|
- 'lib/lutaml/model/xml_adapter/xml_document.rb'
|
47
|
+
- 'lib/lutaml/model/xml_mapping_rule.rb'
|
47
48
|
|
48
|
-
# Offense count:
|
49
|
+
# Offense count: 6
|
49
50
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
|
50
51
|
# AllowedMethods: refine
|
51
52
|
Metrics/BlockLength:
|
52
|
-
Max:
|
53
|
+
Max: 47
|
53
54
|
|
54
|
-
# Offense count:
|
55
|
+
# Offense count: 27
|
55
56
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
56
57
|
Metrics/CyclomaticComplexity:
|
57
58
|
Exclude:
|
@@ -63,7 +64,7 @@ Metrics/CyclomaticComplexity:
|
|
63
64
|
- 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
|
64
65
|
- 'lib/lutaml/model/xml_adapter/xml_document.rb'
|
65
66
|
|
66
|
-
# Offense count:
|
67
|
+
# Offense count: 50
|
67
68
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
68
69
|
Metrics/MethodLength:
|
69
70
|
Max: 51
|
@@ -71,7 +72,7 @@ Metrics/MethodLength:
|
|
71
72
|
# Offense count: 4
|
72
73
|
# Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
|
73
74
|
Metrics/ParameterLists:
|
74
|
-
Max:
|
75
|
+
Max: 12
|
75
76
|
|
76
77
|
# Offense count: 22
|
77
78
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
@@ -84,7 +85,7 @@ Metrics/PerceivedComplexity:
|
|
84
85
|
- 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
|
85
86
|
- 'lib/lutaml/model/xml_adapter/xml_document.rb'
|
86
87
|
|
87
|
-
# Offense count:
|
88
|
+
# Offense count: 8
|
88
89
|
# Configuration parameters: Prefixes, AllowedPatterns.
|
89
90
|
# Prefixes: when, with, without
|
90
91
|
RSpec/ContextWording:
|
@@ -93,8 +94,9 @@ RSpec/ContextWording:
|
|
93
94
|
- 'spec/lutaml/model/xml_adapter/oga_adapter_spec.rb'
|
94
95
|
- 'spec/lutaml/model/xml_adapter/ox_adapter_spec.rb'
|
95
96
|
- 'spec/lutaml/model/xml_adapter/xml_namespace_spec.rb'
|
97
|
+
- 'spec/lutaml/model/xml_mapping_spec.rb'
|
96
98
|
|
97
|
-
# Offense count:
|
99
|
+
# Offense count: 109
|
98
100
|
# Configuration parameters: CountAsOne.
|
99
101
|
RSpec/ExampleLength:
|
100
102
|
Max: 54
|
@@ -123,7 +125,7 @@ RSpec/MultipleDescribes:
|
|
123
125
|
- 'spec/lutaml/model/xml_adapter/xml_namespace_spec.rb'
|
124
126
|
- 'spec/lutaml/model/xml_adapter_spec.rb'
|
125
127
|
|
126
|
-
# Offense count:
|
128
|
+
# Offense count: 119
|
127
129
|
RSpec/MultipleExpectations:
|
128
130
|
Max: 14
|
129
131
|
|
data/README.adoc
CHANGED
@@ -423,6 +423,51 @@ end
|
|
423
423
|
----
|
424
424
|
====
|
425
425
|
|
426
|
+
=== Attribute as raw string
|
427
|
+
|
428
|
+
An attribute can be set to read the value as raw string for XML, by using the `raw: true` option.
|
429
|
+
|
430
|
+
Syntax:
|
431
|
+
|
432
|
+
[source,ruby]
|
433
|
+
----
|
434
|
+
attribute :name_of_attribute, :string, raw: true
|
435
|
+
----
|
436
|
+
|
437
|
+
.Using the `raw` option to read raw value for an XML attribute
|
438
|
+
[example]
|
439
|
+
====
|
440
|
+
[source,ruby]
|
441
|
+
----
|
442
|
+
class Person < Lutaml::Model::Serializable
|
443
|
+
attribute :name, :string
|
444
|
+
attribute :description, :string, raw: true
|
445
|
+
end
|
446
|
+
----
|
447
|
+
|
448
|
+
For the following xml
|
449
|
+
[source,xml]
|
450
|
+
----
|
451
|
+
<Person>
|
452
|
+
<name>John Doe</name>
|
453
|
+
<description>
|
454
|
+
A <b>fictional person</b> commonly used as a <i>placeholder name</i>.
|
455
|
+
</description>
|
456
|
+
</Person>
|
457
|
+
----
|
458
|
+
|
459
|
+
[source,ruby]
|
460
|
+
----
|
461
|
+
> Person.from_xml(xml)
|
462
|
+
> # <Person:0x0000000107a3ca70
|
463
|
+
@description="\n A <b>fictional person</b> commonly used as a <i>placeholder name</i>.\n ",
|
464
|
+
@element_order=["text", "name", "text", "description", "text"],
|
465
|
+
@name="John Doe",
|
466
|
+
@ordered=nil,
|
467
|
+
@validate_on_set=false>
|
468
|
+
----
|
469
|
+
====
|
470
|
+
|
426
471
|
== Serialization model mappings
|
427
472
|
|
428
473
|
=== General
|
@@ -612,7 +657,7 @@ end
|
|
612
657
|
|
613
658
|
[source,xml]
|
614
659
|
----
|
615
|
-
<example value=12><name>John Doe</name></example>
|
660
|
+
<example value="12"><name>John Doe</name></example>
|
616
661
|
----
|
617
662
|
|
618
663
|
[source,ruby]
|
@@ -622,6 +667,37 @@ end
|
|
622
667
|
> Example.new(value: 12).to_xml
|
623
668
|
> #<example value="12"></example>
|
624
669
|
----
|
670
|
+
|
671
|
+
The map_attribute method does not inherit the root element's namespace. If you need to specify a namespace for an attribute,
|
672
|
+
you must explicitly declare the namespace and prefix in the map_attribute method.
|
673
|
+
[example]
|
674
|
+
====
|
675
|
+
The following class will parse the XML snippet below:
|
676
|
+
|
677
|
+
[source,ruby]
|
678
|
+
----
|
679
|
+
class Attribute < Lutaml::Model::Serializable
|
680
|
+
attribute :value, :integer
|
681
|
+
|
682
|
+
xml do
|
683
|
+
root 'example'
|
684
|
+
map_attribute 'value', to: :value, namespace: "http://www.tech.co/XMI", prefix: "xl"
|
685
|
+
end
|
686
|
+
end
|
687
|
+
----
|
688
|
+
|
689
|
+
[source,xml]
|
690
|
+
----
|
691
|
+
<example xl:value="20" xmlns:xl="http://www.tech.co/XMI"></example>
|
692
|
+
----
|
693
|
+
|
694
|
+
[source,ruby]
|
695
|
+
----
|
696
|
+
> Attribute.from_xml(xml)
|
697
|
+
> #<Attribute:0x0000000109436db8 @value=20>
|
698
|
+
> Attribute.new(value: 20).to_xml
|
699
|
+
> #<example xmlns:xl=\"http://www.tech.co/XMI\" xl:value=\"20\"/>
|
700
|
+
----
|
625
701
|
====
|
626
702
|
|
627
703
|
|
@@ -698,7 +774,7 @@ end
|
|
698
774
|
|
699
775
|
[source,xml]
|
700
776
|
----
|
701
|
-
<example value=12><name>John Doe</name> is my moniker.</example>
|
777
|
+
<example value="12"><name>John Doe</name> is my moniker.</example>
|
702
778
|
----
|
703
779
|
|
704
780
|
[source,ruby]
|
@@ -7,6 +7,7 @@ module Lutaml
|
|
7
7
|
@name = name
|
8
8
|
@type = cast_type(type)
|
9
9
|
@options = options
|
10
|
+
@raw = !!options[:raw]
|
10
11
|
|
11
12
|
if collection?
|
12
13
|
validate_collection_range
|
@@ -14,6 +15,10 @@ module Lutaml
|
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
18
|
+
def delegate
|
19
|
+
@options[:delegate]
|
20
|
+
end
|
21
|
+
|
17
22
|
def cast_type(type)
|
18
23
|
case type
|
19
24
|
when Class
|
@@ -45,8 +50,14 @@ module Lutaml
|
|
45
50
|
!collection?
|
46
51
|
end
|
47
52
|
|
53
|
+
def raw?
|
54
|
+
@raw
|
55
|
+
end
|
56
|
+
|
48
57
|
def default
|
49
|
-
value = if
|
58
|
+
value = if delegate
|
59
|
+
type.attributes[to].default
|
60
|
+
elsif options[:default].is_a?(Proc)
|
50
61
|
options[:default].call
|
51
62
|
else
|
52
63
|
options[:default]
|
@@ -42,6 +42,16 @@ module Lutaml
|
|
42
42
|
raise IncorrectMappingArgumentsError.new(msg)
|
43
43
|
end
|
44
44
|
end
|
45
|
+
|
46
|
+
def deep_dup
|
47
|
+
self.class.new.tap do |new_mapping|
|
48
|
+
new_mapping.instance_variable_set(:@mappings, duplicate_mappings)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def duplicate_mappings
|
53
|
+
@mappings.map(&:deep_dup)
|
54
|
+
end
|
45
55
|
end
|
46
56
|
end
|
47
57
|
end
|
@@ -3,7 +3,37 @@ require_relative "mapping_rule"
|
|
3
3
|
module Lutaml
|
4
4
|
module Model
|
5
5
|
class KeyValueMappingRule < MappingRule
|
6
|
-
|
6
|
+
attr_reader :child_mappings
|
7
|
+
|
8
|
+
def initialize(
|
9
|
+
name,
|
10
|
+
to:,
|
11
|
+
render_nil: false,
|
12
|
+
with: {},
|
13
|
+
delegate: nil,
|
14
|
+
child_mappings: nil
|
15
|
+
)
|
16
|
+
super(
|
17
|
+
name,
|
18
|
+
to: to,
|
19
|
+
render_nil: render_nil,
|
20
|
+
with: with,
|
21
|
+
delegate: delegate,
|
22
|
+
)
|
23
|
+
|
24
|
+
@child_mappings = child_mappings
|
25
|
+
end
|
26
|
+
|
27
|
+
def deep_dup
|
28
|
+
self.class.new(
|
29
|
+
name.dup,
|
30
|
+
to: to.dup,
|
31
|
+
render_nil: render_nil.dup,
|
32
|
+
with: Utils.deep_dup(custom_methods),
|
33
|
+
delegate: delegate,
|
34
|
+
child_mappings: Utils.deep_dup(child_mappings),
|
35
|
+
)
|
36
|
+
end
|
7
37
|
end
|
8
38
|
end
|
9
39
|
end
|
@@ -14,6 +14,14 @@ module Lutaml
|
|
14
14
|
@item_order&.map { |key| normalize(key) } || keys
|
15
15
|
end
|
16
16
|
|
17
|
+
def fetch(key)
|
18
|
+
self[key.to_s] || self[key.to_sym]
|
19
|
+
end
|
20
|
+
|
21
|
+
def key_exist?(key)
|
22
|
+
key?(key.to_s) || key?(key.to_sym)
|
23
|
+
end
|
24
|
+
|
17
25
|
def item_order=(order)
|
18
26
|
raise "`item order` must be an array" unless order.is_a?(Array)
|
19
27
|
|
@@ -5,56 +5,46 @@ module Lutaml
|
|
5
5
|
:to,
|
6
6
|
:render_nil,
|
7
7
|
:custom_methods,
|
8
|
-
:delegate
|
9
|
-
:mixed_content,
|
10
|
-
:child_mappings
|
8
|
+
:delegate
|
11
9
|
|
12
10
|
def initialize(
|
13
11
|
name,
|
14
12
|
to:,
|
15
13
|
render_nil: false,
|
16
14
|
with: {},
|
17
|
-
|
18
|
-
|
19
|
-
namespace_set: false,
|
20
|
-
prefix_set: false,
|
21
|
-
child_mappings: nil
|
15
|
+
attribute: false,
|
16
|
+
delegate: nil
|
22
17
|
)
|
23
18
|
@name = name
|
24
19
|
@to = to
|
25
20
|
@render_nil = render_nil
|
26
21
|
@custom_methods = with
|
22
|
+
@attribute = attribute
|
27
23
|
@delegate = delegate
|
28
|
-
@mixed_content = mixed_content
|
29
|
-
@namespace_set = namespace_set
|
30
|
-
@prefix_set = prefix_set
|
31
|
-
@child_mappings = child_mappings
|
32
24
|
end
|
33
25
|
|
34
26
|
alias from name
|
35
27
|
alias render_nil? render_nil
|
36
28
|
|
37
|
-
def prefixed_name
|
38
|
-
if prefix
|
39
|
-
"#{prefix}:#{name}"
|
40
|
-
else
|
41
|
-
name
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
29
|
def serialize_attribute(model, element, doc)
|
46
30
|
if custom_methods[:to]
|
47
31
|
model.send(custom_methods[:to], model, element, doc)
|
48
32
|
end
|
49
33
|
end
|
50
34
|
|
35
|
+
def to_value_for(model)
|
36
|
+
if delegate
|
37
|
+
model.public_send(delegate).public_send(to)
|
38
|
+
else
|
39
|
+
model.public_send(to)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
51
43
|
def serialize(model, parent = nil, doc = nil)
|
52
44
|
if custom_methods[:to]
|
53
45
|
model.send(custom_methods[:to], model, parent, doc)
|
54
|
-
elsif delegate
|
55
|
-
model.public_send(delegate).public_send(to)
|
56
46
|
else
|
57
|
-
model
|
47
|
+
to_value_for(model)
|
58
48
|
end
|
59
49
|
end
|
60
50
|
|
@@ -72,16 +62,8 @@ module Lutaml
|
|
72
62
|
end
|
73
63
|
end
|
74
64
|
|
75
|
-
def
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
def prefix_set?
|
80
|
-
@prefix_set
|
81
|
-
end
|
82
|
-
|
83
|
-
def content_mapping?
|
84
|
-
name.nil?
|
65
|
+
def deep_dup
|
66
|
+
raise NotImplementedError, "Subclasses must implement `deep_dup`."
|
85
67
|
end
|
86
68
|
end
|
87
69
|
end
|
@@ -32,8 +32,8 @@ module Lutaml
|
|
32
32
|
@mappings ||= {}
|
33
33
|
@attributes ||= {}
|
34
34
|
|
35
|
-
subclass.instance_variable_set(:@attributes, @attributes
|
36
|
-
subclass.instance_variable_set(:@mappings, @mappings
|
35
|
+
subclass.instance_variable_set(:@attributes, Utils.deep_dup(@attributes))
|
36
|
+
subclass.instance_variable_set(:@mappings, Utils.deep_dup(@mappings))
|
37
37
|
subclass.instance_variable_set(:@model, subclass)
|
38
38
|
end
|
39
39
|
|
@@ -55,6 +55,14 @@ module Lutaml
|
|
55
55
|
!!@ordered
|
56
56
|
end
|
57
57
|
|
58
|
+
Utils.add_method_if_not_defined(klass, :mixed=) do |mixed|
|
59
|
+
@mixed = mixed
|
60
|
+
end
|
61
|
+
|
62
|
+
Utils.add_method_if_not_defined(klass, :mixed?) do
|
63
|
+
!!@mixed
|
64
|
+
end
|
65
|
+
|
58
66
|
Utils.add_method_if_not_defined(klass, :element_order=) do |order|
|
59
67
|
@element_order = order
|
60
68
|
end
|
@@ -85,7 +93,7 @@ module Lutaml
|
|
85
93
|
Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
|
86
94
|
define_method(format) do |&block|
|
87
95
|
klass = format == :xml ? XmlMapping : KeyValueMapping
|
88
|
-
mappings[format]
|
96
|
+
mappings[format] ||= klass.new
|
89
97
|
mappings[format].instance_eval(&block)
|
90
98
|
|
91
99
|
if format == :xml && !mappings[format].root_element
|
@@ -107,7 +115,12 @@ module Lutaml
|
|
107
115
|
end
|
108
116
|
end
|
109
117
|
|
110
|
-
|
118
|
+
if format == :xml
|
119
|
+
doc_hash = doc.parse_element(doc.root, self, :xml)
|
120
|
+
apply_mappings(doc_hash, format)
|
121
|
+
else
|
122
|
+
apply_mappings(doc.to_h, format)
|
123
|
+
end
|
111
124
|
end
|
112
125
|
|
113
126
|
define_method(:"to_#{format}") do |instance|
|
@@ -280,6 +293,12 @@ module Lutaml
|
|
280
293
|
attributes[rule.delegate].type.attributes[rule.to]
|
281
294
|
end
|
282
295
|
|
296
|
+
def attribute_for_child(child_name, format)
|
297
|
+
mapping_rule = mappings_for(format).find_by_name(child_name)
|
298
|
+
|
299
|
+
attribute_for_rule(mapping_rule) if mapping_rule
|
300
|
+
end
|
301
|
+
|
283
302
|
def apply_mappings(doc, format, options = {})
|
284
303
|
instance = options[:instance] || model.new
|
285
304
|
return instance if Utils.blank?(doc)
|
@@ -326,7 +345,8 @@ module Lutaml
|
|
326
345
|
|
327
346
|
if instance.respond_to?(:ordered=) && doc.is_a?(Lutaml::Model::MappingHash)
|
328
347
|
instance.element_order = doc.item_order
|
329
|
-
instance.ordered = mappings_for(:xml).
|
348
|
+
instance.ordered = mappings_for(:xml).ordered? || options[:ordered]
|
349
|
+
instance.mixed = mappings_for(:xml).mixed_content? || options[:mixed_content]
|
330
350
|
end
|
331
351
|
|
332
352
|
if doc["__schema_location"]
|
@@ -342,8 +362,10 @@ module Lutaml
|
|
342
362
|
|
343
363
|
value = if rule.content_mapping?
|
344
364
|
doc["text"]
|
365
|
+
elsif doc.key_exist?(rule.namespaced_name)
|
366
|
+
doc.fetch(rule.namespaced_name)
|
345
367
|
else
|
346
|
-
|
368
|
+
rule.to_value_for(instance)
|
347
369
|
end
|
348
370
|
|
349
371
|
value = normalize_xml_value(value, rule)
|
@@ -407,6 +429,7 @@ module Lutaml
|
|
407
429
|
end
|
408
430
|
|
409
431
|
attr_accessor :element_order, :schema_location
|
432
|
+
attr_writer :ordered, :mixed
|
410
433
|
|
411
434
|
def initialize(attrs = {})
|
412
435
|
@validate_on_set = attrs.delete(:validate_on_set) || false
|
@@ -456,11 +479,11 @@ module Lutaml
|
|
456
479
|
end
|
457
480
|
|
458
481
|
def ordered?
|
459
|
-
|
482
|
+
!!@ordered
|
460
483
|
end
|
461
484
|
|
462
|
-
def
|
463
|
-
|
485
|
+
def mixed?
|
486
|
+
!!@mixed
|
464
487
|
end
|
465
488
|
|
466
489
|
def key_exist?(hash, key)
|
data/lib/lutaml/model/type.rb
CHANGED
data/lib/lutaml/model/utils.rb
CHANGED
@@ -50,6 +50,24 @@ module Lutaml
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
def deep_dup(hash)
|
54
|
+
return hash if hash.nil?
|
55
|
+
|
56
|
+
new_hash = {}
|
57
|
+
|
58
|
+
hash.each do |key, value|
|
59
|
+
new_hash[key] = if value.is_a?(Hash)
|
60
|
+
deep_dup(value)
|
61
|
+
elsif value.respond_to?(:deep_dup)
|
62
|
+
value.deep_dup
|
63
|
+
else
|
64
|
+
value.dup
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
new_hash
|
69
|
+
end
|
70
|
+
|
53
71
|
private
|
54
72
|
|
55
73
|
def camelize_part(part)
|
data/lib/lutaml/model/version.rb
CHANGED
@@ -63,11 +63,9 @@ module Lutaml
|
|
63
63
|
self
|
64
64
|
end
|
65
65
|
|
66
|
-
def method_missing(method_name, *args)
|
66
|
+
def method_missing(method_name, *args, &block)
|
67
67
|
if block_given?
|
68
|
-
xml.public_send(method_name, *args)
|
69
|
-
yield(xml)
|
70
|
-
end
|
68
|
+
xml.public_send(method_name, *args, &block)
|
71
69
|
else
|
72
70
|
xml.public_send(method_name, *args)
|
73
71
|
end
|
@@ -55,6 +55,7 @@ module Lutaml
|
|
55
55
|
end
|
56
56
|
|
57
57
|
index_hash = {}
|
58
|
+
content = []
|
58
59
|
|
59
60
|
element.element_order.each do |name|
|
60
61
|
index_hash[name] ||= -1
|
@@ -71,7 +72,11 @@ module Lutaml
|
|
71
72
|
text = xml_mapping.content_mapping.serialize(element)
|
72
73
|
text = text[curr_index] if text.is_a?(Array)
|
73
74
|
|
74
|
-
|
75
|
+
if element.mixed?
|
76
|
+
prefixed_xml.text text
|
77
|
+
else
|
78
|
+
content << text
|
79
|
+
end
|
75
80
|
elsif !value.nil? || element_rule.render_nil?
|
76
81
|
value = value[curr_index] if attribute_def.collection?
|
77
82
|
|
@@ -87,12 +92,14 @@ module Lutaml
|
|
87
92
|
)
|
88
93
|
end
|
89
94
|
end
|
95
|
+
|
96
|
+
prefixed_xml.text content.join
|
90
97
|
end
|
91
98
|
end
|
92
99
|
end
|
93
100
|
|
94
101
|
class NokogiriElement < XmlElement
|
95
|
-
def initialize(node, root_node: nil)
|
102
|
+
def initialize(node, root_node: nil, default_namespace: nil)
|
96
103
|
if root_node
|
97
104
|
node.namespaces.each do |prefix, name|
|
98
105
|
namespace = XmlNamespace.new(name, prefix)
|
@@ -116,14 +123,15 @@ module Lutaml
|
|
116
123
|
namespace_prefix: attr.namespace&.prefix,
|
117
124
|
)
|
118
125
|
end
|
119
|
-
|
126
|
+
default_namespace = node.namespace&.href if root_node.nil?
|
120
127
|
super(
|
121
128
|
node.name,
|
122
129
|
attributes,
|
123
|
-
parse_all_children(node, root_node: root_node || self),
|
130
|
+
parse_all_children(node, root_node: root_node || self, default_namespace: default_namespace),
|
124
131
|
node.text,
|
125
132
|
parent_document: root_node,
|
126
133
|
namespace_prefix: node.namespace&.prefix,
|
134
|
+
default_namespace: default_namespace
|
127
135
|
)
|
128
136
|
end
|
129
137
|
|
@@ -138,8 +146,10 @@ module Lutaml
|
|
138
146
|
if name == "text"
|
139
147
|
builder.text(text)
|
140
148
|
else
|
141
|
-
builder.
|
142
|
-
children.each
|
149
|
+
builder.public_send(name, build_attributes(self)) do |xml|
|
150
|
+
children.each do |child|
|
151
|
+
child.to_xml(xml)
|
152
|
+
end
|
143
153
|
end
|
144
154
|
end
|
145
155
|
|
@@ -154,9 +164,9 @@ module Lutaml
|
|
154
164
|
end
|
155
165
|
end
|
156
166
|
|
157
|
-
def parse_all_children(node, root_node: nil)
|
167
|
+
def parse_all_children(node, root_node: nil, default_namespace: nil)
|
158
168
|
node.children.map do |child|
|
159
|
-
NokogiriElement.new(child, root_node: root_node)
|
169
|
+
NokogiriElement.new(child, root_node: root_node, default_namespace: default_namespace)
|
160
170
|
end
|
161
171
|
end
|
162
172
|
|
@@ -42,6 +42,7 @@ module Lutaml
|
|
42
42
|
builder.create_and_add_element(tag_name,
|
43
43
|
attributes: attributes) do |el|
|
44
44
|
index_hash = {}
|
45
|
+
content = []
|
45
46
|
|
46
47
|
element.element_order.each do |name|
|
47
48
|
index_hash[name] ||= -1
|
@@ -58,7 +59,11 @@ module Lutaml
|
|
58
59
|
text = element.send(xml_mapping.content_mapping.to)
|
59
60
|
text = text[curr_index] if text.is_a?(Array)
|
60
61
|
|
61
|
-
|
62
|
+
if element.mixed?
|
63
|
+
el.add_text(el, text)
|
64
|
+
else
|
65
|
+
content << text
|
66
|
+
end
|
62
67
|
elsif !value.nil? || element_rule.render_nil?
|
63
68
|
value = value[curr_index] if attribute_def.collection?
|
64
69
|
|
@@ -74,6 +79,8 @@ module Lutaml
|
|
74
79
|
)
|
75
80
|
end
|
76
81
|
end
|
82
|
+
|
83
|
+
el.add_text(el, content.join)
|
77
84
|
end
|
78
85
|
end
|
79
86
|
end
|
@@ -66,24 +66,43 @@ module Lutaml
|
|
66
66
|
options[:tag_name] = rule.name
|
67
67
|
|
68
68
|
options[:mapper_class] = attribute&.type if attribute
|
69
|
+
options[:namespace_set] = set_namespace?(rule)
|
69
70
|
|
70
71
|
options
|
71
72
|
end
|
72
73
|
|
73
|
-
def parse_element(element)
|
74
|
+
def parse_element(element, klass = nil, format = nil)
|
74
75
|
result = Lutaml::Model::MappingHash.new
|
75
76
|
result.item_order = element.order
|
76
77
|
|
77
78
|
element.children.each_with_object(result) do |child, hash|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
79
|
+
attr = klass.attribute_for_child(child.name, format) if klass&.<= Serialize
|
80
|
+
|
81
|
+
value = if child.text?
|
82
|
+
child.text
|
83
|
+
elsif attr&.raw?
|
84
|
+
child.children.map do |c|
|
85
|
+
next c.text if c.text?
|
86
|
+
|
87
|
+
c.to_xml.doc.root.to_xml({})
|
88
|
+
end.join
|
89
|
+
else
|
90
|
+
parse_element(child, attr&.type || klass, format)
|
91
|
+
end
|
92
|
+
|
93
|
+
hash[child.namespaced_name] = if hash[child.namespaced_name]
|
94
|
+
[hash[child.namespaced_name], value].flatten
|
82
95
|
else
|
83
96
|
value
|
84
97
|
end
|
85
98
|
end
|
86
99
|
|
100
|
+
result.merge(attributes_hash(element))
|
101
|
+
end
|
102
|
+
|
103
|
+
def attributes_hash(element)
|
104
|
+
result = Lutaml::Model::MappingHash.new
|
105
|
+
|
87
106
|
element.attributes.each_value do |attr|
|
88
107
|
if attr.unprefixed_name == "schemaLocation"
|
89
108
|
result["__schema_location"] = {
|
@@ -92,7 +111,7 @@ module Lutaml
|
|
92
111
|
schema_location: attr.value,
|
93
112
|
}
|
94
113
|
else
|
95
|
-
result[attr.
|
114
|
+
result[attr.namespaced_name] = attr.value
|
96
115
|
end
|
97
116
|
end
|
98
117
|
|
@@ -231,13 +250,17 @@ module Lutaml
|
|
231
250
|
mapper_class ? mapper_class.mappings_for(:xml).mixed_content? : false
|
232
251
|
end
|
233
252
|
|
234
|
-
def
|
253
|
+
def set_namespace?(rule)
|
254
|
+
rule.nil? || !rule.namespace_set? || !rule.namespace.nil?
|
255
|
+
end
|
256
|
+
|
257
|
+
def build_namespace_attributes(klass, processed = {}, options = {})
|
235
258
|
xml_mappings = klass.mappings_for(:xml)
|
236
259
|
attributes = klass.attributes
|
237
260
|
|
238
261
|
attrs = {}
|
239
262
|
|
240
|
-
if xml_mappings.namespace_uri
|
263
|
+
if xml_mappings.namespace_uri && set_namespace?(options[:caller_rule])
|
241
264
|
prefixed_name = [
|
242
265
|
"xmlns",
|
243
266
|
xml_mappings.namespace_prefix,
|
@@ -262,10 +285,10 @@ module Lutaml
|
|
262
285
|
next unless type
|
263
286
|
|
264
287
|
if type <= Lutaml::Model::Serialize
|
265
|
-
attrs = attrs.merge(build_namespace_attributes(type, processed))
|
288
|
+
attrs = attrs.merge(build_namespace_attributes(type, processed, { caller_rule: mapping_rule }))
|
266
289
|
end
|
267
290
|
|
268
|
-
if mapping_rule.namespace
|
291
|
+
if mapping_rule.namespace && mapping_rule.prefix && mapping_rule.name != "lang"
|
269
292
|
attrs["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
|
270
293
|
end
|
271
294
|
end
|
@@ -274,23 +297,31 @@ module Lutaml
|
|
274
297
|
end
|
275
298
|
|
276
299
|
def build_attributes(element, xml_mapping, options = {})
|
277
|
-
attrs =
|
300
|
+
attrs = if options.fetch(:namespace_set, true)
|
301
|
+
namespace_attributes(xml_mapping)
|
302
|
+
else
|
303
|
+
{}
|
304
|
+
end
|
305
|
+
|
306
|
+
if element.respond_to?(:schema_location) && element.schema_location
|
307
|
+
attrs.merge!(element.schema_location.to_xml_attributes)
|
308
|
+
end
|
278
309
|
|
279
310
|
xml_mapping.attributes.each_with_object(attrs) do |mapping_rule, hash|
|
280
311
|
next if options[:except]&.include?(mapping_rule.to)
|
281
312
|
next if mapping_rule.custom_methods[:to]
|
282
313
|
|
283
|
-
if mapping_rule.namespace
|
314
|
+
if mapping_rule.namespace && mapping_rule.prefix && mapping_rule.name != "lang"
|
284
315
|
hash["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
|
285
316
|
end
|
286
317
|
|
287
|
-
hash[mapping_rule.prefixed_name] =
|
318
|
+
hash[mapping_rule.prefixed_name] = mapping_rule.to_value_for(element)
|
288
319
|
end
|
289
320
|
|
290
321
|
xml_mapping.elements.each_with_object(attrs) do |mapping_rule, hash|
|
291
322
|
next if options[:except]&.include?(mapping_rule.to)
|
292
323
|
|
293
|
-
if mapping_rule.namespace
|
324
|
+
if mapping_rule.namespace && mapping_rule.prefix
|
294
325
|
hash["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
|
295
326
|
end
|
296
327
|
end
|
@@ -16,7 +16,8 @@ module Lutaml
|
|
16
16
|
children = [],
|
17
17
|
text = nil,
|
18
18
|
parent_document: nil,
|
19
|
-
namespace_prefix: nil
|
19
|
+
namespace_prefix: nil,
|
20
|
+
default_namespace: nil
|
20
21
|
)
|
21
22
|
@name = extract_name(name)
|
22
23
|
@namespace_prefix = namespace_prefix || extract_namespace_prefix(name)
|
@@ -24,11 +25,20 @@ module Lutaml
|
|
24
25
|
@children = children
|
25
26
|
@text = text
|
26
27
|
@parent_document = parent_document
|
28
|
+
@default_namespace = default_namespace
|
27
29
|
end
|
28
30
|
|
29
31
|
def name
|
30
|
-
|
31
|
-
|
32
|
+
return @name unless namespace_prefix
|
33
|
+
|
34
|
+
"#{namespace_prefix}:#{@name}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def namespaced_name
|
38
|
+
if namespaces[namespace_prefix] && !text?
|
39
|
+
"#{namespaces[namespace_prefix].uri}:#{@name}"
|
40
|
+
elsif @default_namespace && !text?
|
41
|
+
"#{@default_namespace}:#{name}"
|
32
42
|
else
|
33
43
|
@name
|
34
44
|
end
|
@@ -6,7 +6,8 @@ module Lutaml
|
|
6
6
|
attr_reader :root_element,
|
7
7
|
:namespace_uri,
|
8
8
|
:namespace_prefix,
|
9
|
-
:mixed_content
|
9
|
+
:mixed_content,
|
10
|
+
:ordered
|
10
11
|
|
11
12
|
def initialize
|
12
13
|
@elements = {}
|
@@ -16,10 +17,12 @@ module Lutaml
|
|
16
17
|
end
|
17
18
|
|
18
19
|
alias mixed_content? mixed_content
|
20
|
+
alias ordered? ordered
|
19
21
|
|
20
|
-
def root(name, mixed: false)
|
22
|
+
def root(name, mixed: false, ordered: false)
|
21
23
|
@root_element = name
|
22
24
|
@mixed_content = mixed
|
25
|
+
@ordered = ordered || mixed # mixed contenet will always be ordered
|
23
26
|
end
|
24
27
|
|
25
28
|
def prefixed_root
|
@@ -49,17 +52,19 @@ module Lutaml
|
|
49
52
|
)
|
50
53
|
validate!(name, to, with)
|
51
54
|
|
52
|
-
|
55
|
+
rule = XmlMappingRule.new(
|
53
56
|
name,
|
54
57
|
to: to,
|
55
58
|
render_nil: render_nil,
|
56
59
|
with: with,
|
57
60
|
delegate: delegate,
|
58
61
|
namespace: namespace,
|
62
|
+
default_namespace: namespace_uri,
|
59
63
|
prefix: prefix,
|
60
64
|
namespace_set: namespace_set != false,
|
61
65
|
prefix_set: prefix_set != false,
|
62
66
|
)
|
67
|
+
@elements[rule.namespaced_name] = rule
|
63
68
|
end
|
64
69
|
|
65
70
|
def map_attribute(
|
@@ -75,7 +80,7 @@ module Lutaml
|
|
75
80
|
)
|
76
81
|
validate!(name, to, with)
|
77
82
|
|
78
|
-
|
83
|
+
rule = XmlMappingRule.new(
|
79
84
|
name,
|
80
85
|
to: to,
|
81
86
|
render_nil: render_nil,
|
@@ -83,9 +88,12 @@ module Lutaml
|
|
83
88
|
delegate: delegate,
|
84
89
|
namespace: namespace,
|
85
90
|
prefix: prefix,
|
91
|
+
attribute: true,
|
92
|
+
default_namespace: namespace_uri,
|
86
93
|
namespace_set: namespace_set != false,
|
87
94
|
prefix_set: prefix_set != false,
|
88
95
|
)
|
96
|
+
@attributes[rule.namespaced_name] = rule
|
89
97
|
end
|
90
98
|
|
91
99
|
# rubocop:enable Metrics/ParameterLists
|
@@ -158,6 +166,27 @@ module Lutaml
|
|
158
166
|
end
|
159
167
|
end
|
160
168
|
end
|
169
|
+
|
170
|
+
def deep_dup
|
171
|
+
self.class.new.tap do |xml_mapping|
|
172
|
+
xml_mapping.root(@root_element.dup, mixed: @mixed_content, ordered: @ordered)
|
173
|
+
xml_mapping.namespace(@namespace_uri.dup, @namespace_prefix.dup)
|
174
|
+
|
175
|
+
xml_mapping.instance_variable_set(:@attributes, dup_mappings(@attributes))
|
176
|
+
xml_mapping.instance_variable_set(:@elements, dup_mappings(@elements))
|
177
|
+
xml_mapping.instance_variable_set(:@content_mapping, @content_mapping&.deep_dup)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def dup_mappings(mappings)
|
182
|
+
new_mappings = {}
|
183
|
+
|
184
|
+
mappings.each do |key, mapping_rule|
|
185
|
+
new_mappings[key] = mapping_rule.deep_dup
|
186
|
+
end
|
187
|
+
|
188
|
+
new_mappings
|
189
|
+
end
|
161
190
|
end
|
162
191
|
end
|
163
192
|
end
|
@@ -3,7 +3,7 @@ require_relative "mapping_rule"
|
|
3
3
|
module Lutaml
|
4
4
|
module Model
|
5
5
|
class XmlMappingRule < MappingRule
|
6
|
-
attr_reader :namespace, :prefix
|
6
|
+
attr_reader :namespace, :prefix, :mixed_content, :default_namespace
|
7
7
|
|
8
8
|
def initialize(
|
9
9
|
name,
|
@@ -15,7 +15,9 @@ module Lutaml
|
|
15
15
|
prefix: nil,
|
16
16
|
mixed_content: false,
|
17
17
|
namespace_set: false,
|
18
|
-
prefix_set: false
|
18
|
+
prefix_set: false,
|
19
|
+
attribute: false,
|
20
|
+
default_namespace: nil
|
19
21
|
)
|
20
22
|
super(
|
21
23
|
name,
|
@@ -23,9 +25,7 @@ module Lutaml
|
|
23
25
|
render_nil: render_nil,
|
24
26
|
with: with,
|
25
27
|
delegate: delegate,
|
26
|
-
|
27
|
-
namespace_set: namespace_set,
|
28
|
-
prefix_set: prefix_set,
|
28
|
+
attribute: attribute,
|
29
29
|
)
|
30
30
|
|
31
31
|
@namespace = if namespace.to_s == "inherit"
|
@@ -35,6 +35,64 @@ module Lutaml
|
|
35
35
|
namespace
|
36
36
|
end
|
37
37
|
@prefix = prefix
|
38
|
+
@mixed_content = mixed_content
|
39
|
+
|
40
|
+
@default_namespace = default_namespace
|
41
|
+
|
42
|
+
@namespace_set = namespace_set
|
43
|
+
@prefix_set = prefix_set
|
44
|
+
end
|
45
|
+
|
46
|
+
def namespace_set?
|
47
|
+
!!@namespace_set
|
48
|
+
end
|
49
|
+
|
50
|
+
def prefix_set?
|
51
|
+
!!@prefix_set
|
52
|
+
end
|
53
|
+
|
54
|
+
def content_mapping?
|
55
|
+
name.nil?
|
56
|
+
end
|
57
|
+
|
58
|
+
def mixed_content?
|
59
|
+
!!@mixed_content
|
60
|
+
end
|
61
|
+
|
62
|
+
def prefixed_name
|
63
|
+
if prefix
|
64
|
+
"#{prefix}:#{name}"
|
65
|
+
else
|
66
|
+
name
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def namespaced_name
|
71
|
+
if name == "lang"
|
72
|
+
"#{prefix}:#{name}"
|
73
|
+
elsif namespace_set? || @attribute
|
74
|
+
[namespace, name].compact.join(":")
|
75
|
+
elsif default_namespace
|
76
|
+
"#{default_namespace}:#{name}"
|
77
|
+
else
|
78
|
+
name
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def deep_dup
|
83
|
+
self.class.new(
|
84
|
+
name.dup,
|
85
|
+
to: to,
|
86
|
+
render_nil: render_nil,
|
87
|
+
with: Utils.deep_dup(custom_methods),
|
88
|
+
delegate: delegate,
|
89
|
+
namespace: namespace.dup,
|
90
|
+
prefix: prefix.dup,
|
91
|
+
mixed_content: mixed_content,
|
92
|
+
namespace_set: namespace_set?,
|
93
|
+
prefix_set: prefix_set?,
|
94
|
+
default_namespace: default_namespace.dup,
|
95
|
+
)
|
38
96
|
end
|
39
97
|
end
|
40
98
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lutaml-model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|