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
# 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 1 < path.length and '/' == path[1,1]
			path[0] = ''
		else
			path[0] = 'self::'
		end
	end

	# node check
	nodes = [node]
	path.split('/').each do |step|
		case step
		when ''
			step = 'descendant-or-self::node()'
		when '.'
			step = 'self::node()'
		when '..'
			step = 'parent::node()'
		end

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

		step =~ /^(?:(.*?)::)?(.+?)(\[.*?)?$/
		axis, node_test, predicates = $1, $2, $3
		axis = 'child' if axis.nil?

		# 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{|node| node.validate_predicate(predicate.clone, nodes.index(node)+1)}
				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.



66
67
68
# File 'lib/rind/xpath.rb', line 66

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