canon 0.3.6 → 0.3.8
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/lib/canon/version.rb +1 -1
- data/lib/canon/xml/data_model.rb +48 -36
- data/lib/canon/xml/sax.rb +8 -11
- data/lib/canon/xml_backend.rb +12 -11
- 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: 7e6a28999cdd57c9fb642e7f334546ef2ebe4802057481ef9c9bcf93252b8590
|
|
4
|
+
data.tar.gz: 072ae76310a6f936b81a73f2305bac61cb0b1100f36509724f67c5a763fb718f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '049a48f66a243b20993f677e8789eff4b08ed72f0b22fa9288453d3806718f2a9f7414c7774e51de30290d19d5b012419f50eb8cdf75762944b4f5c28ed6d54b'
|
|
7
|
+
data.tar.gz: b57afbf6f649b27c8a4833742673f2ae7dea0a4608610795d8ae38c76398ed66e833d4a5d29f832e45ecb1f5ced1e23125afc6b383f45ffc545b6257aca986bb
|
data/lib/canon/version.rb
CHANGED
data/lib/canon/xml/data_model.rb
CHANGED
|
@@ -205,9 +205,15 @@ inherited_namespaces: nil)
|
|
|
205
205
|
# scopes cannot flow down during the stream).
|
|
206
206
|
|
|
207
207
|
def self.from_moxml_xml(xml_string, preserve_whitespace:)
|
|
208
|
-
|
|
209
|
-
|
|
208
|
+
# strict: false keeps the Nokogiri-engine contract — malformed
|
|
209
|
+
# input yields a recovered document plus diagnostics instead of
|
|
210
|
+
# an exception (moxml 0.5.15 defaults strict to true; issue
|
|
211
|
+
# #147 rides recover errors on Document#parse_errors).
|
|
212
|
+
doc = Canon::XmlParsing.moxml_context.parse(xml_string,
|
|
213
|
+
readonly: true,
|
|
214
|
+
strict: false)
|
|
210
215
|
result = build_from_moxml(doc, preserve_whitespace: preserve_whitespace)
|
|
216
|
+
result.parse_errors = doc.parse_errors if doc.parse_errors.any?
|
|
211
217
|
# Canon owns this document's full lifecycle: the canon tree holds
|
|
212
218
|
# no engine references once built, so release the native tree now
|
|
213
219
|
# instead of waiting for the GC finalizer (moxml#134; no-op on
|
|
@@ -216,23 +222,6 @@ inherited_namespaces: nil)
|
|
|
216
222
|
result
|
|
217
223
|
end
|
|
218
224
|
|
|
219
|
-
def self.check_moxml_relative_namespace_uris(node)
|
|
220
|
-
case node
|
|
221
|
-
when Moxml::Element
|
|
222
|
-
node.namespace_definitions.each do |ns|
|
|
223
|
-
href = ns.uri
|
|
224
|
-
next if href.nil? || href.empty?
|
|
225
|
-
if relative_uri?(href)
|
|
226
|
-
raise Canon::Error,
|
|
227
|
-
"Relative namespace URI not allowed: #{href}"
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
node.children.each { |child| check_moxml_relative_namespace_uris(child) }
|
|
231
|
-
when Moxml::Document
|
|
232
|
-
node.children.each { |child| check_moxml_relative_namespace_uris(child) }
|
|
233
|
-
end
|
|
234
|
-
end
|
|
235
|
-
|
|
236
225
|
def self.build_from_moxml(moxml_doc, preserve_whitespace: false)
|
|
237
226
|
root = Nodes::RootNode.new
|
|
238
227
|
skip_types = [Moxml::Doctype]
|
|
@@ -260,32 +249,52 @@ preserve_whitespace: false)
|
|
|
260
249
|
frames = []
|
|
261
250
|
own_namespaces = {}.compare_by_identity
|
|
262
251
|
|
|
263
|
-
|
|
252
|
+
# materialize_fields: the zero-allocation hot path (moxml#143).
|
|
253
|
+
# Flat reused buffers — attributes stride 4, namespaces stride
|
|
254
|
+
# 2 — valid only inside the block, so the element builder
|
|
255
|
+
# copies them into pairs before returning.
|
|
256
|
+
moxml_element.materialize_fields do |kind, qname, prefix, namespace_uri, namespaces, attributes, text, depth|
|
|
264
257
|
# The record contract is root-subtree-only since moxml 0.5.11
|
|
265
258
|
# (moxml#140). Older 0.5.x releases — still allowed by canon's
|
|
266
259
|
# gemspec floor — leaked epilog document-level records at depth
|
|
267
260
|
# 0, which the document-children enumeration also covers; one
|
|
268
261
|
# integer compare per record keeps those working.
|
|
269
|
-
next if
|
|
262
|
+
next if depth.zero? && kind != :element
|
|
263
|
+
|
|
264
|
+
# Relative-namespace validation rides the stream (the
|
|
265
|
+
# namespaces buffer is the element's own declarations) — no
|
|
266
|
+
# separate wrapper-tree walk per parse.
|
|
267
|
+
if kind == :element
|
|
268
|
+
i = 1
|
|
269
|
+
while i < namespaces.size
|
|
270
|
+
href = namespaces[i]
|
|
271
|
+
if !href.nil? && !href.empty? && relative_uri?(href)
|
|
272
|
+
raise Canon::Error,
|
|
273
|
+
"Relative namespace URI not allowed: #{href}"
|
|
274
|
+
end
|
|
275
|
+
i += 2
|
|
276
|
+
end
|
|
277
|
+
end
|
|
270
278
|
|
|
271
|
-
node = case
|
|
279
|
+
node = case kind
|
|
272
280
|
when :element
|
|
273
|
-
|
|
274
|
-
|
|
281
|
+
build_moxml_element_from_fields(
|
|
282
|
+
qname, prefix, namespace_uri, namespaces, attributes,
|
|
283
|
+
depth, frames, own_namespaces
|
|
284
|
+
)
|
|
275
285
|
when :text, :cdata
|
|
276
|
-
content =
|
|
286
|
+
content = text.to_s
|
|
277
287
|
TreeBuilder::DEFAULT.text(content,
|
|
278
288
|
keep: WhitespacePolicy.keep_dom_text?(
|
|
279
289
|
content, preserve_whitespace: preserve_whitespace
|
|
280
290
|
))
|
|
281
291
|
when :comment
|
|
282
|
-
TreeBuilder::DEFAULT.comment(
|
|
292
|
+
TreeBuilder::DEFAULT.comment(text)
|
|
283
293
|
when :processing_instruction
|
|
284
|
-
TreeBuilder::DEFAULT.processing_instruction(
|
|
285
|
-
record[:text] || "")
|
|
294
|
+
TreeBuilder::DEFAULT.processing_instruction(qname, text || "")
|
|
286
295
|
end
|
|
287
296
|
|
|
288
|
-
frames.push([
|
|
297
|
+
frames.push([depth, node]) if node
|
|
289
298
|
end
|
|
290
299
|
|
|
291
300
|
top = frames.last
|
|
@@ -295,21 +304,24 @@ preserve_whitespace: false)
|
|
|
295
304
|
top[1]
|
|
296
305
|
end
|
|
297
306
|
|
|
298
|
-
def self.
|
|
307
|
+
def self.build_moxml_element_from_fields(qname, prefix, namespace_uri,
|
|
308
|
+
namespaces, attributes, depth,
|
|
309
|
+
frames, own_namespaces)
|
|
299
310
|
element = TreeBuilder::DEFAULT.element(
|
|
300
|
-
name:
|
|
301
|
-
prefix:
|
|
302
|
-
namespace_uri:
|
|
303
|
-
attributes:
|
|
311
|
+
name: qname,
|
|
312
|
+
prefix: prefix,
|
|
313
|
+
namespace_uri: namespace_uri,
|
|
314
|
+
attributes: attributes.each_slice(4).to_a,
|
|
304
315
|
)
|
|
305
316
|
|
|
306
317
|
# Adopt completed children: they pop in reverse order; reversing
|
|
307
318
|
# once is O(n) (unshift per child would be O(n^2) on wide trees).
|
|
308
319
|
children = []
|
|
309
|
-
children << frames.pop[1] while frames.any? && frames.last[0] >
|
|
320
|
+
children << frames.pop[1] while frames.any? && frames.last[0] > depth
|
|
310
321
|
children.reverse_each { |child| element.add_child(child) }
|
|
311
322
|
|
|
312
|
-
|
|
323
|
+
# Copy the declarations out of the reused buffer.
|
|
324
|
+
own_namespaces[element] = namespaces.each_slice(2).to_a
|
|
313
325
|
element
|
|
314
326
|
end
|
|
315
327
|
|
data/lib/canon/xml/sax.rb
CHANGED
|
@@ -10,16 +10,13 @@ module Canon
|
|
|
10
10
|
# API to that protocol. OCP: a new engine is a new driver plus one line
|
|
11
11
|
# here; the builder never changes.
|
|
12
12
|
#
|
|
13
|
-
# Engine choice
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# Nokogiri driver; the flip is the one-line change below. With
|
|
21
|
-
# leptris absent (moxml resolving nokogiri) the driver calls Nokogiri
|
|
22
|
-
# directly — the wrapper layer only adds overhead there.
|
|
13
|
+
# Engine choice follows the resolved adapter — independent of the
|
|
14
|
+
# DOM engine default: leptris SAX (corruption leptris#625 and the
|
|
15
|
+
# missing xmlns reporting leptris-ruby#99 both fixed) builds canon
|
|
16
|
+
# trees at parity with or faster than Nokogiri SAX, CI-gated. The
|
|
17
|
+
# DOM engine default stays Nokogiri until the record conversion
|
|
18
|
+
# passes the same gate (see XmlBackend). With leptris absent the
|
|
19
|
+
# driver calls Nokogiri directly.
|
|
23
20
|
module Sax
|
|
24
21
|
autoload :NokogiriDriver, "canon/xml/sax/nokogiri_driver"
|
|
25
22
|
autoload :MoxmlDriver, "canon/xml/sax/moxml_driver"
|
|
@@ -27,7 +24,7 @@ module Canon
|
|
|
27
24
|
class << self
|
|
28
25
|
# Drive `builder` with the runtime's SAX engine.
|
|
29
26
|
def parse(xml_string, builder)
|
|
30
|
-
if RUBY_ENGINE == "opal"
|
|
27
|
+
if RUBY_ENGINE == "opal" || Canon::XmlParsing.moxml_adapter_name != :nokogiri
|
|
31
28
|
MoxmlDriver.new(builder).parse(xml_string)
|
|
32
29
|
else
|
|
33
30
|
NokogiriDriver.new(builder).parse(xml_string)
|
data/lib/canon/xml_backend.rb
CHANGED
|
@@ -9,16 +9,13 @@ module Canon
|
|
|
9
9
|
# - HTML support — always Nokogiri on CRuby; moxml has no HTML adapter and
|
|
10
10
|
# leptris no HTML parser (see Canon::Html::NokogiriSupport).
|
|
11
11
|
#
|
|
12
|
-
# SSOT: moxml owns adapter preference (Moxml::Config prefers leptris
|
|
13
|
-
# installed
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# emission upstream. Flipping the default is the one-line change in
|
|
20
|
-
# #detect; CANON_XML_BACKEND=moxml opts in today. Under Opal the engine
|
|
21
|
-
# is moxml (rexml adapter).
|
|
12
|
+
# SSOT: moxml owns adapter preference (Moxml::Config prefers leptris
|
|
13
|
+
# when installed; see XmlParsing.moxml_adapter_name). Both engines
|
|
14
|
+
# follow the resolved adapter: SAX since leptris#625/#99 were fixed,
|
|
15
|
+
# DOM since moxml#143's materialize_fields stream (zero-allocation
|
|
16
|
+
# flat buffers) made the conversion competitive. Conformance parity
|
|
17
|
+
# is complete (engine_parity_spec 12/12). CANON_XML_BACKEND forces
|
|
18
|
+
# either engine; the CI performance gate is the standing referee.
|
|
22
19
|
#
|
|
23
20
|
# HTML stays on Nokogiri on CRuby (Canon::Html::NokogiriSupport) and the
|
|
24
21
|
# pretty-printers keep the Nokogiri pipeline — pretty-printed bytes are
|
|
@@ -63,7 +60,11 @@ module Canon
|
|
|
63
60
|
def detect
|
|
64
61
|
return :moxml if RUBY_ENGINE == "opal"
|
|
65
62
|
|
|
66
|
-
:
|
|
63
|
+
# Both engines now run leptris: SAX since leptris#625/#99
|
|
64
|
+
# (CI-gated), DOM since moxml#143's zero-allocation
|
|
65
|
+
# materialize_fields stream made the conversion competitive.
|
|
66
|
+
# The CI performance gate is the standing referee.
|
|
67
|
+
XmlParsing.moxml_adapter_name == :nokogiri ? :nokogiri : :moxml
|
|
67
68
|
end
|
|
68
69
|
end
|
|
69
70
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: canon
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.8
|
|
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-08-
|
|
11
|
+
date: 2026-08-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: diff-lcs
|