Support for the XPath "=" operator.
This commit is contained in:
parent
1a6c0f0d35
commit
9e5f15787d
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue