Support for the XPath id() function.
This comes with the limitation that it *always* uses the "id" attribute. This is due to Oga not supporting DTD parsing/evaluation.
This commit is contained in:
parent
d351bc26cc
commit
2deb7a6d84
|
@ -99,6 +99,8 @@ module Oga
|
||||||
def on_absolute_path(ast_node, context)
|
def on_absolute_path(ast_node, context)
|
||||||
if @document.respond_to?(:root_node)
|
if @document.respond_to?(:root_node)
|
||||||
context = XML::NodeSet.new([@document.root_node])
|
context = XML::NodeSet.new([@document.root_node])
|
||||||
|
else
|
||||||
|
context = XML::NodeSet.new([@document])
|
||||||
end
|
end
|
||||||
|
|
||||||
return on_path(ast_node, context)
|
return on_path(ast_node, context)
|
||||||
|
@ -651,6 +653,46 @@ module Oga
|
||||||
return retval.length.to_f
|
return retval.length.to_f
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Processes the `id()` function call.
|
||||||
|
#
|
||||||
|
# The XPath specification states that this function's behaviour should be
|
||||||
|
# controlled by a DTD. If a DTD were to specify that the ID attribute for
|
||||||
|
# a certain element would be "foo" then this function should use said
|
||||||
|
# attribute.
|
||||||
|
#
|
||||||
|
# Oga does not support DTD parsing/evaluation and as such always uses the
|
||||||
|
# "id" attribute.
|
||||||
|
#
|
||||||
|
# This function searches the entire document for a matching node,
|
||||||
|
# regardless of the current position.
|
||||||
|
#
|
||||||
|
# @param [Oga::XML::NodeSet] context
|
||||||
|
# @param [Oga::XPath::Node] expression
|
||||||
|
# @return [Oga::XML::NodeSet]
|
||||||
|
#
|
||||||
|
def on_call_id(context, expression)
|
||||||
|
id = process(expression, context)
|
||||||
|
nodes = XML::NodeSet.new
|
||||||
|
|
||||||
|
# Based on Nokogiri's/libxml behaviour it appears that when using a node
|
||||||
|
# set the text of the set is used as the ID.
|
||||||
|
id = id.is_a?(XML::NodeSet) ? id.text : id.to_s
|
||||||
|
ids = id.split(' ')
|
||||||
|
|
||||||
|
@document.each_node do |node|
|
||||||
|
next unless node.is_a?(XML::Element)
|
||||||
|
|
||||||
|
attr = node.attribute('id')
|
||||||
|
|
||||||
|
if attr and ids.include?(attr.value)
|
||||||
|
nodes << node
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Processes an `(int)` node. This method simply returns the value as a
|
# Processes an `(int)` node. This method simply returns the value as a
|
||||||
# Float.
|
# Float.
|
||||||
|
@ -663,6 +705,17 @@ module Oga
|
||||||
return ast_node.children[0].to_f
|
return ast_node.children[0].to_f
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Processes a `(string)` node.
|
||||||
|
#
|
||||||
|
# @param [Oga::XPath::Node] ast_node
|
||||||
|
# @param [Oga::XML::NodeSet] context
|
||||||
|
# @return [String]
|
||||||
|
#
|
||||||
|
def on_string(ast_node, context)
|
||||||
|
return ast_node.children[0]
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Returns a node set containing all the child nodes of the given set of
|
# Returns a node set containing all the child nodes of the given set of
|
||||||
# nodes.
|
# nodes.
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Oga::XPath::Evaluator do
|
||||||
|
context 'id() function' do
|
||||||
|
before do
|
||||||
|
@document = parse('<root id="r1"><a id="a1"></a><a id="a2">a1</a></root>')
|
||||||
|
@first_a = @document.children[0].children[0]
|
||||||
|
@second_a = @document.children[0].children[1]
|
||||||
|
@evaluator = described_class.new(@document)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'using a single string ID' do
|
||||||
|
before do
|
||||||
|
@set = @evaluator.evaluate('id("a1")')
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like :node_set, :length => 1
|
||||||
|
|
||||||
|
example 'return the first <a> node' do
|
||||||
|
@set[0].should == @first_a
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'using multiple string IDs' do
|
||||||
|
before do
|
||||||
|
@set = @evaluator.evaluate('id("a1 a2")')
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like :node_set, :length => 2
|
||||||
|
|
||||||
|
example 'return the first <a> node' do
|
||||||
|
@set[0].should == @first_a
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'return the second <a> node' do
|
||||||
|
@set[1].should == @second_a
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'using a node set' do
|
||||||
|
before do
|
||||||
|
@set = @evaluator.evaluate('id(root/a[2])')
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like :node_set, :length => 1
|
||||||
|
|
||||||
|
example 'return the first <a> node' do
|
||||||
|
@set[0].should == @first_a
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,15 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Oga::XPath::Evaluator do
|
||||||
|
context 'string types' do
|
||||||
|
before do
|
||||||
|
document = parse('<a></a>')
|
||||||
|
evaluator = described_class.new(document)
|
||||||
|
@string = evaluator.evaluate('"foo"')
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'return the literal string' do
|
||||||
|
@string.should == 'foo'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue