lutaml-model 0.3.18 → 0.3.20

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fba4d6c61d5d3e174461bbf07c8a7db59169565726c93472680fa146e833f309
4
- data.tar.gz: 1de02cedf4b5eb2e1971e26960d313831f15214e40fbd89657d5da781b289ad4
3
+ metadata.gz: bab2e14b0b68874c47f728f68e7269e1347ab0997d005f93f16b16e897ba386d
4
+ data.tar.gz: 31238907ad9f6f969e0a557c85207136b9f49b335494d5d2a2dc6552261b0bab
5
5
  SHA512:
6
- metadata.gz: 4c02cb07f6e85f5272e821cffebed2e2b179580a76441641401a8f321a9cf3901dce21022eb846e5d2cd8a6ec1fd9fca4cb7187d6b3a736c191c8697605b0180
7
- data.tar.gz: 84512d6d0695a2176a8c31869fad1f7f3ebbd8449abc0cf44d017bca081ef85c95f4834931842b9e8f9a9b68e143af45ec6ea642445ab82413501727b1fd6818
6
+ metadata.gz: dc788a5dca9293c258488278ac0617fd3658e16f520c75df304e70b5591832a1ae84ae5ee43d091539904e7a66aef0a03801ac21b997f7b2b2f5bdb99d31e7dd
7
+ data.tar.gz: 31026554dadcf54876140fee95c5a2bc4b847cee45e09022b6c5a018a2ea9ff2fa1f067a515acf7cacb880e470935d3e28b34a773d19aae9723a5542c6a55a8a
data/.rubocop_todo.yml CHANGED
@@ -1,12 +1,12 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2024-10-28 08:29:46 UTC using RuboCop version 1.66.1.
3
+ # on 2024-10-30 07:34:33 UTC using RuboCop version 1.66.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 148
9
+ # Offense count: 146
10
10
  # This cop supports safe autocorrection (--autocorrect).
11
11
  # Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
12
12
  # URISchemes: http, https
@@ -46,7 +46,7 @@ Metrics/AbcSize:
46
46
  - 'lib/lutaml/model/xml_adapter/xml_document.rb'
47
47
  - 'lib/lutaml/model/xml_mapping_rule.rb'
48
48
 
49
- # Offense count: 6
49
+ # Offense count: 5
50
50
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
51
51
  # AllowedMethods: refine
52
52
  Metrics/BlockLength:
@@ -64,12 +64,12 @@ Metrics/CyclomaticComplexity:
64
64
  - 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
65
65
  - 'lib/lutaml/model/xml_adapter/xml_document.rb'
66
66
 
67
- # Offense count: 50
67
+ # Offense count: 51
68
68
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
69
69
  Metrics/MethodLength:
70
- Max: 51
70
+ Max: 43
71
71
 
72
- # Offense count: 6
72
+ # Offense count: 7
73
73
  # Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
74
74
  Metrics/ParameterLists:
75
75
  Max: 13
@@ -85,12 +85,11 @@ Metrics/PerceivedComplexity:
85
85
  - 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
86
86
  - 'lib/lutaml/model/xml_adapter/xml_document.rb'
87
87
 
88
- # Offense count: 9
88
+ # Offense count: 7
89
89
  # Configuration parameters: Prefixes, AllowedPatterns.
90
90
  # Prefixes: when, with, without
91
91
  RSpec/ContextWording:
92
92
  Exclude:
93
- - 'spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb'
94
93
  - 'spec/lutaml/model/xml_adapter/oga_adapter_spec.rb'
95
94
  - 'spec/lutaml/model/xml_adapter/ox_adapter_spec.rb'
96
95
  - 'spec/lutaml/model/xml_adapter/xml_namespace_spec.rb'
data/README.adoc CHANGED
@@ -618,6 +618,73 @@ end
618
618
  ----
619
619
  ====
620
620
 
621
+
622
+ ==== Mapping all content (XML only)
623
+
624
+ WARNING: This feature is only applicable to XML (for now).
625
+
626
+ The `map_all` tag in XML mapping captures and maps all content within an XML
627
+ element into a single attribute in the target Ruby object.
628
+
629
+ The use case for `map_all` is to tell Lutaml::Model to not parse the content of
630
+ the XML element at all, and instead handle it as an XML string.
631
+
632
+ This is useful in the case where the content of an XML element is not to be
633
+ handled by a Lutaml::Model::Serializable object.
634
+
635
+ This feature is commonly used with custom methods or a custom model object to
636
+ handle the content.
637
+
638
+ This includes:
639
+
640
+ * nested tags
641
+ * attributes
642
+ * text nodes
643
+
644
+ The `map_all` tag is **exclusive** and cannot be combined with other mappings
645
+ (`map_element`, `map_attribute`, `map_content`) for the same element, ensuring
646
+ it captures the entire inner XML content.
647
+
648
+ NOTE: An error is raised if `map_all` is defined alongside any other mapping in
649
+ the same XML mapping context.
650
+
651
+ Syntax:
652
+
653
+ [source,ruby]
654
+ ----
655
+ xml do
656
+ map_all to: :name_of_attribute
657
+ end
658
+ ----
659
+
660
+ .Mapping all the content using `map_all`
661
+ [example]
662
+ ====
663
+ [source,ruby]
664
+ ----
665
+ class ExampleMapping < Lutaml::Model::Serializable
666
+ attribute :description, :string
667
+
668
+ xml do
669
+ map_all to: :description
670
+ end
671
+ end
672
+ ----
673
+
674
+ [source,xml]
675
+ ----
676
+ <ExampleMapping>Content with <b>tags</b> and <i>formatting</i>.</ExampleMapping>
677
+ ----
678
+
679
+ [source,ruby]
680
+ ----
681
+ > parsed = ExampleMapping.from_xml(xml)
682
+ > puts parsed.all_content
683
+ # "Content with <b>tags</b> and <i>formatting</i>."
684
+ ----
685
+ ====
686
+
687
+
621
688
  ==== Mapping elements
622
689
 
623
690
  The `map_element` method maps an XML element to a data model attribute.
@@ -13,9 +13,12 @@ module Lutaml
13
13
 
14
14
  def initialize(name, type, options = {})
15
15
  @name = name
16
- @type = cast_type(type)
16
+
17
+ validate_type!(type)
18
+ @type = cast_type!(type)
19
+
20
+ validate_options!(options)
17
21
  @options = options
18
- validate_options!
19
22
 
20
23
  @raw = !!options[:raw]
21
24
 
@@ -29,7 +32,7 @@ module Lutaml
29
32
  @options[:delegate]
30
33
  end
31
34
 
32
- def cast_type(type)
35
+ def cast_type!(type)
33
36
  case type
34
37
  when Class
35
38
  type
@@ -212,11 +215,18 @@ module Lutaml
212
215
 
213
216
  private
214
217
 
215
- def validate_options!
216
- if (options = @options.keys - ALLOWED_OPTIONS).any?
217
- raise StandardError, "Invalid options given for `#{name}` #{options}"
218
+ def validate_options!(options)
219
+ if (invalid_opts = options.keys - ALLOWED_OPTIONS).any?
220
+ raise StandardError, "Invalid options given for `#{name}` #{invalid_opts}"
218
221
  end
219
222
  end
223
+
224
+ def validate_type!(type)
225
+ return true if type.is_a?(Class)
226
+ return true if [Symbol, String].include?(type.class) && cast_type!(type)
227
+
228
+ raise ArgumentError, "Invalid type: #{type}, must be a Symbol, String or a Class"
229
+ end
220
230
  end
221
231
  end
222
232
  end
@@ -332,7 +332,9 @@ module Lutaml
332
332
  mappings.each do |rule|
333
333
  raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
334
334
 
335
- value = if rule.content_mapping?
335
+ value = if rule.raw_mapping?
336
+ doc.node.inner_xml
337
+ elsif rule.content_mapping?
336
338
  doc["text"]
337
339
  elsif doc.key_exist?(rule.namespaced_name)
338
340
  doc.fetch(rule.namespaced_name)
@@ -397,16 +399,21 @@ module Lutaml
397
399
  value
398
400
  end
399
401
 
400
- if attr && !rule.content_mapping? && !rule.custom_methods[:from]
401
- value = attr.cast(
402
- value,
403
- :xml,
404
- caller_class: self,
405
- mixed_content: rule.mixed_content,
406
- )
407
- end
402
+ return value unless cast_value?(attr, rule)
408
403
 
409
- value
404
+ attr.cast(
405
+ value,
406
+ :xml,
407
+ caller_class: self,
408
+ mixed_content: rule.mixed_content,
409
+ )
410
+ end
411
+
412
+ def cast_value?(attr, rule)
413
+ attr &&
414
+ !rule.raw_mapping? &&
415
+ !rule.content_mapping? &&
416
+ !rule.custom_methods[:from]
410
417
  end
411
418
 
412
419
  def text_hash?(attr, value)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.3.18"
5
+ VERSION = "0.3.20"
6
6
  end
7
7
  end
@@ -33,6 +33,16 @@ module Lutaml
33
33
 
34
34
  private
35
35
 
36
+ def prefix_xml(xml, mapping, options)
37
+ if options.key?(:namespace_prefix)
38
+ options[:namespace_prefix] ? xml[options[:namespace_prefix]] : xml
39
+ elsif mapping.namespace_prefix
40
+ xml[mapping.namespace_prefix]
41
+ else
42
+ xml
43
+ end
44
+ end
45
+
36
46
  def build_ordered_element(xml, element, options = {})
37
47
  mapper_class = options[:mapper_class] || element.class
38
48
  xml_mapping = mapper_class.mappings_for(:xml)
@@ -40,13 +50,7 @@ module Lutaml
40
50
 
41
51
  attributes = build_attributes(element, xml_mapping)&.compact
42
52
 
43
- prefixed_xml = if options.key?(:namespace_prefix)
44
- options[:namespace_prefix] ? xml[options[:namespace_prefix]] : xml
45
- elsif xml_mapping.namespace_prefix
46
- xml[xml_mapping.namespace_prefix]
47
- else
48
- xml
49
- end
53
+ prefixed_xml = prefix_xml(xml, xml_mapping, options)
50
54
 
51
55
  tag_name = options[:tag_name] || xml_mapping.root_element
52
56
  tag_name = "#{tag_name}_" if prefixed_xml.respond_to?(tag_name)
@@ -148,6 +152,10 @@ module Lutaml
148
152
  build_xml.doc.root.to_xml
149
153
  end
150
154
 
155
+ def inner_xml
156
+ children.map(&:to_xml).join
157
+ end
158
+
151
159
  def build_xml(builder = nil)
152
160
  builder ||= Builder::Nokogiri.build
153
161
 
@@ -227,16 +227,36 @@ module Lutaml
227
227
  )
228
228
  end
229
229
 
230
- if (content_rule = xml_mapping.content_mapping)
231
- if content_rule.custom_methods[:to]
232
- @root.send(content_rule.custom_methods[:to], element,
233
- prefixed_xml.parent, prefixed_xml)
234
- else
235
- text = content_rule.serialize(element)
236
- text = text.join if text.is_a?(Array)
237
- prefixed_xml.add_text(xml, text)
238
- end
239
- end
230
+ process_content_mapping(element, xml_mapping.content_mapping, prefixed_xml)
231
+
232
+ process_raw_mapping(element, xml_mapping.raw_mapping, prefixed_xml)
233
+ end
234
+ end
235
+
236
+ def process_raw_mapping(element, rule, xml)
237
+ return unless rule
238
+
239
+ value = attribute_value_for(element, rule)
240
+ return unless render_element?(rule, element, value)
241
+
242
+ xml.add_text(xml, value)
243
+ end
244
+
245
+ def process_content_mapping(element, content_rule, xml)
246
+ return unless content_rule
247
+
248
+ if content_rule.custom_methods[:to]
249
+ @root.send(
250
+ content_rule.custom_methods[:to],
251
+ element,
252
+ xml.parent,
253
+ xml,
254
+ )
255
+ else
256
+ text = content_rule.serialize(element)
257
+ text = text.join if text.is_a?(Array)
258
+
259
+ xml.add_text(xml, text)
240
260
  end
241
261
  end
242
262
 
@@ -13,6 +13,7 @@ module Lutaml
13
13
  @elements = {}
14
14
  @attributes = {}
15
15
  @content_mapping = nil
16
+ @raw_mapping = nil
16
17
  @mixed_content = false
17
18
  end
18
19
 
@@ -123,7 +124,39 @@ module Lutaml
123
124
  )
124
125
  end
125
126
 
127
+ def map_all(
128
+ to:,
129
+ render_nil: false,
130
+ render_default: false,
131
+ delegate: nil,
132
+ with: {},
133
+ namespace: (namespace_set = false
134
+ nil),
135
+ prefix: (prefix_set = false
136
+ nil)
137
+ )
138
+ validate!("__raw_mapping", to, with)
139
+
140
+ rule = XmlMappingRule.new(
141
+ "__raw_mapping",
142
+ to: to,
143
+ render_nil: render_nil,
144
+ render_default: render_default,
145
+ with: with,
146
+ delegate: delegate,
147
+ namespace: namespace,
148
+ prefix: prefix,
149
+ default_namespace: namespace_uri,
150
+ namespace_set: namespace_set != false,
151
+ prefix_set: prefix_set != false,
152
+ )
153
+
154
+ @raw_mapping = rule
155
+ end
156
+
126
157
  def validate!(key, to, with)
158
+ validate_mappings!(key)
159
+
127
160
  if to.nil? && with.empty?
128
161
  msg = ":to or :with argument is required for mapping '#{key}'"
129
162
  raise IncorrectMappingArgumentsError.new(msg)
@@ -135,6 +168,16 @@ module Lutaml
135
168
  end
136
169
  end
137
170
 
171
+ def validate_mappings!(key)
172
+ unless @raw_mapping.nil?
173
+ raise StandardError, "no other mappings are allowed with map_all"
174
+ end
175
+
176
+ if !mappings.empty? && key == "__raw_mapping"
177
+ raise StandardError, "map_all is not allowed with other mappings"
178
+ end
179
+ end
180
+
138
181
  def elements
139
182
  @elements.values
140
183
  end
@@ -147,8 +190,12 @@ module Lutaml
147
190
  @content_mapping
148
191
  end
149
192
 
193
+ def raw_mapping
194
+ @raw_mapping
195
+ end
196
+
150
197
  def mappings
151
- elements + attributes + [content_mapping].compact
198
+ elements + attributes + [content_mapping, raw_mapping].compact
152
199
  end
153
200
 
154
201
  def element(name)
@@ -57,6 +57,10 @@ module Lutaml
57
57
  name.nil?
58
58
  end
59
59
 
60
+ def raw_mapping?
61
+ name == "__raw_mapping"
62
+ end
63
+
60
64
  def mixed_content?
61
65
  !!@mixed_content
62
66
  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.3.18
4
+ version: 0.3.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-29 00:00:00.000000000 Z
11
+ date: 2024-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor