lutaml-model 0.3.6 → 0.3.7

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