xpath 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +6 -64
- data/lib/xpath.rb +2 -3
- data/lib/xpath/dsl.rb +115 -80
- data/lib/xpath/expression.rb +1 -1
- data/lib/xpath/html.rb +1 -1
- data/lib/xpath/renderer.rb +19 -77
- data/lib/xpath/version.rb +1 -1
- data/spec/fixtures/simple.html +4 -0
- data/spec/html_spec.rb +1 -1
- data/spec/spec_helper.rb +5 -0
- data/spec/xpath_spec.rb +146 -0
- metadata +41 -69
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -1
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ce787a09ca48f5c96580cbced2533d00b741a0e3
|
4
|
+
data.tar.gz: c4310006453b0029c593077b225d36e79dfd35af
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 83b64861c13aecd52661c7cbc598f90c4a17aeb71c86936f0bec36d97ffe4248fc8302cad434742b95074b6aded1de563c33a3f82df12e4baf42d10c42d132ac
|
7
|
+
data.tar.gz: bc2e7c29f1240475904601bc7f8ad2ecda2978536b97e1ab407bc855ec16b7f64b48cc49400a501833bb8ac643bcc8b1e2052d4a05974c3df7ca0355ac26fe4e
|
data/README.md
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
XPath is a Ruby DSL around a subset of XPath 1.0. Its primary purpose is to
|
4
4
|
facilitate writing complex XPath queries from Ruby code.
|
5
5
|
|
6
|
-
[![
|
6
|
+
[![Gem Version](https://badge.fury.io/rb/xpath.png)](http://badge.fury.io/rb/xpath)
|
7
|
+
[![Build Status](https://secure.travis-ci.org/teamcapybara/xpath.png?branch=master)](http://travis-ci.org/teamcapybara/xpath)
|
7
8
|
|
8
9
|
## Generating expressions
|
9
10
|
|
@@ -36,11 +37,9 @@ module MyXPaths
|
|
36
37
|
end
|
37
38
|
```
|
38
39
|
|
39
|
-
Both ways return an
|
40
|
-
|
41
|
-
|
42
|
-
string, just call `#to_s` on it. All available expressions are defined in
|
43
|
-
[`XPath::DSL`](http://rdoc.info/github/jnicklas/xpath/XPath/DSL).
|
40
|
+
Both ways return an `XPath::Expression` instance, which can be further
|
41
|
+
modified. To convert the expression to a string, just call `#to_s` on it. All
|
42
|
+
available expressions are defined in `XPath::DSL`.
|
44
43
|
|
45
44
|
## String, Hashes and Symbols
|
46
45
|
|
@@ -74,63 +73,6 @@ XPath.descendant(:p)[1]
|
|
74
73
|
Keep in mind that XPath is 1-indexed and not 0-indexed like most other
|
75
74
|
programming languages, including Ruby.
|
76
75
|
|
77
|
-
Hashes are automatically converted to equality expressions, so the above
|
78
|
-
example could be written as:
|
79
|
-
|
80
|
-
``` ruby
|
81
|
-
XPath.descendant(:p)[:@id => 'foo']
|
82
|
-
```
|
83
|
-
|
84
|
-
Which would generate the same expression:
|
85
|
-
|
86
|
-
```
|
87
|
-
.//p[@id = 'foo']
|
88
|
-
```
|
89
|
-
|
90
|
-
Note that the same rules apply here, both the keys and values in the hash are
|
91
|
-
treated the same way as any other expression in XPath. Thus the following are
|
92
|
-
not equivalent:
|
93
|
-
|
94
|
-
``` ruby
|
95
|
-
XPath.descendant(:p)[:@id => 'foo'] # => .//p[@id = 'foo']
|
96
|
-
XPath.descendant(:p)[:id => 'foo'] # => .//p[id = 'foo']
|
97
|
-
XPath.descendant(:p)['id' => 'foo'] # => .//p['id' = 'foo']
|
98
|
-
```
|
99
|
-
|
100
|
-
## HTML
|
101
|
-
|
102
|
-
XPath comes with a set of premade XPaths for use with HTML documents.
|
103
|
-
|
104
|
-
You can generate these like this:
|
105
|
-
|
106
|
-
``` ruby
|
107
|
-
XPath::HTML.link('Home')
|
108
|
-
XPath::HTML.field('Name')
|
109
|
-
```
|
110
|
-
|
111
|
-
See [`XPath::HTML`](http://rdoc.info/github/jnicklas/xpath/XPath/HTML) for all
|
112
|
-
available matchers.
|
113
|
-
|
114
76
|
## License
|
115
77
|
|
116
|
-
(
|
117
|
-
|
118
|
-
Copyright © 2010 Jonas Nicklas
|
119
|
-
|
120
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
121
|
-
this software and associated documentation files (the ‘Software’), to deal in
|
122
|
-
the Software without restriction, including without limitation the rights to
|
123
|
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
124
|
-
of the Software, and to permit persons to whom the Software is furnished to do
|
125
|
-
so, subject to the following conditions:
|
126
|
-
|
127
|
-
The above copyright notice and this permission notice shall be included in all
|
128
|
-
copies or substantial portions of the Software.
|
129
|
-
|
130
|
-
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
131
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
132
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
133
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
134
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
135
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
136
|
-
SOFTWARE.
|
78
|
+
See [LICENSE](LICENSE).
|
data/lib/xpath.rb
CHANGED
data/lib/xpath/dsl.rb
CHANGED
@@ -1,112 +1,147 @@
|
|
1
1
|
module XPath
|
2
2
|
module DSL
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
end
|
7
|
-
|
8
|
-
def name
|
9
|
-
Expression.new(:node_name, current)
|
10
|
-
end
|
3
|
+
def current
|
4
|
+
Expression.new(:this_node)
|
5
|
+
end
|
11
6
|
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
def descendant(*expressions)
|
8
|
+
Expression.new(:descendant, current, expressions)
|
9
|
+
end
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
11
|
+
def child(*expressions)
|
12
|
+
Expression.new(:child, current, expressions)
|
13
|
+
end
|
19
14
|
|
20
|
-
|
21
|
-
|
22
|
-
|
15
|
+
def axis(name, *element_names)
|
16
|
+
Expression.new(:axis, current, name, element_names)
|
17
|
+
end
|
23
18
|
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
def anywhere(*expressions)
|
20
|
+
Expression.new(:anywhere, expressions)
|
21
|
+
end
|
27
22
|
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
def attr(expression)
|
24
|
+
Expression.new(:attribute, current, expression)
|
25
|
+
end
|
31
26
|
|
32
|
-
|
33
|
-
|
34
|
-
|
27
|
+
def text
|
28
|
+
Expression.new(:text, current)
|
29
|
+
end
|
35
30
|
|
36
|
-
|
37
|
-
|
38
|
-
|
31
|
+
def css(selector)
|
32
|
+
Expression.new(:css, current, Literal.new(selector))
|
33
|
+
end
|
39
34
|
|
40
|
-
|
41
|
-
|
42
|
-
|
35
|
+
def function(name, *arguments)
|
36
|
+
Expression.new(:function, name, *arguments)
|
37
|
+
end
|
43
38
|
|
44
|
-
|
45
|
-
|
46
|
-
|
39
|
+
def method(name, *arguments)
|
40
|
+
Expression.new(:function, name, current, *arguments)
|
41
|
+
end
|
47
42
|
|
48
|
-
|
49
|
-
|
50
|
-
|
43
|
+
def where(expression)
|
44
|
+
Expression.new(:where, current, expression)
|
45
|
+
end
|
46
|
+
alias_method :[], :where
|
51
47
|
|
52
|
-
|
53
|
-
|
54
|
-
|
48
|
+
def is(expression)
|
49
|
+
Expression.new(:is, current, expression)
|
50
|
+
end
|
55
51
|
|
56
|
-
|
57
|
-
|
58
|
-
end
|
52
|
+
def binary_operator(name, rhs)
|
53
|
+
Expression.new(:binary_operator, name, current, rhs)
|
59
54
|
end
|
60
55
|
|
61
|
-
|
62
|
-
|
56
|
+
def union(*expressions)
|
57
|
+
Union.new(*[self, expressions].flatten)
|
58
|
+
end
|
59
|
+
alias_method :+, :union
|
63
60
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
alias_method :[], :where
|
61
|
+
def last
|
62
|
+
function(:last)
|
63
|
+
end
|
68
64
|
|
69
|
-
|
70
|
-
|
71
|
-
|
65
|
+
def position
|
66
|
+
function(:position)
|
67
|
+
end
|
72
68
|
|
73
|
-
|
74
|
-
|
69
|
+
METHODS = [
|
70
|
+
# node set
|
71
|
+
:count, :id, :local_name, :namespace_uri, :name,
|
72
|
+
# string
|
73
|
+
:string, :concat, :starts_with, :contains, :substring_before,
|
74
|
+
:substring_after, :substring, :string_length, :normalize_space,
|
75
|
+
:translate,
|
76
|
+
# boolean
|
77
|
+
:boolean, :not, :true, :false, :lang,
|
78
|
+
# number
|
79
|
+
:number, :sum, :floor, :ceiling, :round,
|
80
|
+
]
|
81
|
+
|
82
|
+
METHODS.each do |key|
|
83
|
+
name = key.to_s.gsub("_", "-").to_sym
|
84
|
+
define_method key do |*args|
|
85
|
+
method(name, *args)
|
75
86
|
end
|
76
|
-
|
87
|
+
end
|
77
88
|
|
78
|
-
|
79
|
-
|
80
|
-
|
89
|
+
alias_method :inverse, :not
|
90
|
+
alias_method :~, :not
|
91
|
+
alias_method :normalize, :normalize_space
|
92
|
+
alias_method :n, :normalize_space
|
93
|
+
|
94
|
+
OPERATORS = [
|
95
|
+
[:equals, :"=", :==],
|
96
|
+
[:or, :or, :|],
|
97
|
+
[:and, :and, :&],
|
98
|
+
[:lte, :<=, :<=],
|
99
|
+
[:lt, :<, :<],
|
100
|
+
[:gte, :>=, :>=],
|
101
|
+
[:gt, :>, :>],
|
102
|
+
[:plus, :+],
|
103
|
+
[:minus, :-],
|
104
|
+
[:multiply, :*, :*],
|
105
|
+
[:divide, :div, :/],
|
106
|
+
[:mod, :mod, :%],
|
107
|
+
]
|
108
|
+
|
109
|
+
OPERATORS.each do |(name, operator, alias_name)|
|
110
|
+
define_method name do |rhs|
|
111
|
+
binary_operator(operator, rhs)
|
112
|
+
end
|
113
|
+
alias_method alias_name, name if alias_name
|
114
|
+
end
|
81
115
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
116
|
+
AXES = [
|
117
|
+
:ancestor, :ancestor_or_self, :attribute, :descendant_or_self,
|
118
|
+
:following, :following_sibling, :namespace, :parent, :preceding,
|
119
|
+
:preceding_sibling, :self,
|
120
|
+
]
|
86
121
|
|
87
|
-
|
88
|
-
|
122
|
+
AXES.each do |key|
|
123
|
+
name = key.to_s.gsub("_", "-").to_sym
|
124
|
+
define_method key do |*element_names|
|
125
|
+
axis(name, *element_names)
|
89
126
|
end
|
90
|
-
|
127
|
+
end
|
91
128
|
|
92
|
-
|
93
|
-
Union.new(*[self, expressions].flatten)
|
94
|
-
end
|
95
|
-
alias_method :+, :union
|
129
|
+
alias_method :self_axis, :self
|
96
130
|
|
97
|
-
|
98
|
-
|
131
|
+
def one_of(*expressions)
|
132
|
+
expressions.map do |e|
|
133
|
+
current.equals(e)
|
134
|
+
end.reduce do |a, b|
|
135
|
+
a.or(b)
|
99
136
|
end
|
100
|
-
|
137
|
+
end
|
101
138
|
|
102
|
-
|
103
|
-
|
104
|
-
|
139
|
+
def next_sibling(*expressions)
|
140
|
+
axis(:"following-sibling")[1].axis(:self, *expressions)
|
141
|
+
end
|
105
142
|
|
106
|
-
|
107
|
-
|
108
|
-
end
|
109
|
-
alias_method :n, :normalize
|
143
|
+
def previous_sibling(*expressions)
|
144
|
+
axis(:"preceding-sibling")[1].axis(:self, *expressions)
|
110
145
|
end
|
111
146
|
end
|
112
147
|
end
|
data/lib/xpath/expression.rb
CHANGED
data/lib/xpath/html.rb
CHANGED
data/lib/xpath/renderer.rb
CHANGED
@@ -38,32 +38,20 @@ module XPath
|
|
38
38
|
'.'
|
39
39
|
end
|
40
40
|
|
41
|
-
def descendant(
|
42
|
-
|
43
|
-
"#{parent}//#{element_names.first}"
|
44
|
-
elsif element_names.length > 1
|
45
|
-
"#{parent}//*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]"
|
46
|
-
else
|
47
|
-
"#{parent}//*"
|
48
|
-
end
|
41
|
+
def descendant(current, element_names)
|
42
|
+
with_element_conditions("#{current}//", element_names)
|
49
43
|
end
|
50
44
|
|
51
|
-
def child(
|
52
|
-
|
53
|
-
"#{parent}/#{element_names.first}"
|
54
|
-
elsif element_names.length > 1
|
55
|
-
"#{parent}/*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]"
|
56
|
-
else
|
57
|
-
"#{parent}/*"
|
58
|
-
end
|
45
|
+
def child(current, element_names)
|
46
|
+
with_element_conditions("#{current}/", element_names)
|
59
47
|
end
|
60
48
|
|
61
|
-
def axis(
|
62
|
-
"#{
|
49
|
+
def axis(current, name, element_names)
|
50
|
+
with_element_conditions("#{current}/#{name}::", element_names)
|
63
51
|
end
|
64
52
|
|
65
|
-
def
|
66
|
-
"
|
53
|
+
def anywhere(element_names)
|
54
|
+
with_element_conditions("//", element_names)
|
67
55
|
end
|
68
56
|
|
69
57
|
def where(on, condition)
|
@@ -74,15 +62,15 @@ module XPath
|
|
74
62
|
"#{current}/@#{name}"
|
75
63
|
end
|
76
64
|
|
77
|
-
def
|
78
|
-
"#{
|
65
|
+
def binary_operator(name, left, right)
|
66
|
+
"(#{left} #{name} #{right})"
|
79
67
|
end
|
80
68
|
|
81
69
|
def is(one, two)
|
82
70
|
if @type == :exact
|
83
|
-
|
71
|
+
binary_operator("=", one, two)
|
84
72
|
else
|
85
|
-
contains
|
73
|
+
function(:contains, one, two)
|
86
74
|
end
|
87
75
|
end
|
88
76
|
|
@@ -94,10 +82,6 @@ module XPath
|
|
94
82
|
"#{current}/text()"
|
95
83
|
end
|
96
84
|
|
97
|
-
def normalized_space(current)
|
98
|
-
"normalize-space(#{current})"
|
99
|
-
end
|
100
|
-
|
101
85
|
def literal(node)
|
102
86
|
node
|
103
87
|
end
|
@@ -113,62 +97,20 @@ module XPath
|
|
113
97
|
expressions.join(' | ')
|
114
98
|
end
|
115
99
|
|
116
|
-
def
|
117
|
-
|
118
|
-
"//#{element_names.first}"
|
119
|
-
elsif element_names.length > 1
|
120
|
-
"//*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]"
|
121
|
-
else
|
122
|
-
"//*"
|
123
|
-
end
|
100
|
+
def function(name, *arguments)
|
101
|
+
"#{name}(#{arguments.join(", ")})"
|
124
102
|
end
|
125
103
|
|
126
|
-
|
127
|
-
"contains(#{current}, #{value})"
|
128
|
-
end
|
104
|
+
private
|
129
105
|
|
130
|
-
def
|
131
|
-
"starts-with(#{current}, #{value})"
|
132
|
-
end
|
133
|
-
|
134
|
-
def and(one, two)
|
135
|
-
"(#{one} and #{two})"
|
136
|
-
end
|
137
|
-
|
138
|
-
def or(one, two)
|
139
|
-
"(#{one} or #{two})"
|
140
|
-
end
|
141
|
-
|
142
|
-
def one_of(current, values)
|
143
|
-
values.map { |value| "#{current} = #{value}" }.join(' or ')
|
144
|
-
end
|
145
|
-
|
146
|
-
def next_sibling(current, element_names)
|
106
|
+
def with_element_conditions(expression, element_names)
|
147
107
|
if element_names.length == 1
|
148
|
-
"#{
|
108
|
+
"#{expression}#{element_names.first}"
|
149
109
|
elsif element_names.length > 1
|
150
|
-
"#{
|
110
|
+
"#{expression}*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]"
|
151
111
|
else
|
152
|
-
"#{
|
112
|
+
"#{expression}*"
|
153
113
|
end
|
154
114
|
end
|
155
|
-
|
156
|
-
def previous_sibling(current, element_names)
|
157
|
-
if element_names.length == 1
|
158
|
-
"#{current}/preceding-sibling::*[1]/self::#{element_names.first}"
|
159
|
-
elsif element_names.length > 1
|
160
|
-
"#{current}/preceding-sibling::*[1]/self::*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]"
|
161
|
-
else
|
162
|
-
"#{current}/preceding-sibling::*[1]/self::*"
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
def inverse(current)
|
167
|
-
"not(#{current})"
|
168
|
-
end
|
169
|
-
|
170
|
-
def string_function(current)
|
171
|
-
"string(#{current})"
|
172
|
-
end
|
173
115
|
end
|
174
116
|
end
|
data/lib/xpath/version.rb
CHANGED
data/spec/fixtures/simple.html
CHANGED
data/spec/html_spec.rb
CHANGED
@@ -5,13 +5,13 @@ describe XPath::HTML do
|
|
5
5
|
let(:template) { 'form' }
|
6
6
|
let(:template_path) { File.read(File.expand_path("fixtures/#{template}.html", File.dirname(__FILE__))) }
|
7
7
|
let(:doc) { Nokogiri::HTML(template_path) }
|
8
|
+
let(:type) { |example| example.metadata[:type] }
|
8
9
|
|
9
10
|
def get(*args)
|
10
11
|
all(*args).first
|
11
12
|
end
|
12
13
|
|
13
14
|
def all(*args)
|
14
|
-
type = example.metadata[:type]
|
15
15
|
doc.xpath(XPath::HTML.send(subject, *args).to_xpath(type)).map { |node| node[:data] }
|
16
16
|
end
|
17
17
|
|
data/spec/spec_helper.rb
CHANGED
data/spec/xpath_spec.rb
CHANGED
@@ -191,6 +191,43 @@ describe XPath do
|
|
191
191
|
end
|
192
192
|
end
|
193
193
|
|
194
|
+
describe '#substring' do
|
195
|
+
context "when called with one argument" do
|
196
|
+
it "should select the part of a string after the specified character" do
|
197
|
+
@results = xpath { |x| x.descendant(:span).where(x.attr(:id) == "substring").text.substring(7) }
|
198
|
+
@results.should == "there"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context "when called with two arguments" do
|
203
|
+
it "should select the part of a string after the specified character, up to the given length" do
|
204
|
+
@results = xpath { |x| x.descendant(:span).where(x.attr(:id) == "substring").text.substring(2, 4) }
|
205
|
+
@results.should == "ello"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe '#function' do
|
211
|
+
it "should call the given xpath function" do
|
212
|
+
@results = xpath { |x| x.function(:boolean, x.function(:true) == x.function(:false)) }
|
213
|
+
@results.should == false
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe '#method' do
|
218
|
+
it "should call the given xpath function with the current node as the first argument" do
|
219
|
+
@results = xpath { |x| x.descendant(:span).where(x.attr(:id) == "string-length").text.method(:"string-length") }
|
220
|
+
@results.should == 11
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe '#string_length' do
|
225
|
+
it "should return the length of a string" do
|
226
|
+
@results = xpath { |x| x.descendant(:span).where(x.attr(:id) == "string-length").text.string_length }
|
227
|
+
@results.should == 11
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
194
231
|
describe '#where' do
|
195
232
|
it "should limit the expression to find only certain nodes" do
|
196
233
|
xpath { |x| x.descendant(:div).where(:"@id = 'foo'") }.first[:title].should == "fooDiv"
|
@@ -346,4 +383,113 @@ describe XPath do
|
|
346
383
|
end
|
347
384
|
end
|
348
385
|
|
386
|
+
describe "#last" do
|
387
|
+
it "returns the number of elements in the context" do
|
388
|
+
@results = xpath { |x| x.descendant(:p)[XPath.position() == XPath.last()] }
|
389
|
+
@results[0].text.should == "Bax"
|
390
|
+
@results[1].text.should == "Blah"
|
391
|
+
@results[2].text.should == "llama"
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
describe "#position" do
|
396
|
+
it "returns the position of elements in the context" do
|
397
|
+
@results = xpath { |x| x.descendant(:p)[XPath.position() == 2] }
|
398
|
+
@results[0].text.should == "Bax"
|
399
|
+
@results[1].text.should == "Bax"
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
describe "#count" do
|
404
|
+
it "counts the number of occurrences" do
|
405
|
+
@results = xpath { |x| x.descendant(:div)[x.descendant(:p).count == 2] }
|
406
|
+
@results[0][:id].should == "preference"
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
describe "#lte" do
|
411
|
+
it "checks lesser than or equal" do
|
412
|
+
@results = xpath { |x| x.descendant(:p)[XPath.position() <= 2] }
|
413
|
+
@results[0].text.should == "Blah"
|
414
|
+
@results[1].text.should == "Bax"
|
415
|
+
@results[2][:title].should == "gorilla"
|
416
|
+
@results[3].text.should == "Bax"
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
describe "#lt" do
|
421
|
+
it "checks lesser than" do
|
422
|
+
@results = xpath { |x| x.descendant(:p)[XPath.position() < 2] }
|
423
|
+
@results[0].text.should == "Blah"
|
424
|
+
@results[1][:title].should == "gorilla"
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
describe "#gte" do
|
429
|
+
it "checks greater than or equal" do
|
430
|
+
@results = xpath { |x| x.descendant(:p)[XPath.position() >= 2] }
|
431
|
+
@results[0].text.should == "Bax"
|
432
|
+
@results[1][:title].should == "monkey"
|
433
|
+
@results[2].text.should == "Bax"
|
434
|
+
@results[3].text.should == "Blah"
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
describe "#gt" do
|
439
|
+
it "checks greater than" do
|
440
|
+
@results = xpath { |x| x.descendant(:p)[XPath.position() > 2] }
|
441
|
+
@results[0][:title].should == "monkey"
|
442
|
+
@results[1].text.should == "Blah"
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
describe "#plus" do
|
447
|
+
it "adds stuff" do
|
448
|
+
@results = xpath { |x| x.descendant(:p)[XPath.position().plus(1) == 2] }
|
449
|
+
@results[0][:id].should == "fooDiv"
|
450
|
+
@results[1][:title].should == "gorilla"
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
describe "#minus" do
|
455
|
+
it "subtracts stuff" do
|
456
|
+
@results = xpath { |x| x.descendant(:p)[XPath.position().minus(1) == 0] }
|
457
|
+
@results[0][:id].should == "fooDiv"
|
458
|
+
@results[1][:title].should == "gorilla"
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
describe "#multiply" do
|
463
|
+
it "multiplies stuff" do
|
464
|
+
@results = xpath { |x| x.descendant(:p)[XPath.position() * 3 == 3] }
|
465
|
+
@results[0][:id].should == "fooDiv"
|
466
|
+
@results[1][:title].should == "gorilla"
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
describe "#divide" do
|
471
|
+
it "divides stuff" do
|
472
|
+
@results = xpath { |x| x.descendant(:p)[XPath.position() / 2 == 1] }
|
473
|
+
@results[0].text.should == "Bax"
|
474
|
+
@results[1].text.should == "Bax"
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
describe "#mod" do
|
479
|
+
it "take modulo" do
|
480
|
+
@results = xpath { |x| x.descendant(:p)[XPath.position() % 2 == 1] }
|
481
|
+
@results[0].text.should == "Blah"
|
482
|
+
@results[1][:title].should == "monkey"
|
483
|
+
@results[2][:title].should == "gorilla"
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
describe "#ancestor" do
|
488
|
+
it "finds ancestor nodes" do
|
489
|
+
@results = xpath { |x| x.descendant(:p)[1].ancestor }
|
490
|
+
@results[0].node_name.should == "html"
|
491
|
+
@results[1].node_name.should == "body"
|
492
|
+
@results[2][:id].should == "foo"
|
493
|
+
end
|
494
|
+
end
|
349
495
|
end
|
metadata
CHANGED
@@ -1,108 +1,84 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xpath
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
5
|
-
prerelease:
|
4
|
+
version: 2.1.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Jonas Nicklas
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain:
|
12
|
-
-
|
13
|
-
|
14
|
-
Z0lCQVRBTkJna3Foa2lHOXcwQkFRVUZBREJFTVJZd0ZBWURWUVFEREExcWIy
|
15
|
-
NWgKY3k1dWFXTnJiR0Z6TVJVd0V3WUtDWkltaVpQeUxHUUJHUllGWjIxaGFX
|
16
|
-
d3hFekFSQmdvSmtpYUprL0lzWkFFWgpGZ05qYjIwd0hoY05NVE13TXpFMk1E
|
17
|
-
RXpNVEV4V2hjTk1UUXdNekUyTURFek1URXhXakJFTVJZd0ZBWURWUVFECkRB
|
18
|
-
MXFiMjVoY3k1dWFXTnJiR0Z6TVJVd0V3WUtDWkltaVpQeUxHUUJHUllGWjIx
|
19
|
-
aGFXd3hFekFSQmdvSmtpYUoKay9Jc1pBRVpGZ05qYjIwd2dnRWlNQTBHQ1Nx
|
20
|
-
R1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUUMrQjlteAowVFZrdlhJ
|
21
|
-
TjMxSkM0czROaTZtY2hKTC85OGgrZXVNSUVOdDVMb3hjZVlsaWpQMG5HZFlI
|
22
|
-
QU1vQTJDVWw3RStKCi9LbTNXMXppU0MwTXIzWUlxQ3dwNGZ5Z1Axd295UktS
|
23
|
-
WW1veUJSWmRXNUppUEFRell4WGFpYjArQkNCK0poU20KUmV2MitCYUhKdVJS
|
24
|
-
S1dxK1MzYnpaNjFVNlJNVnAvdEFmZUluWE9RZHZtM3ljeWhPR1d4NlhwRVcy
|
25
|
-
T042b1JEbAptQnVBTThiOWhqcE9BekZYaDNPTFhCQzl6cTVaNXBsMmZybGZB
|
26
|
-
VkIyaEt6dHdpRWlOa0dqeC9xQlhpeHZvN2R1Ck14K08yOTZXcEx1dVd3ck9h
|
27
|
-
SXNSM1EzN2FOdkxCdmllaDc0RDlRSmpjd1IzTXIwUlJRTGJYeE5Hc1NRYTZn
|
28
|
-
WTQKbDlqMEV0VVZTRFpCNTRINUFnTUJBQUdqZ1lFd2Z6QUpCZ05WSFJNRUFq
|
29
|
-
QUFNQXNHQTFVZER3UUVBd0lFc0RBZApCZ05WSFE0RUZnUVVmOEd0anduYVJ0
|
30
|
-
U3FndkpEekNjM1RhN0xITXN3SWdZRFZSMFJCQnN3R1lFWGFtOXVZWE11CmJt
|
31
|
-
bGphMnhoYzBCbmJXRnBiQzVqYjIwd0lnWURWUjBTQkJzd0dZRVhhbTl1WVhN
|
32
|
-
dWJtbGphMnhoYzBCbmJXRnAKYkM1amIyMHdEUVlKS29aSWh2Y05BUUVGQlFB
|
33
|
-
RGdnRUJBR2xzbnBjZXY0ZnUrRkRBeWdiT2JmYzczcGx0S25RRgpFd3k4OCtI
|
34
|
-
Z0o0QWhqd0hFUjdPV0RWTXdoNnZIa21FZXAxcmhMbk5XR3p0RC95bTVpRms0
|
35
|
-
RkdJejFENUo3VHFRCms0djJ3K09qZTNZVy9wU0FHZ0FWUEdDQ3JscWl3NGhL
|
36
|
-
WlV6UG9iOE5XaHVTNHRLRFBXTUdCUXgzWlNSeXFJTGMKL1dRL0ZtbmJWYTl4
|
37
|
-
UlVvK1JXaVA5VHB6S1FKaVIzQlA5T3BWclJ4MTNtMGV1Rnh6VGk2aWtKekM0
|
38
|
-
NVEzRHE1VgppUXhVbUNDUnNKVmNzU0ZPUkpMZFdOVnF6Qk9XWlJucXJMRHVu
|
39
|
-
em91Q2JjcXppTldrOEtxalpDcjVpdFJpMzE5Ci8ya1pnd2MzMkZIbENkQ0JY
|
40
|
-
a2hFbEVIVC9iR0NGazIrYWpubVdSdHFacHovcG9UZ0VlaGlJVVk9Ci0tLS0t
|
41
|
-
RU5EIENFUlRJRklDQVRFLS0tLS0K
|
42
|
-
date: 2013-04-09 00:00:00.000000000 Z
|
11
|
+
- gem-public_cert.pem
|
12
|
+
date: 2017-05-25 00:00:00.000000000 Z
|
43
13
|
dependencies:
|
44
14
|
- !ruby/object:Gem::Dependency
|
45
15
|
name: nokogiri
|
46
16
|
requirement: !ruby/object:Gem::Requirement
|
47
|
-
none: false
|
48
17
|
requirements:
|
49
|
-
- - ~>
|
18
|
+
- - "~>"
|
50
19
|
- !ruby/object:Gem::Version
|
51
20
|
version: '1.3'
|
52
21
|
type: :runtime
|
53
22
|
prerelease: false
|
54
23
|
version_requirements: !ruby/object:Gem::Requirement
|
55
|
-
none: false
|
56
24
|
requirements:
|
57
|
-
- - ~>
|
25
|
+
- - "~>"
|
58
26
|
- !ruby/object:Gem::Version
|
59
27
|
version: '1.3'
|
60
28
|
- !ruby/object:Gem::Dependency
|
61
29
|
name: rspec
|
62
30
|
requirement: !ruby/object:Gem::Requirement
|
63
|
-
none: false
|
64
31
|
requirements:
|
65
|
-
- - ~>
|
32
|
+
- - "~>"
|
66
33
|
- !ruby/object:Gem::Version
|
67
|
-
version: '
|
34
|
+
version: '3.0'
|
68
35
|
type: :development
|
69
36
|
prerelease: false
|
70
37
|
version_requirements: !ruby/object:Gem::Requirement
|
71
|
-
none: false
|
72
38
|
requirements:
|
73
|
-
- - ~>
|
39
|
+
- - "~>"
|
74
40
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
41
|
+
version: '3.0'
|
76
42
|
- !ruby/object:Gem::Dependency
|
77
43
|
name: yard
|
78
44
|
requirement: !ruby/object:Gem::Requirement
|
79
|
-
none: false
|
80
45
|
requirements:
|
81
|
-
- -
|
46
|
+
- - ">="
|
82
47
|
- !ruby/object:Gem::Version
|
83
48
|
version: 0.5.8
|
84
49
|
type: :development
|
85
50
|
prerelease: false
|
86
51
|
version_requirements: !ruby/object:Gem::Requirement
|
87
|
-
none: false
|
88
52
|
requirements:
|
89
|
-
- -
|
53
|
+
- - ">="
|
90
54
|
- !ruby/object:Gem::Version
|
91
55
|
version: 0.5.8
|
92
56
|
- !ruby/object:Gem::Dependency
|
93
57
|
name: rake
|
94
58
|
requirement: !ruby/object:Gem::Requirement
|
95
|
-
none: false
|
96
59
|
requirements:
|
97
|
-
- -
|
60
|
+
- - ">="
|
98
61
|
- !ruby/object:Gem::Version
|
99
62
|
version: '0'
|
100
63
|
type: :development
|
101
64
|
prerelease: false
|
102
65
|
version_requirements: !ruby/object:Gem::Requirement
|
103
|
-
none: false
|
104
66
|
requirements:
|
105
|
-
- -
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: pry
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
106
82
|
- !ruby/object:Gem::Version
|
107
83
|
version: '0'
|
108
84
|
description: XPath is a Ruby DSL for generating XPath expressions
|
@@ -110,9 +86,10 @@ email:
|
|
110
86
|
- jonas.nicklas@gmail.com
|
111
87
|
executables: []
|
112
88
|
extensions: []
|
113
|
-
extra_rdoc_files:
|
114
|
-
- README.md
|
89
|
+
extra_rdoc_files: []
|
115
90
|
files:
|
91
|
+
- README.md
|
92
|
+
- lib/xpath.rb
|
116
93
|
- lib/xpath/dsl.rb
|
117
94
|
- lib/xpath/expression.rb
|
118
95
|
- lib/xpath/html.rb
|
@@ -120,7 +97,6 @@ files:
|
|
120
97
|
- lib/xpath/renderer.rb
|
121
98
|
- lib/xpath/union.rb
|
122
99
|
- lib/xpath/version.rb
|
123
|
-
- lib/xpath.rb
|
124
100
|
- spec/fixtures/form.html
|
125
101
|
- spec/fixtures/simple.html
|
126
102
|
- spec/fixtures/stuff.html
|
@@ -128,32 +104,28 @@ files:
|
|
128
104
|
- spec/spec_helper.rb
|
129
105
|
- spec/union_spec.rb
|
130
106
|
- spec/xpath_spec.rb
|
131
|
-
|
132
|
-
|
133
|
-
|
107
|
+
homepage: https://github.com/teamcapybara/xpath
|
108
|
+
licenses:
|
109
|
+
- MIT
|
110
|
+
metadata: {}
|
134
111
|
post_install_message:
|
135
|
-
rdoc_options:
|
136
|
-
- --main
|
137
|
-
- README.md
|
112
|
+
rdoc_options: []
|
138
113
|
require_paths:
|
139
114
|
- lib
|
140
115
|
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
-
none: false
|
142
116
|
requirements:
|
143
|
-
- -
|
117
|
+
- - ">="
|
144
118
|
- !ruby/object:Gem::Version
|
145
|
-
version:
|
119
|
+
version: 1.9.3
|
146
120
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
|
-
none: false
|
148
121
|
requirements:
|
149
|
-
- -
|
122
|
+
- - ">="
|
150
123
|
- !ruby/object:Gem::Version
|
151
124
|
version: '0'
|
152
125
|
requirements: []
|
153
|
-
rubyforge_project:
|
154
|
-
rubygems_version:
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 2.5.2
|
155
128
|
signing_key:
|
156
|
-
specification_version:
|
129
|
+
specification_version: 4
|
157
130
|
summary: Generate XPath expressions from Ruby
|
158
131
|
test_files: []
|
159
|
-
has_rdoc:
|
data.tar.gz.sig
DELETED
Binary file
|
metadata.gz.sig
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
Q�יִ.�C�����hiΑj�5hg/�Ϋ!7�z�4� <X��Yn�W�o��A��U����W�]?��5S�,�mj�/Iqp<��|�~��Q�ݺS���ޟD-��@�x�oɶT� K~o{���qΣT���J���F{� �za�=�չ��36���+;SC������1�S&�/I�d���O�8�Nz�`F�4��Y�c�X��G�S�rj4�U�7D�(�{���AO�k�k�u���xa���;��vG�
|