Class: REXML::Attributes

Inherits:
Hash
  • Object
show all
Defined in:
lib/rexml/element.rb

Overview

A class that defines the set of Attributes of an Element and provides operations for accessing elements in that set.

Instance Method Summary collapse

Constructor Details

#initialize(element) ⇒ Attributes

:call-seq:

new(element)

Creates and returns a new REXML::Attributes object. The element given by argument element is stored, but its own attributes are not modified:

ele = REXML::Element.new('foo')
attrs = REXML::Attributes.new(ele)
attrs.object_id == ele.attributes.object_id # => false

Other instance methods in class REXML::Attributes may refer to:

  • element.document.

  • element.prefix.

  • element.expanded_name.



2160
2161
2162
# File 'lib/rexml/element.rb', line 2160

def initialize element
  @element = element
end

Instance Method Details

#[](name) ⇒ Object

:call-seq:

[name] -> attribute_value or nil

Returns the value for the attribute given by name, if it exists; otherwise nil. The value returned is the unnormalized attribute value, with entities expanded:

xml_string = <<-EOT
  <root xmlns:foo="http://foo" xmlns:bar="http://bar">
     <ele foo:att='1' bar:att='2' att='&lt;'/>
  </root>
EOT
d = REXML::Document.new(xml_string)
ele = d.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
ele.attributes['att']     # => "<"
ele.attributes['bar:att'] # => "2"
ele.attributes['nosuch']  # => nil

Related: get_attribute (returns an Attribute object).



2185
2186
2187
2188
2189
# File 'lib/rexml/element.rb', line 2185

def [](name)
  attr = get_attribute(name)
  return attr.value unless attr.nil?
  return nil
end

#[]=(name, value) ⇒ Object

:call-seq:

[name] = value -> value

When value is non-nil, assigns that to the attribute for the given name, overwriting the previous value if it exists:

xml_string = <<-EOT
  <root xmlns:foo="http://foo" xmlns:bar="http://bar">
     <ele foo:att='1' bar:att='2' att='&lt;'/>
  </root>
EOT
d = REXML::Document.new(xml_string)
ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
attrs = ele.attributes
attrs['foo:att'] = '2' # => "2"
attrs['baz:att'] = '3' # => "3"

When value is nil, deletes the attribute if it exists:

attrs['baz:att'] = nil
attrs.include?('baz:att') # => false


2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
# File 'lib/rexml/element.rb', line 2369

def []=( name, value )
  if value.nil?             # Delete the named attribute
    attr = get_attribute(name)
    delete attr
    return
  end

  unless value.kind_of? Attribute
    if @element.document and @element.document.doctype
      value = Text::normalize( value, @element.document.doctype )
    else
      value = Text::normalize( value, nil )
    end
    value = Attribute.new(name, value)
  end
  value.element = @element
  old_attr = fetch(value.name, nil)
  if old_attr.nil?
    store(value.name, value)
  elsif old_attr.kind_of? Hash
    old_attr[value.prefix] = value
  elsif old_attr.prefix != value.prefix
    # Check for conflicting namespaces
    if value.prefix != "xmlns" and old_attr.prefix != "xmlns"
      old_namespace = old_attr.namespace
      new_namespace = value.namespace
      if old_namespace == new_namespace
        raise ParseException.new(
                "Namespace conflict in adding attribute \"#{value.name}\": "+
                "Prefix \"#{old_attr.prefix}\" = \"#{old_namespace}\" and "+
                "prefix \"#{value.prefix}\" = \"#{new_namespace}\"")
      end
    end
    store value.name, {old_attr.prefix => old_attr,
                       value.prefix    => value}
  else
    store value.name, value
  end
  return @element
end

#add(attribute) ⇒ Object Also known as: <<

:call-seq:

add(attribute) -> attribute

Adds attribute attribute, replacing the previous attribute of the same name if it exists; returns attribute:

xml_string = <<-EOT
  <root xmlns:foo="http://foo" xmlns:bar="http://bar">
     <ele foo:att='1' bar:att='2' att='&lt;'/>
  </root>
EOT
d = REXML::Document.new(xml_string)
ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
attrs = ele.attributes
attrs # => {"att"=>{"foo"=>foo:att='1', "bar"=>bar:att='2', ""=>att='&lt;'}}
attrs.add(REXML::Attribute.new('foo:att', '2')) # => foo:att='2'
attrs.add(REXML::Attribute.new('baz', '3')) # => baz='3'
attrs.include?('baz') # => true


2537
2538
2539
# File 'lib/rexml/element.rb', line 2537

def add( attribute )
  self[attribute.name] = attribute
end

#delete(attribute) ⇒ Object

:call-seq:

delete(name) -> element
delete(attribute) -> element

Removes a specified attribute if it exists; returns the attributes’ element.

When string argument name is given, removes the attribute of that name if it exists:

xml_string = <<-EOT
  <root xmlns:foo="http://foo" xmlns:bar="http://bar">
     <ele foo:att='1' bar:att='2' att='&lt;'/>
  </root>
EOT
d = REXML::Document.new(xml_string)
ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
attrs = ele.attributes
attrs.delete('foo:att') # => <ele bar:att='2' att='&lt;'/>
attrs.delete('foo:att') # => <ele bar:att='2' att='&lt;'/>

When attribute argument attribute is given, removes that attribute if it exists:

attr = REXML::Attribute.new('bar:att', '2')
attrs.delete(attr) # => <ele att='&lt;'/> # => <ele att='&lt;'/>
attrs.delete(attr) # => <ele att='&lt;'/> # => <ele/>


2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
# File 'lib/rexml/element.rb', line 2490

def delete( attribute )
  name = nil
  prefix = nil
  if attribute.kind_of? Attribute
    name = attribute.name
    prefix = attribute.prefix
  else
    attribute =~ Namespace::NAMESPLIT
    prefix, name = $1, $2
    prefix = '' unless prefix
  end
  old = fetch(name, nil)
  if old.kind_of? Hash # the supplied attribute is one of many
    old.delete(prefix)
    if old.size == 1
      repl = nil
      old.each_value{|v| repl = v}
      store name, repl
    end
  elsif old.nil?
    return @element
  else # the supplied attribute is a top-level one
    super(name)
  end
  @element
end

#delete_all(name) ⇒ Object

:call-seq:

delete_all(name) -> array_of_removed_attributes

Removes all attributes matching the given name; returns an array of the removed attributes:

xml_string = <<-EOT
  <root xmlns:foo="http://foo" xmlns:bar="http://bar">
     <ele foo:att='1' bar:att='2' att='&lt;'/>
  </root>
EOT
d = REXML::Document.new(xml_string)
ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
attrs = ele.attributes
attrs.delete_all('att') # => [att='&lt;']


2559
2560
2561
2562
2563
2564
2565
2566
# File 'lib/rexml/element.rb', line 2559

def delete_all( name )
  rv = []
  each_attribute { |attribute|
    rv << attribute if attribute.expanded_name == name
  }
  rv.each{ |attr| attr.remove }
  return rv
end

#eachObject

:call-seq:

each {|expanded_name, value| ... }

Calls the given block with each expanded-name/value pair:

xml_string = <<-EOT
  <root xmlns:foo="http://foo" xmlns:bar="http://bar">
     <ele foo:att='1' bar:att='2' att='&lt;'/>
  </root>
EOT
d = REXML::Document.new(xml_string)
ele = d.root.elements['//ele']   # => <a foo:att='1' bar:att='2' att='&lt;'/>
ele.attributes.each do |expanded_name, value|
  p [expanded_name, value]
end

Output:

["foo:att", "1"]
["bar:att", "2"]
["att", "<"]


2287
2288
2289
2290
2291
2292
# File 'lib/rexml/element.rb', line 2287

def each
  return to_enum(__method__) unless block_given?
  each_attribute do |attr|
    yield [attr.expanded_name, attr.value]
  end
end

#each_attributeObject

:call-seq:

each_attribute {|attr| ... }

Calls the given block with each REXML::Attribute object:

xml_string = <<-EOT
  <root xmlns:foo="http://foo" xmlns:bar="http://bar">
     <ele foo:att='1' bar:att='2' att='&lt;'/>
  </root>
EOT
d = REXML::Document.new(xml_string)
ele = d.root.elements['//ele']   # => <a foo:att='1' bar:att='2' att='&lt;'/>
ele.attributes.each_attribute do |attr|
  p [attr.class, attr]
end

Output:

[REXML::Attribute, foo:att='1']
[REXML::Attribute, bar:att='2']
[REXML::Attribute, att='&lt;']


2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
# File 'lib/rexml/element.rb', line 2254

def each_attribute # :yields: attribute
  return to_enum(__method__) unless block_given?
  each_value do |val|
    if val.kind_of? Attribute
      yield val
    else
      val.each_value { |atr| yield atr }
    end
  end
end

#get_attribute(name) ⇒ Object

:call-seq:

get_attribute(name) -> attribute_object or nil

Returns the REXML::Attribute object for the given name:

xml_string = <<-EOT
  <root xmlns:foo="http://foo" xmlns:bar="http://bar">
     <ele foo:att='1' bar:att='2' att='&lt;'/>
  </root>
EOT
d = REXML::Document.new(xml_string)
ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
attrs = ele.attributes
attrs.get_attribute('foo:att')       # => foo:att='1'
attrs.get_attribute('foo:att').class # => REXML::Attribute
attrs.get_attribute('bar:att')       # => bar:att='2'
attrs.get_attribute('att')           # => att='&lt;'
attrs.get_attribute('nosuch')        # => nil


2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
# File 'lib/rexml/element.rb', line 2313

def get_attribute( name )
  attr = fetch( name, nil )
  if attr.nil?
    return nil if name.nil?
    # Look for prefix
    name =~ Namespace::NAMESPLIT
    prefix, n = $1, $2
    if prefix
      attr = fetch( n, nil )
      # check prefix
      if attr == nil
      elsif attr.kind_of? Attribute
        return attr if prefix == attr.prefix
      else
        attr = attr[ prefix ]
        return attr
      end
    end
    element_document = @element.document
    if element_document and element_document.doctype
      expn = @element.expanded_name
      expn = element_document.doctype.name if expn.size == 0
      attr_val = element_document.doctype.attribute_of(expn, name)
      return Attribute.new( name, attr_val ) if attr_val
    end
    return nil
  end
  if attr.kind_of? Hash
    attr = attr[ @element.prefix ]
  end
  return attr
end

#get_attribute_ns(namespace, name) ⇒ Object

:call-seq:

get_attribute_ns(namespace, name)

Returns the REXML::Attribute object among the attributes that matches the given namespace and name:

xml_string = <<-EOT
  <root xmlns:foo="http://foo" xmlns:bar="http://bar">
     <ele foo:att='1' bar:att='2' att='&lt;'/>
  </root>
EOT
d = REXML::Document.new(xml_string)
ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
attrs = ele.attributes
attrs.get_attribute_ns('http://foo', 'att')    # => foo:att='1'
attrs.get_attribute_ns('http://foo', 'nosuch') # => nil


2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
# File 'lib/rexml/element.rb', line 2585

def get_attribute_ns(namespace, name)
  result = nil
  each_attribute() { |attribute|
    if name == attribute.name &&
      namespace == attribute.namespace() &&
      ( !namespace.empty? || !attribute.fully_expanded_name.index(':') )
      # foo will match xmlns:foo, but only if foo isn't also an attribute
      result = attribute if !result or !namespace.empty? or
                            !attribute.fully_expanded_name.index(':')
    end
  }
  result
end

#lengthObject Also known as: size

:call-seq:

length

Returns the count of attributes:

xml_string = <<-EOT
  <root xmlns:foo="http://foo" xmlns:bar="http://bar">
     <ele foo:att='1' bar:att='2' att='&lt;'/>
  </root>
EOT
d = REXML::Document.new(xml_string)
ele = d.root.elements['//ele']   # => <a foo:att='1' bar:att='2' att='&lt;'/>
ele.attributes.length # => 3


2225
2226
2227
2228
2229
# File 'lib/rexml/element.rb', line 2225

def length
  c = 0
  each_attribute { c+=1 }
  c
end

#namespacesObject

:call-seq:

namespaces

Returns a hash of name/value pairs for the namespaces:

xml_string = '<a xmlns="foo" xmlns:x="bar" xmlns:y="twee" z="glorp"/>'
d = REXML::Document.new(xml_string)
d.root.attributes.namespaces # => {"xmlns"=>"foo", "x"=>"bar", "y"=>"twee"}


2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
# File 'lib/rexml/element.rb', line 2446

def namespaces
  namespaces = {}
  each_attribute do |attribute|
    namespaces[attribute.name] = attribute.value if attribute.prefix == 'xmlns' or attribute.name == 'xmlns'
  end
  if @element.document and @element.document.doctype
    expn = @element.expanded_name
    expn = @element.document.doctype.name if expn.size == 0
    @element.document.doctype.attributes_of(expn).each {
      |attribute|
      namespaces[attribute.name] = attribute.value if attribute.prefix == 'xmlns' or attribute.name == 'xmlns'
    }
  end
  namespaces
end

#prefixesObject

:call-seq:

prefixes -> array_of_prefix_strings

Returns an array of prefix strings in the attributes. The array does not include the default namespace declaration, if one exists.

xml_string = '<a xmlns="foo" xmlns:x="bar" xmlns:y="twee" z="glorp"/>'
d = REXML::Document.new(xml_string)
d.root.attributes.prefixes # => ["x", "y"]


2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
# File 'lib/rexml/element.rb', line 2421

def prefixes
  ns = []
  each_attribute do |attribute|
    ns << attribute.name if attribute.prefix == 'xmlns'
  end
  if @element.document and @element.document.doctype
    expn = @element.expanded_name
    expn = @element.document.doctype.name if expn.size == 0
    @element.document.doctype.attributes_of(expn).each {
      |attribute|
      ns << attribute.name if attribute.prefix == 'xmlns'
    }
  end
  ns
end

#to_aObject

:call-seq:

to_a -> array_of_attribute_objects

Returns an array of REXML::Attribute objects representing the attributes:

xml_string = <<-EOT
  <root xmlns:foo="http://foo" xmlns:bar="http://bar">
     <ele foo:att='1' bar:att='2' att='&lt;'/>
  </root>
EOT
d = REXML::Document.new(xml_string)
ele = d.root.elements['//ele']   # => <a foo:att='1' bar:att='2' att='&lt;'/>
attrs = ele.attributes.to_a      # => [foo:att='1', bar:att='2', att='&lt;']
attrs.first.class                # => REXML::Attribute


2207
2208
2209
# File 'lib/rexml/element.rb', line 2207

def to_a
  enum_for(:each_attribute).to_a
end