Class: Makiri::Node
- Inherits:
-
Object
- Object
- Makiri::Node
- Includes:
- Comparable, Enumerable
- Defined in:
- lib/makiri/node.rb,
ext/makiri/makiri.c
Overview
Base class for every DOM node (element, attribute, text, comment, …). The bulk of the API lives in the C extension; this file defines the Ruby-only conveniences.
Direct Known Subclasses
Attr, CDATASection, Comment, Document, DocumentFragment, DocumentType, Element, HTML::Node, ProcessingInstruction, Text, XML::Node
Instance Method Summary collapse
-
#add_class(names) ⇒ self
Add each class in
names(space-separated) that is not already present. -
#append_class(names) ⇒ self
Append each class in
namesunconditionally (duplicates allowed). -
#at(path) ⇒ Object
First result of #search: the first node for a node-set, else the value.
-
#attribute(name) ⇒ Makiri::Attr?
The Attr node named
name, or nil (cf. #[], which returns the value). - #attribute? ⇒ Boolean
-
#attributes ⇒ Hash{String => Makiri::Attr}
Attributes as a name => Attr Hash (empty for non-elements).
-
#blank? ⇒ Boolean
True for a blank/whitespace-only text or CDATA node.
- #cdata? ⇒ Boolean
-
#classes ⇒ Array<String>
The element’s class names.
-
#clone(freeze: nil) ⇒ Object
Like #dup, always a deep copy, and honouring Ruby’s
freeze:keyword:truereturns a frozen copy,falsean unfrozen one, the default (nil) copies the receiver’s frozen state. - #comment? ⇒ Boolean
- #document? ⇒ Boolean
- #document_fragment? ⇒ Boolean (also: #fragment?)
-
#dup(level = 1) ⇒ Object
An independent copy of this node, detached from any parent and owned by the same document (like Nokogiri’s Node#dup).
-
#each {|child| ... } ⇒ self, Enumerator
Yield each child node in document order.
- #element? ⇒ Boolean (also: #elem?)
-
#inspect ⇒ Object
Inspect representation.
-
#path ⇒ String
An absolute XPath that locates this node, e.g.
- #processing_instruction? ⇒ Boolean
-
#remove_class(names = nil) ⇒ self
Remove each class in
names(or every class whennamesis nil); drops the ‘class` attribute entirely when none remain. -
#root ⇒ Makiri::Element?
The root element of the owning document (e.g. <html>).
-
#search(path) ⇒ Makiri::NodeSet, ...
Query with CSS or XPath, auto-detecting which from the string shape.
-
#set_attribute(name, value) ⇒ Object
Set an attribute (alias for #[]=).
- #text? ⇒ Boolean
-
#to_h ⇒ Hash{String => String}
Attributes as a plain name => value Hash (empty for non-elements).
-
#traverse(&block) ⇒ self
Yield this node and every descendant, depth-first, children before self (post-order, matching Nokogiri).
Instance Method Details
#add_class(names) ⇒ self
Add each class in names (space-separated) that is not already present.
106 107 108 109 110 111 |
# File 'lib/makiri/node.rb', line 106 def add_class(names) have = classes have.concat(names.to_s.split(/\s+/).reject { |c| c.empty? || have.include?(c) }) self["class"] = have.join(" ") self end |
#append_class(names) ⇒ self
Append each class in names unconditionally (duplicates allowed).
115 116 117 118 |
# File 'lib/makiri/node.rb', line 115 def append_class(names) self["class"] = (classes + names.to_s.split(/\s+/).reject(&:empty?)).join(" ") self end |
#at(path) ⇒ Object
First result of #search: the first node for a node-set, else the value.
169 170 171 172 |
# File 'lib/makiri/node.rb', line 169 def at(path) result = search(path) result.is_a?(NodeSet) ? result.first : result end |
#attribute(name) ⇒ Makiri::Attr?
The Attr node named name, or nil (cf. #[], which returns the value).
93 94 95 |
# File 'lib/makiri/node.rb', line 93 def attribute(name) attributes[name.to_s] end |
#attributes ⇒ Hash{String => Makiri::Attr}
Attributes as a name => Attr Hash (empty for non-elements).
150 151 152 |
# File 'lib/makiri/node.rb', line 150 def attributes attribute_nodes.each_with_object({}) { |attr, h| h[attr.name] = attr } end |
#blank? ⇒ Boolean
Returns true for a blank/whitespace-only text or CDATA node.
72 73 74 |
# File 'lib/makiri/node.rb', line 72 def blank? (text? || cdata?) && content.strip.empty? end |
#classes ⇒ Array<String>
Returns the element’s class names.
100 101 102 |
# File 'lib/makiri/node.rb', line 100 def classes self["class"].to_s.split(/\s+/).reject(&:empty?) end |
#clone(freeze: nil) ⇒ Object
Like #dup, always a deep copy, and honouring Ruby’s freeze: keyword: true returns a frozen copy, false an unfrozen one, the default (nil) copies the receiver’s frozen state. A frozen node is genuinely immutable - its mutators raise FrozenError (see Makiri’s mutation methods).
212 213 214 215 216 |
# File 'lib/makiri/node.rb', line 212 def clone(freeze: nil) copy = clone_node(true) copy.freeze if freeze || (freeze.nil? && frozen?) copy end |
#document? ⇒ Boolean
57 58 59 |
# File 'lib/makiri/node.rb', line 57 def document? is_a?(Document) end |
#document_fragment? ⇒ Boolean Also known as: fragment?
67 68 69 |
# File 'lib/makiri/node.rb', line 67 def document_fragment? is_a?(DocumentFragment) end |
#dup(level = 1) ⇒ Object
An independent copy of this node, detached from any parent and owned by the same document (like Nokogiri’s Node#dup). Deep by default; level 0 makes a shallow copy (matching Nokogiri’s level argument). The native allocator is undef’d to keep wrappers memory-safe, so #dup/#clone delegate to #clone_node rather than Ruby’s default allocate-and-copy (which would otherwise raise “allocator undefined”).
204 205 206 |
# File 'lib/makiri/node.rb', line 204 def dup(level = 1) clone_node(level != 0) end |
#each {|child| ... } ⇒ self, Enumerator
Yield each child node in document order. Iterates a snapshot of the children (taken when called), so the block may safely move or remove the current node. Returns an Enumerator when no block is given.
24 25 26 27 28 29 |
# File 'lib/makiri/node.rb', line 24 def each(&block) return enum_for(:each) { children.length } unless block_given? children.each(&block) self end |
#element? ⇒ Boolean Also known as: elem?
32 33 34 |
# File 'lib/makiri/node.rb', line 32 def element? is_a?(Element) end |
#inspect ⇒ Object
Inspect representation. Avoids dumping the whole subtree.
192 193 194 195 196 |
# File 'lib/makiri/node.rb', line 192 def inspect "#<#{self.class.name} name=#{name.inspect}>" rescue NoMethodError "#<#{self.class.name}>" end |
#path ⇒ String
An absolute XPath that locates this node, e.g. “/html/body/p”. Element/text/comment steps carry a 1-based position among same-kind siblings (omitted when unique); attributes use “@name”. Round-trips through #at_xpath.
179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/makiri/node.rb', line 179 def path return "/" if document? segments = [] node = self until node.nil? || node.document? segments.unshift(node.send(:path_segment)) node = node.parent end "/#{segments.join("/")}" end |
#processing_instruction? ⇒ Boolean
62 63 64 |
# File 'lib/makiri/node.rb', line 62 def processing_instruction? is_a?(ProcessingInstruction) end |
#remove_class(names = nil) ⇒ self
Remove each class in names (or every class when names is nil); drops the ‘class` attribute entirely when none remain.
123 124 125 126 127 128 129 130 131 |
# File 'lib/makiri/node.rb', line 123 def remove_class(names = nil) if names.nil? delete("class") else remaining = classes - names.to_s.split(/\s+/) remaining.empty? ? delete("class") : (self["class"] = remaining.join(" ")) end self end |
#root ⇒ Makiri::Element?
The root element of the owning document (e.g. <html>).
144 145 146 |
# File 'lib/makiri/node.rb', line 144 def root document.root end |
#search(path) ⇒ Makiri::NodeSet, ...
Query with CSS or XPath, auto-detecting which from the string shape. Strings that look like a location path (start with “/”, “./”, “..”, “.//”, “(”, “@” or contain “::”) are treated as XPath; everything else as CSS.
164 165 166 |
# File 'lib/makiri/node.rb', line 164 def search(path) xpath?(path) ? xpath(path) : css(path) end |
#set_attribute(name, value) ⇒ Object
Set an attribute (alias for #[]=). @return [String]
87 88 89 |
# File 'lib/makiri/node.rb', line 87 def set_attribute(name, value) self[name] = value end |
#to_h ⇒ Hash{String => String}
Attributes as a plain name => value Hash (empty for non-elements).
156 157 158 |
# File 'lib/makiri/node.rb', line 156 def to_h attribute_nodes.each_with_object({}) { |attr, h| h[attr.name] = attr.value } end |
#traverse(&block) ⇒ self
Yield this node and every descendant, depth-first, children before self (post-order, matching Nokogiri).
136 137 138 139 140 |
# File 'lib/makiri/node.rb', line 136 def traverse(&block) children.each { |child| child.traverse(&block) } block.call(self) self end |