moxml 0.5.9 → 0.5.10
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/moxml/adapter/leptris.rb +63 -11
- data/lib/moxml/element.rb +10 -0
- data/lib/moxml/materializer.rb +5 -0
- data/lib/moxml/version.rb +1 -1
- data/spec/moxml/adapter/leptris_spec.rb +23 -9
- data/spec/moxml/materializer_spec.rb +5 -1
- 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: 0307f3c9e6060b1ca344c69633788790dc899121f213be74870be3cd2c2600ac
|
|
4
|
+
data.tar.gz: 22135905e79bcb1099a04809bedb69c734b1893ee4fa4749761427641bfcf9b2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 50413c5cddd8680fc5c6cf4bd67dcf75e54e0cbf202b45f1ff5b985648ff948286b92c0a4efc9ef725d9fd0af677e0239eefb7fec37f27e43ece9795240a5db7
|
|
7
|
+
data.tar.gz: 696949b3fb51358b4841e2a2fdeb68824659a7ffefdbb6ac4a2006758006d649ddb64127728cd2b5b653cc54dd888e14566d012d5d6b6c382ce012f3b0ea0112
|
|
@@ -19,6 +19,11 @@ module Moxml
|
|
|
19
19
|
# programmatic DOCTYPE, so those live in CustomizedLeptris value
|
|
20
20
|
# objects stored through NativeAttachment.
|
|
21
21
|
class Leptris < Base
|
|
22
|
+
# libleptris 1.9.7 (leptris-ruby 1.9.26): Document#node exposes
|
|
23
|
+
# the libxml2-model document node — prolog/epilog parts in
|
|
24
|
+
# document order. Older bindings keep the flat pre-root PI path.
|
|
25
|
+
DOC_NODE_SUPPORTED = ::Leptris::XML::Document.method_defined?(:node)
|
|
26
|
+
|
|
22
27
|
class << self
|
|
23
28
|
def attachments
|
|
24
29
|
@attachments ||= Moxml::NativeAttachment.new
|
|
@@ -283,6 +288,12 @@ module Moxml
|
|
|
283
288
|
qname: node.name,
|
|
284
289
|
prefix: node.prefix,
|
|
285
290
|
namespace_uri: ns&.href,
|
|
291
|
+
# Own declarations only — same shape as the generic
|
|
292
|
+
# path's Element#declared_namespaces (issue #138).
|
|
293
|
+
namespaces: begin
|
|
294
|
+
decls = node.namespace_definitions.map { |d| [d.prefix, d.href] }
|
|
295
|
+
decls.empty? ? Materializer::EMPTY_ATTRIBUTES : decls
|
|
296
|
+
end,
|
|
286
297
|
attributes: attributes,
|
|
287
298
|
text: nil,
|
|
288
299
|
depth: material_depth(node, depth_memo),
|
|
@@ -295,6 +306,7 @@ module Moxml
|
|
|
295
306
|
qname: nil,
|
|
296
307
|
prefix: nil,
|
|
297
308
|
namespace_uri: nil,
|
|
309
|
+
namespaces: Materializer::EMPTY_ATTRIBUTES,
|
|
298
310
|
attributes: Materializer::EMPTY_ATTRIBUTES,
|
|
299
311
|
text: text,
|
|
300
312
|
depth: material_depth(node, depth_memo),
|
|
@@ -971,9 +983,22 @@ module Moxml
|
|
|
971
983
|
native = native_doctype_xml(doc)
|
|
972
984
|
parts << native << "\n" if native
|
|
973
985
|
|
|
974
|
-
|
|
986
|
+
if DOC_NODE_SUPPORTED
|
|
987
|
+
# The libxml2-model document node: prolog PIs/comments,
|
|
988
|
+
# the root, epilog PIs/comments — in document order, so
|
|
989
|
+
# epilog parts serialize after the root (issue #130).
|
|
990
|
+
doc_children = document_node_children(doc)
|
|
991
|
+
if doc_children
|
|
992
|
+
doc_children.each { |child| parts << raw_serialize(child, options) << "\n" }
|
|
993
|
+
else
|
|
994
|
+
document_pi_nodes(doc).each { |pi| parts << pi.to_xml << "\n" }
|
|
995
|
+
parts << raw_serialize(doc.root, options) << "\n" if doc.root
|
|
996
|
+
end
|
|
997
|
+
else
|
|
998
|
+
document_pi_nodes(doc).each { |pi| parts << pi.to_xml << "\n" }
|
|
975
999
|
|
|
976
|
-
|
|
1000
|
+
parts << raw_serialize(doc.root, options) << "\n" if doc.root
|
|
1001
|
+
end
|
|
977
1002
|
|
|
978
1003
|
texts = attachments.get(doc, :document_text)
|
|
979
1004
|
texts&.each { |text| parts << XmlEmitter.escape_text(text.content.to_s) }
|
|
@@ -1046,6 +1071,20 @@ module Moxml
|
|
|
1046
1071
|
# the same objects, so wrapper mutations round-trip. Build the
|
|
1047
1072
|
# cache BEFORE appending a PI with add_pi, or the C-side
|
|
1048
1073
|
# addition would be double-counted.
|
|
1074
|
+
# The document node's children, or nil when the node does not
|
|
1075
|
+
# reflect reality: parsed documents always list the root
|
|
1076
|
+
# element among their children, but programmatically built
|
|
1077
|
+
# ones do not (binding gap) — those keep the legacy parts
|
|
1078
|
+
# path.
|
|
1079
|
+
def document_node_children(doc)
|
|
1080
|
+
doc_children = doc.children.to_a
|
|
1081
|
+
has_root = doc_children.any?(::Leptris::XML::Element)
|
|
1082
|
+
return doc_children if has_root
|
|
1083
|
+
return doc_children if doc.root.nil?
|
|
1084
|
+
|
|
1085
|
+
nil
|
|
1086
|
+
end
|
|
1087
|
+
|
|
1049
1088
|
def document_pi_nodes(doc)
|
|
1050
1089
|
attachments.get(doc, :doc_pi_nodes) || begin
|
|
1051
1090
|
nodes = doc.processing_instructions.map do |(target, data)|
|
|
@@ -1065,16 +1104,29 @@ module Moxml
|
|
|
1065
1104
|
doctype_wrapper = attachments.get(doc, :doctype)
|
|
1066
1105
|
children << doctype_wrapper if doctype_wrapper
|
|
1067
1106
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1107
|
+
if DOC_NODE_SUPPORTED
|
|
1108
|
+
# The libxml2-model document node lists prolog PIs/comments,
|
|
1109
|
+
# the root, and epilog PIs/comments in document order —
|
|
1110
|
+
# the Nokogiri-shaped contract, epilog anchoring included
|
|
1111
|
+
# (issue #130). Built (programmatic) documents are not yet
|
|
1112
|
+
# fully reflected by the node (binding gap: an attached
|
|
1113
|
+
# root does not appear); document_node_children answers
|
|
1114
|
+
# nil there for the legacy parts path.
|
|
1115
|
+
doc_children = document_node_children(doc)
|
|
1116
|
+
if doc_children
|
|
1117
|
+
children.concat(doc_children)
|
|
1118
|
+
else
|
|
1119
|
+
children.concat(document_pi_nodes(doc))
|
|
1120
|
+
children << doc.root if doc.root
|
|
1121
|
+
end
|
|
1122
|
+
else
|
|
1123
|
+
# Legacy path: document-level PIs live outside the element
|
|
1124
|
+
# tree in a flat pre-root list (libleptris < 1.9.7 C
|
|
1125
|
+
# model); epilog anchoring is not representable there.
|
|
1126
|
+
children.concat(document_pi_nodes(doc))
|
|
1076
1127
|
|
|
1077
|
-
|
|
1128
|
+
children << doc.root if doc.root
|
|
1129
|
+
end
|
|
1078
1130
|
|
|
1079
1131
|
texts = attachments.get(doc, :document_text)
|
|
1080
1132
|
children.concat(texts) if texts
|
data/lib/moxml/element.rb
CHANGED
|
@@ -119,6 +119,16 @@ module Moxml
|
|
|
119
119
|
end
|
|
120
120
|
alias namespace_definitions namespaces
|
|
121
121
|
|
|
122
|
+
# The element's OWN namespace declarations as [prefix, uri]
|
|
123
|
+
# pairs (nil prefix = default namespace) — not the inherited
|
|
124
|
+
# scope. The shape materialize records carry (issue #138).
|
|
125
|
+
def declared_namespaces
|
|
126
|
+
adapter.namespace_definitions(@native).map do |ns|
|
|
127
|
+
wrapper = Namespace.new(ns, context)
|
|
128
|
+
[wrapper.prefix, wrapper.uri]
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
122
132
|
# Returns all namespaces in scope for this element,
|
|
123
133
|
# including those inherited from ancestor elements.
|
|
124
134
|
def in_scope_namespaces
|
data/lib/moxml/materializer.rb
CHANGED
|
@@ -69,6 +69,10 @@ module Moxml
|
|
|
69
69
|
qname: element.name,
|
|
70
70
|
prefix: element.namespace_prefix,
|
|
71
71
|
namespace_uri: ns&.uri,
|
|
72
|
+
# The element's OWN namespace declarations ([prefix, uri]
|
|
73
|
+
# pairs; nil prefix = default), not the in-scope set — enough
|
|
74
|
+
# for a consumer to rebuild scope while walking (issue #138).
|
|
75
|
+
namespaces: element.declared_namespaces,
|
|
72
76
|
attributes: attributes,
|
|
73
77
|
text: nil,
|
|
74
78
|
depth: depth,
|
|
@@ -81,6 +85,7 @@ module Moxml
|
|
|
81
85
|
qname: nil,
|
|
82
86
|
prefix: nil,
|
|
83
87
|
namespace_uri: nil,
|
|
88
|
+
namespaces: EMPTY_ATTRIBUTES,
|
|
84
89
|
attributes: EMPTY_ATTRIBUTES,
|
|
85
90
|
text: text,
|
|
86
91
|
depth: depth,
|
data/lib/moxml/version.rb
CHANGED
|
@@ -85,37 +85,51 @@ RSpec.describe Moxml::Adapter::Leptris do
|
|
|
85
85
|
let(:ctx) { Moxml.new(:leptris) }
|
|
86
86
|
|
|
87
87
|
it "lists document PIs as children with the root" do
|
|
88
|
-
doc = ctx.parse('<?xml version="1.0"?><?pi-prolog before?><root/><?pi-epilog after
|
|
88
|
+
doc = ctx.parse('<?xml version="1.0"?><?pi-prolog before?><root/><?pi-epilog after?><!-- tail -->')
|
|
89
89
|
kids = doc.children.to_a
|
|
90
90
|
|
|
91
91
|
expect(kids.map(&:class)).to eq(
|
|
92
|
-
[Moxml::ProcessingInstruction, Moxml::
|
|
92
|
+
[Moxml::ProcessingInstruction, Moxml::Element,
|
|
93
|
+
Moxml::ProcessingInstruction, Moxml::Comment],
|
|
93
94
|
)
|
|
94
95
|
expect(kids.select(&:processing_instruction?).map(&:target)).to eq(%w[pi-prolog pi-epilog])
|
|
95
96
|
expect(kids[0].content).to eq("before")
|
|
97
|
+
expect(doc.to_xml.index("pi-epilog")).to be > doc.to_xml.index("</root>")
|
|
98
|
+
expect(doc.to_xml.index("<!-- tail -->")).to be > doc.to_xml.index("</root>")
|
|
96
99
|
end
|
|
97
100
|
|
|
98
|
-
it "round-trips
|
|
99
|
-
doc = ctx.parse("
|
|
100
|
-
pi = doc.children.to_a
|
|
101
|
+
it "round-trips tree-level PI mutations through serialization" do
|
|
102
|
+
doc = ctx.parse("<root><a><?pi-original x?></a></root>")
|
|
103
|
+
pi = doc.at_xpath("//a").children.to_a.find(&:processing_instruction?)
|
|
101
104
|
pi.target = "renamed"
|
|
102
105
|
pi.content = "changed"
|
|
103
106
|
expect(doc.to_xml).to include("<?renamed changed?>")
|
|
107
|
+
end
|
|
104
108
|
|
|
109
|
+
it "adds document PIs and lists them as children" do
|
|
110
|
+
doc = ctx.parse("<root/>")
|
|
105
111
|
doc.add_child(doc.create_processing_instruction("added", "now"))
|
|
106
112
|
expect(doc.to_xml).to include("<?added now?>")
|
|
107
|
-
expect(doc.children.to_a.select(&:processing_instruction?).map(&:target)).to eq(%w[
|
|
113
|
+
expect(doc.children.to_a.select(&:processing_instruction?).map(&:target)).to eq(%w[added])
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "reports document-level PI mutation as unsupported on the native path" do
|
|
117
|
+
# libleptris 1.9.7 exposes document PIs read-only: target=/data=
|
|
118
|
+
# and unlink are rejected for nodes outside the element tree.
|
|
119
|
+
doc = ctx.parse("<?pi x?><root/>")
|
|
120
|
+
pi = doc.children.to_a[0]
|
|
121
|
+
|
|
122
|
+
expect { pi.target = "renamed" }.to raise_error(Leptris::XML::Error)
|
|
108
123
|
end
|
|
109
124
|
|
|
110
125
|
it "serializes children and document output in agreement" do
|
|
111
126
|
# libleptris stores document PIs as one flat pre-root list (no
|
|
112
127
|
# epilog anchoring); children and to_xml must at least agree.
|
|
113
128
|
doc = ctx.parse('<?xml version="1.0"?><?pi-a 1?><root/><?pi-b 2?>')
|
|
114
|
-
|
|
115
|
-
.map(&:to_xml).join("\n")
|
|
129
|
+
parts = doc.children.to_a.map { |c| "#{c.to_xml}\n" }.join
|
|
116
130
|
from_document = doc.to_xml.sub(%r{\A<\?xml[^>]*\?>\n}, "")
|
|
117
131
|
|
|
118
|
-
expect(from_document).to
|
|
132
|
+
expect(from_document).to eq(parts)
|
|
119
133
|
end
|
|
120
134
|
|
|
121
135
|
it "matches raw Nokogiri byte-for-byte for pretty-printing (issue #129)" do
|
|
@@ -36,6 +36,10 @@ RSpec.describe Moxml::Materializer do
|
|
|
36
36
|
expect(book[:namespace_uri]).to eq("urn:c")
|
|
37
37
|
expect(book[:text]).to be_nil
|
|
38
38
|
|
|
39
|
+
catalog = records.last
|
|
40
|
+
expect(catalog[:namespaces]).to eq([[nil, "urn:c"], ["p", "urn:p"]])
|
|
41
|
+
expect(book[:namespaces]).to eq([])
|
|
42
|
+
|
|
39
43
|
title_text = records.find { |r| r[:kind] == :text && r[:text] == "T" }
|
|
40
44
|
expect(title_text[:attributes]).to eq([])
|
|
41
45
|
end
|
|
@@ -77,6 +81,6 @@ RSpec.describe Moxml::Materializer do
|
|
|
77
81
|
|
|
78
82
|
# Records themselves allocate (hash + FFI strings); the wrapper
|
|
79
83
|
# tree does not: far fewer allocations than 2 wrappers per node.
|
|
80
|
-
expect(allocated).to be < node_count *
|
|
84
|
+
expect(allocated).to be < node_count * 24
|
|
81
85
|
end
|
|
82
86
|
end
|
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.10
|
|
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-28 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
|