xpath 2.0.0 → 3.2.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/dsl.rb +141 -79
- data/lib/xpath/expression.rb +4 -2
- data/lib/xpath/literal.rb +2 -0
- data/lib/xpath/renderer.rb +34 -82
- data/lib/xpath/union.rb +4 -2
- data/lib/xpath/version.rb +3 -1
- data/lib/xpath.rb +4 -4
- data/spec/fixtures/simple.html +11 -3
- data/spec/spec_helper.rb +7 -0
- data/spec/union_spec.rb +15 -16
- data/spec/xpath_spec.rb +336 -123
- metadata +50 -80
- data/lib/xpath/html.rb +0 -175
- data/spec/html_spec.rb +0 -308
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -1
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a243005210afa835c320ea204ab3105d0b253dfdb905d4ff53c8267e53a23d6d
|
4
|
+
data.tar.gz: 4978108857ddbd50e336926db82b6bb056656861572aa518654cff2dcadb2bc7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e17ddd74ff29ac77050bec7cb5ee93f8b1c2386c56ea4390272d998540a69cf8f26f61541de3a583866b4531836d051e722ca93eabc9d52636e722aa78de491b
|
7
|
+
data.tar.gz: c75b155e85156faafa9e3ac63e7c16b7fbc20385b80ada25dcb5c0c680ea06fd06490a5d2e9096d68a51b22b62b06f5ad6b5d7b6e0191396e748bbd7e415d3ce
|
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/dsl.rb
CHANGED
@@ -1,112 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module XPath
|
2
4
|
module DSL
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
end
|
5
|
+
def current
|
6
|
+
Expression.new(:this_node)
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
def descendant(*expressions)
|
10
|
+
Expression.new(:descendant, current, expressions)
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
def child(*expressions)
|
14
|
+
Expression.new(:child, current, expressions)
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
def axis(name, *element_names)
|
18
|
+
Expression.new(:axis, current, name, element_names)
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
def anywhere(*expressions)
|
22
|
+
Expression.new(:anywhere, expressions)
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
def attr(expression)
|
26
|
+
Expression.new(:attribute, current, expression)
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
def text
|
30
|
+
Expression.new(:text, current)
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
def css(selector)
|
34
|
+
Expression.new(:css, current, Literal.new(selector))
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
def function(name, *arguments)
|
38
|
+
Expression.new(:function, name, *arguments)
|
39
|
+
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
def method(name, *arguments)
|
42
|
+
Expression.new(:function, name, current, *arguments)
|
43
|
+
end
|
43
44
|
|
44
|
-
|
45
|
-
|
45
|
+
def where(expression)
|
46
|
+
if expression
|
47
|
+
Expression.new(:where, current, expression)
|
48
|
+
else
|
49
|
+
current
|
46
50
|
end
|
51
|
+
end
|
52
|
+
alias_method :[], :where
|
47
53
|
|
48
|
-
|
49
|
-
|
50
|
-
|
54
|
+
def is(expression)
|
55
|
+
Expression.new(:is, current, expression)
|
56
|
+
end
|
51
57
|
|
52
|
-
|
53
|
-
|
54
|
-
|
58
|
+
def binary_operator(name, rhs)
|
59
|
+
Expression.new(:binary_operator, name, current, rhs)
|
60
|
+
end
|
55
61
|
|
56
|
-
|
57
|
-
|
58
|
-
end
|
62
|
+
def union(*expressions)
|
63
|
+
Union.new(*[self, expressions].flatten)
|
59
64
|
end
|
65
|
+
alias_method :+, :union
|
60
66
|
|
61
|
-
|
62
|
-
|
67
|
+
def last
|
68
|
+
function(:last)
|
69
|
+
end
|
63
70
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
alias_method :[], :where
|
71
|
+
def position
|
72
|
+
function(:position)
|
73
|
+
end
|
68
74
|
|
69
|
-
|
70
|
-
|
75
|
+
METHODS = [
|
76
|
+
# node set
|
77
|
+
:count, :id, :local_name, :namespace_uri,
|
78
|
+
# string
|
79
|
+
:string, :concat, :starts_with, :contains, :substring_before,
|
80
|
+
:substring_after, :substring, :string_length, :normalize_space,
|
81
|
+
:translate,
|
82
|
+
# boolean
|
83
|
+
:boolean, :not, :true, :false, :lang,
|
84
|
+
# number
|
85
|
+
:number, :sum, :floor, :ceiling, :round
|
86
|
+
].freeze
|
87
|
+
|
88
|
+
METHODS.each do |key|
|
89
|
+
name = key.to_s.tr('_', '-').to_sym
|
90
|
+
define_method key do |*args|
|
91
|
+
method(name, *args)
|
71
92
|
end
|
93
|
+
end
|
72
94
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
alias_method :==, :equals
|
95
|
+
def qname
|
96
|
+
method(:name)
|
97
|
+
end
|
77
98
|
|
78
|
-
|
79
|
-
|
80
|
-
|
99
|
+
alias_method :inverse, :not
|
100
|
+
alias_method :~, :not
|
101
|
+
alias_method :!, :not
|
102
|
+
alias_method :normalize, :normalize_space
|
103
|
+
alias_method :n, :normalize_space
|
104
|
+
|
105
|
+
OPERATORS = [
|
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
|
120
|
+
|
121
|
+
OPERATORS.each do |(name, operator, alias_name)|
|
122
|
+
define_method name do |rhs|
|
123
|
+
binary_operator(operator, rhs)
|
124
|
+
end
|
125
|
+
alias_method alias_name, name if alias_name
|
126
|
+
end
|
81
127
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
86
133
|
|
87
|
-
|
88
|
-
|
134
|
+
AXES.each do |key|
|
135
|
+
name = key.to_s.tr('_', '-').to_sym
|
136
|
+
define_method key do |*element_names|
|
137
|
+
axis(name, *element_names)
|
89
138
|
end
|
90
|
-
|
139
|
+
end
|
91
140
|
|
92
|
-
|
93
|
-
Union.new(*[self, expressions].flatten)
|
94
|
-
end
|
95
|
-
alias_method :+, :union
|
141
|
+
alias_method :self_axis, :self
|
96
142
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
alias_method :~, :inverse
|
143
|
+
def ends_with(suffix)
|
144
|
+
function(:substring, current, function(:'string-length', current).minus(function(:'string-length', suffix)).plus(1)) == suffix
|
145
|
+
end
|
101
146
|
|
102
|
-
|
103
|
-
|
104
|
-
|
147
|
+
def contains_word(word)
|
148
|
+
function(:concat, ' ', current.normalize_space, ' ').contains(" #{word} ")
|
149
|
+
end
|
105
150
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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)
|
160
|
+
end
|
161
|
+
|
162
|
+
def one_of(*expressions)
|
163
|
+
expressions.map { |e| current.equals(e) }.reduce(:or)
|
164
|
+
end
|
165
|
+
|
166
|
+
def next_sibling(*expressions)
|
167
|
+
axis(:"following-sibling")[1].axis(:self, *expressions)
|
168
|
+
end
|
169
|
+
|
170
|
+
def previous_sibling(*expressions)
|
171
|
+
axis(:"preceding-sibling")[1].axis(:self, *expressions)
|
110
172
|
end
|
111
173
|
end
|
112
174
|
end
|
data/lib/xpath/expression.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module XPath
|
2
4
|
class Expression
|
3
5
|
attr_accessor :expression, :arguments
|
4
|
-
include XPath::DSL
|
6
|
+
include XPath::DSL
|
5
7
|
|
6
8
|
def initialize(expression, *arguments)
|
7
9
|
@expression = expression
|
@@ -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}'"
|
@@ -38,32 +40,20 @@ module XPath
|
|
38
40
|
'.'
|
39
41
|
end
|
40
42
|
|
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
|
43
|
+
def descendant(current, element_names)
|
44
|
+
with_element_conditions("#{current}//", element_names)
|
49
45
|
end
|
50
46
|
|
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
|
47
|
+
def child(current, element_names)
|
48
|
+
with_element_conditions("#{current}/", element_names)
|
59
49
|
end
|
60
50
|
|
61
|
-
def axis(
|
62
|
-
"#{
|
51
|
+
def axis(current, name, element_names)
|
52
|
+
with_element_conditions("#{current}/#{name}::", element_names)
|
63
53
|
end
|
64
54
|
|
65
|
-
def
|
66
|
-
|
55
|
+
def anywhere(element_names)
|
56
|
+
with_element_conditions('//', element_names)
|
67
57
|
end
|
68
58
|
|
69
59
|
def where(on, condition)
|
@@ -71,18 +61,22 @@ module XPath
|
|
71
61
|
end
|
72
62
|
|
73
63
|
def attribute(current, name)
|
74
|
-
|
64
|
+
if valid_xml_name?(name)
|
65
|
+
"#{current}/@#{name}"
|
66
|
+
else
|
67
|
+
"#{current}/attribute::*[local-name(.) = #{string_literal(name)}]"
|
68
|
+
end
|
75
69
|
end
|
76
70
|
|
77
|
-
def
|
78
|
-
"#{
|
71
|
+
def binary_operator(name, left, right)
|
72
|
+
"(#{left} #{name} #{right})"
|
79
73
|
end
|
80
74
|
|
81
75
|
def is(one, two)
|
82
76
|
if @type == :exact
|
83
|
-
|
77
|
+
binary_operator('=', one, two)
|
84
78
|
else
|
85
|
-
contains
|
79
|
+
function(:contains, one, two)
|
86
80
|
end
|
87
81
|
end
|
88
82
|
|
@@ -94,10 +88,6 @@ module XPath
|
|
94
88
|
"#{current}/text()"
|
95
89
|
end
|
96
90
|
|
97
|
-
def normalized_space(current)
|
98
|
-
"normalize-space(#{current})"
|
99
|
-
end
|
100
|
-
|
101
91
|
def literal(node)
|
102
92
|
node
|
103
93
|
end
|
@@ -113,62 +103,24 @@ module XPath
|
|
113
103
|
expressions.join(' | ')
|
114
104
|
end
|
115
105
|
|
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
|
124
|
-
end
|
125
|
-
|
126
|
-
def contains(current, value)
|
127
|
-
"contains(#{current}, #{value})"
|
128
|
-
end
|
129
|
-
|
130
|
-
def starts_with(current, value)
|
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})"
|
106
|
+
def function(name, *arguments)
|
107
|
+
"#{name}(#{arguments.join(', ')})"
|
140
108
|
end
|
141
109
|
|
142
|
-
|
143
|
-
values.map { |value| "#{current} = #{value}" }.join(' or ')
|
144
|
-
end
|
110
|
+
private
|
145
111
|
|
146
|
-
def
|
112
|
+
def with_element_conditions(expression, element_names)
|
147
113
|
if element_names.length == 1
|
148
|
-
"#{
|
114
|
+
"#{expression}#{element_names.first}"
|
149
115
|
elsif element_names.length > 1
|
150
|
-
"#{
|
116
|
+
"#{expression}*[#{element_names.map { |e| "self::#{e}" }.join(' | ')}]"
|
151
117
|
else
|
152
|
-
"#{
|
118
|
+
"#{expression}*"
|
153
119
|
end
|
154
120
|
end
|
155
121
|
|
156
|
-
def
|
157
|
-
|
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})"
|
122
|
+
def valid_xml_name?(name)
|
123
|
+
name =~ /^[a-zA-Z_:][a-zA-Z0-9_:\.\-]*$/
|
172
124
|
end
|
173
125
|
end
|
174
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/lib/xpath.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'nokogiri'
|
2
4
|
|
3
5
|
require 'xpath/dsl'
|
@@ -5,12 +7,10 @@ require 'xpath/expression'
|
|
5
7
|
require 'xpath/literal'
|
6
8
|
require 'xpath/union'
|
7
9
|
require 'xpath/renderer'
|
8
|
-
require 'xpath/html'
|
9
10
|
|
10
11
|
module XPath
|
11
|
-
|
12
|
-
|
13
|
-
include XPath::DSL::TopLevel
|
12
|
+
extend XPath::DSL
|
13
|
+
include XPath::DSL
|
14
14
|
|
15
15
|
def self.generate
|
16
16
|
yield(self)
|
data/spec/fixtures/simple.html
CHANGED
@@ -17,7 +17,7 @@
|
|
17
17
|
|
18
18
|
<div id="woo" title="wooDiv" data="id">
|
19
19
|
<ul><li>A list</li></ul>
|
20
|
-
<p title="gorilla">Bax</p>
|
20
|
+
<p class="cat fish dog" title="gorilla">Bax</p>
|
21
21
|
<p>Bax</p>
|
22
22
|
<p id="wooDiv">Blah</p>
|
23
23
|
</div>
|
@@ -26,7 +26,7 @@
|
|
26
26
|
|
27
27
|
<div id="preference">
|
28
28
|
<p id="is-fuzzy">allamas</p>
|
29
|
-
<p id="is-exact">llama</p>
|
29
|
+
<p class="fish" id="is-exact">llama</p>
|
30
30
|
</div>
|
31
31
|
|
32
32
|
<p id="whitespace">
|
@@ -37,9 +37,17 @@
|
|
37
37
|
</p>
|
38
38
|
|
39
39
|
<div id="moar">
|
40
|
-
<p id="impchay">chimp</p>
|
40
|
+
<p class="catfish" id="impchay">chimp</p>
|
41
41
|
<div id="elephantay">elephant</div>
|
42
42
|
<p id="amingoflay">flamingo</p>
|
43
43
|
</div>
|
44
|
+
|
45
|
+
<span id="substring">Hello there</span>
|
46
|
+
|
47
|
+
<span id="string-length">Hello there</span>
|
48
|
+
|
49
|
+
<div id="oof" title="viDoof" data="id">
|
50
|
+
<p id="viDoof">Blah</p>
|
51
|
+
</div>
|
44
52
|
</body>
|
45
53
|
</html>
|