mechanize 2.7.3 → 2.7.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of mechanize might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +15 -0
- data/.travis.yml +5 -6
- data/CHANGELOG.rdoc +22 -0
- data/EXAMPLES.rdoc +1 -1
- data/Gemfile +3 -0
- data/Manifest.txt +0 -1
- data/README.rdoc +11 -17
- data/Rakefile +22 -38
- data/examples/{rubyforge.rb → rubygems.rb} +5 -4
- data/lib/mechanize.rb +72 -31
- data/lib/mechanize/directory_saver.rb +14 -2
- data/lib/mechanize/element_matcher.rb +24 -13
- data/lib/mechanize/file_response.rb +1 -3
- data/lib/mechanize/form.rb +8 -2
- data/lib/mechanize/http/agent.rb +38 -30
- data/lib/mechanize/http/auth_store.rb +2 -0
- data/lib/mechanize/http/www_authenticate_parser.rb +1 -1
- data/lib/mechanize/page.rb +162 -54
- data/lib/mechanize/page/image.rb +2 -0
- data/lib/mechanize/page/link.rb +5 -0
- data/lib/mechanize/pluggable_parsers.rb +13 -1
- data/lib/mechanize/test_case.rb +5 -0
- data/lib/mechanize/unsupported_scheme_error.rb +4 -2
- data/lib/mechanize/util.rb +88 -43
- data/lib/mechanize/version.rb +3 -0
- data/mechanize.gemspec +61 -0
- data/test/test_mechanize.rb +55 -41
- data/test/test_mechanize_form.rb +19 -0
- data/test/test_mechanize_form_encoding.rb +2 -7
- data/test/test_mechanize_http_agent.rb +61 -12
- data/test/test_mechanize_http_www_authenticate_parser.rb +8 -0
- data/test/test_mechanize_link.rb +14 -1
- data/test/test_mechanize_page.rb +53 -6
- data/test/test_mechanize_page_encoding.rb +2 -3
- data/test/test_mechanize_page_link.rb +17 -2
- data/test/test_mechanize_util.rb +45 -10
- metadata +147 -72
- data/.gemtest +0 -0
- data/lib/mechanize/monkey_patch.rb +0 -17
@@ -16,7 +16,8 @@ class Mechanize::DirectorySaver < Mechanize::Download
|
|
16
16
|
# Creates a DirectorySaver subclass that will save responses to the given
|
17
17
|
# +directory+. If +options+ includes a +decode_filename+ value set to +true+
|
18
18
|
# then the downloaded filename will be ran through +CGI.unescape+ before
|
19
|
-
# being saved.
|
19
|
+
# being saved. If +options+ includes a +overwrite+ value set to +true+ then
|
20
|
+
# downloaded file will be overwritten if two files with the same names exist.
|
20
21
|
|
21
22
|
def self.save_to directory, options = {}
|
22
23
|
directory = File.expand_path directory
|
@@ -41,6 +42,13 @@ class Mechanize::DirectorySaver < Mechanize::Download
|
|
41
42
|
@options[:decode_filename]
|
42
43
|
end
|
43
44
|
|
45
|
+
##
|
46
|
+
# Checks if +overwrite+ parameter is set to true
|
47
|
+
|
48
|
+
def self.overwrite?
|
49
|
+
@options[:overwrite]
|
50
|
+
end
|
51
|
+
|
44
52
|
##
|
45
53
|
# Saves the +body_io+ into the directory specified for this DirectorySaver
|
46
54
|
# by save_to. The filename is chosen by Mechanize::Parser#extract_filename.
|
@@ -58,7 +66,11 @@ class Mechanize::DirectorySaver < Mechanize::Download
|
|
58
66
|
@filename = CGI.unescape(@filename) if self.class.decode_filename?
|
59
67
|
path = File.join directory, @filename
|
60
68
|
|
61
|
-
|
69
|
+
if self.class.overwrite?
|
70
|
+
save! path
|
71
|
+
else
|
72
|
+
save path
|
73
|
+
end
|
62
74
|
end
|
63
75
|
|
64
76
|
end
|
@@ -3,17 +3,28 @@ module Mechanize::ElementMatcher
|
|
3
3
|
def elements_with singular, plural = "#{singular}s"
|
4
4
|
class_eval <<-CODE
|
5
5
|
def #{plural}_with criteria = {}
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
6
|
+
selector = method = nil
|
7
|
+
if String === criteria then
|
8
|
+
criteria = {:name => criteria}
|
9
|
+
else
|
10
|
+
criteria = criteria.each_with_object({}) { |(k, v), h|
|
11
|
+
case k = k.to_sym
|
12
|
+
when :id
|
13
|
+
h[:dom_id] = v
|
14
|
+
when :class
|
15
|
+
h[:dom_class] = v
|
16
|
+
when :search, :xpath, :css
|
17
|
+
if v
|
18
|
+
selector = v
|
19
|
+
method = k
|
20
|
+
end
|
21
|
+
else
|
22
|
+
h[k] = v
|
23
|
+
end
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
f = select_#{plural}(selector, method).find_all do |thing|
|
17
28
|
criteria.all? do |k,v|
|
18
29
|
v === thing.send(k)
|
19
30
|
end
|
@@ -35,11 +46,11 @@ module Mechanize::ElementMatcher
|
|
35
46
|
f
|
36
47
|
end
|
37
48
|
|
38
|
-
def select_#{plural} selector
|
49
|
+
def select_#{plural} selector, method = :search
|
39
50
|
if selector.nil? then
|
40
51
|
#{plural}
|
41
52
|
else
|
42
|
-
nodes =
|
53
|
+
nodes = __send__(method, selector)
|
43
54
|
#{plural}.find_all do |element|
|
44
55
|
nodes.include?(element.node)
|
45
56
|
end
|
@@ -69,9 +69,7 @@ class Mechanize::FileResponse
|
|
69
69
|
}
|
70
70
|
body << %w[</body></html>]
|
71
71
|
|
72
|
-
body
|
73
|
-
body.force_encoding Encoding::BINARY if body.respond_to? :force_encoding
|
74
|
-
body
|
72
|
+
body.join("\n").force_encoding(Encoding::BINARY)
|
75
73
|
end
|
76
74
|
|
77
75
|
def directory?
|
data/lib/mechanize/form.rb
CHANGED
@@ -330,6 +330,13 @@ class Mechanize::Form
|
|
330
330
|
@clicked_buttons << button
|
331
331
|
end
|
332
332
|
|
333
|
+
# This method allows the same form to be submitted second time
|
334
|
+
# with the different submit button being clicked.
|
335
|
+
def reset
|
336
|
+
# In the future, should add more functionality here to reset the form values to their defaults.
|
337
|
+
@clicked_buttons = []
|
338
|
+
end
|
339
|
+
|
333
340
|
# This method calculates the request data to be sent back to the server
|
334
341
|
# for this form, depending on if this is a regular post, get, or a
|
335
342
|
# multi-part post,
|
@@ -348,8 +355,7 @@ class Mechanize::Form
|
|
348
355
|
params.concat @file_uploads.map { |f| file_to_multipart(f) }
|
349
356
|
|
350
357
|
params.map do |part|
|
351
|
-
part.force_encoding(
|
352
|
-
"--#{boundary}\r\n#{part}"
|
358
|
+
"--#{boundary}\r\n#{part.force_encoding(Encoding::ASCII_8BIT)}"
|
353
359
|
end.join('') +
|
354
360
|
"--#{boundary}--\r\n"
|
355
361
|
else
|
data/lib/mechanize/http/agent.rb
CHANGED
@@ -167,7 +167,7 @@ class Mechanize::HTTP::Agent
|
|
167
167
|
|
168
168
|
@scheme_handlers = Hash.new { |h, scheme|
|
169
169
|
h[scheme] = lambda { |link, page|
|
170
|
-
raise Mechanize::UnsupportedSchemeError,
|
170
|
+
raise Mechanize::UnsupportedSchemeError.new(scheme, link)
|
171
171
|
}
|
172
172
|
}
|
173
173
|
|
@@ -213,6 +213,10 @@ class Mechanize::HTTP::Agent
|
|
213
213
|
# +method+ is used to retrieve it, along with the HTTP +headers+, request
|
214
214
|
# +params+ and HTTP +referer+.
|
215
215
|
#
|
216
|
+
# The final URI to access is built with +uri+ and +params+, the
|
217
|
+
# latter of which is formatted into a string using
|
218
|
+
# Mechanize::Util.build_query_string, which see.
|
219
|
+
#
|
216
220
|
# +redirects+ tracks the number of redirects experienced when retrieving the
|
217
221
|
# page. If it is over the redirection_limit an error will be raised.
|
218
222
|
|
@@ -246,9 +250,13 @@ class Mechanize::HTTP::Agent
|
|
246
250
|
request['If-Modified-Since'] = last_modified
|
247
251
|
end if @conditional_requests
|
248
252
|
|
249
|
-
# Specify timeouts if
|
250
|
-
|
251
|
-
|
253
|
+
# Specify timeouts if supplied and our connection supports them
|
254
|
+
if @open_timeout && connection.respond_to?(:open_timeout=)
|
255
|
+
connection.open_timeout = @open_timeout
|
256
|
+
end
|
257
|
+
if @read_timeout && connection.respond_to?(:read_timeout=)
|
258
|
+
connection.read_timeout = @read_timeout
|
259
|
+
end
|
252
260
|
|
253
261
|
request_log request
|
254
262
|
|
@@ -515,6 +523,8 @@ class Mechanize::HTTP::Agent
|
|
515
523
|
|
516
524
|
def request_auth request, uri
|
517
525
|
base_uri = uri + '/'
|
526
|
+
base_uri.user = nil
|
527
|
+
base_uri.password = nil
|
518
528
|
schemes = @authenticate_methods[base_uri]
|
519
529
|
|
520
530
|
if realm = schemes[:digest].find { |r| r.uri == base_uri } then
|
@@ -530,9 +540,7 @@ class Mechanize::HTTP::Agent
|
|
530
540
|
def request_auth_digest request, uri, realm, base_uri, iis
|
531
541
|
challenge = @digest_challenges[realm]
|
532
542
|
|
533
|
-
user, password, = @auth_store.credentials_for uri, realm.realm
|
534
|
-
uri.user = user
|
535
|
-
uri.password = password
|
543
|
+
uri.user, uri.password, = @auth_store.credentials_for uri, realm.realm
|
536
544
|
|
537
545
|
auth = @digest_auth.auth_header uri, challenge.to_s, request.method, iis
|
538
546
|
request['Authorization'] = auth
|
@@ -608,17 +616,7 @@ class Mechanize::HTTP::Agent
|
|
608
616
|
end
|
609
617
|
|
610
618
|
url.gsub!(/[^#{0.chr}-#{126.chr}]/o) { |match|
|
611
|
-
|
612
|
-
Mechanize::Util.uri_escape(match)
|
613
|
-
else
|
614
|
-
begin
|
615
|
-
sprintf('%%%X', match.unpack($KCODE == 'UTF8' ? 'U' : 'C').first)
|
616
|
-
rescue ArgumentError
|
617
|
-
# workaround for ruby 1.8 with -Ku but ISO-8859-1 characters in
|
618
|
-
# URIs. See #227. I can't wait to drop 1.8 support
|
619
|
-
sprintf('%%%X', match.unpack('C').first)
|
620
|
-
end
|
621
|
-
end
|
619
|
+
Mechanize::Util.uri_escape(match)
|
622
620
|
}
|
623
621
|
|
624
622
|
escaped_url = Mechanize::Util.html_unescape(
|
@@ -679,6 +677,18 @@ class Mechanize::HTTP::Agent
|
|
679
677
|
uri
|
680
678
|
end
|
681
679
|
|
680
|
+
def secure_resolve!(uri, referer = current_page)
|
681
|
+
new_uri = resolve(uri, referer)
|
682
|
+
|
683
|
+
if (referer_uri = referer && referer.uri) &&
|
684
|
+
referer_uri.scheme != 'file'.freeze &&
|
685
|
+
new_uri.scheme == 'file'.freeze
|
686
|
+
raise Mechanize::Error, "insecure redirect to a file URI"
|
687
|
+
end
|
688
|
+
|
689
|
+
new_uri
|
690
|
+
end
|
691
|
+
|
682
692
|
def resolve_parameters uri, method, parameters
|
683
693
|
case method
|
684
694
|
when :head, :get, :delete, :trace then
|
@@ -740,7 +750,7 @@ class Mechanize::HTTP::Agent
|
|
740
750
|
|
741
751
|
if existing_realms.include? realm
|
742
752
|
message = 'Digest authentication failed'
|
743
|
-
raise Mechanize::UnauthorizedError.new(page,
|
753
|
+
raise Mechanize::UnauthorizedError.new(page, challenges, message)
|
744
754
|
end
|
745
755
|
|
746
756
|
existing_realms << realm
|
@@ -822,7 +832,7 @@ class Mechanize::HTTP::Agent
|
|
822
832
|
ensure
|
823
833
|
begin
|
824
834
|
if Tempfile === body_io and
|
825
|
-
(StringIO === out_io or out_io.path != body_io.path) then
|
835
|
+
(StringIO === out_io or (out_io and out_io.path != body_io.path)) then
|
826
836
|
body_io.close!
|
827
837
|
end
|
828
838
|
rescue IOError
|
@@ -861,7 +871,7 @@ class Mechanize::HTTP::Agent
|
|
861
871
|
def response_follow_meta_refresh response, uri, page, redirects
|
862
872
|
delay, new_url = get_meta_refresh(response, uri, page)
|
863
873
|
return nil unless delay
|
864
|
-
new_url = new_url ?
|
874
|
+
new_url = new_url ? secure_resolve!(new_url, page) : uri
|
865
875
|
|
866
876
|
raise Mechanize::RedirectLimitReachedError.new(page, redirects) if
|
867
877
|
redirects + 1 > @redirection_limit
|
@@ -893,10 +903,9 @@ class Mechanize::HTTP::Agent
|
|
893
903
|
if use_tempfile? content_length then
|
894
904
|
body_io = make_tempfile 'mechanize-raw'
|
895
905
|
else
|
896
|
-
body_io = StringIO.new
|
906
|
+
body_io = StringIO.new.set_encoding(Encoding::BINARY)
|
897
907
|
end
|
898
908
|
|
899
|
-
body_io.set_encoding Encoding::BINARY if body_io.respond_to? :set_encoding
|
900
909
|
total = 0
|
901
910
|
|
902
911
|
begin
|
@@ -970,8 +979,9 @@ class Mechanize::HTTP::Agent
|
|
970
979
|
headers.delete key
|
971
980
|
end
|
972
981
|
|
982
|
+
new_uri = secure_resolve! response['Location'].to_s, page
|
983
|
+
|
973
984
|
@history.push(page, page.uri)
|
974
|
-
new_uri = resolve response['Location'].to_s, page
|
975
985
|
|
976
986
|
fetch new_uri, redirect_method, headers, [], referer, redirects + 1
|
977
987
|
end
|
@@ -1086,12 +1096,12 @@ class Mechanize::HTTP::Agent
|
|
1086
1096
|
# SSL version to use
|
1087
1097
|
def ssl_version
|
1088
1098
|
@http.ssl_version
|
1089
|
-
end
|
1099
|
+
end
|
1090
1100
|
|
1091
1101
|
# Sets the SSL version to use
|
1092
1102
|
def ssl_version= ssl_version
|
1093
1103
|
@http.ssl_version = ssl_version
|
1094
|
-
end
|
1104
|
+
end
|
1095
1105
|
|
1096
1106
|
# A callback for additional certificate verification. See
|
1097
1107
|
# OpenSSL::SSL::SSLContext#verify_callback
|
@@ -1141,9 +1151,7 @@ class Mechanize::HTTP::Agent
|
|
1141
1151
|
# processing.
|
1142
1152
|
|
1143
1153
|
def auto_io name, read_size, input_io
|
1144
|
-
out_io = StringIO.new
|
1145
|
-
|
1146
|
-
out_io.set_encoding Encoding::BINARY if out_io.respond_to? :set_encoding
|
1154
|
+
out_io = StringIO.new.set_encoding(Encoding::BINARY)
|
1147
1155
|
|
1148
1156
|
until input_io.eof? do
|
1149
1157
|
if StringIO === out_io and use_tempfile? out_io.size then
|
@@ -1216,7 +1224,7 @@ class Mechanize::HTTP::Agent
|
|
1216
1224
|
def make_tempfile name
|
1217
1225
|
io = Tempfile.new name
|
1218
1226
|
io.unlink
|
1219
|
-
io.binmode
|
1227
|
+
io.binmode
|
1220
1228
|
io
|
1221
1229
|
end
|
1222
1230
|
|
@@ -128,7 +128,7 @@ class Mechanize::HTTP::WWWAuthenticateParser
|
|
128
128
|
|
129
129
|
def auth_param
|
130
130
|
return nil unless name = token
|
131
|
-
return nil unless @scanner.scan(
|
131
|
+
return nil unless @scanner.scan(/ *= */)
|
132
132
|
|
133
133
|
value = if @scanner.peek(1) == '"' then
|
134
134
|
quoted_string
|
data/lib/mechanize/page.rb
CHANGED
@@ -42,7 +42,7 @@ class Mechanize::Page < Mechanize::File
|
|
42
42
|
if body
|
43
43
|
# Force the encoding to be 8BIT so we can perform regular expressions.
|
44
44
|
# We'll set it to the detected encoding later
|
45
|
-
body.force_encoding
|
45
|
+
body.force_encoding(Encoding::ASCII_8BIT)
|
46
46
|
|
47
47
|
@encodings.concat self.class.meta_charset body
|
48
48
|
|
@@ -92,7 +92,9 @@ class Mechanize::Page < Mechanize::File
|
|
92
92
|
end
|
93
93
|
|
94
94
|
def encoding
|
95
|
-
parser.
|
95
|
+
parser.encoding
|
96
|
+
rescue NoMethodError
|
97
|
+
nil
|
96
98
|
end
|
97
99
|
|
98
100
|
# Return whether parser result has errors related to encoding or not.
|
@@ -109,15 +111,17 @@ class Mechanize::Page < Mechanize::File
|
|
109
111
|
|
110
112
|
def parser
|
111
113
|
return @parser if @parser
|
112
|
-
return
|
114
|
+
return unless @body
|
115
|
+
|
116
|
+
url = @uri && @uri.to_s
|
113
117
|
|
114
|
-
if @encoding
|
115
|
-
@parser =
|
116
|
-
elsif mech.force_default_encoding
|
117
|
-
@parser =
|
118
|
+
if @encoding
|
119
|
+
@parser = mech.html_parser.parse html_body, url, @encoding
|
120
|
+
elsif mech.force_default_encoding
|
121
|
+
@parser = mech.html_parser.parse html_body, url, @mech.default_encoding
|
118
122
|
else
|
119
123
|
@encodings.reverse_each do |encoding|
|
120
|
-
@parser =
|
124
|
+
@parser = mech.html_parser.parse html_body, url, encoding
|
121
125
|
|
122
126
|
break unless encoding_error? @parser
|
123
127
|
end
|
@@ -192,39 +196,71 @@ class Mechanize::Page < Mechanize::File
|
|
192
196
|
##
|
193
197
|
# :method: search
|
194
198
|
#
|
195
|
-
#
|
196
|
-
# be XPath or CSS and an optional Hash of namespaces may be appended.
|
199
|
+
# Shorthand for +parser.search+.
|
197
200
|
#
|
198
|
-
# See Nokogiri::XML::Node#search for
|
201
|
+
# See Nokogiri::XML::Node#search for details.
|
199
202
|
|
200
|
-
|
203
|
+
##
|
204
|
+
# :method: css
|
205
|
+
#
|
206
|
+
# Shorthand for +parser.css+.
|
207
|
+
#
|
208
|
+
# See also Nokogiri::XML::Node#css for details.
|
201
209
|
|
202
|
-
|
210
|
+
##
|
211
|
+
# :method: xpath
|
212
|
+
#
|
213
|
+
# Shorthand for +parser.xpath+.
|
214
|
+
#
|
215
|
+
# See also Nokogiri::XML::Node#xpath for details.
|
203
216
|
|
204
217
|
##
|
205
218
|
# :method: at
|
206
219
|
#
|
207
|
-
#
|
208
|
-
#
|
220
|
+
# Shorthand for +parser.at+.
|
221
|
+
#
|
222
|
+
# See also Nokogiri::XML::Node#at for details.
|
223
|
+
|
224
|
+
##
|
225
|
+
# :method: at_css
|
226
|
+
#
|
227
|
+
# Shorthand for +parser.at_css+.
|
228
|
+
#
|
229
|
+
# See also Nokogiri::XML::Node#at_css for details.
|
230
|
+
|
231
|
+
##
|
232
|
+
# :method: at_xpath
|
209
233
|
#
|
210
|
-
#
|
234
|
+
# Shorthand for +parser.at_xpath+.
|
235
|
+
#
|
236
|
+
# See also Nokogiri::XML::Node#at_xpath for details.
|
211
237
|
|
212
|
-
|
238
|
+
def_delegators :parser, :search, :css, :xpath, :at, :at_css, :at_xpath
|
239
|
+
|
240
|
+
alias / search
|
241
|
+
alias % at
|
213
242
|
|
214
243
|
##
|
215
244
|
# :method: form_with
|
216
245
|
#
|
217
246
|
# :call-seq:
|
218
247
|
# form_with(criteria)
|
248
|
+
# form_with(criteria) { |form| ... }
|
219
249
|
#
|
220
|
-
# Find a single form matching +criteria+.
|
221
|
-
#
|
222
|
-
#
|
250
|
+
# Find a single form matching +criteria+. See +forms_with+ for
|
251
|
+
# details of +criteria+.
|
252
|
+
#
|
253
|
+
# Examples:
|
254
|
+
# page.form_with(action: '/post/login.php') do |f|
|
223
255
|
# ...
|
224
256
|
# end
|
225
257
|
|
226
258
|
##
|
227
|
-
# :
|
259
|
+
# :method: form_with!(criteria)
|
260
|
+
#
|
261
|
+
# :call-seq:
|
262
|
+
# form_with!(criteria)
|
263
|
+
# form_with!(criteria) { |form| ... }
|
228
264
|
#
|
229
265
|
# Same as +form_with+ but raises an ElementNotFoundError if no button matches
|
230
266
|
# +criteria+
|
@@ -232,11 +268,33 @@ class Mechanize::Page < Mechanize::File
|
|
232
268
|
##
|
233
269
|
# :method: forms_with
|
234
270
|
#
|
235
|
-
# :call-seq:
|
271
|
+
# :call-seq:
|
272
|
+
# forms_with(name)
|
273
|
+
# forms_with(name: name_matcher, id: id_matcher, class: class_matcher,
|
274
|
+
# search: search_expression, xpath: xpath_expression, css: css_expression,
|
275
|
+
# action: action_matcher, ...)
|
276
|
+
#
|
277
|
+
# Find all forms form matching criteria. If a string is given, it
|
278
|
+
# is taken as a name attribute value. If a hash is given, forms
|
279
|
+
# are narrowed by the key-value pairs as follows.
|
280
|
+
#
|
281
|
+
# :id, :dom_id: selects forms with a #dom_id value that matches this
|
282
|
+
# value.
|
283
|
+
#
|
284
|
+
# :class, :dom_class: selects forms with a #dom_class value that
|
285
|
+
# matches this value.
|
286
|
+
#
|
287
|
+
# :search: only selects forms matching this selector expression.
|
288
|
+
#
|
289
|
+
# :xpath: only selects forms matching this XPath expression.
|
290
|
+
#
|
291
|
+
# :css: only selects forms matching this CSS selector expression.
|
292
|
+
#
|
293
|
+
# :action, :method, etc.: narrows forms by a given attribute value
|
294
|
+
# using the === operator.
|
236
295
|
#
|
237
|
-
# Find all forms form matching +criteria+.
|
238
296
|
# Example:
|
239
|
-
# page.forms_with(:
|
297
|
+
# page.forms_with(css: '#content table.login_box form', method: /\APOST\z/i, ).each do |f|
|
240
298
|
# ...
|
241
299
|
# end
|
242
300
|
|
@@ -245,14 +303,22 @@ class Mechanize::Page < Mechanize::File
|
|
245
303
|
##
|
246
304
|
# :method: link_with
|
247
305
|
#
|
248
|
-
# :call-seq:
|
306
|
+
# :call-seq:
|
307
|
+
# link_with(criteria)
|
308
|
+
# link_with(criteria) { |link| ... }
|
309
|
+
#
|
310
|
+
# Find a single link matching +criteria+. See +forms_with+ for
|
311
|
+
# details of +criteria+, where for "form(s)" read "link(s)".
|
249
312
|
#
|
250
|
-
# Find a single link matching +criteria+.
|
251
313
|
# Example:
|
252
|
-
# page.link_with(:
|
314
|
+
# page.link_with(href: /foo/).click
|
253
315
|
|
254
316
|
##
|
255
|
-
# :
|
317
|
+
# :method: link_with!
|
318
|
+
#
|
319
|
+
# :call-seq:
|
320
|
+
# link_with!(criteria)
|
321
|
+
# link_with!(criteria) { |link| ... }
|
256
322
|
#
|
257
323
|
# Same as +link_with+ but raises an ElementNotFoundError if no button matches
|
258
324
|
# +criteria+
|
@@ -263,9 +329,11 @@ class Mechanize::Page < Mechanize::File
|
|
263
329
|
# :call-seq:
|
264
330
|
# links_with(criteria)
|
265
331
|
#
|
266
|
-
# Find all links matching +criteria+.
|
332
|
+
# Find all links matching +criteria+. See +forms_with+ for details
|
333
|
+
# of +criteria+, where for "form(s)" read "link(s)".
|
334
|
+
#
|
267
335
|
# Example:
|
268
|
-
# page.links_with(:
|
336
|
+
# page.links_with(href: /foo/).each do |link|
|
269
337
|
# puts link.href
|
270
338
|
# end
|
271
339
|
|
@@ -274,14 +342,22 @@ class Mechanize::Page < Mechanize::File
|
|
274
342
|
##
|
275
343
|
# :method: base_with
|
276
344
|
#
|
277
|
-
# :call-seq:
|
345
|
+
# :call-seq:
|
346
|
+
# base_with(criteria)
|
347
|
+
# base_with(criteria) { |base| ... }
|
348
|
+
#
|
349
|
+
# Find a single base tag matching +criteria+. See +forms_with+ for
|
350
|
+
# details of +criteria+, where for "form(s)" read "base tag(s)".
|
278
351
|
#
|
279
|
-
# Find a single base tag matching +criteria+.
|
280
352
|
# Example:
|
281
|
-
# page.base_with(:
|
353
|
+
# page.base_with(href: /foo/).click
|
282
354
|
|
283
355
|
##
|
284
|
-
# :
|
356
|
+
# :method: base_with!(criteria)
|
357
|
+
#
|
358
|
+
# :call-seq:
|
359
|
+
# base_with!(criteria)
|
360
|
+
# base_with!(criteria) { |base| ... }
|
285
361
|
#
|
286
362
|
# Same as +base_with+ but raises an ElementNotFoundError if no button matches
|
287
363
|
# +criteria+
|
@@ -291,9 +367,11 @@ class Mechanize::Page < Mechanize::File
|
|
291
367
|
#
|
292
368
|
# :call-seq: bases_with(criteria)
|
293
369
|
#
|
294
|
-
# Find all base tags matching +criteria+.
|
370
|
+
# Find all base tags matching +criteria+. See +forms_with+ for
|
371
|
+
# details of +criteria+, where for "form(s)" read "base tag(s)".
|
372
|
+
#
|
295
373
|
# Example:
|
296
|
-
# page.bases_with(:
|
374
|
+
# page.bases_with(href: /foo/).each do |base|
|
297
375
|
# puts base.href
|
298
376
|
# end
|
299
377
|
|
@@ -302,14 +380,22 @@ class Mechanize::Page < Mechanize::File
|
|
302
380
|
##
|
303
381
|
# :method: frame_with
|
304
382
|
#
|
305
|
-
# :call-seq:
|
383
|
+
# :call-seq:
|
384
|
+
# frame_with(criteria)
|
385
|
+
# frame_with(criteria) { |frame| ... }
|
386
|
+
#
|
387
|
+
# Find a single frame tag matching +criteria+. See +forms_with+ for
|
388
|
+
# details of +criteria+, where for "form(s)" read "frame tag(s)".
|
306
389
|
#
|
307
|
-
# Find a single frame tag matching +criteria+.
|
308
390
|
# Example:
|
309
|
-
# page.frame_with(:
|
391
|
+
# page.frame_with(src: /foo/).click
|
310
392
|
|
311
393
|
##
|
312
|
-
# :
|
394
|
+
# :method: frame_with!
|
395
|
+
#
|
396
|
+
# :call-seq:
|
397
|
+
# frame_with!(criteria)
|
398
|
+
# frame_with!(criteria) { |frame| ... }
|
313
399
|
#
|
314
400
|
# Same as +frame_with+ but raises an ElementNotFoundError if no button matches
|
315
401
|
# +criteria+
|
@@ -319,9 +405,11 @@ class Mechanize::Page < Mechanize::File
|
|
319
405
|
#
|
320
406
|
# :call-seq: frames_with(criteria)
|
321
407
|
#
|
322
|
-
# Find all frame tags matching +criteria+.
|
408
|
+
# Find all frame tags matching +criteria+. See +forms_with+ for
|
409
|
+
# details of +criteria+, where for "form(s)" read "frame tag(s)".
|
410
|
+
#
|
323
411
|
# Example:
|
324
|
-
# page.frames_with(:
|
412
|
+
# page.frames_with(src: /foo/).each do |frame|
|
325
413
|
# p frame.src
|
326
414
|
# end
|
327
415
|
|
@@ -330,14 +418,22 @@ class Mechanize::Page < Mechanize::File
|
|
330
418
|
##
|
331
419
|
# :method: iframe_with
|
332
420
|
#
|
333
|
-
# :call-seq:
|
421
|
+
# :call-seq:
|
422
|
+
# iframe_with(criteria)
|
423
|
+
# iframe_with(criteria) { |iframe| ... }
|
424
|
+
#
|
425
|
+
# Find a single iframe tag matching +criteria+. See +forms_with+ for
|
426
|
+
# details of +criteria+, where for "form(s)" read "iframe tag(s)".
|
334
427
|
#
|
335
|
-
# Find a single iframe tag matching +criteria+.
|
336
428
|
# Example:
|
337
|
-
# page.iframe_with(:
|
429
|
+
# page.iframe_with(src: /foo/).click
|
338
430
|
|
339
431
|
##
|
340
|
-
# :
|
432
|
+
# :method: iframe_with!
|
433
|
+
#
|
434
|
+
# :call-seq:
|
435
|
+
# iframe_with!(criteria)
|
436
|
+
# iframe_with!(criteria) { |iframe| ... }
|
341
437
|
#
|
342
438
|
# Same as +iframe_with+ but raises an ElementNotFoundError if no button
|
343
439
|
# matches +criteria+
|
@@ -347,9 +443,11 @@ class Mechanize::Page < Mechanize::File
|
|
347
443
|
#
|
348
444
|
# :call-seq: iframes_with(criteria)
|
349
445
|
#
|
350
|
-
# Find all iframe tags matching +criteria+.
|
446
|
+
# Find all iframe tags matching +criteria+. See +forms_with+ for
|
447
|
+
# details of +criteria+, where for "form(s)" read "iframe tag(s)".
|
448
|
+
#
|
351
449
|
# Example:
|
352
|
-
# page.iframes_with(:
|
450
|
+
# page.iframes_with(src: /foo/).each do |iframe|
|
353
451
|
# p iframe.src
|
354
452
|
# end
|
355
453
|
|
@@ -358,14 +456,22 @@ class Mechanize::Page < Mechanize::File
|
|
358
456
|
##
|
359
457
|
# :method: image_with
|
360
458
|
#
|
361
|
-
# :call-seq:
|
459
|
+
# :call-seq:
|
460
|
+
# image_with(criteria)
|
461
|
+
# image_with(criteria) { |image| ... }
|
462
|
+
#
|
463
|
+
# Find a single image matching +criteria+. See +forms_with+ for
|
464
|
+
# details of +criteria+, where for "form(s)" read "image(s)".
|
362
465
|
#
|
363
|
-
# Find a single image matching +criteria+.
|
364
466
|
# Example:
|
365
|
-
# page.image_with(:
|
467
|
+
# page.image_with(alt: /main/).fetch.save
|
366
468
|
|
367
469
|
##
|
368
|
-
# :
|
470
|
+
# :method: image_with!
|
471
|
+
#
|
472
|
+
# :call-seq:
|
473
|
+
# image_with!(criteria)
|
474
|
+
# image_with!(criteria) { |image| ... }
|
369
475
|
#
|
370
476
|
# Same as +image_with+ but raises an ElementNotFoundError if no button matches
|
371
477
|
# +criteria+
|
@@ -375,9 +481,11 @@ class Mechanize::Page < Mechanize::File
|
|
375
481
|
#
|
376
482
|
# :call-seq: images_with(criteria)
|
377
483
|
#
|
378
|
-
# Find all images matching +criteria+.
|
484
|
+
# Find all images matching +criteria+. See +forms_with+ for
|
485
|
+
# details of +criteria+, where for "form(s)" read "image(s)".
|
486
|
+
#
|
379
487
|
# Example:
|
380
|
-
# page.images_with(:
|
488
|
+
# page.images_with(src: /jpg\Z/).each do |img|
|
381
489
|
# img.fetch.save
|
382
490
|
# end
|
383
491
|
|