class REXML::Elements

A class which provides filtering of children for Elements, and XPath search support. You are expected to only encounter this class as the element.elements object. Therefore, you are not expected to instantiate this yourself.

xml_string = <<-EOT
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
  <book category="cooking">
    <title lang="en">Everyday Italian</title>
    <author>Giada De Laurentiis</author>
    <year>2005</year>
    <price>30.00</price>
  </book>
  <book category="children">
    <title lang="en">Harry Potter</title>
    <author>J K. Rowling</author>
    <year>2005</year>
    <price>29.99</price>
  </book>
  <book category="web">
    <title lang="en">XQuery Kick Start</title>
    <author>James McGovern</author>
    <author>Per Bothner</author>
    <author>Kurt Cagle</author>
    <author>James Linn</author>
    <author>Vaidyanathan Nagarajan</author>
    <year>2003</year>
    <price>49.99</price>
  </book>
  <book category="web" cover="paperback">
    <title lang="en">Learning XML</title>
    <author>Erik T. Ray</author>
    <year>2003</year>
    <price>39.95</price>
  </book>
</bookstore>
EOT
d = REXML::Document.new(xml_string)
elements = d.root.elements
elements # => #<REXML::Elements @element=<bookstore> ... </>>

Public Class Methods

new(parent) → new_elements_object click to toggle source

Returns a new Elements object with the given parent. Does not assign parent.elements = self:

d = REXML::Document.new(xml_string)
eles = REXML::Elements.new(d.root)
eles # => #<REXML::Elements @element=<bookstore> ... </>>
eles == d.root.elements # => false
# File rexml-3.3.6/lib/rexml/element.rb, line 1604
def initialize parent
  @element = parent
end

Public Instance Methods

<<(element=nil)
Alias for: add
elements[index] → element or nil click to toggle source
elements[xpath] → element or nil
elements[n, name] → element or nil

Returns the first Element object selected by the arguments, if any found, or nil if none found.

Notes:

  • The index is 1-based, not 0-based, so that:

    • The first element has index 1

    • The nth element has index n.

  • The selection ignores non-Element nodes.

When the single argument index is given, returns the element given by the index, if any; otherwise, nil:

d = REXML::Document.new(xml_string)
eles = d.root.elements
eles # => #<REXML::Elements @element=<bookstore> ... </>>
eles[1] # => <book category='cooking'> ... </>
eles.size # => 4
eles[4] # => <book category='web' cover='paperback'> ... </>
eles[5] # => nil

The node at this index is not an Element, and so is not returned:

eles = d.root.first.first # => <title lang='en'> ... </>
eles.to_a # => ["Everyday Italian"]
eles[1] # => nil

When the single argument xpath is given, returns the first element found via that xpath, if any; otherwise, nil:

eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>>
eles['/bookstore']                    # => <bookstore> ... </>
eles['//book']                        # => <book category='cooking'> ... </>
eles['//book [@category="children"]'] # => <book category='children'> ... </>
eles['/nosuch']                       # => nil
eles['//nosuch']                      # => nil
eles['//book [@category="nosuch"]']   # => nil
eles['.']                             # => <bookstore> ... </>
eles['..'].class                      # => REXML::Document

With arguments n and name given, returns the nth found element that has the given name, or nil if there is no such nth element:

eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>>
eles[1, 'book'] # => <book category='cooking'> ... </>
eles[4, 'book'] # => <book category='web' cover='paperback'> ... </>
eles[5, 'book'] # => nil
# File rexml-3.3.6/lib/rexml/element.rb, line 1676
def []( index, name=nil)
  if index.kind_of? Integer
    raise "index (#{index}) must be >= 1" if index < 1
    name = literalize(name) if name
    num = 0
    @element.find { |child|
      child.kind_of? Element and
      (name.nil? ? true : child.has_name?( name )) and
      (num += 1) == index
    }
  else
    return XPath::first( @element, index )
    #{ |element|
    #       return element if element.kind_of? Element
    #}
    #return nil
  end
end
elements[] = index, replacement_element → replacement_element or nil click to toggle source

Replaces or adds an element.

When eles[index] exists, replaces it with replacement_element and returns replacement_element:

d = REXML::Document.new(xml_string)
eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>>
eles[1] # => <book category='cooking'> ... </>
eles[1] = REXML::Element.new('foo')
eles[1] # => <foo/>

Does nothing (or raises an exception) if replacement_element is not an Element:

eles[2] # => <book category='web' cover='paperback'> ... </>
eles[2] = REXML::Text.new('bar')
eles[2] # => <book category='web' cover='paperback'> ... </>

When eles[index] does not exist, adds replacement_element to the element and returns

d = REXML::Document.new(xml_string)
eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>>
eles.size # => 4
eles[50] = REXML::Element.new('foo') # => <foo/>
eles.size # => 5
eles[5] # => <foo/>

Does nothing (or raises an exception) if replacement_element is not an Element:

eles[50] = REXML::Text.new('bar') # => "bar"
eles.size # => 5
# File rexml-3.3.6/lib/rexml/element.rb, line 1731
def []=( index, element )
  previous = self[index]
  if previous.nil?
    @element.add element
  else
    previous.replace_with element
  end
  return previous
end
add → new_element click to toggle source
add(name) → new_element
add(element) → element

Adds an element; returns the element added.

With no argument, creates and adds a new element. The new element has:

  • No name.

  • Parent from the Elements object.

  • Context from the that parent.

Example:

d = REXML::Document.new(xml_string)
elements = d.root.elements
parent = elements.parent     # => <bookstore> ... </>
parent.context = {raw: :all}
elements.size                # => 4
new_element = elements.add   # => </>
elements.size                # => 5
new_element.name             # => nil
new_element.parent           # => <bookstore> ... </>
new_element.context          # => {:raw=>:all}

With string argument name, creates and adds a new element. The new element has:

  • Name name.

  • Parent from the Elements object.

  • Context from the that parent.

Example:

d = REXML::Document.new(xml_string)
elements = d.root.elements
parent = elements.parent          # => <bookstore> ... </>
parent.context = {raw: :all}
elements.size                     # => 4
new_element = elements.add('foo') # => <foo/>
elements.size                     # => 5
new_element.name                  # => "foo"
new_element.parent                # => <bookstore> ... </>
new_element.context               # => {:raw=>:all}

With argument element, creates and adds a clone of the given element. The new element has name, parent, and context from the given element.

d = REXML::Document.new(xml_string)
elements = d.root.elements
elements.size                 # => 4
e0 = REXML::Element.new('foo')
e1 = REXML::Element.new('bar', e0, {raw: :all})
element = elements.add(e1) # => <bar/>
elements.size                 # => 5
element.name                  # => "bar"
element.parent                # => <bookstore> ... </>
element.context               # => {:raw=>:all}
# File rexml-3.3.6/lib/rexml/element.rb, line 1921
def add element=nil
  if element.nil?
    Element.new("", self, @element.context)
  elsif not element.kind_of?(Element)
    Element.new(element, self, @element.context)
  else
    @element << element
    element.context = @element.context
    element
  end
end
Also aliased as: <<
collect(xpath = nil) {|element| ... } → array click to toggle source

Iterates over the elements; returns the array of block return values.

With no argument, iterates over all elements:

d = REXML::Document.new(xml_string)
elements = d.root.elements
elements.collect {|element| element.size } # => [9, 9, 17, 9]

With argument xpath, iterates over elements that match the given xpath:

xpath = '//book [@category="web"]'
elements.collect(xpath) {|element| element.size } # => [17, 9]
# File rexml-3.3.6/lib/rexml/element.rb, line 1984
def collect( xpath=nil )
  collection = []
  XPath::each( @element, xpath ) {|e|
    collection << yield(e)  if e.kind_of?(Element)
  }
  collection
end
delete(index) → removed_element or nil click to toggle source
delete(element) → removed_element or nil
delete(xpath) → removed_element or nil

Removes an element; returns the removed element, or nil if none removed.

With integer argument index given, removes the child element at that offset:

d = REXML::Document.new(xml_string)
elements = d.root.elements
elements.size # => 4
elements[2] # => <book category='children'> ... </>
elements.delete(2) # => <book category='children'> ... </>
elements.size # => 3
elements[2] # => <book category='web'> ... </>
elements.delete(50) # => nil

With element argument element given, removes that child element:

d = REXML::Document.new(xml_string)
elements = d.root.elements
ele_1, ele_2, ele_3, ele_4 = *elements
elements.size # => 4
elements[2] # => <book category='children'> ... </>
elements.delete(ele_2) # => <book category='children'> ... </>
elements.size # => 3
elements[2] # => <book category='web'> ... </>
elements.delete(ele_2) # => nil

With string argument xpath given, removes the first element found via that xpath:

d = REXML::Document.new(xml_string)
elements = d.root.elements
elements.delete('//book') # => <book category='cooking'> ... </>
elements.delete('//book [@category="children"]') # => <book category='children'> ... </>
elements.delete('//nosuch') # => nil
# File rexml-3.3.6/lib/rexml/element.rb, line 1821
def delete element
  if element.kind_of? Element
    @element.delete element
  else
    el = self[element]
    el.remove if el
  end
end
delete_all(xpath) click to toggle source

Removes all elements found via the given xpath; returns the array of removed elements, if any, else nil.

d = REXML::Document.new(xml_string)
elements = d.root.elements
elements.size # => 4
deleted_elements = elements.delete_all('//book [@category="web"]')
deleted_elements.size # => 2
elements.size # => 2
deleted_elements = elements.delete_all('//book')
deleted_elements.size # => 2
elements.size # => 0
elements.delete_all('//book') # => []
# File rexml-3.3.6/lib/rexml/element.rb, line 1847
def delete_all( xpath )
  rv = []
  XPath::each( @element, xpath) {|element|
    rv << element if element.kind_of? Element
  }
  rv.each do |element|
    @element.delete element
    element.remove
  end
  return rv
end
each(xpath = nil) {|element| ... } → self click to toggle source

Iterates over the elements.

With no argument, calls the block with each element:

d = REXML::Document.new(xml_string)
elements = d.root.elements
elements.each {|element| p element }

Output:

<book category='cooking'> ... </>
<book category='children'> ... </>
<book category='web'> ... </>
<book category='web' cover='paperback'> ... </>

With argument xpath, calls the block with each element that matches the given xpath:

elements.each('//book [@category="web"]') {|element| p element }

Output:

<book category='web'> ... </>
<book category='web' cover='paperback'> ... </>
# File rexml-3.3.6/lib/rexml/element.rb, line 1963
def each( xpath=nil )
  XPath::each( @element, xpath ) {|e| yield e if e.kind_of? Element }
end
empty? → true or false click to toggle source

Returns true if there are no children, false otherwise.

d = REXML::Document.new('')
d.elements.empty? # => true
d = REXML::Document.new(xml_string)
d.elements.empty? # => false
# File rexml-3.3.6/lib/rexml/element.rb, line 1751
def empty?
  @element.find{ |child| child.kind_of? Element}.nil?
end
index(element) click to toggle source

Returns the 1-based index of the given element, if found; otherwise, returns -1:

d = REXML::Document.new(xml_string)
elements = d.root.elements
ele_1, ele_2, ele_3, ele_4 = *elements
elements.index(ele_4) # => 4
elements.delete(ele_3)
elements.index(ele_4) # => 3
elements.index(ele_3) # => -1
# File rexml-3.3.6/lib/rexml/element.rb, line 1769
def index element
  rv = 0
  found = @element.find do |child|
    child.kind_of? Element and
    (rv += 1) and
    child == element
  end
  return rv if found == element
  return -1
end
inject(xpath = nil, initial = nil) → object click to toggle source

Calls the block with elements; returns the last block return value.

With no argument, iterates over the elements, calling the block elements.size - 1 times.

  • The first call passes the first and second elements.

  • The second call passes the first block return value and the third element.

  • The third call passes the second block return value and the fourth element.

  • And so on.

In this example, the block returns the passed element, which is then the object argument to the next call:

d = REXML::Document.new(xml_string)
elements = d.root.elements
elements.inject do |object, element|
  p [elements.index(object), elements.index(element)]
  element
end

Output:

[1, 2]
[2, 3]
[3, 4]

With the single argument xpath, calls the block only with elements matching that xpath:

elements.inject('//book [@category="web"]') do |object, element|
  p [elements.index(object), elements.index(element)]
  element
end

Output:

[3, 4]

With argument xpath given as nil and argument initial also given, calls the block once for each element.

  • The first call passes the initial and the first element.

  • The second call passes the first block return value and the second element.

  • The third call passes the second block return value and the third element.

  • And so on.

In this example, the first object index is -1

elements.inject(nil, 'Initial') do |object, element|
  p [elements.index(object), elements.index(element)]
  element
end

Output:

[-1, 1]
[1, 2]
[2, 3]
[3, 4]

In this form the passed object can be used as an accumulator:

elements.inject(nil, 0) do |total, element|
  total += element.size
end # => 44

With both arguments xpath and initial are given, calls the block only with elements matching that xpath:

elements.inject('//book [@category="web"]', 0) do |total, element|
  total += element.size
end # => 26
# File rexml-3.3.6/lib/rexml/element.rb, line 2069
def inject( xpath=nil, initial=nil )
  first = true
  XPath::each( @element, xpath ) {|e|
    if (e.kind_of? Element)
      if (first and initial == nil)
        initial = e
        first = false
      else
        initial = yield( initial, e ) if e.kind_of? Element
      end
    end
  }
  initial
end
parent click to toggle source

Returns the parent element cited in creating the Elements object. This element is also the default starting point for searching in the Elements object.

d = REXML::Document.new(xml_string)
elements = REXML::Elements.new(d.root)
elements.parent == d.root # => true
# File rexml-3.3.6/lib/rexml/element.rb, line 1619
def parent
  @element
end
size → integer click to toggle source

Returns the count of Element children:

d = REXML::Document.new '<a>sean<b/>elliott<b/>russell<b/></a>'
d.root.elements.size # => 3 # Three elements.
d.root.size          # => 6 # Three elements plus three text nodes..
# File rexml-3.3.6/lib/rexml/element.rb, line 2093
def size
  count = 0
  @element.each {|child| count+=1 if child.kind_of? Element }
  count
end
to_a(xpath = nil) → array_of_elements click to toggle source

Returns an array of element children (not including non-element children).

With no argument, returns an array of all element children:

d = REXML::Document.new '<a>sean<b/>elliott<c/></a>'
elements = d.root.elements
elements.to_a # => [<b/>, <c/>]               # Omits non-element children.
children = d.root.children
children # => ["sean", <b/>, "elliott", <c/>] # Includes non-element children.

With argument xpath, returns an array of element children that match the xpath:

elements.to_a('//c') # => [<c/>]
# File rexml-3.3.6/lib/rexml/element.rb, line 2117
def to_a( xpath=nil )
  rv = XPath.match( @element, xpath )
  return rv.find_all{|e| e.kind_of? Element} if xpath
  rv
end

Private Instance Methods

literalize(name) click to toggle source

Private helper class. Removes quotes from quoted strings

# File rexml-3.3.6/lib/rexml/element.rb, line 2125
def literalize name
  name = name[1..-2] if name[0] == ?' or name[0] == ?"               #'
  name
end