123456789_123456789_123456789_123456789_123456789_

Class: Nokogiri::XML::NodeSet

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
self, Enumerable, Searchable
Inherits: Object
Defined in: lib/nokogiri/xml/node_set.rb,
ext/nokogiri/xml_node_set.c

Overview

A NodeSet is an Enumerable that contains a list of Node objects.

Typically a NodeSet is returned as a result of searching a Document via Searchable#css or Searchable#xpath.

Note that the #dup and #clone methods perform shallow copies; these methods do not copy the Nodes contained in the NodeSet (similar to how Array and other Enumerable classes work).

Constant Summary

Searchable - Included

LOOKS_LIKE_XPATH

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Searchable - Included

#%

Alias for Searchable#at.

#/
#>

Search this node’s immediate children using ::Nokogiri::CSS selector selector

#at

Search this object for paths, and return only the first result.

#at_css

Search this object for ::Nokogiri::CSS rules, and return only the first match.

#at_xpath

Search this node for XPath paths, and return only the first match.

#css

Search this object for ::Nokogiri::CSS rules.

#search

Search this object for paths.

#xpath

Search this node for XPath paths.

#css_internal, #css_rules_to_xpath, #xpath_impl, #xpath_internal, #xpath_query_from_css_rule, #extract_params

Constructor Details

.new(document, list = []) {|_self| ... } ⇒ NodeSet

Create a NodeSet with #document defaulting to list

Yields:

  • (_self)

Yield Parameters:

  • _self (NodeSet)

    the object that the method was called on

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 22

def initialize(document, list = [])
  @document = document
  document.decorate(self)
  list.each { |x| self << x }
  yield self if block_given?
end

Instance Attribute Details

#document (rw)

The Document this NodeSet is associated with

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 19

attr_accessor :document

#empty?Boolean (readonly)

Is this NodeSet empty?

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 47

def empty?
  length == 0
end

Instance Method Details

#%(*args)

Alias for #at.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 128

alias_method :%, :at

#&(node_set)

Set Intersection — Returns a new NodeSet containing nodes common to the two NodeSets.

[ GitHub ]

  
# File 'ext/nokogiri/xml_node_set.c', line 205

static VALUE
intersection(VALUE rb_self, VALUE rb_other)
{
  xmlNodeSetPtr c_self, c_other ;
  xmlNodeSetPtr intersection;

  if (!rb_obj_is_kind_of(rb_other, cNokogiriXmlNodeSet)) {
    rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet");
  }

  TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self);
  TypedData_Get_Struct(rb_other, xmlNodeSet, &xml_node_set_type, c_other);

  intersection = xmlXPathIntersection(c_self, c_other);
  return noko_xml_node_set_wrap(intersection, rb_iv_get(rb_self, "@document"));
}

#+(rb_other)

Alias for #|.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 433

alias_method :+, :|

#-(node_set)

Difference - returns a new NodeSet that is a copy of this NodeSet, removing each item that also appears in node_set

[ GitHub ]

  
# File 'ext/nokogiri/xml_node_set.c', line 277

static VALUE
minus(VALUE rb_self, VALUE rb_other)
{
  xmlNodeSetPtr c_self, c_other;
  xmlNodeSetPtr new;
  int j ;

  if (!rb_obj_is_kind_of(rb_other, cNokogiriXmlNodeSet)) {
    rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet");
  }

  TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self);
  TypedData_Get_Struct(rb_other, xmlNodeSet, &xml_node_set_type, c_other);

  new = xmlXPathNodeSetMerge(NULL, c_self);
  for (j = 0 ; j < c_other->nodeNr ; ++j) {
    xpath_node_set_del(new, c_other->nodeTab[j]);
  }

  return noko_xml_node_set_wrap(new, rb_iv_get(rb_self, "@document"));
}

#<<(rb_node)

Alias for #push.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 75

alias_method :<<, :push

#==(other)

Equality – Two NodeSets are equal if the contain the same number of elements and if each element is equal to the corresponding element in the other NodeSet

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 395

def ==(other)
  return false unless other.is_a?(Nokogiri::XML::NodeSet)
  return false unless length == other.length

  each_with_index do |node, i|
    return false unless node == other[i]
  end
  true
end

#[](index) ⇒ Node? #[](start, length) ⇒ NodeSet? #[](range) ⇒ NodeSet? #slice(index) ⇒ Node? #slice(start, length) ⇒ NodeSet? #slice(range) ⇒ NodeSet?
Also known as: #slice

Element reference - returns the node at #index, or returns a NodeSet containing nodes starting at start and continuing for #length elements, or returns a NodeSet containing nodes specified by range. Negative indices count backward from the end of the node_set (-1 is the last node). Returns nil if the #index (or start) are out of range.

[ GitHub ]

  
# File 'ext/nokogiri/xml_node_set.c', line 354

static VALUE
slice(int argc, VALUE *argv, VALUE rb_self)
{
  VALUE arg ;
  long beg, len ;
  xmlNodeSetPtr c_self;

  TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self);

  if (argc == 2) {
    beg = NUM2LONG(argv[0]);
    len = NUM2LONG(argv[1]);
    if (beg < 0) {
      beg += c_self->nodeNr ;
    }
    return subseq(rb_self, beg, len);
  }

  if (argc != 1) {
    rb_scan_args(argc, argv, "11", NULL, NULL);
  }
  arg = argv[0];

  if (FIXNUM_P(arg)) {
    return index_at(rb_self, FIX2LONG(arg));
  }

  /* if arg is Range */
  switch (rb_range_beg_len(arg, &beg, &len, (long)c_self->nodeNr, 0)) {
    case Qfalse:
      break;
    case Qnil:
      return Qnil;
    default:
      return subseq(rb_self, beg, len);
  }

  return index_at(rb_self, NUM2LONG(arg));
}

#add_class(name)

Add the class attribute name to all Node objects in the NodeSet.

See Node#add_class for more information.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 141

def add_class(name)
  each do |el|
    el.add_class(name)
  end
  self
end

#after(datum)

Insert datum after the last Node in this NodeSet

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 71

def after(datum)
  last.after(datum)
end

#append_class(name)

Append the class attribute name to all Node objects in the NodeSet.

See Node#append_class for more information.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 153

def append_class(name)
  each do |el|
    el.append_class(name)
  end
  self
end

#search(*paths, [namespace-bindings, xpath-variable-bindings, custom-handler-class]) Also known as: #%

Search this object for paths, and return only the first result. paths must be one or more XPath or ::Nokogiri::CSS queries.

See Searchable#search for more information.

Or, if passed an integer, index into the NodeSet:

node_set.at(3) # same as node_set[3]
[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 121

def at(*args)
  if args.length == 1 && args.first.is_a?(Numeric)
    return self[args.first]
  end

  super(*args)
end

#attr(key, value = nil, &block) Also known as: #set, #attribute

Set attributes on each Node in the NodeSet, or get an attribute from the first Node in the NodeSet.

To get an attribute from the first Node in a NodeSet:

node_set.attr("href") # => "https://www.nokogiri.org"

Note that an empty NodeSet will return nil when #attr is called as a getter.

To set an attribute on each node, key can either be an attribute name, or a Hash of attribute names and values. When called as a setter, #attr returns the NodeSet.

If key is an attribute name, then either value or block must be passed.

If key is a Hash then attributes will be set for each key/value pair:

node_set.attr("href" => "https://www.nokogiri.org", "class" => "member")

If value is passed, it will be used as the attribute value for all nodes:

node_set.attr("href", "https://www.nokogiri.org")

If block is passed, it will be called on each Node object in the NodeSet and the return value used as the attribute value for that node:

node_set.attr("class") { |node| node.name }
[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 205

def attr(key, value = nil, &block)
  unless key.is_a?(Hash) || (key && (value || block))
    return first&.attribute(key)
  end

  hash = key.is_a?(Hash) ? key : { key => value }

  hash.each do |k, v|
    each do |node|
      node[k] = v || yield(node)
    end
  end

  self
end

#attribute(key, value = nil, &block)

Alias for #attr.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 221

alias_method :attribute, :attr

#before(datum)

Insert datum before the first Node in this NodeSet

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 65

def before(datum)
  first.before(datum)
end

#children

Returns a new NodeSet containing all the children of all the nodes in the NodeSet

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 408

def children
  node_set = NodeSet.new(document)
  each do |node|
    node.children.each { |n| node_set.push(n) }
  end
  node_set
end

#css(*rules, [namespace-bindings, custom-pseudo-class])

Search this node set for ::Nokogiri::CSS rules. rules must be one or more ::Nokogiri::CSS selectors. For example:

For more information see Searchable#css

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 85

def css(*args)
  rules, handler, ns, _ = extract_params(args)
  paths = css_rules_to_xpath(rules, ns)

  inject(NodeSet.new(document)) do |set, node|
    set + xpath_internal(node, paths, handler, ns, nil)
  end
end

#deconstruct() → Array)

Returns the members of this NodeSet as an array, to use in pattern matching.

Since v1.14.0

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 442

def deconstruct
  to_a
end

#delete(node)

Delete node from the Nodeset, if it is a member. Returns the deleted node if found, otherwise returns nil.

[ GitHub ]

  
# File 'ext/nokogiri/xml_node_set.c', line 180

static VALUE
delete (VALUE rb_self, VALUE rb_node)
{
  xmlNodeSetPtr c_self;
  xmlNodePtr node;

  Check_Node_Set_Node_Type(rb_node);

  TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self);
  Noko_Node_Get_Struct(rb_node, xmlNode, node);

  if (xmlXPathNodeSetContains(c_self, node)) {
    xpath_node_set_del(c_self, node);
    return rb_node;
  }
  return Qnil ;
}

#each

Iterate over each node, yielding to block

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 233

def each
  return to_enum unless block_given?

  0.upto(length - 1) do |x|
    yield self[x]
  end
  self
end

#filter(expr)

Filter this list for nodes that match expr

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 132

def filter(expr)
  find_all { |node| node.matches?(expr) }
end

#first(n = nil)

Get the first element of the NodeSet.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 31

def first(n = nil)
  return self[0] unless n

  list = []
  [n, length].min.times { |i| list << self[i] }
  list
end

#include?(node)

Returns true if any member of node set equals node.

[ GitHub ]

  
# File 'ext/nokogiri/xml_node_set.c', line 229

static VALUE
include_eh(VALUE rb_self, VALUE rb_node)
{
  xmlNodeSetPtr c_self;
  xmlNodePtr node;

  Check_Node_Set_Node_Type(rb_node);

  TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self);
  Noko_Node_Get_Struct(rb_node, xmlNode, node);

  return (xmlXPathNodeSetContains(c_self, node) ? Qtrue : Qfalse);
}

#index(node = nil)

Returns the index of the first node in self that is == to node or meets the given block. Returns nil if no match is found.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 53

def index(node = nil)
  if node
    warn("given block not used") if block_given?
    each_with_index { |member, j| return j if member == node }
  elsif block_given?
    each_with_index { |member, j| return j if yield(member) }
  end
  nil
end

#initialize_copy(rb_other) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'ext/nokogiri/xml_node_set.c', line 87

static VALUE
rb_xml_node_set_initialize_copy(VALUE rb_self, VALUE rb_other)
{
  xmlNodeSetPtr c_self, c_other;
  VALUE rb_document;

  TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self);
  TypedData_Get_Struct(rb_other, xmlNodeSet, &xml_node_set_type, c_other);

  xmlXPathNodeSetMerge(c_self, c_other);

  rb_document = rb_iv_get(rb_other, "@document");
  if (!NIL_P(rb_document)) {
    rb_iv_set(rb_self, "@document", rb_document);
    rb_funcall(rb_document, decorate, 1, rb_self);
  }

  return rb_self;
}

#inner_html(*args)

Get the inner html of all contained Node objects

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 262

def inner_html(*args)
  collect { |j| j.inner_html(*args) }.join("")
end

#inner_text Also known as: #text

Get the inner text of all contained Node objects

Note: This joins the text of all Node objects in the NodeSet:

doc = Nokogiri::XML('<xml><a><d>foo</d><d>bar</d></a></xml>')
doc.css('d').text # => "foobar"

Instead, if you want to return the text of all nodes in the NodeSet:

doc.css('d').map(&:text) # => ["foo", "bar"]

See Node#content for more information.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 255

def inner_text
  collect(&:inner_text).join("")
end

#inspect

Return a nicely formatted string representation

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 429

def inspect
  "[#{map(&:inspect).join(", ")}]"
end

#last

Get the last element of the NodeSet.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 41

def last
  self[-1]
end

#length Also known as: #size

Get the length of the node set

[ GitHub ]

  
# File 'ext/nokogiri/xml_node_set.c', line 141

static VALUE
length(VALUE rb_self)
{
  xmlNodeSetPtr c_self;

  TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self);

  return c_self ? INT2NUM(c_self->nodeNr) : INT2NUM(0);
}

#pop

Removes the last element from set and returns it, or nil if the set is empty

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 376

def pop
  return if length == 0

  delete(last)
end

#push(node) Also known as: #<<

Append node to the NodeSet.

[ GitHub ]

  
# File 'ext/nokogiri/xml_node_set.c', line 157

static VALUE
push(VALUE rb_self, VALUE rb_node)
{
  xmlNodeSetPtr c_self;
  xmlNodePtr node;

  Check_Node_Set_Node_Type(rb_node);

  TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self);
  Noko_Node_Get_Struct(rb_node, xmlNode, node);

  xmlXPathNodeSetAdd(c_self, node);

  return rb_self;
}

#remove

Alias for #unlink.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 76

alias_method :remove, :unlink

#remove_attr(name) Also known as: #remove_attribute

Remove the attributed named name from all Node objects in the NodeSet

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 225

def remove_attr(name)
  each { |el| el.delete(name) }
  self
end

#remove_attribute(name)

Alias for #remove_attr.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 229

alias_method :remove_attribute, :remove_attr

#remove_class(name = nil)

Remove the class attribute name from all Node objects in the NodeSet.

See Node#remove_class for more information.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 165

def remove_class(name = nil)
  each do |el|
    el.remove_class(name)
  end
  self
end

#reverse

Returns a new NodeSet containing all the nodes in the NodeSet in reverse order

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 419

def reverse
  node_set = NodeSet.new(document)
  (length - 1).downto(0) do |x|
    node_set.push(self[x])
  end
  node_set
end

#set(key, value = nil, &block)

Alias for #attr.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 220

alias_method :set, :attr

#shift

Returns the first element of the NodeSet and removes it. Returns nil if the set is empty.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 385

def shift
  return if length == 0

  delete(first)
end

#size

Alias for #length.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 370

alias_method :size, :length

#[](index) ⇒ Node? #[](start, length) ⇒ NodeSet? #[](range) ⇒ NodeSet? #slice(index) ⇒ Node? #slice(start, length) ⇒ NodeSet? #slice(range) ⇒ NodeSet?

Alias for #[].

#text

Alias for #inner_text.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 258

alias_method :text, :inner_text

#to_a ⇒ ? Also known as: #to_ary

Return this list as an Array

[ GitHub ]

  
# File 'ext/nokogiri/xml_node_set.c', line 401

static VALUE
to_array(VALUE rb_self)
{
  xmlNodeSetPtr c_self ;
  VALUE list;
  int i;

  TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self);

  list = rb_ary_new2(c_self->nodeNr);
  for (i = 0; i < c_self->nodeNr; i++) {
    VALUE elt = noko_xml_node_wrap_node_set_result(c_self->nodeTab[i], rb_self);
    rb_ary_push(list, elt);
  }

  return list;
}

#to_ary

Alias for #to_a.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 371

alias_method :to_ary, :to_a

#to_html(*args)

Convert this NodeSet to ::Nokogiri::HTML

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 343

def to_html(*args)
  if Nokogiri.jruby?
    options = args.first.is_a?(Hash) ? args.shift : {}
    options[:save_with] ||= Node::SaveOptions::DEFAULT_HTML
    args.insert(0, options)
  end
  if empty?
    encoding = (args.first.is_a?(Hash) ? args.first[:encoding] : nil)
    encoding ||= document.encoding
    encoding.nil? ? "" : "".encode(encoding)
  else
    map { |x| x.to_html(*args) }.join
  end
end

#to_s

Convert this NodeSet to a string.

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 337

def to_s
  map(&:to_s).join
end

#to_xhtml(*args)

Convert this NodeSet to XHTML

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 360

def to_xhtml(*args)
  map { |x| x.to_xhtml(*args) }.join
end

#to_xml(*args)

Convert this NodeSet to ::Nokogiri::XML

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 366

def to_xml(*args)
  map { |x| x.to_xml(*args) }.join
end

#wrap(markup) ⇒ self #wrap(node) ⇒ self

Wrap each member of this NodeSet with the node parsed from markup or a dup of the node.

Parameters
  • markup (String) Markup that is parsed, once per member of the NodeSet, and used as the wrapper. Each node’s parent, if it exists, is used as the context node for parsing; otherwise the associated document is used. If the parsed fragment has multiple roots, the first root node is used as the wrapper.

  • node (Nokogiri::XML::Node) An element that is ‘#dup`ed and used as the wrapper.

Returns

self, to support chaining.

⚠ Note that if a String is passed, the markup will be parsed once per node in the NodeSet. You can avoid this overhead in cases where you know exactly the wrapper you wish to use by passing a Node instead.

Also see Node#wrap

Example with a String argument:

doc = Nokogiri::HTML5(<<~HTML)
  <html><body>
    <a>a</a>
    <a>b</a>
    <a>c</a>
    <a>d</a>
  </body></html>
HTML
doc.css("a").wrap("<div></div>")
doc.to_html
# => <html><head></head><body>
#      <div><a>a</a></div>
#      <div><a>b</a></div>
#      <div><a>c</a></div>
#      <div><a>d</a></div>
#    </body></html>

Example with a Node argument

💡 Note that this is faster than the equivalent call passing a String because it avoids having to reparse the wrapper markup for each node.

doc = Nokogiri::HTML5(<<~HTML)
  <html><body>
    <a>a</a>
    <a>b</a>
    <a>c</a>
    <a>d</a>
  </body></html>
HTML
doc.css("a").wrap(doc.create_element("div"))
doc.to_html
# => <html><head></head><body>
#      <div><a>a</a></div>
#      <div><a>b</a></div>
#      <div><a>c</a></div>
#      <div><a>d</a></div>
#    </body></html>
[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 330

def wrap(node_or_tags)
  map { |node| node.wrap(node_or_tags) }
  self
end

#xpath(*paths, [namespace-bindings, variable-bindings, custom-handler-class])

Search this node set for XPath paths. paths must be one or more XPath queries.

For more information see Searchable#xpath

[ GitHub ]

  
# File 'lib/nokogiri/xml/node_set.rb', line 101

def xpath(*args)
  paths, handler, ns, binds = extract_params(args)

  inject(NodeSet.new(document)) do |set, node|
    set + xpath_internal(node, paths, handler, ns, binds)
  end
end

#|(node_set) Also known as: #+

Returns a new set built by merging the set and the elements of the given set.

[ GitHub ]

  
# File 'ext/nokogiri/xml_node_set.c', line 251

static VALUE
rb_xml_node_set_union(VALUE rb_self, VALUE rb_other)
{
  xmlNodeSetPtr c_self, c_other;
  xmlNodeSetPtr c_new_node_set;

  if (!rb_obj_is_kind_of(rb_other, cNokogiriXmlNodeSet)) {
    rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet");
  }

  TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self);
  TypedData_Get_Struct(rb_other, xmlNodeSet, &xml_node_set_type, c_other);

  c_new_node_set = xmlXPathNodeSetMerge(NULL, c_self);
  c_new_node_set = xmlXPathNodeSetMerge(c_new_node_set, c_other);

  return noko_xml_node_set_wrap(c_new_node_set, rb_iv_get(rb_self, "@document"));
}