lutaml-model 0.3.6 → 0.3.8

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.
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.3.6"
5
+ VERSION = "0.3.8"
6
6
  end
7
7
  end
@@ -0,0 +1,73 @@
1
+ module Lutaml
2
+ module Model
3
+ module XmlAdapter
4
+ module Builder
5
+ class Nokogiri
6
+ def self.build(options = {})
7
+ if block_given?
8
+ ::Nokogiri::XML::Builder.new(options) do |xml|
9
+ yield(new(xml))
10
+ end
11
+ else
12
+ new(::Nokogiri::XML::Builder.new(options))
13
+ end
14
+ end
15
+
16
+ attr_reader :xml
17
+
18
+ def initialize(xml)
19
+ @xml = xml
20
+ end
21
+
22
+ def create_element(name, attributes = {})
23
+ xml.doc.create_element(name, attributes)
24
+ end
25
+
26
+ def add_element(element, child)
27
+ element.add_child(child)
28
+ end
29
+
30
+ def create_and_add_element(element_name, prefix: nil, attributes: {})
31
+ add_namespace_prefix(prefix) if prefix
32
+
33
+ if block_given?
34
+ public_send(element_name, attributes) do
35
+ yield(self)
36
+ end
37
+ else
38
+ public_send(element_name, attributes)
39
+ end
40
+ end
41
+
42
+ def add_text(element, text)
43
+ if element.is_a?(self.class)
44
+ element = element.xml.parent
45
+ end
46
+
47
+ add_element(element, ::Nokogiri::XML::Text.new(text.to_s, xml.doc))
48
+ end
49
+
50
+ def add_namespace_prefix(prefix)
51
+ xml[prefix] if prefix
52
+
53
+ self
54
+ end
55
+
56
+ def method_missing(method_name, *args)
57
+ if block_given?
58
+ xml.public_send(method_name, *args) do
59
+ yield(xml)
60
+ end
61
+ else
62
+ xml.public_send(method_name, *args)
63
+ end
64
+ end
65
+
66
+ def respond_to_missing?(method_name, include_private = false)
67
+ xml.respond_to?(method_name) || super
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,89 @@
1
+ module Lutaml
2
+ module Model
3
+ module XmlAdapter
4
+ module Builder
5
+ class Ox
6
+ def self.build(options = {})
7
+ if block_given?
8
+ ::Ox::Builder.new(options) do |xml|
9
+ yield(new(xml))
10
+ end
11
+ else
12
+ new(::Ox::Builder.new(options))
13
+ end
14
+ end
15
+
16
+ attr_reader :xml
17
+
18
+ def initialize(xml)
19
+ @xml = xml
20
+ end
21
+
22
+ def create_element(name, attributes = {})
23
+ if block_given?
24
+ xml.element(name, attributes) do |element|
25
+ yield(self.class.new(element))
26
+ end
27
+ else
28
+ xml.element(name, attributes)
29
+ end
30
+ end
31
+
32
+ def add_element(element, child)
33
+ element << child
34
+ end
35
+
36
+ def create_and_add_element(element_name, prefix: nil, attributes: {})
37
+ prefixed_name = if prefix
38
+ "#{prefix}:#{element_name}"
39
+ else
40
+ element_name
41
+ end
42
+
43
+ if block_given?
44
+ xml.element(prefixed_name, attributes) do |element|
45
+ yield(self.class.new(element))
46
+ end
47
+ else
48
+ xml.element(prefixed_name, attributes)
49
+ end
50
+ end
51
+
52
+ def <<(text)
53
+ xml.text(text)
54
+ end
55
+
56
+ def add_text(element, text)
57
+ element << text
58
+ end
59
+
60
+ # Add XML namespace to document
61
+ #
62
+ # Ox doesn't support XML namespaces so this method does nothing.
63
+ def add_namespace_prefix(_prefix)
64
+ # :noop:
65
+ self
66
+ end
67
+
68
+ def parent
69
+ xml
70
+ end
71
+
72
+ def method_missing(method_name, *args)
73
+ if block_given?
74
+ xml.public_send(method_name, *args) do
75
+ yield(xml)
76
+ end
77
+ else
78
+ xml.public_send(method_name, *args)
79
+ end
80
+ end
81
+
82
+ def respond_to_missing?(method_name, include_private = false)
83
+ xml.respond_to?(method_name) || super
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -1,5 +1,6 @@
1
1
  require "nokogiri"
2
2
  require_relative "xml_document"
3
+ require_relative "builder/nokogiri"
3
4
 
4
5
  module Lutaml
5
6
  module Model
@@ -12,12 +13,13 @@ module Lutaml
12
13
  end
13
14
 
14
15
  def to_xml(options = {})
15
- builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
16
+ builder = Builder::Nokogiri.build(encoding: "UTF-8") do |xml|
16
17
  if root.is_a?(Lutaml::Model::XmlAdapter::NokogiriElement)
17
18
  root.to_xml(xml)
18
19
  else
19
20
  mapper_class = options[:mapper_class] || @root.class
20
- options[:xml_attributes] = build_namespace_attributes(mapper_class)
21
+ options[:xml_attributes] =
22
+ build_namespace_attributes(mapper_class)
21
23
  build_element(xml, @root, options)
22
24
  end
23
25
  end
@@ -31,59 +33,6 @@ module Lutaml
31
33
 
32
34
  private
33
35
 
34
- def build_unordered_element(xml, element, options = {})
35
- mapper_class = options[:mapper_class] || element.class
36
- xml_mapping = mapper_class.mappings_for(:xml)
37
- return xml unless xml_mapping
38
-
39
- attributes = options[:xml_attributes] ||= {}
40
- attributes = build_attributes(element,
41
- xml_mapping).merge(attributes)&.compact
42
-
43
- prefixed_xml = if options.key?(:namespace_prefix)
44
- options[:namespace_prefix] ? xml[options[:namespace_prefix]] : xml
45
- elsif xml_mapping.namespace_prefix
46
- xml[xml_mapping.namespace_prefix]
47
- else
48
- xml
49
- end
50
-
51
- tag_name = options[:tag_name] || xml_mapping.root_element
52
- prefixed_xml.public_send(tag_name, attributes) do
53
- if options.key?(:namespace_prefix) && !options[:namespace_prefix]
54
- xml.parent.namespace = nil
55
- end
56
-
57
- xml_mapping.elements.each do |element_rule|
58
- attribute_def = attribute_definition_for(element, element_rule, mapper_class: mapper_class)
59
- value = attribute_value_for(element, element_rule)
60
-
61
- next if value.nil? && !element_rule.render_nil?
62
-
63
- nsp_xml = element_rule.prefix ? xml[element_rule.prefix] : xml
64
-
65
- if attribute_def.collection?
66
- value.each do |v|
67
- add_to_xml(nsp_xml, v, attribute_def, element_rule)
68
- end
69
- elsif !value.nil? || element_rule.render_nil?
70
- add_to_xml(nsp_xml, value, attribute_def, element_rule)
71
- end
72
- end
73
-
74
- if (content_rule = xml_mapping.content_mapping)
75
- text = element.send(content_rule.to)
76
- text = text.join if text.is_a?(Array)
77
-
78
- if content_rule.custom_methods[:to]
79
- text = @root.send(content_rule.custom_methods[:to], @root, text)
80
- end
81
-
82
- prefixed_xml.text text
83
- end
84
- end
85
- end
86
-
87
36
  def build_ordered_element(xml, element, options = {})
88
37
  mapper_class = options[:mapper_class] || element.class
89
38
  xml_mapping = mapper_class.mappings_for(:xml)
@@ -114,49 +63,27 @@ module Lutaml
114
63
  element_rule = xml_mapping.find_by_name(name)
115
64
  next if element_rule.nil?
116
65
 
117
- attribute_def = attribute_definition_for(element, element_rule, mapper_class: mapper_class)
66
+ attribute_def = attribute_definition_for(element, element_rule,
67
+ mapper_class: mapper_class)
118
68
  value = attribute_value_for(element, element_rule)
119
- nsp_xml = element_rule.prefix ? xml[element_rule.prefix] : xml
120
69
 
121
70
  if element_rule == xml_mapping.content_mapping
122
71
  text = element.send(xml_mapping.content_mapping.to)
123
72
  text = text[curr_index] if text.is_a?(Array)
124
73
 
125
74
  prefixed_xml.text text
126
- elsif attribute_def.collection?
127
- add_to_xml(nsp_xml, value[curr_index], attribute_def,
128
- element_rule)
129
75
  elsif !value.nil? || element_rule.render_nil?
130
- add_to_xml(nsp_xml, value, attribute_def, element_rule)
131
- end
132
- end
133
- end
134
- end
135
-
136
- def add_to_xml(xml, value, attribute, rule)
137
- if rule.custom_methods[:to]
138
- value = @root.send(rule.custom_methods[:to], @root, value)
139
- end
140
-
141
- if value && (attribute&.type&.<= Lutaml::Model::Serialize)
142
- handle_nested_elements(
143
- xml,
144
- value,
145
- rule: rule,
146
- attribute: attribute,
147
- )
148
- else
149
- xml.public_send(rule.name) do
150
- if !value.nil?
151
- serialized_value = attribute.type.serialize(value)
152
-
153
- if attribute.type == Lutaml::Model::Type::Hash
154
- serialized_value.each do |key, val|
155
- xml.public_send(key) { xml.text val }
156
- end
157
- else
158
- xml.text(serialized_value)
159
- end
76
+ value = value[curr_index] if attribute_def.collection?
77
+
78
+ add_to_xml(
79
+ xml,
80
+ element_rule.prefix,
81
+ value,
82
+ options.merge(
83
+ attribute: attribute_def,
84
+ rule: element_rule,
85
+ ),
86
+ )
160
87
  end
161
88
  end
162
89
  end
@@ -205,7 +132,7 @@ module Lutaml
205
132
  end
206
133
 
207
134
  def to_xml(builder = nil)
208
- builder ||= Nokogiri::XML::Builder.new
135
+ builder ||= Builder::Nokogiri.build
209
136
 
210
137
  if name == "text"
211
138
  builder.text(text)
@@ -232,7 +159,7 @@ module Lutaml
232
159
  end
233
160
  end
234
161
 
235
- def build_attributes(node)
162
+ def build_attributes(node, _options = {})
236
163
  attrs = node.attributes.transform_values(&:value)
237
164
 
238
165
  attrs.merge(build_namespace_attributes(node))
@@ -1,5 +1,6 @@
1
1
  require "ox"
2
2
  require_relative "xml_document"
3
+ require_relative "builder/ox"
3
4
 
4
5
  module Lutaml
5
6
  module Model
@@ -12,7 +13,7 @@ module Lutaml
12
13
  end
13
14
 
14
15
  def to_xml(options = {})
15
- builder = Ox::Builder.new
16
+ builder = Builder::Ox.build
16
17
 
17
18
  if @root.is_a?(Lutaml::Model::XmlAdapter::OxElement)
18
19
  @root.to_xml(builder)
@@ -22,66 +23,12 @@ module Lutaml
22
23
  build_element(builder, @root, options)
23
24
  end
24
25
 
25
- # xml_data = Ox.dump(builder)
26
- xml_data = builder.to_s
26
+ xml_data = builder.xml.to_s
27
27
  options[:declaration] ? declaration(options) + xml_data : xml_data
28
28
  end
29
29
 
30
30
  private
31
31
 
32
- def build_unordered_element(builder, element, options = {})
33
- mapper_class = options[:mapper_class] || element.class
34
- xml_mapping = mapper_class.mappings_for(:xml)
35
- return xml unless xml_mapping
36
-
37
- attributes = build_attributes(element, xml_mapping).compact
38
-
39
- tag_name = options[:tag_name] || xml_mapping.root_element
40
- prefixed_name = if options.key?(:namespace_prefix)
41
- [options[:namespace_prefix], tag_name].compact.join(":")
42
- elsif xml_mapping.namespace_prefix
43
- "#{xml_mapping.namespace_prefix}:#{tag_name}"
44
- else
45
- tag_name
46
- end
47
-
48
- builder.element(prefixed_name, attributes) do |el|
49
- xml_mapping.elements.each do |element_rule|
50
- attribute_def = attribute_definition_for(element, element_rule, mapper_class: mapper_class)
51
- value = attribute_value_for(element, element_rule)
52
-
53
- val = if attribute_def.collection?
54
- value
55
- elsif value || element_rule.render_nil?
56
- [value]
57
- else
58
- []
59
- end
60
-
61
- val.each do |v|
62
- if attribute_def&.type&.<= Lutaml::Model::Serialize
63
- handle_nested_elements(el, v, rule: element_rule, attribute: attribute_def)
64
- else
65
- builder.element(element_rule.prefixed_name) do |el|
66
- el.text(attribute_def.type.serialize(v)) if v
67
- end
68
- end
69
- end
70
- end
71
-
72
- if (content_rule = xml_mapping.content_mapping)
73
- text = element.send(xml_mapping.content_mapping.to)
74
- text = text.join if text.is_a?(Array)
75
-
76
- if content_rule.custom_methods[:to]
77
- text = @root.send(content_rule.custom_methods[:to], @root, text)
78
- end
79
-
80
- el.text text
81
- end
82
- end
83
- end
84
-
85
32
  def build_ordered_element(builder, element, options = {})
86
33
  mapper_class = options[:mapper_class] || element.class
87
34
  xml_mapping = mapper_class.mappings_for(:xml)
@@ -90,7 +37,8 @@ module Lutaml
90
37
  attributes = build_attributes(element, xml_mapping).compact
91
38
 
92
39
  tag_name = options[:tag_name] || xml_mapping.root_element
93
- builder.element(tag_name, attributes) do |el|
40
+ builder.create_and_add_element(tag_name,
41
+ attributes: attributes) do |el|
94
42
  index_hash = {}
95
43
 
96
44
  element.element_order.each do |name|
@@ -99,47 +47,27 @@ module Lutaml
99
47
 
100
48
  element_rule = xml_mapping.find_by_name(name)
101
49
 
102
- attribute_def = attribute_definition_for(element, element_rule, mapper_class: mapper_class)
50
+ attribute_def = attribute_definition_for(element, element_rule,
51
+ mapper_class: mapper_class)
103
52
  value = attribute_value_for(element, element_rule)
104
53
 
105
54
  if element_rule == xml_mapping.content_mapping
106
55
  text = element.send(xml_mapping.content_mapping.to)
107
56
  text = text[curr_index] if text.is_a?(Array)
108
57
 
109
- el.text text
110
- elsif attribute_def.collection?
111
- add_to_xml(el, value[curr_index], attribute_def, element_rule)
58
+ el.add_text(el, text)
112
59
  elsif !value.nil? || element_rule.render_nil?
113
- add_to_xml(el, value, attribute_def, element_rule)
114
- end
115
- end
116
- end
117
- end
118
-
119
- def add_to_xml(xml, value, attribute, rule)
120
- if rule.custom_methods[:to]
121
- value = @root.send(rule.custom_methods[:to], @root, value)
122
- end
123
-
124
- if value && (attribute&.type&.<= Lutaml::Model::Serialize)
125
- handle_nested_elements(
126
- xml,
127
- value,
128
- rule: rule,
129
- attribute: attribute,
130
- )
131
- else
132
- xml.element(rule.name) do |el|
133
- if !value.nil?
134
- serialized_value = attribute.type.serialize(value)
135
-
136
- if attribute.type == Lutaml::Model::Type::Hash
137
- serialized_value.each do |key, val|
138
- el.element(key) { |child_el| child_el.text val }
139
- end
140
- else
141
- el.text(serialized_value)
142
- end
60
+ value = value[curr_index] if attribute_def.collection?
61
+
62
+ add_to_xml(
63
+ el,
64
+ nil,
65
+ value,
66
+ options.merge(
67
+ attribute: attribute_def,
68
+ rule: element_rule,
69
+ ),
70
+ )
143
71
  end
144
72
  end
145
73
  end
@@ -188,13 +116,13 @@ module Lutaml
188
116
  end
189
117
 
190
118
  def to_xml(builder = nil)
191
- builder ||= Ox::Builder.new
119
+ builder ||= Builder::Ox.build
192
120
  attrs = build_attributes(self)
193
121
 
194
122
  if text?
195
- builder.text(text)
123
+ builder.add_text(builder, text)
196
124
  else
197
- builder.element(name, attrs) do |el|
125
+ builder.create_and_add_element(name, attributes: attrs) do |el|
198
126
  children.each { |child| child.to_xml(el) }
199
127
  end
200
128
  end
@@ -42,21 +42,24 @@ module Lutaml
42
42
  @root.order
43
43
  end
44
44
 
45
- def handle_nested_elements(builder, value, rule: nil, attribute: nil)
46
- options = build_options_for_nested_elements(attribute, rule)
45
+ def handle_nested_elements(builder, value, options = {})
46
+ element_options = build_options_for_nested_elements(options)
47
47
 
48
48
  case value
49
49
  when Array
50
- value.each { |val| build_element(builder, val, options) }
50
+ value.each { |val| build_element(builder, val, element_options) }
51
51
  else
52
- build_element(builder, value, options)
52
+ build_element(builder, value, element_options)
53
53
  end
54
54
  end
55
55
 
56
- def build_options_for_nested_elements(attribute, rule)
56
+ def build_options_for_nested_elements(options = {})
57
+ attribute = options.delete(:attribute)
58
+ rule = options.delete(:rule)
59
+
57
60
  return {} unless rule
58
61
 
59
- options = {}
62
+ # options = {}
60
63
 
61
64
  options[:namespace_prefix] = rule.prefix if rule&.namespace_set?
62
65
  options[:mixed_content] = rule.mixed_content
@@ -97,6 +100,103 @@ module Lutaml
97
100
  end
98
101
  end
99
102
 
103
+ def add_to_xml(xml, prefix, value, options = {})
104
+ if value.is_a?(Array)
105
+ value.each do |item|
106
+ add_to_xml(xml, prefix, item, options)
107
+ end
108
+
109
+ return
110
+ end
111
+
112
+ attribute = options[:attribute]
113
+ rule = options[:rule]
114
+
115
+ if rule.custom_methods[:to]
116
+ @root.send(rule.custom_methods[:to], @root, xml.parent, xml)
117
+ return
118
+ end
119
+
120
+ if value && (attribute&.type&.<= Lutaml::Model::Serialize)
121
+ handle_nested_elements(
122
+ xml,
123
+ value,
124
+ options.merge({ rule: rule, attribute: attribute }),
125
+ )
126
+ else
127
+ xml.create_and_add_element(rule.name, prefix: prefix) do
128
+ if !value.nil?
129
+ serialized_value = attribute.type.serialize(value)
130
+
131
+ if attribute.type == Lutaml::Model::Type::Hash
132
+ serialized_value.each do |key, val|
133
+ xml.create_and_add_element(key) do |element|
134
+ element.text(val)
135
+ end
136
+ end
137
+ else
138
+ xml.add_text(xml, serialized_value)
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ def build_unordered_element(xml, element, options = {})
146
+ mapper_class = options[:mapper_class] || element.class
147
+ xml_mapping = mapper_class.mappings_for(:xml)
148
+ return xml unless xml_mapping
149
+
150
+ attributes = options[:xml_attributes] ||= {}
151
+ attributes = build_attributes(element,
152
+ xml_mapping, options).merge(attributes)&.compact
153
+
154
+ prefix = if options.key?(:namespace_prefix)
155
+ options[:namespace_prefix]
156
+ elsif xml_mapping.namespace_prefix
157
+ xml_mapping.namespace_prefix
158
+ end
159
+
160
+ prefixed_xml = xml.add_namespace_prefix(prefix)
161
+ tag_name = options[:tag_name] || xml_mapping.root_element
162
+
163
+ xml.create_and_add_element(tag_name, prefix: prefix,
164
+ attributes: attributes) do
165
+ if options.key?(:namespace_prefix) && !options[:namespace_prefix]
166
+ xml.add_namespace_prefix(nil)
167
+ end
168
+
169
+ xml_mapping.elements.each do |element_rule|
170
+ attribute_def = attribute_definition_for(element, element_rule,
171
+ mapper_class: mapper_class)
172
+
173
+ value = attribute_value_for(element, element_rule)
174
+
175
+ next if value.nil? && !element_rule.render_nil?
176
+
177
+ value = [value] if attribute_def.collection? && !value.is_a?(Array)
178
+
179
+ add_to_xml(
180
+ xml,
181
+ element_rule.prefix,
182
+ value,
183
+ options.merge({ attribute: attribute_def, rule: element_rule }),
184
+ )
185
+ end
186
+
187
+ if (content_rule = xml_mapping.content_mapping)
188
+ if content_rule.custom_methods[:to]
189
+ @root.send(content_rule.custom_methods[:to], element,
190
+ prefixed_xml.parent, prefixed_xml)
191
+ else
192
+ text = element.send(content_rule.to)
193
+ text = text.join if text.is_a?(Array)
194
+ prefixed_xml.add_text(xml, text)
195
+ end
196
+ end
197
+ end
198
+ end
199
+
100
200
  def ordered?(element, options = {})
101
201
  return false unless element.respond_to?(:element_order)
102
202
  return element.ordered? if element.respond_to?(:ordered?)
@@ -142,10 +242,12 @@ module Lutaml
142
242
  attrs
143
243
  end
144
244
 
145
- def build_attributes(element, xml_mapping)
245
+ def build_attributes(element, xml_mapping, options = {})
146
246
  attrs = namespace_attributes(xml_mapping)
147
247
 
148
248
  xml_mapping.attributes.each_with_object(attrs) do |mapping_rule, hash|
249
+ next if options[:except]&.include?(mapping_rule.to)
250
+
149
251
  if mapping_rule.namespace
150
252
  hash["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
151
253
  end
@@ -154,6 +256,8 @@ module Lutaml
154
256
  end
155
257
 
156
258
  xml_mapping.elements.each_with_object(attrs) do |mapping_rule, hash|
259
+ next if options[:except]&.include?(mapping_rule.to)
260
+
157
261
  if mapping_rule.namespace
158
262
  hash["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
159
263
  end
@@ -16,18 +16,6 @@ module Lutaml
16
16
  def to_yaml(options = {})
17
17
  YAML.dump(@attributes, options)
18
18
  end
19
-
20
- # TODO: Is this really needed?
21
- def self.to_yaml(attributes, *args)
22
- new(attributes).to_yaml(*args)
23
- end
24
-
25
- # TODO: Is this really needed?
26
- def self.from_yaml(yaml, klass)
27
- data = parse(yaml)
28
- mapped_attrs = klass.send(:apply_mappings, data, :yaml)
29
- klass.new(mapped_attrs)
30
- end
31
19
  end
32
20
  end
33
21
  end
data/lib/lutaml/model.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "model/version"
4
4
  require_relative "model/type"
5
+ require_relative "model/utils"
5
6
  require_relative "model/serializable"
6
7
  require_relative "model/json_adapter/standard_json_adapter"
7
8
  require_relative "model/yaml_adapter/standard_yaml_adapter"