Class: XMLROCS::XMLNode

Inherits:
String
  • Object
show all
Includes:
Comparable, Enumerable
Defined in:
lib/xmlrocs.rb

Overview

Represents an XML Element. You can access and modify it’s children and attributes.

Each XMLNode has exactly one parent (that happens to be nil if the node is the root of the tree) and a (possibly empty) list of children.

It also has a name (the name of the XML tag) that can be modified.

You can compare two XMLNodes or an XMLNode and a String. When you provide a String, only the text value of the XMLNode is compared.

You can enumerate the XMLNode Tree in the traversal-order defined in the @traversal accessor.

For more information have a look at the README or instance method documentation.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ XMLNode

Create a new XMLNode You have to either provide an REXML::Element as options or plaintext xml data as options. Otherwise an ArgumentError will be thrown.

Supported options:

:root       # => REXML::Element that will be traversed.
:text       # => XML plaintext that will be parsed by REXML and then
                 traversed.
:nil        # => Will create a nil node. Useful if you need a global
                 parent.
:traversal  # => The traversal order. See traversal.

Raises:

  • (ArgumentError)


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/xmlrocs.rb', line 118

def initialize(options = {})
  xmlroot = if options[:root]
    options[:root]
  elsif options[:text]
    REXML::Document.new(options[:text]).root
  elsif options[:nil]
    nil
  end
  raise(ArgumentError, "Undefined") if !xmlroot and !options[:nil]
  @children, @attributes = {}, {}
  @parent ,@traversal = options[:parent], options[:traversal] || :preorder
  @xmlname = xmlroot ? xmlroot.name.to_sym : :NIL
  set_text(xmlroot ? xmlroot.get_text.to_s : '')
  if xmlroot 
    xmlroot.attributes.each { |k,v| self[k.to_sym] = v }
  
    # this recursions makes the whole library slow. basically we have
    # all information in xmlroot, so no recursive calls would be necessary
    xmlroot.select { |e| e.class == REXML::Element }.each do |e| 
      self << XMLNode.new({ :root => e, :parent => self })
    end
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object



294
295
296
297
298
299
300
# File 'lib/xmlrocs.rb', line 294

def method_missing(method, *args)
  if method.to_s[-1] == 33 and @children.has_key?(real_method = method.to_s.chomp("!").to_sym)
    return @children[real_method] 
  end
  return @children[method].last if @children.has_key?(method)
  super(method, *args)
end

Instance Attribute Details

#attributesObject (readonly)

Hash containing the attributes of the current Node:

{ :attribute_name => "Attribute" }

Attributes can also be modified using this accessor.



78
79
80
# File 'lib/xmlrocs.rb', line 78

def attributes
  @attributes
end

#childrenObject (readonly)

Hash containing the children of the current Node. The Hash has the following structure:

{ :childname => [ XMLNode, ... ] }

You can either use the >> or << operators to modify children or use this accessor directly.



70
71
72
# File 'lib/xmlrocs.rb', line 70

def children
  @children
end

#parentObject (readonly)

The parent of the current Node. The parent of the Root-Node is nil.



83
84
85
# File 'lib/xmlrocs.rb', line 83

def parent
  @parent
end

#traversalObject

Defines the order in which the tree is traversed.

The following traversals are suppported:

:preorder
:postorder
:inorder


102
103
104
# File 'lib/xmlrocs.rb', line 102

def traversal
  @traversal
end

#xmlnameObject (readonly)

The name of the current Node.

Example:

x = XMLNode.new :text => '<a></a>'
x.xmlname # => :a


92
93
94
# File 'lib/xmlrocs.rb', line 92

def xmlname
  @xmlname
end

Instance Method Details

#<<(child) ⇒ Object

Append a child. child has to be an XMLNode or a String that contains the XML data in plaintext.



169
170
171
172
# File 'lib/xmlrocs.rb', line 169

def <<(child)
  return self << XMLROCS::XMLNode.new(:text => child) if is_real_string(child)
  (@children[child.xmlname] = (@children[child.xmlname] || []) << child).last
end

#==(other) ⇒ Object

Deep-compares to XMLNodes.



272
273
274
275
276
277
278
279
280
281
282
# File 'lib/xmlrocs.rb', line 272

def ==(other)
  # pure string comparison
  return super(other) if is_real_string(other)
  return false unless other == self.to_s
  [ [ self, other ], [ other, self ] ].each do |a,b|
    a.children.each do |k,v| 
      return false if !b.children.has_key?(k) or v != b.children[k]
    end
  end
  true
end

#>>(childname = nil) ⇒ Object

Remove a child from the current XMLNode. Providing only a childname, it will delete all children with the given name. If you also provide a block, the block will be evaluated with each child as an argument and according to the return value of the call the child will be deleted or not (when the block returns true, the child will be deleted). If you provide a block you can optionally filter the children by providing a childname so only children with the given name will be evaluated.



186
187
188
189
190
191
192
193
194
# File 'lib/xmlrocs.rb', line 186

def >>(childname = nil)
  if block_given?
    @children.select { |k,v| childname ? k == childname : true }.each do |k,v| 
      v.reject! { |child| yield(child) }
    end
  else
    @children.delete(childname)
  end
end

#[](attribute) ⇒ Object

Access attributes by name. Attributenames are stored as symbols.



145
146
147
# File 'lib/xmlrocs.rb', line 145

def [](attribute)
  @attributes[attribute]
end

#[]=(attribute, value) ⇒ Object

Modify attributes. Behaves like a Hash. Keys are symbols by convention, values are XMLNode-objects.



153
154
155
# File 'lib/xmlrocs.rb', line 153

def []=(attribute, value)
  @attributes[attribute] = value
end

#all(name = nil) ⇒ Object

Returns an array of all XMLNodes in the order that is specified in the traversal accessor.



200
201
202
# File 'lib/xmlrocs.rb', line 200

def all(name = nil)
  name ? select { |x| x.xmlname == name } : traverse
end

#delete_attribute!(attribute) ⇒ Object

Delete attributes. attribute has to be a symbol with the name of the attribute you want to delete.



161
162
163
# File 'lib/xmlrocs.rb', line 161

def delete_attribute!(attribute)
  @attributes.delete(attribute)
end

#dupObject

Deep-copies the Tree.



231
232
233
# File 'lib/xmlrocs.rb', line 231

def dup
  XMLROCS::XMLNode.new(:text => to_xml)
end

#each(&block) ⇒ Object

Iterates over the XMLNode-tree in the order that is specified in the traversal accessor.



224
225
226
# File 'lib/xmlrocs.rb', line 224

def each(&block)
  traverse.each(&block)
end

#leaf?(tag = nil) ⇒ Boolean

If a tag is provided it checks if the child with the given name is a leaf. Otherwise it does the same for the current node.

Returns:

  • (Boolean)


248
249
250
# File 'lib/xmlrocs.rb', line 248

def leaf?(tag = nil)
  tag ? children[tag].all? { |x| x.leaf? } : children.empty?
end

#leafsObject

Generates an array of all leafs in the order specified in the traversal accessor.



256
257
258
# File 'lib/xmlrocs.rb', line 256

def leafs
  traverse.select { |x| x.leaf? }
end

#map(&block) ⇒ Object

Maps a function over the XMLNode-tree and returns a new XMLNode-tree with the mapped values.



208
209
210
# File 'lib/xmlrocs.rb', line 208

def map(&block)
  dup.map!(&block)
end

#map!(&block) ⇒ Object

Maps a function over the current XMLNode-tree.



215
216
217
218
# File 'lib/xmlrocs.rb', line 215

def map!(&block)  
  traverse.map!(&block)
  self
end

#set_text(text) ⇒ Object

Sets the text of the current node to text.



263
264
265
266
267
# File 'lib/xmlrocs.rb', line 263

def set_text(text)
  # special match for whitespace-only
  return gsub!(self.to_s, "") if text =~ /^\s+$/
  gsub!(self.to_s, text)
end

#single?(tag = nil) ⇒ Boolean

If a tag is provided it checks if the child with the given name has siblings. Otherwise it does the same for the current node.

Returns:

  • (Boolean)


240
241
242
# File 'lib/xmlrocs.rb', line 240

def single?(tag = nil)
  tag ? @children[tag].length == 1 : @parent.children[@xmlname].length == 1
end

#to_xml(flat = false) ⇒ Object

Deep-transforms the current node into plaintext XML. If flat is true, all children will be omitted.



288
289
290
291
292
# File 'lib/xmlrocs.rb', line 288

def to_xml(flat = false)
  "<#{@xmlname} " + @attributes.map { |k,v| "#{k}=\"#{v}\" "}.join(" ") + ">" + 
    (flat ? "" : (leaf? ? self.to_s : @children.values.flatten.map { |e| e.to_xml }.join)) + 
  "</#{@xmlname}>"
end