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)
|
return on_call_number(context, left) - on_call_number(context, right)
|
||||||
end
|
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.
|
# Delegates function calls to specific handlers.
|
||||||
#
|
#
|
||||||
|
@ -903,14 +939,7 @@ module Oga
|
||||||
if convert.respond_to?(:text)
|
if convert.respond_to?(:text)
|
||||||
return convert.text
|
return convert.text
|
||||||
else
|
else
|
||||||
# If we have a number that has a zero decimal (e.g. 10.0) we want to
|
return to_string(convert)
|
||||||
# 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
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -950,7 +979,7 @@ module Oga
|
||||||
convert = current_node.text
|
convert = current_node.text
|
||||||
end
|
end
|
||||||
|
|
||||||
return Float(convert) rescue Float::NAN
|
return to_float(convert)
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -1528,6 +1557,35 @@ module Oga
|
||||||
return ast_node.respond_to?(:parent) && !!ast_node.parent
|
return ast_node.respond_to?(:parent) && !!ast_node.parent
|
||||||
end
|
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
|
# Stores the node in the node stack, yields the block and removes the node
|
||||||
# from the stack.
|
# from the stack.
|
||||||
|
|
|
@ -154,4 +154,24 @@ describe Oga::XPath::Evaluator do
|
||||||
@evaluator.has_parent?(@parent).should == false
|
@evaluator.has_parent?(@parent).should == false
|
||||||
end
|
end
|
||||||
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
|
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