leptris 1.1.0 → 1.2.0
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 +82 -0
- data/README.adoc +1 -1
- data/Rakefile +91 -0
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/attr.rb +14 -5
- data/lib/leptris/xml/document.rb +21 -0
- data/lib/leptris/xml/element.rb +31 -17
- data/lib/leptris/xml/ffi.rb +42 -0
- data/lib/leptris/xml/node.rb +11 -2
- data/lib/leptris/xml/node_set.rb +8 -1
- metadata +2 -3
- data/lib/leptris/xml/c14n.rb +0 -23
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 28a6d0b01b14207e6a928eead2f38f84bf2dda03b9c7ae4a3c6bcc3163f2760d
|
|
4
|
+
data.tar.gz: e23962a817090c8707dc691fe58276f25da7075e1b0b64bb1fa39774cd113d4e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 83e9f30fd09ded2f909d651423db46be8a6baca9f39f310e9b8c3632f5fd7cd5627d193cd57c80d6191a2c00145858b87d9454a72df12e96a28fbd928864a663
|
|
7
|
+
data.tar.gz: 5991c97a7ea5b8a3462c62492fd70808f4094d2758525ce2ec6ac47a9ffe61bb318c71d1999c95081e5e48ce10cc98d9e671ea8c52400c3a90b01e4f9a5b3ce1
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,88 @@ All notable changes to Leptris will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.2.0] - 2026-08-23
|
|
9
|
+
|
|
10
|
+
Lockstep with libleptris 1.2.0.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `Leptris::XML::Document.create` — empty document with its own memory
|
|
15
|
+
pool (backs `leptris_document_create`); elements are created against
|
|
16
|
+
it via the factories and attached with `root=`.
|
|
17
|
+
- `Document#root=` — attach an element as the document root
|
|
18
|
+
(`leptris_document_set_root`). Companion to the moxml Leptris
|
|
19
|
+
adapter (lutaml/moxml#96).
|
|
20
|
+
- `Attr#to_xml` — serialized `name="value"` form with the five XML
|
|
21
|
+
special characters escaped.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- `Node#text` / `#inner_text` now dispatch to `#content` instead of
|
|
26
|
+
aliasing the base implementation — the alias snapshot meant subclass
|
|
27
|
+
overrides (`Text#content`, `Comment#content`, …) were never seen
|
|
28
|
+
through `text`.
|
|
29
|
+
- `Element#namespace` carries the element's own prefix, so consumers
|
|
30
|
+
can distinguish `{"p" => "urn:p"}` from the default namespace.
|
|
31
|
+
- CI/Rakefile pin libleptris v1.2.0, whose fixes resolve leptris#477
|
|
32
|
+
(mixed-nodeset kinds, `node_name`/`node_value` on non-element
|
|
33
|
+
entries, and `//node()` now including the context root). NodeSet
|
|
34
|
+
keeps its per-index fallback for the still-under-copying batch
|
|
35
|
+
accessor.
|
|
36
|
+
|
|
37
|
+
### Unbound v1.2.0 surface (follow-up)
|
|
38
|
+
|
|
39
|
+
Namespace-bound XPath (`leptris_xpath_ns_set_*`, `leptris_xpath_eval_ns`)
|
|
40
|
+
and error introspection (`leptris_error_message`, `leptris_last_error`).
|
|
41
|
+
|
|
42
|
+
## [1.1.1] - 2026-08-22
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- **Precompiled platform gems** (emf2svg-ruby model): `rake compile`
|
|
47
|
+
vendors libleptris into `lib/`, and `rake gem:native:<platform>`
|
|
48
|
+
builds binary gems for x86_64/aarch64-linux (incl. musl, via Alpine
|
|
49
|
+
containers), x64-mingw32/ucrt + aarch64-mingw-ucrt, and
|
|
50
|
+
x86_64/arm64-darwin. `gem install leptris` resolves the platform
|
|
51
|
+
gem and works with no system libleptris and no env vars — the FFI
|
|
52
|
+
search order prefers the vendored library. The pure-Ruby gem
|
|
53
|
+
remains the fallback for other setups.
|
|
54
|
+
- `Element#prefix` — the element's own namespace prefix (nil-safe).
|
|
55
|
+
- `Element#each_attribute` — Enumerator over the v1.1.0
|
|
56
|
+
attribute-iteration face; `attributes`/`attribute_nodes`/`keys`/
|
|
57
|
+
`values` now use it (O(n) vs the O(n^2) index-re-walk API).
|
|
58
|
+
- Bindings for the v1.1.0 public surface: attribute-iteration face,
|
|
59
|
+
mixed-nodeset API (`xpath_result_node_kind/get_node/node_name/
|
|
60
|
+
node_value` + kind constants), `leptris_serialize_document`,
|
|
61
|
+
`leptris_document_get_dtd`, `leptris_element_prefix`,
|
|
62
|
+
`leptris_xpath_register_function` (Ruby sugar deferred until the
|
|
63
|
+
callback return-string ownership is verified).
|
|
64
|
+
- `NodeSet` fetches via `get_node` (all node kinds) instead of the
|
|
65
|
+
elements-only `result_get`.
|
|
66
|
+
|
|
67
|
+
### Changed
|
|
68
|
+
|
|
69
|
+
- `release.yml` is standalone again, building the 8-platform matrix
|
|
70
|
+
plus musl containers and publishing all gems via OIDC trusted
|
|
71
|
+
publishing. Requires the rubygems.org trusted publisher to be
|
|
72
|
+
re-pointed at this workflow (filename `release.yml`, no
|
|
73
|
+
reusable-workflow fields).
|
|
74
|
+
- CI builds libleptris via `rake compile` (single version pin in the
|
|
75
|
+
Rakefile) instead of an inline cmake script.
|
|
76
|
+
|
|
77
|
+
### Fixed
|
|
78
|
+
|
|
79
|
+
- Removes the resurrected `c14n.rb` (its deletion was lost in a
|
|
80
|
+
rebase-merge; nothing referenced it).
|
|
81
|
+
|
|
82
|
+
### Known issues
|
|
83
|
+
|
|
84
|
+
- Mixed-kind XPath results misreport node types upstream
|
|
85
|
+
(leptris#477: enum-space collision; `node_name`/`node_value`
|
|
86
|
+
crash on text entries). Specs assert only the correct surface;
|
|
87
|
+
`NodeSet` carries a per-index fallback for the under-copying batch
|
|
88
|
+
accessor.
|
|
89
|
+
|
|
8
90
|
## [1.1.0] - 2026-08-22
|
|
9
91
|
|
|
10
92
|
Lockstep with libleptris 1.1.0.
|
data/README.adoc
CHANGED
|
@@ -397,7 +397,7 @@ bundle exec rspec spec/xml/xpath_spec.rb:42 # one example by line
|
|
|
397
397
|
bundle exec rubocop # lint
|
|
398
398
|
----
|
|
399
399
|
|
|
400
|
-
CI pins libleptris to a released tag (currently v1.1.
|
|
400
|
+
CI pins libleptris to a released tag (currently v1.1.1) and builds it
|
|
401
401
|
from source on each runner; see `.github/workflows/build.yml`.
|
|
402
402
|
|
|
403
403
|
== License
|
data/Rakefile
CHANGED
|
@@ -4,4 +4,95 @@ require "bundler/gem_tasks"
|
|
|
4
4
|
require "rspec/core/rake_task"
|
|
5
5
|
|
|
6
6
|
RSpec::Core::RakeTask.new(:spec)
|
|
7
|
+
|
|
8
|
+
# Pin for `rake compile` and the platform-gem builds. Keep in lockstep
|
|
9
|
+
# with .github/workflows/build.yml (which calls `rake compile`) and the
|
|
10
|
+
# CHANGELOG when libleptris releases.
|
|
11
|
+
LIBLEPTRIS_VERSION = "1.2.0"
|
|
12
|
+
|
|
13
|
+
CMAKE_FLAGS = %w[
|
|
14
|
+
-DCMAKE_BUILD_TYPE=Release
|
|
15
|
+
-DLEPTRIS_BUILD_SHARED=ON
|
|
16
|
+
-DLEPTRIS_BUILD_STATIC=OFF
|
|
17
|
+
-DBUILD_TESTING=OFF
|
|
18
|
+
-DLEPTRIS_BUILD_CLI=OFF
|
|
19
|
+
-DLEPTRIS_BUILD_BENCHMARKS=OFF
|
|
20
|
+
-DLEPTRIS_BUILD_MAN_PAGES=OFF
|
|
21
|
+
-DLEPTRIS_ENABLE_UTF8PROC=OFF
|
|
22
|
+
-DLEPTRIS_ENABLE_ICONV=OFF
|
|
23
|
+
].freeze
|
|
24
|
+
|
|
25
|
+
desc "Build libleptris #{LIBLEPTRIS_VERSION} from its release tarball into lib/"
|
|
26
|
+
task :compile do
|
|
27
|
+
version = ENV.fetch("LIBLEPTRIS_VERSION", LIBLEPTRIS_VERSION)
|
|
28
|
+
build = File.expand_path("tmp/libleptris-#{version}", __dir__)
|
|
29
|
+
rm_rf(build)
|
|
30
|
+
mkdir_p(build)
|
|
31
|
+
url = "https://api.github.com/repos/leptris/leptris/tarball/v#{version}"
|
|
32
|
+
sh "curl -sL #{url} | tar xz -C #{build} --strip-components=1"
|
|
33
|
+
sh "cmake -B #{build}/build -S #{build} #{CMAKE_FLAGS.join(' ')}"
|
|
34
|
+
sh "cmake --build #{build}/build --config Release -j 4"
|
|
35
|
+
# Windows names the shared library leptris.dll (no "lib" prefix);
|
|
36
|
+
# vendoring under the uniform libleptris.* name keeps the FFI
|
|
37
|
+
# search order simple.
|
|
38
|
+
lib = Dir.glob("#{build}/build/**/libleptris.{dylib,so}").first ||
|
|
39
|
+
Dir.glob("#{build}/build/**/leptris.dll").first
|
|
40
|
+
raise "libleptris shared library not found after build" unless lib
|
|
41
|
+
ext = File.extname(lib)
|
|
42
|
+
cp(lib, "lib/libleptris#{ext}")
|
|
43
|
+
puts "Vendored #{File.basename(lib)} as lib/libleptris#{ext}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
task spec: :compile unless ENV.key?("LEPTRIS_LIB_PATH")
|
|
47
|
+
|
|
7
48
|
task default: :spec
|
|
49
|
+
|
|
50
|
+
require "rubygems/package_task"
|
|
51
|
+
|
|
52
|
+
desc "Build the pure-Ruby gem"
|
|
53
|
+
task "gem:native:any" do
|
|
54
|
+
sh "rake platform:any gem"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
desc "Define the gem task to build the pure-Ruby gem"
|
|
58
|
+
task "platform:any" do
|
|
59
|
+
spec = Gem::Specification::load("leptris.gemspec").dup
|
|
60
|
+
task = Gem::PackageTask.new(spec)
|
|
61
|
+
task.define
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
platforms = [
|
|
65
|
+
"x64-mingw32",
|
|
66
|
+
"x64-mingw-ucrt",
|
|
67
|
+
"aarch64-mingw-ucrt",
|
|
68
|
+
"x86_64-linux",
|
|
69
|
+
"x86_64-linux-musl",
|
|
70
|
+
"aarch64-linux",
|
|
71
|
+
"aarch64-linux-musl",
|
|
72
|
+
"x86_64-darwin",
|
|
73
|
+
"arm64-darwin",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
platforms.each do |platform|
|
|
77
|
+
desc "Build pre-compiled gem for the #{platform} platform"
|
|
78
|
+
task "gem:native:#{platform}" do
|
|
79
|
+
sh "rake compile platform:#{platform} gem"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
desc "Define the gem task to build on the #{platform} platform (binary gem)"
|
|
83
|
+
task "platform:#{platform}" do
|
|
84
|
+
spec = Gem::Specification::load("leptris.gemspec").dup
|
|
85
|
+
spec.platform = Gem::Platform.new(platform)
|
|
86
|
+
spec.files += Dir.glob("lib/libleptris.{dll,so,dylib}")
|
|
87
|
+
task = Gem::PackageTask.new(spec)
|
|
88
|
+
task.define
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
require "rake/clean"
|
|
93
|
+
|
|
94
|
+
CLOBBER.include("pkg")
|
|
95
|
+
CLEAN.include("tmp",
|
|
96
|
+
"lib/libleptris.dll",
|
|
97
|
+
"lib/libleptris.dylib",
|
|
98
|
+
"lib/libleptris.so")
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/attr.rb
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Lightweight attribute wrapper
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# element via #[]. Once upstream adds attribute-level accessors, this can
|
|
7
|
-
# become a pointer-backed wrapper.
|
|
3
|
+
# Lightweight attribute wrapper: a (name, value, parent_element) triple
|
|
4
|
+
# materialized from the attribute-iteration face. Mutation goes through
|
|
5
|
+
# the parent element via #[].
|
|
8
6
|
|
|
9
7
|
class Leptris::XML::Attr
|
|
10
8
|
attr_reader :name, :value, :element
|
|
@@ -31,6 +29,17 @@ class Leptris::XML::Attr
|
|
|
31
29
|
def to_s; @value; end
|
|
32
30
|
def to_str; @value; end
|
|
33
31
|
|
|
32
|
+
# Serialized attribute form: name="value" with the five XML special
|
|
33
|
+
# characters escaped in the value.
|
|
34
|
+
def to_xml
|
|
35
|
+
escaped = @value.to_s.gsub("&", "&")
|
|
36
|
+
.gsub("<", "<")
|
|
37
|
+
.gsub(">", ">")
|
|
38
|
+
.gsub('"', """)
|
|
39
|
+
.gsub("'", "'")
|
|
40
|
+
"#{@name}=\"#{escaped}\""
|
|
41
|
+
end
|
|
42
|
+
|
|
34
43
|
def ==(other)
|
|
35
44
|
other.is_a?(Leptris::XML::Attr) &&
|
|
36
45
|
@name == other.name && @value == other.value &&
|
data/lib/leptris/xml/document.rb
CHANGED
|
@@ -66,6 +66,16 @@ class Leptris::XML::Document
|
|
|
66
66
|
wrap(raw)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
# Create an empty document (no root element) backed by its own memory
|
|
70
|
+
# pool. Elements for the tree are created against it via
|
|
71
|
+
# #create_element and friends, then attached with #root=.
|
|
72
|
+
def self.create
|
|
73
|
+
raw = Leptris::XML::FFI.leptris_document_create
|
|
74
|
+
raise Leptris::XML::Error,
|
|
75
|
+
"leptris_document_create failed" if raw.null?
|
|
76
|
+
wrap(raw)
|
|
77
|
+
end
|
|
78
|
+
|
|
69
79
|
# Convert a raw LeptrisDocument pointer into a Ruby Document with safe
|
|
70
80
|
# GC lifetime management. The finalizer captures the raw address
|
|
71
81
|
# integer (not the Document or Pointer object — those would prevent
|
|
@@ -107,6 +117,17 @@ class Leptris::XML::Document
|
|
|
107
117
|
Leptris::XML::Node.wrap(ptr, self)
|
|
108
118
|
end
|
|
109
119
|
|
|
120
|
+
# Attach +element+ as the document's root element. The element must
|
|
121
|
+
# have been created against this document and must not already have
|
|
122
|
+
# a parent. Any previous root is left detached (still owned by the
|
|
123
|
+
# document's pool until #free).
|
|
124
|
+
def root=(element)
|
|
125
|
+
raise Leptris::XML::UseAfterFreeError if @freed.state == :freed
|
|
126
|
+
Leptris::XML::FFI.check_status(
|
|
127
|
+
Leptris::XML::FFI.leptris_document_set_root(@c_ptr, element.c_ptr))
|
|
128
|
+
element
|
|
129
|
+
end
|
|
130
|
+
|
|
110
131
|
def create_element(name)
|
|
111
132
|
ptr = Leptris::XML::FFI.leptris_element_create(@c_ptr, name)
|
|
112
133
|
raise Leptris::XML::Error, "leptris_element_create failed" if ptr.null?
|
data/lib/leptris/xml/element.rb
CHANGED
|
@@ -48,34 +48,43 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
48
48
|
end
|
|
49
49
|
alias_method :delete, :remove_attribute
|
|
50
50
|
|
|
51
|
+
# Iterates the element's attributes via the v1.1.0 linked-list face
|
|
52
|
+
# (one FFI call per attribute; the name_at/value_at indexing API
|
|
53
|
+
# re-walks the list per index, making it O(n^2) per element).
|
|
54
|
+
def each_attribute
|
|
55
|
+
return enum_for(:each_attribute) unless block_given?
|
|
56
|
+
attr = Leptris::XML::FFI.leptris_element_first_attribute(@c_ptr)
|
|
57
|
+
until attr.nil? || attr.null?
|
|
58
|
+
name = Leptris::XML::FFI.leptris_attribute_get_name(attr)
|
|
59
|
+
value = Leptris::XML::FFI.leptris_attribute_get_value(@c_ptr, attr)
|
|
60
|
+
yield Leptris::XML::Attr.new(name, value, self)
|
|
61
|
+
attr = Leptris::XML::FFI.leptris_attribute_next(attr)
|
|
62
|
+
end
|
|
63
|
+
self
|
|
64
|
+
end
|
|
65
|
+
|
|
51
66
|
def keys
|
|
52
|
-
|
|
53
|
-
count.times.map { |i| Leptris::XML::FFI.leptris_element_attribute_name_at(@c_ptr, i) }
|
|
67
|
+
each_attribute.to_a.map(&:name)
|
|
54
68
|
end
|
|
55
69
|
|
|
56
70
|
def values
|
|
57
|
-
|
|
58
|
-
count.times.map { |i| Leptris::XML::FFI.leptris_element_attribute_value_at(@c_ptr, i) }
|
|
71
|
+
each_attribute.to_a.map(&:value)
|
|
59
72
|
end
|
|
60
73
|
|
|
61
74
|
def attributes
|
|
62
|
-
count = Leptris::XML::FFI.leptris_element_attribute_count(@c_ptr)
|
|
63
75
|
result = {}
|
|
64
|
-
|
|
65
|
-
name = Leptris::XML::FFI.leptris_element_attribute_name_at(@c_ptr, i)
|
|
66
|
-
value = Leptris::XML::FFI.leptris_element_attribute_value_at(@c_ptr, i)
|
|
67
|
-
result[name] = Leptris::XML::Attr.new(name, value, self)
|
|
68
|
-
end
|
|
76
|
+
each_attribute { |attr| result[attr.name] = attr }
|
|
69
77
|
result
|
|
70
78
|
end
|
|
71
79
|
|
|
72
80
|
def attribute_nodes
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
each_attribute.to_a
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# The element's own namespace prefix (e.g. "foo" for <foo:child/>),
|
|
85
|
+
# or nil when the element has none.
|
|
86
|
+
def prefix
|
|
87
|
+
Leptris::XML::FFI.leptris_element_prefix(@c_ptr)
|
|
79
88
|
end
|
|
80
89
|
|
|
81
90
|
def prepend_child(node)
|
|
@@ -182,7 +191,12 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
182
191
|
def namespace
|
|
183
192
|
uri = Leptris::XML::FFI.leptris_element_namespace(@c_ptr)
|
|
184
193
|
return nil if uri.nil? || uri.empty?
|
|
185
|
-
|
|
194
|
+
# The resolved namespace is reached via this element's own prefix,
|
|
195
|
+
# so carry it through: consumers that distinguish
|
|
196
|
+
# {"p" => "urn:p"} from the default {"nil => "urn:p"} need it.
|
|
197
|
+
prefix = Leptris::XML::FFI.leptris_element_prefix(@c_ptr)
|
|
198
|
+
prefix = nil if prefix.nil? || prefix.empty?
|
|
199
|
+
Leptris::XML::Namespace.new(self, uri, prefix: prefix)
|
|
186
200
|
end
|
|
187
201
|
|
|
188
202
|
def namespace_definitions
|
data/lib/leptris/xml/ffi.rb
CHANGED
|
@@ -11,6 +11,7 @@ module Leptris
|
|
|
11
11
|
ENV["LEPTRIS_LIB_PATH"],
|
|
12
12
|
File.expand_path("../../libleptris.dylib", __dir__),
|
|
13
13
|
File.expand_path("../../libleptris.so", __dir__),
|
|
14
|
+
File.expand_path("../../libleptris.dll", __dir__),
|
|
14
15
|
"/usr/local/lib/libleptris.dylib",
|
|
15
16
|
"/usr/local/lib/libleptris.so",
|
|
16
17
|
"leptris",
|
|
@@ -65,6 +66,10 @@ module Leptris
|
|
|
65
66
|
[:string, :pointer], :pointer
|
|
66
67
|
attach_function :leptris_document_free,
|
|
67
68
|
[:leptris_document], :void
|
|
69
|
+
attach_function :leptris_document_create,
|
|
70
|
+
[], :leptris_document
|
|
71
|
+
attach_function :leptris_document_set_root,
|
|
72
|
+
[:leptris_document, :leptris_element], :leptris_status
|
|
68
73
|
attach_function :leptris_document_root,
|
|
69
74
|
[:leptris_document], :leptris_element
|
|
70
75
|
attach_function :leptris_document_encoding,
|
|
@@ -186,6 +191,16 @@ module Leptris
|
|
|
186
191
|
[:leptris_element, :string, :int], :int
|
|
187
192
|
attach_function :leptris_element_has_attribute,
|
|
188
193
|
[:leptris_element, :string], :int
|
|
194
|
+
attach_function :leptris_element_first_attribute,
|
|
195
|
+
[:leptris_element], :leptris_attribute
|
|
196
|
+
attach_function :leptris_attribute_next,
|
|
197
|
+
[:leptris_attribute], :leptris_attribute
|
|
198
|
+
attach_function :leptris_attribute_get_name,
|
|
199
|
+
[:leptris_attribute], :string
|
|
200
|
+
attach_function :leptris_attribute_get_value,
|
|
201
|
+
[:leptris_element, :leptris_attribute], :string
|
|
202
|
+
attach_function :leptris_element_prefix,
|
|
203
|
+
[:leptris_element], :string
|
|
189
204
|
attach_function :leptris_element_attribute_count,
|
|
190
205
|
[:leptris_element], :size_t
|
|
191
206
|
attach_function :leptris_element_attribute_name_at,
|
|
@@ -310,6 +325,22 @@ module Leptris
|
|
|
310
325
|
[:leptris_xpath_result], :size_t
|
|
311
326
|
attach_function :leptris_xpath_result_get,
|
|
312
327
|
[:leptris_xpath_result, :size_t], :leptris_element
|
|
328
|
+
# Mixed nodesets (v1.1.0): unlike result_get (elements only),
|
|
329
|
+
# get_node returns the node whatever its kind; node_kind reports
|
|
330
|
+
# which (element / synthetic attribute / text / other).
|
|
331
|
+
attach_function :leptris_xpath_result_node_kind,
|
|
332
|
+
[:leptris_xpath_result, :size_t], :int
|
|
333
|
+
attach_function :leptris_xpath_result_get_node,
|
|
334
|
+
[:leptris_xpath_result, :size_t], :leptris_node_ref
|
|
335
|
+
attach_function :leptris_xpath_result_node_name,
|
|
336
|
+
[:leptris_xpath_result, :size_t], :string
|
|
337
|
+
attach_function :leptris_xpath_result_node_value,
|
|
338
|
+
[:leptris_xpath_result, :size_t], :string
|
|
339
|
+
# Document-scoped custom XPath functions. The callback receives
|
|
340
|
+
# (const char* const* args, int argc, void* user_data) and returns
|
|
341
|
+
# a heap string the library frees.
|
|
342
|
+
attach_function :leptris_xpath_register_function,
|
|
343
|
+
[:leptris_document, :string, :pointer, :pointer], :leptris_status
|
|
313
344
|
attach_function :leptris_xpath_result_get_nodes,
|
|
314
345
|
[:leptris_xpath_result, :pointer, :size_t], :size_t
|
|
315
346
|
attach_function :leptris_xpath_result_boolean,
|
|
@@ -347,6 +378,12 @@ module Leptris
|
|
|
347
378
|
|
|
348
379
|
attach_function :leptris_document_serialize,
|
|
349
380
|
[:leptris_document, :pointer], :pointer
|
|
381
|
+
# Legacy simple serializer, declared retroactively in v1.1.0.
|
|
382
|
+
# Prefer leptris_document_serialize; bound for API completeness.
|
|
383
|
+
attach_function :leptris_serialize_document,
|
|
384
|
+
[:leptris_document], :pointer
|
|
385
|
+
attach_function :leptris_document_get_dtd,
|
|
386
|
+
[:leptris_document], :pointer
|
|
350
387
|
attach_function :leptris_element_serialize,
|
|
351
388
|
[:leptris_element, :pointer], :pointer
|
|
352
389
|
attach_function :leptris_document_save_file,
|
|
@@ -398,6 +435,11 @@ module Leptris
|
|
|
398
435
|
XPATH_NUMBER = 2
|
|
399
436
|
XPATH_STRING = 3
|
|
400
437
|
|
|
438
|
+
XPATH_NODE_ELEMENT = 0
|
|
439
|
+
XPATH_NODE_ATTRIBUTE = 1
|
|
440
|
+
XPATH_NODE_TEXT = 2
|
|
441
|
+
XPATH_NODE_OTHER = 3
|
|
442
|
+
|
|
401
443
|
NODE_ELEMENT = 0
|
|
402
444
|
NODE_TEXT = 1
|
|
403
445
|
NODE_COMMENT = 2
|
data/lib/leptris/xml/node.rb
CHANGED
|
@@ -45,8 +45,17 @@ class Leptris::XML::Node
|
|
|
45
45
|
def content
|
|
46
46
|
raise NotImplementedError, "#{self.class}#content not implemented"
|
|
47
47
|
end
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
|
|
49
|
+
# Dispatching defs, not alias_method: an alias snapshots this base
|
|
50
|
+
# #content (the raise), so subclass overrides would never be seen
|
|
51
|
+
# through the alias. A plain method resolves #content per-call.
|
|
52
|
+
def text
|
|
53
|
+
content
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def inner_text
|
|
57
|
+
content
|
|
58
|
+
end
|
|
50
59
|
|
|
51
60
|
def type
|
|
52
61
|
Leptris::XML::FFI.leptris_node_get_type(@c_ptr)
|
data/lib/leptris/xml/node_set.rb
CHANGED
|
@@ -52,7 +52,7 @@ class Leptris::XML::NodeSet
|
|
|
52
52
|
def [](idx)
|
|
53
53
|
if @result_ptr
|
|
54
54
|
return nil if idx < 0 || idx >= length
|
|
55
|
-
ptr = Leptris::XML::FFI.
|
|
55
|
+
ptr = Leptris::XML::FFI.leptris_xpath_result_get_node(@result_ptr, idx)
|
|
56
56
|
return nil if ptr.null?
|
|
57
57
|
Leptris::XML::Node.wrap(ptr, @document)
|
|
58
58
|
else
|
|
@@ -77,6 +77,13 @@ class Leptris::XML::NodeSet
|
|
|
77
77
|
next if ptr.null?
|
|
78
78
|
yield Leptris::XML::Node.wrap(ptr, @document)
|
|
79
79
|
end
|
|
80
|
+
# The batch accessor under-copies mixed-kind results (upstream
|
|
81
|
+
# leptris#477); fall back to per-index fetch for the remainder.
|
|
82
|
+
(copied...n).each do |i|
|
|
83
|
+
ptr = Leptris::XML::FFI.leptris_xpath_result_get_node(@result_ptr, i)
|
|
84
|
+
next if ptr.null?
|
|
85
|
+
yield Leptris::XML::Node.wrap(ptr, @document)
|
|
86
|
+
end
|
|
80
87
|
ensure
|
|
81
88
|
buf.free
|
|
82
89
|
end
|
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.
|
|
4
|
+
version: 1.2.0
|
|
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-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ffi
|
|
@@ -112,7 +112,6 @@ files:
|
|
|
112
112
|
- lib/leptris/version.rb
|
|
113
113
|
- lib/leptris/xml.rb
|
|
114
114
|
- lib/leptris/xml/attr.rb
|
|
115
|
-
- lib/leptris/xml/c14n.rb
|
|
116
115
|
- lib/leptris/xml/c_string_array.rb
|
|
117
116
|
- lib/leptris/xml/cdata.rb
|
|
118
117
|
- lib/leptris/xml/comment.rb
|
data/lib/leptris/xml/c14n.rb
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Leptris
|
|
4
|
-
module XML
|
|
5
|
-
# @api private
|
|
6
|
-
#
|
|
7
|
-
# Builds a NULL-terminated `const char**` from a Ruby array of strings,
|
|
8
|
-
# suitable for passing to leptris_c14n_canonicalize_ex's
|
|
9
|
-
# `inclusive_ns_prefixes` argument. Returns [pointer, anchor] where
|
|
10
|
-
# anchor is the underlying MemoryPointer that the caller must keep
|
|
11
|
-
# alive (e.g. via local variable) for the duration of the FFI call.
|
|
12
|
-
def self.c14n_build_ns_pointer(inclusive_namespaces)
|
|
13
|
-
return [nil, nil] if inclusive_namespaces.nil? || inclusive_namespaces.empty?
|
|
14
|
-
strs = Array(inclusive_namespaces).map(&:to_s)
|
|
15
|
-
ptr_size = ::FFI.type_size(:pointer)
|
|
16
|
-
buffer = ::FFI::MemoryPointer.new(:pointer, strs.size + 1)
|
|
17
|
-
anchors = strs.map { |s| ::FFI::MemoryPointer.from_string(s) }
|
|
18
|
-
anchors.each_with_index { |p, i| buffer.put_pointer(i * ptr_size, p) }
|
|
19
|
-
buffer.put_pointer(strs.size * ptr_size, nil)
|
|
20
|
-
[buffer, anchors]
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|