Cache output of Element#available_namespaces
This cache is flushed whenever Element#register_namespace is called. When this cache is flushed it's also recursively flushed for all child elements. This makes calls to Element#register_namespace a bit more expensive but in turn calls to Element#available_namespaces will be a lot faster.
This commit is contained in:
parent
fa838154fc
commit
b42f9aaf32
|
@ -299,14 +299,17 @@ module Oga
|
|||
#
|
||||
# @param [String] name
|
||||
# @param [String] uri
|
||||
# @param [TrueClass|FalseClass] flush
|
||||
# @see [Oga::XML::Namespace#initialize]
|
||||
#
|
||||
def register_namespace(name, uri)
|
||||
def register_namespace(name, uri, flush = true)
|
||||
if namespaces[name]
|
||||
raise ArgumentError, "The namespace #{name.inspect} already exists"
|
||||
end
|
||||
|
||||
namespaces[name] = Namespace.new(:name => name, :uri => uri)
|
||||
|
||||
flush_namespaces_cache if flush
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -316,20 +319,25 @@ module Oga
|
|||
# @return [Hash]
|
||||
#
|
||||
def available_namespaces
|
||||
return {} if html? # HTML(5) completely ignores namespaces
|
||||
# HTML(5) completely ignores namespaces
|
||||
if html?
|
||||
return @available_namespaces ||= {}
|
||||
elsif !@available_namespaces
|
||||
merged = namespaces.dup
|
||||
node = parent
|
||||
|
||||
merged = namespaces.dup
|
||||
node = parent
|
||||
while node && node.respond_to?(:namespaces)
|
||||
node.namespaces.each do |prefix, ns|
|
||||
merged[prefix] = ns unless merged[prefix]
|
||||
end
|
||||
|
||||
while node && node.respond_to?(:namespaces)
|
||||
node.namespaces.each do |prefix, ns|
|
||||
merged[prefix] = ns unless merged[prefix]
|
||||
node = node.parent
|
||||
end
|
||||
|
||||
node = node.parent
|
||||
@available_namespaces = merged
|
||||
end
|
||||
|
||||
return merged
|
||||
return @available_namespaces
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -349,19 +357,40 @@ module Oga
|
|||
return self_closing
|
||||
end
|
||||
|
||||
##
|
||||
# Flushes the namespaces cache of the current element and all its child
|
||||
# elements.
|
||||
#
|
||||
def flush_namespaces_cache
|
||||
@available_namespaces = nil
|
||||
@namespace = nil
|
||||
|
||||
children.each do |child|
|
||||
child.flush_namespaces_cache if child.is_a?(Element)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
##
|
||||
# Registers namespaces based on any "xmlns" attributes.
|
||||
#
|
||||
def register_namespaces_from_attributes
|
||||
flush = false
|
||||
|
||||
attributes.each do |attr|
|
||||
# We're using `namespace_name` opposed to `namespace.name` as "xmlns"
|
||||
# is not a registered namespace.
|
||||
if attr.name == XMLNS_PREFIX or attr.namespace_name == XMLNS_PREFIX
|
||||
register_namespace(attr.name, attr.value)
|
||||
flush = true
|
||||
|
||||
# Ensures we only flush the cache once instead of flushing it on
|
||||
# every register_namespace call.
|
||||
register_namespace(attr.name, attr.value, false)
|
||||
end
|
||||
end
|
||||
|
||||
flush_namespaces_cache if flush
|
||||
end
|
||||
|
||||
##
|
||||
|
|
|
@ -488,6 +488,25 @@ describe Oga::XML::Element do
|
|||
|
||||
block.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it 'flushes the cache when registering a namespace' do
|
||||
@element.available_namespaces.should == {
|
||||
'foo' => @element.namespaces['foo']
|
||||
}
|
||||
|
||||
@element.register_namespace('bar', 'http://exmaple.com')
|
||||
|
||||
@element.available_namespaces.should == {
|
||||
'foo' => @element.namespaces['foo'],
|
||||
'bar' => @element.namespaces['bar']
|
||||
}
|
||||
end
|
||||
|
||||
it 'does not flush the cache when "flush" is set to false' do
|
||||
@element.should_not receive(:flush_namespaces_cache)
|
||||
|
||||
@element.register_namespace('bar', 'http://example.com', false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#available_namespaces' do
|
||||
|
@ -564,4 +583,29 @@ describe Oga::XML::Element do
|
|||
element.should_not be_self_closing
|
||||
end
|
||||
end
|
||||
|
||||
describe '#flush_namespaces_cache' do
|
||||
it 'flushes the namespaces cache of the current element' do
|
||||
element = described_class.new(:name => 'a')
|
||||
|
||||
element.available_namespaces.should == {}
|
||||
|
||||
element.register_namespace('foo', 'bar', false)
|
||||
|
||||
element.flush_namespaces_cache
|
||||
|
||||
element.available_namespaces.should == {
|
||||
'foo' => element.namespaces['foo']
|
||||
}
|
||||
end
|
||||
|
||||
it 'flushes the namespace cache of all child elements' do
|
||||
child = described_class.new(:name => 'b')
|
||||
parent = described_class.new(:name => 'a', :children => [child])
|
||||
|
||||
child.should_receive(:flush_namespaces_cache)
|
||||
|
||||
parent.flush_namespaces_cache
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue