Proper namespace support for attributes.

This separates namespace handling into namespace names and namespace objects.
The namespace objects are retrieved from the element an attribute belongs to.
Once retrieved the namespace is cached, due to the overhead of retrieving
namespaces in large documents.
This commit is contained in:
Yorick Peterse 2014-08-11 00:40:17 +02:00
parent fe8f77cf45
commit 04cbbdcf9e
4 changed files with 63 additions and 36 deletions

View File

@ -7,35 +7,44 @@ module Oga
# The name of the attribute. # The name of the attribute.
# @return [String] # @return [String]
# #
# @!attribute [rw] namespace # @!attribute [rw] namespace_name
# The namespace of the attribute. # @return [String]
# @return [Oga::XML::Namespace]
# #
# @!attribute [rw] value # @!attribute [rw] value
# The value of the attribute. # The value of the attribute.
# @return [String] # @return [String]
# #
# @!attribute [r] element
# The element this attribute belongs to.
# @return [Oga::XML::Element]
#
class Attribute class Attribute
attr_accessor :name, :namespace, :value attr_accessor :name, :namespace_name, :element, :value
## ##
# @param [Hash] options # @param [Hash] options
# #
# @option options [String] :name # @option options [String] :name
# @option options [Oga::XML::Namespace] :namespace # @option options [String] :namespace_name
# @option options [String] :value # @option options [String] :value
# @option options [Oga::XML::Element] :element
# #
def initialize(options = {}) def initialize(options = {})
if options[:namespace] and !options[:namespace].is_a?(Namespace) @name = options[:name]
raise( @value = options[:value]
TypeError, @element = options[:element]
':namespace must be an instance of Oga::XML::Namespace'
)
end
@name = options[:name] @namespace_name = options[:namespace_name]
@namespace = options[:namespace] end
@value = options[:value]
##
# Returns the {Oga::XML::Namespace} instance for the current namespace
# name.
#
# @return [Oga::XML::Namespace]
#
def namespace
return @namespace ||= element.available_namespaces[namespace_name]
end end
## ##

View File

@ -4,16 +4,24 @@ module Oga
# The Namespace class contains information about XML namespaces such as the # The Namespace class contains information about XML namespaces such as the
# name and URI. # name and URI.
# #
# @!attribute [r] name
# @return [String]
#
# @!attribute [r] uri
# @return [String]
#
class Namespace class Namespace
attr_accessor :name attr_accessor :name, :uri
## ##
# @param [Hash] options # @param [Hash] options
# #
# @option options [String] :name # @option options [String] :name
# @option options [String] :uri
# #
def initialize(options = {}) def initialize(options = {})
@name = options[:name] @name = options[:name]
@uri = options[:uri]
end end
## ##
@ -27,7 +35,7 @@ module Oga
# @return [String] # @return [String]
# #
def inspect def inspect
return "Namespace(name: #{name.inspect})" return "Namespace(name: #{name.inspect} uri: #{uri.inspect})"
end end
end # Namespace end # Namespace
end # XML end # XML

View File

@ -150,9 +150,7 @@ rule
# foo:bar # foo:bar
| T_ATTR_NS T_ATTR | T_ATTR_NS T_ATTR
{ {
ns = Namespace.new(:name => val[0]) Attribute.new(:namespace_name => val[0], :name => val[1])
Attribute.new(:namespace => ns, :name => val[1])
} }
; ;

View File

@ -6,24 +6,31 @@ describe Oga::XML::Attribute do
described_class.new(:name => 'a').name.should == 'a' described_class.new(:name => 'a').name.should == 'a'
end end
example 'set the namespace' do
ns = Oga::XML::Namespace.new(:name => 'foo')
attr = described_class.new(:namespace => ns)
attr.namespace.should == ns
end
example 'raise TypeError when using a String for the namespace' do
block = lambda { described_class.new(:namespace => 'x') }
block.should raise_error(TypeError)
end
example 'set the value' do example 'set the value' do
described_class.new(:value => 'a').value.should == 'a' described_class.new(:value => 'a').value.should == 'a'
end end
end end
context '#namespace' do
before do
@namespace = Oga::XML::Namespace.new(:name => 'b')
element = Oga::XML::Element.new(
:namespaces => {'b' => @namespace}
)
@attribute = described_class.new(
:namespace_name => 'b',
:name => 'a',
:element => element
)
end
example 'return a Namespace instance' do
@attribute.namespace.should == @namespace
end
end
context '#to_s' do context '#to_s' do
example 'return an empty String when there is no value' do example 'return an empty String when there is no value' do
described_class.new.to_s.should == '' described_class.new.to_s.should == ''
@ -44,14 +51,19 @@ describe Oga::XML::Attribute do
context '#inspect' do context '#inspect' do
example 'return the inspect value' do example 'return the inspect value' do
element = Oga::XML::Element.new(
:namespaces => {'b' => Oga::XML::Namespace.new(:name => 'b')}
)
obj = described_class.new( obj = described_class.new(
:name => 'a', :namespace_name => 'b',
:namespace => Oga::XML::Namespace.new(:name => 'b'), :name => 'a',
:value => 'c' :value => 'c',
:element => element
) )
obj.inspect.should == <<-EOF.strip obj.inspect.should == <<-EOF.strip
Attribute(name: "a" namespace: Namespace(name: "b") value: "c") Attribute(name: "a" namespace: Namespace(name: "b" uri: nil) value: "c")
EOF EOF
end end
end end