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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91ff48fd66cf1435e5e18508b24cc24f7cc92dc8b5d1619c7796932eb21dcea2
4
- data.tar.gz: 51eabdb2aede716ce86cea46325cad0b7b9f0da98df3235fc3b8596fe9f96bd9
3
+ metadata.gz: 5b36d07a850d4736c0634735a198f05dd34c49b906cd1265b3dd49266931eb22
4
+ data.tar.gz: cefd0559ad4010ecd2ab0a547ab87493b4958a480f6cb19ab317c248836b8f75
5
5
  SHA512:
6
- metadata.gz: 3ce25727c43d1c25f97e49c529c9ded5f51d8f2c13ae2766dba8c94d2c1d9a7c646bad564bfb9cd4c5555ee71395f6ff39023268e9e92044f10985c20dd826ca
7
- data.tar.gz: edf1d699e944a2427b947e1a262258e93623c44bc765a5f5497c13bd4d806984722ececd6318254c15196d435b2b809f44389a1be1d9cd5f33e005169b232eaf
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-09-23 15:00:23 UTC using RuboCop version 1.65.1.
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: 100
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: 34
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: 4
49
+ # Offense count: 6
49
50
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
50
51
  # AllowedMethods: refine
51
52
  Metrics/BlockLength:
52
- Max: 42
53
+ Max: 47
53
54
 
54
- # Offense count: 26
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: 45
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: 10
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: 7
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: 105
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: 98
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 options[:default].is_a?(Proc)
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
- # No additional attributes or methods required for now
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
- delegate: nil,
18
- mixed_content: false,
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.public_send(to)
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 namespace_set?
76
- @namespace_set
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.dup)
36
- subclass.instance_variable_set(:@mappings, @mappings.dup)
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] = klass.new
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
- apply_mappings(doc.to_h, format)
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).mixed_content? || options[:mixed_content]
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
- doc[rule.name.to_s] || doc[rule.name.to_sym]
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
- @ordered
482
+ !!@ordered
460
483
  end
461
484
 
462
- def ordered=(ordered)
463
- @ordered = ordered
485
+ def mixed?
486
+ !!@mixed
464
487
  end
465
488
 
466
489
  def key_exist?(hash, key)
@@ -116,6 +116,8 @@ module Lutaml
116
116
  def self.normalize_hash(hash)
117
117
  return hash["text"] if hash.keys == ["text"]
118
118
 
119
+ hash = hash.to_h if hash.is_a?(Lutaml::Model::MappingHash)
120
+
119
121
  hash.filter_map do |key, value|
120
122
  next if key == "text"
121
123
 
@@ -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)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.3.11"
5
+ VERSION = "0.3.14"
6
6
  end
7
7
  end
@@ -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) do
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
- prefixed_xml.text text
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.send(name, build_attributes(self)) do |xml|
142
- children.each { |child| child.to_xml(xml) }
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
- el.add_text(el, text)
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
@@ -21,6 +21,16 @@ module Lutaml
21
21
  name
22
22
  end
23
23
  end
24
+
25
+ def namespaced_name
26
+ if unprefixed_name == "lang"
27
+ name
28
+ elsif namespace
29
+ "#{namespace}:#{unprefixed_name}"
30
+ else
31
+ unprefixed_name
32
+ end
33
+ end
24
34
  end
25
35
  end
26
36
  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
- value = child.text? ? child.text : parse_element(child)
79
-
80
- hash[child.unprefixed_name] = if hash[child.unprefixed_name]
81
- [hash[child.unprefixed_name], value].flatten
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.unprefixed_name] = attr.value
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 build_namespace_attributes(klass, processed = {})
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 = namespace_attributes(xml_mapping)
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] = element.send(mapping_rule.to)
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
- if namespace_prefix
31
- "#{namespace_prefix}:#{@name}"
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
- @elements[name] = XmlMappingRule.new(
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
- @attributes[name] = XmlMappingRule.new(
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
- mixed_content: mixed_content,
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.11
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-04 00:00:00.000000000 Z
11
+ date: 2024-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor