leptris 1.9.15 → 1.9.16
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 +14 -0
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/searchable.rb +42 -5
- 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: 85d05772181a6d3c913f331b48f900f0781596428308759b12ac07d6cdc392d5
|
|
4
|
+
data.tar.gz: 2e267b1225848fff3d265d0b810df576d3114e84e2a1627aacb143e650340a59
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 959165b993b24b641b64246799168ef0d53d2180866596996069c28adabbf7305d1d78600af0eb25bf7bccac8a8053f6a0714d97315df61fb032fd4fcb2ef4ac
|
|
7
|
+
data.tar.gz: 2a41355f33fcfefff1ee6b8aa2274a1d5464a8f567f611c7035df328eae3620f5b74018cea500f9bae4240593d4e8f9b9e263b17b32573e13cfcd81f4d2cd847
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ 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.16] - 2026-08-27
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **at_xpath / at_css take the single-node path**: a new
|
|
13
|
+
`wrap_xpath_first_result` seam answers nodeset results via
|
|
14
|
+
get_node(0) + wrap + free — no NodeSet container, no AutoPointer,
|
|
15
|
+
one fewer FFI than xpath().first; scalar results keep full-wrapper
|
|
16
|
+
semantics. Measured on the canonical scraper loop: at_xpath 0.018 s
|
|
17
|
+
-> 0.005 s (3.6x — the per-iteration container churn was GC
|
|
18
|
+
pressure the old path paid twice), at_css ~24%. Equivalence with
|
|
19
|
+
xpath().first is spec-pinned across nodeset/empty/scalar shapes.
|
|
20
|
+
Also drops a vestigial `.send(:from_result)` on a public factory.
|
|
21
|
+
|
|
8
22
|
## [1.9.15] - 2026-08-27
|
|
9
23
|
|
|
10
24
|
### Changed
|
data/lib/leptris/version.rb
CHANGED
|
@@ -24,8 +24,24 @@ module Leptris::XML::Searchable
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def at_xpath(*paths)
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
handler, ns, _vars = parse_search_args(paths)
|
|
28
|
+
raise ArgumentError, "custom XPath handlers not supported" if handler
|
|
29
|
+
expr = paths.join(" | ")
|
|
30
|
+
|
|
31
|
+
doc_ptr = is_a?(Leptris::XML::Document) ? c_ptr : document.c_ptr
|
|
32
|
+
context_ptr = is_a?(Leptris::XML::Document) ? nil : c_ptr
|
|
33
|
+
|
|
34
|
+
result_ptr =
|
|
35
|
+
if ns && !ns.empty?
|
|
36
|
+
xpath_eval_with_namespaces(doc_ptr, context_ptr, expr, ns)
|
|
37
|
+
else
|
|
38
|
+
Leptris::XML::FFI.leptris_xpath_eval(doc_ptr, context_ptr, expr)
|
|
39
|
+
end
|
|
40
|
+
if result_ptr.null?
|
|
41
|
+
raise Leptris::XML::XPathError,
|
|
42
|
+
Leptris::XML::FFI.leptris_last_error.to_s
|
|
43
|
+
end
|
|
44
|
+
Leptris::XML::Searchable.wrap_xpath_first_result(document, result_ptr)
|
|
29
45
|
end
|
|
30
46
|
|
|
31
47
|
def search(*args)
|
|
@@ -53,8 +69,13 @@ module Leptris::XML::Searchable
|
|
|
53
69
|
end
|
|
54
70
|
|
|
55
71
|
def at_css(*args)
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
handler, ns, _ = parse_search_args(args)
|
|
73
|
+
raise ArgumentError, "namespace bindings not supported in css" if ns && !ns.empty?
|
|
74
|
+
raise ArgumentError, "custom CSS handlers not supported" if handler
|
|
75
|
+
prefix = is_a?(Leptris::XML::Document) ? "//" : ".//"
|
|
76
|
+
expr = args.map { |r| Leptris::XML::CssToXPath.convert(r, prefix: prefix) }
|
|
77
|
+
.join(" | ")
|
|
78
|
+
at_xpath(expr)
|
|
58
79
|
end
|
|
59
80
|
|
|
60
81
|
protected
|
|
@@ -86,6 +107,22 @@ module Leptris::XML::Searchable
|
|
|
86
107
|
%r{\A(\./|/|\.\.|\.)}.match?(str)
|
|
87
108
|
end
|
|
88
109
|
|
|
110
|
+
# Single-node seam beside wrap_xpath_result: a nodeset answers
|
|
111
|
+
# via result_get_node(0) + wrap + free — no NodeSet container, no
|
|
112
|
+
# AutoPointer, one fewer FFI than xpath().first; scalars keep the
|
|
113
|
+
# full-wrapper semantics.
|
|
114
|
+
def self.wrap_xpath_first_result(document, result_ptr)
|
|
115
|
+
type = Leptris::XML::FFI.leptris_xpath_result_type(result_ptr)
|
|
116
|
+
if type == Leptris::XML::FFI::XPATH_NODESET
|
|
117
|
+
ptr = Leptris::XML::FFI.leptris_xpath_result_get_node(result_ptr, 0)
|
|
118
|
+
node = ptr.null? ? nil : Leptris::XML::Node.wrap(ptr, document)
|
|
119
|
+
Leptris::XML::FFI.leptris_xpath_result_free(result_ptr)
|
|
120
|
+
node
|
|
121
|
+
else
|
|
122
|
+
wrap_xpath_result(document, result_ptr)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
89
126
|
# Shared by Searchable#xpath and Leptris::XML::XPath (compiled
|
|
90
127
|
# expressions): wraps a raw XPathResult pointer into the
|
|
91
128
|
# Ruby-typed result and frees the C handle.
|
|
@@ -93,7 +130,7 @@ module Leptris::XML::Searchable
|
|
|
93
130
|
type = Leptris::XML::FFI.leptris_xpath_result_type(result_ptr)
|
|
94
131
|
case type
|
|
95
132
|
when Leptris::XML::FFI::XPATH_NODESET
|
|
96
|
-
Leptris::XML::NodeSet.
|
|
133
|
+
Leptris::XML::NodeSet.from_result(document, result_ptr)
|
|
97
134
|
when Leptris::XML::FFI::XPATH_BOOLEAN
|
|
98
135
|
v = Leptris::XML::FFI.leptris_xpath_result_boolean(result_ptr) != 0
|
|
99
136
|
Leptris::XML::FFI.leptris_xpath_result_free(result_ptr)
|