xpath 0.1.4 → 1.0.0.beta1
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.
- 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:
|