Class: Leptris::XML::Document
- Inherits:
-
Object
- Object
- Leptris::XML::Document
show all
- Includes:
- Searchable
- Defined in:
- lib/leptris/xml/document.rb
Defined Under Namespace
Classes: Freed
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
-
#add_pi(target, data = "") ⇒ Object
Append a document-level processing instruction.
-
#canonicalize(version = Leptris::XML::FFI::C14N_1_0, inclusive_namespaces = nil, with_comments: false, exclusive: false, mode: nil) ⇒ Object
(also: #c14n)
-
#create_cdata(content) ⇒ Object
-
#create_comment(content) ⇒ Object
-
#create_element(name) ⇒ Object
-
#create_processing_instruction(target, data = "") ⇒ Object
-
#create_text_node(content) ⇒ Object
-
#doctype ⇒ Object
(also: #internal_subset)
-
#document ⇒ Object
-
#dup ⇒ Object
(also: #clone)
-
#encoding ⇒ Object
-
#exslt ⇒ Object
Enable the first-party EXSLT-style extension pack on this document: str:/set:/math: prefixed functions (replace, tokenize, split, concat, padding; distinct, intersection, difference, leading, trailing; max, min, abs, sqrt, power) as native C handlers.
-
#fragment(markup) ⇒ Object
-
#free ⇒ Object
-
#freed? ⇒ Boolean
True once #free has run (or the GC finalizer fired) — borrowed handles check this before dereferencing their c_ptr.
-
#initialize(c_ptr = nil, freed = Freed.new(:alive)) ⇒ Document
constructor
A new instance of Document.
-
#last_error ⇒ Object
The most recent error recorded against this document, or nil.
-
#last_error_position ⇒ Object
The thread-global last-failure [line, column] (1-based), or nil when no error is recorded — the position companion to Document#last_error; populated by recover parses.
-
#name ⇒ Object
-
#processing_instructions ⇒ Object
Document-level processing instructions (not tree nodes): an array of [target, data] pairs in document order.
-
#readonly! ⇒ Object
Marks the document read-only: tree mutations raise Leptris::XML::ReadOnlyError, and read paths memoize aggressively (names, content, children, attributes) since they can never go stale.
-
#readonly? ⇒ Boolean
-
#root ⇒ Object
-
#root=(element) ⇒ Object
Attach element as the document's root element.
-
#save(path, **opts) ⇒ Object
-
#to_xml(indent: 0, no_decl: false, encoding: nil) ⇒ Object
(also: #to_s, #serialize)
Methods included from Searchable
#at, #at_css, #at_xpath, #css, #search, wrap_xpath_result, #xpath
Constructor Details
#initialize(c_ptr = nil, freed = Freed.new(:alive)) ⇒ Document
Returns a new instance of Document.
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
# File 'lib/leptris/xml/document.rb', line 17
def initialize(c_ptr = nil, freed = Freed.new(:alive))
@c_ptr = c_ptr
@freed = freed
@readonly = false
@wrapper_cache = {}
end
|
Instance Attribute Details
#c_ptr ⇒ Object
Returns the value of attribute c_ptr.
6
7
8
|
# File 'lib/leptris/xml/document.rb', line 6
def c_ptr
@c_ptr
end
|
#wrapper_cache ⇒ Object
Returns the value of attribute wrapper_cache.
6
7
8
|
# File 'lib/leptris/xml/document.rb', line 6
def wrapper_cache
@wrapper_cache
end
|
Class Method Details
.create ⇒ Object
Create an empty document (no root element) backed by its own memory
pool. Elements for the tree are created against it via
#create_element and friends, then attached with #root=.
92
93
94
95
96
97
|
# File 'lib/leptris/xml/document.rb', line 92
def self.create
raw = Leptris::XML::FFI.leptris_document_create
raise Leptris::XML::Error,
"leptris_document_create failed" if raw.null?
wrap(raw)
end
|
.parse(xml_or_io, options: nil, readonly: false, recover: false) ⇒ Object
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
# File 'lib/leptris/xml/document.rb', line 37
def self.parse(xml_or_io, options: nil, readonly: false, recover: false)
xml = xml_or_io.respond_to?(:read) ? xml_or_io.read : xml_or_io.to_s
if xml.empty?
raise Leptris::XML::ParseError, "empty input"
end
if options.nil?
options = Leptris::XML::ParseOptions.new(recover: recover)
elsif recover && !options.recover?
options = options | Leptris::XML::ParseOptions.new(recover: true)
elsif !options.is_a?(Leptris::XML::ParseOptions)
raise ArgumentError, "options must be a Leptris::XML::ParseOptions"
end
raw =
if options.struct_required?
options_struct = options.to_c_struct
Leptris::XML::FFI.leptris_parse_string_ex(
xml, xml.bytesize, options_struct.pointer, nil)
elsif options.flags.zero?
Leptris::XML::FFI.leptris_parse_string(xml, xml.bytesize, nil)
else
Leptris::XML::FFI.leptris_parse_string_flags(
xml, xml.bytesize, options.flags, nil)
end
if raw.null?
if options.recover?
raise Leptris::XML::Error,
"leptris_parse_string_ex returned NULL under recover"
end
raise Leptris::XML::ParseError,
"leptris_parse_string failed: " +
Leptris::XML::FFI.leptris_last_error.to_s
end
wrap(raw).tap { |doc| doc.readonly! if readonly }
end
|
.parse_file(path, readonly: false) ⇒ Object
79
80
81
82
83
84
85
86
87
|
# File 'lib/leptris/xml/document.rb', line 79
def self.parse_file(path, readonly: false)
raw = Leptris::XML::FFI.leptris_parse_file(path, nil)
if raw.null?
raise Leptris::XML::ParseError,
"leptris_parse_file failed: " +
Leptris::XML::FFI.leptris_last_error.to_s
end
wrap(raw).tap { |doc| doc.readonly! if readonly }
end
|
.wrap(raw_address) ⇒ Object
Convert a raw LeptrisDocument pointer into a Ruby Document with safe
GC lifetime management. The finalizer captures the raw address
integer (not the Document or Pointer object — those would prevent
GC) and shares a one-shot flag with the instance so explicit
#free and the GC finalizer can never both call
leptris_document_free on the same address.
105
106
107
108
109
110
111
112
|
# File 'lib/leptris/xml/document.rb', line 105
def self.wrap(raw_address)
addr = raw_address.is_a?(::FFI::Pointer) ? raw_address.address : raw_address
ptr = ::FFI::Pointer.new(addr)
freed = Freed.new(:alive)
doc = new(ptr, freed)
ObjectSpace.define_finalizer(doc, finalizer(addr, freed))
doc
end
|
Instance Method Details
#add_pi(target, data = "") ⇒ Object
Append a document-level processing instruction. Returns self.
263
264
265
266
267
268
|
# File 'lib/leptris/xml/document.rb', line 263
def add_pi(target, data = "")
witness = Leptris::XML::FFI.leptris_document_add_pi(
@c_ptr, target.to_s, data.to_s)
raise Leptris::XML::Error, "leptris_document_add_pi failed" if witness.null?
self
end
|
#canonicalize(version = Leptris::XML::FFI::C14N_1_0, inclusive_namespaces = nil, with_comments: false, exclusive: false, mode: nil) ⇒ Object
Also known as:
c14n
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
|
# File 'lib/leptris/xml/document.rb', line 211
def canonicalize(version = Leptris::XML::FFI::C14N_1_0,
inclusive_namespaces = nil,
with_comments: false,
exclusive: false,
mode: nil)
raise Leptris::XML::UseAfterFreeError if @freed.state == :freed
return "" if @c_ptr.nil?
resolved_mode = mode || (exclusive ? Leptris::XML::FFI::C14N_MODE_EXCLUSIVE
: Leptris::XML::FFI::C14N_MODE_CANONICAL)
Leptris::XML::Serialization.canonicalize(
Leptris::XML::FFI.method(:leptris_c14n_canonicalize_ex), @c_ptr,
version: version, mode: resolved_mode,
inclusive_namespaces: inclusive_namespaces,
with_comments: )
end
|
#create_cdata(content) ⇒ Object
160
161
162
163
164
|
# File 'lib/leptris/xml/document.rb', line 160
def create_cdata(content)
ptr = Leptris::XML::FFI.leptris_cdata_node_create(@c_ptr, content.to_s)
raise Leptris::XML::Error, "leptris_cdata_node_create failed" if ptr.null?
Leptris::XML::Node.wrap(ptr, self)
end
|
154
155
156
157
158
|
# File 'lib/leptris/xml/document.rb', line 154
def (content)
ptr = Leptris::XML::FFI.(@c_ptr, content.to_s)
raise Leptris::XML::Error, "leptris_comment_node_create failed" if ptr.null?
Leptris::XML::Node.wrap(ptr, self)
end
|
#create_element(name) ⇒ Object
142
143
144
145
146
|
# File 'lib/leptris/xml/document.rb', line 142
def create_element(name)
ptr = Leptris::XML::FFI.leptris_element_create(@c_ptr, name)
raise Leptris::XML::Error, "leptris_element_create failed" if ptr.null?
Leptris::XML::Node.wrap(ptr, self)
end
|
#create_processing_instruction(target, data = "") ⇒ Object
166
167
168
169
170
|
# File 'lib/leptris/xml/document.rb', line 166
def create_processing_instruction(target, data = "")
ptr = Leptris::XML::FFI.leptris_pi_node_create(@c_ptr, target.to_s, data.to_s)
raise Leptris::XML::Error, "leptris_pi_node_create failed" if ptr.null?
Leptris::XML::Node.wrap(ptr, self)
end
|
#create_text_node(content) ⇒ Object
148
149
150
151
152
|
# File 'lib/leptris/xml/document.rb', line 148
def create_text_node(content)
ptr = Leptris::XML::FFI.leptris_text_node_create(@c_ptr, content.to_s)
raise Leptris::XML::Error, "leptris_text_node_create failed" if ptr.null?
Leptris::XML::Node.wrap(ptr, self)
end
|
#doctype ⇒ Object
Also known as:
internal_subset
183
184
185
186
187
|
# File 'lib/leptris/xml/document.rb', line 183
def doctype
ptr = Leptris::XML::FFI.leptris_document_internal_subset(@c_ptr)
return nil if ptr.null?
Leptris::XML::DocType.new(ptr, self)
end
|
#document ⇒ Object
313
|
# File 'lib/leptris/xml/document.rb', line 313
def document; self; end
|
#dup ⇒ Object
Also known as:
clone
176
177
178
179
180
|
# File 'lib/leptris/xml/document.rb', line 176
def dup
raw = Leptris::XML::FFI.leptris_document_copy(@c_ptr)
raise Leptris::XML::Error, "leptris_document_copy failed" if raw.null?
self.class.wrap(raw)
end
|
#encoding ⇒ Object
314
315
316
317
|
# File 'lib/leptris/xml/document.rb', line 314
def encoding
return nil if @c_ptr.nil?
Leptris::XML::FFI.leptris_document_encoding(@c_ptr)
end
|
#exslt ⇒ Object
Enable the first-party EXSLT-style extension pack on this
document: str:/set:/math: prefixed functions (replace, tokenize,
split, concat, padding; distinct, intersection, difference,
leading, trailing; max, min, abs, sqrt, power) as native C
handlers. Returns self for chaining.
#fragment(markup) ⇒ Object
#free ⇒ Object
228
229
230
231
232
233
234
|
# File 'lib/leptris/xml/document.rb', line 228
def free
return if @freed.state == :freed
@freed.state = :freed
Leptris::XML::FFI.leptris_document_free(@c_ptr) unless @c_ptr.nil?
@c_ptr = nil
@wrapper_cache.clear
end
|
#freed? ⇒ Boolean
True once #free has run (or the GC finalizer fired) — borrowed
handles check this before dereferencing their c_ptr.
286
287
288
|
# File 'lib/leptris/xml/document.rb', line 286
def freed?
@freed.state == :freed || @c_ptr.nil?
end
|
#last_error ⇒ Object
The most recent error recorded against this document, or nil.
307
308
309
310
|
# File 'lib/leptris/xml/document.rb', line 307
def last_error
msg = Leptris::XML::FFI.leptris_document_last_error(@c_ptr)
msg.nil? || msg.empty? ? nil : msg
end
|
#last_error_position ⇒ Object
The thread-global last-failure [line, column] (1-based), or nil
when no error is recorded — the position companion to
Document#last_error; populated by recover parses.
293
294
295
296
297
298
299
300
301
302
303
304
|
# File 'lib/leptris/xml/document.rb', line 293
def last_error_position
line = ::FFI::MemoryPointer.new(:int)
column = ::FFI::MemoryPointer.new(:int)
begin
Leptris::XML::FFI.leptris_last_error_position(line, column)
line.read_int.zero? && column.read_int.zero? ? nil :
[line.read_int, column.read_int]
ensure
line.free
column.free
end
end
|
#name ⇒ Object
312
|
# File 'lib/leptris/xml/document.rb', line 312
def name; "document"; end
|
#processing_instructions ⇒ Object
Document-level processing instructions (not tree nodes):
an array of [target, data] pairs in document order.
249
250
251
252
253
254
255
256
257
258
259
260
|
# File 'lib/leptris/xml/document.rb', line 249
def processing_instructions
if readonly? && instance_variable_defined?(:@processing_instructions)
return @processing_instructions
end
count = Leptris::XML::FFI.leptris_document_pi_count(@c_ptr)
result = count.times.map do |i|
[Leptris::XML::FFI.leptris_document_pi_target(@c_ptr, i),
Leptris::XML::FFI.leptris_document_pi_data(@c_ptr, i)]
end
@processing_instructions = result if readonly?
result
end
|
#readonly! ⇒ Object
Marks the document read-only: tree mutations raise
Leptris::XML::ReadOnlyError, and read paths memoize aggressively
(names, content, children, attributes) since they can never go
stale. The C document is also frozen (advisory upstream). One-way.
274
275
276
277
278
|
# File 'lib/leptris/xml/document.rb', line 274
def readonly!
Leptris::XML::FFI.leptris_document_freeze(@c_ptr)
@readonly = true
self
end
|
#readonly? ⇒ Boolean
280
281
282
|
# File 'lib/leptris/xml/document.rb', line 280
def readonly?
@readonly == true
end
|
#root=(element) ⇒ Object
Attach element as the document's root element. The element must
have been created against this document and must not already have
a parent. Any previous root is left detached (still owned by the
document's pool until #free).
#save(path, **opts) ⇒ Object
200
201
202
203
204
205
206
207
208
209
|
# File 'lib/leptris/xml/document.rb', line 200
def save(path, **opts)
opts_struct, _encoding_anchor = Leptris::XML::Serialization.build_options(
indent: opts.fetch(:indent, 0),
no_decl: opts.fetch(:no_decl, false),
encoding: opts[:encoding])
status = Leptris::XML::FFI.leptris_document_save_file(
@c_ptr, path, opts_struct.pointer)
Leptris::XML::FFI.check_status(status)
self
end
|
#to_xml(indent: 0, no_decl: false, encoding: nil) ⇒ Object
Also known as:
to_s, serialize
190
191
192
193
194
195
196
|
# File 'lib/leptris/xml/document.rb', line 190
def to_xml(indent: 0, no_decl: false, encoding: nil)
raise Leptris::XML::UseAfterFreeError if @freed.state == :freed
return "" if @c_ptr.nil?
Leptris::XML::Serialization.to_xml(
Leptris::XML::FFI.method(:leptris_document_serialize_into), @c_ptr,
indent: indent, no_decl: no_decl, encoding: encoding)
end
|