-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixed SAX parsing of XML attributes.
This was utterly broken, mainly due to me overlooking it. There are now 2 new callbacks to handle this properly: * on_attribute: to handle a single attribute/value pair * on_attributes: to handle a collection of attributes (as returned by on_attribute) By default on_attribut returns a Hash, on_attributes in turn merges all attribute hashes into a single one. This ensures that on_element _actually_ receives the attributes as a Hash, instead of an Array with random nil/XML::Attribute values.
- Loading branch information
Yorick Peterse
committed
Mar 21, 2015
1 parent
605d565
commit d8b9725
Showing
3 changed files
with
163 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,114 @@ | ||
require 'spec_helper' | ||
|
||
describe Oga::XML::SaxParser do | ||
before do | ||
@handler = Class.new do | ||
attr_reader :name, :after_namespace, :after_name | ||
describe '#parse' do | ||
before do | ||
@handler = Class.new do | ||
attr_reader :name, :attrs, :after_namespace, :after_name | ||
|
||
def on_element(namespace, name, attrs = {}) | ||
@name = name | ||
end | ||
def on_element(namespace, name, attrs = {}) | ||
@name = name | ||
@attrs = attrs | ||
end | ||
|
||
def after_element(namespace, name) | ||
@after_namespace = namespace | ||
@after_name = name | ||
def after_element(namespace, name) | ||
@after_namespace = namespace | ||
@after_name = name | ||
end | ||
end | ||
end | ||
end | ||
|
||
it 'ignores return values of callback methods' do | ||
parser = described_class.new(@handler.new, 'foo') | ||
it 'ignores return values of callback methods' do | ||
parser = described_class.new(@handler.new, 'foo') | ||
|
||
parser.parse.should be_nil | ||
end | ||
parser.parse.should be_nil | ||
end | ||
|
||
it 'uses custom callback methods if defined' do | ||
handler = @handler.new | ||
parser = described_class.new(handler, '<foo />') | ||
|
||
parser.parse | ||
|
||
handler.name.should == 'foo' | ||
end | ||
|
||
it 'always passes element names to after_element' do | ||
handler = @handler.new | ||
parser = described_class.new(handler, '<namespace:foo />') | ||
|
||
parser.parse | ||
|
||
handler.after_name.should == 'foo' | ||
handler.after_namespace.should == 'namespace' | ||
end | ||
|
||
it 'ignores callbacks that are not defined in the handler' do | ||
parser = described_class.new(@handler.new, '<!--foo-->') | ||
|
||
# This would raise if undefined callbacks were _not_ ignored. | ||
lambda { parser.parse }.should_not raise_error | ||
end | ||
|
||
it 'uses custom callback methods if defined' do | ||
handler = @handler.new | ||
parser = described_class.new(handler, '<foo />') | ||
it 'passes the attributes to the on_element callback' do | ||
handler = @handler.new | ||
parser = described_class.new(handler, '<a b="10" x:c="20" />') | ||
|
||
parser.parse | ||
parser.parse | ||
|
||
handler.name.should == 'foo' | ||
handler.attrs.should == {'b' => '10', 'x:c' => '20'} | ||
end | ||
end | ||
|
||
it 'always passes element names to after_element' do | ||
handler = @handler.new | ||
parser = described_class.new(handler, '<namespace:foo />') | ||
describe '#on_attribute' do | ||
before do | ||
@handler_without = Class.new.new | ||
|
||
@handler_with = Class.new do | ||
def on_attribute(name, ns = nil, value = nil) | ||
return {name.upcase => value} | ||
end | ||
end.new | ||
end | ||
|
||
parser.parse | ||
it 'returns a default Hash if no custom callback exists' do | ||
parser = described_class.new(@handler_without, '<a x:foo="bar" />') | ||
hash = parser.on_attribute('foo', 'x', 'bar') | ||
|
||
handler.after_name.should == 'foo' | ||
handler.after_namespace.should == 'namespace' | ||
hash.should == {'x:foo' => 'bar'} | ||
end | ||
|
||
it 'returns the return value of a custom callback' do | ||
parser = described_class.new(@handler_with, nil) | ||
hash = parser.on_attribute('foo', 'x', 'bar') | ||
|
||
hash.should == {'FOO' => 'bar'} | ||
end | ||
end | ||
|
||
it 'ignores callbacks that are not defined in the handler' do | ||
parser = described_class.new(@handler.new, '<!--foo-->') | ||
describe '#on_attributes' do | ||
before do | ||
@handler_without = Class.new.new | ||
|
||
# This would raise if undefined callbacks were _not_ ignored. | ||
lambda { parser.parse }.should_not raise_error | ||
@handler_with = Class.new do | ||
def on_attributes(attrs) | ||
return %w{Alice Bob} # these two again | ||
end | ||
end.new | ||
end | ||
|
||
it 'merges all attributes into a Hash if no callback is defined' do | ||
parser = described_class.new(@handler_without, nil) | ||
hash = parser.on_attributes([{'a' => 'b'}, {'c' => 'd'}]) | ||
|
||
hash.should == {'a' => 'b', 'c' => 'd'} | ||
end | ||
|
||
it 'returns the return value of a custom callback' do | ||
parser = described_class.new(@handler_with, nil) | ||
retval = parser.on_attributes([{'a' => 'b'}, {'c' => 'd'}]) | ||
|
||
retval.should == %w{Alice Bob} | ||
end | ||
end | ||
end |