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.



2150
2151
2152
# File 'lib/rexml/element.rb', line 2150

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 = "  <root xmlns:foo=\"http://foo\" xmlns:bar=\"http://bar\">\n     <ele foo:att='1' bar:att='2' att='&lt;'/>\n  </root>\n"
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).



2175
2176
2177
2178
# File 'lib/rexml/element.rb', line 2175

def [](name)
  attr = get_attribute(name)
  attr&.value
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 = "  <root xmlns:foo=\"http://foo\" xmlns:bar=\"http://bar\">\n     <ele foo:att='1' bar:att='2' att='&lt;'/>\n  </root>\n"
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


2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
# File 'lib/rexml/element.rb', line 2358

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

  unless value.kind_of? Attribute
    doctype = @element.document&.doctype
    if doctype
      value = Text::normalize( value, 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
    store value.name, {old_attr.prefix => old_attr,
                       value.prefix    => value}
  else
    store value.name, value
  end
  @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 = "  <root xmlns:foo=\"http://foo\" xmlns:bar=\"http://bar\">\n     <ele foo:att='1' bar:att='2' att='&lt;'/>\n  </root>\n"
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


2516
2517
2518
# File 'lib/rexml/element.rb', line 2516

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 = "  <root xmlns:foo=\"http://foo\" xmlns:bar=\"http://bar\">\n     <ele foo:att='1' bar:att='2' att='&lt;'/>\n  </root>\n"
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/>


2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
# File 'lib/rexml/element.rb', line 2471

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 # 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 = "  <root xmlns:foo=\"http://foo\" xmlns:bar=\"http://bar\">\n     <ele foo:att='1' bar:att='2' att='&lt;'/>\n  </root>\n"
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;']


2538
2539
2540
2541
2542
2543
2544
2545
# File 'lib/rexml/element.rb', line 2538

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

#eachObject

:call-seq:

each {|expanded_name, value| ... }

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

xml_string = "  <root xmlns:foo=\"http://foo\" xmlns:bar=\"http://bar\">\n     <ele foo:att='1' bar:att='2' att='&lt;'/>\n  </root>\n"
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", "<"]


2276
2277
2278
2279
2280
2281
# File 'lib/rexml/element.rb', line 2276

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 = "  <root xmlns:foo=\"http://foo\" xmlns:bar=\"http://bar\">\n     <ele foo:att='1' bar:att='2' att='&lt;'/>\n  </root>\n"
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;']


2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
# File 'lib/rexml/element.rb', line 2243

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 = "  <root xmlns:foo=\"http://foo\" xmlns:bar=\"http://bar\">\n     <ele foo:att='1' bar:att='2' att='&lt;'/>\n  </root>\n"
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


2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
# File 'lib/rexml/element.rb', line 2302

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
    doctype = @element.document&.doctype
    if doctype
      expn = @element.expanded_name
      expn = doctype.name if expn.size == 0
      attr_val = 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
  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 = "  <root xmlns:foo=\"http://foo\" xmlns:bar=\"http://bar\">\n     <ele foo:att='1' bar:att='2' att='&lt;'/>\n  </root>\n"
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


2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
# File 'lib/rexml/element.rb', line 2564

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


2214
2215
2216
2217
2218
# File 'lib/rexml/element.rb', line 2214

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


2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
# File 'lib/rexml/element.rb', line 2426

def namespaces
  namespaces = {}
  each_attribute do |attribute|
    namespaces[attribute.name] = attribute.value if attribute.prefix == 'xmlns' or attribute.name == 'xmlns'
  end
  doctype = @element.document&.doctype
  if doctype
    expn = @element.expanded_name
    expn = doctype.name if expn.size == 0
    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"]


2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
# File 'lib/rexml/element.rb', line 2400

def prefixes
  ns = []
  each_attribute do |attribute|
    ns << attribute.name if attribute.prefix == 'xmlns'
  end
  doctype = @element.document&.doctype
  if doctype
    expn = @element.expanded_name
    expn = doctype.name if expn.size == 0
    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 = "  <root xmlns:foo=\"http://foo\" xmlns:bar=\"http://bar\">\n     <ele foo:att='1' bar:att='2' att='&lt;'/>\n  </root>\n"
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


2196
2197
2198
# File 'lib/rexml/element.rb', line 2196

def to_a
  enum_for(:each_attribute).to_a
end