xpath 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
-
[](http://badge.fury.io/rb/xpath)
|
7
|
+
[](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�
|