Support for the "following-sibling" axis.
This also comes with some small cleanups regarding XPath::Evaluator#node_matches?. This change removes the need to, every time, also use can_match_node?() to prevent NoMethodError errors from popping up.
This commit is contained in:
parent
57c0f4b35e
commit
a1f80b4995
|
|
@ -101,9 +101,7 @@ module Oga
|
||||||
nodes = XML::NodeSet.new
|
nodes = XML::NodeSet.new
|
||||||
|
|
||||||
context.each do |xml_node|
|
context.each do |xml_node|
|
||||||
if can_match_node?(xml_node) and node_matches?(xml_node, ast_node)
|
nodes << xml_node if node_matches?(xml_node, ast_node)
|
||||||
nodes << xml_node
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return nodes
|
return nodes
|
||||||
|
|
@ -144,7 +142,7 @@ module Oga
|
||||||
while has_parent?(xml_node)
|
while has_parent?(xml_node)
|
||||||
xml_node = xml_node.parent
|
xml_node = xml_node.parent
|
||||||
|
|
||||||
if can_match_node?(xml_node) and node_matches?(xml_node, ast_node)
|
if node_matches?(xml_node, ast_node)
|
||||||
nodes << xml_node
|
nodes << xml_node
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
@ -265,8 +263,43 @@ module Oga
|
||||||
|
|
||||||
next unless check
|
next unless check
|
||||||
|
|
||||||
if can_match_node?(doc_node) and node_matches?(doc_node, ast_node)
|
nodes << doc_node if node_matches?(doc_node, ast_node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Evaluates the `following-sibling` axis.
|
||||||
|
#
|
||||||
|
# @param [Oga::XPath::Node] ast_node
|
||||||
|
# @param [Oga::XML::NodeSet] context
|
||||||
|
# @return [Oga::XML::NodeSet]
|
||||||
|
#
|
||||||
|
def on_axis_following_sibling(ast_node, context)
|
||||||
|
nodes = XML::NodeSet.new
|
||||||
|
|
||||||
|
context.each do |context_node|
|
||||||
|
check = false
|
||||||
|
parent = context_node.respond_to?(:parent) ? context_node.parent : nil
|
||||||
|
|
||||||
|
@document.each_node do |doc_node|
|
||||||
|
# Skip child nodes of the current context node, compare all
|
||||||
|
# following nodes.
|
||||||
|
if doc_node == context_node
|
||||||
|
check = true
|
||||||
|
throw :skip_children
|
||||||
|
end
|
||||||
|
|
||||||
|
if !check or parent != doc_node.parent
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
if node_matches?(doc_node, ast_node)
|
||||||
nodes << doc_node
|
nodes << doc_node
|
||||||
|
|
||||||
|
throw :skip_children
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -307,6 +340,8 @@ module Oga
|
||||||
# @return [Oga::XML::NodeSet]
|
# @return [Oga::XML::NodeSet]
|
||||||
#
|
#
|
||||||
def node_matches?(xml_node, ast_node)
|
def node_matches?(xml_node, ast_node)
|
||||||
|
return false unless can_match_node?(xml_node)
|
||||||
|
|
||||||
ns, name = *ast_node
|
ns, name = *ast_node
|
||||||
|
|
||||||
name_matches = xml_node.name == name || name == '*'
|
name_matches = xml_node.name == name || name == '*'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Oga::XPath::Evaluator do
|
||||||
|
context 'following-sibling axis' do
|
||||||
|
before do
|
||||||
|
# Strip whitespace so it's easier to retrieve/compare elements.
|
||||||
|
@document = parse(<<-EOF.strip.gsub(/\s+/m, ''))
|
||||||
|
<root>
|
||||||
|
<foo>
|
||||||
|
<bar></bar>
|
||||||
|
<baz>
|
||||||
|
<baz></baz>
|
||||||
|
</baz>
|
||||||
|
</foo>
|
||||||
|
<baz></baz>
|
||||||
|
</root>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
@first_baz = @document.children[0].children[0].children[1]
|
||||||
|
@second_baz = @first_baz.children[0]
|
||||||
|
@third_baz = @document.children[0].children[1]
|
||||||
|
@evaluator = described_class.new(@document)
|
||||||
|
end
|
||||||
|
|
||||||
|
# This should return an empty set since the document doesn't have any
|
||||||
|
# following nodes.
|
||||||
|
context 'using a document as the root' do
|
||||||
|
before do
|
||||||
|
@set = @evaluator.evaluate('following-sibling::foo')
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like :empty_node_set
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'matching nodes in the current context' do
|
||||||
|
before do
|
||||||
|
@set = @evaluator.evaluate('root/foo/following-sibling::baz')
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like :node_set, :length => 1
|
||||||
|
|
||||||
|
example 'return the third <baz> node' do
|
||||||
|
@set[0].should == @third_baz
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'matching nodes in other contexts' do
|
||||||
|
before do
|
||||||
|
@set = @evaluator.evaluate('root/foo/bar/following-sibling::baz')
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like :node_set, :length => 1
|
||||||
|
|
||||||
|
example 'return the first <baz> node' do
|
||||||
|
@set[0].should == @first_baz
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -61,6 +61,12 @@ describe Oga::XPath::Evaluator do
|
||||||
example 'return true if a node is matched without having a namespace' do
|
example 'return true if a node is matched without having a namespace' do
|
||||||
@evaluator.node_matches?(@name_node, s(:test, '*', 'a')).should == true
|
@evaluator.node_matches?(@name_node, s(:test, '*', 'a')).should == true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
example 'return false when trying to match an XML::Text instance' do
|
||||||
|
text = Oga::XML::Text.new(:text => 'Foobar')
|
||||||
|
|
||||||
|
@evaluator.node_matches?(text, s(:test, nil, 'a')).should == false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context '#can_match_node?' do
|
context '#can_match_node?' do
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue