moxml 0.5.41 → 0.5.43
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/docs/_pages/performance.adoc +13 -6
- data/lib/moxml/adapter/leptris.rb +59 -14
- data/lib/moxml/adapter/rexml.rb +1 -0
- data/lib/moxml/version.rb +1 -1
- data/spec/integration/shared_examples/integration_workflows.rb +34 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0c8fd5df5889694c9d7a3ee99c4caad866ddc40edd13ba46945f55e4e9f77a9b
|
|
4
|
+
data.tar.gz: ce14f6e9199e22576c0c5149e716a672efb9e44ef3a9b68d1b78abad02754a92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 93ac1cd541ed454ac89c10179e5e56f919a4cd309ae8f16abe02950ea5a6fedfdb03c53a7a8c23520616cd202a2b8f5384616ac8cd7b9e884d9f127c1d38bd98
|
|
7
|
+
data.tar.gz: ed75757fec960bd181921f9562650f4f3d44f2261fa0eb41a09437906d26581ad07c8b5446d0719a1d26b5308de2e1e25c60d2f0013da6861d144692d53eada4
|
|
@@ -293,12 +293,19 @@ scratch there. 1.9.163.2 moved the bulk into C and fixed the
|
|
|
293
293
|
truncation — newer bindings use their C path (version-memoized,
|
|
294
294
|
cheaper than a per-call count+copy).
|
|
295
295
|
|
|
296
|
-
The native
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
per-
|
|
301
|
-
|
|
296
|
+
The native builder factories (native_create_element/text) are
|
|
297
|
+
adopted from 1.9.163.5, where native mutations became
|
|
298
|
+
version-coherent with the binding (leptris-ruby#204/#208 — both
|
|
299
|
+
surfaces' memos drop on mutation). The earlier rejection — stale
|
|
300
|
+
binding memos plus a per-attach `to_binding` bridge costing more
|
|
301
|
+
than the factory saved (5816 -> 7697ns vs nokogiri 1750) — no
|
|
302
|
+
longer applies: `add_child` carries a both-native fast path with
|
|
303
|
+
no bridge, and `doc_for` reads the 1.9.163.5 `NativeNode#document`
|
|
304
|
+
accessor instead of the parent climb. create ×2 + attach:
|
|
305
|
+
**4846 -> 2333ns (0.30x -> 0.78x of raw nokogiri)**. Attribute
|
|
306
|
+
writes on native nodes still bridge (the native layer has no
|
|
307
|
+
`[]=`), and the native `attributes` hash (name → String) does not
|
|
308
|
+
fit the attribute-node contract — enumeration stays bridged.
|
|
302
309
|
|
|
303
310
|
Serialize (document, 30KB): passing `encoding: "UTF-8"` when it
|
|
304
311
|
equals the document's own ran a conversion pass for byte-identical
|
|
@@ -65,6 +65,15 @@ module Moxml
|
|
|
65
65
|
defined?(::Leptris::XML::NativeNode) &&
|
|
66
66
|
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.163.2")
|
|
67
67
|
|
|
68
|
+
# 1.9.163.5 (leptris-ruby#204/#208): native mutations are
|
|
69
|
+
# version-coherent with the binding (both surfaces' memos
|
|
70
|
+
# drop on mutation) — the builder factories and native
|
|
71
|
+
# add_child are adoptable end-to-end; NativeNode grew a
|
|
72
|
+
# document accessor and line numbers.
|
|
73
|
+
NATIVE_MUTATIONS_COHERENT =
|
|
74
|
+
defined?(::Leptris::XML::NativeNode) &&
|
|
75
|
+
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.163.5")
|
|
76
|
+
|
|
68
77
|
# 1.9.163.2 moved the native bulk children into C
|
|
69
78
|
# (Native.bulk_children) and fixed the 512 truncation
|
|
70
79
|
# (leptris-ruby#202). The 162.6-163.1 window carries the
|
|
@@ -80,7 +89,17 @@ module Moxml
|
|
|
80
89
|
# sites must never depend on the layer having loaded
|
|
81
90
|
# (issue #217 — binding-only installs crashed on the
|
|
82
91
|
# unguarded bridges).
|
|
92
|
+
# Class-body level: a bare @ivar inside `class << self` lands
|
|
93
|
+
# on the singleton's singleton, invisible to the methods at
|
|
94
|
+
# call time (same shape as @native_doc_roots below).
|
|
95
|
+
@binding_of = ObjectSpace::WeakMap.new
|
|
96
|
+
|
|
83
97
|
class << self
|
|
98
|
+
# native -> bridged binding node. The bridge object is
|
|
99
|
+
# identity-stable for the native's lifetime (the binding's
|
|
100
|
+
# per-document wrap cache), so repeat bridges — the write
|
|
101
|
+
# loop on a factory-created element, re-accessed attribute
|
|
102
|
+
# lists — skip the document resolution and pointer wrap.
|
|
84
103
|
# Binding node for any native: identity for binding nodes
|
|
85
104
|
# (and on installs without the native layer — the constant
|
|
86
105
|
# check short-circuits), a Node.wrap over the shared C
|
|
@@ -89,11 +108,14 @@ module Moxml
|
|
|
89
108
|
return node unless NATIVE_READ_LAYER &&
|
|
90
109
|
node.is_a?(::Leptris::XML::NativeNode)
|
|
91
110
|
|
|
111
|
+
bridged = @binding_of[node]
|
|
112
|
+
return bridged if bridged
|
|
113
|
+
|
|
92
114
|
doc = doc_for(node)
|
|
93
115
|
ptr = ::FFI::Pointer.new(node.address)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
116
|
+
bridged = ::Leptris::XML::Node.wrap(ptr, doc)
|
|
117
|
+
@binding_of[node] = bridged unless bridged.nil?
|
|
118
|
+
bridged
|
|
97
119
|
end
|
|
98
120
|
end
|
|
99
121
|
|
|
@@ -130,6 +152,10 @@ module Moxml
|
|
|
130
152
|
# to the root (C-bound reads) and look the doc up in the
|
|
131
153
|
# root registry. Detached subtrees answer nil.
|
|
132
154
|
def doc_for(node)
|
|
155
|
+
# The 1.9.163.5 native layer exposes the owning document
|
|
156
|
+
# directly; the root climb serves older bindings.
|
|
157
|
+
return node.document if NATIVE_MUTATIONS_COHERENT
|
|
158
|
+
|
|
133
159
|
current = node
|
|
134
160
|
current = current.parent while current&.parent
|
|
135
161
|
@native_doc_roots[current]
|
|
@@ -268,7 +294,7 @@ module Moxml
|
|
|
268
294
|
end
|
|
269
295
|
|
|
270
296
|
def set_root(doc, element)
|
|
271
|
-
doc.root = element
|
|
297
|
+
to_binding(doc).root = to_binding(element)
|
|
272
298
|
end
|
|
273
299
|
|
|
274
300
|
def bare_set_qname_safe?
|
|
@@ -477,18 +503,24 @@ module Moxml
|
|
|
477
503
|
::Leptris::XML::Document.create
|
|
478
504
|
end
|
|
479
505
|
|
|
480
|
-
#
|
|
481
|
-
#
|
|
482
|
-
#
|
|
483
|
-
#
|
|
484
|
-
#
|
|
485
|
-
# that costs more than the factory saves (create+attach
|
|
486
|
-
# measured 5816 -> 7697ns vs nokogiri 1750).
|
|
506
|
+
# Native builder factories: one C call and a TypedData wrap.
|
|
507
|
+
# Adopted from 1.9.163.5, where native mutations became
|
|
508
|
+
# version-coherent (leptris-ruby#204/#208) — the earlier
|
|
509
|
+
# rejection (stale binding memos + per-attach bridge cost)
|
|
510
|
+
# no longer applies: add_child carries a native fast path.
|
|
487
511
|
def create_native_element(name, owner_doc = nil)
|
|
512
|
+
if NATIVE_MUTATIONS_COHERENT && owner_doc
|
|
513
|
+
return owner_doc.native_create_element(name.to_s)
|
|
514
|
+
end
|
|
515
|
+
|
|
488
516
|
(owner_doc || create_document).create_element(name.to_s)
|
|
489
517
|
end
|
|
490
518
|
|
|
491
519
|
def create_native_text(content, owner_doc = nil)
|
|
520
|
+
if NATIVE_MUTATIONS_COHERENT && owner_doc
|
|
521
|
+
return owner_doc.native_create_text(content)
|
|
522
|
+
end
|
|
523
|
+
|
|
492
524
|
(owner_doc || create_document).create_text_node(content)
|
|
493
525
|
end
|
|
494
526
|
|
|
@@ -986,6 +1018,11 @@ module Moxml
|
|
|
986
1018
|
end
|
|
987
1019
|
|
|
988
1020
|
def line_number(node)
|
|
1021
|
+
if NATIVE_MUTATIONS_COHERENT &&
|
|
1022
|
+
node.is_a?(::Leptris::XML::NativeNode)
|
|
1023
|
+
line = node.line
|
|
1024
|
+
return line.nil? || line.zero? ? nil : line
|
|
1025
|
+
end
|
|
989
1026
|
return nil unless node.is_a?(::Leptris::XML::Node)
|
|
990
1027
|
|
|
991
1028
|
line = node.line
|
|
@@ -1050,7 +1087,9 @@ module Moxml
|
|
|
1050
1087
|
end
|
|
1051
1088
|
|
|
1052
1089
|
def add_child(parent, child)
|
|
1053
|
-
if NATIVE_READ_LAYER
|
|
1090
|
+
if NATIVE_READ_LAYER && !(NATIVE_MUTATIONS_COHERENT &&
|
|
1091
|
+
parent.is_a?(::Leptris::XML::NativeNode) &&
|
|
1092
|
+
child.is_a?(::Leptris::XML::NativeNode))
|
|
1054
1093
|
parent = to_binding(parent)
|
|
1055
1094
|
child = to_binding(child)
|
|
1056
1095
|
end
|
|
@@ -1070,7 +1109,10 @@ module Moxml
|
|
|
1070
1109
|
end
|
|
1071
1110
|
|
|
1072
1111
|
def add_previous_sibling(node, new_node)
|
|
1073
|
-
|
|
1112
|
+
if NATIVE_READ_LAYER
|
|
1113
|
+
node = to_binding(node)
|
|
1114
|
+
new_node = to_binding(new_node)
|
|
1115
|
+
end
|
|
1074
1116
|
# A PI inserted before the root lives at document level in
|
|
1075
1117
|
# libleptris's model, not in the element tree.
|
|
1076
1118
|
if new_node.is_a?(::Leptris::XML::ProcessingInstruction) &&
|
|
@@ -1082,7 +1124,10 @@ module Moxml
|
|
|
1082
1124
|
end
|
|
1083
1125
|
|
|
1084
1126
|
def add_next_sibling(node, new_node)
|
|
1085
|
-
|
|
1127
|
+
if NATIVE_READ_LAYER
|
|
1128
|
+
node = to_binding(node)
|
|
1129
|
+
new_node = to_binding(new_node)
|
|
1130
|
+
end
|
|
1086
1131
|
node.add_next_sibling(new_node)
|
|
1087
1132
|
end
|
|
1088
1133
|
|
data/lib/moxml/adapter/rexml.rb
CHANGED
data/lib/moxml/version.rb
CHANGED
|
@@ -143,4 +143,38 @@ RSpec.shared_examples "Moxml Integration" do
|
|
|
143
143
|
)
|
|
144
144
|
end
|
|
145
145
|
end
|
|
146
|
+
|
|
147
|
+
describe "document root assignment" do
|
|
148
|
+
it "attaches a factory-created element as the root and serializes" do
|
|
149
|
+
doc = context.create_document
|
|
150
|
+
root = doc.create_element("root")
|
|
151
|
+
root["id"] = "1"
|
|
152
|
+
child = doc.create_element("child")
|
|
153
|
+
child.text = "data"
|
|
154
|
+
root.add_child(child)
|
|
155
|
+
|
|
156
|
+
doc.root = root
|
|
157
|
+
|
|
158
|
+
expect(doc.root.name).to eq("root")
|
|
159
|
+
expect(doc.root["id"]).to eq("1")
|
|
160
|
+
expect(doc.root.parent).to eq(doc)
|
|
161
|
+
expect(doc.to_xml).to include("<root id=\"1\">", "<child>data</child>")
|
|
162
|
+
|
|
163
|
+
reparsed = context.parse(doc.to_xml)
|
|
164
|
+
expect(reparsed.root["id"]).to eq("1")
|
|
165
|
+
expect(reparsed.at_xpath("//child").text).to eq("data")
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it "replaces an existing root" do
|
|
169
|
+
doc = context.parse("<old><nested/></old>")
|
|
170
|
+
replacement = doc.create_element("new")
|
|
171
|
+
replacement.add_child("text")
|
|
172
|
+
|
|
173
|
+
doc.root = replacement
|
|
174
|
+
|
|
175
|
+
expect(doc.root.name).to eq("new")
|
|
176
|
+
expect(doc.to_xml).to include("<new>text</new>")
|
|
177
|
+
expect(doc.to_xml).not_to include("old")
|
|
178
|
+
end
|
|
179
|
+
end
|
|
146
180
|
end
|