leptris 1.9.156.1-aarch64-linux → 1.9.156.2-aarch64-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 +21 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/searchable.rb +60 -7
- data/lib/leptris/xml/xpath.rb +20 -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: 80010a60a2c2db3fa067f4dcd34b1bd4285eda3616dde355513b31cf6413bca6
|
|
4
|
+
data.tar.gz: 819cd39c42877cd843b6ddfea5de5be603c94b5f72a4fb4cd6cef5eb842d4154
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6b8e1b80577f7a5f6aa33f77ce66d2a5efa4af816e89be7912a69263c06365e17e157d58ab15482a873e9e370bf88ad69b0dcb3b83074af70022c0c24fd2efbd
|
|
7
|
+
data.tar.gz: 0ab363abd9bd98daccfd95c1c9b4bb75359b9003cb3e5a675671e5a4a2929407ba533d48be95010f430d58a7fe75806e4d56c383ea51acc8767fa1a6c9547732
|
data/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,27 @@ 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.156.
|
|
8
|
+
## [1.9.156.2] - 2026-09-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **#183: XPath version selection** (mirroring leptris-py#105):
|
|
13
|
+
`xpath`/`at_xpath` take a trailing `version:` selector —
|
|
14
|
+
`doc.xpath("count(//b)", version: "1.0")` evaluates through the
|
|
15
|
+
strict 1.0 engine lane (3.x syntax raises), `"3.1"` the full
|
|
16
|
+
grammar; `XPath.compile(expr, version:)` pins compiled
|
|
17
|
+
expressions the same way. Carried positionally — a declared
|
|
18
|
+
keyword is impossible without breaking the namespace-binding
|
|
19
|
+
hash channel (Ruby converts a trailing String-keyed hash into
|
|
20
|
+
keywords the moment one exists), so a `:version` key whose
|
|
21
|
+
VALUE is a version selector ("1.0"/"3.1"/:xpath10/:xpath31)
|
|
22
|
+
selects the channel; dotted-number values raise at the boundary
|
|
23
|
+
with the valid values; any other value stays a prefix binding
|
|
24
|
+
(a prefix literally named "version" keeps working). `version:`
|
|
25
|
+
combined with namespace bindings raises (the versioned engine
|
|
26
|
+
entry takes no ns set yet), as does `version:` on css. The nil
|
|
27
|
+
default keeps the existing eval path verbatim.
|
|
28
|
+
\n## [1.9.156.1] - 2026-09-13
|
|
9
29
|
|
|
10
30
|
### Added
|
|
11
31
|
|
data/lib/leptris/version.rb
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Leptris::XML::Searchable
|
|
4
|
+
# Values that make a :version key the version channel (#183,
|
|
5
|
+
# mirroring leptris-py#105); anything else is a namespace prefix.
|
|
6
|
+
VERSION_SELECTORS = ["1.0", "3.1", :xpath10, :xpath31].freeze
|
|
7
|
+
|
|
4
8
|
def xpath(*paths)
|
|
5
|
-
handler, ns,
|
|
9
|
+
handler, ns, version = parse_search_args(paths)
|
|
6
10
|
raise ArgumentError, "custom XPath handlers not supported" if handler
|
|
7
11
|
expr = paths.join(" | ")
|
|
8
12
|
|
|
@@ -10,7 +14,16 @@ module Leptris::XML::Searchable
|
|
|
10
14
|
context_ptr = is_a?(Leptris::XML::Document) ? nil : c_ptr
|
|
11
15
|
|
|
12
16
|
result_ptr =
|
|
13
|
-
if
|
|
17
|
+
if version
|
|
18
|
+
if ns && !ns.empty?
|
|
19
|
+
raise ArgumentError,
|
|
20
|
+
"version: cannot be combined with namespace bindings " \
|
|
21
|
+
"(the versioned engine entry takes no ns set yet)"
|
|
22
|
+
end
|
|
23
|
+
Leptris::XML::FFI.leptris_xpath_eval_versioned(
|
|
24
|
+
doc_ptr, context_ptr, expr,
|
|
25
|
+
Leptris::XML::Searchable.xpath_version_code(version), nil)
|
|
26
|
+
elsif ns && !ns.empty?
|
|
14
27
|
xpath_eval_with_namespaces(doc_ptr, context_ptr, expr, ns)
|
|
15
28
|
else
|
|
16
29
|
Leptris::XML::FFI.leptris_xpath_eval(doc_ptr, context_ptr, expr)
|
|
@@ -24,7 +37,7 @@ module Leptris::XML::Searchable
|
|
|
24
37
|
end
|
|
25
38
|
|
|
26
39
|
def at_xpath(*paths)
|
|
27
|
-
handler, ns,
|
|
40
|
+
handler, ns, version = parse_search_args(paths)
|
|
28
41
|
raise ArgumentError, "custom XPath handlers not supported" if handler
|
|
29
42
|
expr = paths.join(" | ")
|
|
30
43
|
|
|
@@ -32,7 +45,16 @@ module Leptris::XML::Searchable
|
|
|
32
45
|
context_ptr = is_a?(Leptris::XML::Document) ? nil : c_ptr
|
|
33
46
|
|
|
34
47
|
result_ptr =
|
|
35
|
-
if
|
|
48
|
+
if version
|
|
49
|
+
if ns && !ns.empty?
|
|
50
|
+
raise ArgumentError,
|
|
51
|
+
"version: cannot be combined with namespace bindings " \
|
|
52
|
+
"(the versioned engine entry takes no ns set yet)"
|
|
53
|
+
end
|
|
54
|
+
Leptris::XML::FFI.leptris_xpath_eval_versioned(
|
|
55
|
+
doc_ptr, context_ptr, expr,
|
|
56
|
+
Leptris::XML::Searchable.xpath_version_code(version), nil)
|
|
57
|
+
elsif ns && !ns.empty?
|
|
36
58
|
xpath_eval_with_namespaces(doc_ptr, context_ptr, expr, ns)
|
|
37
59
|
else
|
|
38
60
|
Leptris::XML::FFI.leptris_xpath_eval(doc_ptr, context_ptr, expr)
|
|
@@ -64,8 +86,9 @@ module Leptris::XML::Searchable
|
|
|
64
86
|
alias_method :%, :at
|
|
65
87
|
|
|
66
88
|
def css(*args)
|
|
67
|
-
handler, ns,
|
|
89
|
+
handler, ns, version = parse_search_args(args)
|
|
68
90
|
raise ArgumentError, "namespace bindings not supported in css" if ns && !ns.empty?
|
|
91
|
+
raise ArgumentError, "version: is XPath-only" if version
|
|
69
92
|
raise ArgumentError, "custom CSS handlers not supported" if handler
|
|
70
93
|
# Nokogiri semantics: css is receiver-relative — absolute "//"
|
|
71
94
|
# from a Document, descendant ".//" from elements and fragments.
|
|
@@ -76,8 +99,9 @@ module Leptris::XML::Searchable
|
|
|
76
99
|
end
|
|
77
100
|
|
|
78
101
|
def at_css(*args)
|
|
79
|
-
handler, ns,
|
|
102
|
+
handler, ns, version = parse_search_args(args)
|
|
80
103
|
raise ArgumentError, "namespace bindings not supported in css" if ns && !ns.empty?
|
|
104
|
+
raise ArgumentError, "version: is XPath-only" if version
|
|
81
105
|
raise ArgumentError, "custom CSS handlers not supported" if handler
|
|
82
106
|
prefix = is_a?(Leptris::XML::Document) ? "//" : ".//"
|
|
83
107
|
expr = args.map { |r| Leptris::XML::CssToXPath.convert(r, prefix: prefix) }
|
|
@@ -131,6 +155,15 @@ module Leptris::XML::Searchable
|
|
|
131
155
|
end
|
|
132
156
|
end
|
|
133
157
|
|
|
158
|
+
# Trailing-argument contract (#183): a String-keyed hash is the
|
|
159
|
+
# namespace-binding channel — as is a Symbol-keyed hash (the
|
|
160
|
+
# legacy prefix vocabulary, {x: "urn:x"}); a :version key whose
|
|
161
|
+
# VALUE is a version selector is the version-selection channel
|
|
162
|
+
# (xpath("//b", version: "1.0"), mirroring leptris-py#105). A
|
|
163
|
+
# DECLARED keyword cannot carry this: the moment #xpath declares
|
|
164
|
+
# one, Ruby converts the trailing namespace hash into keywords
|
|
165
|
+
# (unknown keyword: "p") — so both channels stay positional and
|
|
166
|
+
# dispatch on key/value here.
|
|
134
167
|
def parse_search_args(args)
|
|
135
168
|
handler = args.find { |a| !a.is_a?(String) && !a.is_a?(Hash) && !a.is_a?(Symbol) }
|
|
136
169
|
args = args - [handler] if handler
|
|
@@ -143,7 +176,27 @@ module Leptris::XML::Searchable
|
|
|
143
176
|
unless vars.nil? || vars.empty?
|
|
144
177
|
raise ArgumentError, "XPath variable bindings are not supported"
|
|
145
178
|
end
|
|
146
|
-
|
|
179
|
+
# Value-based disambiguation is load-bearing: Symbol keys are
|
|
180
|
+
# also the namespace vocabulary, and bare keywords FUSE into a
|
|
181
|
+
# preceding String-keyed hash at the call site ({"p" => uri,
|
|
182
|
+
# version: "1.0"} arrives as ONE hash). A :version key with any
|
|
183
|
+
# other value stays a prefix binding.
|
|
184
|
+
version = nil
|
|
185
|
+
if ns.is_a?(Hash)
|
|
186
|
+
v = ns[:version]
|
|
187
|
+
if VERSION_SELECTORS.include?(v)
|
|
188
|
+
version = ns.delete(:version)
|
|
189
|
+
ns = nil if ns.empty?
|
|
190
|
+
elsif v.is_a?(String) && v.match?(/\A\d+\.\d+\z/)
|
|
191
|
+
# A dotted-number :version value is version INTENT — raise
|
|
192
|
+
# with the valid values rather than silently binding a
|
|
193
|
+
# prefix literally. Genuine prefix bindings carry URIs.
|
|
194
|
+
raise ArgumentError,
|
|
195
|
+
"unknown XPath version #{v.inspect} " \
|
|
196
|
+
"(valid: \"1.0\"/:xpath10, \"3.1\"/:xpath31)"
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
[handler, ns, version]
|
|
147
200
|
end
|
|
148
201
|
|
|
149
202
|
def looks_like_xpath?(str)
|
data/lib/leptris/xml/xpath.rb
CHANGED
|
@@ -18,28 +18,44 @@ class Leptris::XML::XPath
|
|
|
18
18
|
|
|
19
19
|
attr_reader :expression
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
# +version+ (#183, mirroring leptris-py#105): nil compiles the
|
|
22
|
+
# full-grammar expression (the default); :xpath10 / "1.0" pins
|
|
23
|
+
# the strict XPath 1.0 surface. Validated at the boundary.
|
|
24
|
+
def self.compile(expression, version: nil)
|
|
22
25
|
raw = Leptris::XML::FFI.leptris_xpath_compile(expression.to_s)
|
|
23
26
|
if raw.null?
|
|
24
27
|
raise Leptris::XML::XPathError,
|
|
25
28
|
"invalid expression: #{Leptris::XML::FFI.leptris_last_error}"
|
|
26
29
|
end
|
|
27
|
-
|
|
30
|
+
version_code =
|
|
31
|
+
Leptris::XML::Searchable.xpath_version_code(version) if version
|
|
32
|
+
new(expression.to_s, CompiledHandle.new(raw), version_code)
|
|
28
33
|
end
|
|
29
34
|
|
|
30
|
-
def initialize(expression, handle)
|
|
35
|
+
def initialize(expression, handle, version_code = nil)
|
|
31
36
|
@expression = expression
|
|
32
37
|
@handle = handle
|
|
38
|
+
@version_code = version_code
|
|
33
39
|
end
|
|
34
40
|
|
|
35
41
|
# Evaluates against +doc_or_element+. An optional trailing hash of
|
|
36
42
|
# namespace bindings ("prefix" => uri) routes the evaluation through
|
|
37
43
|
# the namespace-bound path, matching Searchable#xpath semantics.
|
|
44
|
+
# A compiled version pin routes through the engine's versioned
|
|
45
|
+
# entry instead of the compiled handle — there is no compiled
|
|
46
|
+
# versioned lane yet (engine gap), so the strict-1.0 surface is
|
|
47
|
+
# enforced per evaluation over the stored expression.
|
|
38
48
|
def eval(doc_or_element, ns = nil)
|
|
39
49
|
context = Leptris::XML::EvaluationContext.of(doc_or_element)
|
|
40
50
|
document = context.document
|
|
41
51
|
result_ptr =
|
|
42
|
-
if
|
|
52
|
+
if @version_code
|
|
53
|
+
raise ArgumentError,
|
|
54
|
+
"version: cannot be combined with namespace bindings " "(the versioned engine entry takes no ns set yet)" if ns && !ns.empty?
|
|
55
|
+
Leptris::XML::FFI.leptris_xpath_eval_versioned(
|
|
56
|
+
document.c_ptr, context.context_node_ptr,
|
|
57
|
+
@expression, @version_code, nil)
|
|
58
|
+
elsif ns && !ns.empty?
|
|
43
59
|
Leptris::XML::FFI.with_ns_set(ns) do |set|
|
|
44
60
|
Leptris::XML::FFI.leptris_xpath_compiled_eval_ns(
|
|
45
61
|
@handle, document.c_ptr, context.context_node_ptr, set)
|