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
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"));
}