Lazy decoding of XML/HTML entities.
Instead of decoding entities in the lexer we'll do this whenever XML::Text#text is called. This removes the overhead from the parsing phase and ensures the process is only triggered when actually needed. Note that calling #to_xml and/or the #inspect methods on a Text (or parent) instance will also trigger the entity conversion process. The new entity decoding API supports both regular entities (e.g. &) as well as codepoint based entities (both regular and hexadecimal codepoints). To allow safe read-only access to Text instances from multiple threads a mutex is used. This mutex ensures that only 1 thread can trigger the conversion process. Fixes #68
This commit is contained in:
1 parent
7409257702
commit
2ec91f130f
9 files changed
+2337
-76
No files matched your search
@@ -0,0 +1,15 @@
|
||||
# encoding: utf-8
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Oga::HTML::Entities do
|
||||
describe 'decode' do
|
||||
it 'decodes & into &' do
|
||||
described_class.decode('&').should == '&'
|
||||
end
|
||||
|
||||
it 'decodes λ into λ' do
|
||||
described_class.decode('λ').should == 'λ'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -65,6 +65,10 @@ describe Oga::XML::Entities do
|
||||
it 'decodes &&lt; into &<' do
|
||||
described_class.decode('&&lt;').should == '&<'
|
||||
end
|
||||
|
||||
it 'decodes < into <' do
|
||||
described_class.decode('<').should == '<'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'encode' do
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Oga::XML::Lexer do
|
||||
describe 'converting XML entities in text tokens' do
|
||||
it 'converts & into &' do
|
||||
lex('&').should == [[:T_TEXT, '&', 1]]
|
||||
end
|
||||
|
||||
it 'converts < into <' do
|
||||
lex('<').should == [[:T_TEXT, '<', 1]]
|
||||
end
|
||||
|
||||
it 'converts > into >' do
|
||||
lex('>').should == [[:T_TEXT, '>', 1]]
|
||||
end
|
||||
end
|
||||
|
||||
describe 'converting XML entities in string tokens' do
|
||||
it 'converts & into &' do
|
||||
lex('<foo class="&" />').should == [
|
||||
[:T_ELEM_START, nil, 1],
|
||||
[:T_ELEM_NAME, 'foo', 1],
|
||||
[:T_ATTR, 'class', 1],
|
||||
[:T_STRING_DQUOTE, nil, 1],
|
||||
[:T_STRING_BODY, '&', 1],
|
||||
[:T_STRING_DQUOTE, nil, 1],
|
||||
[:T_ELEM_END, nil, 1]
|
||||
]
|
||||
end
|
||||
|
||||
it 'converts < into <' do
|
||||
lex('<foo class="<" />').should == [
|
||||
[:T_ELEM_START, nil, 1],
|
||||
[:T_ELEM_NAME, 'foo', 1],
|
||||
[:T_ATTR, 'class', 1],
|
||||
[:T_STRING_DQUOTE, nil, 1],
|
||||
[:T_STRING_BODY, '<', 1],
|
||||
[:T_STRING_DQUOTE, nil, 1],
|
||||
[:T_ELEM_END, nil, 1]
|
||||
]
|
||||
end
|
||||
|
||||
it 'converts > into >' do
|
||||
lex('<foo class=">" />').should == [
|
||||
[:T_ELEM_START, nil, 1],
|
||||
[:T_ELEM_NAME, 'foo', 1],
|
||||
[:T_ATTR, 'class', 1],
|
||||
[:T_STRING_DQUOTE, nil, 1],
|
||||
[:T_STRING_BODY, '>', 1],
|
||||
[:T_STRING_DQUOTE, nil, 1],
|
||||
[:T_ELEM_END, nil, 1]
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -14,6 +14,79 @@ describe Oga::XML::Text do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#text' do
|
||||
describe 'with XML entities' do
|
||||
it 'converts & to &' do
|
||||
described_class.new(:text => '&').text.should == '&'
|
||||
end
|
||||
|
||||
it 'converts < to <' do
|
||||
described_class.new(:text => '<').text.should == '<'
|
||||
end
|
||||
|
||||
it 'converts > to >' do
|
||||
described_class.new(:text => '>').text.should == '>'
|
||||
end
|
||||
|
||||
it 'caches the converted text' do
|
||||
node = described_class.new(:text => '&')
|
||||
|
||||
Oga::XML::Entities.should_receive(:decode).once.and_call_original
|
||||
|
||||
node.text.should == '&'
|
||||
node.text.should == '&'
|
||||
end
|
||||
|
||||
it 'converts new text set using text=' do
|
||||
node = described_class.new(:text => '&')
|
||||
|
||||
node.text.should == '&'
|
||||
|
||||
node.text = '<'
|
||||
|
||||
node.text.should == '<'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with HTML entities' do
|
||||
before do
|
||||
@document = Oga::XML::Document.new(:type => :html)
|
||||
end
|
||||
|
||||
it 'converts & to &' do
|
||||
node = described_class.new(:text => '&')
|
||||
|
||||
@document.children << node
|
||||
|
||||
node.text.should == '&'
|
||||
end
|
||||
|
||||
it 'converts < to <' do
|
||||
node = described_class.new(:text => '<')
|
||||
|
||||
@document.children << node
|
||||
|
||||
node.text.should == '<'
|
||||
end
|
||||
|
||||
it 'converts > to >' do
|
||||
node = described_class.new(:text => '>')
|
||||
|
||||
@document.children << node
|
||||
|
||||
node.text.should == '>'
|
||||
end
|
||||
|
||||
it 'converts into a space' do
|
||||
node = described_class.new(:text => ' ')
|
||||
|
||||
@document.children << node
|
||||
|
||||
node.text.should == [160].pack('U')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_xml' do
|
||||
it 'generates the corresponding XML' do
|
||||
node = described_class.new(:text => 'foo')
|
||||
|
||||
Reference in new issue
Block a user