leptris 1.9.31-x86_64-linux → 1.9.32-x86_64-linux
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/CHANGELOG.md +36 -0
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/document.rb +36 -1
- data/lib/leptris/xml/node.rb +34 -5
- data/lib/leptris/xml/processing_instruction.rb +21 -4
- 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: e0126843719aa5ffea0bfe6d60e13a57f6a4e8dc1ac0cd92f0c91b203ee5853a
|
|
4
|
+
data.tar.gz: 50678d08aa8cba33138d447731cd267e0a3ca7be7df4f7a5a9121a5e7bc98de8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 33976c129c10325e338a9fcfb0df286cb99bbcb5a5fe5541fdf0b310d2a7c42babc5704cc60f7dce61eb5bbe399fd65d059cbee97f0e91303bb580ba1ff8fbc3
|
|
7
|
+
data.tar.gz: 5939d862df5f8e412815219962d7261dbf533d8ed0ee3d038cd4a0961d4c97d8ac8addf67c58db80f5b64fbd5e753eac0ee36323aabfb3938a0558fd550b643c
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,42 @@ All notable changes to Leptris will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.9.32] - 2026-08-28
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **traverse is subtree-bounded (leptris-ruby#89)**: the C walker
|
|
13
|
+
was never bounded — after visiting the receiver it pushed the
|
|
14
|
+
receiver's next sibling and continued to the end of the document
|
|
15
|
+
chain, so `element.traverse` swept following siblings and their
|
|
16
|
+
subtrees, and `root.traverse` swept the epilog. In post-order the
|
|
17
|
+
receiver is the LAST node of its own subtree, so the callback now
|
|
18
|
+
returns non-zero at self — the C loop honors it and the walk
|
|
19
|
+
stops exactly at the boundary.
|
|
20
|
+
- **traverse re-raises callback exceptions (leptris-ruby#90)**: a
|
|
21
|
+
rescue inside the FFI callback stashes the exception and returns
|
|
22
|
+
non-zero (aborting the walk); the stashed exception is re-raised
|
|
23
|
+
after `leptris_node_traverse` returns. Previously the dispatch
|
|
24
|
+
silently swallowed Ruby exceptions and the walk continued with
|
|
25
|
+
partially processed data.
|
|
26
|
+
- **built documents list the attached root (leptris-ruby#91)**:
|
|
27
|
+
libleptris's `document_set_root` does not register the root into
|
|
28
|
+
the document node's child chain, so `Document#children` missed it
|
|
29
|
+
until another document mutation refreshed the chain. `#children`
|
|
30
|
+
now splices the attached root in: a replaced root's stale chain
|
|
31
|
+
entry is dropped and the new root inherits its position (the
|
|
32
|
+
prolog/epilog split follows the old slot); on rootless chains
|
|
33
|
+
placement falls back to document-order comparison. Parsed and
|
|
34
|
+
built documents read the same.
|
|
35
|
+
- **document-level PI writes name the contract (leptris-ruby#92)**:
|
|
36
|
+
parse-created document-level PIs carry no document linkage in
|
|
37
|
+
libleptris, so `target=`/`data=` fail INVALID_ARG and `unlink`
|
|
38
|
+
NOT_FOUND while tree-level and `Document#add_pi` PIs mutate fine.
|
|
39
|
+
Those failures now raise a descriptive error naming the contract
|
|
40
|
+
(mutable alternatives included) instead of a bare "Invalid
|
|
41
|
+
argument". Full write-through and a remove API need C surface
|
|
42
|
+
(filed upstream); the binding cannot supply them.
|
|
43
|
+
|
|
8
44
|
## [1.9.31] - 2026-08-28
|
|
9
45
|
|
|
10
46
|
### Fixed
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/document.rb
CHANGED
|
@@ -166,9 +166,44 @@ class Leptris::XML::Document
|
|
|
166
166
|
# The document's children, via the document node: prolog
|
|
167
167
|
# comments/PIs, the root element, epilog comments/PIs, in
|
|
168
168
|
# document order (Nokogiri-parity shape).
|
|
169
|
+
#
|
|
170
|
+
# On programmatically built documents the C document node's chain
|
|
171
|
+
# misses a root attached via #root= until some other document
|
|
172
|
+
# mutation refreshes it (leptris-ruby#91 — libleptris's
|
|
173
|
+
# document_set_root does not register into the chain). The merge
|
|
174
|
+
# below splices the attached root in by document order whenever
|
|
175
|
+
# the chain lacks it, so parsed and built documents read the
|
|
176
|
+
# same. Prolog/epilog placement uses node_compare against the
|
|
177
|
+
# root.
|
|
169
178
|
def children
|
|
170
179
|
doc_node = node
|
|
171
|
-
|
|
180
|
+
return Leptris::XML::NodeSet.new(self, []) if doc_node.nil?
|
|
181
|
+
kids = doc_node.children.to_a
|
|
182
|
+
root_ptr = Leptris::XML::FFI.leptris_document_root(@c_ptr)
|
|
183
|
+
return kids if root_ptr.null?
|
|
184
|
+
return kids if kids.any? { |child| child.c_ptr == root_ptr }
|
|
185
|
+
root = Leptris::XML::Node.wrap(root_ptr, self)
|
|
186
|
+
# A replaced root stays in the C chain until another document
|
|
187
|
+
# mutation refreshes it — the chain's element slots other than
|
|
188
|
+
# the current root are stale old roots. The new root inherits
|
|
189
|
+
# the first stale slot's position (the prolog/epilog split
|
|
190
|
+
# follows where the old root sat); with no stale element
|
|
191
|
+
# (programmatically built documents) placement falls back to
|
|
192
|
+
# document-order comparison.
|
|
193
|
+
stale = kids.select do |child|
|
|
194
|
+
child.element? && child.c_ptr != root_ptr
|
|
195
|
+
end
|
|
196
|
+
if stale.any?
|
|
197
|
+
kept = kids.reject { |child| stale.include?(child) }
|
|
198
|
+
slot = kids.index(stale.first)
|
|
199
|
+
kept.insert(slot, root)
|
|
200
|
+
Leptris::XML::NodeSet.new(self, kept)
|
|
201
|
+
else
|
|
202
|
+
prolog, epilog = kids.partition do |child|
|
|
203
|
+
Leptris::XML::FFI.leptris_node_compare(child.c_ptr, root_ptr).negative?
|
|
204
|
+
end
|
|
205
|
+
Leptris::XML::NodeSet.new(self, prolog + [root] + epilog)
|
|
206
|
+
end
|
|
172
207
|
end
|
|
173
208
|
|
|
174
209
|
# Attach +element+ as the document's root element. The element must
|
data/lib/leptris/xml/node.rb
CHANGED
|
@@ -263,27 +263,56 @@ class Leptris::XML::Node
|
|
|
263
263
|
|
|
264
264
|
def unlink
|
|
265
265
|
ensure_writable!
|
|
266
|
-
Leptris::XML::FFI.
|
|
267
|
-
|
|
266
|
+
rc = Leptris::XML::FFI.leptris_node_unlink(@c_ptr)
|
|
267
|
+
if rc == Leptris::XML::FFI::LEPTRIS_ERROR_NOT_FOUND && processing_instruction?
|
|
268
|
+
raise Leptris::XML::Error,
|
|
269
|
+
Leptris::XML::ProcessingInstruction::READ_ONLY_NATIVE
|
|
270
|
+
end
|
|
271
|
+
Leptris::XML::FFI.check_status(rc)
|
|
268
272
|
@parent = nil
|
|
269
273
|
self
|
|
270
274
|
end
|
|
271
275
|
alias_method :remove, :unlink
|
|
272
276
|
|
|
273
|
-
# Walks the subtree in post-order DFS (matches Nokogiri's
|
|
277
|
+
# Walks the subtree in post-order DFS (matches Nokogiri's
|
|
278
|
+
# semantics): the receiver, its descendants, nothing else.
|
|
274
279
|
#
|
|
275
280
|
# One FFI call dispatches the whole walk; the C engine invokes the
|
|
276
281
|
# callback once per visited node (the only per-node cost is the
|
|
277
282
|
# C-to-Ruby callback dispatch, not FFI round-trips).
|
|
283
|
+
#
|
|
284
|
+
# Subtree-bounded by abort-at-self (leptris-ruby#89): the C
|
|
285
|
+
# walker was never bounded — after visiting the receiver it
|
|
286
|
+
# pushes the receiver's NEXT SIBLING and continues to the end of
|
|
287
|
+
# the document chain. In post-order the receiver is the LAST
|
|
288
|
+
# node of its own subtree, so returning non-zero at self stops
|
|
289
|
+
# the walk exactly at the boundary (the C loop honors a non-zero
|
|
290
|
+
# callback return). The self comparison is by address; the
|
|
291
|
+
# receiver's handle is stable for the walk's duration.
|
|
292
|
+
#
|
|
293
|
+
# Exceptions raised by the block are re-raised after the walk
|
|
294
|
+
# (leptris-ruby#90): a rescue inside the callback stashes the
|
|
295
|
+
# exception and returns non-zero, aborting the C walk — without
|
|
296
|
+
# it the FFI dispatch silently swallowed the exception and the
|
|
297
|
+
# walk continued with partially processed data.
|
|
278
298
|
def traverse
|
|
279
299
|
return enum_for(:traverse) unless block_given?
|
|
280
300
|
ensure_alive!
|
|
301
|
+
error = nil
|
|
302
|
+
self_address = @c_ptr.address
|
|
281
303
|
callback = ::FFI::Function.new(:int, [:pointer, :pointer], blocking: true) do |node_ptr, _|
|
|
282
|
-
|
|
283
|
-
|
|
304
|
+
begin
|
|
305
|
+
yield Leptris::XML::Node.wrap(node_ptr, @document)
|
|
306
|
+
node_ptr.address == self_address ? 1 : 0
|
|
307
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
308
|
+
error = e
|
|
309
|
+
1
|
|
310
|
+
end
|
|
284
311
|
end
|
|
285
312
|
Leptris::XML::FFI.leptris_node_traverse(
|
|
286
313
|
@c_ptr, Leptris::XML::FFI::TRAVERSE_POST_ORDER, callback, nil)
|
|
314
|
+
raise error if error
|
|
315
|
+
self
|
|
287
316
|
end
|
|
288
317
|
|
|
289
318
|
def path
|
|
@@ -25,17 +25,34 @@ class Leptris::XML::ProcessingInstruction < Leptris::XML::Node
|
|
|
25
25
|
result
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
# Parse-created document-level PIs carry no document linkage in
|
|
29
|
+
# libleptris (leptris-ruby#92): the C setters reject them with
|
|
30
|
+
# INVALID_ARG while tree-level and Document#add_pi PIs mutate
|
|
31
|
+
# fine. Map that failure to the contract instead of a bare
|
|
32
|
+
# "Invalid argument".
|
|
33
|
+
READ_ONLY_NATIVE =
|
|
34
|
+
"this PI is a parse-created document-level PI — its node has " \
|
|
35
|
+
"no document linkage in libleptris, so target/data cannot be " \
|
|
36
|
+
"written and it cannot be unlinked (leptris-ruby#92); " \
|
|
37
|
+
"tree-level and Document#add_pi PIs are mutable"
|
|
38
|
+
|
|
28
39
|
def target=(new_target)
|
|
29
40
|
ensure_writable!
|
|
30
|
-
Leptris::XML::FFI.
|
|
31
|
-
|
|
41
|
+
rc = Leptris::XML::FFI.leptris_pi_node_set_target(
|
|
42
|
+
@c_ptr, new_target.to_s)
|
|
43
|
+
raise Leptris::XML::Error, READ_ONLY_NATIVE if
|
|
44
|
+
rc == Leptris::XML::FFI::LEPTRIS_ERROR_INVALID_ARG
|
|
45
|
+
Leptris::XML::FFI.check_status(rc)
|
|
32
46
|
new_target
|
|
33
47
|
end
|
|
34
48
|
|
|
35
49
|
def data=(new_data)
|
|
36
50
|
ensure_writable!
|
|
37
|
-
Leptris::XML::FFI.
|
|
38
|
-
|
|
51
|
+
rc = Leptris::XML::FFI.leptris_pi_node_set_data(
|
|
52
|
+
@c_ptr, new_data.to_s)
|
|
53
|
+
raise Leptris::XML::Error, READ_ONLY_NATIVE if
|
|
54
|
+
rc == Leptris::XML::FFI::LEPTRIS_ERROR_INVALID_ARG
|
|
55
|
+
Leptris::XML::FFI.check_status(rc)
|
|
39
56
|
new_data
|
|
40
57
|
end
|
|
41
58
|
end
|