ruby-changes:51071
From: kou <ko1@a...>
Date: Sat, 28 Apr 2018 10:36:24 +0900 (JST)
Subject: [ruby-changes:51071] kou:r63278 (trunk): rexml: disable XPath 1.0 compatible "#{ELEMENT_NAME}" processing by default
kou 2018-04-28 10:36:18 +0900 (Sat, 28 Apr 2018) New Revision: 63278 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=63278 Log: rexml: disable XPath 1.0 compatible "#{ELEMENT_NAME}" processing by default It breaks backward compatibility than I thought. So it's disabled by default. It means that REXML's XPath processor isn't compatible with XPath 1.0. But it will be acceptable for users. We can enable it by specifying "strict: true" to REXML::XPathParser.new explicitly. * lib/rexml/xpath.rb, lib/rexml/xpath_parser.rb: Accept "strict: true" option. * test/rexml/test_contrib.rb, test/rexml/xpath/test_base.rb: Use not XPath 1.0 compatible behavior. * test/rexml/test_jaxen.rb: Use XPath 1.0 compatible behavior. * test/rss/test_1.0.rb, test/rss/test_dublincore.rb, spec/ruby/library/rexml/element/namespace_spec.rb, spec/ruby/library/rexml/element/namespaces_spec.rb, spec/ruby/library/rexml/element/prefixes_spec.rb: Enable again. Modified files: trunk/lib/rexml/xpath.rb trunk/lib/rexml/xpath_parser.rb trunk/spec/ruby/library/rexml/element/namespace_spec.rb trunk/spec/ruby/library/rexml/element/namespaces_spec.rb trunk/spec/ruby/library/rexml/element/prefixes_spec.rb trunk/test/rexml/test_contrib.rb trunk/test/rexml/test_jaxen.rb trunk/test/rexml/xpath/test_base.rb trunk/test/rss/test_1.0.rb trunk/test/rss/test_dublincore.rb Index: test/rexml/xpath/test_base.rb =================================================================== --- test/rexml/xpath/test_base.rb (revision 63277) +++ test/rexml/xpath/test_base.rb (revision 63278) @@ -880,10 +880,12 @@ module REXMLTests https://github.com/ruby/ruby/blob/trunk/test/rexml/xpath/test_base.rb#L880 <tada xmlns=''>xb</tada> </tag1> XML - x = d.root - num = 0 - x.each_element('tada') { num += 1 } - assert_equal(1, num) + actual = [] + d.root.each_element('tada') do |element| + actual << element.to_s + end + assert_equal(["<tada>xa</tada>", "<tada xmlns=''>xb</tada>"], + actual) end def test_ticket_39 Index: test/rexml/test_contrib.rb =================================================================== --- test/rexml/test_contrib.rb (revision 63277) +++ test/rexml/test_contrib.rb (revision 63278) @@ -451,7 +451,7 @@ EOL https://github.com/ruby/ruby/blob/trunk/test/rexml/test_contrib.rb#L451 end def test_external_entity - xp = '//*:channel/*:title' + xp = '//channel/title' %w{working.rss broken.rss}.each do |path| File.open(File.join(fixture_path(path))) do |file| Index: test/rexml/test_jaxen.rb =================================================================== --- test/rexml/test_jaxen.rb (revision 63277) +++ test/rexml/test_jaxen.rb (revision 63278) @@ -84,7 +84,7 @@ module REXMLTests https://github.com/ruby/ruby/blob/trunk/test/rexml/test_jaxen.rb#L84 def process_value_of(context, variables, namespaces, value_of) expected = value_of.text xpath = value_of.attributes["select"] - matched = XPath.match(context, xpath, namespaces, variables) + matched = XPath.match(context, xpath, namespaces, variables, strict: true) message = user_message(context, xpath, matched) assert_equal(expected || "", @@ -95,7 +95,7 @@ module REXMLTests https://github.com/ruby/ruby/blob/trunk/test/rexml/test_jaxen.rb#L95 # processes a tests/document/context/test node ( where @exception is false or doesn't exist ) def process_nominal_test(context, variables, namespaces, test) xpath = test.attributes["select"] - matched = XPath.match(context, xpath, namespaces, variables) + matched = XPath.match(context, xpath, namespaces, variables, strict: true) # might be a test with no count attribute, but nested valueOf elements expected = test.attributes["count"] if expected @@ -113,7 +113,7 @@ module REXMLTests https://github.com/ruby/ruby/blob/trunk/test/rexml/test_jaxen.rb#L113 def process_exceptional_test(context, variables, namespaces, test) xpath = test.attributes["select"] assert_raise(REXML::ParseException) do - XPath.match(context, xpath, namespaces, variables) + XPath.match(context, xpath, namespaces, variables, strict: true) end end Index: test/rss/test_dublincore.rb =================================================================== --- test/rss/test_dublincore.rb (revision 63277) +++ test/rss/test_dublincore.rb (revision 63278) @@ -103,8 +103,6 @@ EOR https://github.com/ruby/ruby/blob/trunk/test/rss/test_dublincore.rb#L103 end def test_to_s - skip # FIXME - assert_dc_to_s(@rss10_source, @rss10_parents, false) assert_dc_to_s(@rss10_source, @rss10_parents, true) Index: test/rss/test_1.0.rb =================================================================== --- test/rss/test_1.0.rb (revision 63277) +++ test/rss/test_1.0.rb (revision 63278) @@ -54,8 +54,6 @@ module RSS https://github.com/ruby/ruby/blob/trunk/test/rss/test_1.0.rb#L54 end def test_channel - skip # FIXME - about = "http://hoge.com" resource = "http://hoge.com/hoge.png" @@ -207,8 +205,6 @@ EOR https://github.com/ruby/ruby/blob/trunk/test/rss/test_1.0.rb#L205 end def test_image - skip # FIXME - about = "http://hoge.com" h = { 'title' => "fugafuga", @@ -234,8 +230,6 @@ EOR https://github.com/ruby/ruby/blob/trunk/test/rss/test_1.0.rb#L230 end def test_item - skip # FIXME - about = "http://hoge.com" h = { 'title' => "fugafuga", @@ -261,8 +255,6 @@ EOR https://github.com/ruby/ruby/blob/trunk/test/rss/test_1.0.rb#L255 end def test_textinput - skip # FIXME - about = "http://hoge.com" h = { 'title' => "fugafuga", Index: lib/rexml/xpath_parser.rb =================================================================== --- lib/rexml/xpath_parser.rb (revision 63277) +++ lib/rexml/xpath_parser.rb (revision 63278) @@ -47,11 +47,12 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/xpath_parser.rb#L47 include XMLTokens LITERAL = /^'([^']*)'|^"([^"]*)"/u - def initialize( ) + def initialize(strict: false) @parser = REXML::Parsers::XPathParser.new @namespaces = nil @variables = {} @nest = 0 + @strict = strict end def namespaces=( namespaces={} ) @@ -139,7 +140,9 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/xpath_parser.rb#L140 end private - + def strict? + @strict + end # Returns a String namespace for a node, given a prefix # The rules are: @@ -474,7 +477,13 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/xpath_parser.rb#L477 if prefix.nil? raw_node.name == name elsif prefix.empty? - raw_node.name == name and raw_node.namespace == "" + if strict? + raw_node.name == name and raw_node.namespace == "" + else + # FIXME: This DOUBLES the time XPath searches take + ns = get_namespace(raw_node, prefix) + raw_node.name == name and raw_node.namespace == ns + end else # FIXME: This DOUBLES the time XPath searches take ns = get_namespace(raw_node, prefix) Index: lib/rexml/xpath.rb =================================================================== --- lib/rexml/xpath.rb (revision 63277) +++ lib/rexml/xpath.rb (revision 63278) @@ -28,10 +28,10 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/xpath.rb#L28 # XPath.first( doc, "//b"} ) # XPath.first( node, "a/x:b", { "x"=>"http://doofus" } ) # XPath.first( node, '/book/publisher/text()=$publisher', {}, {"publisher"=>"O'Reilly"}) - def XPath::first element, path=nil, namespaces=nil, variables={} + def XPath::first(element, path=nil, namespaces=nil, variables={}, options={}) raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash) raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash) - parser = XPathParser.new + parser = XPathParser.new(**options) parser.namespaces = namespaces parser.variables = variables path = "*" unless path @@ -57,10 +57,10 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/xpath.rb#L57 # XPath.each( node, 'ancestor::x' ) { |el| ... } # XPath.each( node, '/book/publisher/text()=$publisher', {}, {"publisher"=>"O'Reilly"}) \ # {|el| ... } - def XPath::each element, path=nil, namespaces=nil, variables={}, &block + def XPath::each(element, path=nil, namespaces=nil, variables={}, options={}, &block) raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash) raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash) - parser = XPathParser.new + parser = XPathParser.new(**options) parser.namespaces = namespaces parser.variables = variables path = "*" unless path @@ -69,8 +69,8 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/xpath.rb#L69 end # Returns an array of nodes matching a given XPath. - def XPath::match element, path=nil, namespaces=nil, variables={} - parser = XPathParser.new + def XPath::match(element, path=nil, namespaces=nil, variables={}, options={}) + parser = XPathParser.new(**options) parser.namespaces = namespaces parser.variables = variables path = "*" unless path Index: spec/ruby/library/rexml/element/prefixes_spec.rb =================================================================== --- spec/ruby/library/rexml/element/prefixes_spec.rb (revision 63277) +++ spec/ruby/library/rexml/element/prefixes_spec.rb (revision 63278) @@ -7,7 +7,6 @@ describe "REXML::Element#prefixes" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/library/rexml/element/prefixes_spec.rb#L7 @elem = doc.elements["//c"] end -=begin it "returns an array of the prefixes of the namespaces" do @elem.prefixes.should == ["y", "z"] end @@ -15,7 +14,6 @@ describe "REXML::Element#prefixes" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/library/rexml/element/prefixes_spec.rb#L14 it "does not include the default namespace" do @elem.prefixes.include?("xmlns").should == false end -=end it "returns an empty array if no namespace was defined" do doc = REXML::Document.new "<root><something/></root>" Index: spec/ruby/library/rexml/element/namespaces_spec.rb =================================================================== --- spec/ruby/library/rexml/element/namespaces_spec.rb (revision 63277) +++ spec/ruby/library/rexml/element/namespaces_spec.rb (revision 63278) @@ -7,7 +7,6 @@ describe "REXML::Element#namespaces" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/library/rexml/element/namespaces_spec.rb#L7 @elem = doc.elements["//c"] end -=begin it "returns a hash of the namespaces" do ns = {"y"=>"2", "z"=>"3", "xmlns"=>"1"} @elem.namespaces.keys.sort.should == ns.keys.sort @@ -29,5 +28,4 @@ describe "REXML::Element#namespaces" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/library/rexml/element/namespaces_spec.rb#L28 values = ["2", "3", "1"] @elem.namespaces.values.sort.should == values.sort end -=end end Index: spec/ruby/library/rexml/element/namespace_spec.rb =================================================================== --- spec/ruby/library/rexml/element/namespace_spec.rb (revision 63277) +++ spec/ruby/library/rexml/element/namespace_spec.rb (revision 63278) @@ -1,8 +1,6 @@ https://github.com/ruby/ruby/blob/trunk/spec/ruby/library/rexml/element/namespace_spec.rb#L1 require 'rexml/document' require_relative '../../../spec_helper' -=begin -# FIXME describe "REXML::Element#namespace" do before :each do @doc = REXML::Document.new("<a xmlns='1' xmlns:y='2'><b/><c xmlns:z='3'/></a>") @@ -27,4 +25,3 @@ describe "REXML::Element#namespace" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/library/rexml/element/namespace_spec.rb#L25 @elem.namespace("z").should be_nil end end -=end -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/