lutaml-model 0.7.1 → 0.7.2
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/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +49 -48
- data/Gemfile +4 -1
- data/README.adoc +791 -143
- data/RELEASE_NOTES.adoc +346 -0
- data/docs/custom_adapters.adoc +144 -0
- data/lib/lutaml/model/attribute.rb +17 -11
- data/lib/lutaml/model/config.rb +48 -42
- data/lib/lutaml/model/error/polymorphic_error.rb +7 -2
- data/lib/lutaml/model/format_registry.rb +41 -0
- data/lib/lutaml/model/hash/document.rb +11 -0
- data/lib/lutaml/model/hash/mapping.rb +19 -0
- data/lib/lutaml/model/hash/mapping_rule.rb +9 -0
- data/lib/lutaml/model/hash/standard_adapter.rb +17 -0
- data/lib/lutaml/model/hash/transform.rb +8 -0
- data/lib/lutaml/model/hash.rb +21 -0
- data/lib/lutaml/model/json/document.rb +11 -0
- data/lib/lutaml/model/json/mapping.rb +19 -0
- data/lib/lutaml/model/json/mapping_rule.rb +9 -0
- data/lib/lutaml/model/{json_adapter → json}/multi_json_adapter.rb +4 -5
- data/lib/lutaml/model/{json_adapter/standard_json_adapter.rb → json/standard_adapter.rb} +5 -3
- data/lib/lutaml/model/json/transform.rb +8 -0
- data/lib/lutaml/model/json.rb +21 -0
- data/lib/lutaml/model/key_value_document.rb +27 -0
- data/lib/lutaml/model/mapping/key_value_mapping.rb +8 -4
- data/lib/lutaml/model/mapping/mapping.rb +13 -0
- data/lib/lutaml/model/mapping/mapping_rule.rb +7 -6
- data/lib/lutaml/model/serialization_adapter.rb +22 -0
- data/lib/lutaml/model/serialize.rb +146 -521
- data/lib/lutaml/model/services/logger.rb +54 -0
- data/lib/lutaml/model/services/transformer.rb +48 -0
- data/lib/lutaml/model/services.rb +2 -0
- data/lib/lutaml/model/toml/document.rb +11 -0
- data/lib/lutaml/model/toml/mapping.rb +27 -0
- data/lib/lutaml/model/toml/mapping_rule.rb +9 -0
- data/lib/lutaml/model/{toml_adapter → toml}/toml_rb_adapter.rb +3 -3
- data/lib/lutaml/model/toml/tomlib_adapter.rb +19 -0
- data/lib/lutaml/model/toml/transform.rb +8 -0
- data/lib/lutaml/model/toml.rb +30 -0
- data/lib/lutaml/model/transform/key_value_transform.rb +291 -0
- data/lib/lutaml/model/transform/xml_transform.rb +239 -0
- data/lib/lutaml/model/transform.rb +78 -0
- data/lib/lutaml/model/type/value.rb +6 -9
- data/lib/lutaml/model/uninitialized_class.rb +1 -1
- data/lib/lutaml/model/utils.rb +30 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/{xml_adapter → xml}/builder/nokogiri.rb +2 -2
- data/lib/lutaml/model/{xml_adapter → xml}/builder/oga.rb +10 -10
- data/lib/lutaml/model/{xml_adapter → xml}/builder/ox.rb +1 -1
- data/lib/lutaml/model/{xml_adapter/xml_document.rb → xml/document.rb} +6 -7
- data/lib/lutaml/model/xml/element.rb +32 -0
- data/lib/lutaml/model/xml/mapping.rb +410 -0
- data/lib/lutaml/model/xml/mapping_rule.rb +141 -0
- data/lib/lutaml/model/xml/nokogiri_adapter.rb +232 -0
- data/lib/lutaml/model/{xml_adapter → xml}/oga/document.rb +1 -1
- data/lib/lutaml/model/{xml_adapter → xml}/oga/element.rb +3 -1
- data/lib/lutaml/model/xml/oga_adapter.rb +171 -0
- data/lib/lutaml/model/xml/ox_adapter.rb +215 -0
- data/lib/lutaml/model/xml/transform.rb +8 -0
- data/lib/lutaml/model/{xml_adapter → xml}/xml_attribute.rb +1 -1
- data/lib/lutaml/model/{xml_adapter → xml}/xml_element.rb +6 -3
- data/lib/lutaml/model/{xml_adapter → xml}/xml_namespace.rb +1 -1
- data/lib/lutaml/model/xml.rb +31 -0
- data/lib/lutaml/model/xml_adapter/element.rb +11 -25
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +6 -223
- data/lib/lutaml/model/xml_adapter/oga_adapter.rb +13 -163
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +10 -207
- data/lib/lutaml/model/yaml/document.rb +10 -0
- data/lib/lutaml/model/yaml/mapping.rb +19 -0
- data/lib/lutaml/model/yaml/mapping_rule.rb +9 -0
- data/lib/lutaml/model/{yaml_adapter/standard_yaml_adapter.rb → yaml/standard_adapter.rb} +4 -3
- data/lib/lutaml/model/yaml/transform.rb +8 -0
- data/lib/lutaml/model/yaml.rb +21 -0
- data/lib/lutaml/model.rb +39 -4
- data/lutaml-model.gemspec +0 -4
- data/spec/benchmarks/xml_parsing_benchmark_spec.rb +4 -4
- data/spec/lutaml/model/cdata_spec.rb +7 -7
- data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +598 -0
- data/spec/lutaml/model/custom_vobject_adapter_spec.rb +1226 -0
- data/spec/lutaml/model/group_spec.rb +18 -7
- data/spec/lutaml/model/hash/adapter_spec.rb +255 -0
- data/spec/lutaml/model/json_adapter_spec.rb +6 -6
- data/spec/lutaml/model/key_value_mapping_spec.rb +25 -1
- data/spec/lutaml/model/mixed_content_spec.rb +24 -24
- data/spec/lutaml/model/multiple_mapping_spec.rb +5 -5
- data/spec/lutaml/model/ordered_content_spec.rb +6 -6
- data/spec/lutaml/model/polymorphic_spec.rb +178 -0
- data/spec/lutaml/model/root_mappings_spec.rb +3 -3
- data/spec/lutaml/model/schema/xml_compiler_spec.rb +6 -6
- data/spec/lutaml/model/serializable_spec.rb +179 -103
- data/spec/lutaml/model/toml_adapter_spec.rb +6 -6
- data/spec/lutaml/model/toml_spec.rb +51 -0
- data/spec/lutaml/model/transformation_spec.rb +72 -15
- data/spec/lutaml/model/uninitialized_class_spec.rb +96 -0
- data/spec/lutaml/model/xml/namespace_spec.rb +57 -0
- data/spec/lutaml/model/xml/xml_element_spec.rb +1 -1
- data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
- data/spec/lutaml/model/xml_adapter_spec.rb +6 -6
- data/spec/lutaml/model/xml_mapping_rule_spec.rb +3 -3
- data/spec/lutaml/model/xml_mapping_spec.rb +26 -14
- data/spec/lutaml/model/xml_spec.rb +63 -0
- data/spec/lutaml/model/yaml_adapter_spec.rb +3 -5
- data/spec/spec_helper.rb +3 -3
- metadata +64 -59
- data/lib/lutaml/model/json_adapter/json_document.rb +0 -20
- data/lib/lutaml/model/json_adapter/json_object.rb +0 -28
- data/lib/lutaml/model/loggable.rb +0 -15
- data/lib/lutaml/model/mapping/json_mapping.rb +0 -17
- data/lib/lutaml/model/mapping/toml_mapping.rb +0 -25
- data/lib/lutaml/model/mapping/xml_mapping.rb +0 -389
- data/lib/lutaml/model/mapping/xml_mapping_rule.rb +0 -139
- data/lib/lutaml/model/mapping/yaml_mapping.rb +0 -17
- data/lib/lutaml/model/mapping.rb +0 -14
- data/lib/lutaml/model/toml_adapter/toml_document.rb +0 -20
- data/lib/lutaml/model/toml_adapter/toml_object.rb +0 -28
- data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +0 -20
- data/lib/lutaml/model/toml_adapter.rb +0 -6
- data/lib/lutaml/model/yaml_adapter/yaml_document.rb +0 -20
- data/lib/lutaml/model/yaml_adapter/yaml_object.rb +0 -28
- data/lib/lutaml/model/yaml_adapter.rb +0 -8
@@ -0,0 +1,239 @@
|
|
1
|
+
module Lutaml
|
2
|
+
module Model
|
3
|
+
class XmlTransform < Lutaml::Model::Transform
|
4
|
+
def data_to_model(data, _format, options = {})
|
5
|
+
instance = model_class.new
|
6
|
+
apply_xml_mapping(data, instance, options)
|
7
|
+
end
|
8
|
+
|
9
|
+
# TODO: this should be extracted from adapters and moved here to be reused
|
10
|
+
def model_to_data(model, _format, _options = {})
|
11
|
+
model
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def apply_xml_mapping(doc, instance, options = {})
|
17
|
+
options = prepare_options(options)
|
18
|
+
instance.encoding = options[:encoding]
|
19
|
+
return instance unless doc
|
20
|
+
|
21
|
+
mappings = options[:mappings] || mappings_for(:xml).mappings
|
22
|
+
|
23
|
+
validate_document!(doc, options)
|
24
|
+
|
25
|
+
set_instance_ordering(instance, doc, options)
|
26
|
+
set_schema_location(instance, doc)
|
27
|
+
|
28
|
+
defaults_used = []
|
29
|
+
validate_sequence!(doc.root.order)
|
30
|
+
|
31
|
+
mappings.each do |rule|
|
32
|
+
raise "Attribute '#{rule.to}' not found in #{context}" unless valid_rule?(rule)
|
33
|
+
|
34
|
+
attr = attribute_for_rule(rule)
|
35
|
+
next if attr&.derived?
|
36
|
+
|
37
|
+
new_opts = options.dup
|
38
|
+
if rule.namespace_set?
|
39
|
+
new_opts[:default_namespace] = rule.namespace
|
40
|
+
end
|
41
|
+
|
42
|
+
value = if rule.raw_mapping?
|
43
|
+
doc.root.inner_xml
|
44
|
+
elsif rule.content_mapping?
|
45
|
+
rule.cdata ? doc.cdata : doc.text
|
46
|
+
else
|
47
|
+
val = value_for_rule(doc, rule, new_opts, instance)
|
48
|
+
|
49
|
+
if (Utils.uninitialized?(val) || val.nil?) && (instance.using_default?(rule.to) || rule.render_default)
|
50
|
+
defaults_used << rule.to
|
51
|
+
attr&.default || rule.to_value_for(instance)
|
52
|
+
else
|
53
|
+
val
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
value = apply_value_map(value, rule.value_map(:from, new_opts), attr)
|
58
|
+
value = normalize_xml_value(value, rule, attr, new_opts)
|
59
|
+
rule.deserialize(instance, value, attributes, context)
|
60
|
+
end
|
61
|
+
|
62
|
+
defaults_used.each do |attr_name|
|
63
|
+
instance.using_default_for(attr_name)
|
64
|
+
end
|
65
|
+
|
66
|
+
instance
|
67
|
+
end
|
68
|
+
|
69
|
+
def prepare_options(options)
|
70
|
+
opts = Utils.deep_dup(options)
|
71
|
+
opts[:default_namespace] ||= mappings_for(:xml)&.namespace_uri
|
72
|
+
|
73
|
+
opts
|
74
|
+
end
|
75
|
+
|
76
|
+
def validate_document!(doc, options)
|
77
|
+
return unless doc.is_a?(Array)
|
78
|
+
|
79
|
+
raise Lutaml::Model::CollectionTrueMissingError(
|
80
|
+
context,
|
81
|
+
options[:caller_class],
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
def set_instance_ordering(instance, doc, options)
|
86
|
+
return unless instance.respond_to?(:ordered=)
|
87
|
+
|
88
|
+
instance.element_order = doc.root.order
|
89
|
+
instance.ordered = mappings_for(:xml).ordered? || options[:ordered]
|
90
|
+
instance.mixed = mappings_for(:xml).mixed_content? || options[:mixed_content]
|
91
|
+
end
|
92
|
+
|
93
|
+
def set_schema_location(instance, doc)
|
94
|
+
schema_location = doc.attributes.values.find do |a|
|
95
|
+
a.unprefixed_name == "schemaLocation"
|
96
|
+
end
|
97
|
+
|
98
|
+
return if schema_location.nil?
|
99
|
+
|
100
|
+
instance.schema_location = Lutaml::Model::SchemaLocation.new(
|
101
|
+
schema_location: schema_location.value,
|
102
|
+
prefix: schema_location.namespace_prefix,
|
103
|
+
namespace: schema_location.namespace,
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
def value_for_rule(doc, rule, options, instance)
|
108
|
+
rule_names = rule.namespaced_names(options[:default_namespace])
|
109
|
+
|
110
|
+
if rule.attribute?
|
111
|
+
doc.root.find_attribute_value(rule_names)
|
112
|
+
else
|
113
|
+
attr = attribute_for_rule(rule)
|
114
|
+
children = doc.children.select do |child|
|
115
|
+
rule_names.include?(child.namespaced_name) && !child.text?
|
116
|
+
end
|
117
|
+
|
118
|
+
if rule.has_custom_method_for_deserialization? || attr.type == Lutaml::Model::Type::Hash
|
119
|
+
return_child = attr.type == Lutaml::Model::Type::Hash || !attr.collection? if attr
|
120
|
+
return return_child ? children.first : children
|
121
|
+
end
|
122
|
+
|
123
|
+
return handle_cdata(children) if rule.cdata
|
124
|
+
|
125
|
+
values = []
|
126
|
+
|
127
|
+
if Utils.present?(children)
|
128
|
+
instance.value_set_for(attr.name)
|
129
|
+
else
|
130
|
+
children = nil
|
131
|
+
values = Lutaml::Model::UninitializedClass.instance
|
132
|
+
end
|
133
|
+
|
134
|
+
children&.each do |child|
|
135
|
+
if !rule.has_custom_method_for_deserialization? && attr.type <= Serialize
|
136
|
+
cast_options = options.except(:mappings)
|
137
|
+
cast_options[:polymorphic] = rule.polymorphic if rule.polymorphic
|
138
|
+
|
139
|
+
values << attr.cast(child, :xml, cast_options)
|
140
|
+
elsif attr.raw?
|
141
|
+
values << inner_xml_of(child)
|
142
|
+
else
|
143
|
+
return nil if rule.render_nil_as_nil? && child.nil_element?
|
144
|
+
|
145
|
+
text = child.nil_element? ? nil : (child&.text&.+ child&.cdata)
|
146
|
+
values << text
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
normalized_value_for_attr(values, attr)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def handle_cdata(children)
|
155
|
+
values = children.map do |child|
|
156
|
+
child.cdata_children&.map(&:text)
|
157
|
+
end.flatten
|
158
|
+
|
159
|
+
children.count > 1 ? values : values.first
|
160
|
+
end
|
161
|
+
|
162
|
+
def normalized_value_for_attr(values, attr)
|
163
|
+
# for xml collection: true cases like
|
164
|
+
# <store><items /></store>
|
165
|
+
# <store><items xsi:nil="true"/></store>
|
166
|
+
# <store><items></items></store>
|
167
|
+
#
|
168
|
+
# these are considered empty collection
|
169
|
+
return [] if attr&.collection? && [[nil], [""]].include?(values)
|
170
|
+
return values if attr&.collection?
|
171
|
+
|
172
|
+
values.is_a?(Array) ? values.first : values
|
173
|
+
end
|
174
|
+
|
175
|
+
def normalize_xml_value(value, rule, attr, options = {})
|
176
|
+
value = [value].compact if attr&.collection? && !value.is_a?(Array) && !value.nil?
|
177
|
+
|
178
|
+
return value unless cast_value?(attr, rule)
|
179
|
+
|
180
|
+
options.merge(caller_class: self, mixed_content: rule.mixed_content)
|
181
|
+
attr.cast(
|
182
|
+
value,
|
183
|
+
:xml,
|
184
|
+
options,
|
185
|
+
)
|
186
|
+
end
|
187
|
+
|
188
|
+
def cast_value?(attr, rule)
|
189
|
+
attr &&
|
190
|
+
!rule.raw_mapping? &&
|
191
|
+
!rule.content_mapping? &&
|
192
|
+
!rule.custom_methods[:from]
|
193
|
+
end
|
194
|
+
|
195
|
+
def text_hash?(attr, value)
|
196
|
+
return false unless value.is_a?(Hash)
|
197
|
+
return value.one? && value.text? unless attr
|
198
|
+
|
199
|
+
!(attr.type <= Serialize) && attr.type != Lutaml::Model::Type::Hash
|
200
|
+
end
|
201
|
+
|
202
|
+
def ensure_utf8(value)
|
203
|
+
case value
|
204
|
+
when String
|
205
|
+
value.encode("UTF-8", invalid: :replace, undef: :replace,
|
206
|
+
replace: "")
|
207
|
+
when Array
|
208
|
+
value.map { |v| ensure_utf8(v) }
|
209
|
+
when Hash
|
210
|
+
value.transform_keys do |k|
|
211
|
+
ensure_utf8(k)
|
212
|
+
end.transform_values do |v|
|
213
|
+
ensure_utf8(v)
|
214
|
+
end
|
215
|
+
else
|
216
|
+
value
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def inner_xml_of(node)
|
221
|
+
case node
|
222
|
+
when Xml::XmlElement
|
223
|
+
node.inner_xml
|
224
|
+
else
|
225
|
+
node.children.map(&:to_xml).join
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def validate_sequence!(element_order)
|
230
|
+
mapping_sequence = mappings_for(:xml).element_sequence
|
231
|
+
current_order = element_order.filter_map(&:element_tag)
|
232
|
+
|
233
|
+
mapping_sequence.each do |mapping|
|
234
|
+
mapping.validate_content!(current_order)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Lutaml
|
2
|
+
module Model
|
3
|
+
class Transform
|
4
|
+
def self.data_to_model(context, data, format, options = {})
|
5
|
+
new(context).data_to_model(data, format, options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.model_to_data(context, model, format, options = {})
|
9
|
+
new(context).model_to_data(model, format, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :context, :attributes
|
13
|
+
|
14
|
+
def initialize(context)
|
15
|
+
@context = context
|
16
|
+
@attributes = context.attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
def model_class
|
20
|
+
@context.model
|
21
|
+
end
|
22
|
+
|
23
|
+
def data_to_model(data, options = {})
|
24
|
+
raise NotImplementedError, "#{self.class.name} must implement `data_to_model`."
|
25
|
+
end
|
26
|
+
|
27
|
+
def model_to_data(model, options = {})
|
28
|
+
raise NotImplementedError, "#{self.class.name} must implement `model_to_data`."
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def apply_value_map(value, value_map, attr)
|
34
|
+
if value.nil?
|
35
|
+
value_for_option(value_map[:nil], attr)
|
36
|
+
elsif Utils.empty?(value)
|
37
|
+
value_for_option(value_map[:empty], attr, value)
|
38
|
+
elsif Utils.uninitialized?(value)
|
39
|
+
value_for_option(value_map[:omitted], attr)
|
40
|
+
else
|
41
|
+
value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def value_for_option(option, attr, empty_value = nil)
|
46
|
+
return nil if option == :nil
|
47
|
+
return empty_value || empty_object(attr) if option == :empty
|
48
|
+
|
49
|
+
Lutaml::Model::UninitializedClass.instance
|
50
|
+
end
|
51
|
+
|
52
|
+
def empty_object(attr)
|
53
|
+
return [] if attr.collection?
|
54
|
+
|
55
|
+
""
|
56
|
+
end
|
57
|
+
|
58
|
+
def mappings_for(format)
|
59
|
+
context.mappings_for(format)
|
60
|
+
end
|
61
|
+
|
62
|
+
def valid_rule?(rule)
|
63
|
+
attribute = attribute_for_rule(rule)
|
64
|
+
|
65
|
+
!!attribute || rule.custom_methods[:from]
|
66
|
+
end
|
67
|
+
|
68
|
+
def attribute_for_rule(rule)
|
69
|
+
return attributes[rule.to] unless rule.delegate
|
70
|
+
|
71
|
+
attributes[rule.delegate].type.attributes[rule.to]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
require_relative "transform/key_value_transform"
|
78
|
+
require_relative "transform/xml_transform"
|
@@ -28,20 +28,17 @@ module Lutaml
|
|
28
28
|
value.to_s
|
29
29
|
end
|
30
30
|
|
31
|
-
# Format-specific instance methods
|
32
|
-
::Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
|
33
|
-
define_method(:"to_#{format}") do
|
34
|
-
value
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
31
|
# Class-level format conversion
|
39
32
|
def self.from_format(value, format)
|
40
33
|
new(send(:"from_#{format}", value))
|
41
34
|
end
|
42
35
|
|
43
|
-
#
|
44
|
-
|
36
|
+
# called from config when a new format is added
|
37
|
+
def self.register_format_to_from_methods(format)
|
38
|
+
define_method(:"to_#{format}") do
|
39
|
+
value
|
40
|
+
end
|
41
|
+
|
45
42
|
define_singleton_method(:"from_#{format}") do |value|
|
46
43
|
cast(value)
|
47
44
|
end
|
data/lib/lutaml/model/utils.rb
CHANGED
@@ -63,6 +63,36 @@ module Lutaml
|
|
63
63
|
value.respond_to?(:empty?) ? value.empty? : false
|
64
64
|
end
|
65
65
|
|
66
|
+
# Check if the hash contains the given key in string or symbol format
|
67
|
+
# @param hash [Hash] the hash to check
|
68
|
+
# @param key [String, Symbol] the key to check
|
69
|
+
# @return [Boolean] true if the hash contains the key, false otherwise
|
70
|
+
# @example
|
71
|
+
# hash = { "key" => "value" }
|
72
|
+
# string_or_symbol_key?(hash, "key") # => true
|
73
|
+
# string_or_symbol_key?(hash, :key) # => true
|
74
|
+
# string_or_symbol_key?(hash, "invalid_key") # => false
|
75
|
+
def string_or_symbol_key?(hash, key)
|
76
|
+
hash.key?(key.to_s) || hash.key?(key.to_sym)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Fetch the value from the hash using the key in string or symbol format
|
80
|
+
# @param hash [Hash] the hash to fetch the value from
|
81
|
+
# @param key [String, Symbol] the key to fetch the value for
|
82
|
+
# @return [Object] the value associated with the key
|
83
|
+
# @example
|
84
|
+
# hash = { "key" => "value" }
|
85
|
+
# fetch_with_string_or_symbol_key(hash, "key") # => "value"
|
86
|
+
# fetch_with_string_or_symbol_key(hash, :key) # => "value"
|
87
|
+
# fetch_with_string_or_symbol_key(hash, "invalid_key") # => nil
|
88
|
+
def fetch_with_string_or_symbol_key(hash, key)
|
89
|
+
if hash.key?(key.to_s)
|
90
|
+
hash[key.to_s]
|
91
|
+
elsif hash.key?(key.to_sym)
|
92
|
+
hash[key.to_sym]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
66
96
|
def add_method_if_not_defined(klass, method_name, &block)
|
67
97
|
unless klass.method_defined?(method_name)
|
68
98
|
klass.class_eval do
|
data/lib/lutaml/model/version.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Lutaml
|
2
2
|
module Model
|
3
|
-
module
|
3
|
+
module Xml
|
4
4
|
module Builder
|
5
5
|
class Nokogiri
|
6
6
|
def self.build(options = {})
|
@@ -84,7 +84,7 @@ module Lutaml
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def method_missing(method_name, *args, &block)
|
87
|
-
if
|
87
|
+
if block
|
88
88
|
xml.public_send(method_name, *args, &block)
|
89
89
|
else
|
90
90
|
xml.public_send(method_name, *args)
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Lutaml
|
4
4
|
module Model
|
5
|
-
module
|
5
|
+
module Xml
|
6
6
|
module Builder
|
7
7
|
class Oga
|
8
8
|
def self.build(options = {}, &block)
|
@@ -12,7 +12,7 @@ module Lutaml
|
|
12
12
|
attr_reader :document, :current_node, :encoding
|
13
13
|
|
14
14
|
def initialize(options = {})
|
15
|
-
@document =
|
15
|
+
@document = Xml::Oga::Document.new
|
16
16
|
@current_node = @document
|
17
17
|
@encoding = options[:encoding]
|
18
18
|
yield(self) if block_given?
|
@@ -23,7 +23,7 @@ module Lutaml
|
|
23
23
|
name = "#{@current_namespace}:#{name}"
|
24
24
|
end
|
25
25
|
|
26
|
-
if
|
26
|
+
if block
|
27
27
|
element(name, attributes, &block)
|
28
28
|
else
|
29
29
|
element(name, attributes)
|
@@ -48,9 +48,9 @@ module Lutaml
|
|
48
48
|
|
49
49
|
def add_element(oga_element, child)
|
50
50
|
if child.is_a?(String)
|
51
|
-
current_element = oga_element.is_a?(
|
51
|
+
current_element = oga_element.is_a?(Xml::Oga::Document) ? current_node : oga_element
|
52
52
|
add_xml_fragment(current_element, child)
|
53
|
-
elsif oga_element.is_a?(
|
53
|
+
elsif oga_element.is_a?(Xml::Oga::Document)
|
54
54
|
oga_element.children.last.children << child
|
55
55
|
else
|
56
56
|
oga_element.children << child
|
@@ -62,7 +62,7 @@ module Lutaml
|
|
62
62
|
name: name,
|
63
63
|
value: value.to_s,
|
64
64
|
)
|
65
|
-
if element.is_a?(
|
65
|
+
if element.is_a?(Xml::Oga::Document)
|
66
66
|
element.children.last.attributes << attribute
|
67
67
|
else
|
68
68
|
element.attributes << attribute
|
@@ -85,7 +85,7 @@ module Lutaml
|
|
85
85
|
element_name
|
86
86
|
end
|
87
87
|
|
88
|
-
if
|
88
|
+
if block
|
89
89
|
element(prefixed_name, attributes, &block)
|
90
90
|
else
|
91
91
|
element(prefixed_name, attributes)
|
@@ -100,7 +100,7 @@ module Lutaml
|
|
100
100
|
fragment = "<fragment>#{content}</fragment>"
|
101
101
|
parsed_fragment = ::Oga.parse_xml(fragment)
|
102
102
|
parsed_children = parsed_fragment.children.first.children
|
103
|
-
if element.is_a?(
|
103
|
+
if element.is_a?(Xml::Oga::Document)
|
104
104
|
element.children.last.children += parsed_children
|
105
105
|
else
|
106
106
|
element.children += parsed_children
|
@@ -116,7 +116,7 @@ module Lutaml
|
|
116
116
|
end
|
117
117
|
|
118
118
|
def append_text_node(element, oga_text)
|
119
|
-
if element.is_a?(
|
119
|
+
if element.is_a?(Xml::Oga::Document)
|
120
120
|
children = element.children
|
121
121
|
children.empty? ? children << oga_text : children.last.children << oga_text
|
122
122
|
else
|
@@ -126,7 +126,7 @@ module Lutaml
|
|
126
126
|
|
127
127
|
def add_cdata(element, value)
|
128
128
|
oga_cdata = ::Oga::XML::CData.new(text: value.to_s)
|
129
|
-
if element.is_a?(
|
129
|
+
if element.is_a?(Xml::Oga::Document)
|
130
130
|
element.children.last.children << oga_cdata
|
131
131
|
else
|
132
132
|
element.children << oga_cdata
|
@@ -6,8 +6,8 @@ require_relative "element"
|
|
6
6
|
|
7
7
|
module Lutaml
|
8
8
|
module Model
|
9
|
-
module
|
10
|
-
class
|
9
|
+
module Xml
|
10
|
+
class Document
|
11
11
|
attr_reader :root, :encoding
|
12
12
|
|
13
13
|
def initialize(root, encoding = nil)
|
@@ -154,8 +154,8 @@ module Lutaml
|
|
154
154
|
end
|
155
155
|
|
156
156
|
# Only transform when recursion is not called
|
157
|
-
if
|
158
|
-
value =
|
157
|
+
if !attribute.collection? || value.is_a?(Array)
|
158
|
+
value = ExportTransformer.call(value, rule, attribute)
|
159
159
|
end
|
160
160
|
|
161
161
|
if value.is_a?(Array) && !Utils.empty_collection?(value)
|
@@ -233,7 +233,6 @@ module Lutaml
|
|
233
233
|
end
|
234
234
|
|
235
235
|
mappings = xml_mapping.elements + [xml_mapping.raw_mapping].compact
|
236
|
-
|
237
236
|
mappings.each do |element_rule|
|
238
237
|
attribute_def = attribute_definition_for(element, element_rule,
|
239
238
|
mapper_class: mapper_class)
|
@@ -271,8 +270,6 @@ module Lutaml
|
|
271
270
|
text = content_rule.serialize(element)
|
272
271
|
text = text.join if text.is_a?(Array)
|
273
272
|
|
274
|
-
return unless content_rule.render?(text, element)
|
275
|
-
|
276
273
|
xml.add_text(xml, text, cdata: content_rule.cdata)
|
277
274
|
end
|
278
275
|
end
|
@@ -368,6 +365,8 @@ module Lutaml
|
|
368
365
|
attr = attribute_definition_for(element, mapping_rule, mapper_class: options[:mapper_class])
|
369
366
|
value = attr.serialize(value, :xml) if attr
|
370
367
|
|
368
|
+
value = ExportTransformer.call(value, mapping_rule, attr)
|
369
|
+
|
371
370
|
if render_element?(mapping_rule, element, value)
|
372
371
|
hash[mapping_rule.prefixed_name] = value ? value.to_s : value
|
373
372
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Lutaml
|
2
|
+
module Model
|
3
|
+
module Xml
|
4
|
+
class Element
|
5
|
+
attr_reader :type, :name
|
6
|
+
|
7
|
+
def initialize(type, name)
|
8
|
+
@type = type
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
def text?
|
13
|
+
@type == "Text" && @name != "#cdata-section"
|
14
|
+
end
|
15
|
+
|
16
|
+
def element_tag
|
17
|
+
@name unless text?
|
18
|
+
end
|
19
|
+
|
20
|
+
def eql?(other)
|
21
|
+
return false unless other.is_a?(self.class)
|
22
|
+
|
23
|
+
instance_variables.all? do |var|
|
24
|
+
instance_variable_get(var) == other.instance_variable_get(var)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
alias == eql?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|