lutaml-model 0.3.18 → 0.3.20

Sign up to get free protection for your applications and to get access to all the features.
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