xpath 0.1.4 → 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +61 -4
- data/lib/xpath.rb +6 -57
- data/lib/xpath/dsl.rb +104 -0
- data/lib/xpath/expression.rb +8 -298
- data/lib/xpath/html.rb +101 -62
- data/lib/xpath/literal.rb +8 -0
- data/lib/xpath/renderer.rb +146 -0
- data/lib/xpath/union.rb +10 -12
- data/lib/xpath/version.rb +1 -1
- data/spec/fixtures/form.html +49 -7
- data/spec/html_spec.rb +45 -47
- data/spec/union_spec.rb +14 -13
- data/spec/xpath_spec.rb +35 -88
- metadata +78 -69
data/spec/html_spec.rb
CHANGED
@@ -11,9 +11,7 @@ describe XPath::HTML do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def all(*args)
|
14
|
-
XPath::HTML.send(subject, *args).
|
15
|
-
doc.xpath(xpath)
|
16
|
-
end.flatten.uniq.map { |node| node[:data] }
|
14
|
+
doc.xpath(XPath::HTML.send(subject, *args).to_s).map { |node| node[:data] }
|
17
15
|
end
|
18
16
|
|
19
17
|
describe '#link' do
|
@@ -25,16 +23,11 @@ describe XPath::HTML do
|
|
25
23
|
it("finds links with child tags by content") { get('An emphatic link').should == 'link-children' }
|
26
24
|
it("finds links by the content of theur child tags") { get('emphatic').should == 'link-children' }
|
27
25
|
it("finds links by approximate content") { get('awesome').should == 'link-text' }
|
28
|
-
it("prefers exact matches of content") { all('A link').should == ['link-exact', 'link-fuzzy'] }
|
29
26
|
it("finds links by title") { get('My title').should == 'link-title' }
|
30
27
|
it("finds links by approximate title") { get('title').should == 'link-title' }
|
31
|
-
it("prefers exact matches of title") { all('This title').should == ['link-exact', 'link-fuzzy'] }
|
32
28
|
it("finds links by image's alt attribute") { get('Alt link').should == 'link-img' }
|
33
29
|
it("finds links by image's approximate alt attribute") { get('Alt').should == 'link-img' }
|
34
|
-
it("prefers exact matches of image's alt attribute") { all('An image').should == ['link-img-exact', 'link-img-fuzzy'] }
|
35
30
|
it("does not find links without href attriutes") { get('Wrong Link').should be_nil }
|
36
|
-
it("finds links with an href") { get("Href-ed link", :href => 'http://www.example.com').should == 'link-href' }
|
37
|
-
it("does not find links with an incorrect href") { get("Href-ed link", :href => 'http://www.somewhere.com').should be_nil }
|
38
31
|
end
|
39
32
|
|
40
33
|
describe '#button' do
|
@@ -44,20 +37,24 @@ describe XPath::HTML do
|
|
44
37
|
it("finds buttons by id") { get('submit-with-id').should == 'id-submit' }
|
45
38
|
it("finds buttons by value") { get('submit-with-value').should == 'value-submit' }
|
46
39
|
it("finds buttons by approximate value") { get('mit-with-val').should == 'value-submit' }
|
47
|
-
it("prefers buttons with exact value") { all('exact value submit').should == ['exact-value-submit', 'not-exact-value-submit'] }
|
48
40
|
it("finds buttons by title") { get('My submit title').should == 'title-submit' }
|
49
41
|
it("finds buttons by approximate title") { get('submit title').should == 'title-submit' }
|
50
|
-
|
42
|
+
end
|
43
|
+
|
44
|
+
context "with reset type" do
|
45
|
+
it("finds buttons by id") { get('reset-with-id').should == 'id-reset' }
|
46
|
+
it("finds buttons by value") { get('reset-with-value').should == 'value-reset' }
|
47
|
+
it("finds buttons by approximate value") { get('set-with-val').should == 'value-reset' }
|
48
|
+
it("finds buttons by title") { get('My reset title').should == 'title-reset' }
|
49
|
+
it("finds buttons by approximate title") { get('reset title').should == 'title-reset' }
|
51
50
|
end
|
52
51
|
|
53
52
|
context "with button type" do
|
54
53
|
it("finds buttons by id") { get('button-with-id').should == 'id-button' }
|
55
54
|
it("finds buttons by value") { get('button-with-value').should == 'value-button' }
|
56
55
|
it("finds buttons by approximate value") { get('ton-with-val').should == 'value-button' }
|
57
|
-
it("prefers buttons with exact value") { all('exact value button').should == ['exact-value-button', 'not-exact-value-button'] }
|
58
56
|
it("finds buttons by title") { get('My button title').should == 'title-button' }
|
59
57
|
it("finds buttons by approximate title") { get('button title').should == 'title-button' }
|
60
|
-
it("prefers exact matches of title") { all('Exact button title').should == ['exact-title-button', 'not-exact-title-button'] }
|
61
58
|
end
|
62
59
|
|
63
60
|
context "with image type" do
|
@@ -65,26 +62,21 @@ describe XPath::HTML do
|
|
65
62
|
it("finds buttons by value") { get('imgbut-with-value').should == 'value-imgbut' }
|
66
63
|
it("finds buttons by approximate value") { get('gbut-with-val').should == 'value-imgbut' }
|
67
64
|
it("finds buttons by alt attribute") { get('imgbut-with-alt').should == 'alt-imgbut' }
|
68
|
-
it("prefers buttons with exact value") { all('exact value imgbut').should == ['exact-value-imgbut', 'not-exact-value-imgbut'] }
|
69
65
|
it("finds buttons by title") { get('My imgbut title').should == 'title-imgbut' }
|
70
66
|
it("finds buttons by approximate title") { get('imgbut title').should == 'title-imgbut' }
|
71
|
-
it("prefers exact matches of title") { all('Exact imgbut title').should == ['exact-title-imgbut', 'not-exact-title-imgbut'] }
|
72
67
|
end
|
73
68
|
|
74
69
|
context "with button tag" do
|
75
70
|
it("finds buttons by id") { get('btag-with-id').should == 'id-btag' }
|
76
71
|
it("finds buttons by value") { get('btag-with-value').should == 'value-btag' }
|
77
72
|
it("finds buttons by approximate value") { get('tag-with-val').should == 'value-btag' }
|
78
|
-
it("finds prefers buttons with exact value") { all('exact value btag').should == ['exact-value-btag', 'not-exact-value-btag'] }
|
79
73
|
it("finds buttons by text") { get('btag-with-text').should == 'text-btag' }
|
80
74
|
it("finds buttons by text ignoring whitespace") { get('My whitespaced button').should == 'btag-with-whitespace' }
|
81
75
|
it("finds buttons by approximate text ") { get('tag-with-tex').should == 'text-btag' }
|
82
76
|
it("finds buttons with child tags by text") { get('An emphatic button').should == 'btag-with-children' }
|
83
77
|
it("finds buttons by text of their children") { get('emphatic').should == 'btag-with-children' }
|
84
|
-
it("prefers buttons with exact text") { all('exact text btag').should == ['exact-text-btag', 'not-exact-text-btag'] }
|
85
78
|
it("finds buttons by title") { get('My btag title').should == 'title-btag' }
|
86
79
|
it("finds buttons by approximate title") { get('btag title').should == 'title-btag' }
|
87
|
-
it("prefers exact matches of title") { all('Exact btag title').should == ['exact-title-btag', 'not-exact-title-btag'] }
|
88
80
|
end
|
89
81
|
|
90
82
|
context "with unkown type" do
|
@@ -99,7 +91,7 @@ describe XPath::HTML do
|
|
99
91
|
it("finds fieldsets by legend") { get('Some Legend').should == 'fieldset-legend' }
|
100
92
|
it("finds fieldsets by legend child tags") { get('Span Legend').should == 'fieldset-legend-span' }
|
101
93
|
it("accepts approximate legends") { get('Legend').should == 'fieldset-legend' }
|
102
|
-
it("
|
94
|
+
it("finds nested fieldsets by legend") { get('Inner legend').should == 'fieldset-inner' }
|
103
95
|
end
|
104
96
|
|
105
97
|
describe '#field' do
|
@@ -129,6 +121,15 @@ describe XPath::HTML do
|
|
129
121
|
it("does not find hidden fields") { get('input-hidden-with-name').should be_nil }
|
130
122
|
end
|
131
123
|
|
124
|
+
context "by placeholder" do
|
125
|
+
it("finds inputs with no type") { get('input-with-placeholder').should == 'input-with-placeholder-data' }
|
126
|
+
it("finds inputs with text type") { get('input-text-with-placeholder').should == 'input-text-with-placeholder-data' }
|
127
|
+
it("finds inputs with password type") { get('input-password-with-placeholder').should == 'input-password-with-placeholder-data' }
|
128
|
+
it("finds inputs with custom type") { get('input-custom-with-placeholder').should == 'input-custom-with-placeholder-data' }
|
129
|
+
it("finds textareas") { get('textarea-with-placeholder').should == 'textarea-with-placeholder-data' }
|
130
|
+
it("does not find hidden fields") { get('input-hidden-with-placeholder').should be_nil }
|
131
|
+
end
|
132
|
+
|
132
133
|
context "by referenced label" do
|
133
134
|
it("finds inputs with no type") { get('Input with label').should == 'input-with-label-data' }
|
134
135
|
it("finds inputs with text type") { get('Input text with label').should == 'input-text-with-label-data' }
|
@@ -152,28 +153,6 @@ describe XPath::HTML do
|
|
152
153
|
it("does not find image buttons") { get('Input image with parent label').should be_nil }
|
153
154
|
it("does not find hidden fields") { get('Input hidden with parent label').should be_nil }
|
154
155
|
end
|
155
|
-
|
156
|
-
context "with :with option" do
|
157
|
-
it("finds inputs that match option") { get('input-with-id', :with => 'correct-value').should == 'input-with-id-data' }
|
158
|
-
it("omits inputs that don't match option") { get('input-with-id', :with => 'wrong-value').should be_nil }
|
159
|
-
it("finds textareas that match option") { get('textarea-with-id', :with => 'Correct value').should == 'textarea-with-id-data' }
|
160
|
-
it("omits textareas that don't match option") { get('textarea-with-id', :with => 'Wrong value').should be_nil }
|
161
|
-
end
|
162
|
-
|
163
|
-
context "with :checked option" do
|
164
|
-
context "when true" do
|
165
|
-
it("finds checked fields") {}
|
166
|
-
it("omits unchecked fields") {}
|
167
|
-
end
|
168
|
-
context "when false" do
|
169
|
-
it("finds unchecked fields") {}
|
170
|
-
it("omits checked fields") {}
|
171
|
-
end
|
172
|
-
context "when ommitted" do
|
173
|
-
it("finds unchecked fields") {}
|
174
|
-
it("finds checked fields") {}
|
175
|
-
end
|
176
|
-
end
|
177
156
|
end
|
178
157
|
|
179
158
|
describe '#fillable_field' do
|
@@ -186,34 +165,53 @@ describe XPath::HTML do
|
|
186
165
|
end
|
187
166
|
|
188
167
|
describe '#select' do
|
189
|
-
|
168
|
+
subject{ :select }
|
169
|
+
it("finds selects by id") { get('select-with-id').should == 'select-with-id-data' }
|
170
|
+
it("finds selects by name") { get('select-with-name').should == 'select-with-name-data' }
|
171
|
+
it("finds selects by label") { get('Select with label').should == 'select-with-label-data' }
|
172
|
+
it("finds selects by parent label") { get('Select with parent label').should == 'select-with-parent-label-data' }
|
190
173
|
end
|
191
174
|
|
192
175
|
describe '#checkbox' do
|
193
|
-
|
176
|
+
subject{ :checkbox }
|
177
|
+
it("finds checkboxes by id") { get('input-checkbox-with-id').should == 'input-checkbox-with-id-data' }
|
178
|
+
it("finds checkboxes by name") { get('input-checkbox-with-name').should == 'input-checkbox-with-name-data' }
|
179
|
+
it("finds checkboxes by label") { get('Input checkbox with label').should == 'input-checkbox-with-label-data' }
|
180
|
+
it("finds checkboxes by parent label") { get('Input checkbox with parent label').should == 'input-checkbox-with-parent-label-data' }
|
194
181
|
end
|
195
182
|
|
196
183
|
describe '#radio_button' do
|
184
|
+
subject{ :radio_button }
|
185
|
+
it("finds radio buttons by id") { get('input-radio-with-id').should == 'input-radio-with-id-data' }
|
186
|
+
it("finds radio buttons by name") { get('input-radio-with-name').should == 'input-radio-with-name-data' }
|
187
|
+
it("finds radio buttons by label") { get('Input radio with label').should == 'input-radio-with-label-data' }
|
188
|
+
it("finds radio buttons by parent label") { get('Input radio with parent label').should == 'input-radio-with-parent-label-data' }
|
197
189
|
|
198
190
|
end
|
199
191
|
|
200
192
|
describe '#file_field' do
|
201
|
-
|
193
|
+
subject{ :file_field }
|
194
|
+
it("finds file fields by id") { get('input-file-with-id').should == 'input-file-with-id-data' }
|
195
|
+
it("finds file fields by name") { get('input-file-with-name').should == 'input-file-with-name-data' }
|
196
|
+
it("finds file fields by label") { get('Input file with label').should == 'input-file-with-label-data' }
|
197
|
+
it("finds file fields by parent label") { get('Input file with parent label').should == 'input-file-with-parent-label-data' }
|
202
198
|
end
|
203
199
|
|
204
200
|
describe '#option' do
|
205
|
-
|
201
|
+
subject{ :option }
|
202
|
+
it("finds options by text") { get('Option with text').should == 'option-with-text-data' }
|
203
|
+
it("does not find option by partial text") { get('Option with').should be_nil }
|
206
204
|
end
|
207
205
|
|
208
206
|
describe "#optgroup" do
|
209
207
|
subject { :optgroup }
|
210
|
-
|
211
208
|
it("finds optgroups by label") { get('Group A').should == 'optgroup-a' }
|
212
209
|
end
|
213
210
|
|
214
211
|
describe "#table" do
|
215
212
|
subject {:table}
|
216
|
-
|
217
|
-
it("finds
|
213
|
+
it("finds tables by id") { get('table-with-id').should == 'table-with-id-data' }
|
214
|
+
it("finds tables by caption") { get('Table with caption').should == 'table-with-caption-data' }
|
218
215
|
end
|
216
|
+
|
219
217
|
end
|
data/spec/union_spec.rb
CHANGED
@@ -6,8 +6,8 @@ describe XPath::Union do
|
|
6
6
|
|
7
7
|
describe '#expressions' do
|
8
8
|
it "should return the expressions" do
|
9
|
-
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
10
|
-
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
9
|
+
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
10
|
+
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
11
11
|
@collection = XPath::Union.new(@expr1, @expr2)
|
12
12
|
@collection.expressions.should == [@expr1, @expr2]
|
13
13
|
end
|
@@ -15,8 +15,8 @@ describe XPath::Union do
|
|
15
15
|
|
16
16
|
describe '#each' do
|
17
17
|
it "should iterate through the expressions" do
|
18
|
-
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
19
|
-
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
18
|
+
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
19
|
+
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
20
20
|
@collection = XPath::Union.new(@expr1, @expr2)
|
21
21
|
exprs = []
|
22
22
|
@collection.each { |expr| exprs << expr }
|
@@ -26,17 +26,17 @@ describe XPath::Union do
|
|
26
26
|
|
27
27
|
describe '#map' do
|
28
28
|
it "should map the expressions" do
|
29
|
-
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
30
|
-
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
29
|
+
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
30
|
+
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
31
31
|
@collection = XPath::Union.new(@expr1, @expr2)
|
32
|
-
@collection.map { |expr| expr.
|
32
|
+
@collection.map { |expr| expr.expression }.should == [:descendant, :descendant]
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
36
|
describe '#to_xpath' do
|
37
37
|
it "should create a valid xpath expression" do
|
38
|
-
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
39
|
-
@expr2 = XPath.generate { |x| x.descendant(:div).where(x.attr(:id) == 'foo') }
|
38
|
+
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
39
|
+
@expr2 = XPath.generate { |x| x.descendant(:div).where(x.attr(:id) == 'foo') }
|
40
40
|
@collection = XPath::Union.new(@expr1, @expr2)
|
41
41
|
@results = doc.xpath(@collection.to_xpath)
|
42
42
|
@results[0][:title].should == 'fooDiv'
|
@@ -45,13 +45,14 @@ describe XPath::Union do
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
|
48
|
+
|
49
|
+
describe '#where and others' do
|
49
50
|
it "should be delegated to the individual expressions" do
|
50
|
-
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
51
|
-
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
51
|
+
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
52
|
+
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
52
53
|
@collection = XPath::Union.new(@expr1, @expr2)
|
53
54
|
@xpath1 = @collection.where(XPath.attr(:id) == 'foo').to_xpath
|
54
|
-
@xpath2 = @collection.where(XPath.attr(:id) ==
|
55
|
+
@xpath2 = @collection.where(XPath.attr(:id) == 'fooDiv').to_xpath
|
55
56
|
@results = doc.xpath(@xpath1)
|
56
57
|
@results[0][:title].should == 'fooDiv'
|
57
58
|
@results = doc.xpath(@xpath2)
|
data/spec/xpath_spec.rb
CHANGED
@@ -14,8 +14,8 @@ describe XPath do
|
|
14
14
|
let(:template) { File.read(File.expand_path('fixtures/simple.html', File.dirname(__FILE__))) }
|
15
15
|
let(:doc) { Nokogiri::HTML(template) }
|
16
16
|
|
17
|
-
def xpath(
|
18
|
-
doc.xpath XPath.generate(&block).to_xpath
|
17
|
+
def xpath(&block)
|
18
|
+
doc.xpath XPath.generate(&block).to_xpath
|
19
19
|
end
|
20
20
|
|
21
21
|
it "should work as a mixin" do
|
@@ -76,6 +76,18 @@ describe XPath do
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
+
describe '#axis' do
|
80
|
+
it "should find nodes given the xpath axis" do
|
81
|
+
@results = xpath { |x| x.axis(:descendant, :p) }
|
82
|
+
@results[0].text.should == "Blah"
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should find nodes given the xpath axis without a specific tag" do
|
86
|
+
@results = xpath { |x| x.descendant(:div)[x.attr(:id) == 'foo'].axis(:descendant) }
|
87
|
+
@results[0][:id].should == "fooDiv"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
79
91
|
describe '#next_sibling' do
|
80
92
|
it "should find nodes which are immediate siblings of the current node" do
|
81
93
|
xpath { |x| x.descendant(:p)[x.attr(:id) == 'fooDiv'].next_sibling(:p) }.first.text.should == 'Bax'
|
@@ -96,14 +108,6 @@ describe XPath do
|
|
96
108
|
end
|
97
109
|
end
|
98
110
|
|
99
|
-
describe '#tag' do
|
100
|
-
it "should filter elements by tag" do
|
101
|
-
@results = xpath { |x| x.descendant[x.tag(:p) | x.tag(:li)] }
|
102
|
-
@results[0].text.should == 'Blah'
|
103
|
-
@results[3].text.should == 'A list'
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
111
|
describe '#contains' do
|
108
112
|
it "should find nodes that contain the given string" do
|
109
113
|
@results = xpath do |x|
|
@@ -121,6 +125,25 @@ describe XPath do
|
|
121
125
|
end
|
122
126
|
end
|
123
127
|
|
128
|
+
describe '#starts_with' do
|
129
|
+
it "should find nodes that begin with the given string" do
|
130
|
+
@results = xpath do |x|
|
131
|
+
x.descendant(:*).where(x.attr(:id).starts_with('foo'))
|
132
|
+
end
|
133
|
+
@results.size.should == 2
|
134
|
+
@results[0][:id].should == "foo"
|
135
|
+
@results[1][:id].should == "fooDiv"
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should find nodes that contain the given expression" do
|
139
|
+
@results = xpath do |x|
|
140
|
+
expression = x.anywhere(:div).where(x.attr(:title) == 'fooDiv').attr(:id)
|
141
|
+
x.descendant(:div).where(x.attr(:title).starts_with(expression))
|
142
|
+
end
|
143
|
+
@results[0][:id].should == "foo"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
124
147
|
describe '#text' do
|
125
148
|
it "should select a node's text" do
|
126
149
|
@results = xpath { |x| x.descendant(:p).where(x.text == 'Bax') }
|
@@ -161,35 +184,6 @@ describe XPath do
|
|
161
184
|
end
|
162
185
|
end
|
163
186
|
|
164
|
-
describe '#is' do
|
165
|
-
it "should limit the expression to only nodes that contain the given expression" do
|
166
|
-
@results = xpath { |x| x.descendant(:p).where(x.text.is('llama')) }
|
167
|
-
@results[0][:id].should == 'is-fuzzy'
|
168
|
-
@results[1][:id].should == 'is-exact'
|
169
|
-
end
|
170
|
-
|
171
|
-
it "should limit the expression to only nodes that contain the given expression if fuzzy predicate given" do
|
172
|
-
@results = xpath(:fuzzy) { |x| x.descendant(:p).where(x.text.is('llama')) }
|
173
|
-
@results[0][:id].should == 'is-fuzzy'
|
174
|
-
@results[1][:id].should == 'is-exact'
|
175
|
-
end
|
176
|
-
|
177
|
-
it "should limit the expression to only nodes that equal the given expression if exact predicate given" do
|
178
|
-
@results = xpath(:exact) { |x| x.descendant(:p).where(x.text.is('llama')) }
|
179
|
-
@results[0][:id].should == 'is-exact'
|
180
|
-
@results[1].should be_nil
|
181
|
-
end
|
182
|
-
|
183
|
-
context "with to_xpaths" do
|
184
|
-
it "should prefer exact matches" do
|
185
|
-
@xpath = XPath.generate { |x| x.descendant(:p).where(x.text.is('llama')) }
|
186
|
-
@results = @xpath.to_xpaths.map { |path| doc.xpath(path) }.flatten
|
187
|
-
@results[0][:id].should == 'is-exact'
|
188
|
-
@results[1][:id].should == 'is-fuzzy'
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
187
|
describe '#one_of' do
|
194
188
|
it "should return all nodes where the condition matches" do
|
195
189
|
@results = xpath do |x|
|
@@ -242,14 +236,6 @@ describe XPath do
|
|
242
236
|
@results[0][:title].should == "barDiv"
|
243
237
|
@results[1][:title].should == "fooDiv"
|
244
238
|
end
|
245
|
-
|
246
|
-
it "should be closed" do
|
247
|
-
@results = xpath do |x|
|
248
|
-
foo_div = x.anywhere(:div).where(x.attr(:id) == 'foo')
|
249
|
-
id = x.attr(foo_div.attr(:data))
|
250
|
-
x.descendant(:div).where(id == 'bar')
|
251
|
-
end.first[:title].should == "barDiv"
|
252
|
-
end
|
253
239
|
end
|
254
240
|
|
255
241
|
describe '#css' do
|
@@ -285,52 +271,13 @@ describe XPath do
|
|
285
271
|
end
|
286
272
|
end
|
287
273
|
|
288
|
-
describe '#apply and #var' do
|
289
|
-
it "should interpolate variables in the xpath expression" do
|
290
|
-
@xpath = XPath.generate do |x|
|
291
|
-
exp = x.descendant(:*).where(x.attr(:id) == x.var(:id).string_literal)
|
292
|
-
end
|
293
|
-
@result1 = doc.xpath(@xpath.apply(:id => 'foo').to_xpath).first
|
294
|
-
@result1[:title].should == 'fooDiv'
|
295
|
-
@result2 = doc.xpath(@xpath.apply(:id => 'baz').to_xpath).first
|
296
|
-
@result2[:title].should == 'bazDiv'
|
297
|
-
end
|
298
|
-
|
299
|
-
it "should raise an argument error if the interpolation key is not given" do
|
300
|
-
@xpath = XPath.generate { |x| x.descendant(:*).where(x.attr(:id) == x.var(:id).string_literal) }
|
301
|
-
if defined?(KeyError)
|
302
|
-
lambda { @xpath.apply.to_xpath }.should raise_error(KeyError)
|
303
|
-
else
|
304
|
-
lambda { @xpath.apply.to_xpath }.should raise_error(ArgumentError)
|
305
|
-
end
|
306
|
-
end
|
307
|
-
end
|
308
|
-
|
309
|
-
describe '#varstring' do
|
310
|
-
it "should add a literal string variable" do
|
311
|
-
@xpath = XPath.generate { |x| x.descendant(:*).where(x.attr(:id) == x.varstring(:id)) }
|
312
|
-
@result1 = doc.xpath(@xpath.apply(:id => 'foo').to_xpath).first
|
313
|
-
@result1[:title].should == 'fooDiv'
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
describe '#normalize' do
|
318
|
-
it "should normalize whitespace" do
|
319
|
-
xpath { |x| x.descendant(:p).where(x.text.normalize == 'A lot of whitespace') }.first[:id].should == "whitespace"
|
320
|
-
end
|
321
|
-
|
322
|
-
it "should be aliased as 'n'" do
|
323
|
-
xpath { |x| x.descendant(:p).where(x.text.n == 'A lot of whitespace') }.first[:id].should == "whitespace"
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
274
|
describe '#union' do
|
328
275
|
it "should create a union expression" do
|
329
276
|
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
330
277
|
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
331
278
|
@collection = @expr1.union(@expr2)
|
332
279
|
@xpath1 = @collection.where(XPath.attr(:id) == 'foo').to_xpath
|
333
|
-
@xpath2 = @collection.where(XPath.attr(:id) ==
|
280
|
+
@xpath2 = @collection.where(XPath.attr(:id) == 'fooDiv').to_xpath
|
334
281
|
@results = doc.xpath(@xpath1)
|
335
282
|
@results[0][:title].should == 'fooDiv'
|
336
283
|
@results = doc.xpath(@xpath2)
|
@@ -342,7 +289,7 @@ describe XPath do
|
|
342
289
|
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
343
290
|
@collection = @expr1 + @expr2
|
344
291
|
@xpath1 = @collection.where(XPath.attr(:id) == 'foo').to_xpath
|
345
|
-
@xpath2 = @collection.where(XPath.attr(:id) ==
|
292
|
+
@xpath2 = @collection.where(XPath.attr(:id) == 'fooDiv').to_xpath
|
346
293
|
@results = doc.xpath(@xpath1)
|
347
294
|
@results[0][:title].should == 'fooDiv'
|
348
295
|
@results = doc.xpath(@xpath2)
|
metadata
CHANGED
@@ -1,77 +1,93 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: xpath
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 1
|
8
|
-
- 4
|
9
|
-
version: 0.1.4
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0.beta1
|
5
|
+
prerelease: 6
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Jonas Nicklas
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
dependencies:
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-07-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: nokogiri
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
18
|
+
requirements:
|
26
19
|
- - ~>
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
- 1
|
30
|
-
- 3
|
31
|
-
version: "1.3"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
32
22
|
type: :runtime
|
33
|
-
version_requirements: *id001
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
name: rspec
|
36
23
|
prerelease: false
|
37
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
38
33
|
none: false
|
39
|
-
requirements:
|
34
|
+
requirements:
|
40
35
|
- - ~>
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
|
43
|
-
- 2
|
44
|
-
- 0
|
45
|
-
version: "2.0"
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '2.0'
|
46
38
|
type: :development
|
47
|
-
|
48
|
-
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '2.0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
49
47
|
name: yard
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.5.8
|
54
|
+
type: :development
|
50
55
|
prerelease: false
|
51
|
-
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
57
|
none: false
|
53
|
-
requirements:
|
54
|
-
- -
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
segments:
|
57
|
-
- 0
|
58
|
-
- 5
|
59
|
-
- 8
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
60
61
|
version: 0.5.8
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
61
70
|
type: :development
|
62
|
-
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
63
78
|
description: XPath is a Ruby DSL for generating XPath expressions
|
64
|
-
email:
|
79
|
+
email:
|
65
80
|
- jonas.nicklas@gmail.com
|
66
81
|
executables: []
|
67
|
-
|
68
82
|
extensions: []
|
69
|
-
|
70
|
-
extra_rdoc_files:
|
83
|
+
extra_rdoc_files:
|
71
84
|
- README.rdoc
|
72
|
-
files:
|
85
|
+
files:
|
86
|
+
- lib/xpath/dsl.rb
|
73
87
|
- lib/xpath/expression.rb
|
74
88
|
- lib/xpath/html.rb
|
89
|
+
- lib/xpath/literal.rb
|
90
|
+
- lib/xpath/renderer.rb
|
75
91
|
- lib/xpath/union.rb
|
76
92
|
- lib/xpath/version.rb
|
77
93
|
- lib/xpath.rb
|
@@ -83,38 +99,31 @@ files:
|
|
83
99
|
- spec/union_spec.rb
|
84
100
|
- spec/xpath_spec.rb
|
85
101
|
- README.rdoc
|
86
|
-
has_rdoc: true
|
87
102
|
homepage: http://github.com/jnicklas/xpath
|
88
103
|
licenses: []
|
89
|
-
|
90
104
|
post_install_message:
|
91
|
-
rdoc_options:
|
105
|
+
rdoc_options:
|
92
106
|
- --main
|
93
107
|
- README.rdoc
|
94
|
-
require_paths:
|
108
|
+
require_paths:
|
95
109
|
- lib
|
96
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
111
|
none: false
|
98
|
-
requirements:
|
99
|
-
- -
|
100
|
-
- !ruby/object:Gem::Version
|
101
|
-
|
102
|
-
|
103
|
-
version: "0"
|
104
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ! '>='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
117
|
none: false
|
106
|
-
requirements:
|
107
|
-
- -
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
|
110
|
-
- 0
|
111
|
-
version: "0"
|
118
|
+
requirements:
|
119
|
+
- - ! '>'
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: 1.3.1
|
112
122
|
requirements: []
|
113
|
-
|
114
123
|
rubyforge_project: xpath
|
115
|
-
rubygems_version: 1.
|
124
|
+
rubygems_version: 1.8.21
|
116
125
|
signing_key:
|
117
126
|
specification_version: 3
|
118
127
|
summary: Generate XPath expressions from Ruby
|
119
128
|
test_files: []
|
120
|
-
|
129
|
+
has_rdoc:
|