moxml 0.5.119 → 0.5.121
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/benchmarks/generate_report.rb +2 -2
- data/lib/moxml/adapter/base.rb +24 -0
- data/lib/moxml/adapter/leptris.rb +54 -5
- data/lib/moxml/context.rb +8 -0
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml/xsd.rb +37 -0
- data/spec/moxml/adapter/leptris_adoption_spec.rb +42 -0
- data/spec/moxml/xsd_spec.rb +47 -1
- data/spec/performance/benchmark_spec.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3e70385e5a2ffc34bc69d65011c6fead0dfdb3311ac44d68893990b714aa750b
|
|
4
|
+
data.tar.gz: 1d7d88feac80fdef1a56a5e09d333ee350bd9e2cc19c42a26e57a5283e8ab8ef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 846a93feb34b6de7dab678e91b72cfe8691b17fab1987605906919225ce406204b4beb2dbae3d8e95b54bdf42f1ec9895598c22e49d3816d866241f5cb6e0fad
|
|
7
|
+
data.tar.gz: 915721b24c27eef6790fa31f22f82dd1f6b3d2998b9c5824c4515a50f4d0500ff2bd40874b414f8a02dce433d90455eda067dd07f35ad127df46182dac8133a1
|
|
@@ -143,10 +143,10 @@ class MoxmlBenchmarkReport
|
|
|
143
143
|
end
|
|
144
144
|
|
|
145
145
|
def benchmark_parse(context, xml)
|
|
146
|
-
|
|
146
|
+
nil
|
|
147
147
|
Benchmark.ips do |x|
|
|
148
148
|
x.config(time: 2, warmup: 1)
|
|
149
|
-
x.report("parse") {
|
|
149
|
+
x.report("parse") { context.parse(xml) }
|
|
150
150
|
end.entries.first.ips.round(2)
|
|
151
151
|
rescue StandardError => e
|
|
152
152
|
@errors[context.config.adapter_name] << "Parse error: #{e.message}"
|
data/lib/moxml/adapter/base.rb
CHANGED
|
@@ -272,6 +272,30 @@ module Moxml
|
|
|
272
272
|
"XSD validation requires an adapter with an XSD engine"
|
|
273
273
|
end
|
|
274
274
|
|
|
275
|
+
def xsd_compile_file(_path)
|
|
276
|
+
raise Moxml::NotImplementedError,
|
|
277
|
+
"XSD validation requires an adapter with an XSD engine"
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def xsd_builtin_valid?(_builtin, _lexical)
|
|
281
|
+
raise Moxml::NotImplementedError,
|
|
282
|
+
"XSD validation requires an adapter with an XSD engine"
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def xsd_declarations(_xsd_handle); end
|
|
286
|
+
|
|
287
|
+
def xsd_compile_error(_xsd_handle); end
|
|
288
|
+
|
|
289
|
+
def xsd_simple_valid?(_xsd_handle, _type_name, _lexical)
|
|
290
|
+
raise Moxml::NotImplementedError,
|
|
291
|
+
"XSD validation requires an adapter with an XSD engine"
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def xsd_content_valid?(_xsd_handle, _element_name, _children)
|
|
295
|
+
raise Moxml::NotImplementedError,
|
|
296
|
+
"XSD validation requires an adapter with an XSD engine"
|
|
297
|
+
end
|
|
298
|
+
|
|
275
299
|
# Apply a compiled stylesheet; returns the engine's result
|
|
276
300
|
# document, or nil when the output method cannot produce one
|
|
277
301
|
# (text). @params is the flat quoted name/value list.
|
|
@@ -239,6 +239,44 @@ module Moxml
|
|
|
239
239
|
::Leptris::XML::XSD.compile(schema_source)
|
|
240
240
|
end
|
|
241
241
|
|
|
242
|
+
def xsd_compile_file(path)
|
|
243
|
+
unless XSD_SUPPORTED && ::Leptris::XML::XSD.respond_to?(:compile_file)
|
|
244
|
+
raise Moxml::NotImplementedError,
|
|
245
|
+
"XSD validation requires the leptris 1.9.326+ engine"
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
::Leptris::XML::XSD.compile_file(path)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def xsd_builtin_valid?(builtin, lexical)
|
|
252
|
+
unless XSD_SUPPORTED
|
|
253
|
+
raise Moxml::NotImplementedError,
|
|
254
|
+
"XSD validation requires an adapter with an XSD engine"
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
::Leptris::XML::XSD.builtin_valid?(builtin, lexical)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def xsd_declarations(xsd_handle)
|
|
261
|
+
xsd_handle.declaration_count
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def xsd_compile_error(xsd_handle)
|
|
265
|
+
xsd_handle.error
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def xsd_simple_valid?(xsd_handle, type_name, lexical)
|
|
269
|
+
xsd_handle.simple_valid?(type_name, lexical)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def xsd_content_valid?(xsd_handle, element_name, children)
|
|
273
|
+
# The binding reads each child's name + effective
|
|
274
|
+
# namespace through its own node API — pass the natives.
|
|
275
|
+
xsd_handle.content_valid?(
|
|
276
|
+
element_name, children.map(&:native)
|
|
277
|
+
)
|
|
278
|
+
end
|
|
279
|
+
|
|
242
280
|
def xsd_validate(xsd_handle, native_document)
|
|
243
281
|
# +xsd_handle+ is the binding's Leptris::XML::XSD::Schema;
|
|
244
282
|
# validate_errors takes the binding document and returns
|
|
@@ -2220,11 +2258,22 @@ module Moxml
|
|
|
2220
2258
|
# absorbed-source nodes upstream.
|
|
2221
2259
|
child_doc = NN_DOCUMENT.bind_call(child)
|
|
2222
2260
|
parent_doc = NN_DOCUMENT.bind_call(parent)
|
|
2223
|
-
if child_doc && parent_doc &&
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2261
|
+
if child_doc && parent_doc && !child_doc.equal?(parent_doc)
|
|
2262
|
+
if child.node_type == :element
|
|
2263
|
+
absorb_source_document(to_binding(parent_doc), to_binding(child_doc))
|
|
2264
|
+
return structural_adopt(parent, child) unless
|
|
2265
|
+
source_absorbed_into?(parent, child)
|
|
2266
|
+
else
|
|
2267
|
+
# moxml#347: the C add_child's foreign-child
|
|
2268
|
+
# fallback dispatches parent.add_child through
|
|
2269
|
+
# RUBY — on Identity natives the contract modules
|
|
2270
|
+
# shadow the C method, so that dispatch re-enters
|
|
2271
|
+
# Moxml's add_child forever. Foreign content kinds
|
|
2272
|
+
# (text/comment/CDATA/PI) rebuild in the parent's
|
|
2273
|
+
# pool directly; they carry no cross-document
|
|
2274
|
+
# identity worth a move.
|
|
2275
|
+
return rebuild_foreign_child(parent, child)
|
|
2276
|
+
end
|
|
2228
2277
|
end
|
|
2229
2278
|
|
|
2230
2279
|
doc = NN_DOCUMENT.bind_call(parent)
|
data/lib/moxml/context.rb
CHANGED
|
@@ -156,6 +156,14 @@ module Moxml
|
|
|
156
156
|
Moxml::XSD::Schema.new(handle, self)
|
|
157
157
|
end
|
|
158
158
|
|
|
159
|
+
# Compile an XSD schema from a file path — <xs:include href>
|
|
160
|
+
# resolves relative to the file's directory (the engine
|
|
161
|
+
# resolves it; reading the file into #xsd would lose that).
|
|
162
|
+
def xsd_file(path)
|
|
163
|
+
handle = config.adapter.xsd_compile_file(path.to_s)
|
|
164
|
+
Moxml::XSD::Schema.new(handle, self)
|
|
165
|
+
end
|
|
166
|
+
|
|
159
167
|
def xslt(stylesheet)
|
|
160
168
|
source = case stylesheet
|
|
161
169
|
when Moxml::Document then stylesheet.to_xml
|
data/lib/moxml/version.rb
CHANGED
data/lib/moxml/xsd.rb
CHANGED
|
@@ -38,6 +38,14 @@ module Moxml
|
|
|
38
38
|
adapter.const_defined?(:XSD_SUPPORTED) &&
|
|
39
39
|
adapter.const_get(:XSD_SUPPORTED)
|
|
40
40
|
end
|
|
41
|
+
|
|
42
|
+
# Lexical validation against the built-in type table (the
|
|
43
|
+
# "xs:NAME" reference spelling). Raises ArgumentError when the
|
|
44
|
+
# name is not in the table; NotImplementedError on adapters
|
|
45
|
+
# without an engine.
|
|
46
|
+
def builtin_valid?(context, builtin, lexical)
|
|
47
|
+
context.config.adapter.xsd_builtin_valid?(builtin, lexical)
|
|
48
|
+
end
|
|
41
49
|
end
|
|
42
50
|
|
|
43
51
|
# A compiled schema (adapter-owned handle). Validation results
|
|
@@ -61,6 +69,35 @@ module Moxml
|
|
|
61
69
|
def valid?(node_or_xml)
|
|
62
70
|
validate(node_or_xml).empty?
|
|
63
71
|
end
|
|
72
|
+
|
|
73
|
+
# Number of top-level schema declarations the compiler
|
|
74
|
+
# recognized.
|
|
75
|
+
def declarations
|
|
76
|
+
@context.config.adapter.xsd_declarations(@handle)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# The engine's schema-level compile error, or nil when the
|
|
80
|
+
# schema compiled cleanly.
|
|
81
|
+
def compile_error
|
|
82
|
+
@context.config.adapter.xsd_compile_error(@handle)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Lexical validation of +lexical+ against the schema's user
|
|
86
|
+
# simpleType +type_name+. Unknown type names raise.
|
|
87
|
+
def simple_valid?(type_name, lexical)
|
|
88
|
+
@context.config.adapter.xsd_simple_valid?(
|
|
89
|
+
@handle, type_name, lexical
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Content-model check for one element's children. +children+
|
|
94
|
+
# are Moxml nodes (names + effective namespaces are read
|
|
95
|
+
# engine-side). Unknown element names raise.
|
|
96
|
+
def content_valid?(element_name, children)
|
|
97
|
+
@context.config.adapter.xsd_content_valid?(
|
|
98
|
+
@handle, element_name, children
|
|
99
|
+
)
|
|
100
|
+
end
|
|
64
101
|
end
|
|
65
102
|
end
|
|
66
103
|
end
|
|
@@ -146,4 +146,46 @@ RSpec.describe "leptris cross-document adoption" do
|
|
|
146
146
|
expect(doc.at("//r/e").text).to eq("t")
|
|
147
147
|
end
|
|
148
148
|
end
|
|
149
|
+
|
|
150
|
+
# moxml#347: the C add_child's foreign-child fallback dispatches
|
|
151
|
+
# parent.add_child through Ruby — on Identity natives the contract
|
|
152
|
+
# modules shadow the C method and the dispatch re-enters Moxml's
|
|
153
|
+
# add_child (SystemStackError). Foreign content kinds rebuild in
|
|
154
|
+
# the parent's pool; foreign elements absorb/move.
|
|
155
|
+
context "when adding foreign children (moxml#347)" do
|
|
156
|
+
let(:fragment_xml) do
|
|
157
|
+
"<__root__>hello<b attr=\"1\"/>world<!-- note --><?tgt data?><![CDATA[x < y]]></__root__>"
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "appends every node kind from a same-context fragment" do
|
|
161
|
+
doc = ctx.parse("<bibitem/>")
|
|
162
|
+
frag = ctx.parse(fragment_xml)
|
|
163
|
+
frag.root.children.each { |c| doc.root.add_child(c) }
|
|
164
|
+
expect(doc.root.to_xml).to include("<b attr=\"1\">")
|
|
165
|
+
expect(doc.root.text).to include("hello")
|
|
166
|
+
expect(doc.root.to_xml).to include("<!-- note -->")
|
|
167
|
+
expect(doc.root.to_xml).to include("<?tgt data?>")
|
|
168
|
+
expect(doc.root.to_xml).to include("<![CDATA[x < y]]>")
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "appends every node kind across contexts without recursing" do
|
|
172
|
+
doc = ctx.parse("<bibitem/>")
|
|
173
|
+
frag = Moxml.new(:leptris).parse(fragment_xml)
|
|
174
|
+
frag.root.children.each { |c| doc.root.add_child(c) }
|
|
175
|
+
expect(doc.root.to_xml).to include("<b attr=\"1\">")
|
|
176
|
+
expect(doc.root.to_xml).to include("<!-- note -->")
|
|
177
|
+
expect(doc.root.to_xml).to include("<?tgt data?>")
|
|
178
|
+
expect(doc.root.to_xml).to include("<![CDATA[x < y]]>")
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "moves foreign elements by reference (the absorb path stays first)" do
|
|
182
|
+
doc = ctx.parse("<r><a/></r>")
|
|
183
|
+
other = Moxml.new(:leptris).parse("<other><new/></other>")
|
|
184
|
+
child = other.root.children.first
|
|
185
|
+
doc.root.add_child(child)
|
|
186
|
+
expect(doc.root.children.last).to eq(child)
|
|
187
|
+
expect(child["probe"] = "1").to eq("1")
|
|
188
|
+
expect(doc.at("//r/new")["probe"]).to eq("1")
|
|
189
|
+
end
|
|
190
|
+
end
|
|
149
191
|
end
|
data/spec/moxml/xsd_spec.rb
CHANGED
|
@@ -14,9 +14,12 @@ RSpec.describe Moxml::XSD do
|
|
|
14
14
|
<xs:element name="note" type="t_note"/>
|
|
15
15
|
<xs:complexType name="t_note">
|
|
16
16
|
<xs:sequence>
|
|
17
|
-
<xs:element name="to" type="
|
|
17
|
+
<xs:element name="to" type="t_string"/>
|
|
18
18
|
</xs:sequence>
|
|
19
19
|
</xs:complexType>
|
|
20
|
+
<xs:simpleType name="t_string">
|
|
21
|
+
<xs:restriction base="xs:string"/>
|
|
22
|
+
</xs:simpleType>
|
|
20
23
|
</xs:schema>
|
|
21
24
|
XSD
|
|
22
25
|
end
|
|
@@ -55,6 +58,49 @@ RSpec.describe Moxml::XSD do
|
|
|
55
58
|
expect(schema.valid?("<note><wrong/></note>")).to be(false)
|
|
56
59
|
end
|
|
57
60
|
|
|
61
|
+
it "surfaces the lexical, census, and content faces" do
|
|
62
|
+
schema = ctx.xsd(schema_text)
|
|
63
|
+
expect(schema.declarations).to be >= 2
|
|
64
|
+
expect(schema.compile_error).to be_nil
|
|
65
|
+
expect(schema.simple_valid?("t_string", "abc")).to be(true)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "checks built-in lexical forms" do
|
|
69
|
+
expect(described_class.builtin_valid?(ctx, "xs:integer", "3")).to be(true)
|
|
70
|
+
expect(described_class.builtin_valid?(ctx, "xs:integer", "x")).to be(false)
|
|
71
|
+
expect do
|
|
72
|
+
described_class.builtin_valid?(ctx, "xs:nope", "3")
|
|
73
|
+
end.to raise_error(ArgumentError)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "checks content models through moxml nodes" do
|
|
77
|
+
schema = ctx.xsd(schema_text)
|
|
78
|
+
doc = ctx.parse("<note><to>You</to></note>")
|
|
79
|
+
expect(schema.content_valid?("note", [doc.at("//to")])).to be(true)
|
|
80
|
+
expect(schema.content_valid?("note", [])).to be(false)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "compiles from a file with relative includes" do
|
|
84
|
+
require "tmpdir"
|
|
85
|
+
Dir.mktmpdir do |dir|
|
|
86
|
+
File.write(File.join(dir, "main.xsd"), <<~XSD)
|
|
87
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
88
|
+
<xs:include schemaLocation="inc.xsd"/>
|
|
89
|
+
</xs:schema>
|
|
90
|
+
XSD
|
|
91
|
+
File.write(File.join(dir, "inc.xsd"), <<~XSD)
|
|
92
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
93
|
+
<xs:element name="note" type="xs:string"/>
|
|
94
|
+
</xs:schema>
|
|
95
|
+
XSD
|
|
96
|
+
schema = ctx.xsd_file(File.join(dir, "main.xsd"))
|
|
97
|
+
# The include pulled the declaration in (both files' schema
|
|
98
|
+
# elements census).
|
|
99
|
+
expect(schema.declarations).to be >= 1
|
|
100
|
+
expect(schema.valid?("<note>x</note>")).to be(true)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
58
104
|
it "derives XSD_SUPPORTED from the engine face gate" do
|
|
59
105
|
# The capability flag must not outrun the engine faces: a
|
|
60
106
|
# misreleased lockstep (NATIVE_PLAN_STRUCTS lesson) would arm
|
|
@@ -46,10 +46,10 @@ RSpec.shared_examples "Performance Examples" do
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
it "meets Parser performance threshold", :performance do
|
|
49
|
-
|
|
49
|
+
nil
|
|
50
50
|
report = Benchmark.ips do |x|
|
|
51
51
|
x.config(time: 5, warmup: 2)
|
|
52
|
-
x.report("Parser") {
|
|
52
|
+
x.report("Parser") { context.parse(large_xml) }
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
threshold = thresholds.dig(context.config.adapter_name, :parser) || 1
|
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.121
|
|
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-10 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
|