leptris 1.0.0 → 1.1.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 +42 -0
- data/README.adoc +1 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/c_string_array.rb +37 -0
- data/lib/leptris/xml/cdata.rb +2 -3
- data/lib/leptris/xml/comment.rb +2 -3
- data/lib/leptris/xml/document.rb +53 -43
- data/lib/leptris/xml/element.rb +40 -68
- data/lib/leptris/xml/ffi.rb +23 -6
- data/lib/leptris/xml/node.rb +5 -15
- data/lib/leptris/xml/parse_options.rb +21 -11
- data/lib/leptris/xml/processing_instruction.rb +4 -6
- data/lib/leptris/xml/sax/parser.rb +3 -15
- data/lib/leptris/xml/searchable.rb +1 -2
- data/lib/leptris/xml/serialization.rb +41 -0
- data/lib/leptris/xml/text.rb +2 -3
- data/lib/leptris/xml.rb +4 -3
- metadata +8 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ec7aed2502125d89ff790ecd754cab242aec563ee58c45ceaf7e86cac4e96f85
|
|
4
|
+
data.tar.gz: 6e9a72b02fc76907143756992afa7866415912bbe5fa9282d1800974cfde8b74
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1e2a587372714a54f60e560bd175885f09fa3dd5ec3c0d620715c58e152f36651606e63770000f1a7c5162590299328967942093a96a45f9cac923db2c86d369
|
|
7
|
+
data.tar.gz: 60aee00fff31eeba0e89a4b5d4affdf93dff22e5080db1093d17b05e6b029d94608f7934f5ec9c1c4eed3b7bfb3d905adb47f8e51ecc0645f8d52cb3e51da453
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,48 @@ 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.1.0] - 2026-08-22
|
|
9
|
+
|
|
10
|
+
Lockstep with libleptris 1.1.0.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `Leptris::XML.parse(xml, options:)` and
|
|
15
|
+
`Leptris::XML::ParseOptions.noblanks` — whitespace-only text nodes
|
|
16
|
+
can now be dropped at parse time (libxml2 `XML_PARSE_NOBLANKS` /
|
|
17
|
+
Nokogiri `noblanks` parity), via the new
|
|
18
|
+
`leptris_parse_string_flags` FFI entry point.
|
|
19
|
+
- Wrapper identity is now a guarantee: every path to a C node
|
|
20
|
+
(`root`, factories, navigation, xpath results) goes through
|
|
21
|
+
`Node.wrap` and its per-document cache, so the same node always
|
|
22
|
+
yields the same Ruby object.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Architecture deepening: one status seam (`FFI.check_status`)
|
|
27
|
+
replaces 21 copy-pasted raises; one owned-string read
|
|
28
|
+
(`FFI.read_owned_string`) replaces 6 hand-rolled read-then-free
|
|
29
|
+
dances; a `Serialization` module owns the serialize/canonicalize
|
|
30
|
+
options lifecycle for both `Document` and `Element`; a
|
|
31
|
+
`CStringArray` adapter owns the NULL-terminated `char**` wire
|
|
32
|
+
format in both directions (c14n.rb folded in; the eager require is
|
|
33
|
+
gone and the autoload convention is uniform again).
|
|
34
|
+
|
|
35
|
+
### Removed
|
|
36
|
+
|
|
37
|
+
- `ParseOptions` constants that mapped to nothing (`RECOVER`,
|
|
38
|
+
`STRICT`, `NOCDATA`, …). They were never honored by any code path;
|
|
39
|
+
the class now exposes only the real flag surface.
|
|
40
|
+
|
|
41
|
+
### Changed (libleptris 1.1.0 lockstep)
|
|
42
|
+
|
|
43
|
+
- `Element#key?` now calls native `leptris_element_has_attribute`
|
|
44
|
+
(the v0.4.4-era export gap is closed) instead of emulating via
|
|
45
|
+
attribute lookup. The two stale "not exported" comments in ffi.rb
|
|
46
|
+
are gone; `leptris_xinclude_get_encoding` is bound.
|
|
47
|
+
- CI builds libleptris v1.1.0, whose SAX `LEPTRIS_API` annotations
|
|
48
|
+
(#430 fix) unblock Windows: all 9 matrix jobs expected green.
|
|
49
|
+
|
|
8
50
|
## [1.0.0] - 2026-08-21
|
|
9
51
|
|
|
10
52
|
The leptris rebrand, in lockstep with libleptris 1.0.0. Every
|
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
|
|
400
|
+
CI pins libleptris to a released tag (currently v1.1.0) and builds it
|
|
401
401
|
from source on each runner; see `.github/workflows/build.yml`.
|
|
402
402
|
|
|
403
403
|
== License
|
data/lib/leptris/version.rb
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapter for libleptris's NULL-terminated char** wire format. Both
|
|
4
|
+
# directions of the conversion live here so the pointer arithmetic has
|
|
5
|
+
# one home (previously c14n built the array by hand and SAX walked it
|
|
6
|
+
# by hand).
|
|
7
|
+
module Leptris::XML::CStringArray
|
|
8
|
+
# Ruby strings -> NULL-terminated char**. Returns [buffer, anchors];
|
|
9
|
+
# the caller must keep both referenced for the duration of the FFI
|
|
10
|
+
# call (e.g. via local variables).
|
|
11
|
+
def self.to_c(strings)
|
|
12
|
+
strs = Array(strings).map(&:to_s)
|
|
13
|
+
return [nil, nil] if strs.empty?
|
|
14
|
+
|
|
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
|
+
|
|
23
|
+
# NULL-terminated char** -> Ruby strings. Stops at the first NULL
|
|
24
|
+
# entry. Returns [] for a NULL pointer.
|
|
25
|
+
def self.to_ruby(ptr)
|
|
26
|
+
return [] if ptr.nil? || ptr.null?
|
|
27
|
+
|
|
28
|
+
ptr_size = ::FFI::type_size(:pointer)
|
|
29
|
+
result = []
|
|
30
|
+
offset = 0
|
|
31
|
+
while (entry = ptr.get_pointer(offset)) && !entry.null?
|
|
32
|
+
result << entry.read_string
|
|
33
|
+
offset += ptr_size
|
|
34
|
+
end
|
|
35
|
+
result
|
|
36
|
+
end
|
|
37
|
+
end
|
data/lib/leptris/xml/cdata.rb
CHANGED
|
@@ -8,9 +8,8 @@ class Leptris::XML::CDATA < Leptris::XML::Text
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def content=(new_content)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
11
|
+
Leptris::XML::FFI.check_status(
|
|
12
|
+
Leptris::XML::FFI.leptris_cdata_node_set_content(@c_ptr, new_content.to_s))
|
|
14
13
|
new_content
|
|
15
14
|
end
|
|
16
15
|
end
|
data/lib/leptris/xml/comment.rb
CHANGED
|
@@ -8,9 +8,8 @@ class Leptris::XML::Comment < Leptris::XML::Node
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def content=(new_content)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
11
|
+
Leptris::XML::FFI.check_status(
|
|
12
|
+
Leptris::XML::FFI.leptris_comment_node_set_content(@c_ptr, new_content.to_s))
|
|
14
13
|
new_content
|
|
15
14
|
end
|
|
16
15
|
end
|
data/lib/leptris/xml/document.rb
CHANGED
|
@@ -17,20 +17,36 @@ class Leptris::XML::Document
|
|
|
17
17
|
def initialize(c_ptr = nil, freed = Freed.new(:alive))
|
|
18
18
|
@c_ptr = c_ptr
|
|
19
19
|
@freed = freed
|
|
20
|
-
# Per-document
|
|
21
|
-
# address.
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
# Per-document STRONG cache for Node wrappers, keyed on c_ptr
|
|
21
|
+
# address. Every wrapper is created through Node.wrap, which is the
|
|
22
|
+
# single construction path, so the same C node always yields the
|
|
23
|
+
# same Ruby object. Cleared when the Document is freed — no stale
|
|
24
|
+
# entries.
|
|
25
|
+
#
|
|
26
|
+
# Deliberately NOT ObjectSpace::WeakMap: a weak cache makes wrapper
|
|
27
|
+
# identity a GC race. `doc.root.equal?(doc.root)` failed on the
|
|
28
|
+
# Windows CI matrix (188 examples, the 4 identity specs) because
|
|
29
|
+
# between the two calls the first wrapper was referenced only by
|
|
30
|
+
# the weak map — any GC sweep evicted it and the second call built
|
|
31
|
+
# a fresh object. A strong cache costs at most one wrapper per node
|
|
32
|
+
# actually visited, held until the document dies.
|
|
33
|
+
@wrapper_cache = {}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.parse(xml_or_io, options: nil)
|
|
28
37
|
xml = xml_or_io.respond_to?(:read) ? xml_or_io.read : xml_or_io.to_s
|
|
29
38
|
if xml.empty?
|
|
30
39
|
raise Leptris::XML::ParseError, "empty input"
|
|
31
40
|
end
|
|
41
|
+
flags = resolve_flags(options)
|
|
32
42
|
status_ptr = ::FFI::MemoryPointer.new(:int)
|
|
33
|
-
raw =
|
|
43
|
+
raw =
|
|
44
|
+
if flags.zero?
|
|
45
|
+
Leptris::XML::FFI.leptris_parse_string(xml, xml.bytesize, status_ptr)
|
|
46
|
+
else
|
|
47
|
+
Leptris::XML::FFI.leptris_parse_string_flags(
|
|
48
|
+
xml, xml.bytesize, flags, status_ptr)
|
|
49
|
+
end
|
|
34
50
|
if raw.null?
|
|
35
51
|
status = status_ptr.read_int
|
|
36
52
|
raise Leptris::XML::ParseError,
|
|
@@ -74,42 +90,51 @@ class Leptris::XML::Document
|
|
|
74
90
|
end
|
|
75
91
|
private_class_method :finalizer
|
|
76
92
|
|
|
93
|
+
def self.resolve_flags(options)
|
|
94
|
+
return Leptris::XML::FFI::LEPTRIS_PARSE_DEFAULT if options.nil?
|
|
95
|
+
unless options.is_a?(Leptris::XML::ParseOptions)
|
|
96
|
+
raise ArgumentError, "options must be a Leptris::XML::ParseOptions"
|
|
97
|
+
end
|
|
98
|
+
options.flags
|
|
99
|
+
end
|
|
100
|
+
private_class_method :resolve_flags
|
|
101
|
+
|
|
77
102
|
def root
|
|
78
103
|
raise Leptris::XML::UseAfterFreeError if @freed.state == :freed
|
|
79
104
|
return nil if @c_ptr.nil?
|
|
80
105
|
ptr = Leptris::XML::FFI.leptris_document_root(@c_ptr)
|
|
81
106
|
return nil if ptr.null?
|
|
82
|
-
Leptris::XML::
|
|
107
|
+
Leptris::XML::Node.wrap(ptr, self)
|
|
83
108
|
end
|
|
84
109
|
|
|
85
110
|
def create_element(name)
|
|
86
111
|
ptr = Leptris::XML::FFI.leptris_element_create(@c_ptr, name)
|
|
87
112
|
raise Leptris::XML::Error, "leptris_element_create failed" if ptr.null?
|
|
88
|
-
Leptris::XML::
|
|
113
|
+
Leptris::XML::Node.wrap(ptr, self)
|
|
89
114
|
end
|
|
90
115
|
|
|
91
116
|
def create_text_node(content)
|
|
92
117
|
ptr = Leptris::XML::FFI.leptris_text_node_create(@c_ptr, content.to_s)
|
|
93
118
|
raise Leptris::XML::Error, "leptris_text_node_create failed" if ptr.null?
|
|
94
|
-
Leptris::XML::
|
|
119
|
+
Leptris::XML::Node.wrap(ptr, self)
|
|
95
120
|
end
|
|
96
121
|
|
|
97
122
|
def create_comment(content)
|
|
98
123
|
ptr = Leptris::XML::FFI.leptris_comment_node_create(@c_ptr, content.to_s)
|
|
99
124
|
raise Leptris::XML::Error, "leptris_comment_node_create failed" if ptr.null?
|
|
100
|
-
Leptris::XML::
|
|
125
|
+
Leptris::XML::Node.wrap(ptr, self)
|
|
101
126
|
end
|
|
102
127
|
|
|
103
128
|
def create_cdata(content)
|
|
104
129
|
ptr = Leptris::XML::FFI.leptris_cdata_node_create(@c_ptr, content.to_s)
|
|
105
130
|
raise Leptris::XML::Error, "leptris_cdata_node_create failed" if ptr.null?
|
|
106
|
-
Leptris::XML::
|
|
131
|
+
Leptris::XML::Node.wrap(ptr, self)
|
|
107
132
|
end
|
|
108
133
|
|
|
109
134
|
def create_processing_instruction(target, data = "")
|
|
110
135
|
ptr = Leptris::XML::FFI.leptris_pi_node_create(@c_ptr, target.to_s, data.to_s)
|
|
111
136
|
raise Leptris::XML::Error, "leptris_pi_node_create failed" if ptr.null?
|
|
112
|
-
Leptris::XML::
|
|
137
|
+
Leptris::XML::Node.wrap(ptr, self)
|
|
113
138
|
end
|
|
114
139
|
|
|
115
140
|
def fragment(markup)
|
|
@@ -133,22 +158,21 @@ class Leptris::XML::Document
|
|
|
133
158
|
def to_xml(indent: 0, no_decl: false, encoding: nil)
|
|
134
159
|
raise Leptris::XML::UseAfterFreeError if @freed.state == :freed
|
|
135
160
|
return "" if @c_ptr.nil?
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
str_ptr.read_string.tap { |s| Leptris::XML::FFI.leptris_free_string(str_ptr) }
|
|
161
|
+
Leptris::XML::Serialization.to_xml(
|
|
162
|
+
Leptris::XML::FFI.method(:leptris_document_serialize), @c_ptr,
|
|
163
|
+
indent: indent, no_decl: no_decl, encoding: encoding)
|
|
140
164
|
end
|
|
141
165
|
alias_method :to_s, :to_xml
|
|
142
166
|
alias_method :serialize, :to_xml
|
|
143
167
|
|
|
144
168
|
def save(path, **opts)
|
|
145
|
-
opts_struct,
|
|
169
|
+
opts_struct, _encoding_anchor = Leptris::XML::Serialization.build_options(
|
|
146
170
|
indent: opts.fetch(:indent, 0),
|
|
147
171
|
no_decl: opts.fetch(:no_decl, false),
|
|
148
172
|
encoding: opts[:encoding])
|
|
149
|
-
status = Leptris::XML::FFI.leptris_document_save_file(
|
|
150
|
-
|
|
151
|
-
|
|
173
|
+
status = Leptris::XML::FFI.leptris_document_save_file(
|
|
174
|
+
@c_ptr, path, opts_struct.pointer)
|
|
175
|
+
Leptris::XML::FFI.check_status(status)
|
|
152
176
|
self
|
|
153
177
|
end
|
|
154
178
|
|
|
@@ -161,12 +185,11 @@ class Leptris::XML::Document
|
|
|
161
185
|
return "" if @c_ptr.nil?
|
|
162
186
|
resolved_mode = mode || (exclusive ? Leptris::XML::FFI::C14N_MODE_EXCLUSIVE
|
|
163
187
|
: Leptris::XML::FFI::C14N_MODE_CANONICAL)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
str_ptr.read_string.tap { Leptris::XML::FFI.leptris_free_string(str_ptr) }
|
|
188
|
+
Leptris::XML::Serialization.canonicalize(
|
|
189
|
+
Leptris::XML::FFI.method(:leptris_c14n_canonicalize_ex), @c_ptr,
|
|
190
|
+
version: version, mode: resolved_mode,
|
|
191
|
+
inclusive_namespaces: inclusive_namespaces,
|
|
192
|
+
with_comments: with_comments)
|
|
170
193
|
end
|
|
171
194
|
alias_method :c14n, :canonicalize
|
|
172
195
|
|
|
@@ -175,6 +198,7 @@ class Leptris::XML::Document
|
|
|
175
198
|
@freed.state = :freed
|
|
176
199
|
Leptris::XML::FFI.leptris_document_free(@c_ptr) unless @c_ptr.nil?
|
|
177
200
|
@c_ptr = nil
|
|
201
|
+
@wrapper_cache.clear
|
|
178
202
|
end
|
|
179
203
|
|
|
180
204
|
def name; "document"; end
|
|
@@ -184,19 +208,5 @@ class Leptris::XML::Document
|
|
|
184
208
|
Leptris::XML::FFI.leptris_document_encoding(@c_ptr)
|
|
185
209
|
end
|
|
186
210
|
|
|
187
|
-
private
|
|
188
|
-
|
|
189
|
-
def build_serialize_options(indent:, no_decl:, encoding:)
|
|
190
|
-
opts = Leptris::XML::FFI::SerializeOptions.new
|
|
191
|
-
opts[:indent] = indent.to_i
|
|
192
|
-
opts[:xml_declaration] = no_decl ? 0 : 1
|
|
193
|
-
enc_ptr = nil
|
|
194
|
-
if encoding
|
|
195
|
-
enc_ptr = ::FFI::MemoryPointer.from_string(encoding.to_s)
|
|
196
|
-
opts[:encoding] = enc_ptr
|
|
197
|
-
end
|
|
198
|
-
[opts, enc_ptr]
|
|
199
|
-
end
|
|
200
|
-
|
|
201
211
|
include Leptris::XML::Searchable
|
|
202
212
|
end
|
data/lib/leptris/xml/element.rb
CHANGED
|
@@ -7,9 +7,8 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
7
7
|
alias_method :node_name, :name
|
|
8
8
|
|
|
9
9
|
def name=(new_name)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
10
|
+
Leptris::XML::FFI.check_status(
|
|
11
|
+
Leptris::XML::FFI.leptris_element_set_name(@c_ptr, new_name))
|
|
13
12
|
new_name
|
|
14
13
|
end
|
|
15
14
|
alias_method :node_name=, :name=
|
|
@@ -19,9 +18,8 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
19
18
|
end
|
|
20
19
|
|
|
21
20
|
def content=(new_content)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
21
|
+
Leptris::XML::FFI.check_status(
|
|
22
|
+
Leptris::XML::FFI.leptris_element_set_text(@c_ptr, new_content.to_s))
|
|
25
23
|
new_content
|
|
26
24
|
end
|
|
27
25
|
|
|
@@ -32,22 +30,20 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
32
30
|
alias_method :get_attribute, :[]
|
|
33
31
|
|
|
34
32
|
def []=(key, value)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
33
|
+
Leptris::XML::FFI.check_status(
|
|
34
|
+
Leptris::XML::FFI.leptris_element_set_attribute(@c_ptr, key.to_s, value.to_s))
|
|
38
35
|
value
|
|
39
36
|
end
|
|
40
37
|
alias_method :set_attribute, :[]=
|
|
41
38
|
|
|
42
39
|
def key?(name)
|
|
43
|
-
|
|
40
|
+
Leptris::XML::FFI.leptris_element_has_attribute(@c_ptr, name.to_s) != 0
|
|
44
41
|
end
|
|
45
42
|
alias_method :has_attribute?, :key?
|
|
46
43
|
|
|
47
44
|
def remove_attribute(name)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
45
|
+
Leptris::XML::FFI.check_status(
|
|
46
|
+
Leptris::XML::FFI.leptris_element_remove_attribute(@c_ptr, name.to_s))
|
|
51
47
|
self
|
|
52
48
|
end
|
|
53
49
|
alias_method :delete, :remove_attribute
|
|
@@ -82,45 +78,34 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
82
78
|
end
|
|
83
79
|
end
|
|
84
80
|
|
|
85
|
-
def add_child(node)
|
|
86
|
-
status = Leptris::XML::FFI.leptris_element_append_child(@c_ptr, node.c_ptr)
|
|
87
|
-
raise Leptris::XML::Error,
|
|
88
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
89
|
-
node
|
|
90
|
-
end
|
|
91
|
-
alias_method :<<, :add_child
|
|
92
|
-
|
|
93
81
|
def prepend_child(node)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
82
|
+
Leptris::XML::FFI.check_status(
|
|
83
|
+
Leptris::XML::FFI.leptris_element_prepend_child(@c_ptr, node.c_ptr))
|
|
97
84
|
node
|
|
98
85
|
end
|
|
99
86
|
|
|
100
87
|
def add_next_sibling(node)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
88
|
+
Leptris::XML::FFI.check_status(
|
|
89
|
+
Leptris::XML::FFI.leptris_element_insert_after(@c_ptr, node.c_ptr))
|
|
104
90
|
node
|
|
105
91
|
end
|
|
106
92
|
|
|
107
93
|
def add_previous_sibling(node)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
94
|
+
Leptris::XML::FFI.check_status(
|
|
95
|
+
Leptris::XML::FFI.leptris_element_insert_before(@c_ptr, node.c_ptr))
|
|
111
96
|
node
|
|
112
97
|
end
|
|
113
98
|
|
|
114
99
|
def remove_child(node)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
100
|
+
Leptris::XML::FFI.check_status(
|
|
101
|
+
Leptris::XML::FFI.leptris_element_remove_child(@c_ptr, node.c_ptr))
|
|
118
102
|
node
|
|
119
103
|
end
|
|
120
104
|
|
|
121
105
|
def children=(node_or_nodes)
|
|
122
106
|
# Remove existing children, then attach the new ones in source order.
|
|
123
|
-
Leptris::XML::FFI.
|
|
107
|
+
Leptris::XML::FFI.check_status(
|
|
108
|
+
Leptris::XML::FFI.leptris_element_remove_children(@c_ptr))
|
|
124
109
|
Array(node_or_nodes).each { |n| add_child(n) }
|
|
125
110
|
end
|
|
126
111
|
|
|
@@ -169,24 +154,22 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
169
154
|
def dup
|
|
170
155
|
copy_ptr = Leptris::XML::FFI.leptris_element_copy(@c_ptr, @document.c_ptr)
|
|
171
156
|
raise Leptris::XML::Error, "leptris_element_copy failed" if copy_ptr.null?
|
|
172
|
-
Leptris::XML::
|
|
157
|
+
Leptris::XML::Node.wrap(copy_ptr, @document)
|
|
173
158
|
end
|
|
174
159
|
alias_method :clone, :dup
|
|
175
160
|
|
|
176
161
|
def add_child(node_or_markup)
|
|
177
162
|
case node_or_markup
|
|
178
163
|
when Leptris::XML::Node
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
164
|
+
Leptris::XML::FFI.check_status(
|
|
165
|
+
Leptris::XML::FFI.leptris_element_append_child(@c_ptr, node_or_markup.c_ptr))
|
|
182
166
|
node_or_markup
|
|
183
167
|
when String
|
|
184
168
|
frag = Leptris::XML::DocumentFragment.parse(node_or_markup, @document)
|
|
185
169
|
added = []
|
|
186
170
|
frag.children.each do |n|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
171
|
+
Leptris::XML::FFI.check_status(
|
|
172
|
+
Leptris::XML::FFI.leptris_element_append_child(@c_ptr, n.c_ptr))
|
|
190
173
|
added << n
|
|
191
174
|
end
|
|
192
175
|
Leptris::XML::NodeSet.new(@document, added)
|
|
@@ -194,6 +177,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
194
177
|
raise ArgumentError, "add_child expects a Node or String, got #{node_or_markup.class}"
|
|
195
178
|
end
|
|
196
179
|
end
|
|
180
|
+
alias_method :<<, :add_child
|
|
197
181
|
|
|
198
182
|
def namespace
|
|
199
183
|
uri = Leptris::XML::FFI.leptris_element_namespace(@c_ptr)
|
|
@@ -224,17 +208,9 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
224
208
|
end
|
|
225
209
|
|
|
226
210
|
def to_xml(indent: 0, no_decl: false, encoding: nil)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
enc_ptr = nil
|
|
231
|
-
if encoding
|
|
232
|
-
enc_ptr = ::FFI::MemoryPointer.from_string(encoding.to_s)
|
|
233
|
-
opts[:encoding] = enc_ptr
|
|
234
|
-
end
|
|
235
|
-
str_ptr = Leptris::XML::FFI.leptris_element_serialize(@c_ptr, opts.pointer)
|
|
236
|
-
return "" if str_ptr.null?
|
|
237
|
-
str_ptr.read_string.tap { Leptris::XML::FFI.leptris_free_string(str_ptr) }
|
|
211
|
+
Leptris::XML::Serialization.to_xml(
|
|
212
|
+
Leptris::XML::FFI.method(:leptris_element_serialize), @c_ptr,
|
|
213
|
+
indent: indent, no_decl: no_decl, encoding: encoding)
|
|
238
214
|
end
|
|
239
215
|
|
|
240
216
|
def canonicalize(version = Leptris::XML::FFI::C14N_1_0,
|
|
@@ -244,35 +220,31 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
244
220
|
mode: nil)
|
|
245
221
|
resolved_mode = mode || (exclusive ? Leptris::XML::FFI::C14N_MODE_EXCLUSIVE
|
|
246
222
|
: Leptris::XML::FFI::C14N_MODE_CANONICAL)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
str_ptr.read_string.tap { Leptris::XML::FFI.leptris_free_string(str_ptr) }
|
|
223
|
+
Leptris::XML::Serialization.canonicalize(
|
|
224
|
+
Leptris::XML::FFI.method(:leptris_c14n_canonicalize_subtree_ex), @c_ptr,
|
|
225
|
+
version: version, mode: resolved_mode,
|
|
226
|
+
inclusive_namespaces: inclusive_namespaces,
|
|
227
|
+
with_comments: with_comments)
|
|
253
228
|
end
|
|
254
229
|
alias_method :c14n, :canonicalize
|
|
255
230
|
|
|
256
231
|
def add_namespace_definition(prefix, href)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
232
|
+
Leptris::XML::FFI.check_status(
|
|
233
|
+
Leptris::XML::FFI.leptris_element_add_namespace_definition(
|
|
234
|
+
@c_ptr, prefix.to_s, href.to_s))
|
|
261
235
|
Leptris::XML::Namespace.new(self, href.to_s, prefix: prefix.nil? ? nil : prefix.to_s)
|
|
262
236
|
end
|
|
263
237
|
alias_method :add_namespace, :add_namespace_definition
|
|
264
238
|
|
|
265
239
|
def default_namespace=(href)
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
240
|
+
Leptris::XML::FFI.check_status(
|
|
241
|
+
Leptris::XML::FFI.leptris_element_set_default_namespace(@c_ptr, href.to_s))
|
|
269
242
|
href
|
|
270
243
|
end
|
|
271
244
|
|
|
272
245
|
def remove_namespace_definition(prefix)
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
246
|
+
Leptris::XML::FFI.check_status(
|
|
247
|
+
Leptris::XML::FFI.leptris_element_remove_namespace_definition(@c_ptr, prefix.to_s))
|
|
276
248
|
self
|
|
277
249
|
end
|
|
278
250
|
end
|
data/lib/leptris/xml/ffi.rb
CHANGED
|
@@ -53,6 +53,8 @@ module Leptris
|
|
|
53
53
|
|
|
54
54
|
attach_function :leptris_parse_string,
|
|
55
55
|
[:string, :size_t, :pointer], :leptris_document
|
|
56
|
+
attach_function :leptris_parse_string_flags,
|
|
57
|
+
[:string, :size_t, :uint, :pointer], :leptris_document
|
|
56
58
|
attach_function :leptris_parse_string_inplace,
|
|
57
59
|
[:pointer, :size_t, :pointer], :leptris_document
|
|
58
60
|
attach_function :leptris_parse_string_with_encoding,
|
|
@@ -92,9 +94,7 @@ module Leptris
|
|
|
92
94
|
attach_function :leptris_xinclude_get_href, [:leptris_element], :string
|
|
93
95
|
attach_function :leptris_xinclude_get_parse, [:leptris_element], :string
|
|
94
96
|
attach_function :leptris_xinclude_get_xpointer, [:leptris_element], :string
|
|
95
|
-
|
|
96
|
-
# from the v0.4.4 dylib (likely same visibility bug as the serialize
|
|
97
|
-
# functions; see upstream issue #166). Re-add once #173 / v0.4.5 ships.
|
|
97
|
+
attach_function :leptris_xinclude_get_encoding, [:leptris_element], :string
|
|
98
98
|
|
|
99
99
|
attach_function :leptris_node_get_type,
|
|
100
100
|
[:leptris_node_ref], :int
|
|
@@ -184,9 +184,8 @@ module Leptris
|
|
|
184
184
|
[:leptris_element, :string, :float], :float
|
|
185
185
|
attach_function :leptris_element_attribute_bool,
|
|
186
186
|
[:leptris_element, :string, :int], :int
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
# Ruby-level Element#key? will emulate via attribute lookup.
|
|
187
|
+
attach_function :leptris_element_has_attribute,
|
|
188
|
+
[:leptris_element, :string], :int
|
|
190
189
|
attach_function :leptris_element_attribute_count,
|
|
191
190
|
[:leptris_element], :size_t
|
|
192
191
|
attach_function :leptris_element_attribute_name_at,
|
|
@@ -415,6 +414,24 @@ module Leptris
|
|
|
415
414
|
|
|
416
415
|
TRAVERSE_PRE_ORDER = 0
|
|
417
416
|
TRAVERSE_POST_ORDER = 1
|
|
417
|
+
|
|
418
|
+
LEPTRIS_PARSE_DEFAULT = 0
|
|
419
|
+
LEPTRIS_PARSE_DROP_WS_TEXT = 1
|
|
420
|
+
|
|
421
|
+
# Reads an libleptris-owned char* result and frees it as one unit,
|
|
422
|
+
# so a call site can neither leak nor double-free.
|
|
423
|
+
def self.read_owned_string(ptr)
|
|
424
|
+
return "" if ptr.nil? || ptr.null?
|
|
425
|
+
ptr.read_string.tap { leptris_free_string(ptr) }
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# Single status seam: turns a C status code into a Ruby error.
|
|
429
|
+
def self.check_status(status)
|
|
430
|
+
unless status == LEPTRIS_OK
|
|
431
|
+
raise Leptris::XML::Error, leptris_status_string(status)
|
|
432
|
+
end
|
|
433
|
+
status
|
|
434
|
+
end
|
|
418
435
|
end
|
|
419
436
|
end
|
|
420
437
|
end
|
data/lib/leptris/xml/node.rb
CHANGED
|
@@ -66,7 +66,7 @@ class Leptris::XML::Node
|
|
|
66
66
|
return @parent if @parent
|
|
67
67
|
ptr = Leptris::XML::FFI.leptris_node_parent(@c_ptr)
|
|
68
68
|
return nil if ptr.null?
|
|
69
|
-
Leptris::XML::
|
|
69
|
+
Leptris::XML::Node.wrap(ptr, @document)
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def line
|
|
@@ -141,9 +141,8 @@ class Leptris::XML::Node
|
|
|
141
141
|
end
|
|
142
142
|
|
|
143
143
|
def unlink
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
144
|
+
Leptris::XML::FFI.check_status(
|
|
145
|
+
Leptris::XML::FFI.leptris_node_unlink(@c_ptr))
|
|
147
146
|
@parent = nil
|
|
148
147
|
self
|
|
149
148
|
end
|
|
@@ -173,7 +172,7 @@ class Leptris::XML::Node
|
|
|
173
172
|
def path
|
|
174
173
|
str_ptr = Leptris::XML::FFI.leptris_node_get_xpath(@c_ptr)
|
|
175
174
|
return nil if str_ptr.null?
|
|
176
|
-
|
|
175
|
+
Leptris::XML::FFI.read_owned_string(str_ptr)
|
|
177
176
|
end
|
|
178
177
|
|
|
179
178
|
def css_path
|
|
@@ -189,7 +188,7 @@ class Leptris::XML::Node
|
|
|
189
188
|
raise Leptris::XML::Error, "dup is only supported for element nodes" if elem_ptr.null?
|
|
190
189
|
copy_ptr = Leptris::XML::FFI.leptris_element_copy(elem_ptr, @document.c_ptr)
|
|
191
190
|
raise Leptris::XML::Error, "leptris_element_copy failed" if copy_ptr.null?
|
|
192
|
-
Leptris::XML::
|
|
191
|
+
Leptris::XML::Node.wrap(copy_ptr, @document)
|
|
193
192
|
end
|
|
194
193
|
alias_method :clone, :dup
|
|
195
194
|
|
|
@@ -208,14 +207,5 @@ class Leptris::XML::Node
|
|
|
208
207
|
is_a?(Leptris::XML::Element) ? self : nil
|
|
209
208
|
end
|
|
210
209
|
|
|
211
|
-
private
|
|
212
|
-
|
|
213
|
-
# Walks the subtree in post-order DFS (matches Nokogiri's semantics).
|
|
214
|
-
#
|
|
215
|
-
# Specialized hot path for #traverse: skips the intermediate NodeSet
|
|
216
|
-
# allocation that Element#children would create, walking via raw FFI
|
|
217
|
-
# calls and wrapping nodes directly. Saves one Array + one NodeSet
|
|
218
|
-
# allocation per parent node.
|
|
219
|
-
#
|
|
220
210
|
include Leptris::XML::Searchable
|
|
221
211
|
end
|
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Parser flags, mirroring LeptrisParseFlags in libleptris's public
|
|
4
|
+
# headers. Pass an instance as `Leptris::XML.parse(xml, options:)`.
|
|
3
5
|
class Leptris::XML::ParseOptions
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
STRICT = 1 << 18
|
|
6
|
+
# Discard whitespace-only text nodes (runs between tags that contain
|
|
7
|
+
# nothing but spaces/tabs/newlines). Matches libxml2's
|
|
8
|
+
# XML_PARSE_NOBLANKS and Nokogiri's noblanks. Documents parsed this
|
|
9
|
+
# way do not round-trip pretty-printed formatting byte-for-byte.
|
|
10
|
+
NOBLANKS = Leptris::XML::FFI::LEPTRIS_PARSE_DROP_WS_TEXT
|
|
10
11
|
|
|
11
|
-
attr_reader :
|
|
12
|
+
attr_reader :flags
|
|
12
13
|
|
|
13
|
-
def initialize(
|
|
14
|
-
@
|
|
14
|
+
def initialize(flags = Leptris::XML::FFI::LEPTRIS_PARSE_DEFAULT)
|
|
15
|
+
@flags = flags.to_i
|
|
15
16
|
end
|
|
16
17
|
|
|
17
|
-
def
|
|
18
|
-
|
|
18
|
+
def self.noblanks
|
|
19
|
+
new(NOBLANKS)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def noblanks?
|
|
23
|
+
@flags & NOBLANKS != 0
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def |(other)
|
|
27
|
+
self.class.new(@flags | other.flags)
|
|
28
|
+
end
|
|
19
29
|
end
|
|
@@ -11,16 +11,14 @@ class Leptris::XML::ProcessingInstruction < Leptris::XML::Node
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def target=(new_target)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
14
|
+
Leptris::XML::FFI.check_status(
|
|
15
|
+
Leptris::XML::FFI.leptris_pi_node_set_target(@c_ptr, new_target.to_s))
|
|
17
16
|
new_target
|
|
18
17
|
end
|
|
19
18
|
|
|
20
19
|
def data=(new_data)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
20
|
+
Leptris::XML::FFI.check_status(
|
|
21
|
+
Leptris::XML::FFI.leptris_pi_node_set_data(@c_ptr, new_data.to_s))
|
|
24
22
|
new_data
|
|
25
23
|
end
|
|
26
24
|
end
|
|
@@ -128,21 +128,9 @@ class Leptris::XML::SAX::Parser
|
|
|
128
128
|
::FFI::Function.new(return_type, params, blocking: blocking, &block)
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
# The C `const char** attrs` is a NULL-terminated flat array of
|
|
132
|
-
# pairs
|
|
131
|
+
# The C `const char** attrs` is a NULL-terminated flat array of
|
|
132
|
+
# name/value pairs; CStringArray owns the format, we slice into pairs.
|
|
133
133
|
def walk_attr_array(attrs_ptr)
|
|
134
|
-
|
|
135
|
-
ptr_size = ::FFI.type_size(:pointer)
|
|
136
|
-
result = []
|
|
137
|
-
offset = 0
|
|
138
|
-
loop do
|
|
139
|
-
name_ptr = attrs_ptr.get_pointer(offset)
|
|
140
|
-
break if name_ptr.null?
|
|
141
|
-
value_ptr = attrs_ptr.get_pointer(offset + ptr_size)
|
|
142
|
-
break if value_ptr.null?
|
|
143
|
-
result << [name_ptr.read_string, value_ptr.read_string]
|
|
144
|
-
offset += 2 * ptr_size
|
|
145
|
-
end
|
|
146
|
-
result
|
|
134
|
+
Leptris::XML::CStringArray.to_ruby(attrs_ptr).each_slice(2).to_a
|
|
147
135
|
end
|
|
148
136
|
end
|
|
@@ -81,8 +81,7 @@ module Leptris::XML::Searchable
|
|
|
81
81
|
v
|
|
82
82
|
when Leptris::XML::FFI::XPATH_STRING
|
|
83
83
|
str_ptr = Leptris::XML::FFI.leptris_xpath_result_string(result_ptr)
|
|
84
|
-
v =
|
|
85
|
-
Leptris::XML::FFI.leptris_free_string(str_ptr) unless str_ptr.null?
|
|
84
|
+
v = Leptris::XML::FFI.read_owned_string(str_ptr)
|
|
86
85
|
Leptris::XML::FFI.leptris_xpath_result_free(result_ptr)
|
|
87
86
|
v
|
|
88
87
|
else
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Owns the SerializeOptions lifecycle and the owned-string read for
|
|
4
|
+
# document and subtree serialization/canonicalization. Document and
|
|
5
|
+
# Element expose one-line methods over this module — the options
|
|
6
|
+
# struct, the encoding anchor, the C call, and the read-and-free all
|
|
7
|
+
# live here.
|
|
8
|
+
module Leptris::XML::Serialization
|
|
9
|
+
# +ffi_function+ is a bound FFI function taking (c_ptr, opts_ptr):
|
|
10
|
+
# leptris_document_serialize or leptris_element_serialize.
|
|
11
|
+
def self.to_xml(ffi_function, c_ptr, indent: 0, no_decl: false, encoding: nil)
|
|
12
|
+
opts, _encoding_anchor = build_options(
|
|
13
|
+
indent: indent, no_decl: no_decl, encoding: encoding)
|
|
14
|
+
str_ptr = ffi_function.call(c_ptr, opts.pointer)
|
|
15
|
+
Leptris::XML::FFI.read_owned_string(str_ptr)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# +ffi_function+ is a bound FFI function taking
|
|
19
|
+
# (c_ptr, version, mode, ns_ptr, flags): leptris_c14n_canonicalize_ex
|
|
20
|
+
# or leptris_c14n_canonicalize_subtree_ex.
|
|
21
|
+
def self.canonicalize(ffi_function, c_ptr, version:, mode:,
|
|
22
|
+
inclusive_namespaces: nil, with_comments: false)
|
|
23
|
+
ns_ptr, _ns_anchor = Leptris::XML::CStringArray.to_c(inclusive_namespaces)
|
|
24
|
+
str_ptr = ffi_function.call(c_ptr, version, mode, ns_ptr, with_comments ? 1 : 0)
|
|
25
|
+
Leptris::XML::FFI.read_owned_string(str_ptr)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Builds a SerializeOptions struct. Returns [opts, encoding_anchor];
|
|
29
|
+
# the anchor must stay referenced while opts is in an FFI call.
|
|
30
|
+
def self.build_options(indent: 0, no_decl: false, encoding: nil)
|
|
31
|
+
opts = Leptris::XML::FFI::SerializeOptions.new
|
|
32
|
+
opts[:indent] = indent.to_i
|
|
33
|
+
opts[:xml_declaration] = no_decl ? 0 : 1
|
|
34
|
+
encoding_anchor = nil
|
|
35
|
+
if encoding
|
|
36
|
+
encoding_anchor = ::FFI::MemoryPointer.from_string(encoding.to_s)
|
|
37
|
+
opts[:encoding] = encoding_anchor
|
|
38
|
+
end
|
|
39
|
+
[opts, encoding_anchor]
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/leptris/xml/text.rb
CHANGED
|
@@ -8,9 +8,8 @@ class Leptris::XML::Text < Leptris::XML::Node
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def content=(new_content)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Leptris::XML::FFI.leptris_status_string(status) unless status == Leptris::XML::FFI::LEPTRIS_OK
|
|
11
|
+
Leptris::XML::FFI.check_status(
|
|
12
|
+
Leptris::XML::FFI.leptris_text_node_set_content(@c_ptr, new_content.to_s))
|
|
14
13
|
new_content
|
|
15
14
|
end
|
|
16
15
|
end
|
data/lib/leptris/xml.rb
CHANGED
|
@@ -18,13 +18,14 @@ module Leptris
|
|
|
18
18
|
autoload :Searchable, "leptris/xml/searchable"
|
|
19
19
|
autoload :ParseOptions, "leptris/xml/parse_options"
|
|
20
20
|
autoload :CssToXPath, "leptris/xml/css_to_xpath"
|
|
21
|
+
autoload :CStringArray, "leptris/xml/c_string_array"
|
|
22
|
+
autoload :Serialization, "leptris/xml/serialization"
|
|
21
23
|
autoload :SAX, "leptris/xml/sax"
|
|
22
|
-
require "leptris/xml/c14n" # module-level helper, eager load
|
|
23
24
|
|
|
24
25
|
# Nokogiri-style top-level entry points (Nokogiri::XML(...) /
|
|
25
26
|
# Nokogiri::XML.parse). Delegates to Document.parse.
|
|
26
|
-
def self.parse(xml_or_io)
|
|
27
|
-
Document.parse(xml_or_io)
|
|
27
|
+
def self.parse(xml_or_io, options: nil)
|
|
28
|
+
Document.parse(xml_or_io, options: options)
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
def self.parse_file(path)
|
metadata
CHANGED
|
@@ -1,13 +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.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-08-22 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: ffi
|
|
@@ -112,6 +113,7 @@ files:
|
|
|
112
113
|
- lib/leptris/xml.rb
|
|
113
114
|
- lib/leptris/xml/attr.rb
|
|
114
115
|
- lib/leptris/xml/c14n.rb
|
|
116
|
+
- lib/leptris/xml/c_string_array.rb
|
|
115
117
|
- lib/leptris/xml/cdata.rb
|
|
116
118
|
- lib/leptris/xml/comment.rb
|
|
117
119
|
- lib/leptris/xml/css_to_xpath.rb
|
|
@@ -129,6 +131,7 @@ files:
|
|
|
129
131
|
- lib/leptris/xml/sax/document.rb
|
|
130
132
|
- lib/leptris/xml/sax/parser.rb
|
|
131
133
|
- lib/leptris/xml/searchable.rb
|
|
134
|
+
- lib/leptris/xml/serialization.rb
|
|
132
135
|
- lib/leptris/xml/text.rb
|
|
133
136
|
homepage: https://github.com/leptris/leptris-ruby
|
|
134
137
|
licenses:
|
|
@@ -137,6 +140,7 @@ metadata:
|
|
|
137
140
|
homepage_uri: https://github.com/leptris/leptris-ruby
|
|
138
141
|
source_code_uri: https://github.com/leptris/leptris-ruby
|
|
139
142
|
changelog_uri: https://github.com/leptris/leptris-ruby/blob/main/CHANGELOG.md
|
|
143
|
+
post_install_message:
|
|
140
144
|
rdoc_options: []
|
|
141
145
|
require_paths:
|
|
142
146
|
- lib
|
|
@@ -151,7 +155,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
151
155
|
- !ruby/object:Gem::Version
|
|
152
156
|
version: '0'
|
|
153
157
|
requirements: []
|
|
154
|
-
rubygems_version:
|
|
158
|
+
rubygems_version: 3.5.22
|
|
159
|
+
signing_key:
|
|
155
160
|
specification_version: 4
|
|
156
161
|
summary: Nokogiri-compatible Ruby binding for libleptris (XML 1.0, XPath 1.0, SAX)
|
|
157
162
|
test_files: []
|