lutaml-model 0.8.14 → 0.8.16

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +14 -73
  3. data/Gemfile +3 -5
  4. data/README.adoc +188 -25
  5. data/docs/_guides/xml-mapping.adoc +178 -24
  6. data/docs/_pages/importable_models.adoc +7 -1
  7. data/lib/lutaml/jsonld/transform.rb +44 -13
  8. data/lib/lutaml/model/attribute.rb +4 -0
  9. data/lib/lutaml/model/liquefiable.rb +9 -0
  10. data/lib/lutaml/model/liquid/indexed_access.rb +33 -0
  11. data/lib/lutaml/model/liquid.rb +1 -0
  12. data/lib/lutaml/model/version.rb +1 -1
  13. data/lib/lutaml/model.rb +2 -1
  14. data/lib/lutaml/rdf/mapping.rb +10 -1
  15. data/lib/lutaml/rdf/member_rule.rb +29 -4
  16. data/lib/lutaml/turtle/transform.rb +55 -35
  17. data/lib/lutaml/xml/adapter/plan_based_builder.rb +3 -1
  18. data/lib/lutaml/xml/adapter/xml_serializer.rb +15 -5
  19. data/lib/lutaml/xml/adapter.rb +2 -1
  20. data/lib/lutaml/xml/builder/base.rb +2 -1
  21. data/lib/lutaml/xml/data_model.rb +19 -3
  22. data/lib/lutaml/xml/mapping.rb +3 -1
  23. data/lib/lutaml/xml/mapping_rule.rb +28 -2
  24. data/lib/lutaml/xml/model_transform.rb +9 -1
  25. data/lib/lutaml/xml/serialization/instance_methods.rb +16 -9
  26. data/lib/lutaml/xml/transformation/element_builder.rb +1 -3
  27. data/lib/lutaml/xml/transformation/rule_applier.rb +21 -0
  28. data/lib/lutaml/xml/transformation/rule_compiler.rb +12 -3
  29. data/lutaml-model.gemspec +1 -1
  30. data/spec/lutaml/jsonld/transform_spec.rb +149 -0
  31. data/spec/lutaml/model/liquid/indexed_access_spec.rb +135 -0
  32. data/spec/lutaml/model/mixed_content_spec.rb +48 -7
  33. data/spec/lutaml/model/raw_element_spec.rb +533 -0
  34. data/spec/lutaml/rdf/mapping_spec.rb +71 -6
  35. data/spec/lutaml/rdf/member_rule_spec.rb +103 -1
  36. data/spec/lutaml/turtle/transform_spec.rb +144 -0
  37. metadata +9 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6e4dad188f815cf35cc931ab16476a0af158de6c64150f6733ffc5a2683e04a
4
- data.tar.gz: 7ae7d3fc7009a3ac3031f37a4d11786ec503c8ac7d13122b3d03dba6df9f5296
3
+ metadata.gz: 0b419ac1cce55a5e56ca59dd217042047801d2420751f89ce0b875558e517f44
4
+ data.tar.gz: ed7c6b7dcd749c35d7fa434d2adf4b9e036c7329afd80c2420ef75f57f65895f
5
5
  SHA512:
6
- metadata.gz: 0a8a4b84223eb10df5ecc114ee97b2551d62eb7f5550ce73903a90fd8a0b35f236ced31751bbff670f0a46d25c5e0ca3dff805fd370ada00b0b44359fcaf5c48
7
- data.tar.gz: 769f97b0a540b0e1a365bc1ceeeab8b5b45f046356e903d1fd71a52b0f13b8f2b720249a211ad5cbd7e579a6ecd9c1c4a9f981bf4ec374d5a81cfc4377abb3f0
6
+ metadata.gz: c923ed6933f06ca678d4b76c19b46553f1196e0b30c0f202113a792619c91c0e6b3e3885963f4b910d101722d0cee22df8bf71c0c593fc7de4f0832d9062d87d
7
+ data.tar.gz: cfc1cc06ebeb1affa7875ecc522d8625147f70ed81aa6ae2feb2b4debd92f652ce2cf565f9984e52170e65559a75f23139c85762df5018545b60187cb5974744
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-05-27 05:02:01 UTC using RuboCop version 1.86.0.
3
+ # on 2026-06-05 08:01:07 UTC using RuboCop version 1.87.0.
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
@@ -11,60 +11,13 @@ Gemspec/RequiredRubyVersion:
11
11
  Exclude:
12
12
  - 'lutaml-model.gemspec'
13
13
 
14
- # Offense count: 2
15
- # This cop supports safe autocorrection (--autocorrect).
16
- # Configuration parameters: EnforcedStyle, IndentationWidth.
17
- # SupportedStyles: with_first_argument, with_fixed_indentation
18
- Layout/ArgumentAlignment:
19
- Exclude:
20
- - 'spec/lutaml/model/opal_smoke_spec.rb'
21
-
22
- # Offense count: 1
23
- # This cop supports safe autocorrection (--autocorrect).
24
- Layout/ClosingParenthesisIndentation:
25
- Exclude:
26
- - 'spec/lutaml/model/opal_smoke_spec.rb'
27
-
28
- # Offense count: 1
29
- # This cop supports safe autocorrection (--autocorrect).
30
- # Configuration parameters: EnforcedStyle, IndentationWidth.
31
- # SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses
32
- Layout/FirstArgumentIndentation:
33
- Exclude:
34
- - 'spec/lutaml/model/opal_smoke_spec.rb'
35
-
36
- # Offense count: 2
37
- # This cop supports safe autocorrection (--autocorrect).
38
- # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
39
- # SupportedHashRocketStyles: key, separator, table
40
- # SupportedColonStyles: key, separator, table
41
- # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
42
- Layout/HashAlignment:
43
- Exclude:
44
- - 'spec/lutaml/model/opal_smoke_spec.rb'
45
-
46
- # Offense count: 3022
14
+ # Offense count: 3052
47
15
  # This cop supports safe autocorrection (--autocorrect).
48
16
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
49
17
  # URISchemes: http, https
50
18
  Layout/LineLength:
51
19
  Enabled: false
52
20
 
53
- # Offense count: 1
54
- # This cop supports safe autocorrection (--autocorrect).
55
- # Configuration parameters: EnforcedStyle.
56
- # SupportedStyles: symmetrical, new_line, same_line
57
- Layout/MultilineMethodCallBraceLayout:
58
- Exclude:
59
- - 'spec/lutaml/model/opal_smoke_spec.rb'
60
-
61
- # Offense count: 1
62
- # This cop supports safe autocorrection (--autocorrect).
63
- # Configuration parameters: AllowInHeredoc.
64
- Layout/TrailingWhitespace:
65
- Exclude:
66
- - 'spec/lutaml/model/opal_smoke_spec.rb'
67
-
68
21
  # Offense count: 21
69
22
  # Configuration parameters: AllowedMethods.
70
23
  # AllowedMethods: enums
@@ -76,21 +29,15 @@ Lint/ConstantDefinitionInBlock:
76
29
  - 'spec/lutaml/xml/type_namespace_prefix_spec.rb'
77
30
  - 'spec/lutaml/xml/xml_space_type_spec.rb'
78
31
 
79
- # Offense count: 36
32
+ # Offense count: 35
80
33
  # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
81
34
  Lint/DuplicateBranch:
82
35
  Enabled: false
83
36
 
84
- # Offense count: 22
37
+ # Offense count: 3
85
38
  Lint/DuplicateMethods:
86
39
  Exclude:
87
40
  - 'lib/lutaml/xml/mapping.rb'
88
- - 'spec/lutaml/model/liquid_compatibility_spec.rb'
89
- - 'spec/lutaml/xml/namespace_no_hoisting_spec.rb'
90
- - 'spec/lutaml/xml/namespace_scope_declare_spec.rb'
91
- - 'spec/lutaml/xml/namespace_scope_vcard_spec.rb'
92
- - 'spec/lutaml/xml/prefix_control_spec.rb'
93
- - 'spec/lutaml/xml/type_namespace_examples_spec.rb'
94
41
 
95
42
  # Offense count: 1
96
43
  # This cop supports safe autocorrection (--autocorrect).
@@ -156,18 +103,12 @@ Lint/UnusedMethodArgument:
156
103
  - 'lib/lutaml/xml/unqualified_inheritance_strategy.rb'
157
104
  - 'spec/support/xml/xsd/code_example_validator.rb'
158
105
 
159
- # Offense count: 1
160
- # This cop supports safe autocorrection (--autocorrect).
161
- Lint/UselessAssignment:
162
- Exclude:
163
- - 'spec/lutaml/xml/opal_xml_spec.rb'
164
-
165
106
  # Offense count: 1
166
107
  Lint/UselessConstantScoping:
167
108
  Exclude:
168
109
  - 'lib/lutaml/xml/mapping_rule.rb'
169
110
 
170
- # Offense count: 345
111
+ # Offense count: 346
171
112
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
172
113
  Metrics/AbcSize:
173
114
  Enabled: false
@@ -183,12 +124,12 @@ Metrics/BlockLength:
183
124
  Metrics/BlockNesting:
184
125
  Max: 6
185
126
 
186
- # Offense count: 308
127
+ # Offense count: 307
187
128
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
188
129
  Metrics/CyclomaticComplexity:
189
130
  Enabled: false
190
131
 
191
- # Offense count: 553
132
+ # Offense count: 554
192
133
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
193
134
  Metrics/MethodLength:
194
135
  Max: 514
@@ -196,10 +137,10 @@ Metrics/MethodLength:
196
137
  # Offense count: 81
197
138
  # Configuration parameters: CountKeywordArgs.
198
139
  Metrics/ParameterLists:
199
- Max: 24
200
140
  MaxOptionalParameters: 5
141
+ Max: 25
201
142
 
202
- # Offense count: 262
143
+ # Offense count: 261
203
144
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
204
145
  Metrics/PerceivedComplexity:
205
146
  Enabled: false
@@ -291,7 +232,7 @@ RSpec/BeforeAfterAll:
291
232
  RSpec/ContextWording:
292
233
  Enabled: false
293
234
 
294
- # Offense count: 98
235
+ # Offense count: 101
295
236
  # Configuration parameters: IgnoredMetadata.
296
237
  RSpec/DescribeClass:
297
238
  Enabled: false
@@ -302,7 +243,7 @@ RSpec/DescribeMethod:
302
243
  - 'spec/lutaml/xml/schema/xsd/schema_helper_methods_spec.rb'
303
244
  - 'spec/lutaml/xml/serializable_namespace_spec.rb'
304
245
 
305
- # Offense count: 1301
246
+ # Offense count: 1328
306
247
  # Configuration parameters: CountAsOne.
307
248
  RSpec/ExampleLength:
308
249
  Max: 68
@@ -377,7 +318,7 @@ RSpec/MultipleDescribes:
377
318
  - 'spec/lutaml/xml/namespace_resolution_strategy_spec.rb'
378
319
  - 'spec/lutaml/xml/xml_space_type_spec.rb'
379
320
 
380
- # Offense count: 1515
321
+ # Offense count: 1560
381
322
  RSpec/MultipleExpectations:
382
323
  Max: 21
383
324
 
@@ -544,10 +485,10 @@ Style/StringConcatenation:
544
485
  - 'lib/lutaml/model/schema/xml_compiler/simple_type.rb'
545
486
  - 'lib/lutaml/model/schema/xml_compiler/xml_namespace_class.rb'
546
487
 
547
- # Offense count: 1
488
+ # Offense count: 3
548
489
  # This cop supports safe autocorrection (--autocorrect).
549
490
  # Configuration parameters: EnforcedStyleForMultiline.
550
491
  # SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
551
492
  Style/TrailingCommaInArguments:
552
493
  Exclude:
553
- - 'spec/lutaml/model/opal_smoke_spec.rb'
494
+ - 'spec/lutaml/model/raw_element_spec.rb'
data/Gemfile CHANGED
@@ -5,14 +5,12 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in lutaml-model.gemspec
6
6
  gemspec
7
7
 
8
- gem "moxml", git: "https://github.com/lutaml/moxml", branch: "main"
9
-
10
8
  # needed for liquid with ruby 3.4
11
9
  gem "base64"
12
10
  gem "benchmark-ips"
13
11
  gem "bigdecimal"
14
12
  gem "canon" # , path: "../canon"
15
- gem "json-ld", "~> 3.3"
13
+ gem "json-ld"
16
14
  gem "liquid", "~> 5"
17
15
  gem "multi_json"
18
16
  gem "nokogiri"
@@ -21,7 +19,7 @@ gem "oj"
21
19
  gem "openssl", "~> 3.0"
22
20
  gem "ox"
23
21
  gem "rake"
24
- gem "rdf-turtle", "~> 3.3"
22
+ gem "rdf-turtle"
25
23
  gem "rexml"
26
24
  gem "rspec"
27
25
  gem "rubocop"
@@ -33,4 +31,4 @@ gem "toml-rb"
33
31
 
34
32
  # ruby-prof works on all platforms including Windows (unlike stackprof)
35
33
  # Provides both CPU and memory profiling
36
- gem "ruby-prof", "~> 2.0", group: :development
34
+ gem "ruby-prof", group: :development
data/README.adoc CHANGED
@@ -3554,7 +3554,14 @@ the current value is the same as the default value.
3554
3554
 
3555
3555
 
3556
3556
 
3557
- === Attribute as raw string
3557
+ === Attribute as raw string (deprecated)
3558
+
3559
+ [WARNING]
3560
+ ====
3561
+ `attribute :name, :string, raw: true` is deprecated.
3562
+ Use `map_element "name", to: :name, raw: :content` instead.
3563
+ See <<xml-raw-element>> for the full raw XML capture documentation.
3564
+ ====
3558
3565
 
3559
3566
  An attribute can be set to read the value as raw string for XML, by using the `raw: true` option.
3560
3567
 
@@ -7969,42 +7976,186 @@ end
7969
7976
  ====
7970
7977
 
7971
7978
 
7972
- [[xml-map-all]]
7973
- ==== Mapping entire XML element into an attribute
7974
7979
 
7975
- The `map_all` tag in XML mapping captures and maps all content within an XML
7976
- element into a single attribute in the target Ruby object.
7980
+ [[xml-raw-element]]
7981
+ ==== Capturing raw XML content
7977
7982
 
7978
- The use case for `map_all` is to tell Lutaml::Model to not parse the content of
7979
- the XML element at all, and instead handle it as an XML string.
7983
+ Lutaml::Model provides several mechanisms for capturing XML content as raw strings.
7984
+ Use these when you need to embed foreign XML vocabularies (SVG, MathML, XSL-FO) or
7985
+ preserve markup that your model doesn't need to parse.
7980
7986
 
7981
- NOTE: The corresponding method for key-value formats is at <<key-value-map-all>>.
7987
+ ===== `raw: :element` on `map_element` full element capture (recommended)
7982
7988
 
7983
- WARNING: Notice that usage of mapping all will lead to incompatibility between
7984
- serialization formats, i.e. the raw string content will not be portable as
7985
- objects are across different formats.
7989
+ The `raw: :element` option captures a specific mapped element as a complete XML
7990
+ string, including its opening tag, attributes, children, and closing tag.
7991
+ This provides **lossless round-trip** fidelity — the captured string is
7992
+ self-contained and serialized verbatim.
7993
+
7994
+ .Syntax
7995
+ [source,ruby]
7996
+ ----
7997
+ xml do
7998
+ map_element "svg", to: :svg_data, raw: :element
7999
+ end
8000
+ ----
8001
+
8002
+ .Capturing an SVG element as raw XML
8003
+ [example]
8004
+ ====
8005
+ [source,ruby]
8006
+ ----
8007
+ class SvgContainer < Lutaml::Model::Serializable
8008
+ attribute :name, :string
8009
+ attribute :svg_data, :string
8010
+
8011
+ xml do
8012
+ element "container"
8013
+ map_element "name", to: :name
8014
+ map_element "svg", to: :svg_data, raw: :element
8015
+ end
8016
+ end
8017
+ ----
7986
8018
 
7987
- This is useful in the case where the content of an XML element is not to be
7988
- handled by a Lutaml::Model::Serializable object.
8019
+ [source,xml]
8020
+ ----
8021
+ <container>
8022
+ <name>diagram</name>
8023
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
8024
+ <rect x="0" y="0" width="100" height="100" fill="red"/>
8025
+ </svg>
8026
+ </container>
8027
+ ----
7989
8028
 
7990
- This feature is commonly used with custom methods or a custom model object to
7991
- handle the content.
8029
+ [source,ruby]
8030
+ ----
8031
+ > doc = SvgContainer.from_xml(xml)
8032
+ > doc.name
8033
+ # => "diagram"
8034
+ > doc.svg_data
8035
+ # => "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\">\n <rect x=\"0\" y=\"0\" width=\"100\" height=\"100\" fill=\"red\"/>\n</svg>"
8036
+ ----
7992
8037
 
7993
- This includes:
8038
+ The captured string round-trips correctly -- serializing the model back to XML
8039
+ outputs the raw element verbatim without escaping or wrapping.
7994
8040
 
7995
- * nested tags
7996
- * attributes
7997
- * text nodes
8041
+ `raw: :element` also works with collection attributes:
7998
8042
 
7999
- The `map_all` tag is **exclusive** and cannot be combined with other mappings
8000
- (`map_element`, `map_content`) except for `map_attribute` for the same element,
8001
- ensuring it captures the entire inner XML content.
8043
+ [source,ruby]
8044
+ ----
8045
+ class MultiFragment < Lutaml::Model::Serializable
8046
+ attribute :fragments, :string, collection: true
8002
8047
 
8003
- NOTE: An error is raised if `map_all` is defined alongside any other mapping in
8004
- the same XML mapping context.
8048
+ xml do
8049
+ element "container"
8050
+ map_element "fragment", to: :fragments, raw: :element
8051
+ end
8052
+ end
8053
+ ----
8054
+ ====
8005
8055
 
8006
- Syntax:
8056
+ ===== `raw: :content` on `map_element` — inner content capture
8057
+
8058
+ The `raw: :content` option captures only the *inner XML* of the matched element,
8059
+ without the wrapper tags. The wrapper element is reconstructed from the mapping rule
8060
+ during serialization.
8061
+
8062
+ .Syntax
8063
+ [source,ruby]
8064
+ ----
8065
+ xml do
8066
+ map_element "street", to: :street, raw: :content
8067
+ end
8068
+ ----
8069
+
8070
+ .Capturing inner XML content
8071
+ [example]
8072
+ ====
8073
+ [source,ruby]
8074
+ ----
8075
+ class Address < Lutaml::Model::Serializable
8076
+ attribute :street, :string
8077
+
8078
+ xml do
8079
+ element "address"
8080
+ map_element "street", to: :street, raw: :content
8081
+ end
8082
+ end
8083
+ ----
8084
+
8085
+ [source,xml]
8086
+ ----
8087
+ <address><street><b>123</b> Main St</street></address>
8088
+ ----
8089
+
8090
+ [source,ruby]
8091
+ ----
8092
+ > doc = Address.from_xml(xml)
8093
+ > doc.street
8094
+ # => "<b>123</b> Main St"
8095
+ ----
8096
+ ====
8097
+
8098
+ NOTE: `raw: :content` is **lossy** — namespace prefixes declared on the wrapper
8099
+ element are not captured. For lossless capture, use `raw: :element` instead.
8100
+
8101
+ ===== Namespace behavior
8102
+
8103
+ Both `raw: :element` and `raw: :content` match elements by local name regardless
8104
+ of namespace or prefix:
8105
+
8106
+ * Elements with no namespace (`<svg>`)
8107
+ * Elements with an `xmlns` declaration on themselves (`<svg xmlns="...">`)
8108
+ * Elements inheriting a default namespace from a parent
8109
+ * Elements with an explicit namespace prefix (`<ns:svg>`)
8110
+
8111
+ This is intentional — raw capture is designed to handle foreign XML vocabularies
8112
+ without needing model classes for them.
8113
+
8114
+ ===== Round-trip fidelity comparison
8115
+
8116
+ | Aspect | `raw: :element` | `raw: :content` |
8117
+ |--------|----------------|-----------------|
8118
+ | Element name | Preserved (in string) | Reconstructed from rule |
8119
+ | Element attributes | Preserved (in string) | Lost unless separately mapped |
8120
+ | Inner content | Preserved (in string) | Preserved (in string) |
8121
+ | Namespace prefixes | Safe (declared in string) | May break if declared on wrapper |
8122
+ | Fidelity | **Lossless** | **Lossy** |
8123
+
8124
+ ===== Wrapper model pattern for attribute extraction
8125
+
8126
+ Raw capture stores the element as a string. If you need to inspect or modify
8127
+ attributes on the captured element, use a wrapper model with `map_all`:
8007
8128
 
8129
+ [source,ruby]
8130
+ ----
8131
+ class SvgInner < Lutaml::Model::Serializable
8132
+ attribute :raw, :string
8133
+
8134
+ xml do
8135
+ element "svg"
8136
+ map_all to: :raw
8137
+ end
8138
+ end
8139
+
8140
+ class Container < Lutaml::Model::Serializable
8141
+ attribute :svg_data, SvgInner
8142
+
8143
+ xml do
8144
+ element "container"
8145
+ map_element "svg", to: :svg_data
8146
+ end
8147
+ end
8148
+ ----
8149
+
8150
+ ===== `map_all` — root inner XML capture
8151
+
8152
+ The `map_all` directive captures the entire inner XML of the root element into
8153
+ a single attribute. It is **exclusive** — it cannot be combined with other
8154
+ `map_element` or `map_content` mappings (only `map_attribute` is allowed).
8155
+
8156
+ NOTE: The corresponding method for key-value formats is at <<key-value-map-all>>.
8157
+
8158
+ .Syntax
8008
8159
  [source,ruby]
8009
8160
  ----
8010
8161
  xml do
@@ -8039,6 +8190,18 @@ end
8039
8190
  ----
8040
8191
  ====
8041
8192
 
8193
+ ===== Deprecated: `attribute :x, :string, raw: true`
8194
+
8195
+ [WARNING]
8196
+ ====
8197
+ `attribute :name, :string, raw: true` is deprecated. Use
8198
+ `map_element "name", to: :name, raw: :content` instead.
8199
+ ====
8200
+
8201
+ This legacy syntax captures the inner XML of the matched element.
8202
+ It is equivalent to `raw: :content` on the mapping. Existing code continues
8203
+ to work but emits a deprecation warning.
8204
+
8042
8205
 
8043
8206
  ==== Mapping CDATA nodes
8044
8207