Support for the XPath "=" operator.

This commit is contained in:
Yorick Peterse 2014-09-01 18:57:12 +02:00
parent 1a6c0f0d35
commit 9e5f15787d
3 changed files with 137 additions and 9 deletions

View File

@ -706,6 +706,42 @@ module Oga
return on_call_number(context, left) - on_call_number(context, right)
end
##
# Processes the `=` operator.
#
# This operator evaluates the expression on the left and right and returns
# `true` if they are equal. This operator can be used to compare strings,
# numbers and node sets. When using node sets the text of the set is
# compared instead of the nodes themselves. That is, nodes with different
# names but the same text are considered to be equal.
#
# @param [Oga::XPath::Node] ast_node
# @param [Oga::XML::NodeSet] context
# @return [TrueClass|FalseClass]
#
def on_eq(ast_node, context)
left = process(ast_node.children[0], context)
right = process(ast_node.children[1], context)
if left.is_a?(XML::NodeSet)
left = left.text
end
if right.is_a?(XML::NodeSet)
right = right.text
end
if left.is_a?(Numeric) and !right.is_a?(Numeric)
right = to_float(right)
end
if left.is_a?(String) and !right.is_a?(String)
right = to_string(right)
end
return left == right
end
##
# Delegates function calls to specific handlers.
#
@ -903,14 +939,7 @@ module Oga
if convert.respond_to?(:text)
return convert.text
else
# If we have a number that has a zero decimal (e.g. 10.0) we want to
# get rid of that decimal. For this we'll first convert the number to
# an integer.
if convert.is_a?(Float) and convert.modulo(1).zero?
convert = convert.to_i
end
return convert.to_s
return to_string(convert)
end
end
@ -950,7 +979,7 @@ module Oga
convert = current_node.text
end
return Float(convert) rescue Float::NAN
return to_float(convert)
end
##
@ -1528,6 +1557,35 @@ module Oga
return ast_node.respond_to?(:parent) && !!ast_node.parent
end
##
# Converts the given value to a float. If the value can't be converted to
# a float NaN is returned instead.
#
# @param [Mixed] value
# @return [Float]
#
def to_float(value)
return Float(value) rescue Float::NAN
end
##
# Converts the given value to a string according to the XPath string
# conversion rules.
#
# @param [Mixed] value
# @return [String]
#
def to_string(value)
# If we have a number that has a zero decimal (e.g. 10.0) we want to
# get rid of that decimal. For this we'll first convert the number to
# an integer.
if value.is_a?(Float) and value.modulo(1).zero?
value = value.to_i
end
return value.to_s
end
##
# Stores the node in the node stack, yields the block and removes the node
# from the stack.

View File

@ -154,4 +154,24 @@ describe Oga::XPath::Evaluator do
@evaluator.has_parent?(@parent).should == false
end
end
context '#to_string' do
example 'convert a float to a string' do
@evaluator.to_string(10.5).should == '10.5'
end
example 'convert a float without decimals to a string' do
@evaluator.to_string(10.0).should == '10'
end
end
context '#to_float' do
example 'convert a string to a float' do
@evaluator.to_float('10').should == 10.0
end
example "return NaN for values that can't be converted to floats" do
@evaluator.to_float('a').should be_nan
end
end
end

View File

@ -0,0 +1,50 @@
require 'spec_helper'
describe Oga::XPath::Evaluator do
context 'equal operator' do
before do
@document = parse('<root><a>10</a><b>10</b></root>')
@evaluator = described_class.new(@document)
end
example 'return true if two numbers are equal' do
@evaluator.evaluate('10 = 10').should == true
end
example 'return false if two numbers are not equal' do
@evaluator.evaluate('10 = 15').should == false
end
example 'return true if a number and a string are equal' do
@evaluator.evaluate('10 = "10"').should == true
end
example 'return true if two strings are equal' do
@evaluator.evaluate('"10" = "10"').should == true
end
example 'return true if a string and a number are equal' do
@evaluator.evaluate('"10" = 10').should == true
end
example 'return false if two strings are not equal' do
@evaluator.evaluate('"a" = "b"').should == false
end
example 'return true if two node sets are equal' do
@evaluator.evaluate('root/a = root/b').should == true
end
example 'return false if two node sets are not equal' do
@evaluator.evaluate('root/a = root/c').should == false
end
example 'return true if a node set and a number are equal' do
@evaluator.evaluate('root/a = 10').should == true
end
example 'return true if a node set and a string are equal' do
@evaluator.evaluate('root/a = "10"').should == true
end
end
end