lutaml-model 0.3.11 → 0.3.14

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 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