Skip to content

Commit

Permalink
feat: Add global XPath functions handler
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanhefner authored and flavorjones committed Aug 28, 2022
1 parent dbb228a commit abc9765
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 1 deletion.
11 changes: 10 additions & 1 deletion ext/java/nokogiri/XmlXpathContext.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package nokogiri;

import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand All @@ -13,8 +14,11 @@
import org.apache.xpath.jaxp.JAXPVariableStack;
import org.apache.xpath.objects.XObject;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBasicObject;
import org.jruby.RubyClass;
import org.jruby.RubyObject;
import org.jruby.RubySymbol;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.RaiseException;
Expand Down Expand Up @@ -114,7 +118,12 @@ public class XmlXpathContext extends RubyObject
int jchar = 0;

// Find the methods on the handler object
Set<String> methodNames = handler.getMetaClass().getMethods().keySet();
List<RubySymbol> methodNames = (RubyArray)((RubyBasicObject)handler).send(
context,
context.getRuntime().newSymbol("public_methods"),
context.getRuntime().newBoolean(false),
null
);

// Find the function calls in the xpath query
Matcher xpathFunctionCalls = XPathFunctionCaptureRE.matcher(query);
Expand Down
1 change: 1 addition & 0 deletions lib/nokogiri/xml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def fragment(string, options = ParseOptions::DEFAULT_XML, &block)
require_relative "xml/syntax_error"
require_relative "xml/xpath"
require_relative "xml/xpath_context"
require_relative "xml/xpath_functions"
require_relative "xml/builder"
require_relative "xml/reader"
require_relative "xml/notation"
Expand Down
1 change: 1 addition & 0 deletions lib/nokogiri/xml/searchable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ def extract_params(params) # :nodoc:
![Hash, String, Symbol].include?(param.class)
end
params -= [handler] if handler
handler = XPathFunctions.wrap(handler)

hashes = []
while Hash === params.last || params.last.nil?
Expand Down
19 changes: 19 additions & 0 deletions lib/nokogiri/xml/xpath_functions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

require "delegate"

module Nokogiri
module XML
class XPathFunctions < SimpleDelegator
class << self
def wrap(handler)
if handler.nil?
@wrap_nil ||= new(Object.new)
else
new(handler)
end
end
end
end
end
end
1 change: 1 addition & 0 deletions nokogiri.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ Gem::Specification.new do |spec|
"lib/nokogiri/xml/xpath.rb",
"lib/nokogiri/xml/xpath/syntax_error.rb",
"lib/nokogiri/xml/xpath_context.rb",
"lib/nokogiri/xml/xpath_functions.rb",
"lib/nokogiri/xslt.rb",
"lib/nokogiri/xslt/stylesheet.rb",
"lib/xsd/xmlparser/nokogiri.rb",
Expand Down
18 changes: 18 additions & 0 deletions test/xml/test_xpath.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,24 @@ def test_search_with_xpath_query_uses_custom_selectors_with_arguments
end
end

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

set = if Nokogiri.uses_libxml?
@xml.search('//employee/address[our_filter(., "domestic", "Yes")]', @handler)
else
@xml.search('//employee/address[nokogiri:our_filter(., "domestic", "Yes")]', @ns, @handler)
end
refute_empty(set)
set.each do |node|
assert_equal("Yes", node["domestic"])
end
end

def test_pass_self_to_function
set = if Nokogiri.uses_libxml?
@xml.xpath('//employee/address[my_filter(., "domestic", "Yes")]', @handler)
Expand Down

0 comments on commit abc9765

Please sign in to comment.