Class: Makiri::Node

Inherits:
Object
  • Object
show all
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.

Instance Method Summary collapse

Instance Method Details

#add_class(names) ⇒ self

Add each class in names (space-separated) that is not already present.

Returns:

  • (self)


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).

Returns:

  • (self)


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).

Returns:



93
94
95
# File 'lib/makiri/node.rb', line 93

def attribute(name)
  attributes[name.to_s]
end

#attribute?Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/makiri/node.rb', line 52

def attribute?
  is_a?(Attr)
end

#attributesHash{String => Makiri::Attr}

Attributes as a name => Attr Hash (empty for non-elements).

Returns:



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.

Returns:

  • (Boolean)

    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

#cdata?Boolean

Returns:

  • (Boolean)


47
48
49
# File 'lib/makiri/node.rb', line 47

def cdata?
  is_a?(CDATASection)
end

#classesArray<String>

Returns the element’s class names.

Returns:

  • (Array<String>)

    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

#comment?Boolean

Returns:

  • (Boolean)


42
43
44
# File 'lib/makiri/node.rb', line 42

def comment?
  is_a?(Comment)
end

#document?Boolean

Returns:

  • (Boolean)


57
58
59
# File 'lib/makiri/node.rb', line 57

def document?
  is_a?(Document)
end

#document_fragment?Boolean Also known as: fragment?

Returns:

  • (Boolean)


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.

Yield Parameters:

Returns:

  • (self, Enumerator)


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?

Returns:

  • (Boolean)


32
33
34
# File 'lib/makiri/node.rb', line 32

def element?
  is_a?(Element)
end

#inspectObject

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

#pathString

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.

Returns:

  • (String)


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

Returns:

  • (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.

Returns:

  • (self)


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

#rootMakiri::Element?

The root element of the owning document (e.g. <html>).

Returns:



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.

Returns:



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

#text?Boolean

Returns:

  • (Boolean)


37
38
39
# File 'lib/makiri/node.rb', line 37

def text?
  is_a?(Text)
end

#to_hHash{String => String}

Attributes as a plain name => value Hash (empty for non-elements).

Returns:

  • (Hash{String => String})


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).

Returns:

  • (self)


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