lutaml-model 0.5.4 → 0.6.0

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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +58 -21
  3. data/Gemfile +1 -0
  4. data/README.adoc +1112 -264
  5. data/lib/lutaml/model/attribute.rb +33 -10
  6. data/lib/lutaml/model/choice.rb +56 -0
  7. data/lib/lutaml/model/config.rb +1 -0
  8. data/lib/lutaml/model/error/choice_lower_bound_error.rb +9 -0
  9. data/lib/lutaml/model/error/choice_upper_bound_error.rb +9 -0
  10. data/lib/lutaml/model/error/import_model_with_root_error.rb +9 -0
  11. data/lib/lutaml/model/error/incorrect_sequence_error.rb +9 -0
  12. data/lib/lutaml/model/error/invalid_choice_range_error.rb +20 -0
  13. data/lib/lutaml/model/error/no_root_mapping_error.rb +9 -0
  14. data/lib/lutaml/model/error/no_root_namespace_error.rb +9 -0
  15. data/lib/lutaml/model/error/unknown_sequence_mapping_error.rb +9 -0
  16. data/lib/lutaml/model/error.rb +8 -0
  17. data/lib/lutaml/model/json_adapter/standard_json_adapter.rb +6 -1
  18. data/lib/lutaml/model/key_value_mapping.rb +3 -1
  19. data/lib/lutaml/model/key_value_mapping_rule.rb +4 -2
  20. data/lib/lutaml/model/liquefiable.rb +59 -0
  21. data/lib/lutaml/model/mapping_hash.rb +1 -1
  22. data/lib/lutaml/model/mapping_rule.rb +15 -2
  23. data/lib/lutaml/model/schema/xml_compiler.rb +68 -26
  24. data/lib/lutaml/model/schema_location.rb +7 -0
  25. data/lib/lutaml/model/sequence.rb +71 -0
  26. data/lib/lutaml/model/serialize.rb +125 -35
  27. data/lib/lutaml/model/type/decimal.rb +0 -4
  28. data/lib/lutaml/model/type/time.rb +3 -3
  29. data/lib/lutaml/model/utils.rb +19 -15
  30. data/lib/lutaml/model/validation.rb +12 -1
  31. data/lib/lutaml/model/version.rb +1 -1
  32. data/lib/lutaml/model/xml_adapter/builder/oga.rb +10 -7
  33. data/lib/lutaml/model/xml_adapter/builder/ox.rb +20 -13
  34. data/lib/lutaml/model/xml_adapter/element.rb +32 -0
  35. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +8 -8
  36. data/lib/lutaml/model/xml_adapter/oga/element.rb +14 -13
  37. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +86 -19
  38. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +19 -15
  39. data/lib/lutaml/model/xml_adapter/xml_document.rb +74 -13
  40. data/lib/lutaml/model/xml_adapter/xml_element.rb +57 -3
  41. data/lib/lutaml/model/xml_mapping.rb +49 -7
  42. data/lib/lutaml/model/xml_mapping_rule.rb +8 -3
  43. data/lib/lutaml/model.rb +1 -0
  44. data/lutaml-model.gemspec +5 -0
  45. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +75 -0
  46. data/spec/ceramic_spec.rb +39 -0
  47. data/spec/fixtures/ceramic.rb +23 -0
  48. data/spec/fixtures/xml/address_example_260.xsd +9 -0
  49. data/spec/fixtures/xml/user.xsd +10 -0
  50. data/spec/lutaml/model/cdata_spec.rb +4 -5
  51. data/spec/lutaml/model/choice_spec.rb +168 -0
  52. data/spec/lutaml/model/collection_spec.rb +1 -1
  53. data/spec/lutaml/model/custom_model_spec.rb +6 -7
  54. data/spec/lutaml/model/custom_serialization_spec.rb +74 -2
  55. data/spec/lutaml/model/defaults_spec.rb +3 -1
  56. data/spec/lutaml/model/delegation_spec.rb +7 -5
  57. data/spec/lutaml/model/enum_spec.rb +35 -0
  58. data/spec/lutaml/model/group_spec.rb +160 -0
  59. data/spec/lutaml/model/inheritance_spec.rb +25 -0
  60. data/spec/lutaml/model/liquefiable_spec.rb +121 -0
  61. data/spec/lutaml/model/mixed_content_spec.rb +80 -41
  62. data/spec/lutaml/model/multiple_mapping_spec.rb +22 -10
  63. data/spec/lutaml/model/schema/xml_compiler_spec.rb +218 -25
  64. data/spec/lutaml/model/sequence_spec.rb +216 -0
  65. data/spec/lutaml/model/transformation_spec.rb +230 -0
  66. data/spec/lutaml/model/type_spec.rb +138 -31
  67. data/spec/lutaml/model/utils_spec.rb +32 -0
  68. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +11 -7
  69. data/spec/lutaml/model/xml_mapping_rule_spec.rb +51 -0
  70. data/spec/lutaml/model/xml_mapping_spec.rb +143 -112
  71. metadata +67 -2
@@ -6,21 +6,22 @@ module Lutaml
6
6
  module Oga
7
7
  class Element < XmlElement
8
8
  def initialize(node, parent: nil)
9
- name = case node
10
- when ::Oga::XML::Element
11
- namespace_name = node.namespace_name
9
+ text = case node
10
+ when Moxml::Element
11
+ namespace_name = node.namespace&.prefix
12
12
  add_namespaces(node)
13
13
  children = parse_children(node)
14
14
  attributes = node_attributes(node)
15
- node.name
16
- when ::Oga::XML::Text
17
- "text"
15
+ @root = node
16
+ node.inner_text
17
+ when Moxml::Text
18
+ node.content
18
19
  end
19
20
  super(
20
- name,
21
+ OgaAdapter.name_of(node),
21
22
  Hash(attributes),
22
23
  Array(children),
23
- node.text,
24
+ text,
24
25
  parent_document: parent,
25
26
  namespace_prefix: namespace_name,
26
27
  )
@@ -31,7 +32,7 @@ module Lutaml
31
32
  end
32
33
 
33
34
  def text
34
- @text
35
+ super || @text
35
36
  end
36
37
 
37
38
  def to_xml(builder = Builder::Oga.build)
@@ -61,7 +62,7 @@ module Lutaml
61
62
  next if attr_is_namespace?(attr)
62
63
 
63
64
  name = if attr.namespace
64
- "#{attr.namespace.name}:#{attr.name}"
65
+ "#{attr.namespace.prefix}:#{attr.name}"
65
66
  else
66
67
  attr.name
67
68
  end
@@ -69,7 +70,7 @@ module Lutaml
69
70
  name,
70
71
  attr.value,
71
72
  namespace: attr.namespace&.uri,
72
- namespace_prefix: attr.namespace&.name,
73
+ namespace_prefix: attr.namespace&.prefix,
73
74
  )
74
75
  end
75
76
  end
@@ -79,8 +80,8 @@ module Lutaml
79
80
  end
80
81
 
81
82
  def add_namespaces(node)
82
- node.namespaces.each_value do |namespace|
83
- add_namespace(XmlNamespace.new(namespace.uri, namespace.name))
83
+ node.namespaces.each do |namespace|
84
+ add_namespace(XmlNamespace.new(namespace.uri, namespace.prefix))
84
85
  end
85
86
  end
86
87
 
@@ -1,4 +1,5 @@
1
1
  require "oga"
2
+ require "moxml/adapter/oga"
2
3
  require_relative "xml_document"
3
4
  require_relative "oga/document"
4
5
  require_relative "oga/element"
@@ -8,35 +9,105 @@ module Lutaml
8
9
  module Model
9
10
  module XmlAdapter
10
11
  class OgaAdapter < XmlDocument
12
+ TEXT_CLASSES = [Moxml::Text, Moxml::Cdata].freeze
13
+
11
14
  def self.parse(xml, options = {})
12
- encoding = options[:encoding] || xml.encoding.to_s
13
- xml = xml.encode("UTF-16").encode("UTF-8") if encoding && encoding != "UTF-8"
14
- parsed = ::Oga.parse_xml(xml)
15
+ parsed = Moxml::Adapter::Oga.parse(xml)
15
16
  @root = Oga::Element.new(parsed.children.first)
16
- new(@root, encoding)
17
+ new(@root, encoding(xml, options))
17
18
  end
18
19
 
19
20
  def to_xml(options = {})
20
21
  builder_options = {}
21
-
22
22
  builder_options[:encoding] = if options.key?(:encoding)
23
- options[:encoding] || "UTF-8"
23
+ options[:encoding]
24
24
  elsif options.key?(:parse_encoding)
25
25
  options[:parse_encoding]
26
26
  else
27
27
  "UTF-8"
28
28
  end
29
- builder = Builder::Oga.build(options) do |xml|
29
+
30
+ builder = Builder::Oga.build(builder_options) do |xml|
30
31
  if @root.is_a?(Oga::Element)
31
32
  @root.build_xml(xml)
32
33
  else
33
34
  build_element(xml, @root, options)
34
35
  end
35
36
  end
36
- xml_data = builder.to_xml.encode!(builder_options[:encoding])
37
+ xml_data = builder.to_xml
37
38
  options[:declaration] ? declaration(options) + xml_data : xml_data
38
- rescue Encoding::ConverterNotFoundError
39
- invalid_encoding!(builder_options[:encoding])
39
+ end
40
+
41
+ def attributes_hash(element)
42
+ result = Lutaml::Model::MappingHash.new
43
+
44
+ element.attributes.each do |attr|
45
+ if attr.name == "schemaLocation"
46
+ result["__schema_location"] = {
47
+ namespace: attr.namespace,
48
+ prefix: attr.namespace.prefix,
49
+ schema_location: attr.value,
50
+ }
51
+ else
52
+ result[self.class.namespaced_attr_name(attr)] = attr.value
53
+ end
54
+ end
55
+
56
+ result
57
+ end
58
+
59
+ def self.name_of(element)
60
+ case element
61
+ when Moxml::Text
62
+ "text"
63
+ when Moxml::Cdata
64
+ "cdata"
65
+ else
66
+ element.name
67
+ end
68
+ end
69
+
70
+ def self.prefixed_name_of(node)
71
+ return name_of(node) if TEXT_CLASSES.include?(node.class)
72
+
73
+ [node&.namespace&.prefix, node.name].compact.join(":")
74
+ end
75
+
76
+ def self.text_of(element)
77
+ element.content
78
+ end
79
+
80
+ def self.namespaced_attr_name(attribute)
81
+ attr_ns = attribute.namespace
82
+ attr_name = attribute.name
83
+ return attr_name unless attr_ns
84
+
85
+ prefix = attr_name == "lang" ? attr_ns.prefix : attr_ns.uri
86
+ [prefix, attr_name].compact.join(":")
87
+ end
88
+
89
+ def self.namespaced_name_of(node)
90
+ return name_of(node) unless node.respond_to?(:namespace)
91
+
92
+ [node&.namespace&.uri, node.name].compact.join(":")
93
+ end
94
+
95
+ def order
96
+ children.map do |child|
97
+ type = child.text? ? "Text" : "Element"
98
+ Element.new(type, child.unprefixed_name)
99
+ end
100
+ end
101
+
102
+ def self.order_of(element)
103
+ element.children.map do |child|
104
+ instance_args = if TEXT_CLASSES.include?(child.class)
105
+ ["Text", "text"]
106
+ else
107
+ ["Element", name_of(child)]
108
+ end
109
+ Element.new(*instance_args)
110
+ end
40
111
  end
41
112
 
42
113
  private
@@ -54,18 +125,18 @@ module Lutaml
54
125
  index_hash = {}
55
126
  content = []
56
127
 
57
- element.element_order.each do |name|
58
- index_hash[name] ||= -1
59
- curr_index = index_hash[name] += 1
128
+ element.element_order.each do |object|
129
+ index_hash[object.name] ||= -1
130
+ curr_index = index_hash[object.name] += 1
60
131
 
61
- element_rule = xml_mapping.find_by_name(name)
132
+ element_rule = xml_mapping.find_by_name(object.name)
62
133
  next if element_rule.nil?
63
134
 
64
135
  attribute_def = attribute_definition_for(element, element_rule,
65
136
  mapper_class: mapper_class)
66
137
  value = attribute_value_for(element, element_rule)
67
138
 
68
- next if element_rule == xml_mapping.content_mapping && element_rule.cdata && name == "text"
139
+ next if element_rule == xml_mapping.content_mapping && element_rule.cdata && object.text?
69
140
 
70
141
  if element_rule == xml_mapping.content_mapping
71
142
  text = xml_mapping.content_mapping.serialize(element)
@@ -94,10 +165,6 @@ module Lutaml
94
165
  el.add_text(el, content.join)
95
166
  end
96
167
  end
97
-
98
- def invalid_encoding!(encoding)
99
- raise Error, "unknown encoding name - #{encoding}"
100
- end
101
168
  end
102
169
  end
103
170
  end
@@ -7,26 +7,26 @@ module Lutaml
7
7
  module XmlAdapter
8
8
  class OxAdapter < XmlDocument
9
9
  def self.parse(xml, options = {})
10
- Ox.default_options = Ox.default_options.merge(encoding: options[:encoding] || "UTF-8")
10
+ Ox.default_options = Ox.default_options.merge(encoding: encoding(xml, options))
11
11
 
12
12
  parsed = Ox.parse(xml)
13
- root = OxElement.new(parsed)
14
- new(root, Ox.default_options[:encoding])
13
+ @root = OxElement.new(parsed)
14
+ new(@root, Ox.default_options[:encoding])
15
15
  end
16
16
 
17
17
  def to_xml(options = {})
18
18
  builder_options = { version: options[:version] }
19
19
 
20
20
  builder_options[:encoding] = if options.key?(:encoding)
21
- options[:encoding]
21
+ options[:encoding] unless options[:encoding].nil?
22
22
  elsif options.key?(:parse_encoding)
23
23
  options[:parse_encoding]
24
24
  else
25
25
  "UTF-8"
26
26
  end
27
27
 
28
- builder = Builder::Ox.build
29
- builder.xml.instruct(:xml, encoding: options[:parse_encoding])
28
+ builder = Builder::Ox.build(builder_options)
29
+ builder.xml.instruct(:xml, encoding: builder_options[:encoding])
30
30
 
31
31
  if @root.is_a?(Lutaml::Model::XmlAdapter::OxElement)
32
32
  @root.build_xml(builder)
@@ -39,10 +39,6 @@ module Lutaml
39
39
  end
40
40
 
41
41
  xml_data = builder.xml.to_s
42
- if builder_options[:encoding] && xml_data.valid_encoding?
43
- xml_data = xml_data.encode(builder_options[:encoding])
44
- end
45
-
46
42
  stripped_data = xml_data.lines.drop(1).join
47
43
  options[:declaration] ? declaration(options) + stripped_data : stripped_data
48
44
  end
@@ -62,18 +58,18 @@ module Lutaml
62
58
  index_hash = {}
63
59
  content = []
64
60
 
65
- element.element_order.each do |name|
66
- index_hash[name] ||= -1
67
- curr_index = index_hash[name] += 1
61
+ element.element_order.each do |object|
62
+ index_hash[object.name] ||= -1
63
+ curr_index = index_hash[object.name] += 1
68
64
 
69
- element_rule = xml_mapping.find_by_name(name)
65
+ element_rule = xml_mapping.find_by_name(object.name)
70
66
  next if element_rule.nil?
71
67
 
72
68
  attribute_def = attribute_definition_for(element, element_rule,
73
69
  mapper_class: mapper_class)
74
70
  value = attribute_value_for(element, element_rule)
75
71
 
76
- next if element_rule == xml_mapping.content_mapping && element_rule.cdata && name == "text"
72
+ next if element_rule == xml_mapping.content_mapping && element_rule.cdata && object.text?
77
73
 
78
74
  if element_rule == xml_mapping.content_mapping
79
75
  text = element.send(xml_mapping.content_mapping.to)
@@ -198,6 +194,14 @@ module Lutaml
198
194
  children
199
195
  end
200
196
 
197
+ def cdata
198
+ super || cdata_children.first&.text
199
+ end
200
+
201
+ def text
202
+ super || cdata
203
+ end
204
+
201
205
  private
202
206
 
203
207
  def parse_children(node, root_node: nil)
@@ -2,6 +2,7 @@ require_relative "../mapping_hash"
2
2
  require_relative "xml_element"
3
3
  require_relative "xml_attribute"
4
4
  require_relative "xml_namespace"
5
+ require_relative "element"
5
6
 
6
7
  module Lutaml
7
8
  module Model
@@ -22,6 +23,22 @@ module Lutaml
22
23
  @root.children
23
24
  end
24
25
 
26
+ def root
27
+ @root
28
+ end
29
+
30
+ def attributes
31
+ root.attributes
32
+ end
33
+
34
+ def self.encoding(xml, options)
35
+ if options.key?(:encoding)
36
+ options[:encoding]
37
+ else
38
+ xml.encoding.to_s
39
+ end
40
+ end
41
+
25
42
  def declaration(options)
26
43
  version = "1.0"
27
44
  version = options[:declaration] if options[:declaration].is_a?(String)
@@ -75,22 +92,32 @@ module Lutaml
75
92
  def parse_element(element, klass = nil, format = nil)
76
93
  result = Lutaml::Model::MappingHash.new
77
94
  result.node = element
78
- result.item_order = element.order
95
+ result.item_order = self.class.order_of(element)
79
96
 
80
97
  element.children.each do |child|
81
98
  if klass&.<= Serialize
82
- attr = klass.attribute_for_child(child.name,
99
+ attr = klass.attribute_for_child(self.class.name_of(child),
83
100
  format)
84
101
  end
85
102
 
86
- next result.assign_or_append_value(child.name, child.text) if child.text?
103
+ if child.respond_to?(:text?) && child.text?
104
+ result.assign_or_append_value(
105
+ self.class.name_of(child),
106
+ self.class.text_of(child),
107
+ )
108
+ next
109
+ end
87
110
 
88
111
  result["elements"] ||= Lutaml::Model::MappingHash.new
89
- result["elements"].assign_or_append_value(child.namespaced_name, parse_element(child, attr&.type || klass, format))
112
+ result["elements"].assign_or_append_value(
113
+ self.class.namespaced_name_of(child),
114
+ parse_element(child, attr&.type || klass, format),
115
+ )
90
116
  end
91
117
 
92
118
  result["attributes"] = attributes_hash(element) if element.attributes&.any?
93
119
 
120
+ result.merge(attributes_hash(element))
94
121
  result
95
122
  end
96
123
 
@@ -121,14 +148,6 @@ module Lutaml
121
148
  end
122
149
 
123
150
  def add_to_xml(xml, element, prefix, value, options = {})
124
- if value.is_a?(Array)
125
- value.each do |item|
126
- add_to_xml(xml, element, prefix, item, options)
127
- end
128
-
129
- return
130
- end
131
-
132
151
  attribute = options[:attribute]
133
152
  rule = options[:rule]
134
153
 
@@ -138,6 +157,19 @@ module Lutaml
138
157
  return
139
158
  end
140
159
 
160
+ # Only transform when recursion is not called
161
+ if (!attribute.collection? || value.is_a?(Array)) && transform_method = rule.transform[:export] || attribute.transform_export_method
162
+ value = transform_method.call(value)
163
+ end
164
+
165
+ if value.is_a?(Array)
166
+ value.each do |item|
167
+ add_to_xml(xml, element, prefix, item, options)
168
+ end
169
+
170
+ return
171
+ end
172
+
141
173
  return if !render_element?(rule, element, value)
142
174
 
143
175
  if value && (attribute&.type&.<= Lutaml::Model::Serialize)
@@ -161,7 +193,7 @@ module Lutaml
161
193
 
162
194
  def add_value(xml, value, attribute, cdata: false)
163
195
  if !value.nil?
164
- serialized_value = attribute.type.serialize(value)
196
+ serialized_value = attribute.serialize(value, :xml)
165
197
  if attribute.raw?
166
198
  xml.add_xml_fragment(xml, value)
167
199
  elsif attribute.type == Lutaml::Model::Type::Hash
@@ -339,6 +371,9 @@ module Lutaml
339
371
  end
340
372
 
341
373
  value = mapping_rule.to_value_for(element)
374
+ attr = attribute_definition_for(element, mapping_rule, mapper_class: options[:mapper_class])
375
+ value = attr.serialize(value, :xml) if attr
376
+
342
377
  if render_element?(mapping_rule, element, value)
343
378
  hash[mapping_rule.prefixed_name] = value ? value.to_s : value
344
379
  end
@@ -376,6 +411,32 @@ module Lutaml
376
411
  def self.type
377
412
  Utils.snake_case(self).split("/").last.split("_").first
378
413
  end
414
+
415
+ def self.order_of(element)
416
+ element.order
417
+ end
418
+
419
+ def self.name_of(element)
420
+ element.name
421
+ end
422
+
423
+ def self.text_of(element)
424
+ element.text
425
+ end
426
+
427
+ def self.namespaced_name_of(element)
428
+ element.namespaced_name
429
+ end
430
+
431
+ def text
432
+ return @root.text_children.map(&:text) if @root.children.count > 1
433
+
434
+ @root.text
435
+ end
436
+
437
+ def cdata
438
+ @root.cdata
439
+ end
379
440
  end
380
441
  end
381
442
  end
@@ -8,7 +8,8 @@ module Lutaml
8
8
  :children,
9
9
  :text,
10
10
  :namespace_prefix,
11
- :parent_document
11
+ :parent_document,
12
+ :default_namespace
12
13
 
13
14
  attr_accessor :adapter_node
14
15
 
@@ -117,10 +118,63 @@ module Lutaml
117
118
  end
118
119
 
119
120
  def order
120
- children.each_with_object([]) do |child, arr|
121
- arr << child.unprefixed_name
121
+ children.map do |child|
122
+ type = child.text? ? "Text" : "Element"
123
+ Lutaml::Model::XmlAdapter::Element.new(type, child.unprefixed_name)
122
124
  end
123
125
  end
126
+
127
+ def root
128
+ self
129
+ end
130
+
131
+ def text
132
+ return text_children.map(&:text) if children.count > 1
133
+
134
+ @text
135
+ end
136
+
137
+ def cdata_children
138
+ find_children_by_name("#cdata-section")
139
+ end
140
+
141
+ def text_children
142
+ find_children_by_name("text")
143
+ end
144
+
145
+ def find_attribute_value(attribute_name)
146
+ if attribute_name.is_a?(Array)
147
+ attributes.values.find do |attr|
148
+ attribute_name.include?(attr.namespaced_name)
149
+ end&.value
150
+ else
151
+ attributes.values.find do |attr|
152
+ attribute_name == attr.namespaced_name
153
+ end&.value
154
+ end
155
+ end
156
+
157
+ def find_children_by_name(name)
158
+ if name.is_a?(Array)
159
+ children.select { |child| name.include?(child.namespaced_name) }
160
+ else
161
+ children.select { |child| child.namespaced_name == name }
162
+ end
163
+ end
164
+
165
+ def find_child_by_name(name)
166
+ find_children_by_name(name).first
167
+ end
168
+
169
+ def cdata
170
+ return cdata_children.map(&:text) if children.count > 1
171
+
172
+ @text
173
+ end
174
+
175
+ def to_h
176
+ document.to_h
177
+ end
124
178
  end
125
179
  end
126
180
  end
@@ -16,11 +16,13 @@ module Lutaml
16
16
  :namespace_uri,
17
17
  :namespace_prefix,
18
18
  :mixed_content,
19
- :ordered
19
+ :ordered,
20
+ :element_sequence
20
21
 
21
22
  def initialize
22
23
  @elements = {}
23
24
  @attributes = {}
25
+ @element_sequence = []
24
26
  @content_mapping = nil
25
27
  @raw_mapping = nil
26
28
  @mixed_content = false
@@ -35,6 +37,18 @@ module Lutaml
35
37
  @ordered = ordered || mixed # mixed contenet will always be ordered
36
38
  end
37
39
 
40
+ def root?
41
+ !!root_element
42
+ end
43
+
44
+ def no_root
45
+ @no_root = true
46
+ end
47
+
48
+ def no_root?
49
+ !!@no_root
50
+ end
51
+
38
52
  def prefixed_root
39
53
  if namespace_uri && namespace_prefix
40
54
  "#{namespace_prefix}:#{root_element}"
@@ -44,6 +58,8 @@ module Lutaml
44
58
  end
45
59
 
46
60
  def namespace(uri, prefix = nil)
61
+ raise Lutaml::Model::NoRootNamespaceError if no_root?
62
+
47
63
  @namespace_uri = uri
48
64
  @namespace_prefix = prefix
49
65
  end
@@ -60,7 +76,8 @@ module Lutaml
60
76
  namespace: (namespace_set = false
61
77
  nil),
62
78
  prefix: (prefix_set = false
63
- nil)
79
+ nil),
80
+ transform: {}
64
81
  )
65
82
  validate!(name, to, with, type: TYPES[:element])
66
83
 
@@ -77,6 +94,7 @@ module Lutaml
77
94
  prefix: prefix,
78
95
  namespace_set: namespace_set != false,
79
96
  prefix_set: prefix_set != false,
97
+ transform: transform,
80
98
  )
81
99
  @elements[rule.namespaced_name] = rule
82
100
  end
@@ -170,6 +188,21 @@ module Lutaml
170
188
 
171
189
  alias map_all_content map_all
172
190
 
191
+ def sequence(&block)
192
+ @element_sequence << Sequence.new(self).tap do |s|
193
+ s.instance_eval(&block)
194
+ end
195
+ end
196
+
197
+ def import_model_mappings(model)
198
+ raise Lutaml::Model::ImportModelWithRootError.new(model) if model.root?
199
+
200
+ mappings = model.mappings_for(:xml)
201
+ @elements.merge!(mappings.instance_variable_get(:@elements))
202
+ @attributes.merge!(mappings.instance_variable_get(:@attributes))
203
+ (@element_sequence << mappings.element_sequence).flatten!
204
+ end
205
+
173
206
  def validate!(key, to, with, type: nil)
174
207
  validate_mappings!(type)
175
208
 
@@ -243,14 +276,23 @@ module Lutaml
243
276
  ordered: @ordered)
244
277
  xml_mapping.namespace(@namespace_uri.dup, @namespace_prefix.dup)
245
278
 
246
- xml_mapping.instance_variable_set(:@attributes,
247
- dup_mappings(@attributes))
248
- xml_mapping.instance_variable_set(:@elements, dup_mappings(@elements))
249
- xml_mapping.instance_variable_set(:@content_mapping,
250
- @content_mapping&.deep_dup)
279
+ attributes_to_dup.each do |var_name|
280
+ value = instance_variable_get(var_name)
281
+ xml_mapping.instance_variable_set(var_name, Utils.deep_dup(value))
282
+ end
251
283
  end
252
284
  end
253
285
 
286
+ def attributes_to_dup
287
+ @attributes_to_dup ||= %i[
288
+ @content_mapping
289
+ @raw_mapping
290
+ @element_sequence
291
+ @attributes
292
+ @elements
293
+ ]
294
+ end
295
+
254
296
  def dup_mappings(mappings)
255
297
  new_mappings = {}
256
298
 
@@ -19,7 +19,8 @@ module Lutaml
19
19
  namespace_set: false,
20
20
  prefix_set: false,
21
21
  attribute: false,
22
- default_namespace: nil
22
+ default_namespace: nil,
23
+ transform: {}
23
24
  )
24
25
  super(
25
26
  name,
@@ -28,7 +29,8 @@ module Lutaml
28
29
  render_default: render_default,
29
30
  with: with,
30
31
  delegate: delegate,
31
- attribute: attribute
32
+ attribute: attribute,
33
+ transform: transform
32
34
  )
33
35
 
34
36
  @namespace = if namespace.to_s == "inherit"
@@ -101,15 +103,18 @@ module Lutaml
101
103
  name.dup,
102
104
  to: to,
103
105
  render_nil: render_nil,
106
+ render_default: render_default,
104
107
  with: Utils.deep_dup(custom_methods),
105
108
  delegate: delegate,
106
109
  namespace: namespace.dup,
107
110
  prefix: prefix.dup,
108
111
  mixed_content: mixed_content,
112
+ cdata: cdata,
109
113
  namespace_set: namespace_set?,
110
- attribute: attribute,
111
114
  prefix_set: prefix_set?,
115
+ attribute: attribute,
112
116
  default_namespace: default_namespace.dup,
117
+ transform: transform.dup,
113
118
  )
114
119
  end
115
120
  end
data/lib/lutaml/model.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "moxml"
3
4
  require_relative "model/version"
4
5
  require_relative "model/loggable"
5
6
  require_relative "model/type"