From fc1d9776f3a5b8fb816ee1810d48985cfd5ede54 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 5 Aug 2014 10:16:37 +0200 Subject: [PATCH] Basic support for the XPath "preceding" axis. --- lib/oga/xpath/evaluator.rb | 26 +++++++++ .../xpath/evaluator/axes/preceding_spec.rb | 53 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 spec/oga/xpath/evaluator/axes/preceding_spec.rb diff --git a/lib/oga/xpath/evaluator.rb b/lib/oga/xpath/evaluator.rb index a112d04..9d4df4b 100644 --- a/lib/oga/xpath/evaluator.rb +++ b/lib/oga/xpath/evaluator.rb @@ -328,6 +328,32 @@ module Oga return nodes end + ## + # Evaluates the `preceding` axis. + # + # @param [Oga::XPath::Node] ast_node + # @param [Oga::XML::NodeSet] context + # @return [Oga::XML::NodeSet] + # + def on_axis_preceding(ast_node, context) + nodes = XML::NodeSet.new + + context.each do |context_node| + check = true + + @document.each_node do |doc_node| + # Test everything *until* we hit the current context node. + if doc_node == context_node + break + elsif node_matches?(doc_node, ast_node) + nodes << doc_node + end + end + end + + return nodes + end + ## # Returns a node set containing all the child nodes of the given set of # nodes. diff --git a/spec/oga/xpath/evaluator/axes/preceding_spec.rb b/spec/oga/xpath/evaluator/axes/preceding_spec.rb new file mode 100644 index 0000000..a87d9f4 --- /dev/null +++ b/spec/oga/xpath/evaluator/axes/preceding_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe Oga::XPath::Evaluator do + context 'preceding axis' do + before do + @document = parse(<<-EOF.strip.gsub(/\s+/m, '')) + + + + + + + + + + EOF + + @first_foo = @document.children[0].children[0] + @first_bar = @first_foo.children[0] + @first_baz = @first_foo.children[1] + @second_baz = @first_baz.children[0] + @evaluator = described_class.new(@document) + end + + context 'matching nodes in the current context' do + before do + @set = @evaluator.evaluate('root/foo/baz/preceding::bar') + end + + it_behaves_like :node_set, :length => 1 + + example 'return the first node' do + @set[0].should == @first_bar + end + end + + context 'matching nodes in other contexts' do + before do + @set = @evaluator.evaluate('root/baz/preceding::baz') + end + + it_behaves_like :node_set, :length => 2 + + example 'return the first node' do + @set[0].should == @first_baz + end + + example 'return the second node' do + @set[1].should == @second_baz + end + end + end +end