Class: Leptris::XML::Iterparse
- Inherits:
-
Object
- Object
- Leptris::XML::Iterparse
- Defined in:
- lib/leptris/xml/iterparse.rb
Overview
Incremental tree iteration (libleptris v1.6.0): yields each completed TOP-LEVEL child element of the document root as it parses. The previous element's whole subtree is released when the next is produced — memory stays bounded by the largest subtree, not the document.
Leptris::XML::Iterparse.parse(huge_xml) do |element|
process(element) # subtree valid until the next iteration
end
v2 (libleptris 1.9.4, #586): an explicit yield mode — :top_level (v1: the root's completed children) or :full_document (every element in document-order completion — post-order, child before parent, subtree attached) — plus namespace resolution on the last yielded element and an error channel for truncated input:
it = Leptris::XML::Iterparse.parse(xml, mode: :full_document)
it.run do |el|
it.namespace_uri("p") # in scope on el, the last yielded
end
it.free
Leptris::XML::Iterparse.parse(xml) { |el| ... }.error
# => nil, or the parse failure (readable after the run: the
# block form frees the iterator on return, snapshotting it)
The yielded elements have no parent Document; #document returns nil and they must not outlive the iteration.
Constant Summary collapse
- MODES =
{ top_level: Leptris::XML::FFI::ITERPARSE_TOP_LEVEL, full_document: Leptris::XML::FFI::ITERPARSE_FULL_DOCUMENT, }.freeze
Class Method Summary collapse
-
.parse(xml_or_io, mode: :top_level, &block) ⇒ Object
NOTE: an IO argument is read fully into memory before the C iterator starts — the bounded-memory path is parse_file (C-side file streaming).
- .parse_file(path, mode: :top_level, &block) ⇒ Object
Instance Method Summary collapse
-
#error ⇒ Object
The parse error for malformed/truncated input, or nil when the document is well-formed and exhausted.
-
#free ⇒ Object
Releases the C iterator.
-
#initialize(handle) ⇒ Iterparse
constructor
A new instance of Iterparse.
-
#namespace_count ⇒ Object
Number of namespace bindings in the last yielded element's scope.
-
#namespace_uri(prefix) ⇒ Object
The URI bound to
prefix("" for the default namespace) in the last yielded element's in-scope namespace snapshot, or nil when unbound (v2, #586). -
#run ⇒ Object
Yields completed elements until the document is exhausted or the parse fails (see #error).
Constructor Details
#initialize(handle) ⇒ Iterparse
Returns a new instance of Iterparse.
76 77 78 79 |
# File 'lib/leptris/xml/iterparse.rb', line 76 def initialize(handle) raise Leptris::XML::ParseError, "leptris_iterparse_new failed" if handle.null? @handle = handle end |
Class Method Details
.parse(xml_or_io, mode: :top_level, &block) ⇒ Object
NOTE: an IO argument is read fully into memory before the C iterator starts — the bounded-memory path is parse_file (C-side file streaming). The C API takes one (xml, len) buffer.
43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/leptris/xml/iterparse.rb', line 43 def self.parse(xml_or_io, mode: :top_level, &block) xml = xml_or_io.respond_to?(:read) ? xml_or_io.read : xml_or_io.to_s iterator = new(Leptris::XML::FFI.leptris_iterparse_new_ex( xml, xml.bytesize, mode_code(mode))) return iterator unless block begin iterator.run(&block) ensure iterator.free end iterator end |
.parse_file(path, mode: :top_level, &block) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/leptris/xml/iterparse.rb', line 56 def self.parse_file(path, mode: :top_level, &block) iterator = new(Leptris::XML::FFI.leptris_iterparse_new_file_ex( path, mode_code(mode))) return iterator unless block begin iterator.run(&block) ensure iterator.free end iterator end |
Instance Method Details
#error ⇒ Object
The parse error for malformed/truncated input, or nil when the document is well-formed and exhausted. Yielded elements stop at the failure point; #free snapshots the message so this stays readable after the block form of .parse returns.
117 118 119 |
# File 'lib/leptris/xml/iterparse.rb', line 117 def error @handle ? live_error : @last_error end |
#free ⇒ Object
Releases the C iterator. Safe to call twice.
122 123 124 125 126 127 |
# File 'lib/leptris/xml/iterparse.rb', line 122 def free return if @handle.nil? @last_error = live_error Leptris::XML::FFI.leptris_iterparse_free(@handle) @handle = nil end |
#namespace_count ⇒ Object
Number of namespace bindings in the last yielded element's scope. Valid only while the iterator is live; 0 after #free.
108 109 110 111 |
# File 'lib/leptris/xml/iterparse.rb', line 108 def namespace_count return 0 if @handle.nil? Leptris::XML::FFI.leptris_iterparse_ns_count(@handle) end |
#namespace_uri(prefix) ⇒ Object
The URI bound to prefix ("" for the default namespace) in the
last yielded element's in-scope namespace snapshot, or nil when
unbound (v2, #586). Valid only while the iterator is live —
call it inside the #run block; nil after #free.
101 102 103 104 |
# File 'lib/leptris/xml/iterparse.rb', line 101 def namespace_uri(prefix) return nil if @handle.nil? Leptris::XML::FFI.leptris_iterparse_ns_uri(@handle, prefix.to_s) end |
#run ⇒ Object
Yields completed elements until the document is exhausted or the parse fails (see #error). Does not free the iterator — the block form of .parse/.parse_file does, or call #free explicitly.
84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/leptris/xml/iterparse.rb', line 84 def run return enum_for(:run) unless block_given? while @handle ptr = Leptris::XML::FFI.leptris_iterparse_next(@handle) break if ptr.null? # document: nil — the subtree belongs to the iterator's pool, # not a long-lived Document, so wrapper caching is skipped and # #document is nil on the yielded elements. yield Leptris::XML::Node.wrap(ptr, nil) end self end |