Module: Xpath

Included in:
Rind::Cdata, Rind::Comment, Rind::Element, Rind::ProcessingInstruction, Rind::Text
Defined in:
lib/rind/xpath.rb

Overview

Current Xpath support is fairly basic but covers almost all axes and node tests. Predicates are limited to attribute and position checks. I intend to expand support but that should cover most of the needed functionality.

Instance Method Summary collapse

Instance Method Details

#s(path) ⇒ Object

Xpath search of a node that returns a list of matching nodes.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/rind/xpath.rb', line 6

def s(path)
	node = self

	# absolute paths to the top
	if '/' == path[0,1]
		while not node.parent.nil?
			node = node.parent
		end
		if '/' != path[1,1]
			path[0] = 'self::'
		end
	end

	# node check
	nodes = [node]
	path.scan(%r{(?:^\/?|\/)
						(?:(.*?)::)?   # axis
						([^\/\[]+)?    # node test
						((?:\[.+?\])*) # predicates
	}x) do |axis, node_test, predicates|
		case node_test
		when nil
			axis = 'descendant-or-self'
			node_test = 'node()'
		when '.'
			axis = 'self'
			node_test = 'node()'
		when '..'
			axis = 'parent'
			node_test = 'node()'
		end

		axis = 'child' if axis.nil?

		node_test.gsub!(/^@/, 'attribute::')
		predicates.gsub!(/^@/, 'attribute::')

		# find matching nodes
		nodes = nodes.collect{|node| node.find_matching_nodes(axis, node_test)}.flatten.compact

		# check predicates
		if not predicates.nil?
			# true() and false()
			predicates.gsub!(/(true|false)\(\)/, '\1')
			# ==
			predicates.gsub!(/=/, '==')

			predicates.scan(/\[(.*?)\]/) do |predicate|
				predicate = predicate[0]
				# last()
				predicate.gsub!(/last\(\)/, nodes.length.to_s)

				nodes = nodes.find_all do |node|
					node.validate_predicate(predicate.clone, Rind::Nodes[*nodes].exact_index(node)+1)
				end
				break if nodes.empty?
			end
		end

		return Rind::Nodes.new if nodes.empty?
	end

	Rind::Nodes.new(nodes)
end

#sf(path) ⇒ Object

Xpath search returning only the first matching node in the list.



72
73
74
# File 'lib/rind/xpath.rb', line 72

def sf(path)
	self.s(path).first
end