xpath 3.1.0 → 3.2.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 +4 -4
- data/lib/xpath.rb +2 -0
- data/lib/xpath/dsl.rb +38 -29
- data/lib/xpath/expression.rb +3 -1
- data/lib/xpath/literal.rb +2 -0
- data/lib/xpath/renderer.rb +21 -11
- data/lib/xpath/union.rb +4 -2
- data/lib/xpath/version.rb +3 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/union_spec.rb +8 -9
- data/spec/xpath_spec.rb +181 -163
- metadata +20 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a243005210afa835c320ea204ab3105d0b253dfdb905d4ff53c8267e53a23d6d
|
4
|
+
data.tar.gz: 4978108857ddbd50e336926db82b6bb056656861572aa518654cff2dcadb2bc7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e17ddd74ff29ac77050bec7cb5ee93f8b1c2386c56ea4390272d998540a69cf8f26f61541de3a583866b4531836d051e722ca93eabc9d52636e722aa78de491b
|
7
|
+
data.tar.gz: c75b155e85156faafa9e3ac63e7c16b7fbc20385b80ada25dcb5c0c680ea06fd06490a5d2e9096d68a51b22b62b06f5ad6b5d7b6e0191396e748bbd7e415d3ce
|
data/lib/xpath.rb
CHANGED
data/lib/xpath/dsl.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module XPath
|
2
4
|
module DSL
|
3
5
|
def current
|
@@ -80,11 +82,11 @@ module XPath
|
|
80
82
|
# boolean
|
81
83
|
:boolean, :not, :true, :false, :lang,
|
82
84
|
# number
|
83
|
-
:number, :sum, :floor, :ceiling, :round
|
84
|
-
]
|
85
|
+
:number, :sum, :floor, :ceiling, :round
|
86
|
+
].freeze
|
85
87
|
|
86
88
|
METHODS.each do |key|
|
87
|
-
name = key.to_s.
|
89
|
+
name = key.to_s.tr('_', '-').to_sym
|
88
90
|
define_method key do |*args|
|
89
91
|
method(name, *args)
|
90
92
|
end
|
@@ -101,20 +103,20 @@ module XPath
|
|
101
103
|
alias_method :n, :normalize_space
|
102
104
|
|
103
105
|
OPERATORS = [
|
104
|
-
[
|
105
|
-
[
|
106
|
-
[
|
107
|
-
[
|
108
|
-
[
|
109
|
-
[
|
110
|
-
[
|
111
|
-
[
|
112
|
-
[
|
113
|
-
[
|
114
|
-
[
|
115
|
-
[
|
116
|
-
[
|
117
|
-
]
|
106
|
+
%i[equals = ==],
|
107
|
+
%i[or or |],
|
108
|
+
%i[and and &],
|
109
|
+
%i[not_equals != !=],
|
110
|
+
%i[lte <= <=],
|
111
|
+
%i[lt < <],
|
112
|
+
%i[gte >= >=],
|
113
|
+
%i[gt > >],
|
114
|
+
%i[plus +],
|
115
|
+
%i[minus -],
|
116
|
+
%i[multiply * *],
|
117
|
+
%i[divide div /],
|
118
|
+
%i[mod mod %]
|
119
|
+
].freeze
|
118
120
|
|
119
121
|
OPERATORS.each do |(name, operator, alias_name)|
|
120
122
|
define_method name do |rhs|
|
@@ -123,14 +125,14 @@ module XPath
|
|
123
125
|
alias_method alias_name, name if alias_name
|
124
126
|
end
|
125
127
|
|
126
|
-
AXES = [
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
]
|
128
|
+
AXES = %i[
|
129
|
+
ancestor ancestor_or_self attribute descendant_or_self
|
130
|
+
following following_sibling namespace parent preceding
|
131
|
+
preceding_sibling self
|
132
|
+
].freeze
|
131
133
|
|
132
134
|
AXES.each do |key|
|
133
|
-
name = key.to_s.
|
135
|
+
name = key.to_s.tr('_', '-').to_sym
|
134
136
|
define_method key do |*element_names|
|
135
137
|
axis(name, *element_names)
|
136
138
|
end
|
@@ -143,15 +145,22 @@ module XPath
|
|
143
145
|
end
|
144
146
|
|
145
147
|
def contains_word(word)
|
146
|
-
function(:concat,
|
148
|
+
function(:concat, ' ', current.normalize_space, ' ').contains(" #{word} ")
|
149
|
+
end
|
150
|
+
|
151
|
+
UPPERCASE_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'
|
152
|
+
LOWERCASE_LETTERS = 'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'
|
153
|
+
|
154
|
+
def lowercase
|
155
|
+
method(:translate, UPPERCASE_LETTERS, LOWERCASE_LETTERS)
|
156
|
+
end
|
157
|
+
|
158
|
+
def uppercase
|
159
|
+
method(:translate, LOWERCASE_LETTERS, UPPERCASE_LETTERS)
|
147
160
|
end
|
148
161
|
|
149
162
|
def one_of(*expressions)
|
150
|
-
expressions.map
|
151
|
-
current.equals(e)
|
152
|
-
end.reduce do |a, b|
|
153
|
-
a.or(b)
|
154
|
-
end
|
163
|
+
expressions.map { |e| current.equals(e) }.reduce(:or)
|
155
164
|
end
|
156
165
|
|
157
166
|
def next_sibling(*expressions)
|
data/lib/xpath/expression.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module XPath
|
2
4
|
class Expression
|
3
5
|
attr_accessor :expression, :arguments
|
@@ -12,7 +14,7 @@ module XPath
|
|
12
14
|
self
|
13
15
|
end
|
14
16
|
|
15
|
-
def to_xpath(type=nil)
|
17
|
+
def to_xpath(type = nil)
|
16
18
|
Renderer.render(self, type)
|
17
19
|
end
|
18
20
|
alias_method :to_s, :to_xpath
|
data/lib/xpath/literal.rb
CHANGED
data/lib/xpath/renderer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module XPath
|
2
4
|
class Renderer
|
3
5
|
def self.render(node, type)
|
@@ -15,11 +17,11 @@ module XPath
|
|
15
17
|
|
16
18
|
def convert_argument(argument)
|
17
19
|
case argument
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
when Expression, Union then render(argument)
|
21
|
+
when Array then argument.map { |element| convert_argument(element) }
|
22
|
+
when String then string_literal(argument)
|
23
|
+
when Literal then argument.value
|
24
|
+
else argument.to_s
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
@@ -27,7 +29,7 @@ module XPath
|
|
27
29
|
if string.include?("'")
|
28
30
|
string = string.split("'", -1).map do |substr|
|
29
31
|
"'#{substr}'"
|
30
|
-
end.join(%q
|
32
|
+
end.join(%q(,"'",))
|
31
33
|
"concat(#{string})"
|
32
34
|
else
|
33
35
|
"'#{string}'"
|
@@ -51,7 +53,7 @@ module XPath
|
|
51
53
|
end
|
52
54
|
|
53
55
|
def anywhere(element_names)
|
54
|
-
with_element_conditions(
|
56
|
+
with_element_conditions('//', element_names)
|
55
57
|
end
|
56
58
|
|
57
59
|
def where(on, condition)
|
@@ -59,7 +61,11 @@ module XPath
|
|
59
61
|
end
|
60
62
|
|
61
63
|
def attribute(current, name)
|
62
|
-
|
64
|
+
if valid_xml_name?(name)
|
65
|
+
"#{current}/@#{name}"
|
66
|
+
else
|
67
|
+
"#{current}/attribute::*[local-name(.) = #{string_literal(name)}]"
|
68
|
+
end
|
63
69
|
end
|
64
70
|
|
65
71
|
def binary_operator(name, left, right)
|
@@ -68,7 +74,7 @@ module XPath
|
|
68
74
|
|
69
75
|
def is(one, two)
|
70
76
|
if @type == :exact
|
71
|
-
binary_operator(
|
77
|
+
binary_operator('=', one, two)
|
72
78
|
else
|
73
79
|
function(:contains, one, two)
|
74
80
|
end
|
@@ -98,7 +104,7 @@ module XPath
|
|
98
104
|
end
|
99
105
|
|
100
106
|
def function(name, *arguments)
|
101
|
-
"#{name}(#{arguments.join(
|
107
|
+
"#{name}(#{arguments.join(', ')})"
|
102
108
|
end
|
103
109
|
|
104
110
|
private
|
@@ -107,10 +113,14 @@ module XPath
|
|
107
113
|
if element_names.length == 1
|
108
114
|
"#{expression}#{element_names.first}"
|
109
115
|
elsif element_names.length > 1
|
110
|
-
"#{expression}*[#{element_names.map { |e| "self::#{e}" }.join(
|
116
|
+
"#{expression}*[#{element_names.map { |e| "self::#{e}" }.join(' | ')}]"
|
111
117
|
else
|
112
118
|
"#{expression}*"
|
113
119
|
end
|
114
120
|
end
|
121
|
+
|
122
|
+
def valid_xml_name?(name)
|
123
|
+
name =~ /^[a-zA-Z_:][a-zA-Z0-9_:\.\-]*$/
|
124
|
+
end
|
115
125
|
end
|
116
126
|
end
|
data/lib/xpath/union.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module XPath
|
2
4
|
class Union
|
3
5
|
include Enumerable
|
@@ -17,11 +19,11 @@ module XPath
|
|
17
19
|
arguments.each(&block)
|
18
20
|
end
|
19
21
|
|
20
|
-
def method_missing(*args)
|
22
|
+
def method_missing(*args) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
|
21
23
|
XPath::Union.new(*arguments.map { |e| e.send(*args) })
|
22
24
|
end
|
23
25
|
|
24
|
-
def to_xpath(type=nil)
|
26
|
+
def to_xpath(type = nil)
|
25
27
|
Renderer.render(self, type)
|
26
28
|
end
|
27
29
|
alias_method :to_s, :to_xpath
|
data/lib/xpath/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
data/spec/union_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe XPath::Union do
|
@@ -5,7 +7,7 @@ describe XPath::Union do
|
|
5
7
|
let(:doc) { Nokogiri::HTML(template) }
|
6
8
|
|
7
9
|
describe '#expressions' do
|
8
|
-
it
|
10
|
+
it 'should return the expressions' do
|
9
11
|
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
10
12
|
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
11
13
|
@collection = XPath::Union.new(@expr1, @expr2)
|
@@ -14,7 +16,7 @@ describe XPath::Union do
|
|
14
16
|
end
|
15
17
|
|
16
18
|
describe '#each' do
|
17
|
-
it
|
19
|
+
it 'should iterate through the expressions' do
|
18
20
|
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
19
21
|
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
20
22
|
@collection = XPath::Union.new(@expr1, @expr2)
|
@@ -25,16 +27,16 @@ describe XPath::Union do
|
|
25
27
|
end
|
26
28
|
|
27
29
|
describe '#map' do
|
28
|
-
it
|
30
|
+
it 'should map the expressions' do
|
29
31
|
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
30
32
|
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
31
33
|
@collection = XPath::Union.new(@expr1, @expr2)
|
32
|
-
@collection.map
|
34
|
+
@collection.map(&:expression).should eq %i[descendant descendant]
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
36
38
|
describe '#to_xpath' do
|
37
|
-
it
|
39
|
+
it 'should create a valid xpath expression' do
|
38
40
|
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
39
41
|
@expr2 = XPath.generate { |x| x.descendant(:div).where(x.attr(:id) == 'foo') }
|
40
42
|
@collection = XPath::Union.new(@expr1, @expr2)
|
@@ -45,9 +47,8 @@ describe XPath::Union do
|
|
45
47
|
end
|
46
48
|
end
|
47
49
|
|
48
|
-
|
49
50
|
describe '#where and others' do
|
50
|
-
it
|
51
|
+
it 'should be delegated to the individual expressions' do
|
51
52
|
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
52
53
|
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
53
54
|
@collection = XPath::Union.new(@expr1, @expr2)
|
@@ -59,6 +60,4 @@ describe XPath::Union do
|
|
59
60
|
@results[0][:id].should eq 'fooDiv'
|
60
61
|
end
|
61
62
|
end
|
62
|
-
|
63
63
|
end
|
64
|
-
|
data/spec/xpath_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
require 'nokogiri'
|
@@ -14,23 +16,23 @@ describe XPath do
|
|
14
16
|
let(:template) { File.read(File.expand_path('fixtures/simple.html', File.dirname(__FILE__))) }
|
15
17
|
let(:doc) { Nokogiri::HTML(template) }
|
16
18
|
|
17
|
-
def xpath(type=nil, &block)
|
19
|
+
def xpath(type = nil, &block)
|
18
20
|
doc.xpath XPath.generate(&block).to_xpath(type)
|
19
21
|
end
|
20
22
|
|
21
|
-
it
|
23
|
+
it 'should work as a mixin' do
|
22
24
|
xpath = Thingy.new.foo_div.to_xpath
|
23
25
|
doc.xpath(xpath).first[:title].should eq 'fooDiv'
|
24
26
|
end
|
25
27
|
|
26
28
|
describe '#descendant' do
|
27
|
-
it
|
29
|
+
it 'should find nodes that are nested below the current node' do
|
28
30
|
@results = xpath { |x| x.descendant(:p) }
|
29
|
-
@results[0].text.should eq
|
30
|
-
@results[1].text.should eq
|
31
|
+
@results[0].text.should eq 'Blah'
|
32
|
+
@results[1].text.should eq 'Bax'
|
31
33
|
end
|
32
34
|
|
33
|
-
it
|
35
|
+
it 'should not find nodes outside the context' do
|
34
36
|
@results = xpath do |x|
|
35
37
|
foo_div = x.descendant(:div).where(x.attr(:id) == 'foo')
|
36
38
|
x.descendant(:p).where(x.attr(:id) == foo_div.attr(:title))
|
@@ -38,13 +40,13 @@ describe XPath do
|
|
38
40
|
@results[0].should be_nil
|
39
41
|
end
|
40
42
|
|
41
|
-
it
|
43
|
+
it 'should find multiple kinds of nodes' do
|
42
44
|
@results = xpath { |x| x.descendant(:p, :ul) }
|
43
45
|
@results[0].text.should eq 'Blah'
|
44
46
|
@results[3].text.should eq 'A list'
|
45
47
|
end
|
46
48
|
|
47
|
-
it
|
49
|
+
it 'should find all nodes when no arguments given' do
|
48
50
|
@results = xpath { |x| x.descendant[x.attr(:id) == 'foo'].descendant }
|
49
51
|
@results[0].text.should eq 'Blah'
|
50
52
|
@results[4].text.should eq 'A list'
|
@@ -52,24 +54,24 @@ describe XPath do
|
|
52
54
|
end
|
53
55
|
|
54
56
|
describe '#child' do
|
55
|
-
it
|
57
|
+
it 'should find nodes that are nested directly below the current node' do
|
56
58
|
@results = xpath { |x| x.descendant(:div).child(:p) }
|
57
|
-
@results[0].text.should eq
|
58
|
-
@results[1].text.should eq
|
59
|
+
@results[0].text.should eq 'Blah'
|
60
|
+
@results[1].text.should eq 'Bax'
|
59
61
|
end
|
60
62
|
|
61
|
-
it
|
63
|
+
it 'should not find nodes that are nested further down below the current node' do
|
62
64
|
@results = xpath { |x| x.child(:p) }
|
63
65
|
@results[0].should be_nil
|
64
66
|
end
|
65
67
|
|
66
|
-
it
|
68
|
+
it 'should find multiple kinds of nodes' do
|
67
69
|
@results = xpath { |x| x.descendant(:div).child(:p, :ul) }
|
68
70
|
@results[0].text.should eq 'Blah'
|
69
71
|
@results[3].text.should eq 'A list'
|
70
72
|
end
|
71
73
|
|
72
|
-
it
|
74
|
+
it 'should find all nodes when no arguments given' do
|
73
75
|
@results = xpath { |x| x.descendant[x.attr(:id) == 'foo'].child }
|
74
76
|
@results[0].text.should eq 'Blah'
|
75
77
|
@results[3].text.should eq 'A list'
|
@@ -77,19 +79,19 @@ describe XPath do
|
|
77
79
|
end
|
78
80
|
|
79
81
|
describe '#axis' do
|
80
|
-
it
|
82
|
+
it 'should find nodes given the xpath axis' do
|
81
83
|
@results = xpath { |x| x.axis(:descendant, :p) }
|
82
|
-
@results[0].text.should eq
|
84
|
+
@results[0].text.should eq 'Blah'
|
83
85
|
end
|
84
86
|
|
85
|
-
it
|
87
|
+
it 'should find nodes given the xpath axis without a specific tag' do
|
86
88
|
@results = xpath { |x| x.descendant(:div)[x.attr(:id) == 'foo'].axis(:descendant) }
|
87
|
-
@results[0][:id].should eq
|
89
|
+
@results[0][:id].should eq 'fooDiv'
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|
91
93
|
describe '#next_sibling' do
|
92
|
-
it
|
94
|
+
it 'should find nodes which are immediate siblings of the current node' do
|
93
95
|
xpath { |x| x.descendant(:p)[x.attr(:id) == 'fooDiv'].next_sibling(:p) }.first.text.should eq 'Bax'
|
94
96
|
xpath { |x| x.descendant(:p)[x.attr(:id) == 'fooDiv'].next_sibling(:ul, :p) }.first.text.should eq 'Bax'
|
95
97
|
xpath { |x| x.descendant(:p)[x.attr(:title) == 'monkey'].next_sibling(:ul, :p) }.first.text.should eq 'A list'
|
@@ -99,7 +101,7 @@ describe XPath do
|
|
99
101
|
end
|
100
102
|
|
101
103
|
describe '#previous_sibling' do
|
102
|
-
it
|
104
|
+
it 'should find nodes which are exactly preceding the current node' do
|
103
105
|
xpath { |x| x.descendant(:p)[x.attr(:id) == 'wooDiv'].previous_sibling(:p) }.first.text.should eq 'Bax'
|
104
106
|
xpath { |x| x.descendant(:p)[x.attr(:id) == 'wooDiv'].previous_sibling(:ul, :p) }.first.text.should eq 'Bax'
|
105
107
|
xpath { |x| x.descendant(:p)[x.attr(:title) == 'gorilla'].previous_sibling(:ul, :p) }.first.text.should eq 'A list'
|
@@ -109,17 +111,17 @@ describe XPath do
|
|
109
111
|
end
|
110
112
|
|
111
113
|
describe '#anywhere' do
|
112
|
-
it
|
114
|
+
it 'should find nodes regardless of the context' do
|
113
115
|
@results = xpath do |x|
|
114
116
|
foo_div = x.anywhere(:div).where(x.attr(:id) == 'foo')
|
115
117
|
x.descendant(:p).where(x.attr(:id) == foo_div.attr(:title))
|
116
118
|
end
|
117
|
-
@results[0].text.should eq
|
119
|
+
@results[0].text.should eq 'Blah'
|
118
120
|
end
|
119
121
|
|
120
|
-
it
|
122
|
+
it 'should find multiple kinds of nodes regardless of the context' do
|
121
123
|
@results = xpath do |x|
|
122
|
-
context=x.descendant(:div).where(x.attr(:id)=='woo')
|
124
|
+
context = x.descendant(:div).where(x.attr(:id) == 'woo')
|
123
125
|
context.anywhere(:p, :ul)
|
124
126
|
end
|
125
127
|
|
@@ -129,9 +131,9 @@ describe XPath do
|
|
129
131
|
@results[6].text.should eq 'Bax'
|
130
132
|
end
|
131
133
|
|
132
|
-
it
|
134
|
+
it 'should find all nodes when no arguments given regardless of the context' do
|
133
135
|
@results = xpath do |x|
|
134
|
-
context=x.descendant(:div).where(x.attr(:id)=='woo')
|
136
|
+
context = x.descendant(:div).where(x.attr(:id) == 'woo')
|
135
137
|
context.anywhere
|
136
138
|
end
|
137
139
|
@results[0].name.should eq 'html'
|
@@ -142,72 +144,89 @@ describe XPath do
|
|
142
144
|
@results[13].text.should eq 'A list'
|
143
145
|
@results[15].text.should eq 'Bax'
|
144
146
|
end
|
145
|
-
|
146
147
|
end
|
147
148
|
|
148
149
|
describe '#contains' do
|
149
|
-
it
|
150
|
+
it 'should find nodes that contain the given string' do
|
150
151
|
@results = xpath do |x|
|
151
152
|
x.descendant(:div).where(x.attr(:title).contains('ooD'))
|
152
153
|
end
|
153
|
-
@results[0][:id].should eq
|
154
|
+
@results[0][:id].should eq 'foo'
|
154
155
|
end
|
155
156
|
|
156
|
-
it
|
157
|
+
it 'should find nodes that contain the given expression' do
|
157
158
|
@results = xpath do |x|
|
158
159
|
expression = x.anywhere(:div).where(x.attr(:title) == 'fooDiv').attr(:id)
|
159
160
|
x.descendant(:div).where(x.attr(:title).contains(expression))
|
160
161
|
end
|
161
|
-
@results[0][:id].should eq
|
162
|
+
@results[0][:id].should eq 'foo'
|
162
163
|
end
|
163
164
|
end
|
164
165
|
|
165
|
-
describe
|
166
|
-
it
|
166
|
+
describe '#contains_word' do
|
167
|
+
it 'should find nodes that contain the given word in its entirety' do
|
167
168
|
@results = xpath do |x|
|
168
169
|
x.descendant.where(x.attr(:class).contains_word('fish'))
|
169
170
|
end
|
170
|
-
@results[0].text.should eq
|
171
|
-
@results[1].text.should eq
|
171
|
+
@results[0].text.should eq 'Bax'
|
172
|
+
@results[1].text.should eq 'llama'
|
172
173
|
@results.length.should eq 2
|
173
174
|
end
|
174
175
|
end
|
175
176
|
|
176
177
|
describe '#starts_with' do
|
177
|
-
it
|
178
|
+
it 'should find nodes that begin with the given string' do
|
178
179
|
@results = xpath do |x|
|
179
180
|
x.descendant(:*).where(x.attr(:id).starts_with('foo'))
|
180
181
|
end
|
181
182
|
@results.size.should eq 2
|
182
|
-
@results[0][:id].should eq
|
183
|
-
@results[1][:id].should eq
|
183
|
+
@results[0][:id].should eq 'foo'
|
184
|
+
@results[1][:id].should eq 'fooDiv'
|
184
185
|
end
|
185
186
|
|
186
|
-
it
|
187
|
+
it 'should find nodes that contain the given expression' do
|
187
188
|
@results = xpath do |x|
|
188
189
|
expression = x.anywhere(:div).where(x.attr(:title) == 'fooDiv').attr(:id)
|
189
190
|
x.descendant(:div).where(x.attr(:title).starts_with(expression))
|
190
191
|
end
|
191
|
-
@results[0][:id].should eq
|
192
|
+
@results[0][:id].should eq 'foo'
|
192
193
|
end
|
193
194
|
end
|
194
195
|
|
195
196
|
describe '#ends_with' do
|
196
|
-
it
|
197
|
+
it 'should find nodes that end with the given string' do
|
197
198
|
@results = xpath do |x|
|
198
199
|
x.descendant(:*).where(x.attr(:id).ends_with('oof'))
|
199
200
|
end
|
200
201
|
@results.size.should eq 2
|
201
|
-
@results[0][:id].should eq
|
202
|
-
@results[1][:id].should eq
|
202
|
+
@results[0][:id].should eq 'oof'
|
203
|
+
@results[1][:id].should eq 'viDoof'
|
203
204
|
end
|
204
205
|
|
205
|
-
it
|
206
|
+
it 'should find nodes that contain the given expression' do
|
206
207
|
@results = xpath do |x|
|
207
208
|
expression = x.anywhere(:div).where(x.attr(:title) == 'viDoof').attr(:id)
|
208
209
|
x.descendant(:div).where(x.attr(:title).ends_with(expression))
|
209
210
|
end
|
210
|
-
@results[0][:id].should eq
|
211
|
+
@results[0][:id].should eq 'oof'
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe '#uppercase' do
|
216
|
+
it 'should match uppercased text' do
|
217
|
+
@results = xpath do |x|
|
218
|
+
x.descendant(:div).where(x.attr(:title).uppercase == 'VIDOOF')
|
219
|
+
end
|
220
|
+
@results[0][:id].should eq 'oof'
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe '#lowercase' do
|
225
|
+
it 'should match lowercased text' do
|
226
|
+
@results = xpath do |x|
|
227
|
+
x.descendant(:div).where(x.attr(:title).lowercase == 'vidoof')
|
228
|
+
end
|
229
|
+
@results[0][:id].should eq 'oof'
|
211
230
|
end
|
212
231
|
end
|
213
232
|
|
@@ -222,177 +241,176 @@ describe XPath do
|
|
222
241
|
end
|
223
242
|
|
224
243
|
describe '#substring' do
|
225
|
-
context
|
226
|
-
it
|
227
|
-
@results = xpath { |x| x.descendant(:span).where(x.attr(:id) ==
|
228
|
-
@results.should eq
|
244
|
+
context 'when called with one argument' do
|
245
|
+
it 'should select the part of a string after the specified character' do
|
246
|
+
@results = xpath { |x| x.descendant(:span).where(x.attr(:id) == 'substring').text.substring(7) }
|
247
|
+
@results.should eq 'there'
|
229
248
|
end
|
230
249
|
end
|
231
250
|
|
232
|
-
context
|
233
|
-
it
|
234
|
-
@results = xpath { |x| x.descendant(:span).where(x.attr(:id) ==
|
235
|
-
@results.should eq
|
251
|
+
context 'when called with two arguments' do
|
252
|
+
it 'should select the part of a string after the specified character, up to the given length' do
|
253
|
+
@results = xpath { |x| x.descendant(:span).where(x.attr(:id) == 'substring').text.substring(2, 4) }
|
254
|
+
@results.should eq 'ello'
|
236
255
|
end
|
237
256
|
end
|
238
257
|
end
|
239
258
|
|
240
259
|
describe '#function' do
|
241
|
-
it
|
260
|
+
it 'should call the given xpath function' do
|
242
261
|
@results = xpath { |x| x.function(:boolean, x.function(:true) == x.function(:false)) }
|
243
262
|
@results.should eq false
|
244
263
|
end
|
245
264
|
end
|
246
265
|
|
247
266
|
describe '#method' do
|
248
|
-
it
|
249
|
-
@results = xpath { |x| x.descendant(:span).where(x.attr(:id) ==
|
267
|
+
it 'should call the given xpath function with the current node as the first argument' do
|
268
|
+
@results = xpath { |x| x.descendant(:span).where(x.attr(:id) == 'string-length').text.method(:"string-length") }
|
250
269
|
@results.should eq 11
|
251
270
|
end
|
252
271
|
end
|
253
272
|
|
254
273
|
describe '#string_length' do
|
255
|
-
it
|
256
|
-
@results = xpath { |x| x.descendant(:span).where(x.attr(:id) ==
|
274
|
+
it 'should return the length of a string' do
|
275
|
+
@results = xpath { |x| x.descendant(:span).where(x.attr(:id) == 'string-length').text.string_length }
|
257
276
|
@results.should eq 11
|
258
277
|
end
|
259
278
|
end
|
260
279
|
|
261
280
|
describe '#where' do
|
262
|
-
it
|
263
|
-
xpath { |x| x.descendant(:div).where(:"@id = 'foo'") }.first[:title].should eq
|
281
|
+
it 'should limit the expression to find only certain nodes' do
|
282
|
+
xpath { |x| x.descendant(:div).where(:"@id = 'foo'") }.first[:title].should eq 'fooDiv'
|
264
283
|
end
|
265
284
|
|
266
|
-
it
|
267
|
-
xpath { |x| x.descendant(:div)[:"@id = 'foo'"] }.first[:title].should eq
|
285
|
+
it 'should be aliased as []' do
|
286
|
+
xpath { |x| x.descendant(:div)[:"@id = 'foo'"] }.first[:title].should eq 'fooDiv'
|
268
287
|
end
|
269
288
|
|
270
|
-
it
|
271
|
-
XPath.descendant(:div).where(nil).to_s.should eq
|
289
|
+
it 'should be a no-op when nil condition is passed' do
|
290
|
+
XPath.descendant(:div).where(nil).to_s.should eq './/div'
|
272
291
|
end
|
273
292
|
end
|
274
293
|
|
275
294
|
describe '#inverse' do
|
276
|
-
it
|
295
|
+
it 'should invert the expression' do
|
277
296
|
xpath { |x| x.descendant(:p).where(x.attr(:id).equals('fooDiv').inverse) }.first.text.should eq 'Bax'
|
278
297
|
end
|
279
298
|
|
280
|
-
it
|
299
|
+
it 'should be aliased as the unary tilde' do
|
281
300
|
xpath { |x| x.descendant(:p).where(~x.attr(:id).equals('fooDiv')) }.first.text.should eq 'Bax'
|
282
301
|
end
|
283
302
|
|
284
|
-
it
|
303
|
+
it 'should be aliased as the unary bang' do
|
285
304
|
xpath { |x| x.descendant(:p).where(!x.attr(:id).equals('fooDiv')) }.first.text.should eq 'Bax'
|
286
305
|
end
|
287
306
|
end
|
288
307
|
|
289
308
|
describe '#equals' do
|
290
|
-
it
|
291
|
-
xpath { |x| x.descendant(:div).where(x.attr(:id).equals('foo')) }.first[:title].should eq
|
309
|
+
it 'should limit the expression to find only certain nodes' do
|
310
|
+
xpath { |x| x.descendant(:div).where(x.attr(:id).equals('foo')) }.first[:title].should eq 'fooDiv'
|
292
311
|
end
|
293
312
|
|
294
|
-
it
|
295
|
-
xpath { |x| x.descendant(:div).where(x.attr(:id) == 'foo') }.first[:title].should eq
|
313
|
+
it 'should be aliased as ==' do
|
314
|
+
xpath { |x| x.descendant(:div).where(x.attr(:id) == 'foo') }.first[:title].should eq 'fooDiv'
|
296
315
|
end
|
297
316
|
end
|
298
317
|
|
299
318
|
describe '#not_equals' do
|
300
|
-
it
|
301
|
-
xpath { |x| x.descendant(:div).where(x.attr(:id).not_equals('bar')) }.first[:title].should eq
|
319
|
+
it 'should match only when not equal' do
|
320
|
+
xpath { |x| x.descendant(:div).where(x.attr(:id).not_equals('bar')) }.first[:title].should eq 'fooDiv'
|
302
321
|
end
|
303
322
|
|
304
|
-
it
|
305
|
-
xpath { |x| x.descendant(:div).where(x.attr(:id) != 'bar') }.first[:title].should eq
|
323
|
+
it 'should be aliased as !=' do
|
324
|
+
xpath { |x| x.descendant(:div).where(x.attr(:id) != 'bar') }.first[:title].should eq 'fooDiv'
|
306
325
|
end
|
307
326
|
end
|
308
327
|
|
309
|
-
|
310
328
|
describe '#is' do
|
311
|
-
it
|
312
|
-
xpath(:exact) { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should eq
|
329
|
+
it 'uses equality when :exact given' do
|
330
|
+
xpath(:exact) { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should eq 'fooDiv'
|
313
331
|
xpath(:exact) { |x| x.descendant(:div).where(x.attr(:id).is('oo')) }.first.should be_nil
|
314
332
|
end
|
315
333
|
|
316
|
-
it
|
317
|
-
xpath { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should eq
|
318
|
-
xpath { |x| x.descendant(:div).where(x.attr(:id).is('oo')) }.first[:title].should eq
|
334
|
+
it 'uses substring matching otherwise' do
|
335
|
+
xpath { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should eq 'fooDiv'
|
336
|
+
xpath { |x| x.descendant(:div).where(x.attr(:id).is('oo')) }.first[:title].should eq 'fooDiv'
|
319
337
|
end
|
320
338
|
end
|
321
339
|
|
322
340
|
describe '#one_of' do
|
323
|
-
it
|
341
|
+
it 'should return all nodes where the condition matches' do
|
324
342
|
@results = xpath do |x|
|
325
343
|
p = x.anywhere(:div).where(x.attr(:id) == 'foo').attr(:title)
|
326
344
|
x.descendant(:*).where(x.attr(:id).one_of('foo', p, 'baz'))
|
327
345
|
end
|
328
|
-
@results[0][:title].should eq
|
329
|
-
@results[1].text.should eq
|
330
|
-
@results[2][:title].should eq
|
346
|
+
@results[0][:title].should eq 'fooDiv'
|
347
|
+
@results[1].text.should eq 'Blah'
|
348
|
+
@results[2][:title].should eq 'bazDiv'
|
331
349
|
end
|
332
350
|
end
|
333
351
|
|
334
352
|
describe '#and' do
|
335
|
-
it
|
353
|
+
it 'should find all nodes in both expression' do
|
336
354
|
@results = xpath do |x|
|
337
355
|
x.descendant(:*).where(x.contains('Bax').and(x.attr(:title).equals('monkey')))
|
338
356
|
end
|
339
|
-
@results[0][:title].should eq
|
357
|
+
@results[0][:title].should eq 'monkey'
|
340
358
|
end
|
341
359
|
|
342
|
-
it
|
360
|
+
it 'should be aliased as ampersand (&)' do
|
343
361
|
@results = xpath do |x|
|
344
362
|
x.descendant(:*).where(x.contains('Bax') & x.attr(:title).equals('monkey'))
|
345
363
|
end
|
346
|
-
@results[0][:title].should eq
|
364
|
+
@results[0][:title].should eq 'monkey'
|
347
365
|
end
|
348
366
|
end
|
349
367
|
|
350
368
|
describe '#or' do
|
351
|
-
it
|
369
|
+
it 'should find all nodes in either expression' do
|
352
370
|
@results = xpath do |x|
|
353
371
|
x.descendant(:*).where(x.attr(:id).equals('foo').or(x.attr(:id).equals('fooDiv')))
|
354
372
|
end
|
355
|
-
@results[0][:title].should eq
|
356
|
-
@results[1].text.should eq
|
373
|
+
@results[0][:title].should eq 'fooDiv'
|
374
|
+
@results[1].text.should eq 'Blah'
|
357
375
|
end
|
358
376
|
|
359
|
-
it
|
377
|
+
it 'should be aliased as pipe (|)' do
|
360
378
|
@results = xpath do |x|
|
361
379
|
x.descendant(:*).where(x.attr(:id).equals('foo') | x.attr(:id).equals('fooDiv'))
|
362
380
|
end
|
363
|
-
@results[0][:title].should eq
|
364
|
-
@results[1].text.should eq
|
381
|
+
@results[0][:title].should eq 'fooDiv'
|
382
|
+
@results[1].text.should eq 'Blah'
|
365
383
|
end
|
366
384
|
end
|
367
385
|
|
368
386
|
describe '#attr' do
|
369
|
-
it
|
387
|
+
it 'should be an attribute' do
|
370
388
|
@results = xpath { |x| x.descendant(:div).where(x.attr(:id)) }
|
371
|
-
@results[0][:title].should eq
|
372
|
-
@results[1][:title].should eq
|
389
|
+
@results[0][:title].should eq 'barDiv'
|
390
|
+
@results[1][:title].should eq 'fooDiv'
|
373
391
|
end
|
374
392
|
end
|
375
393
|
|
376
394
|
describe '#css' do
|
377
|
-
it
|
395
|
+
it 'should find nodes by the given CSS selector' do
|
378
396
|
@results = xpath { |x| x.css('#preference p') }
|
379
397
|
@results[0].text.should eq 'allamas'
|
380
398
|
@results[1].text.should eq 'llama'
|
381
399
|
end
|
382
400
|
|
383
|
-
it
|
401
|
+
it 'should respect previous expression' do
|
384
402
|
@results = xpath { |x| x.descendant[x.attr(:id) == 'moar'].css('p') }
|
385
403
|
@results[0].text.should eq 'chimp'
|
386
404
|
@results[1].text.should eq 'flamingo'
|
387
405
|
end
|
388
406
|
|
389
|
-
it
|
407
|
+
it 'should be composable' do
|
390
408
|
@results = xpath { |x| x.css('#moar').descendant(:p) }
|
391
409
|
@results[0].text.should eq 'chimp'
|
392
410
|
@results[1].text.should eq 'flamingo'
|
393
411
|
end
|
394
412
|
|
395
|
-
it
|
413
|
+
it 'should allow comma separated selectors' do
|
396
414
|
@results = xpath { |x| x.descendant[x.attr(:id) == 'moar'].css('div, p') }
|
397
415
|
@results[0].text.should eq 'chimp'
|
398
416
|
@results[1].text.should eq 'elephant'
|
@@ -402,12 +420,12 @@ describe XPath do
|
|
402
420
|
|
403
421
|
describe '#qname' do
|
404
422
|
it "should match the node's name" do
|
405
|
-
xpath { |x| x.descendant(:*).where(x.qname == 'ul') }.first.text.should eq
|
423
|
+
xpath { |x| x.descendant(:*).where(x.qname == 'ul') }.first.text.should eq 'A list'
|
406
424
|
end
|
407
425
|
end
|
408
426
|
|
409
427
|
describe '#union' do
|
410
|
-
it
|
428
|
+
it 'should create a union expression' do
|
411
429
|
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
412
430
|
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
413
431
|
@collection = @expr1.union(@expr2)
|
@@ -419,7 +437,7 @@ describe XPath do
|
|
419
437
|
@results[0][:id].should eq 'fooDiv'
|
420
438
|
end
|
421
439
|
|
422
|
-
it
|
440
|
+
it 'should be aliased as +' do
|
423
441
|
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
424
442
|
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
425
443
|
@collection = @expr1 + @expr2
|
@@ -432,113 +450,113 @@ describe XPath do
|
|
432
450
|
end
|
433
451
|
end
|
434
452
|
|
435
|
-
describe
|
436
|
-
it
|
453
|
+
describe '#last' do
|
454
|
+
it 'returns the number of elements in the context' do
|
437
455
|
@results = xpath { |x| x.descendant(:p)[XPath.position() == XPath.last()] }
|
438
|
-
@results[0].text.should eq
|
439
|
-
@results[1].text.should eq
|
440
|
-
@results[2].text.should eq
|
456
|
+
@results[0].text.should eq 'Bax'
|
457
|
+
@results[1].text.should eq 'Blah'
|
458
|
+
@results[2].text.should eq 'llama'
|
441
459
|
end
|
442
460
|
end
|
443
461
|
|
444
|
-
describe
|
445
|
-
it
|
462
|
+
describe '#position' do
|
463
|
+
it 'returns the position of elements in the context' do
|
446
464
|
@results = xpath { |x| x.descendant(:p)[XPath.position() == 2] }
|
447
|
-
@results[0].text.should eq
|
448
|
-
@results[1].text.should eq
|
465
|
+
@results[0].text.should eq 'Bax'
|
466
|
+
@results[1].text.should eq 'Bax'
|
449
467
|
end
|
450
468
|
end
|
451
469
|
|
452
|
-
describe
|
453
|
-
it
|
470
|
+
describe '#count' do
|
471
|
+
it 'counts the number of occurrences' do
|
454
472
|
@results = xpath { |x| x.descendant(:div)[x.descendant(:p).count == 2] }
|
455
|
-
@results[0][:id].should eq
|
473
|
+
@results[0][:id].should eq 'preference'
|
456
474
|
end
|
457
475
|
end
|
458
476
|
|
459
|
-
describe
|
460
|
-
it
|
477
|
+
describe '#lte' do
|
478
|
+
it 'checks lesser than or equal' do
|
461
479
|
@results = xpath { |x| x.descendant(:p)[XPath.position() <= 2] }
|
462
|
-
@results[0].text.should eq
|
463
|
-
@results[1].text.should eq
|
464
|
-
@results[2][:title].should eq
|
465
|
-
@results[3].text.should eq
|
480
|
+
@results[0].text.should eq 'Blah'
|
481
|
+
@results[1].text.should eq 'Bax'
|
482
|
+
@results[2][:title].should eq 'gorilla'
|
483
|
+
@results[3].text.should eq 'Bax'
|
466
484
|
end
|
467
485
|
end
|
468
486
|
|
469
|
-
describe
|
470
|
-
it
|
487
|
+
describe '#lt' do
|
488
|
+
it 'checks lesser than' do
|
471
489
|
@results = xpath { |x| x.descendant(:p)[XPath.position() < 2] }
|
472
|
-
@results[0].text.should eq
|
473
|
-
@results[1][:title].should eq
|
490
|
+
@results[0].text.should eq 'Blah'
|
491
|
+
@results[1][:title].should eq 'gorilla'
|
474
492
|
end
|
475
493
|
end
|
476
494
|
|
477
|
-
describe
|
478
|
-
it
|
495
|
+
describe '#gte' do
|
496
|
+
it 'checks greater than or equal' do
|
479
497
|
@results = xpath { |x| x.descendant(:p)[XPath.position() >= 2] }
|
480
|
-
@results[0].text.should eq
|
481
|
-
@results[1][:title].should eq
|
482
|
-
@results[2].text.should eq
|
483
|
-
@results[3].text.should eq
|
498
|
+
@results[0].text.should eq 'Bax'
|
499
|
+
@results[1][:title].should eq 'monkey'
|
500
|
+
@results[2].text.should eq 'Bax'
|
501
|
+
@results[3].text.should eq 'Blah'
|
484
502
|
end
|
485
503
|
end
|
486
504
|
|
487
|
-
describe
|
488
|
-
it
|
505
|
+
describe '#gt' do
|
506
|
+
it 'checks greater than' do
|
489
507
|
@results = xpath { |x| x.descendant(:p)[XPath.position() > 2] }
|
490
|
-
@results[0][:title].should eq
|
491
|
-
@results[1].text.should eq
|
508
|
+
@results[0][:title].should eq 'monkey'
|
509
|
+
@results[1].text.should eq 'Blah'
|
492
510
|
end
|
493
511
|
end
|
494
512
|
|
495
|
-
describe
|
496
|
-
it
|
513
|
+
describe '#plus' do
|
514
|
+
it 'adds stuff' do
|
497
515
|
@results = xpath { |x| x.descendant(:p)[XPath.position().plus(1) == 2] }
|
498
|
-
@results[0][:id].should eq
|
499
|
-
@results[1][:title].should eq
|
516
|
+
@results[0][:id].should eq 'fooDiv'
|
517
|
+
@results[1][:title].should eq 'gorilla'
|
500
518
|
end
|
501
519
|
end
|
502
520
|
|
503
|
-
describe
|
504
|
-
it
|
521
|
+
describe '#minus' do
|
522
|
+
it 'subtracts stuff' do
|
505
523
|
@results = xpath { |x| x.descendant(:p)[XPath.position().minus(1) == 0] }
|
506
|
-
@results[0][:id].should eq
|
507
|
-
@results[1][:title].should eq
|
524
|
+
@results[0][:id].should eq 'fooDiv'
|
525
|
+
@results[1][:title].should eq 'gorilla'
|
508
526
|
end
|
509
527
|
end
|
510
528
|
|
511
|
-
describe
|
512
|
-
it
|
529
|
+
describe '#multiply' do
|
530
|
+
it 'multiplies stuff' do
|
513
531
|
@results = xpath { |x| x.descendant(:p)[XPath.position() * 3 == 3] }
|
514
|
-
@results[0][:id].should eq
|
515
|
-
@results[1][:title].should eq
|
532
|
+
@results[0][:id].should eq 'fooDiv'
|
533
|
+
@results[1][:title].should eq 'gorilla'
|
516
534
|
end
|
517
535
|
end
|
518
536
|
|
519
|
-
describe
|
520
|
-
it
|
537
|
+
describe '#divide' do
|
538
|
+
it 'divides stuff' do
|
521
539
|
@results = xpath { |x| x.descendant(:p)[XPath.position() / 2 == 1] }
|
522
|
-
@results[0].text.should eq
|
523
|
-
@results[1].text.should eq
|
540
|
+
@results[0].text.should eq 'Bax'
|
541
|
+
@results[1].text.should eq 'Bax'
|
524
542
|
end
|
525
543
|
end
|
526
544
|
|
527
|
-
describe
|
528
|
-
it
|
545
|
+
describe '#mod' do
|
546
|
+
it 'take modulo' do
|
529
547
|
@results = xpath { |x| x.descendant(:p)[XPath.position() % 2 == 1] }
|
530
|
-
@results[0].text.should eq
|
531
|
-
@results[1][:title].should eq
|
532
|
-
@results[2][:title].should eq
|
548
|
+
@results[0].text.should eq 'Blah'
|
549
|
+
@results[1][:title].should eq 'monkey'
|
550
|
+
@results[2][:title].should eq 'gorilla'
|
533
551
|
end
|
534
552
|
end
|
535
553
|
|
536
|
-
describe
|
537
|
-
it
|
554
|
+
describe '#ancestor' do
|
555
|
+
it 'finds ancestor nodes' do
|
538
556
|
@results = xpath { |x| x.descendant(:p)[1].ancestor }
|
539
|
-
@results[0].node_name.should eq
|
540
|
-
@results[1].node_name.should eq
|
541
|
-
@results[2][:id].should eq
|
557
|
+
@results[0].node_name.should eq 'html'
|
558
|
+
@results[1].node_name.should eq 'body'
|
559
|
+
@results[2][:id].should eq 'foo'
|
542
560
|
end
|
543
561
|
end
|
544
562
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xpath
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Nicklas
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain:
|
11
11
|
- gem-public_cert.pem
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-10-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
@@ -26,61 +26,61 @@ dependencies:
|
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '1.8'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
|
-
name:
|
29
|
+
name: pry
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- - "
|
32
|
+
- - ">="
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
34
|
+
version: '0'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- - "
|
39
|
+
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '
|
41
|
+
version: '0'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
|
-
name:
|
43
|
+
name: rake
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
46
|
- - ">="
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: 0
|
48
|
+
version: '0'
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
53
|
- - ">="
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: 0
|
55
|
+
version: '0'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
|
-
name:
|
57
|
+
name: rspec
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
59
59
|
requirements:
|
60
|
-
- - "
|
60
|
+
- - "~>"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: '0'
|
62
|
+
version: '3.0'
|
63
63
|
type: :development
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
|
-
- - "
|
67
|
+
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
69
|
+
version: '3.0'
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
|
-
name:
|
71
|
+
name: yard
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
74
|
- - ">="
|
75
75
|
- !ruby/object:Gem::Version
|
76
|
-
version:
|
76
|
+
version: 0.5.8
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
81
|
- - ">="
|
82
82
|
- !ruby/object:Gem::Version
|
83
|
-
version:
|
83
|
+
version: 0.5.8
|
84
84
|
description: XPath is a Ruby DSL for generating XPath expressions
|
85
85
|
email:
|
86
86
|
- jonas.nicklas@gmail.com
|
@@ -114,7 +114,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
114
114
|
requirements:
|
115
115
|
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '2.
|
117
|
+
version: '2.3'
|
118
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
119
|
requirements:
|
120
120
|
- - ">="
|
@@ -122,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
122
|
version: '0'
|
123
123
|
requirements: []
|
124
124
|
rubyforge_project:
|
125
|
-
rubygems_version: 2.7.
|
125
|
+
rubygems_version: 2.7.7
|
126
126
|
signing_key:
|
127
127
|
specification_version: 4
|
128
128
|
summary: Generate XPath expressions from Ruby
|