lutaml-model 0.3.1 → 0.3.3

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.
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,292 +1,7 @@
1
- # lib/lutaml/model/xml_adapter.rb
2
-
3
- require_relative "xml_namespace"
4
- require_relative "mapping_hash"
5
-
6
1
  module Lutaml
7
2
  module Model
8
3
  module XmlAdapter
9
4
  XML_NAMESPACE_URI = "http://www.w3.org/XML/1998/namespace".freeze
10
-
11
- class Document
12
- attr_reader :root
13
-
14
- def initialize(root)
15
- @root = root
16
- end
17
-
18
- def self.parse(xml)
19
- raise NotImplementedError, "Subclasses must implement `parse`."
20
- end
21
-
22
- def children
23
- @root.children
24
- end
25
-
26
- def declaration(options)
27
- version = "1.0"
28
- version = options[:declaration] if options[:declaration].is_a?(String)
29
-
30
- encoding = options[:encoding] ? "UTF-8" : nil
31
- encoding = options[:encoding] if options[:encoding].is_a?(String)
32
-
33
- declaration = "<?xml version=\"#{version}\""
34
- declaration += " encoding=\"#{encoding}\"" if encoding
35
- declaration += "?>\n"
36
- declaration
37
- end
38
-
39
- def to_h
40
- parse_element(@root)
41
- end
42
-
43
- def order
44
- @root.order
45
- end
46
-
47
- def handle_nested_elements(builder, value, rule: nil, attribute: nil)
48
- options = build_options_for_nested_elements(attribute, rule)
49
-
50
- case value
51
- when Array
52
- value.each { |val| build_element(builder, val, options) }
53
- else
54
- build_element(builder, value, options)
55
- end
56
- end
57
-
58
- def build_options_for_nested_elements(attribute, rule)
59
- return {} unless rule
60
-
61
- options = {}
62
-
63
- options[:namespace_prefix] = rule.prefix if rule&.namespace_set?
64
- options[:mixed_content] = rule.mixed_content
65
-
66
- options[:mapper_class] = attribute&.type if attribute
67
-
68
- options
69
- end
70
-
71
- def parse_element(element)
72
- result = Lutaml::Model::MappingHash.new
73
- result.item_order = element.order
74
-
75
- element.children.each_with_object(result) do |child, hash|
76
- value = child.text? ? child.text : parse_element(child)
77
-
78
- if hash[child.unprefixed_name]
79
- hash[child.unprefixed_name] =
80
- [hash[child.unprefixed_name], value].flatten
81
- else
82
- hash[child.unprefixed_name] = value
83
- end
84
- end
85
-
86
- element.attributes.each_value do |attr|
87
- result[attr.unprefixed_name] = attr.value
88
- end
89
-
90
- result
91
- end
92
-
93
- def build_element(xml, element, options = {})
94
- if ordered?(element, options)
95
- build_ordered_element(xml, element, options)
96
- else
97
- build_unordered_element(xml, element, options)
98
- end
99
- end
100
-
101
- def ordered?(element, options = {})
102
- return false unless element.respond_to?(:element_order)
103
- return element.ordered? if element.respond_to?(:ordered?)
104
- return options[:mixed_content] if options.key?(:mixed_content)
105
-
106
- mapper_class = options[:mapper_class]
107
- mapper_class ? mapper_class.mappings_for(:xml).mixed_content? : false
108
- end
109
-
110
- def build_namespace_attributes(klass, processed = {})
111
- xml_mappings = klass.mappings_for(:xml)
112
- attributes = klass.attributes
113
-
114
- attrs = {}
115
-
116
- if xml_mappings.namespace_prefix
117
- attrs["xmlns:#{xml_mappings.namespace_prefix}"] =
118
- xml_mappings.namespace_uri
119
- end
120
-
121
- xml_mappings.mappings.each do |mapping_rule|
122
- processed[klass] ||= {}
123
-
124
- next if processed[klass][mapping_rule.name]
125
-
126
- processed[klass][mapping_rule.name] = true
127
-
128
- type = if mapping_rule.delegate
129
- attributes[mapping_rule.delegate].type.attributes[mapping_rule.to].type
130
- else
131
- attributes[mapping_rule.to].type
132
- end
133
-
134
- if type <= Lutaml::Model::Serialize
135
- attrs = attrs.merge(build_namespace_attributes(type, processed))
136
- end
137
-
138
- if mapping_rule.namespace
139
- attrs["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
140
- end
141
- end
142
-
143
- attrs
144
- end
145
-
146
- def build_attributes(element, xml_mapping)
147
- attrs = namespace_attributes(xml_mapping)
148
-
149
- xml_mapping.attributes.each_with_object(attrs) do |mapping_rule, hash|
150
- if mapping_rule.namespace
151
- hash["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
152
- end
153
-
154
- hash[mapping_rule.prefixed_name] = element.send(mapping_rule.to)
155
- end
156
-
157
- xml_mapping.elements.each_with_object(attrs) do |mapping_rule, hash|
158
- if mapping_rule.namespace
159
- hash["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
160
- end
161
- end
162
- end
163
-
164
- def attribute_definition_for(element, rule, mapper_class: nil)
165
- klass = mapper_class || element.class
166
- return klass.attributes[rule.to] unless rule.delegate
167
-
168
- element.send(rule.delegate).class.attributes[rule.to]
169
- end
170
-
171
- def attribute_value_for(element, rule)
172
- return element.send(rule.to) unless rule.delegate
173
-
174
- element.send(rule.delegate).send(rule.to)
175
- end
176
-
177
- def namespace_attributes(xml_mapping)
178
- return {} unless xml_mapping.namespace_uri
179
-
180
- key = ["xmlns", xml_mapping.namespace_prefix].compact.join(":")
181
- { key => xml_mapping.namespace_uri }
182
- end
183
- end
184
-
185
- class Element
186
- attr_reader :attributes,
187
- :children,
188
- :text,
189
- :namespace_prefix,
190
- :parent_document
191
-
192
- def initialize(
193
- name,
194
- attributes = {},
195
- children = [],
196
- text = nil,
197
- parent_document: nil,
198
- namespace_prefix: nil
199
- )
200
- @name = extract_name(name)
201
- @namespace_prefix = namespace_prefix || extract_namespace_prefix(name)
202
- @attributes = attributes # .map { |k, v| Attribute.new(k, v) }
203
- @children = children
204
- @text = text
205
- @parent_document = parent_document
206
- end
207
-
208
- def name
209
- if namespace_prefix
210
- "#{namespace_prefix}:#{@name}"
211
- else
212
- @name
213
- end
214
- end
215
-
216
- def unprefixed_name
217
- @name
218
- end
219
-
220
- def document
221
- Document.new(self)
222
- end
223
-
224
- def namespaces
225
- @namespaces || @parent_document&.namespaces || {}
226
- end
227
-
228
- def own_namespaces
229
- @namespaces || {}
230
- end
231
-
232
- def namespace
233
- return default_namespace unless namespace_prefix
234
-
235
- namespaces[namespace_prefix]
236
- end
237
-
238
- def attribute_is_namespace?(name)
239
- name.to_s.start_with?("xmlns")
240
- end
241
-
242
- def add_namespace(namespace)
243
- @namespaces ||= {}
244
- @namespaces[namespace.prefix] = namespace
245
- end
246
-
247
- def default_namespace
248
- namespaces[nil] || @parent_document&.namespaces&.dig(nil)
249
- end
250
-
251
- def extract_name(name)
252
- n = name.to_s.split(":")
253
- return name if n.length <= 1
254
-
255
- n[1..].join(":")
256
- end
257
-
258
- def extract_namespace_prefix(name)
259
- n = name.to_s.split(":")
260
- return if n.length <= 1
261
-
262
- n.first
263
- end
264
-
265
- def order
266
- children.each_with_object([]) do |child, arr|
267
- arr << child.unprefixed_name
268
- end
269
- end
270
- end
271
-
272
- class Attribute
273
- attr_reader :name, :value, :namespace, :namespace_prefix
274
-
275
- def initialize(name, value, namespace: nil, namespace_prefix: nil)
276
- @name = name
277
- @value = value
278
- @namespace = namespace
279
- @namespace_prefix = namespace_prefix
280
- end
281
-
282
- def unprefixed_name
283
- if namespace_prefix
284
- name.split(":").last
285
- else
286
- name
287
- end
288
- end
289
- end
290
5
  end
291
6
  end
292
7
  end
@@ -1,4 +1,3 @@
1
- # lib/lutaml/model/xml_mapping.rb
2
1
  require_relative "xml_mapping_rule"
3
2
 
4
3
  module Lutaml
@@ -10,8 +9,8 @@ module Lutaml
10
9
  :mixed_content
11
10
 
12
11
  def initialize
13
- @elements = []
14
- @attributes = []
12
+ @elements = {}
13
+ @attributes = {}
15
14
  @content_mapping = nil
16
15
  @mixed_content = false
17
16
  end
@@ -48,7 +47,7 @@ module Lutaml
48
47
  prefix: nil,
49
48
  mixed: false
50
49
  )
51
- @elements << XmlMappingRule.new(
50
+ @elements[name] = XmlMappingRule.new(
52
51
  name,
53
52
  to: to,
54
53
  render_nil: render_nil,
@@ -71,7 +70,7 @@ module Lutaml
71
70
  nil),
72
71
  prefix: nil
73
72
  )
74
- @attributes << XmlMappingRule.new(
73
+ @attributes[name] = XmlMappingRule.new(
75
74
  name,
76
75
  to: to,
77
76
  render_nil: render_nil,
@@ -82,6 +81,7 @@ module Lutaml
82
81
  namespace_set: namespace_set != false,
83
82
  )
84
83
  end
84
+
85
85
  # rubocop:enable Metrics/ParameterLists
86
86
 
87
87
  def map_content(
@@ -102,11 +102,11 @@ module Lutaml
102
102
  end
103
103
 
104
104
  def elements
105
- @elements
105
+ @elements.values
106
106
  end
107
107
 
108
108
  def attributes
109
- @attributes
109
+ @attributes.values
110
110
  end
111
111
 
112
112
  def content_mapping
@@ -1,4 +1,3 @@
1
- # lib/lutaml/model/xml_mapping_rule.rb
2
1
  require_relative "mapping_rule"
3
2
 
4
3
  module Lutaml
@@ -24,12 +23,12 @@ module Lutaml
24
23
  with: with,
25
24
  delegate: delegate,
26
25
  mixed_content: mixed_content,
27
- namespace_set: namespace_set
26
+ namespace_set: namespace_set,
28
27
  )
29
28
 
30
29
  @namespace = if namespace.to_s == "inherit"
31
- # we are using inherit_namespace in xml builder by
32
- # default so no need to do anything here.
30
+ # we are using inherit_namespace in xml builder by
31
+ # default so no need to do anything here.
33
32
  else
34
33
  namespace
35
34
  end
@@ -0,0 +1,34 @@
1
+ require "yaml"
2
+ require_relative "yaml_document"
3
+
4
+ module Lutaml
5
+ module Model
6
+ module YamlAdapter
7
+ class StandardYamlAdapter < YamlDocument
8
+ def self.parse(yaml)
9
+ YAML.safe_load(
10
+ yaml,
11
+ permitted_classes: [Date, Time, DateTime, Symbol,
12
+ BigDecimal, Hash, Array],
13
+ )
14
+ end
15
+
16
+ def to_yaml(options = {})
17
+ YAML.dump(@attributes, options)
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
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "yaml_object"
4
+
5
+ module Lutaml
6
+ module Model
7
+ module YamlAdapter
8
+ # Base class for YAML documents
9
+ class YamlDocument < YamlObject
10
+ def self.parse(yaml)
11
+ raise NotImplementedError, "Subclasses must implement `parse`."
12
+ end
13
+
14
+ def to_yaml(*args)
15
+ raise NotImplementedError, "Subclasses must implement `to_yaml`."
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module YamlAdapter
6
+ # Base class for YAML objects
7
+ class YamlObject
8
+ attr_reader :attributes
9
+
10
+ def initialize(attributes = {})
11
+ @attributes = attributes
12
+ end
13
+
14
+ def [](key)
15
+ @attributes[key]
16
+ end
17
+
18
+ def []=(key, value)
19
+ @attributes[key] = value
20
+ end
21
+
22
+ def to_h
23
+ @attributes
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,34 +1,8 @@
1
- # lib/lutaml/model/yaml_adapter.rb
2
- require "yaml"
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Lutaml
5
4
  module Model
6
5
  module YamlAdapter
7
- class Standard
8
- def initialize(attributes)
9
- @attributes = attributes
10
- end
11
-
12
- def to_yaml(options = {})
13
- YAML.dump(@attributes, options)
14
- end
15
-
16
- def self.to_yaml(attributes, *args)
17
- new(attributes).to_yaml(*args)
18
- end
19
-
20
- def self.from_yaml(yaml, klass)
21
- data = parse(yaml)
22
- mapped_attrs = klass.send(:apply_mappings, data, :yaml)
23
- klass.new(mapped_attrs)
24
- end
25
-
26
- def self.parse(yaml)
27
- YAML.safe_load(yaml,
28
- permitted_classes: [Date, Time, DateTime, Symbol,
29
- BigDecimal, Hash, Array])
30
- end
31
- end
32
6
  end
33
7
  end
34
8
  end
data/lib/lutaml/model.rb CHANGED
@@ -1,11 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "model/version"
4
-
5
- # lib/lutaml/model.rb
6
- require "nokogiri"
7
- require "json"
8
- require "yaml"
9
4
  require_relative "model/type"
10
5
  require_relative "model/serializable"
11
6
  require_relative "model/json_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.1
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-13 00:00:00.000000000 Z
11
+ date: 2024-08-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'LutaML creating data models in Ruby
14
14
 
@@ -37,8 +37,10 @@ files:
37
37
  - lib/lutaml/model/error.rb
38
38
  - lib/lutaml/model/error/invalid_value_error.rb
39
39
  - lib/lutaml/model/json_adapter.rb
40
- - lib/lutaml/model/json_adapter/multi_json.rb
41
- - lib/lutaml/model/json_adapter/standard.rb
40
+ - lib/lutaml/model/json_adapter/json_document.rb
41
+ - lib/lutaml/model/json_adapter/json_object.rb
42
+ - lib/lutaml/model/json_adapter/multi_json_adapter.rb
43
+ - lib/lutaml/model/json_adapter/standard_json_adapter.rb
42
44
  - lib/lutaml/model/key_value_mapping.rb
43
45
  - lib/lutaml/model/key_value_mapping_rule.rb
44
46
  - lib/lutaml/model/mapping_hash.rb
@@ -51,19 +53,29 @@ files:
51
53
  - lib/lutaml/model/serializable.rb
52
54
  - lib/lutaml/model/serialize.rb
53
55
  - lib/lutaml/model/toml_adapter.rb
56
+ - lib/lutaml/model/toml_adapter/toml_document.rb
57
+ - lib/lutaml/model/toml_adapter/toml_object.rb
54
58
  - lib/lutaml/model/toml_adapter/toml_rb_adapter.rb
55
59
  - lib/lutaml/model/toml_adapter/tomlib_adapter.rb
56
60
  - lib/lutaml/model/type.rb
61
+ - lib/lutaml/model/type/date_time.rb
62
+ - lib/lutaml/model/type/json.rb
57
63
  - lib/lutaml/model/type/time_without_date.rb
58
64
  - lib/lutaml/model/version.rb
59
65
  - lib/lutaml/model/xml_adapter.rb
60
66
  - lib/lutaml/model/xml_adapter/nokogiri_adapter.rb
61
67
  - lib/lutaml/model/xml_adapter/oga_adapter.rb
62
68
  - lib/lutaml/model/xml_adapter/ox_adapter.rb
69
+ - lib/lutaml/model/xml_adapter/xml_attribute.rb
70
+ - lib/lutaml/model/xml_adapter/xml_document.rb
71
+ - lib/lutaml/model/xml_adapter/xml_element.rb
72
+ - lib/lutaml/model/xml_adapter/xml_namespace.rb
63
73
  - lib/lutaml/model/xml_mapping.rb
64
74
  - lib/lutaml/model/xml_mapping_rule.rb
65
- - lib/lutaml/model/xml_namespace.rb
66
75
  - lib/lutaml/model/yaml_adapter.rb
76
+ - lib/lutaml/model/yaml_adapter/standard_yaml_adapter.rb
77
+ - lib/lutaml/model/yaml_adapter/yaml_document.rb
78
+ - lib/lutaml/model/yaml_adapter/yaml_object.rb
67
79
  - lutaml-model.gemspec
68
80
  - sig/lutaml/model.rbs
69
81
  homepage: https://github.com/lutaml/lutaml-model
@@ -1,47 +0,0 @@
1
- # frozen_striing_literal: true
2
-
3
- module Lutaml
4
- module Model
5
- class XmlNamespace
6
- # Return name
7
- #
8
- # @return [String]
9
- #
10
- # @api private
11
- attr_accessor :uri
12
-
13
- # Return prefix
14
- #
15
- # @return [String]
16
- #
17
- # @api private
18
- attr_accessor :prefix
19
-
20
- # Initialize instance
21
- #
22
- # @param [String, nil] name
23
- # @param [String, nil] prefix
24
- #
25
- # @api private
26
- def initialize(uri = nil, prefix = nil)
27
- @uri = uri
28
- @prefix = normalize_prefix(prefix)
29
- end
30
-
31
- def normalize_prefix(prefix)
32
- normalized_prefix = prefix.to_s.gsub(/xmlns:?/, "")
33
- return if normalized_prefix.empty?
34
-
35
- normalized_prefix
36
- end
37
-
38
- def attr_name
39
- if prefix && !prefix.empty?
40
- "xmlns:#{prefix}"
41
- else
42
- "xmlns"
43
- end
44
- end
45
- end
46
- end
47
- end