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.
@@ -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
- [![Build Status](https://secure.travis-ci.org/jnicklas/xpath.png?branch=master)](http://travis-ci.org/jnicklas/xpath)
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
- [`XPath::Expression`](http://rdoc.info/github/jnicklas/xpath/XPath/Expression)
41
- instance, which can be further modified. To convert the expression to a
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
- (The MIT License)
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).
@@ -8,9 +8,8 @@ require 'xpath/renderer'
8
8
  require 'xpath/html'
9
9
 
10
10
  module XPath
11
-
12
- extend XPath::DSL::TopLevel
13
- include XPath::DSL::TopLevel
11
+ extend XPath::DSL
12
+ include XPath::DSL
14
13
 
15
14
  def self.generate
16
15
  yield(self)
@@ -1,112 +1,147 @@
1
1
  module XPath
2
2
  module DSL
3
- module TopLevel
4
- def current
5
- Expression.new(:this_node)
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
- def descendant(*expressions)
13
- Expression.new(:descendant, current, expressions)
14
- end
7
+ def descendant(*expressions)
8
+ Expression.new(:descendant, current, expressions)
9
+ end
15
10
 
16
- def child(*expressions)
17
- Expression.new(:child, current, expressions)
18
- end
11
+ def child(*expressions)
12
+ Expression.new(:child, current, expressions)
13
+ end
19
14
 
20
- def axis(name, tag_name=:*)
21
- Expression.new(:axis, current, name, tag_name)
22
- end
15
+ def axis(name, *element_names)
16
+ Expression.new(:axis, current, name, element_names)
17
+ end
23
18
 
24
- def next_sibling(*expressions)
25
- Expression.new(:next_sibling, current, expressions)
26
- end
19
+ def anywhere(*expressions)
20
+ Expression.new(:anywhere, expressions)
21
+ end
27
22
 
28
- def previous_sibling(*expressions)
29
- Expression.new(:previous_sibling, current, expressions)
30
- end
23
+ def attr(expression)
24
+ Expression.new(:attribute, current, expression)
25
+ end
31
26
 
32
- def anywhere(*expressions)
33
- Expression.new(:anywhere, expressions)
34
- end
27
+ def text
28
+ Expression.new(:text, current)
29
+ end
35
30
 
36
- def attr(expression)
37
- Expression.new(:attribute, current, expression)
38
- end
31
+ def css(selector)
32
+ Expression.new(:css, current, Literal.new(selector))
33
+ end
39
34
 
40
- def contains(expression)
41
- Expression.new(:contains, current, expression)
42
- end
35
+ def function(name, *arguments)
36
+ Expression.new(:function, name, *arguments)
37
+ end
43
38
 
44
- def starts_with(expression)
45
- Expression.new(:starts_with, current, expression)
46
- end
39
+ def method(name, *arguments)
40
+ Expression.new(:function, name, current, *arguments)
41
+ end
47
42
 
48
- def text
49
- Expression.new(:text, current)
50
- end
43
+ def where(expression)
44
+ Expression.new(:where, current, expression)
45
+ end
46
+ alias_method :[], :where
51
47
 
52
- def string
53
- Expression.new(:string_function, current)
54
- end
48
+ def is(expression)
49
+ Expression.new(:is, current, expression)
50
+ end
55
51
 
56
- def css(selector)
57
- Expression.new(:css, current, Literal.new(selector))
58
- end
52
+ def binary_operator(name, rhs)
53
+ Expression.new(:binary_operator, name, current, rhs)
59
54
  end
60
55
 
61
- module ExpressionLevel
62
- include XPath::DSL::TopLevel
56
+ def union(*expressions)
57
+ Union.new(*[self, expressions].flatten)
58
+ end
59
+ alias_method :+, :union
63
60
 
64
- def where(expression)
65
- Expression.new(:where, current, expression)
66
- end
67
- alias_method :[], :where
61
+ def last
62
+ function(:last)
63
+ end
68
64
 
69
- def one_of(*expressions)
70
- Expression.new(:one_of, current, expressions)
71
- end
65
+ def position
66
+ function(:position)
67
+ end
72
68
 
73
- def equals(expression)
74
- Expression.new(:equality, current, expression)
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
- alias_method :==, :equals
87
+ end
77
88
 
78
- def is(expression)
79
- Expression.new(:is, current, expression)
80
- end
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
- def or(expression)
83
- Expression.new(:or, current, expression)
84
- end
85
- alias_method :|, :or
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
- def and(expression)
88
- Expression.new(:and, current, expression)
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
- alias_method :&, :and
127
+ end
91
128
 
92
- def union(*expressions)
93
- Union.new(*[self, expressions].flatten)
94
- end
95
- alias_method :+, :union
129
+ alias_method :self_axis, :self
96
130
 
97
- def inverse
98
- Expression.new(:inverse, current)
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
- alias_method :~, :inverse
137
+ end
101
138
 
102
- def string_literal
103
- Expression.new(:string_literal, self)
104
- end
139
+ def next_sibling(*expressions)
140
+ axis(:"following-sibling")[1].axis(:self, *expressions)
141
+ end
105
142
 
106
- def normalize
107
- Expression.new(:normalized_space, current)
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
@@ -1,7 +1,7 @@
1
1
  module XPath
2
2
  class Expression
3
3
  attr_accessor :expression, :arguments
4
- include XPath::DSL::ExpressionLevel
4
+ include XPath::DSL
5
5
 
6
6
  def initialize(expression, *arguments)
7
7
  @expression = expression
@@ -1,6 +1,6 @@
1
1
  module XPath
2
2
  module HTML
3
- include XPath::DSL::TopLevel
3
+ include XPath::DSL
4
4
  extend self
5
5
 
6
6
  # Match an `a` link element.
@@ -38,32 +38,20 @@ module XPath
38
38
  '.'
39
39
  end
40
40
 
41
- def descendant(parent, element_names)
42
- if element_names.length == 1
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(parent, element_names)
52
- if element_names.length == 1
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(parent, name, tag_name)
62
- "#{parent}/#{name}::#{tag_name}"
49
+ def axis(current, name, element_names)
50
+ with_element_conditions("#{current}/#{name}::", element_names)
63
51
  end
64
52
 
65
- def node_name(current)
66
- "name(#{current})"
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 equality(one, two)
78
- "#{one} = #{two}"
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
- equality(one, two)
71
+ binary_operator("=", one, two)
84
72
  else
85
- contains(one, two)
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 anywhere(element_names)
117
- if element_names.length == 1
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
- def contains(current, value)
127
- "contains(#{current}, #{value})"
128
- end
104
+ private
129
105
 
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})"
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
- "#{current}/following-sibling::*[1]/self::#{element_names.first}"
108
+ "#{expression}#{element_names.first}"
149
109
  elsif element_names.length > 1
150
- "#{current}/following-sibling::*[1]/self::*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]"
110
+ "#{expression}*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]"
151
111
  else
152
- "#{current}/following-sibling::*[1]/self::*"
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
@@ -1,3 +1,3 @@
1
1
  module XPath
2
- VERSION = '2.0.0'
2
+ VERSION = '2.1.0'
3
3
  end
@@ -41,5 +41,9 @@
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>
44
48
  </body>
45
49
  </html>
@@ -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
 
@@ -1 +1,6 @@
1
1
  require 'xpath'
2
+ require 'pry'
3
+
4
+ RSpec.configure do |config|
5
+ config.expect_with(:rspec) { |c| c.syntax = :should }
6
+ end
@@ -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.0.0
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
- - !binary |-
13
- LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURoVENDQW0yZ0F3SUJB
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: '2.0'
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: '2.0'
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
- - README.md
132
- homepage: http://github.com/jnicklas/xpath
133
- licenses: []
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: '0'
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: xpath
154
- rubygems_version: 1.8.25
126
+ rubyforge_project:
127
+ rubygems_version: 2.5.2
155
128
  signing_key:
156
- specification_version: 3
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�