lutaml-xsd 1.1.13 → 1.2.0
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/.gitignore +0 -1
- data/CLAUDE.md +18 -1
- data/Gemfile +2 -1
- data/Gemfile.lock +327 -0
- data/Rakefile +78 -1
- data/TODO.VISION.md +123 -0
- data/TODO.cleanup.md +43 -0
- data/TODO.conformance.md +99 -0
- data/TODO.docs-spa.md +46 -0
- data/TODO.interop.md +52 -0
- data/TODO.performance.md +80 -0
- data/TODO.som.md +47 -0
- data/benchmark/engines.rb +77 -0
- data/config/validation.yml +4 -0
- data/docs/CONFORMANCE.adoc +43 -0
- data/docs/JSON_SCHEMA.adoc +42 -0
- data/docs/superpowers/plans/2026-10-20-moxml-leptris-migration.md +735 -0
- data/docs/superpowers/specs/2026-10-20-moxml-leptris-migration-design.md +169 -0
- data/docs/superpowers/specs/2026-10-21-rule-engine-wiring-design.md +84 -0
- data/lib/lutaml/xsd/conformance/runner.rb +169 -0
- data/lib/lutaml/xsd/json_schema_exporter.rb +240 -0
- data/lib/lutaml/xsd/package_builder.rb +2 -2
- data/lib/lutaml/xsd/spa/schema_serializer.rb +12 -7
- data/lib/lutaml/xsd/validation/engine_context.rb +190 -0
- data/lib/lutaml/xsd/validation/rule_engine.rb +36 -18
- data/lib/lutaml/xsd/validation/rule_registry.rb +34 -0
- data/lib/lutaml/xsd/validation/rules/attribute_validation_rule.rb +126 -17
- data/lib/lutaml/xsd/validation/rules/content_model_validation_rule.rb +304 -194
- data/lib/lutaml/xsd/validation/rules/element_structure_rule.rb +25 -15
- data/lib/lutaml/xsd/validation/rules/identity_constraints_rule.rb +134 -0
- data/lib/lutaml/xsd/validation/rules/occurrence_validation_rule.rb +76 -25
- data/lib/lutaml/xsd/validation/rules/type_validation_rule.rb +134 -17
- data/lib/lutaml/xsd/validation/schema_definition_resolver.rb +254 -0
- data/lib/lutaml/xsd/validation/validation_configuration.rb +1 -0
- data/lib/lutaml/xsd/validation/validation_job.rb +70 -3
- data/lib/lutaml/xsd/validation/validation_rule.rb +44 -5
- data/lib/lutaml/xsd/validation/validator.rb +2 -1
- data/lib/lutaml/xsd/validation/xml_element.rb +2 -2
- data/lib/lutaml/xsd/version.rb +1 -1
- data/lib/lutaml/xsd.rb +14 -0
- data/lutaml-xsd.gemspec +2 -2
- metadata +27 -8
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Xsd
|
|
5
|
+
module Validation
|
|
6
|
+
# Resolves instance elements to their schema declarations for the
|
|
7
|
+
# rule engine's element-scoped dispatch.
|
|
8
|
+
#
|
|
9
|
+
# Indexes top-level and inline element declarations across the
|
|
10
|
+
# repository's schemas. Namespace matching is strict (a target
|
|
11
|
+
# namespace matches only itself; no-namespace schemas match
|
|
12
|
+
# no-namespace instances) with a unique-name fallback for corpora
|
|
13
|
+
# whose instances omit namespaces.
|
|
14
|
+
#
|
|
15
|
+
# Deliberately not resolved here (rule-engine backlog): dynamic
|
|
16
|
+
# typing via xsi:type and derivation closure.
|
|
17
|
+
class SchemaDefinitionResolver
|
|
18
|
+
PARTICLE_KINDS = %i[sequence choice all].freeze
|
|
19
|
+
|
|
20
|
+
def initialize(repository)
|
|
21
|
+
@repository = repository
|
|
22
|
+
@namespaces = {}.compare_by_identity
|
|
23
|
+
@attributes = Hash.new { |hash, key| hash[key] = [] }
|
|
24
|
+
@elements = index_elements
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Owner schema's target namespace for a resolved declaration
|
|
28
|
+
# (parsed element models carry no owner-schema link).
|
|
29
|
+
#
|
|
30
|
+
# @param schema_element [Object]
|
|
31
|
+
# @return [String, nil]
|
|
32
|
+
def target_namespace_for(schema_element)
|
|
33
|
+
@namespaces[schema_element]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @param name [String] local element name
|
|
37
|
+
# @param namespace_uri [String, nil] instance element namespace
|
|
38
|
+
# @return [Lutaml::Xml::Schema::Xsd::Element, nil]
|
|
39
|
+
def resolve(name, namespace_uri)
|
|
40
|
+
return nil if name.nil? || name.empty?
|
|
41
|
+
|
|
42
|
+
candidates = @elements[name]
|
|
43
|
+
return nil if candidates.empty?
|
|
44
|
+
|
|
45
|
+
exact = candidates.find do |_element, namespace, _kind|
|
|
46
|
+
namespace_matches?(namespace, namespace_uri)
|
|
47
|
+
end
|
|
48
|
+
return exact&.first if exact
|
|
49
|
+
|
|
50
|
+
# Unique-name fallback for instances that omit namespaces even
|
|
51
|
+
# though the schema declares one. A foreign namespace never
|
|
52
|
+
# falls back.
|
|
53
|
+
return nil unless namespace_uri.to_s.empty?
|
|
54
|
+
|
|
55
|
+
candidates.length == 1 ? candidates.first.first : nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Named type for an element's `type="prefix:Name"` reference.
|
|
59
|
+
#
|
|
60
|
+
# @param schema_element [Lutaml::Xml::Schema::Xsd::Element]
|
|
61
|
+
# @return [Object, nil]
|
|
62
|
+
def resolve_type(schema_element)
|
|
63
|
+
reference = schema_element.type
|
|
64
|
+
return nil if reference.nil? || reference.to_s.empty?
|
|
65
|
+
|
|
66
|
+
resolve_named_type(reference.to_s.split(":").last)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Named complex or simple type by local name.
|
|
70
|
+
#
|
|
71
|
+
# @param local_name [String]
|
|
72
|
+
# @return [Object, nil]
|
|
73
|
+
def resolve_named_type(local_name)
|
|
74
|
+
return nil if local_name.nil? || local_name.empty?
|
|
75
|
+
|
|
76
|
+
schemas.each do |schema|
|
|
77
|
+
found = Array(schema.complex_type).find { |ct| ct.name == local_name }
|
|
78
|
+
return found if found
|
|
79
|
+
|
|
80
|
+
found = Array(schema.simple_type).find { |st| st.name == local_name }
|
|
81
|
+
return found if found
|
|
82
|
+
end
|
|
83
|
+
nil
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Global attribute declaration by name (for strict xs:anyAttribute
|
|
87
|
+
# checks).
|
|
88
|
+
#
|
|
89
|
+
# @param name [String] local attribute name
|
|
90
|
+
# @param namespace_uri [String, nil] instance attribute namespace
|
|
91
|
+
# @return [Lutaml::Xml::Schema::Xsd::Attribute, nil]
|
|
92
|
+
def resolve_attribute(name, namespace_uri)
|
|
93
|
+
return nil if name.nil? || name.empty?
|
|
94
|
+
|
|
95
|
+
candidates = @attributes[name]
|
|
96
|
+
return nil if candidates.empty?
|
|
97
|
+
|
|
98
|
+
exact = candidates.find do |_attribute, namespace|
|
|
99
|
+
namespace_matches?(namespace, namespace_uri)
|
|
100
|
+
end
|
|
101
|
+
return exact&.first if exact
|
|
102
|
+
|
|
103
|
+
return nil unless namespace_uri.to_s.empty?
|
|
104
|
+
|
|
105
|
+
candidates.length == 1 ? candidates.first.first : nil
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Content-model particle of the element's type, for occurrence
|
|
109
|
+
# and content-model rules.
|
|
110
|
+
#
|
|
111
|
+
# @param schema_element [Lutaml::Xml::Schema::Xsd::Element]
|
|
112
|
+
# @return [Object, nil]
|
|
113
|
+
def content_model_for(schema_element)
|
|
114
|
+
type = schema_element.complex_type || resolve_type(schema_element)
|
|
115
|
+
particle_for_type(type)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Content-model particle of a NAMED type (for xsi:type overrides).
|
|
119
|
+
#
|
|
120
|
+
# @param local_name [String]
|
|
121
|
+
# @return [Object, nil]
|
|
122
|
+
def content_model_for_named(local_name)
|
|
123
|
+
particle_for_type(resolve_named_type(local_name))
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def particle_for_type(type)
|
|
129
|
+
return nil unless type.respond_to?(:sequence) || type.respond_to?(:choice) || type.respond_to?(:all)
|
|
130
|
+
|
|
131
|
+
direct = Array(type.sequence).first ||
|
|
132
|
+
Array(type.choice).first || Array(type.all).first
|
|
133
|
+
return direct if direct
|
|
134
|
+
|
|
135
|
+
# Derived types keep their particles under complex_content.
|
|
136
|
+
if type.respond_to?(:complex_content) && type.complex_content
|
|
137
|
+
content = type.complex_content
|
|
138
|
+
part = Array(content.extension).first ||
|
|
139
|
+
Array(content.restriction).first
|
|
140
|
+
return particle_for_type(part) if part
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
nil
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def schemas
|
|
147
|
+
@schemas ||= @repository.all_schemas.values
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def namespace_matches?(schema_namespace, instance_namespace)
|
|
151
|
+
schema_namespace.to_s.empty? == instance_namespace.to_s.empty? &&
|
|
152
|
+
schema_namespace.to_s == instance_namespace.to_s
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def index_elements
|
|
156
|
+
index = Hash.new { |hash, key| hash[key] = [] }
|
|
157
|
+
schemas.each do |schema|
|
|
158
|
+
index_schema(index, schema)
|
|
159
|
+
rescue StandardError
|
|
160
|
+
# Repositories can carry unparsable entries (broken packages);
|
|
161
|
+
# they contribute no declarations.
|
|
162
|
+
next
|
|
163
|
+
end
|
|
164
|
+
# Prefer top-level declarations over inline ones.
|
|
165
|
+
index.each_value do |candidates|
|
|
166
|
+
candidates.sort_by! { |_element, _ns, kind| kind == :top ? 0 : 1 }
|
|
167
|
+
end
|
|
168
|
+
index
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def index_schema(index, schema)
|
|
172
|
+
namespace = schema.target_namespace
|
|
173
|
+
Array(schema.element).each do |element|
|
|
174
|
+
index_element(index, element, namespace, :top)
|
|
175
|
+
end
|
|
176
|
+
Array(schema.complex_type).each do |complex_type|
|
|
177
|
+
index_type(index, complex_type, namespace)
|
|
178
|
+
end
|
|
179
|
+
Array(schema.attribute).each do |attribute|
|
|
180
|
+
index_attribute(attribute, namespace)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def index_attribute(attribute, namespace)
|
|
185
|
+
return unless attribute
|
|
186
|
+
return if attribute.name.nil? || attribute.name.empty?
|
|
187
|
+
|
|
188
|
+
@namespaces[attribute] = namespace
|
|
189
|
+
@attributes[attribute.name] << [attribute, namespace]
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def index_element(index, element, namespace, kind)
|
|
193
|
+
return unless element
|
|
194
|
+
|
|
195
|
+
@namespaces[element] = namespace
|
|
196
|
+
unless element.name.nil? || element.name.empty?
|
|
197
|
+
index[element.name] << [element, namespace, kind]
|
|
198
|
+
end
|
|
199
|
+
index_type(index, element.complex_type, namespace) if element.complex_type
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def index_type(index, complex_type, namespace)
|
|
203
|
+
return unless complex_type
|
|
204
|
+
|
|
205
|
+
each_particle_element(complex_type) do |element|
|
|
206
|
+
index_element(index, element, namespace, :inline)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def each_particle_element(node, &)
|
|
211
|
+
return unless node
|
|
212
|
+
|
|
213
|
+
if node.is_a?(Array)
|
|
214
|
+
node.each { |child| each_particle_element(child, &) }
|
|
215
|
+
return
|
|
216
|
+
end
|
|
217
|
+
return if visited_particle?(node)
|
|
218
|
+
|
|
219
|
+
PARTICLE_KINDS.each do |kind|
|
|
220
|
+
next unless node.respond_to?(kind) # optional particle collections
|
|
221
|
+
|
|
222
|
+
Array(node.public_send(kind)).each do |particle|
|
|
223
|
+
next unless particle
|
|
224
|
+
|
|
225
|
+
Array(particle.element).each(&)
|
|
226
|
+
PARTICLE_KINDS.each do |nested|
|
|
227
|
+
next unless particle.respond_to?(nested)
|
|
228
|
+
|
|
229
|
+
each_particle_element(particle.public_send(nested), &)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
return unless node.respond_to?(:complex_content) && node.complex_content
|
|
235
|
+
|
|
236
|
+
%i[extension restriction].each do |kind|
|
|
237
|
+
part = node.complex_content.public_send(kind)
|
|
238
|
+
each_particle_element(part, &) if part
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Particle collections can be cyclic in parsed models (a nested
|
|
243
|
+
# sequence pointing back at its container); visit each node once.
|
|
244
|
+
def visited_particle?(node)
|
|
245
|
+
@visited ||= {}.compare_by_identity
|
|
246
|
+
return true if @visited[node]
|
|
247
|
+
|
|
248
|
+
@visited[node] = true
|
|
249
|
+
false
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
@@ -48,9 +48,10 @@ module Lutaml
|
|
|
48
48
|
#
|
|
49
49
|
# Performs validation in the following order:
|
|
50
50
|
# 1. Parse XML document
|
|
51
|
-
# 2.
|
|
52
|
-
# 3. Validate
|
|
53
|
-
# 4. Validate
|
|
51
|
+
# 2. Engine-native XSD validation (when the adapter provides one)
|
|
52
|
+
# 3. Validate structure (elements, namespaces)
|
|
53
|
+
# 4. Validate types (simple and complex types)
|
|
54
|
+
# 5. Validate constraints (occurrences, identity constraints)
|
|
54
55
|
#
|
|
55
56
|
# @return [ValidationResult] The complete validation result
|
|
56
57
|
def execute
|
|
@@ -59,6 +60,8 @@ module Lutaml
|
|
|
59
60
|
parse_xml
|
|
60
61
|
return result_from_collector if should_stop_after_parse?
|
|
61
62
|
|
|
63
|
+
validate_root_declaration
|
|
64
|
+
run_engine_validation if @config.feature_enabled?(:engine_validation)
|
|
62
65
|
validate_structure if @config.feature_enabled?(:validate_types)
|
|
63
66
|
return result_from_collector if should_stop_after_structure?
|
|
64
67
|
|
|
@@ -68,11 +71,75 @@ module Lutaml
|
|
|
68
71
|
validate_constraints if @config.feature_enabled?(:validate_occurrences)
|
|
69
72
|
return result_from_collector if should_stop_after_constraints?
|
|
70
73
|
|
|
74
|
+
validate_identity if @config.feature_enabled?(:validate_identity_constraints)
|
|
75
|
+
|
|
71
76
|
result_from_collector
|
|
72
77
|
end
|
|
73
78
|
|
|
74
79
|
private
|
|
75
80
|
|
|
81
|
+
# An instance whose root element is not declared by any schema in
|
|
82
|
+
# the package can never be valid; surface it explicitly (the
|
|
83
|
+
# engine's root-matching stays silent for unknown roots, and the
|
|
84
|
+
# rule engine skips unresolved declarations by design).
|
|
85
|
+
#
|
|
86
|
+
# @return [void]
|
|
87
|
+
def validate_root_declaration
|
|
88
|
+
root = @document&.root_element
|
|
89
|
+
return unless root
|
|
90
|
+
|
|
91
|
+
resolver = SchemaDefinitionResolver.new(@repository)
|
|
92
|
+
return if resolver.resolve(root.name, root.namespace_uri)
|
|
93
|
+
|
|
94
|
+
@result_collector.add_error(
|
|
95
|
+
ValidationError.new(
|
|
96
|
+
code: "element_not_allowed",
|
|
97
|
+
message: "Root element '#{root.name}' is not declared by any " \
|
|
98
|
+
"schema in the package",
|
|
99
|
+
severity: :error,
|
|
100
|
+
location: root.xpath,
|
|
101
|
+
context: {
|
|
102
|
+
element: root.name,
|
|
103
|
+
namespace: root.namespace_uri,
|
|
104
|
+
},
|
|
105
|
+
),
|
|
106
|
+
)
|
|
107
|
+
rescue ResultCollector::StopValidationError
|
|
108
|
+
# Stop validation if configured to stop on first error
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Validate identity constraints (key/keyref/unique)
|
|
112
|
+
#
|
|
113
|
+
# @return [void]
|
|
114
|
+
def validate_identity
|
|
115
|
+
return unless @document && @rule_engine
|
|
116
|
+
|
|
117
|
+
@rule_engine.execute_category(:identity, @document, @repository)
|
|
118
|
+
rescue ResultCollector::StopValidationError
|
|
119
|
+
# Stop validation if configured to stop on first error
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Run engine-native XSD validation (Moxml::XSD) when the adapter
|
|
123
|
+
# supports it. Findings are additive to rule findings.
|
|
124
|
+
#
|
|
125
|
+
# @return [void]
|
|
126
|
+
def run_engine_validation
|
|
127
|
+
@engine_context ||= EngineContext.compile(@repository)
|
|
128
|
+
return unless @engine_context&.supported?
|
|
129
|
+
|
|
130
|
+
@engine_context.findings_for(@xml_content).each do |finding|
|
|
131
|
+
@result_collector.add_error(finding)
|
|
132
|
+
end
|
|
133
|
+
rescue StandardError => e
|
|
134
|
+
@result_collector.add_warning(
|
|
135
|
+
ValidationError.new(
|
|
136
|
+
code: "engine_validation_unavailable",
|
|
137
|
+
message: "Engine validation skipped: #{e.message}",
|
|
138
|
+
severity: :warning,
|
|
139
|
+
),
|
|
140
|
+
)
|
|
141
|
+
end
|
|
142
|
+
|
|
76
143
|
# Parse the XML content into a document object
|
|
77
144
|
#
|
|
78
145
|
# @return [void]
|
|
@@ -41,21 +41,60 @@ module Lutaml
|
|
|
41
41
|
|
|
42
42
|
# Execute the validation rule
|
|
43
43
|
#
|
|
44
|
-
# This is the main entry point for validation.
|
|
45
|
-
#
|
|
44
|
+
# This is the main entry point for validation. Rules are
|
|
45
|
+
# element-scoped: the engine calls this once per instance element
|
|
46
|
+
# with the schema argument selected by {#schema_argument}.
|
|
46
47
|
#
|
|
47
48
|
# @abstract
|
|
48
|
-
# @param
|
|
49
|
-
# @param
|
|
49
|
+
# @param xml_element [XmlElement] The instance element to validate
|
|
50
|
+
# @param schema_argument [Object, nil] What this rule validates
|
|
51
|
+
# against (schema element by default; see {#schema_argument})
|
|
50
52
|
# @param collector [ResultCollector] Collector for validation results
|
|
53
|
+
# @param repository [SchemaDefinitionResolver, nil] Named-type
|
|
54
|
+
# resolution for `type="prefix:Name"` references
|
|
51
55
|
# @return [void]
|
|
52
56
|
#
|
|
53
57
|
# @raise [NotImplementedError] if not implemented by subclass
|
|
54
|
-
def validate(
|
|
58
|
+
def validate(xml_element, schema_argument, collector, repository: nil)
|
|
55
59
|
raise NotImplementedError,
|
|
56
60
|
"#{self.class} must implement #validate"
|
|
57
61
|
end
|
|
58
62
|
|
|
63
|
+
# The schema object this rule validates the element against.
|
|
64
|
+
#
|
|
65
|
+
# Defaults to the resolved schema element; rules that need a
|
|
66
|
+
# different shape override this (e.g. the occurrence rule wants
|
|
67
|
+
# the content-model particle).
|
|
68
|
+
#
|
|
69
|
+
# @param schema_def [Object, nil] Resolved schema declaration
|
|
70
|
+
# @param repository [SchemaDefinitionResolver, nil]
|
|
71
|
+
# @return [Object, nil]
|
|
72
|
+
def schema_argument(schema_def, repository: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
73
|
+
# `repository` is part of the contract; the default selection
|
|
74
|
+
# does not consult it.
|
|
75
|
+
schema_def
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Whether the rule still runs when no schema declaration resolves.
|
|
79
|
+
#
|
|
80
|
+
# @return [Boolean]
|
|
81
|
+
def runs_without_schema?
|
|
82
|
+
false
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance"
|
|
86
|
+
|
|
87
|
+
# Find an xsi:-namespaced attribute on the instance element.
|
|
88
|
+
#
|
|
89
|
+
# @param xml_element [XmlElement]
|
|
90
|
+
# @param name [String] local name (e.g. "nil", "type")
|
|
91
|
+
# @return [XmlAttribute, nil]
|
|
92
|
+
def xsi_attribute(xml_element, name)
|
|
93
|
+
xml_element.attributes.find do |attr|
|
|
94
|
+
attr.name == name && attr.namespace_uri == XSI_NAMESPACE
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
59
98
|
# Get the category of this validation rule
|
|
60
99
|
#
|
|
61
100
|
# Categories are used to group rules and execute them in phases:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "engine_context"
|
|
3
4
|
require_relative "validation_configuration"
|
|
4
5
|
require_relative "validation_job"
|
|
5
6
|
require_relative "validation_result"
|
|
@@ -118,7 +119,7 @@ module Lutaml
|
|
|
118
119
|
# @return [RuleRegistry] The initialized rule registry
|
|
119
120
|
def build_rule_registry
|
|
120
121
|
require_relative "rule_registry"
|
|
121
|
-
Validation::RuleRegistry.
|
|
122
|
+
Validation::RuleRegistry.default(@config)
|
|
122
123
|
end
|
|
123
124
|
end
|
|
124
125
|
end
|
|
@@ -42,7 +42,7 @@ module Lutaml
|
|
|
42
42
|
#
|
|
43
43
|
# @return [String, nil]
|
|
44
44
|
def namespace_uri
|
|
45
|
-
@moxml_element.namespace&.
|
|
45
|
+
@moxml_element.namespace&.uri
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
# Get the element's namespace prefix
|
|
@@ -84,7 +84,7 @@ module Lutaml
|
|
|
84
84
|
XmlAttribute.new(
|
|
85
85
|
attr.name,
|
|
86
86
|
attr.value,
|
|
87
|
-
attr.namespace&.
|
|
87
|
+
attr.namespace&.uri,
|
|
88
88
|
attr.namespace&.prefix,
|
|
89
89
|
)
|
|
90
90
|
end
|
data/lib/lutaml/xsd/version.rb
CHANGED
data/lib/lutaml/xsd.rb
CHANGED
|
@@ -60,3 +60,17 @@ require_relative "xsd/schema_dependency_analyzer"
|
|
|
60
60
|
require_relative "xsd/type_hierarchy_analyzer"
|
|
61
61
|
require_relative "xsd/xsd_spec_validator"
|
|
62
62
|
require_relative "xsd/cli"
|
|
63
|
+
|
|
64
|
+
module Lutaml
|
|
65
|
+
module Xsd
|
|
66
|
+
# Public parsing entry point, as documented across docs/*.adoc.
|
|
67
|
+
# Delegates to lutaml-model's XML Schema parser.
|
|
68
|
+
#
|
|
69
|
+
# @param xsd [String] XSD content
|
|
70
|
+
# @param location [String, nil] base location for import/include paths
|
|
71
|
+
# @return [Lutaml::Xml::Schema::Xsd::Schema]
|
|
72
|
+
def self.parse(xsd, **)
|
|
73
|
+
Xml::Schema::Xsd.parse(xsd, **)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
data/lutaml-xsd.gemspec
CHANGED
|
@@ -34,9 +34,9 @@ Gem::Specification.new do |spec|
|
|
|
34
34
|
spec.add_dependency "liquid", ">= 4.0", "< 6.0"
|
|
35
35
|
spec.add_dependency "lutaml-model", "~> 0.8.114"
|
|
36
36
|
spec.add_dependency "lutaml-store", "~> 0.3"
|
|
37
|
-
spec.add_dependency "moxml"
|
|
37
|
+
spec.add_dependency "moxml", "~> 0.5.147"
|
|
38
38
|
spec.add_dependency "paint", "~> 2.3"
|
|
39
|
-
spec.add_dependency "rng", "~> 0.3.
|
|
39
|
+
spec.add_dependency "rng", "~> 0.3.9"
|
|
40
40
|
spec.add_dependency "rubyzip", "~> 3.4"
|
|
41
41
|
spec.add_dependency "table_tennis", "~> 0.0.7"
|
|
42
42
|
spec.add_dependency "thor", "~> 1.0"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lutaml-xsd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: liquid
|
|
@@ -62,16 +62,16 @@ dependencies:
|
|
|
62
62
|
name: moxml
|
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
|
-
- - "
|
|
65
|
+
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version:
|
|
67
|
+
version: 0.5.147
|
|
68
68
|
type: :runtime
|
|
69
69
|
prerelease: false
|
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
|
71
71
|
requirements:
|
|
72
|
-
- - "
|
|
72
|
+
- - "~>"
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version:
|
|
74
|
+
version: 0.5.147
|
|
75
75
|
- !ruby/object:Gem::Dependency
|
|
76
76
|
name: paint
|
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -92,14 +92,14 @@ dependencies:
|
|
|
92
92
|
requirements:
|
|
93
93
|
- - "~>"
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: 0.3.
|
|
95
|
+
version: 0.3.9
|
|
96
96
|
type: :runtime
|
|
97
97
|
prerelease: false
|
|
98
98
|
version_requirements: !ruby/object:Gem::Requirement
|
|
99
99
|
requirements:
|
|
100
100
|
- - "~>"
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: 0.3.
|
|
102
|
+
version: 0.3.9
|
|
103
103
|
- !ruby/object:Gem::Dependency
|
|
104
104
|
name: rubyzip
|
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -190,9 +190,18 @@ files:
|
|
|
190
190
|
- CLAUDE.md
|
|
191
191
|
- CODE_OF_CONDUCT.md
|
|
192
192
|
- Gemfile
|
|
193
|
+
- Gemfile.lock
|
|
193
194
|
- LICENSE.md
|
|
194
195
|
- README.adoc
|
|
195
196
|
- Rakefile
|
|
197
|
+
- TODO.VISION.md
|
|
198
|
+
- TODO.cleanup.md
|
|
199
|
+
- TODO.conformance.md
|
|
200
|
+
- TODO.docs-spa.md
|
|
201
|
+
- TODO.interop.md
|
|
202
|
+
- TODO.performance.md
|
|
203
|
+
- TODO.som.md
|
|
204
|
+
- benchmark/engines.rb
|
|
196
205
|
- bin/console
|
|
197
206
|
- bin/setup
|
|
198
207
|
- config/base_type_validators.yml
|
|
@@ -209,6 +218,7 @@ files:
|
|
|
209
218
|
- docs/ARCHITECTURE.adoc
|
|
210
219
|
- docs/CHANGELOG.md
|
|
211
220
|
- docs/CLI.adoc
|
|
221
|
+
- docs/CONFORMANCE.adoc
|
|
212
222
|
- docs/CONTRIBUTING.adoc
|
|
213
223
|
- docs/DEPLOYMENT_GUIDE.adoc
|
|
214
224
|
- docs/FAQ.adoc
|
|
@@ -217,6 +227,7 @@ files:
|
|
|
217
227
|
- docs/INSTALLATION.adoc
|
|
218
228
|
- docs/INTERACTIVE_SHELL_DEMO.adoc
|
|
219
229
|
- docs/JING_ALGORITHM_PORTING_GUIDE.md
|
|
230
|
+
- docs/JSON_SCHEMA.adoc
|
|
220
231
|
- docs/LIQUID_METHODS.adoc
|
|
221
232
|
- docs/LXR_PACKAGES.adoc
|
|
222
233
|
- docs/MAINTENANCE_GUIDE.adoc
|
|
@@ -266,6 +277,9 @@ files:
|
|
|
266
277
|
- docs/schema_mapping.adoc
|
|
267
278
|
- docs/schema_repository.adoc
|
|
268
279
|
- docs/schemas/lxr-package-config.schema.json
|
|
280
|
+
- docs/superpowers/plans/2026-10-20-moxml-leptris-migration.md
|
|
281
|
+
- docs/superpowers/specs/2026-10-20-moxml-leptris-migration-design.md
|
|
282
|
+
- docs/superpowers/specs/2026-10-21-rule-engine-wiring-design.md
|
|
269
283
|
- examples/01-simple/README.adoc
|
|
270
284
|
- examples/01-simple/cli/run.sh
|
|
271
285
|
- examples/01-simple/config.yml
|
|
@@ -431,6 +445,7 @@ files:
|
|
|
431
445
|
- lib/lutaml/xsd/conflicts/namespace_conflict.rb
|
|
432
446
|
- lib/lutaml/xsd/conflicts/schema_conflict.rb
|
|
433
447
|
- lib/lutaml/xsd/conflicts/type_conflict.rb
|
|
448
|
+
- lib/lutaml/xsd/conformance/runner.rb
|
|
434
449
|
- lib/lutaml/xsd/coverage_analyzer.rb
|
|
435
450
|
- lib/lutaml/xsd/definition_extractor.rb
|
|
436
451
|
- lib/lutaml/xsd/dependency_grapher.rb
|
|
@@ -453,6 +468,7 @@ files:
|
|
|
453
468
|
- lib/lutaml/xsd/formatters/text_formatter.rb
|
|
454
469
|
- lib/lutaml/xsd/formatters/yaml_formatter.rb
|
|
455
470
|
- lib/lutaml/xsd/interactive_builder.rb
|
|
471
|
+
- lib/lutaml/xsd/json_schema_exporter.rb
|
|
456
472
|
- lib/lutaml/xsd/namespace_mapping.rb
|
|
457
473
|
- lib/lutaml/xsd/namespace_prefix_manager.rb
|
|
458
474
|
- lib/lutaml/xsd/namespace_remapper.rb
|
|
@@ -533,6 +549,7 @@ files:
|
|
|
533
549
|
- lib/lutaml/xsd/validation/base_types/qname_validator.rb
|
|
534
550
|
- lib/lutaml/xsd/validation/base_types/string_validator.rb
|
|
535
551
|
- lib/lutaml/xsd/validation/base_types/time_validator.rb
|
|
552
|
+
- lib/lutaml/xsd/validation/engine_context.rb
|
|
536
553
|
- lib/lutaml/xsd/validation/facets/enumeration_facet_validator.rb
|
|
537
554
|
- lib/lutaml/xsd/validation/facets/facet_validator.rb
|
|
538
555
|
- lib/lutaml/xsd/validation/facets/facet_validator_registry.rb
|
|
@@ -553,8 +570,10 @@ files:
|
|
|
553
570
|
- lib/lutaml/xsd/validation/rules/attribute_validation_rule.rb
|
|
554
571
|
- lib/lutaml/xsd/validation/rules/content_model_validation_rule.rb
|
|
555
572
|
- lib/lutaml/xsd/validation/rules/element_structure_rule.rb
|
|
573
|
+
- lib/lutaml/xsd/validation/rules/identity_constraints_rule.rb
|
|
556
574
|
- lib/lutaml/xsd/validation/rules/occurrence_validation_rule.rb
|
|
557
575
|
- lib/lutaml/xsd/validation/rules/type_validation_rule.rb
|
|
576
|
+
- lib/lutaml/xsd/validation/schema_definition_resolver.rb
|
|
558
577
|
- lib/lutaml/xsd/validation/schema_location_extractor.rb
|
|
559
578
|
- lib/lutaml/xsd/validation/schema_resolver.rb
|
|
560
579
|
- lib/lutaml/xsd/validation/validation_configuration.rb
|