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