leptris 1.9.197.2-arm-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 +7 -0
- data/CHANGELOG.md +3890 -0
- data/CLAUDE.md +115 -0
- data/CONTEXT.md +39 -0
- data/LICENSE.md +33 -0
- data/README.adoc +755 -0
- data/Rakefile +407 -0
- data/TODO.perf/01-ext-accelerated-default-reads.md +28 -0
- data/TODO.perf/02-auto-enable-native-layer.md +19 -0
- data/TODO.perf/03-bulk-xpath-result-materialization.md +18 -0
- data/TODO.perf/04-ext-bound-serialization.md +15 -0
- data/TODO.perf/05-native-surface-completion.md +19 -0
- data/TODO.perf/06-perf-battery-native-legs.md +16 -0
- data/TODO.perf/07-native-read-floor.md +31 -0
- data/TODO.perf/08-version-coherent-native-mutations.md +24 -0
- data/TODO.perf/09-adoption-lift-fast-path.md +38 -0
- data/TODO.perf/10-bulk-attribute-materialization.md +20 -0
- data/TODO.perf/11-moxml-gap-battery.md +18 -0
- data/TODO.perf/12-document-lifetime-in-c.md +47 -0
- data/TODO.perf/13-structural-memos.md +49 -0
- data/TODO.perf/14-insert-family-c-bound.md +31 -0
- data/TODO.perf/15-compiled-expression-cache.md +35 -0
- data/TODO.perf/16-at-xpath-single-result-seam.md +38 -0
- data/TODO.perf/17-precomputed-fast-path-flags.md +35 -0
- data/TODO.perf/18-inner-html-one-c-pass.md +32 -0
- data/TODO.perf/19-lazy-node-pointer.md +39 -0
- data/TODO.perf/20-css-translation-cache.md +24 -0
- data/TODO.perf/21-key-memo-consult.md +20 -0
- data/TODO.perf/22-eager-nodeset-materialization.md +30 -0
- data/TODO.perf/23-cbound-value-mutations.md +27 -0
- data/TODO.perf/24-immutable-read-lanes.md +22 -0
- data/TODO.perf/25-scope-owned-bulk-path.md +39 -0
- data/TODO.perf/26-post-mutation-memo-seeding.md +26 -0
- data/TODO.perf/27-c-yield-traversal.md +38 -0
- data/TODO.perf/28-address-fills.md +25 -0
- data/TODO.perf/29-ns-xpath-compiled.md +23 -0
- data/TODO.perf/30-copy-and-element-child-faces.md +31 -0
- data/TODO.perf/31-battery-rounds-4-8.md +23 -0
- data/TODO.perf/32-clean-host-floor-table.md +29 -0
- data/TODO.perf/33-cbound-root-set.md +29 -0
- data/TODO.perf/34-fragment-fast-lane.md +41 -0
- data/TODO.perf/35-parse-default-c-face.md +24 -0
- data/TODO.perf/36-raise-in-c-serializer-encoding.md +34 -0
- data/TODO.perf/37-document-lazy-pointer.md +28 -0
- data/TODO.perf/38-bulk-walk-and-bench-gate.md +23 -0
- data/TODO.restructure/01-constraint-compliance-audit.md +40 -0
- data/TODO.restructure/02-deep-copy-seam.md +21 -0
- data/TODO.restructure/03-evaluation-context-seam.md +18 -0
- data/TODO.restructure/04-spec-mece-restructure.md +20 -0
- data/TODO.restructure/05-memory-documentation.md +13 -0
- data/TODO.restructure/06-architecture-map-refresh.md +12 -0
- data/TODO.restructure/07-external-gates.md +16 -0
- data/TODO.restructure/08-new-code-audit-and-ext-memo.md +11 -0
- data/TODO.restructure/09-iteration-scope.md +29 -0
- data/TODO.restructure/10-cold-attr-read-diet.md +19 -0
- data/TODO.restructure/11-construction-factory-diet.md +16 -0
- data/TODO.restructure/12-resultattr-merge.md +12 -0
- data/TODO.restructure/13-spec-mece-followup.md +9 -0
- data/TODO.restructure/14-constraint-audit-refresh.md +15 -0
- data/TODO.restructure/15-readme-feature-documentation.md +10 -0
- data/TODO.restructure/16-claudemd-map-refresh.md +5 -0
- data/TODO.restructure/17-perf-drift-record.md +12 -0
- data/TODO.restructure/18-audit-spec-doubles-and-gem-contents.md +12 -0
- data/TODO.restructure/19-gitignore-hygiene.md +10 -0
- data/TODO.restructure/20-utf8proc-enablement.md +34 -0
- data/TODO.restructure/21-typeddata-variant-prototype.md +42 -0
- data/TODO.restructure/22-sax-drain-bulk-strip.md +28 -0
- data/docs/adr/0001-lockstep-mirror.md +17 -0
- data/docs/adr/0002-utf8-at-the-seam.md +19 -0
- data/docs/adr/0003-readonly-memoization-pattern.md +34 -0
- data/docs/adr/0004-lifetime-guard.md +21 -0
- data/docs/adr/0005-autoload-manifest-ordering.md +19 -0
- data/docs/adr/0006-ruby-variant-policy.md +25 -0
- data/ext/build_windows_native.rb +42 -0
- data/ext/leptris/native/extconf.rb +55 -0
- data/ext/leptris/native/native.c +2646 -0
- data/leptris.gemspec +43 -0
- data/lib/leptris/version.rb +5 -0
- data/lib/leptris/xml/attr.rb +76 -0
- data/lib/leptris/xml/c_string_array.rb +37 -0
- data/lib/leptris/xml/cdata.rb +27 -0
- data/lib/leptris/xml/comment.rb +27 -0
- data/lib/leptris/xml/css_to_xpath.rb +192 -0
- data/lib/leptris/xml/descriptor.rb +214 -0
- data/lib/leptris/xml/diff.rb +74 -0
- data/lib/leptris/xml/doc_type.rb +54 -0
- data/lib/leptris/xml/document.rb +716 -0
- data/lib/leptris/xml/document_fragment.rb +63 -0
- data/lib/leptris/xml/element.rb +771 -0
- data/lib/leptris/xml/entity_reference.rb +13 -0
- data/lib/leptris/xml/evaluation_context.rb +32 -0
- data/lib/leptris/xml/ffi.rb +1527 -0
- data/lib/leptris/xml/iteration_scope.rb +56 -0
- data/lib/leptris/xml/iterparse.rb +144 -0
- data/lib/leptris/xml/namespace.rb +43 -0
- data/lib/leptris/xml/native.so +0 -0
- data/lib/leptris/xml/native_layer.rb +92 -0
- data/lib/leptris/xml/node.rb +686 -0
- data/lib/leptris/xml/node_set.rb +212 -0
- data/lib/leptris/xml/parse_options.rb +97 -0
- data/lib/leptris/xml/plan_value.rb +121 -0
- data/lib/leptris/xml/processing_instruction.rb +90 -0
- data/lib/leptris/xml/pull.rb +212 -0
- data/lib/leptris/xml/relaxng.rb +126 -0
- data/lib/leptris/xml/result_attr.rb +39 -0
- data/lib/leptris/xml/result_text.rb +36 -0
- data/lib/leptris/xml/sax/document.rb +45 -0
- data/lib/leptris/xml/sax/dom_dispatch.rb +144 -0
- data/lib/leptris/xml/sax/parser.rb +326 -0
- data/lib/leptris/xml/sax/recorder.rb +374 -0
- data/lib/leptris/xml/sax.rb +14 -0
- data/lib/leptris/xml/schematron.rb +88 -0
- data/lib/leptris/xml/searchable.rb +361 -0
- data/lib/leptris/xml/serialization.rb +180 -0
- data/lib/leptris/xml/text.rb +32 -0
- data/lib/leptris/xml/xpath.rb +89 -0
- data/lib/leptris/xml/xquery.rb +77 -0
- data/lib/leptris/xml/xslt.rb +85 -0
- data/lib/leptris/xml.rb +108 -0
- data/lib/leptris.rb +53 -0
- data/lib/libleptris.so +0 -0
- data/lib/libutf8proc.so.3 +0 -0
- data/scripts/gem_smoke.rb +36 -0
- metadata +214 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ffi"
|
|
4
|
+
|
|
5
|
+
# Compiled XPath expression: parse once, evaluate many times against
|
|
6
|
+
# any document (libleptris v1.6.0). The C handle is released by GC.
|
|
7
|
+
#
|
|
8
|
+
# expr = Leptris::XML::XPath.compile("//item[@qty > 3]")
|
|
9
|
+
# expr.eval(doc_a).length
|
|
10
|
+
# expr.eval(doc_b, "p" => "urn:p").length
|
|
11
|
+
#
|
|
12
|
+
class Leptris::XML::XPath
|
|
13
|
+
class CompiledHandle < ::FFI::AutoPointer
|
|
14
|
+
def self.release(ptr)
|
|
15
|
+
Leptris::XML::FFI.leptris_xpath_compiled_free(ptr)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
attr_reader :expression
|
|
20
|
+
|
|
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)
|
|
25
|
+
raw = Leptris::XML::FFI.leptris_xpath_compile(expression.to_s)
|
|
26
|
+
if raw.null?
|
|
27
|
+
raise Leptris::XML::XPathError,
|
|
28
|
+
"invalid expression: #{Leptris::XML::FFI.leptris_last_error}"
|
|
29
|
+
end
|
|
30
|
+
version_code =
|
|
31
|
+
Leptris::XML::Searchable.xpath_version_code(version) if version
|
|
32
|
+
new(expression.to_s, CompiledHandle.new(raw), version_code)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Raw-pointer evaluation for Searchable's compiled cache
|
|
36
|
+
# (TODO.perf/15): the same entry the string path uses, against
|
|
37
|
+
# the compiled handle — no per-call parse/cache lookup.
|
|
38
|
+
def eval_ptrs(doc_ptr, context_ptr)
|
|
39
|
+
Leptris::XML::FFI.leptris_xpath_compiled_eval(
|
|
40
|
+
@handle, doc_ptr, context_ptr)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Namespace-bound evaluation against the compiled handle
|
|
44
|
+
# (TODO.perf/29): the handle is ns-independent — compile once,
|
|
45
|
+
# bind per call through the cached ns set.
|
|
46
|
+
def eval_ns_ptrs(doc_ptr, context_ptr, ns_set_ptr)
|
|
47
|
+
Leptris::XML::FFI.leptris_xpath_compiled_eval_ns(
|
|
48
|
+
@handle, doc_ptr, context_ptr, ns_set_ptr)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def initialize(expression, handle, version_code = nil)
|
|
52
|
+
@expression = expression
|
|
53
|
+
@handle = handle
|
|
54
|
+
@version_code = version_code
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Evaluates against +doc_or_element+. An optional trailing hash of
|
|
58
|
+
# namespace bindings ("prefix" => uri) routes the evaluation through
|
|
59
|
+
# the namespace-bound path, matching Searchable#xpath semantics.
|
|
60
|
+
# A compiled version pin routes through the engine's versioned
|
|
61
|
+
# entry instead of the compiled handle — there is no compiled
|
|
62
|
+
# versioned lane yet (engine gap), so the strict-1.0 surface is
|
|
63
|
+
# enforced per evaluation over the stored expression.
|
|
64
|
+
def eval(doc_or_element, ns = nil)
|
|
65
|
+
context = Leptris::XML::EvaluationContext.of(doc_or_element)
|
|
66
|
+
document = context.document
|
|
67
|
+
result_ptr =
|
|
68
|
+
if @version_code
|
|
69
|
+
raise ArgumentError,
|
|
70
|
+
"version: cannot be combined with namespace bindings " "(the versioned engine entry takes no ns set yet)" if ns && !ns.empty?
|
|
71
|
+
Leptris::XML::FFI.leptris_xpath_eval_versioned(
|
|
72
|
+
document.c_ptr, context.context_node_ptr,
|
|
73
|
+
@expression, @version_code, nil)
|
|
74
|
+
elsif ns && !ns.empty?
|
|
75
|
+
Leptris::XML::FFI.with_ns_set(ns) do |set|
|
|
76
|
+
Leptris::XML::FFI.leptris_xpath_compiled_eval_ns(
|
|
77
|
+
@handle, document.c_ptr, context.context_node_ptr, set)
|
|
78
|
+
end
|
|
79
|
+
else
|
|
80
|
+
Leptris::XML::FFI.leptris_xpath_compiled_eval(
|
|
81
|
+
@handle, document.c_ptr, context.context_node_ptr)
|
|
82
|
+
end
|
|
83
|
+
if result_ptr.null?
|
|
84
|
+
raise Leptris::XML::XPathError,
|
|
85
|
+
Leptris::XML::FFI.leptris_last_error.to_s
|
|
86
|
+
end
|
|
87
|
+
Leptris::XML::Searchable.wrap_xpath_result(document, result_ptr)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ffi"
|
|
4
|
+
|
|
5
|
+
# Compiled XQuery 1.0 query (libleptris 1.9.64+): parse once,
|
|
6
|
+
# evaluate many times against any document. The engine layer
|
|
7
|
+
# orchestrates the XPath engine — the prolog (declare variable /
|
|
8
|
+
# namespace / function local:*) binds into the evaluation context,
|
|
9
|
+
# FLWOR clauses run as a tuple stream, and results reuse the XPath
|
|
10
|
+
# result handle.
|
|
11
|
+
#
|
|
12
|
+
# query = Leptris::XML::XQuery.parse(<<~XQ)
|
|
13
|
+
# for $i in //item
|
|
14
|
+
# where $i/@qty > 3
|
|
15
|
+
# order by $i/@qty descending
|
|
16
|
+
# return <big>{$i/name/text()}</big>
|
|
17
|
+
# XQ
|
|
18
|
+
# query.eval(doc) # => Leptris::XML::NodeSet
|
|
19
|
+
#
|
|
20
|
+
class Leptris::XML::XQuery
|
|
21
|
+
class Handle < ::FFI::AutoPointer
|
|
22
|
+
def self.release(ptr)
|
|
23
|
+
Leptris::XML::FFI.leptris_xquery_free(ptr)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.parse(query)
|
|
28
|
+
query = query.to_s
|
|
29
|
+
raw = Leptris::XML::FFI.leptris_xquery_parse(query, query.bytesize)
|
|
30
|
+
if raw.null?
|
|
31
|
+
raise Leptris::XML::XPathError,
|
|
32
|
+
"query parse failed: #{Leptris::XML::FFI.leptris_last_error}"
|
|
33
|
+
end
|
|
34
|
+
new(query, Handle.new(raw))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
attr_reader :query
|
|
38
|
+
|
|
39
|
+
def initialize(query, handle)
|
|
40
|
+
@query = query
|
|
41
|
+
@handle = handle
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Evaluates against +doc_or_element+. Plain-expression bodies keep
|
|
45
|
+
# their XPath result type (NodeSet / true/false / Float / String);
|
|
46
|
+
# FLWOR results arrive as the sequence channel — read them through
|
|
47
|
+
# an aggregate until the engine materializes readable sequence
|
|
48
|
+
# items (same caveat as the XPath for-return subset).
|
|
49
|
+
# +params+ binds external variables (libleptris 1.9.133): each
|
|
50
|
+
# value is an XPath expression evaluated in an empty context
|
|
51
|
+
# (QT3 <param select> semantics) — {"x" => "21 * 2"} binds
|
|
52
|
+
# `declare variable $x external`. A binding overrides the
|
|
53
|
+
# declaration's default initializer; an external with neither
|
|
54
|
+
# fails evaluation. An empty params hash takes the same lane —
|
|
55
|
+
# the engine decides between default initializer and failure.
|
|
56
|
+
def eval(doc_or_element, params = nil)
|
|
57
|
+
context = Leptris::XML::EvaluationContext.of(doc_or_element)
|
|
58
|
+
result_ptr =
|
|
59
|
+
if params
|
|
60
|
+
names, name_anchors = Leptris::XML::CStringArray.to_c(
|
|
61
|
+
params.keys.map(&:to_s))
|
|
62
|
+
selects, select_anchors = Leptris::XML::CStringArray.to_c(
|
|
63
|
+
params.values.map(&:to_s))
|
|
64
|
+
Leptris::XML::FFI.leptris_xquery_eval_params(
|
|
65
|
+
@handle, context.document.c_ptr, context.context_node_ptr,
|
|
66
|
+
names, selects, params.size)
|
|
67
|
+
else
|
|
68
|
+
Leptris::XML::FFI.leptris_xquery_eval(
|
|
69
|
+
@handle, context.document.c_ptr, context.context_node_ptr)
|
|
70
|
+
end
|
|
71
|
+
if result_ptr.null?
|
|
72
|
+
raise Leptris::XML::XPathError,
|
|
73
|
+
Leptris::XML::FFI.leptris_last_error.to_s
|
|
74
|
+
end
|
|
75
|
+
Leptris::XML::Searchable.wrap_xpath_result(context.document, result_ptr)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ffi"
|
|
4
|
+
|
|
5
|
+
module Leptris::XML::XSLT
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def parse(stylesheet_xml)
|
|
9
|
+
Stylesheet.parse(stylesheet_xml)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def parse_file(path)
|
|
13
|
+
Stylesheet.parse_file(path)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# A compiled XSLT 1.0 stylesheet (libleptris 1.9.1): the stylesheet
|
|
17
|
+
# parses ONCE into an immutable instruction forest — patterns to
|
|
18
|
+
# XPath ASTs, selects through the compiled-XPath path — then applies
|
|
19
|
+
# to any number of documents with no re-parsing.
|
|
20
|
+
#
|
|
21
|
+
# style = Leptris::XML::XSLT.parse(<<~XSL)
|
|
22
|
+
# <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
|
|
23
|
+
# <xsl:template match="/"><out><xsl:value-of select="count(//item)"/></out></xsl:template>
|
|
24
|
+
# </xsl:stylesheet>
|
|
25
|
+
# XSL
|
|
26
|
+
# style.apply_to(doc) # => result Document (queryable tree)
|
|
27
|
+
# style.serialize(doc) # => String (fragments and top-level
|
|
28
|
+
# # text nodes preserved)
|
|
29
|
+
#
|
|
30
|
+
class Stylesheet
|
|
31
|
+
# GC-managed compiled handle.
|
|
32
|
+
class Handle < ::FFI::AutoPointer
|
|
33
|
+
def self.release(ptr)
|
|
34
|
+
Leptris::XML::FFI.leptris_xslt_free(ptr)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Compile a stylesheet. Raises XPathError on a stylesheet syntax
|
|
39
|
+
# error (detail on the thread-global last error).
|
|
40
|
+
def self.parse(stylesheet_xml)
|
|
41
|
+
raw = Leptris::XML::FFI.leptris_xslt_parse(
|
|
42
|
+
stylesheet_xml.to_s, stylesheet_xml.to_s.bytesize)
|
|
43
|
+
if raw.null?
|
|
44
|
+
raise Leptris::XML::XPathError,
|
|
45
|
+
"stylesheet parse failed: #{Leptris::XML::FFI.leptris_last_error}"
|
|
46
|
+
end
|
|
47
|
+
new(Handle.new(raw))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Compile from a file. Resolves §2.7 embedded stylesheets
|
|
51
|
+
# (xml-stylesheet PI with an href="#id" fragment) when the file's
|
|
52
|
+
# root is not itself a stylesheet.
|
|
53
|
+
def self.parse_file(path)
|
|
54
|
+
raw = Leptris::XML::FFI.leptris_xslt_parse_file(path.to_s)
|
|
55
|
+
if raw.null?
|
|
56
|
+
raise Leptris::XML::XPathError,
|
|
57
|
+
"stylesheet parse failed: #{Leptris::XML::FFI.leptris_last_error}"
|
|
58
|
+
end
|
|
59
|
+
new(Handle.new(raw))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def initialize(handle)
|
|
63
|
+
@handle = handle
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Apply to +document+ (not modified) and return the result tree
|
|
67
|
+
# as an owning Document — query it with xpath/css like any other.
|
|
68
|
+
def apply_to(document)
|
|
69
|
+
raw = Leptris::XML::FFI.leptris_xslt_apply(@handle, document.c_ptr)
|
|
70
|
+
if raw.null?
|
|
71
|
+
raise Leptris::XML::XPathError,
|
|
72
|
+
"transform failed: #{document.last_error || Leptris::XML::FFI.leptris_last_error}"
|
|
73
|
+
end
|
|
74
|
+
Leptris::XML::Document.wrap(raw)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Apply and serialize in one call — keeps top-level text nodes and
|
|
78
|
+
# result fragments that the tree API would flatten.
|
|
79
|
+
def serialize(document)
|
|
80
|
+
str_ptr = Leptris::XML::FFI.leptris_xslt_apply_string(
|
|
81
|
+
@handle, document.c_ptr)
|
|
82
|
+
Leptris::XML::FFI.read_owned_string(str_ptr)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
data/lib/leptris/xml.rb
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Leptris
|
|
4
|
+
module XML
|
|
5
|
+
autoload :FFI, "leptris/xml/ffi"
|
|
6
|
+
autoload :Node, "leptris/xml/node"
|
|
7
|
+
autoload :Element, "leptris/xml/element"
|
|
8
|
+
autoload :Text, "leptris/xml/text"
|
|
9
|
+
autoload :Comment, "leptris/xml/comment"
|
|
10
|
+
autoload :CDATA, "leptris/xml/cdata"
|
|
11
|
+
autoload :ProcessingInstruction, "leptris/xml/processing_instruction"
|
|
12
|
+
autoload :EntityReference, "leptris/xml/entity_reference"
|
|
13
|
+
autoload :Attr, "leptris/xml/attr"
|
|
14
|
+
autoload :Namespace, "leptris/xml/namespace"
|
|
15
|
+
autoload :Document, "leptris/xml/document"
|
|
16
|
+
autoload :DocumentFragment, "leptris/xml/document_fragment"
|
|
17
|
+
autoload :DocType, "leptris/xml/doc_type"
|
|
18
|
+
autoload :NodeSet, "leptris/xml/node_set"
|
|
19
|
+
autoload :Searchable, "leptris/xml/searchable"
|
|
20
|
+
autoload :ParseOptions, "leptris/xml/parse_options"
|
|
21
|
+
autoload :CssToXPath, "leptris/xml/css_to_xpath"
|
|
22
|
+
autoload :CStringArray, "leptris/xml/c_string_array"
|
|
23
|
+
autoload :Serialization, "leptris/xml/serialization"
|
|
24
|
+
autoload :SAX, "leptris/xml/sax"
|
|
25
|
+
autoload :XPath, "leptris/xml/xpath"
|
|
26
|
+
autoload :EvaluationContext, "leptris/xml/evaluation_context"
|
|
27
|
+
autoload :Schematron, "leptris/xml/schematron"
|
|
28
|
+
autoload :Diff, "leptris/xml/diff"
|
|
29
|
+
autoload :Descriptor, "leptris/xml/descriptor"
|
|
30
|
+
autoload :PlanValue, "leptris/xml/plan_value"
|
|
31
|
+
autoload :IterationScope, "leptris/xml/iteration_scope"
|
|
32
|
+
autoload :ResultText, "leptris/xml/result_text"
|
|
33
|
+
autoload :ResultAttr, "leptris/xml/result_attr"
|
|
34
|
+
autoload :XSLT, "leptris/xml/xslt"
|
|
35
|
+
autoload :XQuery, "leptris/xml/xquery"
|
|
36
|
+
autoload :RelaxNG, "leptris/xml/relaxng"
|
|
37
|
+
autoload :Pull, "leptris/xml/pull"
|
|
38
|
+
autoload :Iterparse, "leptris/xml/iterparse"
|
|
39
|
+
|
|
40
|
+
# Nokogiri-style top-level entry points (Nokogiri::XML(...) /
|
|
41
|
+
# Nokogiri::XML.parse). Delegates to Document.parse.
|
|
42
|
+
def self.parse(xml_or_io, options: nil, readonly: false, recover: false)
|
|
43
|
+
Document.parse(xml_or_io, options: options, readonly: readonly,
|
|
44
|
+
recover: recover)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Tree diff between two documents: equal subtrees prune in
|
|
48
|
+
# O(1) by content digest; diverging regions align by LCS.
|
|
49
|
+
# +ignore_ws+ treats whitespace-only text nodes as absent.
|
|
50
|
+
def self.diff(document_a, document_b, ignore_ws: false)
|
|
51
|
+
Leptris::XML::Diff.compute(document_a, document_b,
|
|
52
|
+
ignore_ws: ignore_ws)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Does +string+ contain a named entity reference that is not
|
|
56
|
+
# one of the five predefined XML entities (nor numeric)? One C
|
|
57
|
+
# pass (libleptris 1.9.62) — downstream adapters use it to skip
|
|
58
|
+
# whole-buffer rewrites when a document is already clean.
|
|
59
|
+
def self.buffer_has_nonstandard_entity?(string)
|
|
60
|
+
string = string.to_s
|
|
61
|
+
Leptris::XML::FFI.leptris_str_has_nonstandard_entity(
|
|
62
|
+
string, string.bytesize) != 0
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Tolerant HTML parse into a standard Document (libleptris
|
|
66
|
+
# 1.9.75): implied end tags, void elements, raw-text script and
|
|
67
|
+
# style, lowercased names, minimized/unquoted attributes, and
|
|
68
|
+
# the HTML named entities. html/head/body are synthesized; no
|
|
69
|
+
# implied tbody. Malformed input degrades to text rather than
|
|
70
|
+
# raising; only an entirely empty result is an error.
|
|
71
|
+
#
|
|
72
|
+
# MODES (libleptris 1.9.104, #659): :html4 (the default) pins
|
|
73
|
+
# the libxml2/Nokogiri compatibility shape — leading
|
|
74
|
+
# script/style stay in <body>, title/meta/link/base still lift
|
|
75
|
+
# — so byte-parity with Nokogiri::HTML holds. :whatwg selects
|
|
76
|
+
# the conformant engine: the full implied-<head> set
|
|
77
|
+
# (script/style/noscript/template/... lift) and foster
|
|
78
|
+
# parenting (1.9.105) — html5lib corpus 294/1555 vs Nokogiri's
|
|
79
|
+
# 372; the shapes genuinely differ by spec.
|
|
80
|
+
def self.parse_html(html, mode: :html4)
|
|
81
|
+
html = html.to_s
|
|
82
|
+
entry =
|
|
83
|
+
case mode
|
|
84
|
+
when :html4 then Leptris::XML::FFI.method(:leptris_parse_html4_string)
|
|
85
|
+
when :whatwg then Leptris::XML::FFI.method(:leptris_parse_html_string)
|
|
86
|
+
else
|
|
87
|
+
raise ArgumentError,
|
|
88
|
+
"mode must be :html4 or :whatwg, got #{mode.inspect}"
|
|
89
|
+
end
|
|
90
|
+
raw = entry.call(html, html.bytesize, nil)
|
|
91
|
+
if raw.null?
|
|
92
|
+
raise Leptris::XML::ParseError,
|
|
93
|
+
"HTML parse failed: #{Leptris::XML::FFI.leptris_last_error}"
|
|
94
|
+
end
|
|
95
|
+
Leptris::XML::Document.wrap(raw)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def self.parse_file(path)
|
|
99
|
+
Document.parse_file(path)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
class Error < StandardError; end
|
|
103
|
+
class ParseError < Error; end
|
|
104
|
+
class ReadOnlyError < Error; end
|
|
105
|
+
class XPathError < Error; end
|
|
106
|
+
class UseAfterFreeError < Error; end
|
|
107
|
+
end
|
|
108
|
+
end
|
data/lib/leptris.rb
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "leptris/version"
|
|
4
|
+
|
|
5
|
+
module Leptris
|
|
6
|
+
autoload :XML, "leptris/xml"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Eager library resolution (issue leptris-ruby#49): resolve the
|
|
10
|
+
# native library at require time instead of lazily inside
|
|
11
|
+
# Document.parse, so a missing library fails immediately.
|
|
12
|
+
#
|
|
13
|
+
# The require MUST come after the autoload registration above.
|
|
14
|
+
# ffi.rb opens `module Leptris; module XML`, and when that runs
|
|
15
|
+
# before `autoload :XML` exists it creates the constant first — the
|
|
16
|
+
# later autoload registration is then shadowed, xml.rb never loads,
|
|
17
|
+
# and the entire API is unreachable (Leptris::XML.constants ==
|
|
18
|
+
# [:FFI]; issue leptris-ruby#53). Requiring from here after the
|
|
19
|
+
# registration makes ffi.rb's module opening trigger the autoload,
|
|
20
|
+
# landing FFI inside the real manifest module; a downstream
|
|
21
|
+
# `require "leptris/xml"` is then a no-op.
|
|
22
|
+
begin
|
|
23
|
+
require "leptris/xml/ffi"
|
|
24
|
+
rescue LoadError => e
|
|
25
|
+
raise LoadError, <<~MSG
|
|
26
|
+
Leptris could not load the native libleptris library.
|
|
27
|
+
The ruby-platform gem is a fallback variant and ships no
|
|
28
|
+
binary; install the platform-specific variant for your system
|
|
29
|
+
(e.g. `gem install leptris --platform=arm64-darwin`), or set
|
|
30
|
+
LEPTRIS_LIB_PATH to a libleptris.{so,dylib,dll} file.
|
|
31
|
+
(Underlying error: #{e.message})
|
|
32
|
+
MSG
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# TODO.perf/02: auto-enable the native acceleration layer when the
|
|
36
|
+
# compiled bundle is present (platform gems). Quietly falls back
|
|
37
|
+
# to pure FFI when absent (ruby-variant on TruffleRuby/JRuby,
|
|
38
|
+
# custom builds). LEPTRIS_NO_NATIVE=1 forces the FFI path (the
|
|
39
|
+
# suite uses it to exercise both surfaces). The explicit
|
|
40
|
+
# `require "leptris/xml/native_layer"` remains the parallel-API
|
|
41
|
+
# entry for NativeNode builders.
|
|
42
|
+
unless ENV["LEPTRIS_NO_NATIVE"] == "1"
|
|
43
|
+
begin
|
|
44
|
+
require "leptris/xml/native_layer"
|
|
45
|
+
rescue LoadError => e
|
|
46
|
+
# #207: a silent fallback hides a platform-gem packaging bug
|
|
47
|
+
# behind a walk-speed regression. Say so, then continue on
|
|
48
|
+
# the FFI surface (the explicit require still raises its full
|
|
49
|
+
# error for callers that opt in).
|
|
50
|
+
warn "leptris: native acceleration unavailable " \
|
|
51
|
+
"(#{e.message.gsub(/\s+/, ' ').strip[0, 200]}); using the FFI surface"
|
|
52
|
+
end
|
|
53
|
+
end
|
data/lib/libleptris.so
ADDED
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Post-publish smoke (the teptris-ruby gem-smoke template): the
|
|
4
|
+
# INSTALLED gem from rubygems — not the checkout — must parse,
|
|
5
|
+
# query, mutate, serialize, and validate, with the native layer
|
|
6
|
+
# when the platform ships it. Also guards the #318 class: the
|
|
7
|
+
# NODE_* constants must exist in the shipped layer (a packaging
|
|
8
|
+
# skew surfaces as a NameError on first use, not at require).
|
|
9
|
+
require "leptris"
|
|
10
|
+
|
|
11
|
+
abort "SMOKE FAIL: leptris did not define Leptris::VERSION" unless defined?(Leptris::VERSION)
|
|
12
|
+
puts "leptris #{Leptris::VERSION} on #{RUBY_PLATFORM} (ruby #{RUBY_VERSION})"
|
|
13
|
+
|
|
14
|
+
doc = Leptris::XML.parse("<r id='1'><a x='2'>t1</a><a>t2</a></r>")
|
|
15
|
+
raise "parse: root" unless doc.root.name == "r"
|
|
16
|
+
raise "xpath: children" unless doc.root.xpath("a").size == 2
|
|
17
|
+
raise "attr read" unless doc.at_xpath("a")["x"] == "2"
|
|
18
|
+
|
|
19
|
+
doc.at_xpath("a").content = "z"
|
|
20
|
+
raise "mutate" unless doc.at_xpath("a").content == "z"
|
|
21
|
+
raise "serialize" unless doc.to_xml.include?('<a x="2">z</a>')
|
|
22
|
+
|
|
23
|
+
rng = Leptris::XML::RelaxNG.parse(
|
|
24
|
+
'<element name="r" xmlns="http://relaxng.org/ns/structure/1.0">' \
|
|
25
|
+
"<attribute name=\"id\"/></element>")
|
|
26
|
+
rng_doc = Leptris::XML::Document.parse(%q{<r id='1'/>})
|
|
27
|
+
raise "rng verdict" unless rng.valid?(rng_doc)
|
|
28
|
+
raise "rng errors shape" unless rng.validate_errors(rng_doc).is_a?(Array)
|
|
29
|
+
raise "rng report shape" unless rng.validate_report(rng_doc).is_a?(Array)
|
|
30
|
+
|
|
31
|
+
# The #318 class: node-kind constants must live in the shipped
|
|
32
|
+
# Ruby layer (plain constants cannot go missing with the lib).
|
|
33
|
+
raise "NODE constants" unless Leptris::XML::FFI::NODE_ELEMENT
|
|
34
|
+
|
|
35
|
+
layer = defined?(Leptris::XML::NATIVE_FAST) ? "native" : "ffi"
|
|
36
|
+
puts "SMOKE OK (#{layer} layer)"
|
metadata
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: leptris
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.9.197.2
|
|
5
|
+
platform: arm-linux
|
|
6
|
+
authors:
|
|
7
|
+
- Ribose Inc.
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-09-18 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: ffi
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.15'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.15'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rspec
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
description: |
|
|
56
|
+
Leptris is a Nokogiri-compatible Ruby binding for libleptris, a pure-C99 XML
|
|
57
|
+
1.0 parser with full XPath 1.0 and SAX support. The C DOM is the single
|
|
58
|
+
source of truth; Ruby objects are thin FFI wrappers (one Ruby method =
|
|
59
|
+
one FFI call).
|
|
60
|
+
email:
|
|
61
|
+
- open.source@ribose.com
|
|
62
|
+
executables: []
|
|
63
|
+
extensions: []
|
|
64
|
+
extra_rdoc_files: []
|
|
65
|
+
files:
|
|
66
|
+
- CHANGELOG.md
|
|
67
|
+
- CLAUDE.md
|
|
68
|
+
- CONTEXT.md
|
|
69
|
+
- LICENSE.md
|
|
70
|
+
- README.adoc
|
|
71
|
+
- Rakefile
|
|
72
|
+
- TODO.perf/01-ext-accelerated-default-reads.md
|
|
73
|
+
- TODO.perf/02-auto-enable-native-layer.md
|
|
74
|
+
- TODO.perf/03-bulk-xpath-result-materialization.md
|
|
75
|
+
- TODO.perf/04-ext-bound-serialization.md
|
|
76
|
+
- TODO.perf/05-native-surface-completion.md
|
|
77
|
+
- TODO.perf/06-perf-battery-native-legs.md
|
|
78
|
+
- TODO.perf/07-native-read-floor.md
|
|
79
|
+
- TODO.perf/08-version-coherent-native-mutations.md
|
|
80
|
+
- TODO.perf/09-adoption-lift-fast-path.md
|
|
81
|
+
- TODO.perf/10-bulk-attribute-materialization.md
|
|
82
|
+
- TODO.perf/11-moxml-gap-battery.md
|
|
83
|
+
- TODO.perf/12-document-lifetime-in-c.md
|
|
84
|
+
- TODO.perf/13-structural-memos.md
|
|
85
|
+
- TODO.perf/14-insert-family-c-bound.md
|
|
86
|
+
- TODO.perf/15-compiled-expression-cache.md
|
|
87
|
+
- TODO.perf/16-at-xpath-single-result-seam.md
|
|
88
|
+
- TODO.perf/17-precomputed-fast-path-flags.md
|
|
89
|
+
- TODO.perf/18-inner-html-one-c-pass.md
|
|
90
|
+
- TODO.perf/19-lazy-node-pointer.md
|
|
91
|
+
- TODO.perf/20-css-translation-cache.md
|
|
92
|
+
- TODO.perf/21-key-memo-consult.md
|
|
93
|
+
- TODO.perf/22-eager-nodeset-materialization.md
|
|
94
|
+
- TODO.perf/23-cbound-value-mutations.md
|
|
95
|
+
- TODO.perf/24-immutable-read-lanes.md
|
|
96
|
+
- TODO.perf/25-scope-owned-bulk-path.md
|
|
97
|
+
- TODO.perf/26-post-mutation-memo-seeding.md
|
|
98
|
+
- TODO.perf/27-c-yield-traversal.md
|
|
99
|
+
- TODO.perf/28-address-fills.md
|
|
100
|
+
- TODO.perf/29-ns-xpath-compiled.md
|
|
101
|
+
- TODO.perf/30-copy-and-element-child-faces.md
|
|
102
|
+
- TODO.perf/31-battery-rounds-4-8.md
|
|
103
|
+
- TODO.perf/32-clean-host-floor-table.md
|
|
104
|
+
- TODO.perf/33-cbound-root-set.md
|
|
105
|
+
- TODO.perf/34-fragment-fast-lane.md
|
|
106
|
+
- TODO.perf/35-parse-default-c-face.md
|
|
107
|
+
- TODO.perf/36-raise-in-c-serializer-encoding.md
|
|
108
|
+
- TODO.perf/37-document-lazy-pointer.md
|
|
109
|
+
- TODO.perf/38-bulk-walk-and-bench-gate.md
|
|
110
|
+
- TODO.restructure/01-constraint-compliance-audit.md
|
|
111
|
+
- TODO.restructure/02-deep-copy-seam.md
|
|
112
|
+
- TODO.restructure/03-evaluation-context-seam.md
|
|
113
|
+
- TODO.restructure/04-spec-mece-restructure.md
|
|
114
|
+
- TODO.restructure/05-memory-documentation.md
|
|
115
|
+
- TODO.restructure/06-architecture-map-refresh.md
|
|
116
|
+
- TODO.restructure/07-external-gates.md
|
|
117
|
+
- TODO.restructure/08-new-code-audit-and-ext-memo.md
|
|
118
|
+
- TODO.restructure/09-iteration-scope.md
|
|
119
|
+
- TODO.restructure/10-cold-attr-read-diet.md
|
|
120
|
+
- TODO.restructure/11-construction-factory-diet.md
|
|
121
|
+
- TODO.restructure/12-resultattr-merge.md
|
|
122
|
+
- TODO.restructure/13-spec-mece-followup.md
|
|
123
|
+
- TODO.restructure/14-constraint-audit-refresh.md
|
|
124
|
+
- TODO.restructure/15-readme-feature-documentation.md
|
|
125
|
+
- TODO.restructure/16-claudemd-map-refresh.md
|
|
126
|
+
- TODO.restructure/17-perf-drift-record.md
|
|
127
|
+
- TODO.restructure/18-audit-spec-doubles-and-gem-contents.md
|
|
128
|
+
- TODO.restructure/19-gitignore-hygiene.md
|
|
129
|
+
- TODO.restructure/20-utf8proc-enablement.md
|
|
130
|
+
- TODO.restructure/21-typeddata-variant-prototype.md
|
|
131
|
+
- TODO.restructure/22-sax-drain-bulk-strip.md
|
|
132
|
+
- docs/adr/0001-lockstep-mirror.md
|
|
133
|
+
- docs/adr/0002-utf8-at-the-seam.md
|
|
134
|
+
- docs/adr/0003-readonly-memoization-pattern.md
|
|
135
|
+
- docs/adr/0004-lifetime-guard.md
|
|
136
|
+
- docs/adr/0005-autoload-manifest-ordering.md
|
|
137
|
+
- docs/adr/0006-ruby-variant-policy.md
|
|
138
|
+
- ext/build_windows_native.rb
|
|
139
|
+
- ext/leptris/native/extconf.rb
|
|
140
|
+
- ext/leptris/native/native.c
|
|
141
|
+
- leptris.gemspec
|
|
142
|
+
- lib/leptris.rb
|
|
143
|
+
- lib/leptris/version.rb
|
|
144
|
+
- lib/leptris/xml.rb
|
|
145
|
+
- lib/leptris/xml/attr.rb
|
|
146
|
+
- lib/leptris/xml/c_string_array.rb
|
|
147
|
+
- lib/leptris/xml/cdata.rb
|
|
148
|
+
- lib/leptris/xml/comment.rb
|
|
149
|
+
- lib/leptris/xml/css_to_xpath.rb
|
|
150
|
+
- lib/leptris/xml/descriptor.rb
|
|
151
|
+
- lib/leptris/xml/diff.rb
|
|
152
|
+
- lib/leptris/xml/doc_type.rb
|
|
153
|
+
- lib/leptris/xml/document.rb
|
|
154
|
+
- lib/leptris/xml/document_fragment.rb
|
|
155
|
+
- lib/leptris/xml/element.rb
|
|
156
|
+
- lib/leptris/xml/entity_reference.rb
|
|
157
|
+
- lib/leptris/xml/evaluation_context.rb
|
|
158
|
+
- lib/leptris/xml/ffi.rb
|
|
159
|
+
- lib/leptris/xml/iteration_scope.rb
|
|
160
|
+
- lib/leptris/xml/iterparse.rb
|
|
161
|
+
- lib/leptris/xml/namespace.rb
|
|
162
|
+
- lib/leptris/xml/native.so
|
|
163
|
+
- lib/leptris/xml/native_layer.rb
|
|
164
|
+
- lib/leptris/xml/node.rb
|
|
165
|
+
- lib/leptris/xml/node_set.rb
|
|
166
|
+
- lib/leptris/xml/parse_options.rb
|
|
167
|
+
- lib/leptris/xml/plan_value.rb
|
|
168
|
+
- lib/leptris/xml/processing_instruction.rb
|
|
169
|
+
- lib/leptris/xml/pull.rb
|
|
170
|
+
- lib/leptris/xml/relaxng.rb
|
|
171
|
+
- lib/leptris/xml/result_attr.rb
|
|
172
|
+
- lib/leptris/xml/result_text.rb
|
|
173
|
+
- lib/leptris/xml/sax.rb
|
|
174
|
+
- lib/leptris/xml/sax/document.rb
|
|
175
|
+
- lib/leptris/xml/sax/dom_dispatch.rb
|
|
176
|
+
- lib/leptris/xml/sax/parser.rb
|
|
177
|
+
- lib/leptris/xml/sax/recorder.rb
|
|
178
|
+
- lib/leptris/xml/schematron.rb
|
|
179
|
+
- lib/leptris/xml/searchable.rb
|
|
180
|
+
- lib/leptris/xml/serialization.rb
|
|
181
|
+
- lib/leptris/xml/text.rb
|
|
182
|
+
- lib/leptris/xml/xpath.rb
|
|
183
|
+
- lib/leptris/xml/xquery.rb
|
|
184
|
+
- lib/leptris/xml/xslt.rb
|
|
185
|
+
- lib/libleptris.so
|
|
186
|
+
- lib/libutf8proc.so.3
|
|
187
|
+
- scripts/gem_smoke.rb
|
|
188
|
+
homepage: https://github.com/leptris/leptris-ruby
|
|
189
|
+
licenses:
|
|
190
|
+
- MIT
|
|
191
|
+
metadata:
|
|
192
|
+
homepage_uri: https://github.com/leptris/leptris-ruby
|
|
193
|
+
source_code_uri: https://github.com/leptris/leptris-ruby
|
|
194
|
+
changelog_uri: https://github.com/leptris/leptris-ruby/blob/main/CHANGELOG.md
|
|
195
|
+
post_install_message:
|
|
196
|
+
rdoc_options: []
|
|
197
|
+
require_paths:
|
|
198
|
+
- lib
|
|
199
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
200
|
+
requirements:
|
|
201
|
+
- - ">="
|
|
202
|
+
- !ruby/object:Gem::Version
|
|
203
|
+
version: 3.0.0
|
|
204
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
205
|
+
requirements:
|
|
206
|
+
- - ">="
|
|
207
|
+
- !ruby/object:Gem::Version
|
|
208
|
+
version: '0'
|
|
209
|
+
requirements: []
|
|
210
|
+
rubygems_version: 3.5.22
|
|
211
|
+
signing_key:
|
|
212
|
+
specification_version: 4
|
|
213
|
+
summary: Nokogiri-compatible Ruby binding for libleptris (XML 1.0, XPath 1.0, SAX)
|
|
214
|
+
test_files: []
|