lutaml-model 0.3.6 → 0.3.8

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