mechanize 0.8.4 → 0.8.5
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.
- data/EXAMPLES.txt +1 -1
- data/GUIDE.txt +11 -12
- data/History.txt +24 -0
- data/Manifest.txt +2 -1
- data/README.txt +1 -1
- data/Rakefile +2 -2
- data/lib/www/mechanize.rb +43 -11
- data/lib/www/mechanize/chain/header_resolver.rb +1 -1
- data/lib/www/mechanize/chain/request_resolver.rb +4 -0
- data/lib/www/mechanize/chain/response_body_parser.rb +1 -1
- data/lib/www/mechanize/chain/response_reader.rb +1 -1
- data/lib/www/mechanize/chain/ssl_resolver.rb +1 -1
- data/lib/www/mechanize/chain/uri_resolver.rb +21 -9
- data/lib/www/mechanize/cookie_jar.rb +11 -6
- data/lib/www/mechanize/file_response.rb +2 -0
- data/lib/www/mechanize/form.rb +33 -13
- data/lib/www/mechanize/form/multi_select_list.rb +2 -2
- data/lib/www/mechanize/form/radio_button.rb +1 -1
- data/lib/www/mechanize/list.rb +15 -38
- data/lib/www/mechanize/page.rb +17 -10
- data/lib/www/mechanize/page/link.rb +2 -2
- data/lib/www/mechanize/redirect_not_get_or_head_error.rb +20 -0
- data/test/helper.rb +4 -0
- data/test/htdocs/relative/tc_relative_links.html +1 -0
- data/test/servlets.rb +38 -8
- data/test/test_checkboxes.rb +14 -15
- data/test/test_cookie_class.rb +5 -4
- data/test/test_cookie_jar.rb +29 -0
- data/test/test_encoded_links.rb +1 -1
- data/test/test_errors.rb +1 -1
- data/test/test_follow_meta.rb +50 -6
- data/test/test_form_as_hash.rb +5 -5
- data/test/test_forms.rb +28 -27
- data/test/test_links.rb +22 -16
- data/test/test_mech.rb +24 -7
- data/test/test_multi_select.rb +27 -27
- data/test/test_pluggable_parser.rb +4 -4
- data/test/test_radiobutton.rb +35 -42
- data/test/test_redirect_verb_handling.rb +45 -0
- data/test/test_referer.rb +1 -1
- data/test/test_relative_links.rb +4 -4
- data/test/test_scheme.rb +4 -0
- data/test/test_select.rb +15 -15
- data/test/test_select_all.rb +1 -1
- data/test/test_select_none.rb +1 -1
- data/test/test_select_noopts.rb +1 -1
- data/test/test_set_fields.rb +4 -4
- data/test/test_textarea.rb +13 -13
- data/test/test_upload.rb +1 -1
- metadata +10 -6
- data/NOTES.txt +0 -318
data/EXAMPLES.txt
CHANGED
@@ -117,7 +117,7 @@ This example also demonstrates subclassing Mechanize.
|
|
117
117
|
search_form.words = 'WWW'
|
118
118
|
submit search_form
|
119
119
|
|
120
|
-
page.
|
120
|
+
page.links_with(:href => %r{/projects/} ).each do |link|
|
121
121
|
next if link.href =~ %r{/projects/support/}
|
122
122
|
|
123
123
|
puts 'Loading %-30s %s' % [link.href, link.text]
|
data/GUIDE.txt
CHANGED
@@ -36,17 +36,16 @@ link to click on. Lets say we wanted to click the link whose text is 'News'.
|
|
36
36
|
Normally, we would have to do this:
|
37
37
|
page = agent.click page.links.find { |l| l.text == 'News' }
|
38
38
|
But Mechanize gives us a shortcut. Instead we can say this:
|
39
|
-
page = agent.click page.
|
39
|
+
page = agent.click page.link_with(:text => 'News')
|
40
40
|
That shortcut says "find all links with the name 'News'". You're probably
|
41
41
|
thinking "there could be multiple links with that text!", and you would be
|
42
|
-
correct! If you
|
43
|
-
|
44
|
-
|
45
|
-
agent.click page.links.text('News')[1]
|
42
|
+
correct! If you use the plural form, you can access the list.
|
43
|
+
If you wanted to click on the second news link, you could do this:
|
44
|
+
agent.click page.links_with(:text => 'News')[1]
|
46
45
|
We can even find a link with a certain href like so:
|
47
|
-
page.
|
46
|
+
page.links_with(:href => '/something')
|
48
47
|
Or chain them together to find a link with certain text and certain href:
|
49
|
-
page.
|
48
|
+
page.links_with(:text => 'News', :href => '/something')
|
50
49
|
|
51
50
|
These shortcuts that mechanize provides are available on any list that you
|
52
51
|
can fetch like frames, iframes, or forms. Now that we know how to find and
|
@@ -102,24 +101,24 @@ have many options associated with them. If you select one option, mechanize
|
|
102
101
|
will deselect the other options (unless it is a multi select!).
|
103
102
|
|
104
103
|
For example, lets select an option on a list:
|
105
|
-
form.
|
104
|
+
form.field_with(:name => 'list').options[0].select
|
106
105
|
|
107
106
|
Now lets take a look at checkboxes and radio buttons. To select a checkbox,
|
108
107
|
just check it like this:
|
109
|
-
form.
|
108
|
+
form.checkbox_with(:name => 'box').check
|
110
109
|
Radio buttons are very similar to checkboxes, but they know how to uncheck
|
111
110
|
other radio buttons of the same name. Just check a radio button like you
|
112
111
|
would a checkbox:
|
113
|
-
form.
|
112
|
+
form.radiobuttons_with(:name => 'box')[1].check
|
114
113
|
Mechanize also makes file uploads easy! Just find the file upload field, and
|
115
114
|
tell it what file name you want to upload:
|
116
|
-
form.file_uploads.file_name = "somefile.jpg"
|
115
|
+
form.file_uploads.first.file_name = "somefile.jpg"
|
117
116
|
|
118
117
|
== Scraping Data
|
119
118
|
Mechanize uses hpricot[http://code.whytheluckystiff.net/hpricot/] to parse
|
120
119
|
html. What does this mean for you? You can treat a mechanize page like
|
121
120
|
an hpricot object. After you have used Mechanize to navigate to the page
|
122
121
|
that you need to scrape, then scrape it using hpricot methods:
|
123
|
-
agent.get('http://someurl.com/').search("
|
122
|
+
agent.get('http://someurl.com/').search(".//p[@class='posted']")
|
124
123
|
For more information on this powerful scraper, take a look at
|
125
124
|
HpricotBasics[http://code.whytheluckystiff.net/hpricot/wiki/HpricotBasics]
|
data/History.txt
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
= Mechanize CHANGELOG
|
2
2
|
|
3
|
+
=== 0.8.5
|
4
|
+
|
5
|
+
* Deprecations
|
6
|
+
* WWW::Mechanize::List will be deprecated in 0.9.0, and warnings have
|
7
|
+
been added to help you upgrade.
|
8
|
+
|
9
|
+
* Bug Fixes:
|
10
|
+
* Stopped raising EOF exceptions on HEAD requests. ありがとう:HIRAKU Kuroda
|
11
|
+
* Fixed exceptions when a logger is set and file:// requests are made.
|
12
|
+
* Made Mechanize 1.9 compatible
|
13
|
+
* Not setting the port in the host header for SSL sites.
|
14
|
+
* Following refresh headers. Thanks Tim Connor!
|
15
|
+
* Cookie Jar handles cookie domains containing ports, like
|
16
|
+
'mydomain.com:443' (Thanks Michal Ochman!)
|
17
|
+
* Fixing strange uri escaping problems [#22604]
|
18
|
+
* Making content-type determintation more robust. (thanks Han Holl!)
|
19
|
+
* Dealing with links that are query string only. [#22402]
|
20
|
+
* Nokogiri may be dropped in as a replacement.
|
21
|
+
WWW::Mechanize.html_parser = Nokogiri::HTML
|
22
|
+
* Making sure the correct page is added to the history on meta refresh.
|
23
|
+
[#22708]
|
24
|
+
* Mechanize#get requests no longer send a referer unless they are relative
|
25
|
+
requests.
|
26
|
+
|
3
27
|
=== 0.8.4
|
4
28
|
|
5
29
|
* Bug Fixes:
|
data/Manifest.txt
CHANGED
@@ -4,7 +4,6 @@ GUIDE.txt
|
|
4
4
|
History.txt
|
5
5
|
LICENSE.txt
|
6
6
|
Manifest.txt
|
7
|
-
NOTES.txt
|
8
7
|
README.txt
|
9
8
|
Rakefile
|
10
9
|
examples/flickr_upload.rb
|
@@ -58,6 +57,7 @@ lib/www/mechanize/page/link.rb
|
|
58
57
|
lib/www/mechanize/page/meta.rb
|
59
58
|
lib/www/mechanize/pluggable_parsers.rb
|
60
59
|
lib/www/mechanize/redirect_limit_reached_error.rb
|
60
|
+
lib/www/mechanize/redirect_not_get_or_head_error.rb
|
61
61
|
lib/www/mechanize/response_code_error.rb
|
62
62
|
lib/www/mechanize/unsupported_scheme_error.rb
|
63
63
|
lib/www/mechanize/util.rb
|
@@ -151,6 +151,7 @@ test/test_post_form.rb
|
|
151
151
|
test/test_pretty_print.rb
|
152
152
|
test/test_radiobutton.rb
|
153
153
|
test/test_redirect_limit_reached.rb
|
154
|
+
test/test_redirect_verb_handling.rb
|
154
155
|
test/test_referer.rb
|
155
156
|
test/test_relative_links.rb
|
156
157
|
test/test_response_code.rb
|
data/README.txt
CHANGED
@@ -29,7 +29,7 @@ Copyright (c) 2005 by Michael Neumann (mneumann@ntecs.de)
|
|
29
29
|
Copyright (c) 2006-2008:
|
30
30
|
|
31
31
|
* {Aaron Patterson}[http://tenderlovemaking.com] (aaronp@rubyforge.org)
|
32
|
-
* Mike Dalessio (mike@csa.net)
|
32
|
+
* {Mike Dalessio}[http://mike.daless.io] (mike@csa.net)
|
33
33
|
|
34
34
|
This library comes with a shameless plug for employing me
|
35
35
|
(Aaron[http://tenderlovemaking.com/]) programming
|
data/Rakefile
CHANGED
@@ -6,8 +6,8 @@ require 'mechanize'
|
|
6
6
|
|
7
7
|
HOE = Hoe.new('mechanize', WWW::Mechanize::VERSION) do |p|
|
8
8
|
p.rubyforge_name = 'mechanize'
|
9
|
-
p.
|
10
|
-
p.
|
9
|
+
p.developer('Aaron Patterson','aaronp@rubyforge.org')
|
10
|
+
p.developer('Mike Dalessio','mike.dalessio@gmail.com')
|
11
11
|
p.summary = "Mechanize provides automated web-browsing"
|
12
12
|
p.extra_deps = [['hpricot', '>= 0.5.0']]
|
13
13
|
end
|
data/lib/www/mechanize.rb
CHANGED
@@ -14,6 +14,7 @@ require 'www/mechanize/content_type_error'
|
|
14
14
|
require 'www/mechanize/response_code_error'
|
15
15
|
require 'www/mechanize/unsupported_scheme_error'
|
16
16
|
require 'www/mechanize/redirect_limit_reached_error'
|
17
|
+
require 'www/mechanize/redirect_not_get_or_head_error'
|
17
18
|
require 'www/mechanize/cookie'
|
18
19
|
require 'www/mechanize/cookie_jar'
|
19
20
|
require 'www/mechanize/history'
|
@@ -39,14 +40,14 @@ module WWW
|
|
39
40
|
# agent = WWW::Mechanize.new { |a| a.log = Logger.new("mech.log") }
|
40
41
|
# agent.user_agent_alias = 'Mac Safari'
|
41
42
|
# page = agent.get("http://www.google.com/")
|
42
|
-
# search_form = page.
|
43
|
-
# search_form.
|
43
|
+
# search_form = page.form_with(:name => "f")
|
44
|
+
# search_form.field_with(:name => "q").value = "Hello"
|
44
45
|
# search_results = agent.submit(search_form)
|
45
46
|
# puts search_results.body
|
46
47
|
class Mechanize
|
47
48
|
##
|
48
49
|
# The version of Mechanize you are using.
|
49
|
-
VERSION = '0.8.
|
50
|
+
VERSION = '0.8.5'
|
50
51
|
|
51
52
|
##
|
52
53
|
# User Agent aliases
|
@@ -203,7 +204,13 @@ module WWW
|
|
203
204
|
headers = options[:headers]
|
204
205
|
end
|
205
206
|
|
206
|
-
referer
|
207
|
+
unless referer
|
208
|
+
if url =~ /^http/
|
209
|
+
referer = Page.new(nil, {'content-type'=>'text/html'})
|
210
|
+
else
|
211
|
+
referer = current_page || Page.new(nil, {'content-type'=>'text/html'})
|
212
|
+
end
|
213
|
+
end
|
207
214
|
|
208
215
|
# FIXME: Huge hack so that using a URI as a referer works. I need to
|
209
216
|
# refactor everything to pass around URIs but still support
|
@@ -440,8 +447,8 @@ module WWW
|
|
440
447
|
http_obj = options[:connection]
|
441
448
|
|
442
449
|
# Add If-Modified-Since if page is in history
|
443
|
-
if( (page = visited_page(uri)) &&
|
444
|
-
request['If-Modified-Since'] =
|
450
|
+
if( (page = visited_page(uri)) && page.response['Last-Modified'] )
|
451
|
+
request['If-Modified-Since'] = page.response['Last-Modified']
|
445
452
|
end if(@conditional_requests)
|
446
453
|
|
447
454
|
# Specify timeouts if given
|
@@ -458,9 +465,9 @@ module WWW
|
|
458
465
|
# Send the request
|
459
466
|
attempts = 0
|
460
467
|
begin
|
461
|
-
response = http_obj.request(request, *request_data) { |
|
468
|
+
response = http_obj.request(request, *request_data) { |r|
|
462
469
|
connection_chain = Chain.new([
|
463
|
-
Chain::ResponseReader.new(
|
470
|
+
Chain::ResponseReader.new(r),
|
464
471
|
Chain::BodyDecodingHandler.new,
|
465
472
|
])
|
466
473
|
connection_chain.handle(options)
|
@@ -488,9 +495,32 @@ module WWW
|
|
488
495
|
|
489
496
|
log.info("status: #{ page.code }") if log
|
490
497
|
|
491
|
-
if follow_meta_refresh
|
492
|
-
|
493
|
-
|
498
|
+
if follow_meta_refresh
|
499
|
+
redirect_uri = nil
|
500
|
+
if (page.respond_to?(:meta) && (redirect = page.meta.first))
|
501
|
+
redirect_uri = redirect.uri.to_s
|
502
|
+
elsif refresh = response['refresh']
|
503
|
+
parsed_refresh = refresh.match(/^\s*(\d+\.?\d*);\s*(url|URL)=(\S*)\s*$/)
|
504
|
+
raise StandardError, "Invalid refresh http header" unless parsed_refresh
|
505
|
+
delay = parsed_refresh[1]
|
506
|
+
location = parsed_refresh[3]
|
507
|
+
location = "http://#{uri.host}#{location}" unless location.include?("http")
|
508
|
+
if redirects + 1 > redirection_limit
|
509
|
+
raise RedirectLimitReachedError.new(page, redirects)
|
510
|
+
end
|
511
|
+
sleep delay.to_i
|
512
|
+
redirect_uri = location
|
513
|
+
end
|
514
|
+
if redirect_uri
|
515
|
+
@history.push(page, page.uri)
|
516
|
+
return fetch_page(
|
517
|
+
:uri => redirect_uri,
|
518
|
+
:referer => page,
|
519
|
+
:params => [],
|
520
|
+
:verb => :get,
|
521
|
+
:redirects => redirects + 1
|
522
|
+
)
|
523
|
+
end
|
494
524
|
end
|
495
525
|
|
496
526
|
return page if res_klass <= Net::HTTPSuccess
|
@@ -503,9 +533,11 @@ module WWW
|
|
503
533
|
log.info("follow redirect to: #{ response['Location'] }") if log
|
504
534
|
from_uri = page.uri
|
505
535
|
raise RedirectLimitReachedError.new(page, redirects) if redirects + 1 > redirection_limit
|
536
|
+
redirect_verb = options[:verb] == :head ? :head : :get
|
506
537
|
page = fetch_page( :uri => response['Location'].to_s,
|
507
538
|
:referer => page,
|
508
539
|
:params => [],
|
540
|
+
:verb => redirect_verb,
|
509
541
|
:redirects => redirects + 1
|
510
542
|
)
|
511
543
|
@history.push(page, from_uri)
|
@@ -23,7 +23,7 @@ module WWW
|
|
23
23
|
end
|
24
24
|
request['Accept-Encoding'] = 'gzip,identity'
|
25
25
|
request['Accept-Language'] = 'en-us,en;q=0.5'
|
26
|
-
host = "#{uri.host}#{uri.port.to_i
|
26
|
+
host = "#{uri.host}#{[80, 443].include?(uri.port.to_i) ? '' : ':' + uri.port.to_s}"
|
27
27
|
request['Host'] = host
|
28
28
|
request['Accept-Charset'] = 'ISO-8859-1,utf-8;q=0.7,*;q=0.7'
|
29
29
|
|
@@ -17,7 +17,7 @@ module WWW
|
|
17
17
|
content_type = nil
|
18
18
|
unless response['Content-Type'].nil?
|
19
19
|
data = response['Content-Type'].match(/^([^;]*)/)
|
20
|
-
content_type = data[1].downcase unless data.nil?
|
20
|
+
content_type = data[1].downcase.split(',')[0] unless data.nil?
|
21
21
|
end
|
22
22
|
|
23
23
|
# Find our pluggable parser
|
@@ -24,7 +24,7 @@ module WWW
|
|
24
24
|
|
25
25
|
# Net::HTTP ignores EOFError if Content-length is given, so we emulate it here.
|
26
26
|
unless res_klass <= Net::HTTPRedirection
|
27
|
-
raise EOFError if @response.content_length() && @response.content_length() != total
|
27
|
+
raise EOFError if (!params[:request].is_a?(Net::HTTP::Head)) && @response.content_length() && @response.content_length() != total
|
28
28
|
end
|
29
29
|
|
30
30
|
@response.each_header { |k,v|
|
@@ -15,7 +15,7 @@ module WWW
|
|
15
15
|
def handle(ctx, params)
|
16
16
|
uri = params[:uri]
|
17
17
|
http_obj = params[:connection]
|
18
|
-
if uri.scheme == 'https' && ! http_obj.started?
|
18
|
+
if uri.scheme == 'https' && ! http_obj.started? && ! http_obj.frozen?
|
19
19
|
http_obj.use_ssl = true
|
20
20
|
http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
21
21
|
if @ca_file
|
@@ -17,19 +17,31 @@ module WWW
|
|
17
17
|
sprintf('%%%X', match.unpack($KCODE == 'UTF8' ? 'U' : 'c')[0])
|
18
18
|
}
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
20
|
+
escaped_uri = Util.html_unescape(
|
21
|
+
uri.split(/(?:%[0-9A-Fa-f]{2})+|#/).zip(
|
22
|
+
uri.scan(/(?:%[0-9A-Fa-f]{2})+|#/)
|
23
|
+
).map { |x,y|
|
24
|
+
"#{URI.escape(x)}#{y}"
|
25
|
+
}.join('')
|
26
|
+
)
|
27
|
+
|
28
|
+
begin
|
29
|
+
uri = URI.parse(escaped_uri)
|
30
|
+
rescue
|
31
|
+
uri = URI.parse(URI.escape(escaped_uri))
|
32
|
+
end
|
33
|
+
|
29
34
|
end
|
30
35
|
uri = @scheme_handlers[
|
31
36
|
uri.relative? ? 'relative' : uri.scheme.downcase
|
32
37
|
].call(uri, params[:referer])
|
38
|
+
|
39
|
+
if params[:referer] && params[:referer].uri
|
40
|
+
if uri.path.length == 0 && uri.relative?
|
41
|
+
uri.path = params[:referer].uri.path
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
33
45
|
uri.path = '/' if uri.path.length == 0
|
34
46
|
|
35
47
|
if uri.relative?
|
@@ -13,7 +13,7 @@ module WWW
|
|
13
13
|
|
14
14
|
# Add a cookie to the Jar.
|
15
15
|
def add(uri, cookie)
|
16
|
-
return unless uri.host =~ /#{cookie.domain}$/i
|
16
|
+
return unless uri.host =~ /#{CookieJar.strip_port(cookie.domain)}$/i
|
17
17
|
normal_domain = cookie.domain.downcase
|
18
18
|
unless @jar.has_key?(normal_domain)
|
19
19
|
@jar[normal_domain] = Hash.new
|
@@ -30,7 +30,7 @@ module WWW
|
|
30
30
|
cookies = []
|
31
31
|
url.path = '/' if url.path.empty?
|
32
32
|
@jar.each_key do |domain|
|
33
|
-
if url.host =~ /#{domain}$/i
|
33
|
+
if url.host =~ /#{CookieJar.strip_port(domain)}$/i
|
34
34
|
@jar[domain].each_key do |name|
|
35
35
|
if url.path =~ /^#{@jar[domain][name].path}/
|
36
36
|
if @jar[domain][name].expires.nil?
|
@@ -68,9 +68,9 @@ module WWW
|
|
68
68
|
def save_as(file, format = :yaml)
|
69
69
|
::File.open(file, "w") { |f|
|
70
70
|
case format
|
71
|
-
when :yaml
|
71
|
+
when :yaml then
|
72
72
|
YAML::dump(@jar, f)
|
73
|
-
when :cookiestxt
|
73
|
+
when :cookiestxt then
|
74
74
|
dump_cookiestxt(f)
|
75
75
|
else
|
76
76
|
raise "Unknown cookie jar file format"
|
@@ -86,9 +86,9 @@ module WWW
|
|
86
86
|
def load(file, format = :yaml)
|
87
87
|
@jar = ::File.open(file) { |f|
|
88
88
|
case format
|
89
|
-
when :yaml
|
89
|
+
when :yaml then
|
90
90
|
YAML::load(f)
|
91
|
-
when :cookiestxt
|
91
|
+
when :cookiestxt then
|
92
92
|
load_cookiestxt(f)
|
93
93
|
else
|
94
94
|
raise "Unknown cookie jar file format"
|
@@ -181,6 +181,11 @@ module WWW
|
|
181
181
|
end
|
182
182
|
end
|
183
183
|
end
|
184
|
+
|
185
|
+
def self.strip_port(host)
|
186
|
+
host.gsub(/:[0-9]+$/,'')
|
187
|
+
end
|
188
|
+
|
184
189
|
end
|
185
190
|
end
|
186
191
|
end
|
data/lib/www/mechanize/form.rb
CHANGED
@@ -61,11 +61,6 @@ module WWW
|
|
61
61
|
|
62
62
|
def values; fields.map { |f| f.value }; end
|
63
63
|
|
64
|
-
# Fetch the first field whose name is equal to +field_name+
|
65
|
-
def field(field_name)
|
66
|
-
fields.find { |f| f.name.eql? field_name }
|
67
|
-
end
|
68
|
-
|
69
64
|
# Add a field with +field_name+ and +value+
|
70
65
|
def add_field!(field_name, value = nil)
|
71
66
|
fields << Field.new(field_name, value)
|
@@ -84,16 +79,16 @@ module WWW
|
|
84
79
|
case v
|
85
80
|
when Hash
|
86
81
|
v.each do |index, value|
|
87
|
-
self.
|
82
|
+
self.fields_with(:name => k.to_s).[](index).value = value
|
88
83
|
end
|
89
84
|
else
|
90
85
|
value = nil
|
91
86
|
index = 0
|
92
|
-
v.each do |val|
|
87
|
+
[v].flatten.each do |val|
|
93
88
|
index = val.to_i unless value.nil?
|
94
89
|
value = val if value.nil?
|
95
90
|
end
|
96
|
-
self.
|
91
|
+
self.fields_with(:name => k.to_s).[](index).value = value
|
97
92
|
end
|
98
93
|
end
|
99
94
|
end
|
@@ -209,7 +204,32 @@ module WWW
|
|
209
204
|
def delete_field!(field_name)
|
210
205
|
@fields.delete_if{ |f| f.name == field_name}
|
211
206
|
end
|
212
|
-
|
207
|
+
|
208
|
+
{ :field => :fields,
|
209
|
+
:button => :buttons,
|
210
|
+
:file_upload => :file_uploads,
|
211
|
+
:radiobutton => :radiobuttons,
|
212
|
+
:checkbox => :checkboxes,
|
213
|
+
}.each do |singular,plural|
|
214
|
+
eval(<<-eomethod)
|
215
|
+
def #{plural}_with criteria = {}
|
216
|
+
criteria = {:name => criteria} if String === criteria
|
217
|
+
f = #{plural}.find_all do |thing|
|
218
|
+
criteria.all? { |k,v| v === thing.send(k) }
|
219
|
+
end
|
220
|
+
yield f if block_given?
|
221
|
+
f
|
222
|
+
end
|
223
|
+
|
224
|
+
def #{singular}_with criteria = {}
|
225
|
+
f = #{plural}_with(criteria).first
|
226
|
+
yield f if block_given?
|
227
|
+
f
|
228
|
+
end
|
229
|
+
alias :#{singular} :#{singular}_with
|
230
|
+
eomethod
|
231
|
+
end
|
232
|
+
|
213
233
|
private
|
214
234
|
def parse
|
215
235
|
@fields = WWW::Mechanize::List.new
|
@@ -219,7 +239,7 @@ module WWW
|
|
219
239
|
@checkboxes = WWW::Mechanize::List.new
|
220
240
|
|
221
241
|
# Find all input tags
|
222
|
-
form_node.search('
|
242
|
+
form_node.search('input').each do |node|
|
223
243
|
type = (node['type'] || 'text').downcase
|
224
244
|
name = node['name']
|
225
245
|
next if name.nil? && !(type == 'submit' || type =='button')
|
@@ -242,13 +262,13 @@ module WWW
|
|
242
262
|
end
|
243
263
|
|
244
264
|
# Find all textarea tags
|
245
|
-
form_node.search('
|
265
|
+
form_node.search('textarea').each do |node|
|
246
266
|
next if node['name'].nil?
|
247
267
|
@fields << Field.new(node['name'], node.inner_text)
|
248
268
|
end
|
249
269
|
|
250
270
|
# Find all select tags
|
251
|
-
form_node.search('
|
271
|
+
form_node.search('select').each do |node|
|
252
272
|
next if node['name'].nil?
|
253
273
|
if node.has_attribute? 'multiple'
|
254
274
|
@fields << MultiSelectList.new(node['name'], node)
|
@@ -259,7 +279,7 @@ module WWW
|
|
259
279
|
|
260
280
|
# Find all submit button tags
|
261
281
|
# FIXME: what can I do with the reset buttons?
|
262
|
-
form_node.search('
|
282
|
+
form_node.search('button').each do |node|
|
263
283
|
type = (node['type'] || 'submit').downcase
|
264
284
|
next if type == 'reset'
|
265
285
|
@buttons << Button.new(node['name'], node['value'])
|