moxml 0.5.169 → 0.5.171
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/README.adoc +6 -7
- data/docs/_pages/model-coverage.adoc +1 -0
- data/docs/_references/processing-model.adoc +8 -2
- data/lib/moxml/adapter/base.rb +6 -0
- data/lib/moxml/adapter/leptris.rb +32 -32
- data/lib/moxml/adapter/nokogiri.rb +12 -4
- data/lib/moxml/adapter.rb +20 -0
- data/lib/moxml/context.rb +19 -0
- data/lib/moxml/version.rb +1 -1
- data/spec/moxml/adapter_deprecation_spec.rb +49 -0
- data/spec/moxml/sax_entity_parity_spec.rb +27 -2
- data/spec/moxml/xsd_set_spec.rb +97 -0
- data/spec/support/model_coverage.rb +4 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd5e790791e306829e87770a45424b006c9a535c95c825adfc7d0800816ceb7d
|
|
4
|
+
data.tar.gz: c3348f9fb7c6c0ccf00ab4bc16bba51b0812331d12d8478d2a77e09b8a600e58
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2972a94dc0bf75896d29432c1856b949090cbd8547b60c402376f2d715c743c8e029af2181531fbd327d6615a3a6aef5cb83144c5ff2b6389f35bd3ab6dfea59
|
|
7
|
+
data.tar.gz: c89e1be189130b17f0a7f61c67c9caba0624f589c29a2407effba7fb50da72304d33db77001185cb7b079729a7a29140d32e8a492269a58cf748a8e99f0276a2
|
data/README.adoc
CHANGED
|
@@ -95,7 +95,8 @@ adapter refuses to load below its floor.
|
|
|
95
95
|
|
|
96
96
|
|Ox
|
|
97
97
|
|>= 2.14
|
|
98
|
-
|
|
|
98
|
+
|DEPRECATED (#491) — warned at configuration; use :leptris (primary)
|
|
99
|
+
or :nokogiri. Removal after the deprecation cycle.
|
|
99
100
|
|
|
100
101
|
|Oga
|
|
101
102
|
|3.4 / 3.5
|
|
@@ -105,8 +106,9 @@ moxml's customized generator and decoder overrides.
|
|
|
105
106
|
|
|
106
107
|
|LibXML (libxml-ruby)
|
|
107
108
|
|>= 5.0
|
|
108
|
-
|
|
|
109
|
-
|
|
109
|
+
|DEPRECATED (#491) — warned at configuration; use :nokogiri (the
|
|
110
|
+
same libxml2 engine, governed by the consistency suite). Removal
|
|
111
|
+
after the deprecation cycle.
|
|
110
112
|
|
|
111
113
|
|Leptris (leptris-ruby)
|
|
112
114
|
|>= 1.9.194.1
|
|
@@ -114,10 +116,7 @@ moxml's customized generator and decoder overrides.
|
|
|
114
116
|
Raised from 1.9.32 (2026-09-20): every known consumer resolves a
|
|
115
117
|
modern binding, and the sub-1.9.194 accommodation paths are deleted.
|
|
116
118
|
Newer bindings unlock more native surface automatically:
|
|
117
|
-
1.9.
|
|
118
|
-
wrappers, 1.9.174.6+ one-dispatch subtree walks, 1.9.177+ first-class
|
|
119
|
-
entity references (`entity_mode: :keep`), 1.9.193.4+ native plan rows
|
|
120
|
-
(`Moxml::Plan`), 1.9.194.1+ hot-read aliases, 1.9.201.1+ the C struct
|
|
119
|
+
1.9.194.1+ hot-read aliases, 1.9.201.1+ the C struct
|
|
121
120
|
executor (`Moxml::StructPlan`; the floor is the varargs-mint fix),
|
|
122
121
|
1.9.204+ native document-level parts (created DOCTYPEs as native
|
|
123
122
|
nodes, document-PI removal, declaration clear), 1.9.240+ the bulk
|
|
@@ -45,6 +45,7 @@ Generated by `bundle exec rake docs:coverage` from the coverage matrix and the d
|
|
|
45
45
|
| validation engine metadata | equivalent | equivalent | equivalent | equivalent | equivalent | equivalent | equivalent
|
|
46
46
|
| validation regex dismissal | equivalent | equivalent | equivalent | equivalent | equivalent | equivalent | equivalent
|
|
47
47
|
| validation builtin lexical table | equivalent | equivalent | equivalent | equivalent | equivalent | equivalent | equivalent
|
|
48
|
+
| validation schema sets | equivalent | equivalent | equivalent | equivalent | equivalent | equivalent | equivalent
|
|
48
49
|
| validation substitution dismissal | equivalent | equivalent | equivalent | equivalent | equivalent | equivalent | equivalent
|
|
49
50
|
| loud capability contracts | equivalent | equivalent | equivalent | equivalent | equivalent | equivalent | equivalent
|
|
50
51
|
|===
|
|
@@ -255,7 +255,7 @@ semantics.
|
|
|
255
255
|
|
|
256
256
|
=== Validation
|
|
257
257
|
|
|
258
|
-
Pins:: `spec/moxml/xsd_builtin_lexical_spec.rb`, `spec/moxml/xsd_engine_meta_spec.rb`, `spec/moxml/xsd_regex_spec.rb`, `spec/moxml/xsd_spec.rb`, `spec/moxml/xsd_substitution_spec.rb`, `spec/moxml/xsd_underdetection_spec.rb`, `spec/moxml/xsd_version_spec.rb`
|
|
258
|
+
Pins:: `spec/moxml/xsd_builtin_lexical_spec.rb`, `spec/moxml/xsd_engine_meta_spec.rb`, `spec/moxml/xsd_regex_spec.rb`, `spec/moxml/xsd_set_spec.rb`, `spec/moxml/xsd_spec.rb`, `spec/moxml/xsd_substitution_spec.rb`, `spec/moxml/xsd_underdetection_spec.rb`, `spec/moxml/xsd_version_spec.rb`
|
|
259
259
|
|
|
260
260
|
XSD validation through engines that ship a validator (libxml2;
|
|
261
261
|
leptris tier-1: recognized roots, one-level content descent, lexical
|
|
@@ -265,7 +265,13 @@ spaces (collapse for the token family, the unbounded integer family
|
|
|
265
265
|
with bignum bounds, calendar and duration grammars, binary, QName
|
|
266
266
|
with the reserved `xmlns` prefix) from the facade's own table on
|
|
267
267
|
every adapter — an engine table converges to it, never the reverse;
|
|
268
|
-
an unknown name raises `ArgumentError`.
|
|
268
|
+
an unknown name raises `ArgumentError`. Schema-set compilation
|
|
269
|
+
compiles a LIST of documents into one schema (`Context#xsd_set`):
|
|
270
|
+
imports resolve across the whole set, so components declared in
|
|
271
|
+
importing documents — substitution-group members, derived types —
|
|
272
|
+
are visible to the head's content models; the single-document
|
|
273
|
+
faces cannot see them. Adapters without a set-capable engine raise
|
|
274
|
+
`Moxml::NotImplementedError`. `validate` returns
|
|
269
275
|
accumulated `ValidationError` entries —
|
|
270
276
|
message always; line/column/path where the engine reports them, nil
|
|
271
277
|
where it does not; `[]` means valid. Documents outside the engine's
|
data/lib/moxml/adapter/base.rb
CHANGED
|
@@ -373,6 +373,12 @@ module Moxml
|
|
|
373
373
|
"XSD validation requires an adapter with an XSD engine"
|
|
374
374
|
end
|
|
375
375
|
|
|
376
|
+
def xsd_compile_set(_paths)
|
|
377
|
+
raise Moxml::NotImplementedError,
|
|
378
|
+
"Schema-set compilation requires an XSD engine with the " \
|
|
379
|
+
"compile_set face"
|
|
380
|
+
end
|
|
381
|
+
|
|
376
382
|
def xsd_declarations(_xsd_handle); end
|
|
377
383
|
|
|
378
384
|
def xsd_compile_error(_xsd_handle); end
|
|
@@ -41,8 +41,7 @@ module Moxml
|
|
|
41
41
|
# leptris_node_digest shipped in bindings 1.9.99 (libleptris
|
|
42
42
|
# 1.9.99, engine #869): on-demand Merkle subtree hash, zero
|
|
43
43
|
# cost when unused. Below it, Node#digest answers nil.
|
|
44
|
-
DIGEST_SUPPORTED =
|
|
45
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.99")
|
|
44
|
+
DIGEST_SUPPORTED = true
|
|
46
45
|
|
|
47
46
|
# 1.9.163.7 added the XPath seams (TODO.perf/15+16):
|
|
48
47
|
# Searchable#at_xpath materializes nodeset entry 0 and frees
|
|
@@ -52,8 +51,7 @@ module Moxml
|
|
|
52
51
|
# raw pointers, skipping the per-call EvaluationContext
|
|
53
52
|
# (~2us on every multi-result query).
|
|
54
53
|
NATIVE_XPATH_SEAMS =
|
|
55
|
-
defined?(::Leptris::XML::Native)
|
|
56
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.163.7")
|
|
54
|
+
defined?(::Leptris::XML::Native)
|
|
57
55
|
|
|
58
56
|
# Opt-in native read layer (leptris-ruby#185, bindings >= 1.9.162.6):
|
|
59
57
|
# TypedData node wrappers with C-bound hot reads and bulk
|
|
@@ -68,8 +66,7 @@ module Moxml
|
|
|
68
66
|
nil
|
|
69
67
|
end
|
|
70
68
|
NATIVE_READ_LAYER =
|
|
71
|
-
defined?(::Leptris::XML::NativeNode)
|
|
72
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.162.6")
|
|
69
|
+
defined?(::Leptris::XML::NativeNode)
|
|
73
70
|
|
|
74
71
|
# leptris-ruby#529: the whole XPath node-set result as an Array
|
|
75
72
|
# of registered natives in ONE C crossing (Document#
|
|
@@ -118,8 +115,7 @@ module Moxml
|
|
|
118
115
|
# (1.9.162.x answered ASCII-8BIT — the reads retagged with a
|
|
119
116
|
# dup+force_encoding per call).
|
|
120
117
|
NATIVE_STRINGS_UTF8 =
|
|
121
|
-
defined?(::Leptris::XML::NativeNode)
|
|
122
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.163.2")
|
|
118
|
+
defined?(::Leptris::XML::NativeNode)
|
|
123
119
|
|
|
124
120
|
# 1.9.163.5 (leptris-ruby#204/#208): native mutations are
|
|
125
121
|
# version-coherent with the binding (both surfaces' memos
|
|
@@ -127,8 +123,7 @@ module Moxml
|
|
|
127
123
|
# add_child are adoptable end-to-end; NativeNode grew a
|
|
128
124
|
# document accessor and line numbers.
|
|
129
125
|
NATIVE_MUTATIONS_COHERENT =
|
|
130
|
-
defined?(::Leptris::XML::NativeNode)
|
|
131
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.163.5")
|
|
126
|
+
defined?(::Leptris::XML::NativeNode)
|
|
132
127
|
|
|
133
128
|
# Identity mode (#230 stage 2): the contract modules extended
|
|
134
129
|
# onto natives shadow the C surface (Element#[] over
|
|
@@ -170,16 +165,14 @@ module Moxml
|
|
|
170
165
|
# 1.9.181 (lockstep): Node#source_position — {line, col_start,
|
|
171
166
|
# col_end} from the engine's source-position API (upstream
|
|
172
167
|
# #1124); created nodes report zeros.
|
|
173
|
-
NATIVE_SOURCE_POSITION =
|
|
174
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.181")
|
|
168
|
+
NATIVE_SOURCE_POSITION = true
|
|
175
169
|
|
|
176
170
|
# Entity-reference preservation (leptris-ruby#212 / upstream
|
|
177
171
|
# #1094): 1.9.177 ships ParseOptions.keep_entity_refs plus
|
|
178
172
|
# first-class EntityReference nodes — the marker machinery
|
|
179
173
|
# becomes bypassable when the Context's entity_mode is :keep.
|
|
180
174
|
NATIVE_ENTITY_REFS =
|
|
181
|
-
defined?(::Leptris::XML::EntityReference)
|
|
182
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.177")
|
|
175
|
+
defined?(::Leptris::XML::EntityReference)
|
|
183
176
|
|
|
184
177
|
# 1.9.163.2 moved the native bulk children into C
|
|
185
178
|
# (Native.bulk_children) and fixed the 512 truncation
|
|
@@ -189,8 +182,7 @@ module Moxml
|
|
|
189
182
|
# C bulk path (version-memoized, cheaper than a per-call
|
|
190
183
|
# count+copy).
|
|
191
184
|
NATIVE_BULK_FIXED =
|
|
192
|
-
defined?(::Leptris::XML::NativeNode)
|
|
193
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.163.2")
|
|
185
|
+
defined?(::Leptris::XML::NativeNode)
|
|
194
186
|
|
|
195
187
|
# Bulk SAX record drain (leptris#1298): leptris_sax_records_parse
|
|
196
188
|
# + Leptris::XML::SAX::Records — one crossing per document,
|
|
@@ -372,6 +364,23 @@ module Moxml
|
|
|
372
364
|
::Leptris::XML::XSD.compile_file(path)
|
|
373
365
|
end
|
|
374
366
|
|
|
367
|
+
# Schema-set compilation (leptris 1.9.387, moxml#483 item 4):
|
|
368
|
+
# every document compiles into one schema, so imports resolve
|
|
369
|
+
# across the set and substitution-group members declared in
|
|
370
|
+
# importing documents are visible to the head's content
|
|
371
|
+
# models. Path-sourced only — the engine face reads files.
|
|
372
|
+
def xsd_compile_set(paths)
|
|
373
|
+
unless XSD_SUPPORTED &&
|
|
374
|
+
defined?(::Leptris::XML::XSD::Schema) &&
|
|
375
|
+
::Leptris::XML::XSD::Schema.respond_to?(:compile_set)
|
|
376
|
+
raise Moxml::NotImplementedError,
|
|
377
|
+
"Schema-set compilation requires the leptris 1.9.387+ " \
|
|
378
|
+
"engine (the compile_set face)"
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
::Leptris::XML::XSD.compile_set(paths)
|
|
382
|
+
end
|
|
383
|
+
|
|
375
384
|
def relaxng_compile(schema_source)
|
|
376
385
|
unless RELAXNG_SUPPORTED
|
|
377
386
|
raise Moxml::NotImplementedError,
|
|
@@ -535,17 +544,13 @@ module Moxml
|
|
|
535
544
|
# classes do not exist. NATIVE_MUTATIONS_COHERENT is false
|
|
536
545
|
# without the native layer, so all three evaluate false
|
|
537
546
|
# there.
|
|
538
|
-
NATIVE_IDENTITY =
|
|
539
|
-
NATIVE_MUTATIONS_COHERENT &&
|
|
540
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.174.4")
|
|
547
|
+
NATIVE_IDENTITY = NATIVE_MUTATIONS_COHERENT
|
|
541
548
|
|
|
542
549
|
NATIVE_C_MINT =
|
|
543
550
|
NATIVE_IDENTITY &&
|
|
544
551
|
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.255.2")
|
|
545
552
|
|
|
546
|
-
NATIVE_KLASS_CHILDREN =
|
|
547
|
-
NATIVE_IDENTITY &&
|
|
548
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.193.2")
|
|
553
|
+
NATIVE_KLASS_CHILDREN = NATIVE_IDENTITY
|
|
549
554
|
end
|
|
550
555
|
|
|
551
556
|
if NATIVE_READ_LAYER
|
|
@@ -853,8 +858,7 @@ module Moxml
|
|
|
853
858
|
end
|
|
854
859
|
|
|
855
860
|
NATIVE_C_WALK =
|
|
856
|
-
defined?(::Leptris::XML::NativeNode)
|
|
857
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.174.6")
|
|
861
|
+
defined?(::Leptris::XML::NativeNode)
|
|
858
862
|
|
|
859
863
|
# One C visit walk (TODO.perf/27: post-order/abort state in
|
|
860
864
|
# the C callback, rb_yield direct — no FFI closure per
|
|
@@ -1092,8 +1096,7 @@ module Moxml
|
|
|
1092
1096
|
# on released 1.9.37–1.9.39; 1.9.40 (engine 1.9.14+) restored
|
|
1093
1097
|
# the fallback. Older bindings keep the Ruby-engine routing in
|
|
1094
1098
|
# native_expression?.
|
|
1095
|
-
PREFIXED_ATTR_PREDICATES_NATIVE =
|
|
1096
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.40")
|
|
1099
|
+
PREFIXED_ATTR_PREDICATES_NATIVE = true
|
|
1097
1100
|
|
|
1098
1101
|
# leptris/leptris#636 (leptris-ruby 1.9.42): libxml2-compatible
|
|
1099
1102
|
# pretty-print — child-PI lines, DOCTYPE internal-subset layout,
|
|
@@ -1101,8 +1104,7 @@ module Moxml
|
|
|
1101
1104
|
# unit (`to_xml`'s indent_text takes the unit string). Older
|
|
1102
1105
|
# floor bindings treat indent_text as a display-form boolean, so
|
|
1103
1106
|
# the passthrough and the parity pins gate on this.
|
|
1104
|
-
LIBXML2_LAYOUT_PARITY =
|
|
1105
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.42")
|
|
1107
|
+
LIBXML2_LAYOUT_PARITY = true
|
|
1106
1108
|
|
|
1107
1109
|
# leptris-ruby#109 (1.9.45): Element#to_xml takes the
|
|
1108
1110
|
# indent-unit string, so moxml's per-child document composition
|
|
@@ -1110,13 +1112,11 @@ module Moxml
|
|
|
1110
1112
|
# emit spaces until the engine fix (leptris/leptris#658); the
|
|
1111
1113
|
# boolean display form stays document-level and is never
|
|
1112
1114
|
# forwarded (the element face raises on it).
|
|
1113
|
-
INDENT_UNIT_SUPPORTED =
|
|
1114
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.45")
|
|
1115
|
+
INDENT_UNIT_SUPPORTED = true
|
|
1115
1116
|
|
|
1116
1117
|
# leptris-ruby#115 (fixed 1.9.50): the element unit path keeps
|
|
1117
1118
|
# child comments.
|
|
1118
|
-
ELEMENT_UNIT_COMMENTS =
|
|
1119
|
-
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.50")
|
|
1119
|
+
ELEMENT_UNIT_COMMENTS = true
|
|
1120
1120
|
|
|
1121
1121
|
# leptris/leptris#677: the engine's DROP_WS_TEXT trims boundary
|
|
1122
1122
|
# whitespace of non-blank text nodes. Probed at load rather
|
|
@@ -800,11 +800,19 @@ module Moxml
|
|
|
800
800
|
@handler.on_end_document
|
|
801
801
|
end
|
|
802
802
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
803
|
+
# libxml hands over namespace declaration URIs already decoded,
|
|
804
|
+
# while attribute values still carry their entity references;
|
|
805
|
+
# decoding the URIs again corrupts "&" into "&".
|
|
806
|
+
def start_element_namespace(name, attrs = [], prefix = nil, _uri = nil,
|
|
807
|
+
namespaces = [])
|
|
808
|
+
attr_hash = {}
|
|
809
|
+
attrs.each do |attr|
|
|
810
|
+
key = attr.prefix ? "#{attr.prefix}:#{attr.localname}" : attr.localname
|
|
811
|
+
attr_hash[key] = Moxml::Adapter::Base.decode_entities(attr.value)
|
|
806
812
|
end
|
|
807
|
-
|
|
813
|
+
ns_hash = namespaces.empty? ? EMPTY_NAMESPACES : namespaces.to_h
|
|
814
|
+
qualified = prefix ? "#{prefix}:#{name}" : name
|
|
815
|
+
@handler.on_start_element(qualified, attr_hash, ns_hash)
|
|
808
816
|
end
|
|
809
817
|
|
|
810
818
|
def end_element(name)
|
data/lib/moxml/adapter.rb
CHANGED
|
@@ -25,7 +25,27 @@ module Moxml
|
|
|
25
25
|
headed_ox: "HeadedOx",
|
|
26
26
|
}.freeze
|
|
27
27
|
|
|
28
|
+
# Adapters in their deprecation cycle (#491): warned once per
|
|
29
|
+
# process at configuration, removed after the cycle. The value is
|
|
30
|
+
# the migration guidance shown in the warning.
|
|
31
|
+
DEPRECATED_ADAPTERS = {
|
|
32
|
+
headed_ox: "use :leptris (primary) or :nokogiri; headed_ox is a pure alias of :ox",
|
|
33
|
+
libxml: "use :nokogiri — the same libxml2 engine, governed by the consistency suite",
|
|
34
|
+
ox: "use :leptris (primary) or :nokogiri",
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
28
37
|
class << self
|
|
38
|
+
# One-time deprecation notice per adapter per process (#491).
|
|
39
|
+
def warn_deprecated!(name)
|
|
40
|
+
guidance = DEPRECATED_ADAPTERS[name.to_sym]
|
|
41
|
+
return unless guidance
|
|
42
|
+
return if (@deprecation_warned ||= {})[name.to_sym]
|
|
43
|
+
|
|
44
|
+
@deprecation_warned[name.to_sym] = true
|
|
45
|
+
warn "moxml: the :#{name} adapter is deprecated and will be " \
|
|
46
|
+
"removed; #{guidance}"
|
|
47
|
+
end
|
|
48
|
+
|
|
29
49
|
def load(name)
|
|
30
50
|
validate_platform!(name)
|
|
31
51
|
require_adapter(name)
|
data/lib/moxml/context.rb
CHANGED
|
@@ -26,6 +26,7 @@ module Moxml
|
|
|
26
26
|
attr_reader :config
|
|
27
27
|
|
|
28
28
|
def initialize(adapter = nil)
|
|
29
|
+
Adapter.warn_deprecated!(adapter) if adapter
|
|
29
30
|
@config = Config.new(adapter)
|
|
30
31
|
# Bumped on any namespace-scope mutation; Element scope caches
|
|
31
32
|
# compare against it so every wrapper sees changes without
|
|
@@ -222,6 +223,24 @@ module Moxml
|
|
|
222
223
|
Moxml::XSD::Schema.new(handle, self, source)
|
|
223
224
|
end
|
|
224
225
|
|
|
226
|
+
# Compile a SET of schema documents (paths) into one schema:
|
|
227
|
+
# imports resolve across the whole set, so components declared
|
|
228
|
+
# in importing documents (substitution-group members, derived
|
|
229
|
+
# types) are visible to the head's content models — the
|
|
230
|
+
# single-document faces cannot see them (#483 item 4). The
|
|
231
|
+
# Schema's #version / SchemaRules index read the HEAD document
|
|
232
|
+
# (the first path); engine-set semantics cover the rest.
|
|
233
|
+
# Adapters without a set-capable engine raise
|
|
234
|
+
# Moxml::NotImplementedError.
|
|
235
|
+
def xsd_set(*paths)
|
|
236
|
+
paths = paths.first if paths.size == 1 && paths.first.is_a?(Array)
|
|
237
|
+
raise ArgumentError, "schema set needs at least one path" if paths.empty?
|
|
238
|
+
|
|
239
|
+
handle = config.adapter.xsd_compile_set(paths.map(&:to_s))
|
|
240
|
+
source = File.read(paths.first.to_s)
|
|
241
|
+
Moxml::XSD::Schema.new(handle, self, source)
|
|
242
|
+
end
|
|
243
|
+
|
|
225
244
|
# Compile a RELAX NG schema from source or from a file path (the
|
|
226
245
|
# engine resolves relative includes against the file's directory).
|
|
227
246
|
# Adapters without an RNG engine raise Moxml::NotImplementedError.
|
data/lib/moxml/version.rb
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Moxml::Adapter do
|
|
6
|
+
describe ".warn_deprecated!" do
|
|
7
|
+
# The once-per-process state is module-level; isolate each
|
|
8
|
+
# example from prior loads and from later suites.
|
|
9
|
+
around do |example|
|
|
10
|
+
described_class.instance_variable_set(:@deprecation_warned, {})
|
|
11
|
+
example.run
|
|
12
|
+
described_class.instance_variable_set(:@deprecation_warned, {})
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "warns once per deprecated adapter per process" do
|
|
16
|
+
expect do
|
|
17
|
+
Moxml.new(:headed_ox)
|
|
18
|
+
end.to output(%r{:headed_ox adapter is deprecated}).to_stderr
|
|
19
|
+
expect do
|
|
20
|
+
Moxml.new(:headed_ox)
|
|
21
|
+
end.not_to output.to_stderr
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "guides every deprecated adapter toward the governed matrix" do
|
|
25
|
+
described_class::DEPRECATED_ADAPTERS.each_value do |guidance|
|
|
26
|
+
expect(guidance).to match(/:nokogiri|:leptris/)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "does not warn for the governed matrix" do
|
|
31
|
+
expect do
|
|
32
|
+
Moxml.new(:leptris)
|
|
33
|
+
Moxml.new(:nokogiri)
|
|
34
|
+
end.not_to output(/deprecated/).to_stderr
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "does not warn for unknown names" do
|
|
38
|
+
expect do
|
|
39
|
+
described_class.warn_deprecated!(:definitely_not_an_adapter)
|
|
40
|
+
end.not_to output.to_stderr
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "keeps the deprecated adapters loadable through the cycle" do
|
|
44
|
+
described_class::DEPRECATED_ADAPTERS.each_key do |name|
|
|
45
|
+
expect(described_class.available?(name)).to be(true)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -15,16 +15,18 @@ require "spec_helper"
|
|
|
15
15
|
# the RSpec/* cops that forbid local-variable use inside examples.
|
|
16
16
|
module SaxEntityParityFixtures
|
|
17
17
|
class CaptureHandler < Moxml::SAX::ElementHandler
|
|
18
|
-
attr_reader :first_attrs, :text
|
|
18
|
+
attr_reader :first_attrs, :first_namespaces, :text
|
|
19
19
|
|
|
20
20
|
def initialize
|
|
21
21
|
super
|
|
22
22
|
@first_attrs = nil
|
|
23
|
+
@first_namespaces = nil
|
|
23
24
|
@text = +""
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
def on_start_element(_name, attrs = {},
|
|
27
|
+
def on_start_element(_name, attrs = {}, namespaces = {})
|
|
27
28
|
@first_attrs = attrs.dup if @first_attrs.nil?
|
|
29
|
+
@first_namespaces = namespaces.dup if @first_namespaces.nil?
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
def on_characters(chunk)
|
|
@@ -51,6 +53,19 @@ module SaxEntityParityFixtures
|
|
|
51
53
|
"no entities" => ['<doc x="plain"/>', "plain", "plain"],
|
|
52
54
|
}.freeze
|
|
53
55
|
|
|
56
|
+
# Adapters whose SAX bridge reports namespace declarations in the
|
|
57
|
+
# handler's namespaces hash with the URI decoded exactly once.
|
|
58
|
+
SAX_NAMESPACE_ADAPTERS = %i[nokogiri ox rexml headed_ox].freeze
|
|
59
|
+
|
|
60
|
+
# Each row: [input XML, decoded namespace URI]. The parser decodes a
|
|
61
|
+
# declaration's URI exactly once; a decoded "&" is data and must
|
|
62
|
+
# reach the handler as-is.
|
|
63
|
+
NAMESPACE_CASES = {
|
|
64
|
+
"wrapped amp + decimal ref" => ['<f:doc xmlns:f="http://f/?x=1&#38;y"/>', "http://f/?x=1&y"],
|
|
65
|
+
"wrapped amp + amp" => ['<f:doc xmlns:f="http://f/?x=&amp;"/>', "http://f/?x=&"],
|
|
66
|
+
"plain amp" => ['<f:doc xmlns:f="http://f/?x=1&y=2"/>', "http://f/?x=1&y=2"],
|
|
67
|
+
}.freeze
|
|
68
|
+
|
|
54
69
|
# Each row: [input XML, decoded text, expected text as it appears in
|
|
55
70
|
# serialized XML].
|
|
56
71
|
TEXT_CASES = {
|
|
@@ -138,6 +153,16 @@ RSpec.describe "SAX/DOM entity parity" do
|
|
|
138
153
|
end
|
|
139
154
|
end
|
|
140
155
|
|
|
156
|
+
if SaxEntityParityFixtures::SAX_NAMESPACE_ADAPTERS.include?(adapter)
|
|
157
|
+
SaxEntityParityFixtures::NAMESPACE_CASES.each do |label, (xml, expected)|
|
|
158
|
+
it "decodes namespace URI once via SAX: #{label}" do
|
|
159
|
+
handler = SaxEntityParityFixtures::CaptureHandler.new
|
|
160
|
+
ctx.sax_parse(xml, handler)
|
|
161
|
+
expect(handler.first_namespaces["f"]).to eq(expected)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
141
166
|
SaxEntityParityFixtures::TEXT_CASES.each do |label, (xml, expected, expected_serialized)|
|
|
142
167
|
it "decodes text via SAX: #{label}" do
|
|
143
168
|
handler = SaxEntityParityFixtures::CaptureHandler.new
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Processing model §Validation (normative): pinned here.
|
|
4
|
+
|
|
5
|
+
require "spec_helper"
|
|
6
|
+
|
|
7
|
+
RSpec.describe "Schema-set compilation" do
|
|
8
|
+
let(:ctx) { Moxml.new(:leptris) }
|
|
9
|
+
|
|
10
|
+
# leptris#1881 / moxml#483 item 4: a head document cannot see
|
|
11
|
+
# substitution-group members declared in schemas that import it;
|
|
12
|
+
# the set face merges every document into one compilation.
|
|
13
|
+
def write_set(dir)
|
|
14
|
+
File.write(File.join(dir, "f.xsd"), <<~XSD)
|
|
15
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
|
16
|
+
xmlns="urn:F" xmlns:f="urn:F" targetNamespace="urn:F"
|
|
17
|
+
elementFormDefault="qualified">
|
|
18
|
+
<xs:complexType name="foo">
|
|
19
|
+
<xs:sequence><xs:any/></xs:sequence>
|
|
20
|
+
</xs:complexType>
|
|
21
|
+
<xs:element name="foo" type="foo"/>
|
|
22
|
+
<xs:element name="bar">
|
|
23
|
+
<xs:complexType>
|
|
24
|
+
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
|
25
|
+
<xs:element ref="f:foo"/>
|
|
26
|
+
<xs:element ref="f:bar"/>
|
|
27
|
+
</xs:choice>
|
|
28
|
+
</xs:complexType>
|
|
29
|
+
</xs:element>
|
|
30
|
+
</xs:schema>
|
|
31
|
+
XSD
|
|
32
|
+
File.write(File.join(dir, "g.xsd"), <<~XSD)
|
|
33
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
|
34
|
+
xmlns:g="urn:G" xmlns:f="urn:F" targetNamespace="urn:G"
|
|
35
|
+
elementFormDefault="qualified">
|
|
36
|
+
<xs:import namespace="urn:F" schemaLocation="f.xsd"/>
|
|
37
|
+
<xs:element name="foo1" substitutionGroup="f:foo">
|
|
38
|
+
<xs:complexType>
|
|
39
|
+
<xs:complexContent>
|
|
40
|
+
<xs:extension base="f:foo">
|
|
41
|
+
<xs:attribute name="x" type="xs:int"/>
|
|
42
|
+
</xs:extension>
|
|
43
|
+
</xs:complexContent>
|
|
44
|
+
</xs:complexType>
|
|
45
|
+
</xs:element>
|
|
46
|
+
</xs:schema>
|
|
47
|
+
XSD
|
|
48
|
+
[File.join(dir, "f.xsd"), File.join(dir, "g.xsd")]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# The binding carries the face from 1.9.386.0 (engine 1.9.387);
|
|
52
|
+
# published bindings below it loud-fail by contract, so the live
|
|
53
|
+
# examples skip until the ride lands.
|
|
54
|
+
def set_capable?
|
|
55
|
+
defined?(Leptris::XML::XSD::Schema) &&
|
|
56
|
+
Leptris::XML::XSD::Schema.respond_to?(:compile_set)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "admits substitution members declared in importing documents" do
|
|
60
|
+
skip "requires the leptris compile_set face (binding 1.9.386.0+)" unless
|
|
61
|
+
set_capable?
|
|
62
|
+
|
|
63
|
+
Dir.mktmpdir do |dir|
|
|
64
|
+
paths = write_set(dir)
|
|
65
|
+
schema = ctx.xsd_set(*paths)
|
|
66
|
+
doc = ctx.parse(
|
|
67
|
+
'<f:foo xmlns:f="urn:F" xmlns:g="urn:G">' \
|
|
68
|
+
'<f:bar><g:foo1 x="1"><f:bar/></g:foo1></f:bar></f:foo>',
|
|
69
|
+
)
|
|
70
|
+
expect(schema.validate(doc)).to be_empty
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "accepts the paths as an array or as arguments" do
|
|
75
|
+
skip "requires the leptris compile_set face (binding 1.9.386.0+)" unless
|
|
76
|
+
set_capable?
|
|
77
|
+
|
|
78
|
+
Dir.mktmpdir do |dir|
|
|
79
|
+
paths = write_set(dir)
|
|
80
|
+
expect(ctx.xsd_set(paths)).to be_a(Moxml::XSD::Schema)
|
|
81
|
+
expect(ctx.xsd_set(*paths)).to be_a(Moxml::XSD::Schema)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "raises on an empty set" do
|
|
86
|
+
expect { ctx.xsd_set }.to raise_error(ArgumentError)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "raises the named error on adapters without a set-capable engine" do
|
|
90
|
+
nk = Moxml.new(:nokogiri)
|
|
91
|
+
Dir.mktmpdir do |dir|
|
|
92
|
+
paths = write_set(dir)
|
|
93
|
+
expect { nk.xsd_set(*paths) }
|
|
94
|
+
.to raise_error(Moxml::NotImplementedError, /compile_set face/)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -101,6 +101,10 @@ module ModelCoverage
|
|
|
101
101
|
section: "Validation",
|
|
102
102
|
pin: "spec/moxml/xsd_builtin_lexical_spec.rb",
|
|
103
103
|
},
|
|
104
|
+
"validation schema sets" => {
|
|
105
|
+
section: "Validation",
|
|
106
|
+
pin: "spec/moxml/xsd_set_spec.rb",
|
|
107
|
+
},
|
|
104
108
|
"validation substitution dismissal" => {
|
|
105
109
|
section: "Validation",
|
|
106
110
|
pin: "spec/moxml/xsd_substitution_spec.rb",
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: moxml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.171
|
|
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-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: |
|
|
14
14
|
Moxml is a unified XML manipulation library that provides a common API
|
|
@@ -512,6 +512,7 @@ files:
|
|
|
512
512
|
- spec/moxml/adapter/rexml_spec.rb
|
|
513
513
|
- spec/moxml/adapter/shared_examples/.gitkeep
|
|
514
514
|
- spec/moxml/adapter/shared_examples/adapter_contract.rb
|
|
515
|
+
- spec/moxml/adapter_deprecation_spec.rb
|
|
515
516
|
- spec/moxml/adapter_spec.rb
|
|
516
517
|
- spec/moxml/allocation_benchmark_spec.rb
|
|
517
518
|
- spec/moxml/allocation_guard_spec.rb
|
|
@@ -638,6 +639,7 @@ files:
|
|
|
638
639
|
- spec/moxml/xsd_handle_mismatch_spec.rb
|
|
639
640
|
- spec/moxml/xsd_regex_spec.rb
|
|
640
641
|
- spec/moxml/xsd_release_spec.rb
|
|
642
|
+
- spec/moxml/xsd_set_spec.rb
|
|
641
643
|
- spec/moxml/xsd_spec.rb
|
|
642
644
|
- spec/moxml/xsd_substitution_spec.rb
|
|
643
645
|
- spec/moxml/xsd_underdetection_spec.rb
|