lutaml-model 0.3.6 → 0.3.7

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.
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Utils
6
+ class << self
7
+ # Convert string to camel case
8
+ def camel_case(str)
9
+ return "" if str.nil? || str.empty?
10
+
11
+ str.split("/").map { |part| camelize_part(part) }.join("::")
12
+ end
13
+
14
+ # Convert string to class name
15
+ def classify(str)
16
+ str = str.to_s.delete(".")
17
+ str = str.sub(/^[a-z\d]*/) { |match| camel_case(match) || match }
18
+
19
+ str.gsub("::", "/").gsub(%r{(?:_|-|(/))([a-z\d]*)}i) do
20
+ word = Regexp.last_match(2)
21
+ substituted = camel_case(word) || word
22
+ Regexp.last_match(1) ? "::#{substituted}" : substituted
23
+ end
24
+ end
25
+
26
+ # Convert string to snake case
27
+ def snake_case(str)
28
+ str = str.to_s.tr(".", "_")
29
+ return str unless /[A-Z-]|::/.match?(str)
30
+
31
+ str.gsub("::", "/")
32
+ .gsub(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { "#{$1 || $2}_" }
33
+ .tr("-", "_")
34
+ .downcase
35
+ end
36
+
37
+ private
38
+
39
+ def camelize_part(part)
40
+ part.gsub(/(?:_|-|^)([a-z\d])/i) { $1.upcase }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.3.6"
5
+ VERSION = "0.3.7"
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,9 +63,9 @@ 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)
@@ -124,39 +73,16 @@ module Lutaml
124
73
 
125
74
  prefixed_xml.text text
126
75
  elsif attribute_def.collection?
127
- add_to_xml(nsp_xml, value[curr_index], attribute_def,
128
- element_rule)
76
+ add_to_xml(
77
+ xml,
78
+ element_rule.prefix,
79
+ value[curr_index],
80
+ attribute_def,
81
+ element_rule,
82
+ )
129
83
  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
84
+ add_to_xml(xml, element_rule.prefix, value, attribute_def,
85
+ element_rule)
160
86
  end
161
87
  end
162
88
  end
@@ -205,7 +131,7 @@ module Lutaml
205
131
  end
206
132
 
207
133
  def to_xml(builder = nil)
208
- builder ||= Nokogiri::XML::Builder.new
134
+ builder ||= Builder::Nokogiri.build
209
135
 
210
136
  if name == "text"
211
137
  builder.text(text)
@@ -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,20 @@ 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
58
+ el.add_text(el, text)
110
59
  elsif attribute_def.collection?
111
- add_to_xml(el, value[curr_index], attribute_def, element_rule)
60
+ add_to_xml(el, nil, value[curr_index], attribute_def,
61
+ element_rule)
112
62
  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
63
+ add_to_xml(el, nil, value, attribute_def, element_rule)
143
64
  end
144
65
  end
145
66
  end
@@ -188,13 +109,13 @@ module Lutaml
188
109
  end
189
110
 
190
111
  def to_xml(builder = nil)
191
- builder ||= Ox::Builder.new
112
+ builder ||= Builder::Ox.build
192
113
  attrs = build_attributes(self)
193
114
 
194
115
  if text?
195
- builder.text(text)
116
+ builder.add_text(builder, text)
196
117
  else
197
- builder.element(name, attrs) do |el|
118
+ builder.create_and_add_element(name, attributes: attrs) do |el|
198
119
  children.each { |child| child.to_xml(el) }
199
120
  end
200
121
  end
@@ -97,6 +97,95 @@ module Lutaml
97
97
  end
98
98
  end
99
99
 
100
+ def add_to_xml(xml, prefix, value, attribute, rule)
101
+ if rule.custom_methods[:to]
102
+ @root.send(rule.custom_methods[:to], @root, xml.parent, xml)
103
+ return
104
+ end
105
+
106
+ if value && (attribute&.type&.<= Lutaml::Model::Serialize)
107
+ handle_nested_elements(
108
+ xml,
109
+ value,
110
+ rule: rule,
111
+ attribute: attribute,
112
+ )
113
+ else
114
+ xml.create_and_add_element(rule.name, prefix: prefix) do
115
+ if !value.nil?
116
+ serialized_value = attribute.type.serialize(value)
117
+
118
+ if attribute.type == Lutaml::Model::Type::Hash
119
+ serialized_value.each do |key, val|
120
+ xml.create_and_add_element(key) do |element|
121
+ element.text(val)
122
+ end
123
+ end
124
+ else
125
+ xml.add_text(xml, serialized_value)
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ def build_unordered_element(xml, element, options = {})
133
+ mapper_class = options[:mapper_class] || element.class
134
+ xml_mapping = mapper_class.mappings_for(:xml)
135
+ return xml unless xml_mapping
136
+
137
+ attributes = options[:xml_attributes] ||= {}
138
+ attributes = build_attributes(element,
139
+ xml_mapping).merge(attributes)&.compact
140
+
141
+ prefix = if options.key?(:namespace_prefix)
142
+ options[:namespace_prefix]
143
+ elsif xml_mapping.namespace_prefix
144
+ xml_mapping.namespace_prefix
145
+ end
146
+
147
+ prefixed_xml = xml.add_namespace_prefix(prefix)
148
+ tag_name = options[:tag_name] || xml_mapping.root_element
149
+
150
+ xml.create_and_add_element(tag_name, prefix: prefix,
151
+ attributes: attributes) do
152
+ if options.key?(:namespace_prefix) && !options[:namespace_prefix]
153
+ xml.add_namespace_prefix(nil)
154
+ end
155
+
156
+ xml_mapping.elements.each do |element_rule|
157
+ attribute_def = attribute_definition_for(element, element_rule,
158
+ mapper_class: mapper_class)
159
+ value = attribute_value_for(element, element_rule)
160
+
161
+ next if value.nil? && !element_rule.render_nil?
162
+
163
+ if attribute_def.collection?
164
+ value = [value] unless value.is_a?(Array)
165
+
166
+ value.each do |v|
167
+ add_to_xml(xml, element_rule.prefix, v, attribute_def,
168
+ element_rule)
169
+ end
170
+ elsif !value.nil? || element_rule.render_nil?
171
+ add_to_xml(xml, element_rule.prefix, value, attribute_def,
172
+ element_rule)
173
+ end
174
+ end
175
+
176
+ if (content_rule = xml_mapping.content_mapping)
177
+ if content_rule.custom_methods[:to]
178
+ @root.send(content_rule.custom_methods[:to], element,
179
+ prefixed_xml.parent, prefixed_xml)
180
+ else
181
+ text = element.send(content_rule.to)
182
+ text = text.join if text.is_a?(Array)
183
+ prefixed_xml.add_text(xml, text)
184
+ end
185
+ end
186
+ end
187
+ end
188
+
100
189
  def ordered?(element, options = {})
101
190
  return false unless element.respond_to?(:element_order)
102
191
  return element.ordered? if element.respond_to?(:ordered?)
@@ -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"
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.6
4
+ version: 0.3.7
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-08-22 00:00:00.000000000 Z
11
+ date: 2024-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -82,8 +82,11 @@ files:
82
82
  - lib/lutaml/model/type.rb
83
83
  - lib/lutaml/model/type/date_time.rb
84
84
  - lib/lutaml/model/type/time_without_date.rb
85
+ - lib/lutaml/model/utils.rb
85
86
  - lib/lutaml/model/version.rb
86
87
  - lib/lutaml/model/xml_adapter.rb
88
+ - lib/lutaml/model/xml_adapter/builder/nokogiri.rb
89
+ - lib/lutaml/model/xml_adapter/builder/ox.rb
87
90
  - lib/lutaml/model/xml_adapter/nokogiri_adapter.rb
88
91
  - lib/lutaml/model/xml_adapter/oga_adapter.rb
89
92
  - lib/lutaml/model/xml_adapter/ox_adapter.rb