Class: Leptris::XML::Node
- Inherits:
-
Object
- Object
- Leptris::XML::Node
show all
- Includes:
- Searchable
- Defined in:
- lib/leptris/xml/node.rb
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
Methods included from Searchable
#at, #at_css, #at_xpath, #css, #search, wrap_xpath_result, #xpath
Constructor Details
#initialize(c_ptr, document, parent: nil, node_type: nil) ⇒ Node
Returns a new instance of Node.
6
7
8
9
10
11
12
13
|
# File 'lib/leptris/xml/node.rb', line 6
def initialize(c_ptr, document, parent: nil, node_type: nil)
@c_ptr = c_ptr
@document = document
@parent = parent
@node_type = node_type
end
|
Instance Attribute Details
#c_ptr ⇒ Object
Returns the value of attribute c_ptr.
4
5
6
|
# File 'lib/leptris/xml/node.rb', line 4
def c_ptr
@c_ptr
end
|
#document ⇒ Object
Returns the value of attribute document.
4
5
6
|
# File 'lib/leptris/xml/node.rb', line 4
def document
@document
end
|
Class Method Details
.wrap(c_ptr, document, parent: nil) ⇒ Object
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
# File 'lib/leptris/xml/node.rb', line 15
def self.wrap(c_ptr, document, parent: nil)
if document && (cached = document.wrapper_cache[c_ptr.address])
return cached
end
node_type = Leptris::XML::FFI.leptris_node_get_type(c_ptr)
node =
case node_type
when Leptris::XML::FFI::NODE_ELEMENT
Leptris::XML::Element.new(c_ptr, document, parent: parent, node_type: node_type)
when Leptris::XML::FFI::NODE_TEXT
Leptris::XML::Text.new(c_ptr, document, parent: parent, node_type: node_type)
when Leptris::XML::FFI::NODE_COMMENT
Leptris::XML::Comment.new(c_ptr, document, parent: parent, node_type: node_type)
when Leptris::XML::FFI::NODE_CDATA
Leptris::XML::CDATA.new(c_ptr, document, parent: parent, node_type: node_type)
when Leptris::XML::FFI::NODE_PI
Leptris::XML::ProcessingInstruction.new(c_ptr, document, parent: parent, node_type: node_type)
else
new(c_ptr, document, parent: parent, node_type: node_type)
end
document.wrapper_cache[c_ptr.address] = node if document
node
end
|
Instance Method Details
#<=>(other) ⇒ Object
97
98
99
100
101
|
# File 'lib/leptris/xml/node.rb', line 97
def <=>(other)
return nil unless other.is_a?(Leptris::XML::Node)
return nil unless @document == other.document
Leptris::XML::FFI.leptris_node_compare(@c_ptr, other.c_ptr)
end
|
#==(other) ⇒ Object
222
223
224
225
|
# File 'lib/leptris/xml/node.rb', line 222
def ==(other)
return false unless other.is_a?(Leptris::XML::Node)
@c_ptr == other.c_ptr
end
|
#child ⇒ Object
103
104
105
106
107
|
# File 'lib/leptris/xml/node.rb', line 103
def child
ptr = Leptris::XML::FFI.leptris_node_first_child(@c_ptr)
return nil if ptr.null?
Leptris::XML::Node.wrap(ptr, @document, parent: as_element_or_self)
end
|
#children ⇒ Object
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
# File 'lib/leptris/xml/node.rb', line 109
def children
return @children if readonly_cached?(:@children)
nodes = []
ptr = Leptris::XML::FFI.leptris_node_first_child(@c_ptr)
until ptr.nil? || ptr.null?
nodes << Leptris::XML::Node.wrap(ptr, @document, parent: as_element_or_self)
ptr = Leptris::XML::FFI.leptris_node_next_sibling(ptr)
end
result = Leptris::XML::NodeSet.new(@document, nodes)
@children = result if @document&.readonly?
result
end
|
#content ⇒ Object
49
50
51
|
# File 'lib/leptris/xml/node.rb', line 49
def content
raise NotImplementedError, "#{self.class}#content not implemented"
end
|
#css_path ⇒ Object
205
206
207
208
209
210
211
|
# File 'lib/leptris/xml/node.rb', line 205
def css_path
return nil if path.nil?
path.split("/").filter_map do |part|
next nil if part.empty?
part.gsub(/\[(\d+)\]/, ':nth-of-type(\1)')
end.join(" > ")
end
|
#dup ⇒ Object
Also known as:
clone
213
214
215
216
217
218
219
|
# File 'lib/leptris/xml/node.rb', line 213
def dup
elem_ptr = Leptris::XML::FFI.leptris_node_as_element(@c_ptr)
raise Leptris::XML::Error, "dup is only supported for element nodes" if elem_ptr.null?
copy_ptr = Leptris::XML::FFI.leptris_element_copy(elem_ptr, @document.c_ptr)
raise Leptris::XML::Error, "leptris_element_copy failed" if copy_ptr.null?
Leptris::XML::Node.wrap(copy_ptr, @document)
end
|
#element_children ⇒ Object
Also known as:
elements
152
153
154
|
# File 'lib/leptris/xml/node.rb', line 152
def element_children
children.select(&:element?)
end
|
#ensure_writable! ⇒ Object
Raises ReadOnlyError when the owning document was marked readonly.
86
87
88
89
90
91
|
# File 'lib/leptris/xml/node.rb', line 86
def ensure_writable!
if @document&.readonly?
raise Leptris::XML::ReadOnlyError,
"document is readonly — mutation attempted on #{inspect}"
end
end
|
#first_element_child ⇒ Object
138
139
140
141
142
143
144
145
146
|
# File 'lib/leptris/xml/node.rb', line 138
def first_element_child
ptr = Leptris::XML::FFI.leptris_node_first_child(@c_ptr)
until ptr.nil? || ptr.null?
node = Leptris::XML::Node.wrap(ptr, @document, parent: as_element_or_self)
return node if node.element?
ptr = Leptris::XML::FFI.leptris_node_next_sibling(ptr)
end
nil
end
|
#inner_text ⇒ Object
60
61
62
|
# File 'lib/leptris/xml/node.rb', line 60
def inner_text
content
end
|
#inspect ⇒ Object
227
228
229
|
# File 'lib/leptris/xml/node.rb', line 227
def inspect
"#<#{self.class.name} ptr=#{c_ptr}>"
end
|
#last_element_child ⇒ Object
148
149
150
|
# File 'lib/leptris/xml/node.rb', line 148
def last_element_child
children.reverse_each.find(&:element?)
end
|
#line ⇒ Object
93
94
95
|
# File 'lib/leptris/xml/node.rb', line 93
def line
Leptris::XML::FFI.leptris_node_line(@c_ptr)
end
|
#name ⇒ Object
45
46
47
|
# File 'lib/leptris/xml/node.rb', line 45
def name
raise NotImplementedError, "#{self.class}#name not implemented"
end
|
#next_element ⇒ Object
157
158
159
160
161
|
# File 'lib/leptris/xml/node.rb', line 157
def next_element
sibling = next_sibling
sibling = sibling.next_sibling until sibling.nil? || sibling.element?
sibling
end
|
#next_sibling ⇒ Object
Also known as:
next
124
125
126
127
128
|
# File 'lib/leptris/xml/node.rb', line 124
def next_sibling
ptr = Leptris::XML::FFI.leptris_node_next_sibling(@c_ptr)
return nil if ptr.null?
Leptris::XML::Node.wrap(ptr, @document, parent: @parent)
end
|
#parent ⇒ Object
78
79
80
81
82
83
|
# File 'lib/leptris/xml/node.rb', line 78
def parent
return @parent if @parent
ptr = Leptris::XML::FFI.leptris_node_parent(@c_ptr)
return nil if ptr.null?
Leptris::XML::Node.wrap(ptr, @document)
end
|
#previous_element ⇒ Object
163
164
165
166
167
|
# File 'lib/leptris/xml/node.rb', line 163
def previous_element
sibling = previous_sibling
sibling = sibling.previous_sibling until sibling.nil? || sibling.element?
sibling
end
|
#previous_sibling ⇒ Object
Also known as:
previous
131
132
133
134
135
|
# File 'lib/leptris/xml/node.rb', line 131
def previous_sibling
ptr = Leptris::XML::FFI.leptris_node_previous_sibling(@c_ptr)
return nil if ptr.null?
Leptris::XML::Node.wrap(ptr, @document, parent: @parent)
end
|
#processing_instruction? ⇒ Boolean
Also known as:
pi?
73
74
75
|
# File 'lib/leptris/xml/node.rb', line 73
def processing_instruction?
type == Leptris::XML::FFI::NODE_PI
end
|
#text ⇒ Object
Dispatching defs, not alias_method: an alias snapshots this base
#content (the raise), so subclass overrides would never be seen
through the alias. A plain method resolves #content per-call.
56
57
58
|
# File 'lib/leptris/xml/node.rb', line 56
def text
content
end
|
#traverse ⇒ Object
Walks the subtree in post-order DFS (matches Nokogiri's semantics).
Specialized hot path: skips the intermediate NodeSet allocation that
Element#children would create, walking via raw FFI calls and wrapping
nodes directly. Saves one Array + one NodeSet allocation per parent
node. For a tree of N nodes that's ~N fewer allocations on a full
traversal.
Still pays ~2 FFI calls per visited node (first_child + next_sibling).
Beating Nokogiri on this benchmark needs C-side traverse with a
callback (libleptris #273); the per-node FFI cost is the floor.
189
190
191
192
193
194
195
196
197
|
# File 'lib/leptris/xml/node.rb', line 189
def traverse
return enum_for(:traverse) unless block_given?
callback = ::FFI::Function.new(:int, [:pointer, :pointer], blocking: true) do |node_ptr, _|
yield Leptris::XML::Node.wrap(node_ptr, @document)
0
end
Leptris::XML::FFI.leptris_node_traverse(
@c_ptr, Leptris::XML::FFI::TRAVERSE_POST_ORDER, callback, nil)
end
|
#type ⇒ Object
Also known as:
node_type
64
65
66
|
# File 'lib/leptris/xml/node.rb', line 64
def type
@node_type ||= Leptris::XML::FFI.leptris_node_get_type(@c_ptr)
end
|
#unlink ⇒ Object
Also known as:
remove
169
170
171
172
173
174
175
|
# File 'lib/leptris/xml/node.rb', line 169
def unlink
ensure_writable!
Leptris::XML::FFI.check_status(
Leptris::XML::FFI.leptris_node_unlink(@c_ptr))
@parent = nil
self
end
|