leptris 1.9.7 → 1.9.9
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 +43 -0
- data/CLAUDE.md +3 -2
- data/README.adoc +9 -3
- data/Rakefile +39 -0
- data/docs/adr/0006-ruby-variant-policy.md +25 -0
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/ffi.rb +18 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b2e2451251a0c5aec3567f67ed11c48f1b48d1a9f3622bc8c3fe62f03192c185
|
|
4
|
+
data.tar.gz: 6be47e0deac36287f736e5b6b7b9755ce4b1e2e4e3986959caa750c5072bcada
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a352efddd7e68f48e17d7126bf1d003d8da0c2eb5cdf09be1c7c7d42b739a761208792e4b45033cd7dfd928123ff7db80c8d629a0f625554d778c7c6b822e4df
|
|
7
|
+
data.tar.gz: 71c3504e8592fab25b026c68d25300c2420c22e932546257dd6d5cc819c47b9cf3ff82cbd80136d55e8250efe1c6b835a82d336b341d3cdbbcde8177ff16933d
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,49 @@ 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.9] - 2026-08-26
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **CI gates the symbol audit**: the build workflow runs
|
|
13
|
+
`rake compile audit:symbols` before the suite — upstream surface
|
|
14
|
+
drift now fails CI on the PR that first sees it instead of
|
|
15
|
+
relying on someone remembering to run it. The task skips
|
|
16
|
+
gracefully where nm is unavailable (Windows).
|
|
17
|
+
- **benchmark/leptris_vs_nokogiri.rb prints live provenance**
|
|
18
|
+
(library, gem, nokogiri, ruby versions) instead of a frozen
|
|
19
|
+
footer claiming "v0.12.0 / 176 specs".
|
|
20
|
+
- README: the `#search` dispatch description now matches the
|
|
21
|
+
heuristic the code implements (path-prefix → xpath; everything
|
|
22
|
+
else translates as CSS, commas included).
|
|
23
|
+
|
|
24
|
+
### Meta
|
|
25
|
+
|
|
26
|
+
- **ADR 0006** records the ruby-variant packaging policy as
|
|
27
|
+
practiced since 1.9.0: the `ruby` platform gem ships as the
|
|
28
|
+
fallback/source variant without a vendored library, safe under
|
|
29
|
+
the eager-load failure mode (issue #49) — clarifying the 1.9.0
|
|
30
|
+
changelog's "platform variants only" phrasing without rewriting
|
|
31
|
+
history.
|
|
32
|
+
|
|
33
|
+
## [1.9.8] - 2026-08-26
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- **`rake audit:symbols`**: the lockstep drift detector as an
|
|
38
|
+
executable step — `nm -gU` on the vendored library vs the ffi.rb
|
|
39
|
+
attach list, failing with both directions of drift (ADR 0001's
|
|
40
|
+
enforcement arm; previously prose plus ad-hoc pipelines).
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- ffi.rb attaches the five header-declared exports past cleanups
|
|
45
|
+
had dropped (element_children, serialize_document,
|
|
46
|
+
xpath_ns_set_add, xpath_result_get, xpath_result_get_nodes) —
|
|
47
|
+
mirror-only, supersession noted inline. The audit now runs green:
|
|
48
|
+
**224/224 symbols in lockstep**. Pull-attribute batch fetch asked
|
|
49
|
+
upstream (leptris/leptris#562).
|
|
50
|
+
|
|
8
51
|
## [1.9.7] - 2026-08-26
|
|
9
52
|
|
|
10
53
|
### Added
|
data/CLAUDE.md
CHANGED
|
@@ -91,6 +91,7 @@ lib/leptris/xml/css_to_xpath.rb — minimal CSS translation
|
|
|
91
91
|
check both before proposing refactors; they exist so reviews
|
|
92
92
|
stop re-deriving settled questions.
|
|
93
93
|
- libleptris public headers (`src/include/leptris/*.h`) are the
|
|
94
|
-
contract; when symbols change, bump lockstep and
|
|
95
|
-
|
|
94
|
+
contract; when symbols change, bump lockstep and run
|
|
95
|
+
`rake audit:symbols` (attached == exported on the vendored
|
|
96
|
+
library; fails listing both directions of drift).
|
|
96
97
|
- Upstream issues worth tracking live at leptris/leptris.
|
data/README.adoc
CHANGED
|
@@ -169,14 +169,15 @@ against a readonly document is legal; mutating the frozen one is not.
|
|
|
169
169
|
|
|
170
170
|
== Searching: XPath and CSS
|
|
171
171
|
|
|
172
|
-
`Document` and `
|
|
172
|
+
`Document`, `Element`, and `DocumentFragment` (via
|
|
173
|
+
`Leptris::XML::Searchable`) support:
|
|
173
174
|
|
|
174
175
|
[horizontal]
|
|
175
176
|
`#xpath(*exprs)` :: evaluate XPath; returns `NodeSet`, `true`/`false`, `Float`, or `String` depending on the expression.
|
|
176
177
|
`#at_xpath(*exprs)` :: first match (or scalar), like `xpath(*exprs).first`.
|
|
177
|
-
`#css(*selectors)` :: minimal CSS-to-XPath translation, then `xpath`.
|
|
178
|
+
`#css(*selectors)` :: minimal CSS-to-XPath translation, then `xpath`. Receiver-relative (Nokogiri semantics): scoped to the element or fragment, document-wide from a Document.
|
|
178
179
|
`#at_css(*selectors)` :: first match of `css`.
|
|
179
|
-
`#search(*exprs)` :: dispatches on syntax —
|
|
180
|
+
`#search(*exprs)` :: dispatches on syntax — path-prefixed expressions (`/`, `.`, `..`) go to `xpath`; everything else translates as CSS (comma unions included).
|
|
180
181
|
`#at(*exprs)` :: first match of `search`.
|
|
181
182
|
|
|
182
183
|
[source,ruby]
|
|
@@ -190,6 +191,11 @@ doc.at_xpath("string(//book[1]/@id)") # => "b1"
|
|
|
190
191
|
doc.css("book[lang='en'] title") # => NodeSet[<title>Refactoring</title>]
|
|
191
192
|
doc.at_css("book#b1 title") # => <title>Refactoring</title> (id selector)
|
|
192
193
|
doc.css("book:first-child") # first <book>
|
|
194
|
+
|
|
195
|
+
# css is receiver-relative: scoped to the receiver, not the document
|
|
196
|
+
doc.root.at_css("book").css("title") # titles under THAT book only
|
|
197
|
+
frag = doc.fragment("<a x='1'><n/></a>")
|
|
198
|
+
frag.css("a > n") # searches the fragment
|
|
193
199
|
----
|
|
194
200
|
|
|
195
201
|
XPath result type follows XPath 1.0 semantics:
|
data/Rakefile
CHANGED
|
@@ -49,6 +49,45 @@ task spec: :compile unless ENV.key?("LEPTRIS_LIB_PATH")
|
|
|
49
49
|
|
|
50
50
|
task default: :spec
|
|
51
51
|
|
|
52
|
+
# Lockstep drift detector (ADR 0001): ffi.rb mirrors the public
|
|
53
|
+
# header, so attached == exported must hold on the vendored library.
|
|
54
|
+
# Run after `rake compile` and before any lockstep release.
|
|
55
|
+
namespace :audit do
|
|
56
|
+
desc "Fail when ffi.rb attachments and library exports drift"
|
|
57
|
+
task :symbols do
|
|
58
|
+
lib = Dir.glob("lib/libleptris.{dylib,so,dll}").first
|
|
59
|
+
unless system("which nm > /dev/null 2>&1")
|
|
60
|
+
puts "audit:symbols: skipped (nm unavailable on this platform)"
|
|
61
|
+
next
|
|
62
|
+
end
|
|
63
|
+
unless lib
|
|
64
|
+
abort "audit:symbols: vendored library not found — run rake compile"
|
|
65
|
+
end
|
|
66
|
+
exported = `nm -gU #{lib}`
|
|
67
|
+
.lines.map { |l| l.split[2] }.compact
|
|
68
|
+
.map { |n| n.sub(/\A_/, "") }
|
|
69
|
+
.select { |n| n.start_with?("leptris_") }
|
|
70
|
+
.map { |n| n.sub(/\Aleptris_/, "") }.sort
|
|
71
|
+
attached = File.read("lib/leptris/xml/ffi.rb")
|
|
72
|
+
.scan(/attach_function :leptris_([a-z_0-9]+)/).flatten.sort
|
|
73
|
+
unattached = exported - attached
|
|
74
|
+
unexported = attached - exported
|
|
75
|
+
unless unattached.empty?
|
|
76
|
+
puts "exported but NOT attached (upstream surface drift):"
|
|
77
|
+
unattached.each { |s| puts " leptris_#{s}" }
|
|
78
|
+
end
|
|
79
|
+
unless unexported.empty?
|
|
80
|
+
puts "attached but NOT exported (stale attachment):"
|
|
81
|
+
unexported.each { |s| puts " leptris_#{s}" }
|
|
82
|
+
end
|
|
83
|
+
if unattached.empty? && unexported.empty?
|
|
84
|
+
puts "audit:symbols: #{attached.length}/#{exported.length} symbols in lockstep"
|
|
85
|
+
else
|
|
86
|
+
abort "audit:symbols: drift detected"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
52
91
|
require "rubygems/package_task"
|
|
53
92
|
|
|
54
93
|
desc "Build the pure-Ruby gem"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# ADR 0006: the ruby-platform variant ships as the fallback, unvendored
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
The 1.9.0-era CHANGELOG said "never publishes ruby-platform releases
|
|
5
|
+
without the vendored library — platform variants only", and an early
|
|
6
|
+
fix removed the `gem:native:any` task. The 1.9.0 lockstep restored
|
|
7
|
+
the `ruby` matrix entry deliberately, and every release since has
|
|
8
|
+
published the variant — practice contradicting the recorded policy.
|
|
9
|
+
|
|
10
|
+
## Decision
|
|
11
|
+
The `ruby`-platform gem ships as the fallback/source variant WITHOUT
|
|
12
|
+
a vendored library, by design. It is safe because library resolution
|
|
13
|
+
is eager (issue #49): on a machine without a usable libleptris the
|
|
14
|
+
gem fails at `require` with the actionable remedy (install the
|
|
15
|
+
platform-specific variant or set LEPTRIS_LIB_PATH) instead of deep
|
|
16
|
+
inside Document.parse. Bundler prefers platform-specific gems
|
|
17
|
+
wherever they exist (9 of 10 published variants are precompiled);
|
|
18
|
+
the ruby variant serves source installs and exotic platforms that
|
|
19
|
+
accept providing their own library.
|
|
20
|
+
|
|
21
|
+
## Consequences
|
|
22
|
+
Do not re-propose removing the `platform: any` release leg without
|
|
23
|
+
new evidence that bundler misresolves it in practice. A future
|
|
24
|
+
change to the eager-load behavior (ADR 0005 ordering aside) must
|
|
25
|
+
keep the ruby variant failing loudly at require time.
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/ffi.rb
CHANGED
|
@@ -173,6 +173,10 @@ module Leptris
|
|
|
173
173
|
# count-only query returning the TOTAL across every child kind.
|
|
174
174
|
attach_function :leptris_node_children,
|
|
175
175
|
[:leptris_node_ref, :pointer, :size_t], :size_t
|
|
176
|
+
# Element-only batch; mirror-only — fetch_children rides the
|
|
177
|
+
# all-kind node_children above.
|
|
178
|
+
attach_function :leptris_element_children,
|
|
179
|
+
[:leptris_element, :pointer, :size_t], :size_t
|
|
176
180
|
attach_function :leptris_node_as_element,
|
|
177
181
|
[:leptris_node_ref], :leptris_element
|
|
178
182
|
attach_function :leptris_element_as_node,
|
|
@@ -402,6 +406,12 @@ module Leptris
|
|
|
402
406
|
# which (element / synthetic attribute / text / other).
|
|
403
407
|
attach_function :leptris_xpath_result_node_kind,
|
|
404
408
|
[:leptris_xpath_result, :size_t], :int
|
|
409
|
+
# Pre-_ex variants, superseded by xpath_result_get_nodes_ex /
|
|
410
|
+
# xpath_result_get_node; mirror-only (ADR 0001).
|
|
411
|
+
attach_function :leptris_xpath_result_get,
|
|
412
|
+
[:leptris_xpath_result, :size_t], :leptris_element
|
|
413
|
+
attach_function :leptris_xpath_result_get_nodes,
|
|
414
|
+
[:leptris_xpath_result, :pointer, :size_t], :size_t
|
|
405
415
|
attach_function :leptris_xpath_result_get_node,
|
|
406
416
|
[:leptris_xpath_result, :size_t], :leptris_node_ref
|
|
407
417
|
attach_function :leptris_xpath_result_node_name,
|
|
@@ -447,6 +457,10 @@ module Leptris
|
|
|
447
457
|
[:leptris_xpath_ns_set], :void
|
|
448
458
|
# One-call constructor: flat array of 2*pair_count alternating
|
|
449
459
|
# prefix/URI strings.
|
|
460
|
+
# Per-pair add; superseded by ns_set_new_from_pairs (one call);
|
|
461
|
+
# mirror-only (ADR 0001).
|
|
462
|
+
attach_function :leptris_xpath_ns_set_add,
|
|
463
|
+
[:leptris_xpath_ns_set, :string, :string], :leptris_status
|
|
450
464
|
attach_function :leptris_xpath_ns_set_new_from_pairs,
|
|
451
465
|
[:pointer, :size_t], :leptris_xpath_ns_set
|
|
452
466
|
attach_function :leptris_xpath_eval_ns,
|
|
@@ -507,6 +521,10 @@ module Leptris
|
|
|
507
521
|
|
|
508
522
|
attach_function :leptris_document_serialize,
|
|
509
523
|
[:leptris_document, :pointer], :pointer
|
|
524
|
+
# Deprecated automatic-options alias (header: prefer
|
|
525
|
+
# leptris_document_serialize); mirror-only.
|
|
526
|
+
attach_function :leptris_serialize_document,
|
|
527
|
+
[:leptris_document], :pointer
|
|
510
528
|
# libleptris >= 1.9.0: caller-buffer serialization with options
|
|
511
529
|
# (leptris#541). buf=NULL is a size query; the size-query +
|
|
512
530
|
# fill pair reuses one serialization through a per-document
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: leptris
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.9.
|
|
4
|
+
version: 1.9.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ffi
|
|
@@ -74,6 +74,7 @@ files:
|
|
|
74
74
|
- docs/adr/0003-readonly-memoization-pattern.md
|
|
75
75
|
- docs/adr/0004-lifetime-guard.md
|
|
76
76
|
- docs/adr/0005-autoload-manifest-ordering.md
|
|
77
|
+
- docs/adr/0006-ruby-variant-policy.md
|
|
77
78
|
- leptris.gemspec
|
|
78
79
|
- lib/leptris.rb
|
|
79
80
|
- lib/leptris/version.rb
|