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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12f29349abdcae71838e3a6c0f9f6d7eac41b378e2fbdc3bcd6996950b76da7e
4
- data.tar.gz: 819e1a3641e979a632bf9358609da87155920ca1fc07e89b089b4a30e3681d39
3
+ metadata.gz: a243005210afa835c320ea204ab3105d0b253dfdb905d4ff53c8267e53a23d6d
4
+ data.tar.gz: 4978108857ddbd50e336926db82b6bb056656861572aa518654cff2dcadb2bc7
5
5
  SHA512:
6
- metadata.gz: 5c6ee15be0134334b4cb7016b252da4985a8f356cba571b84078af03745c612b13640b1cc77617a5f80d90ab643b2401cfe08b5d96fe02affd010d627ede88a4
7
- data.tar.gz: 223c56e87b09e1cf7d89e44d8fe4ac1639ce91280392bbbe25fc8b78d6cbf19a613f1d87a9d6f4ace14ad4973c80fdd7a20cbd292f188ffa571e3b72a2145197
6
+ metadata.gz: e17ddd74ff29ac77050bec7cb5ee93f8b1c2386c56ea4390272d998540a69cf8f26f61541de3a583866b4531836d051e722ca93eabc9d52636e722aa78de491b
7
+ data.tar.gz: c75b155e85156faafa9e3ac63e7c16b7fbc20385b80ada25dcb5c0c680ea06fd06490a5d2e9096d68a51b22b62b06f5ad6b5d7b6e0191396e748bbd7e415d3ce
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'nokogiri'
2
4
 
3
5
  require 'xpath/dsl'
@@ -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.gsub("_", "-").to_sym
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
- [:equals, :"=", :==],
105
- [:or, :or, :|],
106
- [:and, :and, :&],
107
- [:not_equals, :!=, :!=],
108
- [:lte, :<=, :<=],
109
- [:lt, :<, :<],
110
- [:gte, :>=, :>=],
111
- [:gt, :>, :>],
112
- [:plus, :+],
113
- [:minus, :-],
114
- [:multiply, :*, :*],
115
- [:divide, :div, :/],
116
- [:mod, :mod, :%],
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
- :ancestor, :ancestor_or_self, :attribute, :descendant_or_self,
128
- :following, :following_sibling, :namespace, :parent, :preceding,
129
- :preceding_sibling, :self,
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.gsub("_", "-").to_sym
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, " ", current.normalize_space, " ").contains(" #{word} ")
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 do |e|
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)
@@ -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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module XPath
2
4
  class Literal
3
5
  attr_reader :value
@@ -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
- when Expression, Union then render(argument)
19
- when Array then argument.map { |element| convert_argument(element) }
20
- when String then string_literal(argument)
21
- when Literal then argument.value
22
- else argument.to_s
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("//", element_names)
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
- "#{current}/@#{name}"
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("=", one, two)
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
@@ -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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module XPath
2
- VERSION = '3.1.0'
4
+ VERSION = '3.2.0'
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'xpath'
2
4
  require 'pry'
3
5
 
@@ -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 "should return the expressions" do
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 "should iterate through the expressions" do
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 "should map the expressions" do
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 { |expr| expr.expression }.should eq [:descendant, :descendant]
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 "should create a valid xpath expression" do
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 "should be delegated to the individual expressions" do
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
-
@@ -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 "should work as a mixin" do
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 "should find nodes that are nested below the current node" do
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 "Blah"
30
- @results[1].text.should eq "Bax"
31
+ @results[0].text.should eq 'Blah'
32
+ @results[1].text.should eq 'Bax'
31
33
  end
32
34
 
33
- it "should not find nodes outside the context" do
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 "should find multiple kinds of nodes" do
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 "should find all nodes when no arguments given" do
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 "should find nodes that are nested directly below the current node" do
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 "Blah"
58
- @results[1].text.should eq "Bax"
59
+ @results[0].text.should eq 'Blah'
60
+ @results[1].text.should eq 'Bax'
59
61
  end
60
62
 
61
- it "should not find nodes that are nested further down below the current node" do
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 "should find multiple kinds of nodes" do
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 "should find all nodes when no arguments given" do
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 "should find nodes given the xpath axis" do
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 "Blah"
84
+ @results[0].text.should eq 'Blah'
83
85
  end
84
86
 
85
- it "should find nodes given the xpath axis without a specific tag" do
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 "fooDiv"
89
+ @results[0][:id].should eq 'fooDiv'
88
90
  end
89
91
  end
90
92
 
91
93
  describe '#next_sibling' do
92
- it "should find nodes which are immediate siblings of the current node" do
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 "should find nodes which are exactly preceding the current node" do
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 "should find nodes regardless of the context" do
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 "Blah"
119
+ @results[0].text.should eq 'Blah'
118
120
  end
119
121
 
120
- it "should find multiple kinds of nodes regardless of the context" do
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 "should find all nodes when no arguments given regardless of the context" do
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 "should find nodes that contain the given string" do
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 "foo"
154
+ @results[0][:id].should eq 'foo'
154
155
  end
155
156
 
156
- it "should find nodes that contain the given expression" do
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 "foo"
162
+ @results[0][:id].should eq 'foo'
162
163
  end
163
164
  end
164
165
 
165
- describe "#contains_word" do
166
- it "should find nodes that contain the given word in its entirety" do
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 "Bax"
171
- @results[1].text.should eq "llama"
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 "should find nodes that begin with the given string" do
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 "foo"
183
- @results[1][:id].should eq "fooDiv"
183
+ @results[0][:id].should eq 'foo'
184
+ @results[1][:id].should eq 'fooDiv'
184
185
  end
185
186
 
186
- it "should find nodes that contain the given expression" do
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 "foo"
192
+ @results[0][:id].should eq 'foo'
192
193
  end
193
194
  end
194
195
 
195
196
  describe '#ends_with' do
196
- it "should find nodes that end with the given string" do
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 "oof"
202
- @results[1][:id].should eq "viDoof"
202
+ @results[0][:id].should eq 'oof'
203
+ @results[1][:id].should eq 'viDoof'
203
204
  end
204
205
 
205
- it "should find nodes that contain the given expression" do
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 "oof"
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 "when called with one argument" do
226
- it "should select the part of a string after the specified character" do
227
- @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == "substring").text.substring(7) }
228
- @results.should eq "there"
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 "when called with two arguments" do
233
- it "should select the part of a string after the specified character, up to the given length" do
234
- @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == "substring").text.substring(2, 4) }
235
- @results.should eq "ello"
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 "should call the given xpath function" do
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 "should call the given xpath function with the current node as the first argument" do
249
- @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == "string-length").text.method(:"string-length") }
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 "should return the length of a string" do
256
- @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == "string-length").text.string_length }
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 "should limit the expression to find only certain nodes" do
263
- xpath { |x| x.descendant(:div).where(:"@id = 'foo'") }.first[:title].should eq "fooDiv"
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 "should be aliased as []" do
267
- xpath { |x| x.descendant(:div)[:"@id = 'foo'"] }.first[:title].should eq "fooDiv"
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 "should be a no-op when nil condition is passed" do
271
- XPath.descendant(:div).where(nil).to_s.should eq ".//div"
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 "should invert the expression" do
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 "should be aliased as the unary tilde" do
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 "should be aliased as the unary bang" do
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 "should limit the expression to find only certain nodes" do
291
- xpath { |x| x.descendant(:div).where(x.attr(:id).equals('foo')) }.first[:title].should eq "fooDiv"
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 "should be aliased as ==" do
295
- xpath { |x| x.descendant(:div).where(x.attr(:id) == 'foo') }.first[:title].should eq "fooDiv"
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 "should match only when not equal" do
301
- xpath { |x| x.descendant(:div).where(x.attr(:id).not_equals('bar')) }.first[:title].should eq "fooDiv"
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 "should be aliased as !=" do
305
- xpath { |x| x.descendant(:div).where(x.attr(:id) != 'bar') }.first[:title].should eq "fooDiv"
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 "uses equality when :exact given" do
312
- xpath(:exact) { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should eq "fooDiv"
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 "uses substring matching otherwise" do
317
- xpath { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should eq "fooDiv"
318
- xpath { |x| x.descendant(:div).where(x.attr(:id).is('oo')) }.first[:title].should eq "fooDiv"
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 "should return all nodes where the condition matches" do
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 "fooDiv"
329
- @results[1].text.should eq "Blah"
330
- @results[2][:title].should eq "bazDiv"
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 "should find all nodes in both expression" do
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 "monkey"
357
+ @results[0][:title].should eq 'monkey'
340
358
  end
341
359
 
342
- it "should be aliased as ampersand (&)" do
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 "monkey"
364
+ @results[0][:title].should eq 'monkey'
347
365
  end
348
366
  end
349
367
 
350
368
  describe '#or' do
351
- it "should find all nodes in either expression" do
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 "fooDiv"
356
- @results[1].text.should eq "Blah"
373
+ @results[0][:title].should eq 'fooDiv'
374
+ @results[1].text.should eq 'Blah'
357
375
  end
358
376
 
359
- it "should be aliased as pipe (|)" do
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 "fooDiv"
364
- @results[1].text.should eq "Blah"
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 "should be an attribute" do
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 "barDiv"
372
- @results[1][:title].should eq "fooDiv"
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 "should find nodes by the given CSS selector" do
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 "should respect previous expression" do
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 "should be composable" do
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 "should allow comma separated selectors" do
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 "A list"
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 "should create a union expression" do
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 "should be aliased as +" do
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 "#last" do
436
- it "returns the number of elements in the context" do
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 "Bax"
439
- @results[1].text.should eq "Blah"
440
- @results[2].text.should eq "llama"
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 "#position" do
445
- it "returns the position of elements in the context" do
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 "Bax"
448
- @results[1].text.should eq "Bax"
465
+ @results[0].text.should eq 'Bax'
466
+ @results[1].text.should eq 'Bax'
449
467
  end
450
468
  end
451
469
 
452
- describe "#count" do
453
- it "counts the number of occurrences" do
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 "preference"
473
+ @results[0][:id].should eq 'preference'
456
474
  end
457
475
  end
458
476
 
459
- describe "#lte" do
460
- it "checks lesser than or equal" do
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 "Blah"
463
- @results[1].text.should eq "Bax"
464
- @results[2][:title].should eq "gorilla"
465
- @results[3].text.should eq "Bax"
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 "#lt" do
470
- it "checks lesser than" do
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 "Blah"
473
- @results[1][:title].should eq "gorilla"
490
+ @results[0].text.should eq 'Blah'
491
+ @results[1][:title].should eq 'gorilla'
474
492
  end
475
493
  end
476
494
 
477
- describe "#gte" do
478
- it "checks greater than or equal" do
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 "Bax"
481
- @results[1][:title].should eq "monkey"
482
- @results[2].text.should eq "Bax"
483
- @results[3].text.should eq "Blah"
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 "#gt" do
488
- it "checks greater than" do
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 "monkey"
491
- @results[1].text.should eq "Blah"
508
+ @results[0][:title].should eq 'monkey'
509
+ @results[1].text.should eq 'Blah'
492
510
  end
493
511
  end
494
512
 
495
- describe "#plus" do
496
- it "adds stuff" do
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 "fooDiv"
499
- @results[1][:title].should eq "gorilla"
516
+ @results[0][:id].should eq 'fooDiv'
517
+ @results[1][:title].should eq 'gorilla'
500
518
  end
501
519
  end
502
520
 
503
- describe "#minus" do
504
- it "subtracts stuff" do
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 "fooDiv"
507
- @results[1][:title].should eq "gorilla"
524
+ @results[0][:id].should eq 'fooDiv'
525
+ @results[1][:title].should eq 'gorilla'
508
526
  end
509
527
  end
510
528
 
511
- describe "#multiply" do
512
- it "multiplies stuff" do
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 "fooDiv"
515
- @results[1][:title].should eq "gorilla"
532
+ @results[0][:id].should eq 'fooDiv'
533
+ @results[1][:title].should eq 'gorilla'
516
534
  end
517
535
  end
518
536
 
519
- describe "#divide" do
520
- it "divides stuff" do
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 "Bax"
523
- @results[1].text.should eq "Bax"
540
+ @results[0].text.should eq 'Bax'
541
+ @results[1].text.should eq 'Bax'
524
542
  end
525
543
  end
526
544
 
527
- describe "#mod" do
528
- it "take modulo" do
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 "Blah"
531
- @results[1][:title].should eq "monkey"
532
- @results[2][:title].should eq "gorilla"
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 "#ancestor" do
537
- it "finds ancestor nodes" do
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 "html"
540
- @results[1].node_name.should eq "body"
541
- @results[2][:id].should eq "foo"
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.1.0
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-05-26 00:00:00.000000000 Z
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: rspec
29
+ name: pry
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - "~>"
32
+ - - ">="
33
33
  - !ruby/object:Gem::Version
34
- version: '3.0'
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: '3.0'
41
+ version: '0'
42
42
  - !ruby/object:Gem::Dependency
43
- name: yard
43
+ name: rake
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - ">="
47
47
  - !ruby/object:Gem::Version
48
- version: 0.5.8
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.5.8
55
+ version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
- name: rake
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: pry
71
+ name: yard
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - ">="
75
75
  - !ruby/object:Gem::Version
76
- version: '0'
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: '0'
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.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.6
125
+ rubygems_version: 2.7.7
126
126
  signing_key:
127
127
  specification_version: 4
128
128
  summary: Generate XPath expressions from Ruby