nokogiri icon indicating copy to clipboard operation
nokogiri copied to clipboard

Add global XPath functions handler

Open jonathanhefner opened this issue 5 years ago • 8 comments

What problem is this PR intended to solve?

Allow global definition of XPath functions, e.g.

module MyFunctions
  def regex(node_set, pattern)
    node_set.find_all { |node| node['some_attribute'] =~ /#{pattern}/ }
  end
end

Nokogiri::XML::XPathFunctions.include(MyFunctions)

node.search('.//title[regex(., "\w+")]', 'div.employee:regex("[0-9]+")')

This is a revival of #464 with a different approach. Although that PR is shown as merged, I think that's a GitHub bug. The original implementation was mbklein/nokogiri@8fb3da3 which has not been merged.

Have you included adequate test coverage?

I think so. I refrained from adding tests which would overlap with existing tests, but I would be happy to add more, if you have suggestions.

Does this change affect the C or the Java implementations?

~No.~ Apparently, yes. The Java implementation injects XML namespaces into XPath queries based on the Ruby methods the function handler defines. Previously, that list of methods was derived from the function handler class, which omits methods delegated by SimpleDelegator. Now the list of methods is derived directly from the function handler object.

jonathanhefner avatar Apr 17 '19 16:04 jonathanhefner

Code Climate has analyzed commit c3376dd8 and detected 0 issues on this pull request.

The test coverage on the diff in this pull request is 100.0% (80% is the threshold).

This pull request will bring the total coverage in the repository to 93.5% (0.0% change).

View more on Code Climate.

codeclimate[bot] avatar Apr 17 '19 17:04 codeclimate[bot]

Thank you for submitting this PR! I'll take a look as soon as I can, this is a super-busy week for me.

flavorjones avatar Apr 18 '19 14:04 flavorjones

Sorry this has gone so long without review; I'm tagging it for v1.11.0 which I'm hoping to release at the end of 2019, so will review in that timeframe.

flavorjones avatar Dec 04 '19 22:12 flavorjones

I've rebased this onto current main.

flavorjones avatar Aug 28 '22 16:08 flavorjones

I've added a require "delegate" which was needed to not break downstream CI, e.g.

  • https://github.com/sparklemotion/nokogiri/runs/8058997083?check_suite_focus=true
  • https://github.com/sparklemotion/nokogiri/runs/8058997115?check_suite_focus=true

flavorjones avatar Aug 28 '22 18:08 flavorjones

OK, the method lookup in the XmlXpathContext.java:evalute() block doesn't work properly. I spent a few minutes exploring how to get the public methods out of the block but couldn't quite figure it out.

Here's the failing test that needs to be added:

      def test_search_with_xpath_query_uses_global_custom_selectors_with_arguments_without_namespace
        skip("Testing fallback behavior in JRuby") unless Nokogiri.jruby?

        XPathFunctions.class_eval do
          def our_filter(*args)
            my_filter(*args)
          end
        end

        set = @xml.search('//employee/address[our_filter(., "domestic", "Yes")]', @handler)
        refute_empty(set)
        set.each do |node|
          assert_equal("Yes", node["domestic"])
        end
      end

I also want to point out that the intention is to deprecate use of custom XPath functions without the nokogiri: prefix -- see #2147. We might reasonably wait until that lands and then take another look at this.

flavorjones avatar Aug 29 '22 13:08 flavorjones

@flavorjones Oh wow, I forgot about this one! I am fine with waiting until after #2147 — no rush. Still, I was curious to see if I could get it working, so I pushed a commit to fix any failing tests, including the one you mentioned (test_search_with_xpath_query_uses_global_custom_selectors_with_arguments_without_namespace).

jonathanhefner avatar Aug 30 '22 21:08 jonathanhefner

Thank you! I'll take another look

flavorjones avatar Aug 31 '22 12:08 flavorjones