class Oga::XML::NodeSet

The NodeSet class contains a set of unique {Oga::XML::Node} instances that can be queried and modified. Optionally NodeSet instances can take ownership of a node (besides just containing it). This allows the nodes to query their previous and next elements.

There are two types of sets:

  1. Regular node sets

  2. Owned node sets

Both behave similar to Ruby's Array class. The difference between an owned and regular node set is that an owned set modifies nodes that are added or removed by certain operations. For example, when a node is added to an owned set the `node_set` attribute of said node points to the set it was just added to.

Owned node sets are used when building a DOM tree with {Oga::XML::Parser}. By taking ownership of nodes in a set Oga makes it possible to use these sets as following:

document = Oga::XML::Document.new
element  = Oga::XML::Element.new

document.children << element

element.node_set == document.children # => true

If ownership was not handled then you'd have to manually set the `element` variable's `node_set` attribute after pushing it into a set.

Attributes

owner[RW]

@return [Oga::XML::Node]

Public Class Methods

new(nodes = [], owner = nil) click to toggle source

@param [Array] nodes The nodes to add to the set. @param [Oga::XML::NodeSet] owner The owner of the set.

# File lib/oga/xml/node_set.rb, line 40
def initialize(nodes = [], owner = nil)
  @nodes    = nodes
  @owner    = owner
  @existing = {}

  @nodes.each_with_index do |node, index|
    mark_existing(node)

    take_ownership(node, index) if @owner
  end
end

Public Instance Methods

+(other) click to toggle source

Creates a new set based on the current and the specified set. The newly created set does not inherit ownership rules of the current set.

@param [Oga::XML::NodeSet] other @return [Oga::XML::NodeSet]

# File lib/oga/xml/node_set.rb, line 185
def +(other)
  self.class.new(to_a | other.to_a)
end
<<(node)
Alias for: push
==(other) click to toggle source

Returns `true` if the current node set and the one given in `other` are equal to each other.

@param [Oga::XML::NodeSet] other

# File lib/oga/xml/node_set.rb, line 193
def ==(other)
  other.is_a?(NodeSet) && other.equal_nodes?(@nodes)
end
[](index) click to toggle source

Returns the node for the given index.

@param [Fixnum] index @return [Oga::XML::Node]

# File lib/oga/xml/node_set.rb, line 169
def [](index)
  @nodes[index]
end
attr(name)
Alias for: attribute
attribute(name) click to toggle source

Returns the values of the given attribute.

@param [String|Symbol] name The name of the attribute. @return [Array]

# File lib/oga/xml/node_set.rb, line 256
def attribute(name)
  values = []

  @nodes.each do |node|
    if node.respond_to?(:attribute)
      values << node.attribute(name)
    end
  end

  values
end
Also aliased as: attr
concat(other) click to toggle source

Adds the nodes of the given node set to the current node set.

@param [Oga::XML::NodeSet] other

# File lib/oga/xml/node_set.rb, line 210
def concat(other)
  other.each { |node| push(node) }
end
count()
Alias for: length
delete(node) click to toggle source

Removes a node from the current set only.

# File lib/oga/xml/node_set.rb, line 240
def delete(node)
  removed = @nodes.delete(node)

  if removed
    unmark_existing(removed)

    remove_ownership(removed) if @owner
  end

  removed
end
each() { |node| ... } click to toggle source

Yields the supplied block for every node.

@yieldparam [Oga::XML::Node]

# File lib/oga/xml/node_set.rb, line 55
def each
  return to_enum(:each) unless block_given?

  @nodes.each { |node| yield node }
end
empty?() click to toggle source

Returns `true` if the set is empty.

@return [TrueClass|FalseClass]

# File lib/oga/xml/node_set.rb, line 71
def empty?
  @nodes.empty?
end
equal_nodes?(nodes) click to toggle source

Returns `true` if the nodes given in `nodes` are equal to those specified in the current `@nodes` variable. This method allows two NodeSet instances to compare each other without the need of exposing `@nodes` to the public.

@param [Array<Oga::XML::Node>] nodes

# File lib/oga/xml/node_set.rb, line 203
def equal_nodes?(nodes)
  @nodes == nodes
end
index(node) click to toggle source

Returns the index of the given node.

@param [Oga::XML::Node] node @return [Fixnum]

# File lib/oga/xml/node_set.rb, line 89
def index(node)
  @nodes.index(node)
end
insert(index, node) click to toggle source

Inserts a node into the set at the given index.

@param [Fixnum] index The index to insert the node at. @param [Oga::XML::Node] node

# File lib/oga/xml/node_set.rb, line 155
def insert(index, node)
  return if exists?(node)

  @nodes.insert(index, node)

  mark_existing(node)

  take_ownership(node, index) if @owner
end
inspect() click to toggle source

@return [String]

# File lib/oga/xml/node_set.rb, line 286
def inspect
  values = @nodes.map(&:inspect).join(', ')

  "NodeSet(#{values})"
end
last() click to toggle source

Returns the last node in the set.

@return [Oga::XML::Node]

# File lib/oga/xml/node_set.rb, line 64
def last
  @nodes[-1]
end
length() click to toggle source

Returns the amount of nodes in the set.

@return [Fixnum]

# File lib/oga/xml/node_set.rb, line 78
def length
  @nodes.length
end
Also aliased as: count, size
pop() click to toggle source

Pops a node from the end of the set.

@return [Oga::XML::Node]

# File lib/oga/xml/node_set.rb, line 139
def pop
  node = @nodes.pop

  if node
    unmark_existing(node)

    remove_ownership(node) if @owner
  end

  node
end
push(node) click to toggle source

Pushes the node at the end of the set.

@param [Oga::XML::Node] node

# File lib/oga/xml/node_set.rb, line 96
def push(node)
  return if exists?(node)

  @nodes << node

  mark_existing(node)

  take_ownership(node, length - 1) if @owner
end
Also aliased as: <<
remove() click to toggle source

Removes the current nodes from their owning set. The nodes are not removed from the current set.

This method is intended to remove nodes from an XML document/node.

# File lib/oga/xml/node_set.rb, line 218
def remove
  sets = []

  # First we gather all the sets to remove nodse from, then we remove the
  # actual nodes. This is done as you can not reliably remove elements
  # from an Array while iterating on that same Array.
  @nodes.each do |node|
    if node.node_set
      sets << node.node_set

      node.node_set = nil
      node.next     = nil
      node.previous = nil
    end
  end

  sets.each do |set|
    @nodes.each { |node| set.delete(node) }
  end
end
shift() click to toggle source

Shifts a node from the start of the set.

@return [Oga::XML::Node]

# File lib/oga/xml/node_set.rb, line 124
def shift
  node = @nodes.shift

  if node
    unmark_existing(node)

    remove_ownership(node) if @owner
  end

  node
end
size()
Alias for: length
text() click to toggle source

Returns the text of all nodes in the set, ignoring comment nodes.

@return [String]

# File lib/oga/xml/node_set.rb, line 273
def text
  text = ''

  @nodes.each do |node|
    if node.respond_to?(:text) and !node.is_a?(Comment)
      text << node.text
    end
  end

  text
end
to_a() click to toggle source

Converts the current set to an Array.

@return [Array]

# File lib/oga/xml/node_set.rb, line 176
def to_a
  @nodes
end
unshift(node) click to toggle source

Pushes the node at the start of the set.

@param [Oga::XML::Node] node

# File lib/oga/xml/node_set.rb, line 111
def unshift(node)
  return if exists?(node)

  @nodes.unshift(node)

  mark_existing(node)

  take_ownership(node, 0) if @owner
end

Private Instance Methods

exists?(node) click to toggle source

@param [Oga::XML::Node|Oga::XML::Attribute] node @return [TrueClass|FalseClass]

# File lib/oga/xml/node_set.rb, line 330
def exists?(node)
  @existing.key?(node)
end
mark_existing(node) click to toggle source

@param [Oga::XML::Node|Oga::XML::Attribute] node

# File lib/oga/xml/node_set.rb, line 335
def mark_existing(node)
  @existing[node] = true
end
remove_ownership(node) click to toggle source

Removes ownership of the node if it belongs to the current set.

@param [Oga::XML::Node] node

# File lib/oga/xml/node_set.rb, line 312
def remove_ownership(node)
  return unless node.node_set == self

  if previous_node = node.previous
    previous_node.next = node.next
  end

  if next_node = node.next
    next_node.previous = node.previous
  end

  node.node_set = nil
  node.previous = nil
  node.next     = nil
end
take_ownership(node, index) click to toggle source

Takes ownership of the given node. This only occurs when the current set has an owner.

@param [Oga::XML::Node] node @param [Fixnum] index

# File lib/oga/xml/node_set.rb, line 299
def take_ownership(node, index)
  node.node_set = self

  node.previous = index > 0 ? @nodes[index - 1] : nil
  node.next = index + 1 < @nodes.length ? @nodes[index + 1] : nil

  node.previous.next = node if node.previous
  node.next.previous = node if node.next
end
unmark_existing(node) click to toggle source

@param [Oga::XML::Node|Oga::XML::Attribute] node

# File lib/oga/xml/node_set.rb, line 340
def unmark_existing(node)
  @existing.delete(node)
end