Skip to content

Commit

Permalink
doc: improve Searchable#css documentation
Browse files Browse the repository at this point in the history
And officially document the XPath attribute extensions to CSS selector
syntax that we support.

See #2419 for context
  • Loading branch information
flavorjones committed Jan 12, 2022
1 parent 855a812 commit 538e11d
Showing 1 changed file with 51 additions and 40 deletions.
91 changes: 51 additions & 40 deletions lib/nokogiri/xml/searchable.rb
Expand Up @@ -6,7 +6,7 @@ module XML
#
# The Searchable module declares the interface used for searching your DOM.
#
# It implements the public methods `search`, `css`, and `xpath`,
# It implements the public methods #search, #css, and #xpath,
# as well as allowing specific implementations to specialize some
# of the important behaviors.
#
Expand All @@ -30,25 +30,22 @@ module Searchable
# node.search('.//bike:tire', {'bike' => 'http://schwinn.com/'})
# node.search('bike|tire', {'bike' => 'http://schwinn.com/'})
#
# For XPath queries, a hash of variable bindings may also be
# appended to the namespace bindings. For example:
# For XPath queries, a hash of variable bindings may also be appended to the namespace
# bindings. For example:
#
# node.search('.//address[@domestic=$value]', nil, {:value => 'Yes'})
#
# Custom XPath functions and CSS pseudo-selectors may also be
# defined. To define custom functions create a class and
# implement the function you want to define. The first argument
# to the method will be the current matching NodeSet. Any other
# arguments are ones that you pass in. Note that this class may
# appear anywhere in the argument list. For example:
#
# node.search('.//title[regex(., "\w+")]', 'div.employee:regex("[0-9]+")'
# Class.new {
# def regex node_set, regex
# node_set.find_all { |node| node['some_attribute'] =~ /#{regex}/ }
# end
# }.new
# )
# 💡 Custom XPath functions and CSS pseudo-selectors may also be defined. To define custom
# functions create a class and implement the function you want to define. The first argument
# to the method will be the current matching NodeSet. Any other arguments are ones that you
# pass in. Note that this class may appear anywhere in the argument list. For example:
#
# handler = Class.new {
# def regex node_set, regex
# node_set.find_all { |node| node['some_attribute'] =~ /#{regex}/ }
# end
# }.new
# node.search('.//title[regex(., "\w+")]', 'div.employee:regex("[0-9]+")', handler)
#
# See Searchable#xpath and Searchable#css for further usage help.
def search(*args)
Expand Down Expand Up @@ -92,25 +89,40 @@ def at(*args)
#
# node.css('bike|tire', {'bike' => 'http://schwinn.com/'})
#
# Custom CSS pseudo classes may also be defined. To define
# custom pseudo classes, create a class and implement the custom
# pseudo class you want defined. The first argument to the
# method will be the current matching NodeSet. Any other
# arguments are ones that you pass in. For example:
# 💡 Custom CSS pseudo classes may also be defined which are mapped to a custom XPath
# function. To define custom pseudo classes, create a class and implement the custom pseudo
# class you want defined. The first argument to the method will be the matching context
# NodeSet. Any other arguments are ones that you pass in. For example:
#
# node.css('title:regex("\w+")', Class.new {
# def regex node_set, regex
# handler = Class.new {
# def regex(node_set, regex)
# node_set.find_all { |node| node['some_attribute'] =~ /#{regex}/ }
# end
# }.new)
# }.new
# node.css('title:regex("\w+")', handler)
#
# 💡 Some XPath syntax is supported in CSS queries. For example, to query for an attribute:
#
# Note that the CSS query string is case-sensitive with regards
# to your document type. That is, if you're looking for "H1" in
# an HTML document, you'll never find anything, since HTML tags
# will match only lowercase CSS queries. However, "H1" might be
# found in an XML document, where tags names are case-sensitive
# (e.g., "H1" is distinct from "h1").
# node.css('img > @href') # returns all +href+ attributes on an +img+ element
# node.css('img / @href') # same
#
# # ⚠ this returns +class+ attributes from all +div+ elements AND THEIR CHILDREN!
# node.css('div @class')
#
# node.css
#
# 💡 Array-like syntax is supported in CSS queries as an alternative to using +:nth-child()+.
#
# ⚠ NOTE that indices are 1-based like +:nth-child+ and not 0-based like Ruby Arrays. For
# example:
#
# # equivalent to 'li:nth-child(2)'
# node.css('li[2]') # retrieve the second li element in a list
#
# ⚠ NOTE that the CSS query string is case-sensitive with regards to your document type. HTML
# tags will match only lowercase CSS queries, so if you search for "H1" in an HTML document,
# you'll never find anything. However, "H1" might be found in an XML document, where tags
# names are case-sensitive (e.g., "H1" is distinct from "h1").
def css(*args)
rules, handler, ns, _ = extract_params(args)

Expand Down Expand Up @@ -147,18 +159,17 @@ def at_css(*args)
#
# node.xpath('.//address[@domestic=$value]', nil, {:value => 'Yes'})
#
# Custom XPath functions may also be defined. To define custom
# functions create a class and implement the function you want
# to define. The first argument to the method will be the
# current matching NodeSet. Any other arguments are ones that
# you pass in. Note that this class may appear anywhere in the
# argument list. For example:
# 💡 Custom XPath functions may also be defined. To define custom functions create a class and
# implement the function you want to define. The first argument to the method will be the
# current matching NodeSet. Any other arguments are ones that you pass in. Note that this
# class may appear anywhere in the argument list. For example:
#
# node.xpath('.//title[regex(., "\w+")]', Class.new {
# def regex node_set, regex
# handler = Class.new {
# def regex(node_set, regex)
# node_set.find_all { |node| node['some_attribute'] =~ /#{regex}/ }
# end
# }.new)
# }.new
# node.xpath('.//title[regex(., "\w+")]', handler)
#
def xpath(*args)
paths, handler, ns, binds = extract_params(args)
Expand Down

0 comments on commit 538e11d

Please sign in to comment.