lutaml-model 0.2.1 → 0.3.1
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +207 -0
- data/Gemfile +6 -2
- data/README.adoc +1054 -126
- data/lib/lutaml/model/attribute.rb +4 -0
- data/lib/lutaml/model/error/invalid_value_error.rb +18 -0
- data/lib/lutaml/model/error.rb +8 -0
- data/lib/lutaml/model/key_value_mapping.rb +9 -1
- data/lib/lutaml/model/mapping_hash.rb +42 -0
- data/lib/lutaml/model/mapping_rule.rb +24 -2
- data/lib/lutaml/model/serialize.rb +270 -189
- data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +2 -2
- data/lib/lutaml/model/type.rb +19 -13
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +104 -101
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +125 -85
- data/lib/lutaml/model/xml_adapter.rb +138 -7
- data/lib/lutaml/model/xml_mapping.rb +36 -6
- data/lib/lutaml/model/xml_mapping_rule.rb +6 -6
- data/lib/lutaml/model/yaml_adapter.rb +11 -3
- data/lib/lutaml/model.rb +7 -0
- metadata +6 -2
@@ -1,10 +1,13 @@
|
|
1
1
|
# lib/lutaml/model/xml_adapter.rb
|
2
2
|
|
3
3
|
require_relative "xml_namespace"
|
4
|
+
require_relative "mapping_hash"
|
4
5
|
|
5
6
|
module Lutaml
|
6
7
|
module Model
|
7
8
|
module XmlAdapter
|
9
|
+
XML_NAMESPACE_URI = "http://www.w3.org/XML/1998/namespace".freeze
|
10
|
+
|
8
11
|
class Document
|
9
12
|
attr_reader :root
|
10
13
|
|
@@ -33,8 +36,113 @@ module Lutaml
|
|
33
36
|
declaration
|
34
37
|
end
|
35
38
|
|
36
|
-
|
37
|
-
|
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
|
+
|
38
146
|
def build_attributes(element, xml_mapping)
|
39
147
|
attrs = namespace_attributes(xml_mapping)
|
40
148
|
|
@@ -52,8 +160,19 @@ module Lutaml
|
|
52
160
|
end
|
53
161
|
end
|
54
162
|
end
|
55
|
-
|
56
|
-
|
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
|
57
176
|
|
58
177
|
def namespace_attributes(xml_mapping)
|
59
178
|
return {} unless xml_mapping.namespace_uri
|
@@ -70,7 +189,6 @@ module Lutaml
|
|
70
189
|
:namespace_prefix,
|
71
190
|
:parent_document
|
72
191
|
|
73
|
-
# rubocop:disable Metrics/ParameterLists
|
74
192
|
def initialize(
|
75
193
|
name,
|
76
194
|
attributes = {},
|
@@ -86,10 +204,9 @@ module Lutaml
|
|
86
204
|
@text = text
|
87
205
|
@parent_document = parent_document
|
88
206
|
end
|
89
|
-
# rubocop:enable Metrics/ParameterLists
|
90
207
|
|
91
208
|
def name
|
92
|
-
if namespace_prefix
|
209
|
+
if namespace_prefix
|
93
210
|
"#{namespace_prefix}:#{@name}"
|
94
211
|
else
|
95
212
|
@name
|
@@ -144,6 +261,12 @@ module Lutaml
|
|
144
261
|
|
145
262
|
n.first
|
146
263
|
end
|
264
|
+
|
265
|
+
def order
|
266
|
+
children.each_with_object([]) do |child, arr|
|
267
|
+
arr << child.unprefixed_name
|
268
|
+
end
|
269
|
+
end
|
147
270
|
end
|
148
271
|
|
149
272
|
class Attribute
|
@@ -155,6 +278,14 @@ module Lutaml
|
|
155
278
|
@namespace = namespace
|
156
279
|
@namespace_prefix = namespace_prefix
|
157
280
|
end
|
281
|
+
|
282
|
+
def unprefixed_name
|
283
|
+
if namespace_prefix
|
284
|
+
name.split(":").last
|
285
|
+
else
|
286
|
+
name
|
287
|
+
end
|
288
|
+
end
|
158
289
|
end
|
159
290
|
end
|
160
291
|
end
|
@@ -4,16 +4,23 @@ require_relative "xml_mapping_rule"
|
|
4
4
|
module Lutaml
|
5
5
|
module Model
|
6
6
|
class XmlMapping
|
7
|
-
attr_reader :root_element,
|
7
|
+
attr_reader :root_element,
|
8
|
+
:namespace_uri,
|
9
|
+
:namespace_prefix,
|
10
|
+
:mixed_content
|
8
11
|
|
9
12
|
def initialize
|
10
13
|
@elements = []
|
11
14
|
@attributes = []
|
12
15
|
@content_mapping = nil
|
16
|
+
@mixed_content = false
|
13
17
|
end
|
14
18
|
|
15
|
-
|
19
|
+
alias mixed_content? mixed_content
|
20
|
+
|
21
|
+
def root(name, mixed: false)
|
16
22
|
@root_element = name
|
23
|
+
@mixed_content = mixed
|
17
24
|
end
|
18
25
|
|
19
26
|
def prefixed_root
|
@@ -36,8 +43,10 @@ module Lutaml
|
|
36
43
|
render_nil: false,
|
37
44
|
with: {},
|
38
45
|
delegate: nil,
|
39
|
-
namespace:
|
40
|
-
|
46
|
+
namespace: (namespace_set = false
|
47
|
+
nil),
|
48
|
+
prefix: nil,
|
49
|
+
mixed: false
|
41
50
|
)
|
42
51
|
@elements << XmlMappingRule.new(
|
43
52
|
name,
|
@@ -47,6 +56,8 @@ module Lutaml
|
|
47
56
|
delegate: delegate,
|
48
57
|
namespace: namespace,
|
49
58
|
prefix: prefix,
|
59
|
+
mixed_content: mixed,
|
60
|
+
namespace_set: namespace_set != false,
|
50
61
|
)
|
51
62
|
end
|
52
63
|
|
@@ -56,7 +67,8 @@ module Lutaml
|
|
56
67
|
render_nil: false,
|
57
68
|
with: {},
|
58
69
|
delegate: nil,
|
59
|
-
namespace:
|
70
|
+
namespace: (namespace_set = false
|
71
|
+
nil),
|
60
72
|
prefix: nil
|
61
73
|
)
|
62
74
|
@attributes << XmlMappingRule.new(
|
@@ -67,17 +79,25 @@ module Lutaml
|
|
67
79
|
delegate: delegate,
|
68
80
|
namespace: namespace,
|
69
81
|
prefix: prefix,
|
82
|
+
namespace_set: namespace_set != false,
|
70
83
|
)
|
71
84
|
end
|
72
85
|
# rubocop:enable Metrics/ParameterLists
|
73
86
|
|
74
|
-
def map_content(
|
87
|
+
def map_content(
|
88
|
+
to:,
|
89
|
+
render_nil: false,
|
90
|
+
with: {},
|
91
|
+
delegate: nil,
|
92
|
+
mixed: false
|
93
|
+
)
|
75
94
|
@content_mapping = XmlMappingRule.new(
|
76
95
|
nil,
|
77
96
|
to: to,
|
78
97
|
render_nil: render_nil,
|
79
98
|
with: with,
|
80
99
|
delegate: delegate,
|
100
|
+
mixed_content: mixed,
|
81
101
|
)
|
82
102
|
end
|
83
103
|
|
@@ -108,6 +128,16 @@ module Lutaml
|
|
108
128
|
name == rule.to
|
109
129
|
end
|
110
130
|
end
|
131
|
+
|
132
|
+
def find_by_name(name)
|
133
|
+
if name.to_s == "text"
|
134
|
+
content_mapping
|
135
|
+
else
|
136
|
+
mappings.detect do |rule|
|
137
|
+
rule.name == name.to_s || rule.name == name.to_sym
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
111
141
|
end
|
112
142
|
end
|
113
143
|
end
|
@@ -6,8 +6,6 @@ module Lutaml
|
|
6
6
|
class XmlMappingRule < MappingRule
|
7
7
|
attr_reader :namespace, :prefix
|
8
8
|
|
9
|
-
# rubocop:disable Metrics/MethodLength
|
10
|
-
# rubocop:disable Metrics/ParameterLists
|
11
9
|
def initialize(
|
12
10
|
name,
|
13
11
|
to:,
|
@@ -15,14 +13,18 @@ module Lutaml
|
|
15
13
|
with: {},
|
16
14
|
delegate: nil,
|
17
15
|
namespace: nil,
|
18
|
-
prefix: nil
|
16
|
+
prefix: nil,
|
17
|
+
mixed_content: false,
|
18
|
+
namespace_set: false
|
19
19
|
)
|
20
20
|
super(
|
21
21
|
name,
|
22
22
|
to: to,
|
23
23
|
render_nil: render_nil,
|
24
24
|
with: with,
|
25
|
-
delegate: delegate
|
25
|
+
delegate: delegate,
|
26
|
+
mixed_content: mixed_content,
|
27
|
+
namespace_set: namespace_set
|
26
28
|
)
|
27
29
|
|
28
30
|
@namespace = if namespace.to_s == "inherit"
|
@@ -33,8 +35,6 @@ module Lutaml
|
|
33
35
|
end
|
34
36
|
@prefix = prefix
|
35
37
|
end
|
36
|
-
# rubocop:enable Metrics/MethodLength
|
37
|
-
# rubocop:enable Metrics/ParameterLists
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -4,9 +4,17 @@ require "yaml"
|
|
4
4
|
module Lutaml
|
5
5
|
module Model
|
6
6
|
module YamlAdapter
|
7
|
-
|
8
|
-
def
|
9
|
-
|
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)
|
10
18
|
end
|
11
19
|
|
12
20
|
def self.from_yaml(yaml, klass)
|
data/lib/lutaml/model.rb
CHANGED
@@ -12,9 +12,16 @@ require_relative "model/json_adapter"
|
|
12
12
|
require_relative "model/yaml_adapter"
|
13
13
|
require_relative "model/xml_adapter"
|
14
14
|
require_relative "model/toml_adapter"
|
15
|
+
require_relative "model/error"
|
15
16
|
|
16
17
|
module Lutaml
|
17
18
|
module Model
|
19
|
+
# Error for passing incorrect model type
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
class IncorrectModelError < StandardError
|
23
|
+
end
|
24
|
+
|
18
25
|
class BaseModel < Serializable
|
19
26
|
end
|
20
27
|
end
|
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.
|
4
|
+
version: 0.3.1
|
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-
|
11
|
+
date: 2024-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: 'LutaML creating data models in Ruby
|
14
14
|
|
@@ -24,6 +24,7 @@ files:
|
|
24
24
|
- ".gitignore"
|
25
25
|
- ".rspec"
|
26
26
|
- ".rubocop.yml"
|
27
|
+
- ".rubocop_todo.yml"
|
27
28
|
- CODE_OF_CONDUCT.md
|
28
29
|
- Gemfile
|
29
30
|
- README.adoc
|
@@ -33,11 +34,14 @@ files:
|
|
33
34
|
- lib/lutaml/model.rb
|
34
35
|
- lib/lutaml/model/attribute.rb
|
35
36
|
- lib/lutaml/model/config.rb
|
37
|
+
- lib/lutaml/model/error.rb
|
38
|
+
- lib/lutaml/model/error/invalid_value_error.rb
|
36
39
|
- lib/lutaml/model/json_adapter.rb
|
37
40
|
- lib/lutaml/model/json_adapter/multi_json.rb
|
38
41
|
- lib/lutaml/model/json_adapter/standard.rb
|
39
42
|
- lib/lutaml/model/key_value_mapping.rb
|
40
43
|
- lib/lutaml/model/key_value_mapping_rule.rb
|
44
|
+
- lib/lutaml/model/mapping_hash.rb
|
41
45
|
- lib/lutaml/model/mapping_rule.rb
|
42
46
|
- lib/lutaml/model/schema.rb
|
43
47
|
- lib/lutaml/model/schema/json_schema.rb
|