Skip to content

Commit

Permalink
Add global XPath functions handler
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanhefner committed Apr 18, 2019
1 parent 93d1a80 commit c3376dd
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 3 deletions.
1 change: 1 addition & 0 deletions Manifest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ lib/nokogiri/xml/text.rb
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/serializer.jar
Expand Down
14 changes: 12 additions & 2 deletions ext/java/nokogiri/XmlXpathContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

package nokogiri;

import java.util.List;
import java.util.Set;

import javax.xml.transform.TransformerException;
Expand All @@ -43,8 +44,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,9 +118,15 @@ public IRubyObject evaluate(ThreadContext context, IRubyObject expr, IRubyObject
if (!handler.isNil()) {
if (!isContainsPrefix(src)) {
StringBuilder replacement = new StringBuilder();
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
);
final String PREFIX = NokogiriNamespaceContext.NOKOGIRI_PREFIX;
for (String name : methodNames) {
for (RubySymbol symbol : methodNames) {
String name = symbol.toString();
replacement.setLength(0);
replacement.ensureCapacity(PREFIX.length() + 1 + name.length());
replacement.append(PREFIX).append(':').append(name);
Expand Down
1 change: 1 addition & 0 deletions lib/nokogiri/xml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
require 'nokogiri/xml/syntax_error'
require 'nokogiri/xml/xpath'
require 'nokogiri/xml/xpath_context'
require 'nokogiri/xml/xpath_functions'
require 'nokogiri/xml/builder'
require 'nokogiri/xml/reader'
require 'nokogiri/xml/notation'
Expand Down
3 changes: 2 additions & 1 deletion lib/nokogiri/xml/searchable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module Searchable
# Regular expression used by Searchable#search to determine if a query
# string is CSS or XPath
LOOKS_LIKE_XPATH = /^(\.\/|\/|\.\.|\.$)/

###
# call-seq: search *paths, [namespace-bindings, xpath-variable-bindings, custom-handler-class]
#
Expand Down Expand Up @@ -213,6 +213,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
13 changes: 13 additions & 0 deletions lib/nokogiri/xml/xpath_functions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Nokogiri
module XML
class XPathFunctions < SimpleDelegator
def self.wrap(handler)
if handler.nil?
@wrap_nil ||= self.new(Object.new)
else
self.new(handler)
end
end
end
end
end
18 changes: 18 additions & 0 deletions test/xml/test_xpath.rb
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,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
assert set.length > 0
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 c3376dd

Please sign in to comment.