lutaml-model 0.8.16 → 0.8.17
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/.github/workflows/release.yml +0 -3
- data/.rubocop_todo.yml +70 -14
- data/docs/_guides/xml/namespace-semantics.adoc +2 -0
- data/docs/_guides/xml-namespace-qualification.adoc +142 -0
- data/docs/_tutorials/xml-element-attribute-namespace-guide.adoc +2 -0
- data/docs/_tutorials/xml-schema-primer-style-guide.adoc +2 -0
- data/docs/namespace-management.adoc +2 -0
- data/docs/xml-schema-qualification.md +6 -0
- data/lib/lutaml/jsonld.rb +1 -4
- data/lib/lutaml/model/choice.rb +34 -0
- data/lib/lutaml/model/compiled_rule.rb +7 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +1 -0
- data/lib/lutaml/{jsonld → rdf}/context.rb +1 -1
- data/lib/lutaml/{jsonld/transform.rb → rdf/linked_data_transform.rb} +33 -35
- data/lib/lutaml/{jsonld → rdf}/term_definition.rb +1 -1
- data/lib/lutaml/rdf.rb +3 -0
- data/lib/lutaml/xml/adapter_element.rb +2 -2
- data/lib/lutaml/xml/transformation/element_builder.rb +38 -23
- data/lib/lutaml/yamlld/adapter.rb +25 -0
- data/lib/lutaml/yamlld.rb +25 -0
- data/spec/lutaml/integration/multi_format_spec.rb +23 -0
- data/spec/lutaml/model/attribute_spec.rb +8 -1
- data/spec/lutaml/model/choice_restrict_spec.rb +225 -0
- data/spec/lutaml/model/custom_model_spec.rb +4 -4
- data/spec/lutaml/model/mixed_content_spec.rb +2 -2
- data/spec/lutaml/model/multiple_mapping_spec.rb +4 -4
- data/spec/lutaml/model/ordered_content_spec.rb +3 -3
- data/spec/lutaml/model/uninitialized_class_spec.rb +1 -1
- data/spec/lutaml/model/xsd_form_default_patterns_spec.rb +2 -2
- data/spec/lutaml/model/xsd_patterns_spec.rb +4 -4
- data/spec/lutaml/{jsonld → rdf}/context_spec.rb +2 -2
- data/spec/lutaml/{jsonld/transform_spec.rb → rdf/linked_data_transform_spec.rb} +15 -1
- data/spec/lutaml/{jsonld → rdf}/term_definition_spec.rb +2 -2
- data/spec/lutaml/turtle/transform_spec.rb +2 -2
- data/spec/lutaml/xml/enhanced_mapping_spec.rb +230 -1
- data/spec/lutaml/xml/mapping_spec.rb +4 -4
- data/spec/lutaml/xml/namespace_placement_spec.rb +11 -8
- data/spec/lutaml/xml/namespace_three_phase_spec.rb +1 -1
- data/spec/lutaml/xml/prefix_control_spec.rb +4 -4
- data/spec/lutaml/xml/serializable_namespace_spec.rb +6 -6
- data/spec/lutaml/xml/type_namespace_examples_spec.rb +1 -1
- data/spec/lutaml/xml/type_namespace_integration_spec.rb +3 -3
- data/spec/lutaml/yamlld/adapter_spec.rb +56 -0
- data/spec/lutaml/yamlld/registration_spec.rb +33 -0
- metadata +13 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8296b9832eb080f64ba1344b7fa929b179fedc1c4265d9743d6636cd408720f6
|
|
4
|
+
data.tar.gz: 533255e9702cd82fc93cacc78bf88fb90dba17b8543b5ecf22bb223eaacfa9a1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 936d9c1ff8eefb9f2b861d3c29c4d032462f9f6f5624bb2d669ebfc77d49ade62ce2859f171052f3b473a00d0a83a88955ee1c4cbd47f5bbddfe2a625e62d1f8
|
|
7
|
+
data.tar.gz: 79f9f3b4cea4bff89c06df360e6a316507f62af37f2a277cd10dc54b38f72c374cadba6b4b995234e4c67f82dbaa71ecf751d3ec8ed280bf5fe0e64dca42534f
|
|
@@ -23,9 +23,6 @@ jobs:
|
|
|
23
23
|
uses: metanorma/ci/.github/workflows/rubygems-release.yml@main
|
|
24
24
|
with:
|
|
25
25
|
next_version: ${{ github.event.inputs.next_version }}
|
|
26
|
-
post_install: |
|
|
27
|
-
git status
|
|
28
|
-
cat .bundle/config
|
|
29
26
|
secrets:
|
|
30
27
|
rubygems-api-key: ${{ secrets.LUTAML_CI_RUBYGEMS_API_KEY }}
|
|
31
28
|
pat_token: ${{ secrets.LUTAML_CI_PAT_TOKEN }}
|
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-06-
|
|
3
|
+
# on 2026-06-08 11:45:37 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,13 +11,66 @@ Gemspec/RequiredRubyVersion:
|
|
|
11
11
|
Exclude:
|
|
12
12
|
- 'lutaml-model.gemspec'
|
|
13
13
|
|
|
14
|
-
# Offense count:
|
|
14
|
+
# Offense count: 1
|
|
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
|
+
- 'lib/lutaml/rdf/linked_data_transform.rb'
|
|
21
|
+
|
|
22
|
+
# Offense count: 2
|
|
23
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
24
|
+
# Configuration parameters: EnforcedStyleAlignWith.
|
|
25
|
+
# SupportedStylesAlignWith: either, start_of_block, start_of_line
|
|
26
|
+
Layout/BlockAlignment:
|
|
27
|
+
Exclude:
|
|
28
|
+
- 'spec/lutaml/model/choice_restrict_spec.rb'
|
|
29
|
+
|
|
30
|
+
# Offense count: 2
|
|
31
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
32
|
+
Layout/BlockEndNewline:
|
|
33
|
+
Exclude:
|
|
34
|
+
- 'spec/lutaml/model/choice_restrict_spec.rb'
|
|
35
|
+
|
|
36
|
+
# Offense count: 2
|
|
37
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
38
|
+
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
39
|
+
# SupportedStyles: special_inside_parentheses, consistent, align_brackets
|
|
40
|
+
Layout/FirstArrayElementIndentation:
|
|
41
|
+
Exclude:
|
|
42
|
+
- 'spec/lutaml/model/choice_restrict_spec.rb'
|
|
43
|
+
|
|
44
|
+
# Offense count: 4
|
|
45
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
46
|
+
# Configuration parameters: Width, EnforcedStyleAlignWith, AllowedPatterns.
|
|
47
|
+
# SupportedStylesAlignWith: start_of_line, relative_to_receiver
|
|
48
|
+
Layout/IndentationWidth:
|
|
49
|
+
Exclude:
|
|
50
|
+
- 'spec/lutaml/model/choice_restrict_spec.rb'
|
|
51
|
+
|
|
52
|
+
# Offense count: 3071
|
|
15
53
|
# This cop supports safe autocorrection (--autocorrect).
|
|
16
54
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
|
|
17
55
|
# URISchemes: http, https
|
|
18
56
|
Layout/LineLength:
|
|
19
57
|
Enabled: false
|
|
20
58
|
|
|
59
|
+
# Offense count: 1
|
|
60
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
61
|
+
# Configuration parameters: EnforcedStyle.
|
|
62
|
+
# SupportedStyles: symmetrical, new_line, same_line
|
|
63
|
+
Layout/MultilineArrayBraceLayout:
|
|
64
|
+
Exclude:
|
|
65
|
+
- 'spec/lutaml/model/choice_restrict_spec.rb'
|
|
66
|
+
|
|
67
|
+
# Offense count: 1
|
|
68
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
69
|
+
# Configuration parameters: AllowInHeredoc.
|
|
70
|
+
Layout/TrailingWhitespace:
|
|
71
|
+
Exclude:
|
|
72
|
+
- 'lib/lutaml/rdf/linked_data_transform.rb'
|
|
73
|
+
|
|
21
74
|
# Offense count: 21
|
|
22
75
|
# Configuration parameters: AllowedMethods.
|
|
23
76
|
# AllowedMethods: enums
|
|
@@ -232,7 +285,7 @@ RSpec/BeforeAfterAll:
|
|
|
232
285
|
RSpec/ContextWording:
|
|
233
286
|
Enabled: false
|
|
234
287
|
|
|
235
|
-
# Offense count:
|
|
288
|
+
# Offense count: 102
|
|
236
289
|
# Configuration parameters: IgnoredMetadata.
|
|
237
290
|
RSpec/DescribeClass:
|
|
238
291
|
Enabled: false
|
|
@@ -243,7 +296,7 @@ RSpec/DescribeMethod:
|
|
|
243
296
|
- 'spec/lutaml/xml/schema/xsd/schema_helper_methods_spec.rb'
|
|
244
297
|
- 'spec/lutaml/xml/serializable_namespace_spec.rb'
|
|
245
298
|
|
|
246
|
-
# Offense count:
|
|
299
|
+
# Offense count: 1343
|
|
247
300
|
# Configuration parameters: CountAsOne.
|
|
248
301
|
RSpec/ExampleLength:
|
|
249
302
|
Max: 68
|
|
@@ -318,7 +371,7 @@ RSpec/MultipleDescribes:
|
|
|
318
371
|
- 'spec/lutaml/xml/namespace_resolution_strategy_spec.rb'
|
|
319
372
|
- 'spec/lutaml/xml/xml_space_type_spec.rb'
|
|
320
373
|
|
|
321
|
-
# Offense count:
|
|
374
|
+
# Offense count: 1576
|
|
322
375
|
RSpec/MultipleExpectations:
|
|
323
376
|
Max: 21
|
|
324
377
|
|
|
@@ -366,7 +419,7 @@ RSpec/RepeatedExampleGroupDescription:
|
|
|
366
419
|
Exclude:
|
|
367
420
|
- 'spec/lutaml/model/mixed_content_spec.rb'
|
|
368
421
|
|
|
369
|
-
# Offense count:
|
|
422
|
+
# Offense count: 39
|
|
370
423
|
# Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector.
|
|
371
424
|
# SupportedInflectors: default, active_support
|
|
372
425
|
RSpec/SpecFilePathFormat:
|
|
@@ -392,6 +445,17 @@ Security/MarshalLoad:
|
|
|
392
445
|
Exclude:
|
|
393
446
|
- 'scripts-xmi-profile/profile_xmi.rb'
|
|
394
447
|
|
|
448
|
+
# Offense count: 3
|
|
449
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
450
|
+
# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
|
|
451
|
+
# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
|
|
452
|
+
# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
|
|
453
|
+
# FunctionalMethods: let, let!, subject, watch
|
|
454
|
+
# AllowedMethods: lambda, proc, it
|
|
455
|
+
Style/BlockDelimiters:
|
|
456
|
+
Exclude:
|
|
457
|
+
- 'spec/lutaml/model/choice_restrict_spec.rb'
|
|
458
|
+
|
|
395
459
|
# Offense count: 2
|
|
396
460
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
397
461
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
|
@@ -484,11 +548,3 @@ Style/StringConcatenation:
|
|
|
484
548
|
- 'lib/lutaml/model/schema/xml_compiler/complex_type.rb'
|
|
485
549
|
- 'lib/lutaml/model/schema/xml_compiler/simple_type.rb'
|
|
486
550
|
- 'lib/lutaml/model/schema/xml_compiler/xml_namespace_class.rb'
|
|
487
|
-
|
|
488
|
-
# Offense count: 3
|
|
489
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
490
|
-
# Configuration parameters: EnforcedStyleForMultiline.
|
|
491
|
-
# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
|
|
492
|
-
Style/TrailingCommaInArguments:
|
|
493
|
-
Exclude:
|
|
494
|
-
- 'spec/lutaml/model/raw_element_spec.rb'
|
|
@@ -13,6 +13,8 @@ parent: XML namespaces
|
|
|
13
13
|
Lutaml::Model implements W3C XML Namespace specification with a focus on
|
|
14
14
|
**attribute-based namespace resolution**. Each XML element's namespace is
|
|
15
15
|
determined by its attribute's type and parent context, following clear and
|
|
16
|
+
|
|
17
|
+
NOTE: This document covers the *core namespace resolution principles*. For the `form:` override option, the full qualification precedence ladder, and `form:` behavior on nested `Serializable` children, see the companion guide: link:../xml-namespace-qualification[XML Namespace Qualification and Prefix Control].
|
|
16
18
|
consistent rules.
|
|
17
19
|
|
|
18
20
|
This document explains the core namespace principles that govern how namespaces
|
|
@@ -509,6 +509,148 @@ Parent.new(child: Child.new(value: "test")).to_xml(prefix: true)
|
|
|
509
509
|
|
|
510
510
|
All three elements (`parent`, `child`, `value`) use the prefix because all are qualified via `element_form_default: :qualified`.
|
|
511
511
|
|
|
512
|
+
=== Form Override on Serializable Children
|
|
513
|
+
|
|
514
|
+
The `form:` option works on attributes of any type — including attributes whose value type is another `Serializable` model, not only simple types like `:string`. This matters when the parent's namespace declares `element_form_default :unqualified` (the W3C default) and you need a specific nested model element to be qualified anyway.
|
|
515
|
+
|
|
516
|
+
.Worked example: form: :qualified on a Serializable child
|
|
517
|
+
[example]
|
|
518
|
+
====
|
|
519
|
+
[source,ruby]
|
|
520
|
+
----
|
|
521
|
+
NS = Class.new(Lutaml::Model::XmlNamespace) do
|
|
522
|
+
uri "https://example.com/ns"
|
|
523
|
+
prefix_default "ex"
|
|
524
|
+
element_form_default :unqualified # W3C default for local elements
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
class Child < Lutaml::Model::Serializable
|
|
528
|
+
attribute :label, :string
|
|
529
|
+
xml do
|
|
530
|
+
element "child"
|
|
531
|
+
namespace NS
|
|
532
|
+
map_element "label", to: :label
|
|
533
|
+
end
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
class Parent < Lutaml::Model::Serializable
|
|
537
|
+
attribute :child, Child
|
|
538
|
+
xml do
|
|
539
|
+
element "item"
|
|
540
|
+
namespace NS
|
|
541
|
+
map_element "child", to: :child, form: :qualified # Force prefix
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
Parent.new(child: Child.new(label: "x")).to_xml
|
|
546
|
+
----
|
|
547
|
+
|
|
548
|
+
*Output*:
|
|
549
|
+
[source,xml]
|
|
550
|
+
----
|
|
551
|
+
<item xmlns="https://example.com/ns" xmlns:ex="https://example.com/ns">
|
|
552
|
+
<ex:child>
|
|
553
|
+
<label>x</label>
|
|
554
|
+
</ex:child>
|
|
555
|
+
</item>
|
|
556
|
+
----
|
|
557
|
+
|
|
558
|
+
The `ex:` prefix on `<child>` is forced by `form: :qualified`, overriding the parent's `:unqualified` schema default. The inner `<label>` remains unprefixed because the `Child` model's own mapping rule for `label` has no `form:` override — see the next section.
|
|
559
|
+
====
|
|
560
|
+
|
|
561
|
+
=== Form Scope: Per-Rule, Not Transitive
|
|
562
|
+
|
|
563
|
+
`form:` is a per-mapping-rule override. It does **not** propagate transitively to grandchildren. Each level of the model tree applies its own rule against its own parent's `element_form_default`.
|
|
564
|
+
|
|
565
|
+
.Worked example: form on parent does not cascade to grandchild
|
|
566
|
+
[example]
|
|
567
|
+
====
|
|
568
|
+
[source,ruby]
|
|
569
|
+
----
|
|
570
|
+
NS = Class.new(Lutaml::Model::XmlNamespace) do
|
|
571
|
+
uri "https://example.com/ns"
|
|
572
|
+
prefix_default "ex"
|
|
573
|
+
element_form_default :unqualified
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
class Grandchild < Lutaml::Model::Serializable
|
|
577
|
+
attribute :value, :string
|
|
578
|
+
xml do
|
|
579
|
+
element "grandchild"
|
|
580
|
+
namespace NS
|
|
581
|
+
map_element "value", to: :value
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
class Child < Lutaml::Model::Serializable
|
|
586
|
+
attribute :inner, Grandchild
|
|
587
|
+
xml do
|
|
588
|
+
element "child"
|
|
589
|
+
namespace NS
|
|
590
|
+
map_element "grandchild", to: :inner # NO form: override
|
|
591
|
+
end
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
class Parent < Lutaml::Model::Serializable
|
|
595
|
+
attribute :child, Child
|
|
596
|
+
xml do
|
|
597
|
+
element "item"
|
|
598
|
+
namespace NS
|
|
599
|
+
map_element "child", to: :child, form: :qualified # Parent's rule only
|
|
600
|
+
end
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
Parent.new(child: Child.new(inner: Grandchild.new(value: "x"))).to_xml
|
|
604
|
+
----
|
|
605
|
+
|
|
606
|
+
*Output*:
|
|
607
|
+
[source,xml]
|
|
608
|
+
----
|
|
609
|
+
<item xmlns="https://example.com/ns" xmlns:ex="https://example.com/ns">
|
|
610
|
+
<ex:child>
|
|
611
|
+
<grandchild>
|
|
612
|
+
<value>x</value>
|
|
613
|
+
</grandchild>
|
|
614
|
+
</ex:child>
|
|
615
|
+
</item>
|
|
616
|
+
----
|
|
617
|
+
|
|
618
|
+
* `<ex:child>` is qualified because the *Parent* rule has `form: :qualified`.
|
|
619
|
+
* `<grandchild>` is unprefixed because the *Child* rule has no `form:` and the Child's namespace is `:unqualified`.
|
|
620
|
+
* The Parent's `form: :qualified` does **not** cascade.
|
|
621
|
+
====
|
|
622
|
+
|
|
623
|
+
To qualify every level, either set `form: :qualified` on each mapping rule that needs it, or set `element_form_default :qualified` on the namespace so inheritance handles it.
|
|
624
|
+
|
|
625
|
+
== Qualification Precedence
|
|
626
|
+
|
|
627
|
+
When serializing an element, the namespace is resolved by checking the following sources in priority order. The first match wins.
|
|
628
|
+
|
|
629
|
+
[cols="1,4", options="header"]
|
|
630
|
+
|===
|
|
631
|
+
|Priority |Source
|
|
632
|
+
|
|
633
|
+
|1 (highest)
|
|
634
|
+
|Type-level namespace: the attribute's value type (a `Type::Value` subclass) declares `xml_namespace`. Wins over everything else.
|
|
635
|
+
|
|
636
|
+
|2
|
|
637
|
+
|Rule-level namespace: the mapping rule has an explicit `namespace:` option. *However*, if the parent's `element_form_default` is `:unqualified` AND the rule's namespace matches the parent's, the namespace is overridden to blank — W3C unqualified semantics for local elements.
|
|
638
|
+
|
|
639
|
+
|3
|
|
640
|
+
|`form: :unqualified` on the rule: force the element into no namespace.
|
|
641
|
+
|
|
642
|
+
|4
|
|
643
|
+
|Parent's `element_form_default :qualified` inheritance: the child inherits the parent's namespace — but only if the child model itself declares a namespace (W3C: `elementFormDefault` applies to locally-declared elements only).
|
|
644
|
+
|
|
645
|
+
|5
|
|
646
|
+
|`form: :qualified` on the rule: inherit the parent's namespace.
|
|
647
|
+
|
|
648
|
+
|6 (lowest)
|
|
649
|
+
|No namespace.
|
|
650
|
+
|===
|
|
651
|
+
|
|
652
|
+
This ladder is implemented in `Lutaml::Xml::Transformation::ElementBuilder#determine_element_namespace`. The `ElementFormOptionRule` decision rule reads the propagated `form` value during the planning phase and forces prefix (`:qualified`) or default (`:unqualified`) format accordingly.
|
|
653
|
+
|
|
512
654
|
== Type Namespaces
|
|
513
655
|
|
|
514
656
|
Value types can define their own namespaces:
|
|
@@ -9,6 +9,8 @@ This guide explains how XML namespaces work, particularly the distinction betwee
|
|
|
9
9
|
- **Qualified**: Element/attribute needs a namespace declared in the XML
|
|
10
10
|
- **Unqualified**: Element/attribute does not need a namespace declared (uses parent's namespace)
|
|
11
11
|
|
|
12
|
+
> **Authoritative reference:** For the `element_form_default` / `form:` system, the full qualification precedence ladder, and `form:` behavior on nested `Serializable` children, see [docs/_guides/xml-namespace-qualification.adoc](../_guides/xml-namespace-qualification.adoc). This tutorial is a conceptual introduction.
|
|
13
|
+
|
|
12
14
|
## Default Namespace Inheritance
|
|
13
15
|
|
|
14
16
|
**Critical Rule:** Default namespace inheritance ONLY applies to elements, NOT attributes.
|
|
@@ -441,6 +441,8 @@ end
|
|
|
441
441
|
|
|
442
442
|
NOTE: The `<comment>` element uses `xmlns=""` to explicitly declare blank namespace (form: :unqualified override).
|
|
443
443
|
|
|
444
|
+
TIP: The `form:` option also works on attributes whose value type is a nested `Serializable` model, not just simple types like `:string`. It is a per-rule override and does not propagate transitively to grandchildren. For the full qualification precedence ladder and worked nested-model examples, see link:../_guides/xml-namespace-qualification/#form-override-on-serializable-children[XML Namespace Qualification and Prefix Control].
|
|
445
|
+
|
|
444
446
|
== Section 3: Type Namespaces
|
|
445
447
|
|
|
446
448
|
=== General
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
This guide provides comprehensive information about XML namespace management in Lutaml::Model, including the `namespace_scope` directive, W3C compliance, and best practices for multi-namespace documents.
|
|
8
8
|
|
|
9
|
+
NOTE: This guide focuses on namespace *declaration* and *scope*. For the `element_form_default` / `form:` qualification system — including the precedence ladder and `form:` behavior on nested `Serializable` children — see the companion guide: link:./_guides/xml-namespace-qualification[XML Namespace Qualification and Prefix Control].
|
|
10
|
+
|
|
9
11
|
== Understanding XML Namespaces
|
|
10
12
|
|
|
11
13
|
XML namespaces provide a method to avoid element name conflicts by qualifying names used in XML documents through a URI reference.
|
|
@@ -13,6 +13,12 @@ declared in the XML, "unqualified" means the ELEMENT/ATTRIBUTE does not need a
|
|
|
13
13
|
namespace declared in the XML (i.e. we look to its parent's namespace to find
|
|
14
14
|
its namespace).
|
|
15
15
|
|
|
16
|
+
> **Authoritative reference:** For the full qualification precedence ladder,
|
|
17
|
+
> `form:` behavior on nested `Serializable` children, per-rule vs transitive
|
|
18
|
+
> scope, and worked examples, see
|
|
19
|
+
> [docs/_guides/xml-namespace-qualification.adoc](./_guides/xml-namespace-qualification.adoc).
|
|
20
|
+
> This document is a conceptual overview; the guide is the canonical reference.
|
|
21
|
+
|
|
16
22
|
When there is no XML Schema present:
|
|
17
23
|
* element "acts like" unqualified (because of default namespace inheritance, does not need prefix)
|
|
18
24
|
* attribute "acts like" qualified (because of no default namespace inheritance, requires prefix)
|
data/lib/lutaml/jsonld.rb
CHANGED
|
@@ -5,9 +5,6 @@ require_relative "rdf"
|
|
|
5
5
|
|
|
6
6
|
module Lutaml
|
|
7
7
|
module JsonLd
|
|
8
|
-
autoload :Context, "#{__dir__}/jsonld/context"
|
|
9
|
-
autoload :TermDefinition, "#{__dir__}/jsonld/term_definition"
|
|
10
|
-
autoload :Transform, "#{__dir__}/jsonld/transform"
|
|
11
8
|
autoload :Adapter, "#{__dir__}/jsonld/adapter"
|
|
12
9
|
end
|
|
13
10
|
end
|
|
@@ -16,7 +13,7 @@ Lutaml::Model::FormatRegistry.register(
|
|
|
16
13
|
:jsonld,
|
|
17
14
|
mapping_class: Lutaml::Rdf::Mapping,
|
|
18
15
|
adapter_class: Lutaml::JsonLd::Adapter,
|
|
19
|
-
transformer: Lutaml::
|
|
16
|
+
transformer: Lutaml::Rdf::LinkedDataTransform,
|
|
20
17
|
key_value: false,
|
|
21
18
|
rdf: true,
|
|
22
19
|
error_types: ["JSON::ParserError"],
|
data/lib/lutaml/model/choice.rb
CHANGED
|
@@ -36,6 +36,36 @@ module Lutaml
|
|
|
36
36
|
@attributes << @model.attribute(name, type, options)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
# Restrict options on a predefined or imported attribute within this choice
|
|
40
|
+
#
|
|
41
|
+
# @param name [Symbol] The attribute name to restrict
|
|
42
|
+
# @param options [Hash] New options to merge
|
|
43
|
+
# @return [Symbol] The attribute name
|
|
44
|
+
def restrict(name, options = {})
|
|
45
|
+
@model.restrict(name, options)
|
|
46
|
+
attr = @model.attributes[name]
|
|
47
|
+
unless @attributes.include?(attr)
|
|
48
|
+
attr.options[:choice] = self
|
|
49
|
+
@attributes << attr
|
|
50
|
+
end
|
|
51
|
+
invalidate_cache!
|
|
52
|
+
name
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Remove an attribute from this choice block
|
|
56
|
+
#
|
|
57
|
+
# @param name [Symbol] The attribute name to remove
|
|
58
|
+
# @return [Boolean] true if the attribute was removed
|
|
59
|
+
def remove_attribute(name)
|
|
60
|
+
attr = @attributes.find { |a| !a.is_a?(Choice) && a.name == name }
|
|
61
|
+
return nil unless attr
|
|
62
|
+
|
|
63
|
+
@attributes.delete(attr)
|
|
64
|
+
attr.options.delete(:choice)
|
|
65
|
+
invalidate_cache!
|
|
66
|
+
attr
|
|
67
|
+
end
|
|
68
|
+
|
|
39
69
|
def choice(min: 1, max: 1, &block)
|
|
40
70
|
@attributes << Choice.new(@model, min, max, format: @format).tap do |c|
|
|
41
71
|
c.instance_eval(&block)
|
|
@@ -163,6 +193,10 @@ register = nil)
|
|
|
163
193
|
|
|
164
194
|
private
|
|
165
195
|
|
|
196
|
+
def invalidate_cache!
|
|
197
|
+
@flat_attributes = nil
|
|
198
|
+
end
|
|
199
|
+
|
|
166
200
|
def raise_errors(choices_hash)
|
|
167
201
|
flat_attr_names = flat_attributes.map { |attr| attr.name.to_s }
|
|
168
202
|
choices_hash.each do |choice_attr, count|
|
|
@@ -98,6 +98,13 @@ module Lutaml
|
|
|
98
98
|
!custom_methods.empty?
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
# Check if form is explicitly set
|
|
102
|
+
#
|
|
103
|
+
# @return [Boolean] true if form option was provided
|
|
104
|
+
def form_set?
|
|
105
|
+
!form.nil?
|
|
106
|
+
end
|
|
107
|
+
|
|
101
108
|
# Check if a name matches this rule (including alias names for multiple mappings)
|
|
102
109
|
#
|
|
103
110
|
# @param name [String, Symbol] The name to check
|
data/lib/lutaml/model/version.rb
CHANGED
data/lib/lutaml/model.rb
CHANGED
|
@@ -1,34 +1,31 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "json"
|
|
4
|
-
|
|
5
3
|
module Lutaml
|
|
6
|
-
module
|
|
7
|
-
class
|
|
8
|
-
def model_to_data(instance,
|
|
9
|
-
mapping = extract_mapping(options)
|
|
4
|
+
module Rdf
|
|
5
|
+
class LinkedDataTransform < Lutaml::Rdf::Transform
|
|
6
|
+
def model_to_data(instance, format, options = {})
|
|
7
|
+
mapping = extract_mapping(format, options)
|
|
10
8
|
return {} unless mapping
|
|
11
9
|
|
|
12
10
|
if mapping.rdf_members.any?
|
|
13
|
-
build_graph_document(mapping, instance)
|
|
11
|
+
build_graph_document(mapping, instance, format)
|
|
14
12
|
else
|
|
15
|
-
build_resource_object(mapping, instance)
|
|
13
|
+
build_resource_object(mapping, instance, format)
|
|
16
14
|
end
|
|
17
15
|
end
|
|
18
16
|
|
|
19
|
-
def data_to_model(data,
|
|
20
|
-
mapping = extract_mapping(options)
|
|
17
|
+
def data_to_model(data, format, options = {})
|
|
18
|
+
mapping = extract_mapping(format, options)
|
|
21
19
|
return model_class.new unless mapping
|
|
22
20
|
|
|
23
|
-
hash = data.is_a?(
|
|
21
|
+
hash = data.is_a?(Hash) ? data : {}
|
|
24
22
|
|
|
25
23
|
if hash.key?("@graph") && hash["@graph"].is_a?(Array) && !hash["@graph"].empty?
|
|
26
|
-
|
|
27
|
-
first = graph_data.first
|
|
24
|
+
first = hash["@graph"].first
|
|
28
25
|
hash = first.is_a?(Hash) ? first : {}
|
|
29
26
|
end
|
|
30
27
|
|
|
31
|
-
hash =
|
|
28
|
+
hash = strip_linked_data_keywords(hash)
|
|
32
29
|
|
|
33
30
|
attrs = {}
|
|
34
31
|
mapping.rdf_predicates.each do |rule|
|
|
@@ -53,29 +50,29 @@ module Lutaml
|
|
|
53
50
|
|
|
54
51
|
private
|
|
55
52
|
|
|
56
|
-
def extract_mapping(options)
|
|
57
|
-
options[:mappings] || mappings_for(
|
|
53
|
+
def extract_mapping(format, options)
|
|
54
|
+
options[:mappings] || mappings_for(format, lutaml_register)
|
|
58
55
|
end
|
|
59
56
|
|
|
60
|
-
def build_graph_document(mapping, instance)
|
|
61
|
-
context = build_merged_context_recursive(mapping, instance)
|
|
62
|
-
graph = collect_resources(mapping, instance)
|
|
57
|
+
def build_graph_document(mapping, instance, format)
|
|
58
|
+
context = build_merged_context_recursive(mapping, instance, format)
|
|
59
|
+
graph = collect_resources(mapping, instance, format)
|
|
63
60
|
|
|
64
61
|
{ "@context" => context, "@graph" => graph }
|
|
65
62
|
end
|
|
66
63
|
|
|
67
|
-
def collect_resources(mapping, instance)
|
|
64
|
+
def collect_resources(mapping, instance, format)
|
|
68
65
|
graph = []
|
|
69
66
|
|
|
70
|
-
resource = build_resource_data(mapping, instance)
|
|
67
|
+
resource = build_resource_data(mapping, instance, format)
|
|
71
68
|
graph << resource unless resource.empty?
|
|
72
69
|
|
|
73
70
|
mapping.rdf_members.each do |member_rule|
|
|
74
71
|
each_member(instance, member_rule) do |member|
|
|
75
|
-
member_mapping = member_mapping_for(member,
|
|
72
|
+
member_mapping = member_mapping_for(member, format)
|
|
76
73
|
next unless member_mapping
|
|
77
74
|
|
|
78
|
-
child_resources = collect_resources(member_mapping, member)
|
|
75
|
+
child_resources = collect_resources(member_mapping, member, format)
|
|
79
76
|
graph.concat(child_resources)
|
|
80
77
|
end
|
|
81
78
|
end
|
|
@@ -83,17 +80,18 @@ module Lutaml
|
|
|
83
80
|
graph
|
|
84
81
|
end
|
|
85
82
|
|
|
86
|
-
def build_merged_context_recursive(mapping, instance)
|
|
83
|
+
def build_merged_context_recursive(mapping, instance, format)
|
|
87
84
|
context_hash = build_context_from_mapping(mapping).to_hash
|
|
88
85
|
|
|
89
86
|
mapping.rdf_members.each do |member_rule|
|
|
90
87
|
each_member(instance, member_rule) do |member|
|
|
91
|
-
member_mapping = member_mapping_for(member,
|
|
88
|
+
member_mapping = member_mapping_for(member, format)
|
|
92
89
|
next unless member_mapping
|
|
93
90
|
|
|
94
91
|
context_hash.merge!(build_context_from_mapping(member_mapping).to_hash)
|
|
95
92
|
|
|
96
|
-
child_ctx = build_merged_context_recursive(member_mapping, member
|
|
93
|
+
child_ctx = build_merged_context_recursive(member_mapping, member,
|
|
94
|
+
format)
|
|
97
95
|
context_hash.merge!(child_ctx)
|
|
98
96
|
end
|
|
99
97
|
end
|
|
@@ -134,13 +132,13 @@ module Lutaml
|
|
|
134
132
|
end
|
|
135
133
|
end
|
|
136
134
|
|
|
137
|
-
def build_resource_object(mapping, instance)
|
|
135
|
+
def build_resource_object(mapping, instance, format)
|
|
138
136
|
context = build_context_from_mapping(mapping).to_hash
|
|
139
|
-
data = build_resource_data(mapping, instance)
|
|
137
|
+
data = build_resource_data(mapping, instance, format)
|
|
140
138
|
{ "@context" => context }.merge(data)
|
|
141
139
|
end
|
|
142
140
|
|
|
143
|
-
def build_resource_data(mapping, instance)
|
|
141
|
+
def build_resource_data(mapping, instance, format)
|
|
144
142
|
result = {}
|
|
145
143
|
|
|
146
144
|
if mapping.rdf_type.any?
|
|
@@ -166,10 +164,10 @@ module Lutaml
|
|
|
166
164
|
mapping.rdf_members.each do |member_rule|
|
|
167
165
|
next unless member_rule.linked?
|
|
168
166
|
|
|
169
|
-
member_refs = collect_member_references(instance, member_rule)
|
|
167
|
+
member_refs = collect_member_references(instance, member_rule, format)
|
|
170
168
|
next if member_refs.empty?
|
|
171
169
|
|
|
172
|
-
key =
|
|
170
|
+
key = member_key(member_rule)
|
|
173
171
|
result[key] = member_refs
|
|
174
172
|
end
|
|
175
173
|
|
|
@@ -178,10 +176,10 @@ module Lutaml
|
|
|
178
176
|
result
|
|
179
177
|
end
|
|
180
178
|
|
|
181
|
-
def collect_member_references(instance, member_rule)
|
|
179
|
+
def collect_member_references(instance, member_rule, format)
|
|
182
180
|
refs = []
|
|
183
181
|
each_member(instance, member_rule) do |member|
|
|
184
|
-
member_mapping = member_mapping_for(member,
|
|
182
|
+
member_mapping = member_mapping_for(member, format)
|
|
185
183
|
next unless member_mapping
|
|
186
184
|
|
|
187
185
|
refs << { "@id" => resolve_subject_uri(member_mapping, member) }
|
|
@@ -189,7 +187,7 @@ module Lutaml
|
|
|
189
187
|
refs
|
|
190
188
|
end
|
|
191
189
|
|
|
192
|
-
def
|
|
190
|
+
def member_key(member_rule)
|
|
193
191
|
if member_rule.predicate_name
|
|
194
192
|
member_rule.predicate_name.to_s
|
|
195
193
|
elsif member_rule.link.is_a?(String)
|
|
@@ -241,7 +239,7 @@ module Lutaml
|
|
|
241
239
|
end
|
|
242
240
|
end
|
|
243
241
|
|
|
244
|
-
def
|
|
242
|
+
def strip_linked_data_keywords(data)
|
|
245
243
|
return data unless data.is_a?(Hash)
|
|
246
244
|
|
|
247
245
|
data.reject { |key, _| key.start_with?("@") }
|
data/lib/lutaml/rdf.rb
CHANGED
|
@@ -15,5 +15,8 @@ module Lutaml
|
|
|
15
15
|
autoload :MemberRule, "#{__dir__}/rdf/member_rule"
|
|
16
16
|
autoload :Namespaces, "#{__dir__}/rdf/namespaces"
|
|
17
17
|
autoload :Transform, "#{__dir__}/rdf/transform"
|
|
18
|
+
autoload :Context, "#{__dir__}/rdf/context"
|
|
19
|
+
autoload :TermDefinition, "#{__dir__}/rdf/term_definition"
|
|
20
|
+
autoload :LinkedDataTransform, "#{__dir__}/rdf/linked_data_transform"
|
|
18
21
|
end
|
|
19
22
|
end
|