lutaml-model 0.3.1 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -5
  3. data/.rubocop_todo.yml +21 -101
  4. data/Gemfile +3 -18
  5. data/README.adoc +145 -6
  6. data/lib/lutaml/model/attribute.rb +15 -2
  7. data/lib/lutaml/model/config.rb +0 -1
  8. data/lib/lutaml/model/json_adapter/json_document.rb +20 -0
  9. data/lib/lutaml/model/json_adapter/json_object.rb +28 -0
  10. data/lib/lutaml/model/json_adapter/{multi_json.rb → multi_json_adapter.rb} +2 -3
  11. data/lib/lutaml/model/json_adapter/{standard.rb → standard_json_adapter.rb} +2 -3
  12. data/lib/lutaml/model/json_adapter.rb +1 -31
  13. data/lib/lutaml/model/key_value_mapping.rb +0 -1
  14. data/lib/lutaml/model/key_value_mapping_rule.rb +0 -1
  15. data/lib/lutaml/model/mapping_hash.rb +0 -2
  16. data/lib/lutaml/model/mapping_rule.rb +0 -1
  17. data/lib/lutaml/model/schema/json_schema.rb +0 -1
  18. data/lib/lutaml/model/schema/relaxng_schema.rb +0 -1
  19. data/lib/lutaml/model/schema/xsd_schema.rb +0 -1
  20. data/lib/lutaml/model/schema/yaml_schema.rb +0 -1
  21. data/lib/lutaml/model/schema.rb +0 -1
  22. data/lib/lutaml/model/serializable.rb +0 -1
  23. data/lib/lutaml/model/serialize.rb +22 -4
  24. data/lib/lutaml/model/toml_adapter/toml_document.rb +20 -0
  25. data/lib/lutaml/model/toml_adapter/toml_object.rb +28 -0
  26. data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +2 -3
  27. data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +2 -3
  28. data/lib/lutaml/model/toml_adapter.rb +0 -31
  29. data/lib/lutaml/model/type/date_time.rb +20 -0
  30. data/lib/lutaml/model/type/json.rb +34 -0
  31. data/lib/lutaml/model/type/time_without_date.rb +4 -3
  32. data/lib/lutaml/model/type.rb +61 -124
  33. data/lib/lutaml/model/version.rb +1 -1
  34. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +9 -8
  35. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +4 -5
  36. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +14 -13
  37. data/lib/lutaml/model/xml_adapter/xml_attribute.rb +27 -0
  38. data/lib/lutaml/model/xml_adapter/xml_document.rb +185 -0
  39. data/lib/lutaml/model/xml_adapter/xml_element.rb +94 -0
  40. data/lib/lutaml/model/xml_adapter/xml_namespace.rb +49 -0
  41. data/lib/lutaml/model/xml_adapter.rb +0 -285
  42. data/lib/lutaml/model/xml_mapping.rb +7 -7
  43. data/lib/lutaml/model/xml_mapping_rule.rb +3 -4
  44. data/lib/lutaml/model/yaml_adapter/standard_yaml_adapter.rb +34 -0
  45. data/lib/lutaml/model/yaml_adapter/yaml_document.rb +20 -0
  46. data/lib/lutaml/model/yaml_adapter/yaml_object.rb +28 -0
  47. data/lib/lutaml/model/yaml_adapter.rb +1 -27
  48. data/lib/lutaml/model.rb +0 -5
  49. metadata +17 -5
  50. data/lib/lutaml/model/xml_namespace.rb +0 -47
@@ -1,11 +1,10 @@
1
- # lib/lutaml/model/xml_adapter/ox_adapter.rb
2
1
  require "ox"
3
- require_relative "../xml_adapter"
2
+ require_relative "xml_document"
4
3
 
5
4
  module Lutaml
6
5
  module Model
7
6
  module XmlAdapter
8
- class OxDocument < Document
7
+ class OxAdapter < XmlDocument
9
8
  def self.parse(xml)
10
9
  parsed = Ox.parse(xml)
11
10
  root = OxElement.new(parsed)
@@ -37,12 +36,13 @@ module Lutaml
37
36
 
38
37
  attributes = build_attributes(element, xml_mapping).compact
39
38
 
39
+ tag_name = options[:tag_name] || xml_mapping.root_element
40
40
  prefixed_name = if options.key?(:namespace_prefix)
41
- [options[:namespace_prefix], xml_mapping.root_element].compact.join(":")
41
+ [options[:namespace_prefix], tag_name].compact.join(":")
42
42
  elsif xml_mapping.namespace_prefix
43
- "#{xml_mapping.namespace_prefix}:#{xml_mapping.root_element}"
43
+ "#{xml_mapping.namespace_prefix}:#{tag_name}"
44
44
  else
45
- xml_mapping.root_element
45
+ tag_name
46
46
  end
47
47
 
48
48
  builder.element(prefixed_name, attributes) do |el|
@@ -85,7 +85,8 @@ module Lutaml
85
85
 
86
86
  attributes = build_attributes(element, xml_mapping).compact
87
87
 
88
- builder.element(xml_mapping.root_element, attributes) do |el|
88
+ tag_name = options[:tag_name] || xml_mapping.root_element
89
+ builder.element(tag_name, attributes) do |el|
89
90
  index_hash = {}
90
91
 
91
92
  element.element_order.each do |name|
@@ -137,16 +138,16 @@ module Lutaml
137
138
  end
138
139
  end
139
140
 
140
- class OxElement < Element
141
+ class OxElement < XmlElement
141
142
  def initialize(node, root_node: nil)
142
143
  if node.is_a?(String)
143
144
  super("text", {}, [], node, parent_document: root_node)
144
145
  else
145
146
  namespace_attributes(node.attributes).each do |(name, value)|
146
147
  if root_node
147
- root_node.add_namespace(Lutaml::Model::XmlNamespace.new(value, name))
148
+ root_node.add_namespace(XmlNamespace.new(value, name))
148
149
  else
149
- add_namespace(Lutaml::Model::XmlNamespace.new(value, name))
150
+ add_namespace(XmlNamespace.new(value, name))
150
151
  end
151
152
  end
152
153
 
@@ -156,11 +157,11 @@ module Lutaml
156
157
  namespace_prefix = name.to_s.split(":").first
157
158
  if (n = name.to_s.split(":")).length > 1
158
159
  namespace = (root_node || self).namespaces[namespace_prefix]&.uri
159
- namespace ||= Lutaml::Model::XmlAdapter::XML_NAMESPACE_URI
160
+ namespace ||= XML_NAMESPACE_URI
160
161
  prefix = n.first
161
162
  end
162
163
 
163
- hash[name.to_s] = Attribute.new(
164
+ hash[name.to_s] = XmlAttribute.new(
164
165
  name.to_s,
165
166
  value,
166
167
  namespace: namespace,
@@ -173,7 +174,7 @@ module Lutaml
173
174
  attributes,
174
175
  parse_children(node, root_node: root_node || self),
175
176
  node.text,
176
- parent_document: root_node
177
+ parent_document: root_node,
177
178
  )
178
179
  end
179
180
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module XmlAdapter
6
+ # Represents an XML attribute
7
+ class XmlAttribute
8
+ attr_reader :name, :value, :namespace, :namespace_prefix
9
+
10
+ def initialize(name, value, namespace: nil, namespace_prefix: nil)
11
+ @name = name
12
+ @value = value
13
+ @namespace = namespace
14
+ @namespace_prefix = namespace_prefix
15
+ end
16
+
17
+ def unprefixed_name
18
+ if namespace_prefix
19
+ name.split(":").last
20
+ else
21
+ name
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,185 @@
1
+ require_relative "../mapping_hash"
2
+ require_relative "xml_element"
3
+ require_relative "xml_attribute"
4
+ require_relative "xml_namespace"
5
+
6
+ module Lutaml
7
+ module Model
8
+ module XmlAdapter
9
+ class XmlDocument
10
+ attr_reader :root
11
+
12
+ def initialize(root)
13
+ @root = root
14
+ end
15
+
16
+ def self.parse(xml)
17
+ raise NotImplementedError, "Subclasses must implement `parse`."
18
+ end
19
+
20
+ def children
21
+ @root.children
22
+ end
23
+
24
+ def declaration(options)
25
+ version = "1.0"
26
+ version = options[:declaration] if options[:declaration].is_a?(String)
27
+
28
+ encoding = options[:encoding] ? "UTF-8" : nil
29
+ encoding = options[:encoding] if options[:encoding].is_a?(String)
30
+
31
+ declaration = "<?xml version=\"#{version}\""
32
+ declaration += " encoding=\"#{encoding}\"" if encoding
33
+ declaration += "?>\n"
34
+ declaration
35
+ end
36
+
37
+ def to_h
38
+ parse_element(@root)
39
+ end
40
+
41
+ def order
42
+ @root.order
43
+ end
44
+
45
+ def handle_nested_elements(builder, value, rule: nil, attribute: nil)
46
+ options = build_options_for_nested_elements(attribute, rule)
47
+
48
+ case value
49
+ when Array
50
+ value.each { |val| build_element(builder, val, options) }
51
+ else
52
+ build_element(builder, value, options)
53
+ end
54
+ end
55
+
56
+ def build_options_for_nested_elements(attribute, rule)
57
+ return {} unless rule
58
+
59
+ options = {}
60
+
61
+ options[:namespace_prefix] = rule.prefix if rule&.namespace_set?
62
+ options[:mixed_content] = rule.mixed_content
63
+ options[:tag_name] = rule.name
64
+
65
+ options[:mapper_class] = attribute&.type if attribute
66
+
67
+ options
68
+ end
69
+
70
+ def parse_element(element)
71
+ result = Lutaml::Model::MappingHash.new
72
+ result.item_order = element.order
73
+
74
+ element.children.each_with_object(result) do |child, hash|
75
+ value = child.text? ? child.text : parse_element(child)
76
+
77
+ if hash[child.unprefixed_name]
78
+ hash[child.unprefixed_name] =
79
+ [hash[child.unprefixed_name], value].flatten
80
+ else
81
+ hash[child.unprefixed_name] = value
82
+ end
83
+ end
84
+
85
+ element.attributes.each_value do |attr|
86
+ result[attr.unprefixed_name] = attr.value
87
+ end
88
+
89
+ result
90
+ end
91
+
92
+ def build_element(xml, element, options = {})
93
+ if ordered?(element, options)
94
+ build_ordered_element(xml, element, options)
95
+ else
96
+ build_unordered_element(xml, element, options)
97
+ end
98
+ end
99
+
100
+ def ordered?(element, options = {})
101
+ return false unless element.respond_to?(:element_order)
102
+ return element.ordered? if element.respond_to?(:ordered?)
103
+ return options[:mixed_content] if options.key?(:mixed_content)
104
+
105
+ mapper_class = options[:mapper_class]
106
+ mapper_class ? mapper_class.mappings_for(:xml).mixed_content? : false
107
+ end
108
+
109
+ def build_namespace_attributes(klass, processed = {})
110
+ xml_mappings = klass.mappings_for(:xml)
111
+ attributes = klass.attributes
112
+
113
+ attrs = {}
114
+
115
+ if xml_mappings.namespace_prefix
116
+ attrs["xmlns:#{xml_mappings.namespace_prefix}"] =
117
+ xml_mappings.namespace_uri
118
+ end
119
+
120
+ xml_mappings.mappings.each do |mapping_rule|
121
+ processed[klass] ||= {}
122
+
123
+ next if processed[klass][mapping_rule.name]
124
+
125
+ processed[klass][mapping_rule.name] = true
126
+
127
+ type = if mapping_rule.delegate
128
+ attributes[mapping_rule.delegate].type.attributes[mapping_rule.to].type
129
+ else
130
+ attributes[mapping_rule.to].type
131
+ end
132
+
133
+ if type <= Lutaml::Model::Serialize
134
+ attrs = attrs.merge(build_namespace_attributes(type, processed))
135
+ end
136
+
137
+ if mapping_rule.namespace
138
+ attrs["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
139
+ end
140
+ end
141
+
142
+ attrs
143
+ end
144
+
145
+ def build_attributes(element, xml_mapping)
146
+ attrs = namespace_attributes(xml_mapping)
147
+
148
+ xml_mapping.attributes.each_with_object(attrs) do |mapping_rule, hash|
149
+ if mapping_rule.namespace
150
+ hash["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
151
+ end
152
+
153
+ hash[mapping_rule.prefixed_name] = element.send(mapping_rule.to)
154
+ end
155
+
156
+ xml_mapping.elements.each_with_object(attrs) do |mapping_rule, hash|
157
+ if mapping_rule.namespace
158
+ hash["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
159
+ end
160
+ end
161
+ end
162
+
163
+ def attribute_definition_for(element, rule, mapper_class: nil)
164
+ klass = mapper_class || element.class
165
+ return klass.attributes[rule.to] unless rule.delegate
166
+
167
+ element.send(rule.delegate).class.attributes[rule.to]
168
+ end
169
+
170
+ def attribute_value_for(element, rule)
171
+ return element.send(rule.to) unless rule.delegate
172
+
173
+ element.send(rule.delegate).send(rule.to)
174
+ end
175
+
176
+ def namespace_attributes(xml_mapping)
177
+ return {} unless xml_mapping.namespace_uri
178
+
179
+ key = ["xmlns", xml_mapping.namespace_prefix].compact.join(":")
180
+ { key => xml_mapping.namespace_uri }
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,94 @@
1
+ require_relative "xml_attribute"
2
+
3
+ module Lutaml
4
+ module Model
5
+ module XmlAdapter
6
+ class XmlElement
7
+ attr_reader :attributes,
8
+ :children,
9
+ :text,
10
+ :namespace_prefix,
11
+ :parent_document
12
+
13
+ def initialize(
14
+ name,
15
+ attributes = {},
16
+ children = [],
17
+ text = nil,
18
+ parent_document: nil,
19
+ namespace_prefix: nil
20
+ )
21
+ @name = extract_name(name)
22
+ @namespace_prefix = namespace_prefix || extract_namespace_prefix(name)
23
+ @attributes = attributes # .map { |k, v| XmlAttribute.new(k, v) }
24
+ @children = children
25
+ @text = text
26
+ @parent_document = parent_document
27
+ end
28
+
29
+ def name
30
+ if namespace_prefix
31
+ "#{namespace_prefix}:#{@name}"
32
+ else
33
+ @name
34
+ end
35
+ end
36
+
37
+ def unprefixed_name
38
+ @name
39
+ end
40
+
41
+ def document
42
+ XmlDocument.new(self)
43
+ end
44
+
45
+ def namespaces
46
+ @namespaces || @parent_document&.namespaces || {}
47
+ end
48
+
49
+ def own_namespaces
50
+ @namespaces || {}
51
+ end
52
+
53
+ def namespace
54
+ return default_namespace unless namespace_prefix
55
+
56
+ namespaces[namespace_prefix]
57
+ end
58
+
59
+ def attribute_is_namespace?(name)
60
+ name.to_s.start_with?("xmlns")
61
+ end
62
+
63
+ def add_namespace(namespace)
64
+ @namespaces ||= {}
65
+ @namespaces[namespace.prefix] = namespace
66
+ end
67
+
68
+ def default_namespace
69
+ namespaces[nil] || @parent_document&.namespaces&.dig(nil)
70
+ end
71
+
72
+ def extract_name(name)
73
+ n = name.to_s.split(":")
74
+ return name if n.length <= 1
75
+
76
+ n[1..].join(":")
77
+ end
78
+
79
+ def extract_namespace_prefix(name)
80
+ n = name.to_s.split(":")
81
+ return if n.length <= 1
82
+
83
+ n.first
84
+ end
85
+
86
+ def order
87
+ children.each_with_object([]) do |child, arr|
88
+ arr << child.unprefixed_name
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_striing_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module XmlAdapter
6
+ class XmlNamespace
7
+ # Return name
8
+ #
9
+ # @return [String]
10
+ #
11
+ # @api private
12
+ attr_accessor :uri
13
+
14
+ # Return prefix
15
+ #
16
+ # @return [String]
17
+ #
18
+ # @api private
19
+ attr_accessor :prefix
20
+
21
+ # Initialize instance
22
+ #
23
+ # @param [String, nil] name
24
+ # @param [String, nil] prefix
25
+ #
26
+ # @api private
27
+ def initialize(uri = nil, prefix = nil)
28
+ @uri = uri
29
+ @prefix = normalize_prefix(prefix)
30
+ end
31
+
32
+ def normalize_prefix(prefix)
33
+ normalized_prefix = prefix.to_s.gsub(/xmlns:?/, "")
34
+ return if normalized_prefix.empty?
35
+
36
+ normalized_prefix
37
+ end
38
+
39
+ def attr_name
40
+ if prefix && !prefix.empty?
41
+ "xmlns:#{prefix}"
42
+ else
43
+ "xmlns"
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end