lutaml-model 0.8.3 → 0.8.4
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/.rubocop_todo.yml +23 -23
- data/README.adoc +213 -1
- data/docs/_guides/document-validation.adoc +303 -0
- data/docs/_guides/index.adoc +1 -0
- data/docs/_guides/xml-mapping.adoc +9 -1
- data/docs/_guides/xml_mappings/07_best_practices.adoc +36 -0
- data/docs/_guides/xml_mappings/08_troubleshooting.adoc +89 -0
- data/docs/_tutorials/lutaml-xml-architecture.adoc +6 -1
- data/lib/lutaml/model/attribute.rb +19 -1
- data/lib/lutaml/model/error/liquid_drop_already_registered_error.rb +11 -0
- data/lib/lutaml/model/error/ordered_content_mapping_error.rb +17 -0
- data/lib/lutaml/model/global_context.rb +1 -0
- data/lib/lutaml/model/liquefiable.rb +12 -15
- data/lib/lutaml/model/mapping/mapping_rule.rb +10 -2
- data/lib/lutaml/model/mapping_hash.rb +1 -1
- data/lib/lutaml/model/services/transformer.rb +67 -32
- data/lib/lutaml/model/transform.rb +41 -4
- data/lib/lutaml/model/uninitialized_class.rb +11 -5
- data/lib/lutaml/model/validation/concerns/has_issues.rb +27 -0
- data/lib/lutaml/model/validation/context.rb +36 -0
- data/lib/lutaml/model/validation/issue.rb +62 -0
- data/lib/lutaml/model/validation/layer_result.rb +34 -0
- data/lib/lutaml/model/validation/profile.rb +66 -0
- data/lib/lutaml/model/validation/registry.rb +60 -0
- data/lib/lutaml/model/validation/remediation.rb +33 -0
- data/lib/lutaml/model/validation/remediation_result.rb +20 -0
- data/lib/lutaml/model/validation/report.rb +39 -0
- data/lib/lutaml/model/validation/rule.rb +59 -0
- data/lib/lutaml/model/validation.rb +2 -1
- data/lib/lutaml/model/validation_framework.rb +77 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +4 -0
- data/lib/lutaml/xml/adapter/nokogiri_adapter.rb +9 -2
- data/lib/lutaml/xml/adapter/oga_adapter.rb +11 -3
- data/lib/lutaml/xml/adapter/ox_adapter.rb +5 -2
- data/lib/lutaml/xml/adapter/rexml_adapter.rb +10 -3
- data/lib/lutaml/xml/adapter_element.rb +26 -2
- data/lib/lutaml/xml/data_model.rb +14 -0
- data/lib/lutaml/xml/document.rb +3 -0
- data/lib/lutaml/xml/element.rb +8 -2
- data/lib/lutaml/xml/mapping.rb +9 -0
- data/lib/lutaml/xml/model_transform.rb +42 -0
- data/lib/lutaml/xml/schema/xsd/base.rb +4 -1
- data/lib/lutaml/xml/serialization/instance_methods.rb +3 -1
- data/lib/lutaml/xml/transformation/ordered_applier.rb +46 -2
- data/lib/lutaml/xml/transformation.rb +40 -1
- data/lib/lutaml/xml/xml_element.rb +8 -7
- data/lutaml-model.gemspec +1 -1
- data/spec/lutaml/model/attribute_default_cache_spec.rb +58 -0
- data/spec/lutaml/model/liquefiable_spec.rb +22 -6
- data/spec/lutaml/model/liquid_compatibility_spec.rb +442 -0
- data/spec/lutaml/model/ordered_content_spec.rb +5 -5
- data/spec/lutaml/model/services/transformer_spec.rb +43 -0
- data/spec/lutaml/model/transform_cache_spec.rb +62 -0
- data/spec/lutaml/model/transform_dynamic_attributes_spec.rb +41 -0
- data/spec/lutaml/model/uninitialized_class_deep_dup_spec.rb +39 -0
- data/spec/lutaml/model/uninitialized_class_spec.rb +14 -2
- data/spec/lutaml/model/validation/concerns/has_issues_spec.rb +76 -0
- data/spec/lutaml/model/validation/context_spec.rb +60 -0
- data/spec/lutaml/model/validation/issue_spec.rb +77 -0
- data/spec/lutaml/model/validation/layer_result_spec.rb +66 -0
- data/spec/lutaml/model/validation/profile_spec.rb +134 -0
- data/spec/lutaml/model/validation/registry_spec.rb +94 -0
- data/spec/lutaml/model/validation/remediation_result_spec.rb +23 -0
- data/spec/lutaml/model/validation/remediation_spec.rb +72 -0
- data/spec/lutaml/model/validation/report_spec.rb +58 -0
- data/spec/lutaml/model/validation/rule_spec.rb +134 -0
- data/spec/lutaml/model/validation/uninitialized_class_validate_spec.rb +29 -0
- data/spec/lutaml/model/validation/validation_error_spec.rb +29 -0
- data/spec/lutaml/model/validation/validation_framework_spec.rb +110 -0
- data/spec/lutaml/xml/content_model_validation_spec.rb +157 -0
- data/spec/lutaml/xml/mapping_spec.rb +12 -7
- metadata +46 -7
- data/spec/fixtures/liquid_templates/_ceramics.liquid +0 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c512d678682ea756cb7eb7a3d439a7d511b763405936e598900f42f7f07aa705
|
|
4
|
+
data.tar.gz: e6623585797f809090619a9e6268e026773d962cac0f940ccae608627d8e714c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 81558e237a6a7f55c3defee45c8fa8a1d59bf75a875159708065ce8db44308fdaa17d3c727d6ff1074477391ec199008088221f54d5bbb7f2b3b4546ff4d25cf
|
|
7
|
+
data.tar.gz: 242730ac59628f328fe07409cc54d7798e75b40d29b6b47ad7382de37a033d9f907d0578d8ab950c45c9ff1b06cd67f6cb98e3e6215948149e63e8e4591be15d
|
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-
|
|
3
|
+
# on 2026-05-06 04:21:39 UTC using RuboCop version 1.86.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,15 +11,7 @@ Gemspec/RequiredRubyVersion:
|
|
|
11
11
|
Exclude:
|
|
12
12
|
- 'lutaml-model.gemspec'
|
|
13
13
|
|
|
14
|
-
# Offense count:
|
|
15
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
16
|
-
# Configuration parameters: AllowForAlignment.
|
|
17
|
-
Layout/CommentIndentation:
|
|
18
|
-
Exclude:
|
|
19
|
-
- 'lib/lutaml/model/mapping/listener.rb'
|
|
20
|
-
- 'lib/lutaml/model/services/base.rb'
|
|
21
|
-
|
|
22
|
-
# Offense count: 3210
|
|
14
|
+
# Offense count: 3232
|
|
23
15
|
# This cop supports safe autocorrection (--autocorrect).
|
|
24
16
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
|
|
25
17
|
# URISchemes: http, https
|
|
@@ -42,10 +34,16 @@ Lint/ConstantDefinitionInBlock:
|
|
|
42
34
|
Lint/DuplicateBranch:
|
|
43
35
|
Enabled: false
|
|
44
36
|
|
|
45
|
-
# Offense count:
|
|
37
|
+
# Offense count: 22
|
|
46
38
|
Lint/DuplicateMethods:
|
|
47
39
|
Exclude:
|
|
48
40
|
- 'lib/lutaml/xml/mapping.rb'
|
|
41
|
+
- 'spec/lutaml/model/liquid_compatibility_spec.rb'
|
|
42
|
+
- 'spec/lutaml/xml/namespace_no_hoisting_spec.rb'
|
|
43
|
+
- 'spec/lutaml/xml/namespace_scope_declare_spec.rb'
|
|
44
|
+
- 'spec/lutaml/xml/namespace_scope_vcard_spec.rb'
|
|
45
|
+
- 'spec/lutaml/xml/prefix_control_spec.rb'
|
|
46
|
+
- 'spec/lutaml/xml/type_namespace_examples_spec.rb'
|
|
49
47
|
|
|
50
48
|
# Offense count: 1
|
|
51
49
|
# This cop supports safe autocorrection (--autocorrect).
|
|
@@ -118,7 +116,7 @@ Lint/UselessConstantScoping:
|
|
|
118
116
|
- 'lib/lutaml/xml/adapter/nokogiri_adapter.rb'
|
|
119
117
|
- 'lib/lutaml/xml/mapping_rule.rb'
|
|
120
118
|
|
|
121
|
-
# Offense count:
|
|
119
|
+
# Offense count: 346
|
|
122
120
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
|
123
121
|
Metrics/AbcSize:
|
|
124
122
|
Enabled: false
|
|
@@ -134,23 +132,23 @@ Metrics/BlockLength:
|
|
|
134
132
|
Metrics/BlockNesting:
|
|
135
133
|
Max: 6
|
|
136
134
|
|
|
137
|
-
# Offense count:
|
|
135
|
+
# Offense count: 309
|
|
138
136
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
139
137
|
Metrics/CyclomaticComplexity:
|
|
140
138
|
Enabled: false
|
|
141
139
|
|
|
142
|
-
# Offense count:
|
|
140
|
+
# Offense count: 562
|
|
143
141
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
144
142
|
Metrics/MethodLength:
|
|
145
143
|
Max: 514
|
|
146
144
|
|
|
147
|
-
# Offense count:
|
|
145
|
+
# Offense count: 86
|
|
148
146
|
# Configuration parameters: CountKeywordArgs.
|
|
149
147
|
Metrics/ParameterLists:
|
|
150
148
|
Max: 24
|
|
151
149
|
MaxOptionalParameters: 5
|
|
152
150
|
|
|
153
|
-
# Offense count:
|
|
151
|
+
# Offense count: 260
|
|
154
152
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
155
153
|
Metrics/PerceivedComplexity:
|
|
156
154
|
Enabled: false
|
|
@@ -242,7 +240,7 @@ RSpec/BeforeAfterAll:
|
|
|
242
240
|
RSpec/ContextWording:
|
|
243
241
|
Enabled: false
|
|
244
242
|
|
|
245
|
-
# Offense count:
|
|
243
|
+
# Offense count: 88
|
|
246
244
|
# Configuration parameters: IgnoredMetadata.
|
|
247
245
|
RSpec/DescribeClass:
|
|
248
246
|
Enabled: false
|
|
@@ -253,7 +251,7 @@ RSpec/DescribeMethod:
|
|
|
253
251
|
- 'spec/lutaml/xml/schema/xsd/schema_helper_methods_spec.rb'
|
|
254
252
|
- 'spec/lutaml/xml/serializable_namespace_spec.rb'
|
|
255
253
|
|
|
256
|
-
# Offense count:
|
|
254
|
+
# Offense count: 1207
|
|
257
255
|
# Configuration parameters: CountAsOne.
|
|
258
256
|
RSpec/ExampleLength:
|
|
259
257
|
Max: 68
|
|
@@ -328,11 +326,11 @@ RSpec/MultipleDescribes:
|
|
|
328
326
|
- 'spec/lutaml/xml/namespace_resolution_strategy_spec.rb'
|
|
329
327
|
- 'spec/lutaml/xml/xml_space_type_spec.rb'
|
|
330
328
|
|
|
331
|
-
# Offense count:
|
|
329
|
+
# Offense count: 1383
|
|
332
330
|
RSpec/MultipleExpectations:
|
|
333
331
|
Max: 21
|
|
334
332
|
|
|
335
|
-
# Offense count:
|
|
333
|
+
# Offense count: 190
|
|
336
334
|
# Configuration parameters: AllowSubject.
|
|
337
335
|
RSpec/MultipleMemoizedHelpers:
|
|
338
336
|
Max: 15
|
|
@@ -376,13 +374,13 @@ RSpec/RepeatedExampleGroupDescription:
|
|
|
376
374
|
Exclude:
|
|
377
375
|
- 'spec/lutaml/model/mixed_content_spec.rb'
|
|
378
376
|
|
|
379
|
-
# Offense count:
|
|
377
|
+
# Offense count: 35
|
|
380
378
|
# Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector.
|
|
381
379
|
# SupportedInflectors: default, active_support
|
|
382
380
|
RSpec/SpecFilePathFormat:
|
|
383
381
|
Enabled: false
|
|
384
382
|
|
|
385
|
-
# Offense count:
|
|
383
|
+
# Offense count: 32
|
|
386
384
|
# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
|
|
387
385
|
RSpec/VerifiedDoubles:
|
|
388
386
|
Exclude:
|
|
@@ -393,6 +391,7 @@ RSpec/VerifiedDoubles:
|
|
|
393
391
|
- 'spec/lutaml/model/schema/renderer_spec.rb'
|
|
394
392
|
- 'spec/lutaml/model/transformation_builder_spec.rb'
|
|
395
393
|
- 'spec/lutaml/model/transformation_spec.rb'
|
|
394
|
+
- 'spec/lutaml/model/validation/context_spec.rb'
|
|
396
395
|
- 'spec/lutaml/xml/namespace_resolution_strategy_spec.rb'
|
|
397
396
|
|
|
398
397
|
# Offense count: 1
|
|
@@ -400,13 +399,14 @@ Security/MarshalLoad:
|
|
|
400
399
|
Exclude:
|
|
401
400
|
- 'scripts-xmi-profile/profile_xmi.rb'
|
|
402
401
|
|
|
403
|
-
# Offense count:
|
|
402
|
+
# Offense count: 2
|
|
404
403
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
405
404
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
|
406
405
|
# AllowedMethods: ==, equal?, eql?
|
|
407
406
|
Style/ClassEqualityComparison:
|
|
408
407
|
Exclude:
|
|
409
408
|
- 'lib/lutaml/model/attribute.rb'
|
|
409
|
+
- 'lib/lutaml/model/validation/profile.rb'
|
|
410
410
|
|
|
411
411
|
# Offense count: 13
|
|
412
412
|
# This cop supports safe autocorrection (--autocorrect).
|
data/README.adoc
CHANGED
|
@@ -66,6 +66,7 @@ link:docs/migration-guides/0-1-0-migrate-from-shale.adoc[Migrating from Shale to
|
|
|
66
66
|
* Parse XSD schemas into Ruby objects for inspection and manipulation (see <<xsd-schema-parsing>>)
|
|
67
67
|
* Reusable XML mapping classes for sharing mappings across models
|
|
68
68
|
* Consolidation mapping: group sibling XML elements into structured model instances (see <<consolidation-mapping>>)
|
|
69
|
+
* Document-level validation framework with composable rules, profiles, and remediation (see <<document-validation-framework>>)
|
|
69
70
|
|
|
70
71
|
|
|
71
72
|
== Data modeling in a nutshell
|
|
@@ -6349,7 +6350,8 @@ end
|
|
|
6349
6350
|
====
|
|
6350
6351
|
When a model has `mixed_content`, use `map_content` with `collection: true`
|
|
6351
6352
|
to capture the multiple text segments between child elements.
|
|
6352
|
-
Without `collection: true`,
|
|
6353
|
+
Without `collection: true`, a `MixedContentCollectionError` is raised at
|
|
6354
|
+
finalization time.
|
|
6353
6355
|
====
|
|
6354
6356
|
|
|
6355
6357
|
.Complete round-trip with `mixed_content` and `map_content collection: true`
|
|
@@ -6498,6 +6500,51 @@ Without `mixed_content`, serialization groups all text first then all elements
|
|
|
6498
6500
|
(or vice versa), losing the original interleaving. This is the most common
|
|
6499
6501
|
cause of XML round-trip failures in document-processing applications.
|
|
6500
6502
|
|
|
6503
|
+
===== XML Comment round-trip preservation
|
|
6504
|
+
|
|
6505
|
+
XML comments (`<!-- ... -->`) are preserved during round-trip serialization
|
|
6506
|
+
in `mixed_content` and `ordered` modes. Comments appear in `element_order`
|
|
6507
|
+
as entries with `node_type: :comment` and are reconstructed during output.
|
|
6508
|
+
|
|
6509
|
+
.Comment round-trip in mixed content
|
|
6510
|
+
[example]
|
|
6511
|
+
====
|
|
6512
|
+
[source,ruby]
|
|
6513
|
+
----
|
|
6514
|
+
class Paragraph < Lutaml::Model::Serializable
|
|
6515
|
+
attribute :content, :string, collection: true
|
|
6516
|
+
|
|
6517
|
+
xml do
|
|
6518
|
+
element 'p'
|
|
6519
|
+
mixed_content
|
|
6520
|
+
map_content to: :content
|
|
6521
|
+
end
|
|
6522
|
+
end
|
|
6523
|
+
----
|
|
6524
|
+
|
|
6525
|
+
Input XML with a comment between text and an element:
|
|
6526
|
+
|
|
6527
|
+
[source,xml]
|
|
6528
|
+
----
|
|
6529
|
+
<p>Text before<!-- a comment --> text after</p>
|
|
6530
|
+
----
|
|
6531
|
+
|
|
6532
|
+
[source,ruby]
|
|
6533
|
+
----
|
|
6534
|
+
parsed = Paragraph.from_xml(xml)
|
|
6535
|
+
parsed.element_order
|
|
6536
|
+
# => [#<Lutaml::Xml::Element type: "Text", node_type: :text>,
|
|
6537
|
+
# #<Lutaml::Xml::Element type: "Comment", node_type: :comment, text_content: " a comment ">,
|
|
6538
|
+
# #<Lutaml::Xml::Element type: "Text", node_type: :text>]
|
|
6539
|
+
|
|
6540
|
+
parsed.to_xml
|
|
6541
|
+
# => "<p>Text before<!-- a comment --> text after</p>"
|
|
6542
|
+
----
|
|
6543
|
+
====
|
|
6544
|
+
|
|
6545
|
+
NOTE: Comments are tracked in `element_order` but are **not** included in
|
|
6546
|
+
model attributes or the parsed hash. They exist solely for serialization fidelity.
|
|
6547
|
+
|
|
6501
6548
|
==== (DEPRECATED) Mixed content declaration (`root` method with `mixed:`)
|
|
6502
6549
|
|
|
6503
6550
|
To map this to Lutaml::Model we can use the `mixed` option in either way:
|
|
@@ -6558,6 +6605,13 @@ preserves the order of **XML Elements and Content**.
|
|
|
6558
6605
|
|
|
6559
6606
|
NOTE: When both options are used, `mixed: true` takes precedence.
|
|
6560
6607
|
|
|
6608
|
+
[IMPORTANT]
|
|
6609
|
+
====
|
|
6610
|
+
`ordered` models element-only content -- `map_content` is not allowed.
|
|
6611
|
+
If you need to capture text content between elements, use `mixed_content`
|
|
6612
|
+
instead (see <<mixed-content>>).
|
|
6613
|
+
====
|
|
6614
|
+
|
|
6561
6615
|
To specify ordered content, the `ordered: true` option needs to be set at the
|
|
6562
6616
|
`xml` block's `root` method.
|
|
6563
6617
|
|
|
@@ -16938,6 +16992,164 @@ end
|
|
|
16938
16992
|
For a complete architecture overview and migration guide from Register to
|
|
16939
16993
|
GlobalContext, see link:docs/architecture.md[Architecture Documentation].
|
|
16940
16994
|
|
|
16995
|
+
[[document-validation-framework]]
|
|
16996
|
+
== Document Validation Framework
|
|
16997
|
+
|
|
16998
|
+
Lutaml::Model provides a document-level validation framework (`Lutaml::Model::Validation`)
|
|
16999
|
+
for validating structural integrity, cross-references, and conformance against
|
|
17000
|
+
domain-specific rules. This is *orthogonal* to the existing attribute-level
|
|
17001
|
+
validation (`validate`/`validate!` on model instances) -- that validates type
|
|
17002
|
+
constraints, enumerations, and collections; this validates document-level concerns.
|
|
17003
|
+
|
|
17004
|
+
=== Quick start
|
|
17005
|
+
|
|
17006
|
+
[source,ruby]
|
|
17007
|
+
----
|
|
17008
|
+
require "lutaml/model/validation_framework"
|
|
17009
|
+
|
|
17010
|
+
# 1. Define rules by subclassing Lutaml::Model::Validation::Rule
|
|
17011
|
+
class RequiredFieldsRule < Lutaml::Model::Validation::Rule
|
|
17012
|
+
def code = "DOC-001"
|
|
17013
|
+
def severity = "error"
|
|
17014
|
+
|
|
17015
|
+
def check(context)
|
|
17016
|
+
missing = required_fields.select { |f| context[f].nil? || context[f].empty? }
|
|
17017
|
+
missing.map { |f| issue("Missing required field: #{f}") }
|
|
17018
|
+
end
|
|
17019
|
+
|
|
17020
|
+
private
|
|
17021
|
+
|
|
17022
|
+
def required_fields = %w[title author]
|
|
17023
|
+
end
|
|
17024
|
+
|
|
17025
|
+
# 2. Register rules
|
|
17026
|
+
registry = Lutaml::Model::Validation.new_registry
|
|
17027
|
+
registry.register(RequiredFieldsRule)
|
|
17028
|
+
|
|
17029
|
+
# 3. Run validation
|
|
17030
|
+
issues = Lutaml::Model::Validation.validate({ title: "Hello" }, registry)
|
|
17031
|
+
issues.each { |i| puts "[#{i.severity}] #{i.code}: #{i.message}" }
|
|
17032
|
+
# => [error] DOC-001: Missing required field: author
|
|
17033
|
+
|
|
17034
|
+
# 4. Or raise on errors
|
|
17035
|
+
begin
|
|
17036
|
+
Lutaml::Model::Validation.validate!({ title: "Hello" }, registry)
|
|
17037
|
+
rescue Lutaml::Model::Validation::ValidationError => e
|
|
17038
|
+
puts e.message # => "[DOC-001] Missing required field: author"
|
|
17039
|
+
e.issues # => [#<Issue code="DOC-001">]
|
|
17040
|
+
end
|
|
17041
|
+
----
|
|
17042
|
+
|
|
17043
|
+
=== Core components
|
|
17044
|
+
|
|
17045
|
+
[cols="1,4"]
|
|
17046
|
+
|===
|
|
17047
|
+
| Class | Purpose
|
|
17048
|
+
|
|
17049
|
+
| `Validation::Issue`
|
|
17050
|
+
| Serializable issue with severity (`error`/`warning`/`info`/`notice`), code, message, location, line, suggestion
|
|
17051
|
+
|
|
17052
|
+
| `Validation::Rule`
|
|
17053
|
+
| Abstract base class. Override `code`, `severity`, `category`, `applicable?`, and `check`
|
|
17054
|
+
|
|
17055
|
+
| `Validation::Registry`
|
|
17056
|
+
| Instance-based rule registration with cached instantiation via `register`, `all`, `for_category`, `find`
|
|
17057
|
+
|
|
17058
|
+
| `Validation::Profile`
|
|
17059
|
+
| YAML composable profiles with import resolution for rule subsets. `load` validates required `name` key
|
|
17060
|
+
|
|
17061
|
+
| `Validation::Context`
|
|
17062
|
+
| Mutable context with error accumulation and per-rule state (for streaming validation)
|
|
17063
|
+
|
|
17064
|
+
| `Validation::Report` / `LayerResult`
|
|
17065
|
+
| Serializable report models with aggregated severity filtering
|
|
17066
|
+
|
|
17067
|
+
| `Validation::Remediation`
|
|
17068
|
+
| Abstract base for auto-fix logic. Subclass must override `fix` (raises `NotImplementedError` on base class)
|
|
17069
|
+
|===
|
|
17070
|
+
|
|
17071
|
+
=== Composable profiles
|
|
17072
|
+
|
|
17073
|
+
Profiles select *which* rules run, not *how* they run. They are loaded from YAML
|
|
17074
|
+
and support import-based composition.
|
|
17075
|
+
|
|
17076
|
+
[source,yaml]
|
|
17077
|
+
----
|
|
17078
|
+
# profiles/basic.yml
|
|
17079
|
+
name: basic
|
|
17080
|
+
description: Basic DOCX validation
|
|
17081
|
+
rules:
|
|
17082
|
+
- RequiredFieldsRule
|
|
17083
|
+
- StyleReferencesRule
|
|
17084
|
+
----
|
|
17085
|
+
|
|
17086
|
+
[source,yaml]
|
|
17087
|
+
----
|
|
17088
|
+
# profiles/strict.yml
|
|
17089
|
+
name: strict
|
|
17090
|
+
import:
|
|
17091
|
+
- basic
|
|
17092
|
+
rules:
|
|
17093
|
+
- BookmarksRule
|
|
17094
|
+
- ImagesRule
|
|
17095
|
+
----
|
|
17096
|
+
|
|
17097
|
+
[source,ruby]
|
|
17098
|
+
----
|
|
17099
|
+
registry = Lutaml::Model::Validation.new_registry
|
|
17100
|
+
registry.register(RequiredFieldsRule)
|
|
17101
|
+
registry.register(StyleReferencesRule)
|
|
17102
|
+
registry.register(BookmarksRule)
|
|
17103
|
+
registry.register(ImagesRule)
|
|
17104
|
+
|
|
17105
|
+
basic = Lutaml::Model::Validation::Profile.load("profiles/basic.yml")
|
|
17106
|
+
strict = Lutaml::Model::Validation::Profile.load("profiles/strict.yml")
|
|
17107
|
+
profiles = { "basic" => basic, "strict" => strict }
|
|
17108
|
+
|
|
17109
|
+
rules = strict.resolve(registry, profiles) # returns all 4 rule instances
|
|
17110
|
+
|
|
17111
|
+
// Circular imports are detected and raise ArgumentError.
|
|
17112
|
+
// Profile YAML is validated: missing `name` key raises ArgumentError.
|
|
17113
|
+
----
|
|
17114
|
+
|
|
17115
|
+
=== Remediation (auto-fix)
|
|
17116
|
+
|
|
17117
|
+
Subclass `Validation::Remediation` to provide automatic fixes for specific issues:
|
|
17118
|
+
|
|
17119
|
+
[source,ruby]
|
|
17120
|
+
----
|
|
17121
|
+
class FixBrokenReferences < Lutaml::Model::Validation::Remediation
|
|
17122
|
+
def id = "REM-001"
|
|
17123
|
+
def targets = ["DOC-020"]
|
|
17124
|
+
|
|
17125
|
+
def fix(context, report)
|
|
17126
|
+
# ... apply fixes ...
|
|
17127
|
+
Lutaml::Model::Validation::RemediationResult.new(
|
|
17128
|
+
success: true,
|
|
17129
|
+
message: "Fixed 3 broken references",
|
|
17130
|
+
fixed_codes: ["DOC-020"]
|
|
17131
|
+
)
|
|
17132
|
+
end
|
|
17133
|
+
end
|
|
17134
|
+
----
|
|
17135
|
+
|
|
17136
|
+
=== Relationship to attribute-level validation
|
|
17137
|
+
|
|
17138
|
+
|===
|
|
17139
|
+
| Attribute validation (existing) | Document validation framework (new)
|
|
17140
|
+
|
|
17141
|
+
| Validates model instance state (types, ranges, patterns)
|
|
17142
|
+
| Validates document-level concerns (structure, cross-references, conformance)
|
|
17143
|
+
|
|
17144
|
+
| Runs during deserialization via `model.validate`
|
|
17145
|
+
| Runs on parsed documents against domain rules via `Validation.validate`
|
|
17146
|
+
|
|
17147
|
+
| Returns `Array<Error>` error objects
|
|
17148
|
+
| Returns `Array<Issue>` serializable issue objects
|
|
17149
|
+
|===
|
|
17150
|
+
|
|
17151
|
+
See link:docs/_pages/validation.adoc[Validation] for attribute-level validation details.
|
|
17152
|
+
|
|
16941
17153
|
|
|
16942
17154
|
== Comparison with Shale
|
|
16943
17155
|
|