123456789_123456789_123456789_123456789_123456789_

Class: REXML::Elements

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
self, Enumerable
Inherits: Object
Defined in: lib/rexml/element.rb

Overview

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

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(parent) ⇒ Elements

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
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 1608

def initialize parent
  @element = parent
end

Instance Attribute Details

#empty?Boolean (readonly)

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
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 1755

def empty?
  @element.find{ |child| child.kind_of? Element}.nil?
end

Instance Method Details

#<<(element = nil)

Alias for #add.

[ GitHub ]

  
# File 'lib/rexml/element.rb', line 1937

alias :<< :add

#[](index) ⇒ element? #[](xpath) ⇒ element? #[](n, name) ⇒ element?

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
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 1680

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

#[]=(index, replacement_element) ⇒ replacement_element?

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
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 1735

def []=( index, element )
  previous = self[index]
  if previous.nil?
    @element.add element
  else
    previous.replace_with element
  end
  return previous
end

#addnew_element #add(name) ⇒ new_element #add(element) ⇒ element
Also known as: #<<

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}
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 1925

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

#collect(xpath = nil) {|element| ... } ⇒ Array

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]
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 1988

def collect( xpath=nil )
  collection = []
  XPath::each( @element, xpath ) {|e|
    collection << yield(e)  if e.kind_of?(Element)
  }
  collection
end

#delete(index) ⇒ removed_element? #delete(element) ⇒ removed_element? #delete(xpath) ⇒ removed_element?

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
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 1825

def delete element
  if element.kind_of? Element
    @element.delete element
  else
    el = self[element]
    el.remove if el
  end
end

#delete_all(xpath)

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') # => []
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 1851

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

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'> ... </>
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 1967

def each( xpath=nil )
  XPath::each( @element, xpath ) {|e| yield e if e.kind_of? Element }
end

#index(element)

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
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 1773

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

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
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 2073

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

#literalize(name) (private)

Private helper class. Removes quotes from quoted strings

[ GitHub ]

  
# File 'lib/rexml/element.rb', line 2129

def literalize name
  name = name[1..-2] if name[0] == ?' or name[0] == ?"               #'
  name
end

#parent

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
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 1623

def parent
  @element
end

#sizeInteger

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..
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 2097

def size
  count = 0
  @element.each {|child| count+=1 if child.kind_of? Element }
  count
end

#to_a(xpath = nil) ⇒ Elements

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/>]
[ GitHub ]

  
# File 'lib/rexml/element.rb', line 2121

def to_a( xpath=nil )
  rv = XPath.match( @element, xpath )
  return rv.find_all{|e| e.kind_of? Element} if xpath
  rv
end