lutaml-model 0.5.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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"