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.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.rubocop_todo.yml +49 -48
  4. data/Gemfile +4 -1
  5. data/README.adoc +791 -143
  6. data/RELEASE_NOTES.adoc +346 -0
  7. data/docs/custom_adapters.adoc +144 -0
  8. data/lib/lutaml/model/attribute.rb +17 -11
  9. data/lib/lutaml/model/config.rb +48 -42
  10. data/lib/lutaml/model/error/polymorphic_error.rb +7 -2
  11. data/lib/lutaml/model/format_registry.rb +41 -0
  12. data/lib/lutaml/model/hash/document.rb +11 -0
  13. data/lib/lutaml/model/hash/mapping.rb +19 -0
  14. data/lib/lutaml/model/hash/mapping_rule.rb +9 -0
  15. data/lib/lutaml/model/hash/standard_adapter.rb +17 -0
  16. data/lib/lutaml/model/hash/transform.rb +8 -0
  17. data/lib/lutaml/model/hash.rb +21 -0
  18. data/lib/lutaml/model/json/document.rb +11 -0
  19. data/lib/lutaml/model/json/mapping.rb +19 -0
  20. data/lib/lutaml/model/json/mapping_rule.rb +9 -0
  21. data/lib/lutaml/model/{json_adapter → json}/multi_json_adapter.rb +4 -5
  22. data/lib/lutaml/model/{json_adapter/standard_json_adapter.rb → json/standard_adapter.rb} +5 -3
  23. data/lib/lutaml/model/json/transform.rb +8 -0
  24. data/lib/lutaml/model/json.rb +21 -0
  25. data/lib/lutaml/model/key_value_document.rb +27 -0
  26. data/lib/lutaml/model/mapping/key_value_mapping.rb +8 -4
  27. data/lib/lutaml/model/mapping/mapping.rb +13 -0
  28. data/lib/lutaml/model/mapping/mapping_rule.rb +7 -6
  29. data/lib/lutaml/model/serialization_adapter.rb +22 -0
  30. data/lib/lutaml/model/serialize.rb +146 -521
  31. data/lib/lutaml/model/services/logger.rb +54 -0
  32. data/lib/lutaml/model/services/transformer.rb +48 -0
  33. data/lib/lutaml/model/services.rb +2 -0
  34. data/lib/lutaml/model/toml/document.rb +11 -0
  35. data/lib/lutaml/model/toml/mapping.rb +27 -0
  36. data/lib/lutaml/model/toml/mapping_rule.rb +9 -0
  37. data/lib/lutaml/model/{toml_adapter → toml}/toml_rb_adapter.rb +3 -3
  38. data/lib/lutaml/model/toml/tomlib_adapter.rb +19 -0
  39. data/lib/lutaml/model/toml/transform.rb +8 -0
  40. data/lib/lutaml/model/toml.rb +30 -0
  41. data/lib/lutaml/model/transform/key_value_transform.rb +291 -0
  42. data/lib/lutaml/model/transform/xml_transform.rb +239 -0
  43. data/lib/lutaml/model/transform.rb +78 -0
  44. data/lib/lutaml/model/type/value.rb +6 -9
  45. data/lib/lutaml/model/uninitialized_class.rb +1 -1
  46. data/lib/lutaml/model/utils.rb +30 -0
  47. data/lib/lutaml/model/version.rb +1 -1
  48. data/lib/lutaml/model/{xml_adapter → xml}/builder/nokogiri.rb +2 -2
  49. data/lib/lutaml/model/{xml_adapter → xml}/builder/oga.rb +10 -10
  50. data/lib/lutaml/model/{xml_adapter → xml}/builder/ox.rb +1 -1
  51. data/lib/lutaml/model/{xml_adapter/xml_document.rb → xml/document.rb} +6 -7
  52. data/lib/lutaml/model/xml/element.rb +32 -0
  53. data/lib/lutaml/model/xml/mapping.rb +410 -0
  54. data/lib/lutaml/model/xml/mapping_rule.rb +141 -0
  55. data/lib/lutaml/model/xml/nokogiri_adapter.rb +232 -0
  56. data/lib/lutaml/model/{xml_adapter → xml}/oga/document.rb +1 -1
  57. data/lib/lutaml/model/{xml_adapter → xml}/oga/element.rb +3 -1
  58. data/lib/lutaml/model/xml/oga_adapter.rb +171 -0
  59. data/lib/lutaml/model/xml/ox_adapter.rb +215 -0
  60. data/lib/lutaml/model/xml/transform.rb +8 -0
  61. data/lib/lutaml/model/{xml_adapter → xml}/xml_attribute.rb +1 -1
  62. data/lib/lutaml/model/{xml_adapter → xml}/xml_element.rb +6 -3
  63. data/lib/lutaml/model/{xml_adapter → xml}/xml_namespace.rb +1 -1
  64. data/lib/lutaml/model/xml.rb +31 -0
  65. data/lib/lutaml/model/xml_adapter/element.rb +11 -25
  66. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +6 -223
  67. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +13 -163
  68. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +10 -207
  69. data/lib/lutaml/model/yaml/document.rb +10 -0
  70. data/lib/lutaml/model/yaml/mapping.rb +19 -0
  71. data/lib/lutaml/model/yaml/mapping_rule.rb +9 -0
  72. data/lib/lutaml/model/{yaml_adapter/standard_yaml_adapter.rb → yaml/standard_adapter.rb} +4 -3
  73. data/lib/lutaml/model/yaml/transform.rb +8 -0
  74. data/lib/lutaml/model/yaml.rb +21 -0
  75. data/lib/lutaml/model.rb +39 -4
  76. data/lutaml-model.gemspec +0 -4
  77. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +4 -4
  78. data/spec/lutaml/model/cdata_spec.rb +7 -7
  79. data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +598 -0
  80. data/spec/lutaml/model/custom_vobject_adapter_spec.rb +1226 -0
  81. data/spec/lutaml/model/group_spec.rb +18 -7
  82. data/spec/lutaml/model/hash/adapter_spec.rb +255 -0
  83. data/spec/lutaml/model/json_adapter_spec.rb +6 -6
  84. data/spec/lutaml/model/key_value_mapping_spec.rb +25 -1
  85. data/spec/lutaml/model/mixed_content_spec.rb +24 -24
  86. data/spec/lutaml/model/multiple_mapping_spec.rb +5 -5
  87. data/spec/lutaml/model/ordered_content_spec.rb +6 -6
  88. data/spec/lutaml/model/polymorphic_spec.rb +178 -0
  89. data/spec/lutaml/model/root_mappings_spec.rb +3 -3
  90. data/spec/lutaml/model/schema/xml_compiler_spec.rb +6 -6
  91. data/spec/lutaml/model/serializable_spec.rb +179 -103
  92. data/spec/lutaml/model/toml_adapter_spec.rb +6 -6
  93. data/spec/lutaml/model/toml_spec.rb +51 -0
  94. data/spec/lutaml/model/transformation_spec.rb +72 -15
  95. data/spec/lutaml/model/uninitialized_class_spec.rb +96 -0
  96. data/spec/lutaml/model/xml/namespace_spec.rb +57 -0
  97. data/spec/lutaml/model/xml/xml_element_spec.rb +1 -1
  98. data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +2 -2
  99. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +2 -2
  100. data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +2 -2
  101. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
  102. data/spec/lutaml/model/xml_adapter_spec.rb +6 -6
  103. data/spec/lutaml/model/xml_mapping_rule_spec.rb +3 -3
  104. data/spec/lutaml/model/xml_mapping_spec.rb +26 -14
  105. data/spec/lutaml/model/xml_spec.rb +63 -0
  106. data/spec/lutaml/model/yaml_adapter_spec.rb +3 -5
  107. data/spec/spec_helper.rb +3 -3
  108. metadata +64 -59
  109. data/lib/lutaml/model/json_adapter/json_document.rb +0 -20
  110. data/lib/lutaml/model/json_adapter/json_object.rb +0 -28
  111. data/lib/lutaml/model/loggable.rb +0 -15
  112. data/lib/lutaml/model/mapping/json_mapping.rb +0 -17
  113. data/lib/lutaml/model/mapping/toml_mapping.rb +0 -25
  114. data/lib/lutaml/model/mapping/xml_mapping.rb +0 -389
  115. data/lib/lutaml/model/mapping/xml_mapping_rule.rb +0 -139
  116. data/lib/lutaml/model/mapping/yaml_mapping.rb +0 -17
  117. data/lib/lutaml/model/mapping.rb +0 -14
  118. data/lib/lutaml/model/toml_adapter/toml_document.rb +0 -20
  119. data/lib/lutaml/model/toml_adapter/toml_object.rb +0 -28
  120. data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +0 -20
  121. data/lib/lutaml/model/toml_adapter.rb +0 -6
  122. data/lib/lutaml/model/yaml_adapter/yaml_document.rb +0 -20
  123. data/lib/lutaml/model/yaml_adapter/yaml_object.rb +0 -28
  124. 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
- # Default format-specific class methods that can be overridden
44
- ::Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
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
@@ -12,7 +12,7 @@ module Lutaml
12
12
  end
13
13
 
14
14
  def inspect
15
- "unititialized"
15
+ "uninitialized"
16
16
  end
17
17
 
18
18
  def uninitialized?
@@ -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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.7.1"
5
+ VERSION = "0.7.2"
6
6
  end
7
7
  end
@@ -1,6 +1,6 @@
1
1
  module Lutaml
2
2
  module Model
3
- module XmlAdapter
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 block_given?
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 XmlAdapter
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 = XmlAdapter::Oga::Document.new
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 block_given?
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?(XmlAdapter::Oga::Document) ? current_node : oga_element
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?(XmlAdapter::Oga::Document)
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?(XmlAdapter::Oga::Document)
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 block_given?
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?(XmlAdapter::Oga::Document)
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?(XmlAdapter::Oga::Document)
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?(XmlAdapter::Oga::Document)
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
@@ -1,6 +1,6 @@
1
1
  module Lutaml
2
2
  module Model
3
- module XmlAdapter
3
+ module Xml
4
4
  module Builder
5
5
  class Ox
6
6
  def self.build(options = {})
@@ -6,8 +6,8 @@ require_relative "element"
6
6
 
7
7
  module Lutaml
8
8
  module Model
9
- module XmlAdapter
10
- class XmlDocument
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 (!attribute.collection? || value.is_a?(Array)) && transform_method = rule.transform[:export] || attribute.transform_export_method
158
- value = transform_method.call(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