public
Fork of tenderlove/mechanize
Description: Mechanize is a ruby library that makes automated web interaction easy.
Homepage: http://mechanize.rubyforge.org/
Clone URL: git://github.com/github/mechanize.git
merging in 0.5.4

git-svn-id: svn+ssh://rubyforge.org/var/svn/mechanize/trunk@294 
f1cf478b-080f-0410-abad-959bfeec9ea8
aaronp (author)
Wed Aug 30 22:49:15 -0700 2006
commit  86a98b42a7b52824bab86815f6b0fa78d5520c6f
tree    c88af3e0417df86e285509a0846c8a090b3408d4
parent  76fd3fc74af587d07e06e56f09ba4c524b3d0126
...
10
11
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
14
15
...
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
0
@@ -10,6 +10,21 @@
0
   @agent.click (page/"frame").first
0
 * Removed deprecated attr_finder
0
 
0
+== 0.5.4
0
+
0
+* Added WWW::Mechanize#trasact for saving history state between in a
0
+ transaction. See the EXAMPLES file. Thanks Johan Kiviniemi.
0
+* Added support for gzip compressed pages
0
+* Forms can now be accessed like a hash. For example, to set the value
0
+ of an input field named 'name' to "Aaron", you can do this:
0
+ form['name'] = "Aaron"
0
+ Or to get the value of a field named 'name', do this:
0
+ puts form['name']
0
+* File uploads will now read the file specified in FileUpload#file_name
0
+* FileUpload can use an IO object in FileUpload#file_data
0
+* Fixed a bug with saving files on windows
0
+* Fixed a bug with the filename being set in forms
0
+
0
 == 0.5.3
0
 
0
 * Mechanize#click will now act on the first element of an array. So if an
...
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
55
56
...
84
85
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
29
30
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
...
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
0
@@ -29,28 +29,27 @@
0
 == File Upload
0
 This example uploads one image as two different images to flickr.
0
 
0
- require 'rubygems'
0
- require 'mechanize'
0
-
0
- agent = WWW::Mechanize.new
0
- page = agent.get('http://flickr.com/signin/flickr/')
0
- form = page.forms.first
0
- form.email = ARGV[0]
0
- form.password = ARGV[1]
0
- page = agent.submit(form)
0
- page = agent.click page.links.text('Upload')
0
- form = page.forms.first
0
- img1 = form.file_uploads.name('file1')
0
- img2 = form.file_uploads.name('file2')
0
-
0
- img1.file_name = img2.file_name = ARGV[2]
0
- File.open(ARGV[2], "r") { |f|
0
- img1.file_data = img2.file_data = f.read
0
- }
0
-
0
- img1.mime_type = img2.mime_type = 'image/jpeg'
0
-
0
- agent.submit(form)
0
+ require 'rubygems'
0
+ require 'mechanize'
0
+
0
+ agent = WWW::Mechanize.new
0
+
0
+ # Get the flickr sign in page
0
+ page = agent.get('http://flickr.com/signin/flickr/')
0
+
0
+ # Fill out the login form
0
+ form = page.forms.name('flickrloginform').first
0
+ form.email = ARGV[0]
0
+ form.password = ARGV[1]
0
+ page = agent.submit(form)
0
+
0
+ # Go to the upload page
0
+ page = agent.click page.links.text('Upload')
0
+
0
+ # Fill out the form
0
+ form = page.forms.action('/photos_upload_process.gne').first
0
+ form.file_uploads.name('file1').first.file_name = ARGV[2]
0
+ agent.submit(form)
0
   
0
 == Pluggable Parsers
0
 Lets say you want html pages to automatically be parsed with Rubyful Soup.
0
@@ -84,3 +83,42 @@ Beautiful Soup for that page.
0
   agent.set_proxy('localhost', '8000')
0
   page = agent.get(ARGV[0])
0
   puts page.body
0
+
0
+== The transact method
0
+
0
+transact runs the given block and then resets the page history. I.e. after the
0
+block has been executed, you're back at the original page; no need count how
0
+many times to call the back method at the end of a loop (while accounting for
0
+possible exceptions).
0
+
0
+This example also demonstrates subclassing Mechanize.
0
+
0
+ require 'mechanize'
0
+
0
+ class TestMech < WWW::Mechanize
0
+ def process
0
+ get 'http://rubyforge.org/'
0
+ search_form = page.forms.first
0
+ search_form.words = 'WWW'
0
+ submit search_form
0
+
0
+ page.links.with.href( %r{/projects/} ).each do |link|
0
+ next if link.href =~ %r{/projects/support/}
0
+
0
+ puts 'Loading %-30s %s' % [link.href, link.text]
0
+ begin
0
+ transact do
0
+ click link
0
+ # Do stuff, maybe click more links.
0
+ end
0
+ # Now we're back at the original page.
0
+
0
+ rescue => e
0
+ $stderr.puts "#{e.class}: #{e.message}"
0
+ end
0
+ end
0
+ end
0
+ end
0
+
0
+ TestMech.new.process
0
+
0
...
1
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
4
5
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
0
@@ -1,5 +1,25 @@
0
 = Mechanize Release Notes
0
 
0
+== 0.5.4 (Sylvester)
0
+
0
+WWW::Mechanize 0.5.4 aka Sylvester is fresh out the the frying pan and in to
0
+the fire! It is also ready for you to download and use.
0
+
0
+New features include WWW::Mechanize#transact (thanks to Johan Kiviniemi) which
0
+lets you maintain your history state between transactions. Forms can now be
0
+accessed as a hash. For example, to set the value of an input field, you can
0
+do the following:
0
+ form['name'] = "Aaron"
0
+Doing this assumes that you are setting the first field. If there are multiple
0
+fields with the same name, you must use a different method to set the value.
0
+
0
+Form file uploads will now read the file specified by FileUpload#file_name.
0
+The mime type will also be automatically determined for you! Take a look
0
+at the EXAMPLES file for a new flickr upload script.
0
+
0
+Lastly, gzip encoding is now supported! WWW::Mechanize now supports pages
0
+being sent gzip encoded. This means less network bandwidth. Yay!
0
+
0
 == 0.5.3 (Twan)
0
 
0
 Here it is. Mechanize 0.5.3 also named the "Twan" release. There are a few
...
11
12
13
14
 
15
16
17
...
29
30
31
 
32
33
34
...
88
89
90
 
 
 
 
 
 
 
 
 
 
 
...
11
12
13
 
14
15
16
17
...
29
30
31
32
33
34
35
...
89
90
91
92
93
94
95
96
97
98
99
100
101
102
0
@@ -11,7 +11,7 @@ end
0
 
0
 PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
0
 PKG_NAME = 'mechanize'
0
-PKG_VERSION = '0.5.3' + PKG_BUILD
0
+PKG_VERSION = '0.5.4' + PKG_BUILD
0
 PKG_FILES = FileList["{doc,lib,test}/**/*"].exclude("rdoc").to_a
0
 
0
 spec = Gem::Specification.new do |s|
0
@@ -29,6 +29,7 @@ spec = Gem::Specification.new do |s|
0
   s.rdoc_options << "--main" << 'README' << "--title" << "'WWW::Mechanize RDoc'"
0
   s.rubyforge_project = PKG_NAME
0
   s.add_dependency('ruby-web', '>= 1.1.0')
0
+ s.add_dependency('mime-types')
0
 end
0
 
0
 Rake::GemPackageTask.new(spec) do |p|
0
@@ -88,3 +89,14 @@ Rake::Task.define_task("branch") do |p|
0
   sh "svn cp -m 'branched #{ PKG_VERSION }' #{baseurl}/trunk #{ baseurl }/branches/RB-#{ PKG_VERSION }"
0
 end
0
 
0
+desc "Update SSL Certificate"
0
+Rake::Task.define_task('ssl_cert') do |p|
0
+ sh "openssl genrsa -des3 -out server.key 1024"
0
+ sh "openssl req -new -key server.key -out server.csr"
0
+ sh "cp server.key server.key.org"
0
+ sh "openssl rsa -in server.key.org -out server.key"
0
+ sh "openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt"
0
+ sh "cp server.key server.pem"
0
+ sh "mv server.key server.csr server.crt server.pem test/data/"
0
+ sh "rm server.key.org"
0
+end
...
15
16
17
18
19
 
 
20
21
22
...
134
135
136
137
 
 
 
138
139
140
...
218
219
220
 
 
 
 
 
 
 
 
 
 
 
221
222
223
...
244
245
246
247
 
248
249
250
251
252
253
254
 
 
 
 
 
 
255
256
257
 
258
259
260
261
 
 
 
 
 
 
 
 
 
 
262
263
 
264
265
266
 
267
268
269
270
 
271
272
273
...
276
277
278
279
280
 
 
281
282
283
 
 
284
285
286
287
 
 
288
289
290
 
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
 
 
 
 
 
 
 
314
 
 
315
316
317
318
319
320
321
322
 
 
 
 
323
324
 
 
325
326
 
327
 
 
328
329
330
 
331
 
332
333
334
335
336
337
338
339
340
341
342
343
...
355
356
357
 
 
 
 
 
 
 
 
 
 
 
 
358
359
360
361
362
363
 
364
365
366
...
375
376
377
378
 
 
 
 
 
379
380
381
382
 
383
384
385
...
15
16
17
 
18
19
20
21
22
23
...
135
136
137
 
138
139
140
141
142
143
...
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
...
258
259
260
 
261
262
 
 
 
 
 
 
263
264
265
266
267
268
269
270
 
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
 
287
288
289
 
290
291
292
293
 
294
295
296
297
...
300
301
302
 
 
303
304
305
 
 
306
307
308
309
 
 
310
311
312
313
314
315
316
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
318
319
320
321
322
323
324
325
326
327
 
 
 
 
 
 
 
328
329
330
331
332
 
333
334
335
 
336
337
338
339
340
341
342
343
344
345
346
 
347
348
349
350
 
351
 
352
353
354
...
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
 
386
387
388
389
...
398
399
400
 
401
402
403
404
405
406
407
408
 
409
410
411
412
0
@@ -15,8 +15,9 @@ require 'net/http'
0
 require 'net/https'
0
 
0
 require 'uri'
0
-require 'logger'
0
 require 'webrick'
0
+require 'zlib'
0
+require 'stringio'
0
 require 'web/htmltools/xmltree' # narf
0
 require 'mechanize/module'
0
 require 'mechanize/mech_version'
0
@@ -134,7 +135,9 @@ class Mechanize
0
     cur_page = current_page || Page.new( nil, {'content-type'=>'text/html'})
0
 
0
     # fetch the page
0
- page = fetch_page(to_absolute_uri(url, cur_page), :get, cur_page)
0
+ abs_uri = to_absolute_uri(url, cur_page)
0
+ request = fetch_request(abs_uri)
0
+ page = fetch_page(abs_uri, request, cur_page)
0
     add_to_history(page)
0
     page
0
   end
0
@@ -218,6 +221,17 @@ class Mechanize
0
     ! @history.find { |h| h.uri.to_s == uri.to_s }.nil?
0
   end
0
 
0
+ # Runs given block, then resets the page history as it was before. self is
0
+ # given as a parameter to the block. Returns the value of the block.
0
+ def transact
0
+ history_backup = @history.dup
0
+ begin
0
+ yield self
0
+ ensure
0
+ @history = history_backup
0
+ end
0
+ end
0
+
0
   alias :page :current_page
0
 
0
   private
0
@@ -244,30 +258,40 @@ class Mechanize
0
   def post_form(url, form)
0
     cur_page = current_page || Page.new(nil, {'content-type'=>'text/html'})
0
 
0
- request_data = [form.request_data]
0
+ request_data = form.request_data
0
 
0
- # this is called before the request is sent
0
- pre_request_hook = proc {|request|
0
- log.debug("query: #{ request_data.inspect }")
0
- request.add_field('Content-Type', form.enctype)
0
- request.add_field('Content-Length', request_data[0].size.to_s)
0
- }
0
+ abs_url = to_absolute_uri(url, cur_page)
0
+ request = fetch_request(abs_url, :post)
0
+ request.add_field('Content-Type', form.enctype)
0
+ request.add_field('Content-Length', request_data.size.to_s)
0
+
0
+ log.debug("query: #{ request_data.inspect }") if log
0
 
0
     # fetch the page
0
- page = fetch_page(to_absolute_uri(url, cur_page), :post, cur_page, pre_request_hook, request_data)
0
+ page = fetch_page(abs_url, request, cur_page, [request_data])
0
     add_to_history(page)
0
     page
0
   end
0
 
0
+ # Creates a new request object based on the scheme and type
0
+ def fetch_request(uri, type = :get)
0
+ raise "unsupported scheme" unless ['http', 'https'].include?(uri.scheme)
0
+ if type == :get
0
+ Net::HTTP::Get.new(uri.request_uri)
0
+ else
0
+ Net::HTTP::Post.new(uri.request_uri)
0
+ end
0
+ end
0
+
0
   # uri is an absolute URI
0
- def fetch_page(uri, method=:get, cur_page=current_page(), pre_request_hook=nil, request_data=[])
0
+ def fetch_page(uri, request, cur_page=current_page(), request_data=[])
0
     raise "unsupported scheme" unless ['http', 'https'].include?(uri.scheme)
0
 
0
- log.info("#{ method.to_s.upcase }: #{ uri.to_s }")
0
+ log.info("#{ request.class }: #{ uri.to_s }") if log
0
 
0
     page = nil
0
 
0
- http = Net::HTTP.new( uri.host,
0
+ http_obj = Net::HTTP.new( uri.host,
0
                           uri.port,
0
                           @proxy_addr,
0
                           @proxy_port,
0
@@ -276,68 +300,55 @@ class Mechanize
0
                         )
0
 
0
     if uri.scheme == 'https'
0
- http.use_ssl = true
0
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
0
+ http_obj.use_ssl = true
0
+ http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE
0
       if @ca_file
0
- http.ca_file = @ca_file
0
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
0
+ http_obj.ca_file = @ca_file
0
+ http_obj.verify_mode = OpenSSL::SSL::VERIFY_PEER
0
       end
0
       if @cert && @key
0
- http.cert = OpenSSL::X509::Certificate.new(::File.read(@cert))
0
- http.key = OpenSSL::PKey::RSA.new(::File.read(@key), @pass)
0
+ http_obj.cert = OpenSSL::X509::Certificate.new(::File.read(@cert))
0
+ http_obj.key = OpenSSL::PKey::RSA.new(::File.read(@key), @pass)
0
       end
0
     end
0
 
0
+ request.add_field('Accept-Encoding', 'gzip,identity')
0
 
0
- http.start {
0
-
0
- case method
0
- when :get
0
- request = Net::HTTP::Get.new(uri.request_uri)
0
- when :post
0
- request = Net::HTTP::Post.new(uri.request_uri)
0
- else
0
- raise ArgumentError
0
- end
0
-
0
- unless @cookie_jar.empty?(uri)
0
- cookies = @cookie_jar.cookies(uri)
0
- cookie = cookies.length > 0 ? cookies.join("; ") : nil
0
- log.debug("use cookie: #{ cookie }")
0
- request.add_field('Cookie', cookie)
0
- end
0
-
0
- # Add Referer header to request
0
-
0
- unless cur_page.uri.nil?
0
- request.add_field('Referer', cur_page.uri.to_s)
0
+ unless @cookie_jar.empty?(uri)
0
+ cookies = @cookie_jar.cookies(uri)
0
+ cookie = cookies.length > 0 ? cookies.join("; ") : nil
0
+ if log
0
+ cookies.each do |c|
0
+ log.debug("using cookie: #{c}")
0
+ end
0
       end
0
+ request.add_field('Cookie', cookie)
0
+ end
0
 
0
- # Add User-Agent header to request
0
-
0
- request.add_field('User-Agent', @user_agent) if @user_agent
0
-
0
- request.basic_auth(@user, @password) if @user
0
-
0
- # Invoke pre-request-hook (use it to add custom headers or content)
0
+ # Add Referer header to request
0
+ unless cur_page.uri.nil?
0
+ request.add_field('Referer', cur_page.uri.to_s)
0
+ end
0
 
0
- pre_request_hook.call(request) if pre_request_hook
0
+ # Add User-Agent header to request
0
+ request.add_field('User-Agent', @user_agent) if @user_agent
0
 
0
- # Log specified headers for the request
0
+ request.basic_auth(@user, @password) if @user
0
 
0
+ # Log specified headers for the request
0
+ if log
0
       request.each_header do |k, v|
0
         log.debug("request-header: #{ k } => #{ v }")
0
       end
0
+ end
0
 
0
+ http_obj.start { |http|
0
       # Specify timeouts if given
0
-
0
       http.open_timeout = @open_timeout if @open_timeout
0
       http.read_timeout = @read_timeout if @read_timeout
0
 
0
       # Send the request
0
-
0
       http.request(request, *request_data) {|response|
0
-
0
         (response.get_fields('Set-Cookie')||[]).each do |cookie|
0
           log.debug("cookie received: #{ cookie }")
0
           Cookie::parse(uri, cookie) { |c| @cookie_jar.add(c) }
0
@@ -355,12 +366,24 @@ class Mechanize
0
           content_type = data[1].downcase unless data.nil?
0
         end
0
 
0
+ response_body =
0
+ if encoding = response['Content-Encoding']
0
+ case encoding.downcase
0
+ when 'gzip'
0
+ log.debug('gunzip body') if log
0
+ Zlib::GzipReader.new(StringIO.new(response.body)).read
0
+ else
0
+ raise 'Unsupported content encoding'
0
+ end
0
+ else
0
+ response.body
0
+ end
0
 
0
         # Find our pluggable parser
0
         page = @pluggable_parser.parser(content_type).new(
0
           uri,
0
           response,
0
- response.body,
0
+ response_body,
0
           response.code
0
         )
0
 
0
@@ -375,11 +398,15 @@ class Mechanize
0
           return page
0
         when "301", "302"
0
           log.info("follow redirect to: #{ response['Location'] }")
0
- return fetch_page(to_absolute_uri(URI.parse(response['Location'].gsub(/ /, '%20')), page), :get, page)
0
+ abs_uri = to_absolute_uri(
0
+ URI.parse(
0
+ URI.escape(URI.unescape(response['Location'].to_s))), page)
0
+ request = fetch_request(abs_uri)
0
+ return fetch_page(abs_uri, request, page)
0
         else
0
           raise ResponseCodeError.new(page.code), "Unhandled response", caller
0
         end
0
- }
0
+ }
0
     }
0
   end
0
 
...
 
 
1
2
3
...
91
92
93
94
95
96
97
...
99
100
101
102
 
103
104
105
 
106
107
108
109
110
111
...
130
131
132
133
 
134
135
136
...
179
180
181
 
 
 
 
 
 
182
183
184
185
186
187
 
 
 
 
 
 
 
188
189
190
...
198
199
200
 
 
 
201
202
203
...
231
232
233
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
235
236
...
1
2
3
4
5
...
93
94
95
 
96
97
98
...
100
101
102
 
103
104
105
 
106
107
 
 
108
109
110
...
129
130
131
 
132
133
134
135
...
178
179
180
181
182
183
184
185
186
187
188
189
190
 
 
191
192
193
194
195
196
197
198
199
200
...
208
209
210
211
212
213
214
215
216
...
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
0
@@ -1,3 +1,5 @@
0
+require 'mime/types'
0
+
0
 module WWW
0
   class Mechanize
0
     # =Synopsis
0
@@ -91,7 +93,6 @@ module WWW
0
       # multi-part post,
0
       def request_data
0
         query_params = build_query()
0
- query = nil
0
         case @enctype.downcase
0
         when 'multipart/form-data'
0
           boundary = rand_string(20)
0
@@ -99,13 +100,11 @@ module WWW
0
           params = []
0
           query_params.each { |k,v| params << param_to_multipart(k, v) }
0
           @file_uploads.each { |f| params << file_to_multipart(f) }
0
- query = params.collect { |p| "--#{boundary}\r\n#{p}" }.join('') +
0
+ params.collect { |p| "--#{boundary}\r\n#{p}" }.join('') +
0
             "--#{boundary}--\r\n"
0
         else
0
- query = WWW::Mechanize.build_query_string(query_params)
0
+ WWW::Mechanize.build_query_string(query_params)
0
         end
0
-
0
- query
0
       end
0
     
0
       private
0
@@ -130,7 +129,7 @@ module WWW
0
           when 'checkbox'
0
             @checkboxes << CheckBox.new(node.attributes['name'], node.attributes['value'], node.attributes.has_key?('checked'), self)
0
           when 'file'
0
- @file_uploads << FileUpload.new(node.attributes['name'], node.attributes['value'])
0
+ @file_uploads << FileUpload.new(node.attributes['name'], nil)
0
           when 'submit'
0
             @buttons << Button.new(node.attributes['name'], node.attributes['value'])
0
           when 'image'
0
@@ -179,12 +178,23 @@ module WWW
0
                 "#{mime_value_quote(file.name)}\"; " +
0
                 "filename=\"#{mime_value_quote(file.file_name || '')}\"\r\n" +
0
                 "Content-Transfer-Encoding: binary\r\n"
0
+
0
+ if file.file_data.nil? and ! file.file_name.nil?
0
+ file.file_data = ::File.open(file.file_name, "rb") { |f| f.read }
0
+ file.mime_type = MIME::Types.type_for(file.file_name).first
0
+ end
0
+
0
         if file.mime_type != nil
0
           body << "Content-Type: #{file.mime_type}\r\n"
0
         end
0
     
0
- body << "\r\n#{file.file_data}\r\n"
0
-
0
+ body <<
0
+ if file.file_data.respond_to? :read
0
+ "\r\n#{file.file_data.read}\r\n"
0
+ else
0
+ "\r\n#{file.file_data}\r\n"
0
+ end
0
+
0
         body
0
       end
0
     end
0
@@ -198,6 +208,9 @@ module WWW
0
     # Find a form and print out its fields
0
     # form = page.forms.first # => WWW::Mechanize::Form
0
     # form.fields.each { |f| puts f.name }
0
+ # Set the input field 'name' to "Aaron"
0
+ # form['name'] = 'Aaron'
0
+ # puts form['name']
0
     class Form < GlobalForm
0
       attr_reader :node
0
     
0
@@ -231,6 +244,22 @@ module WWW
0
         end
0
       end
0
 
0
+ # Fetch the value of the first input field with the name passed in
0
+ # ==Example
0
+ # Fetch the value set in the input field 'name'
0
+ # puts form['name']
0
+ def [](field_name)
0
+ field(field_name).value
0
+ end
0
+
0
+ # Set the value of the first input field with the name passed in
0
+ # ==Example
0
+ # Set the value in the input field 'name' to "Aaron"
0
+ # form['name'] = 'Aaron'
0
+ def []=(field_name, value)
0
+ field(field_name).value = value
0
+ end
0
+
0
       # Treat form fields like accessors.
0
       def method_missing(id,*args)
0
         method = id.to_s.gsub(/=$/, '')
...
2
3
4
5
 
6
7
...
2
3
4
 
5
6
7
0
@@ -2,6 +2,6 @@
0
 # This file is auto-generated by build scripts
0
 module WWW
0
   class Mechanize
0
- Version = '0.5.3'
0
+ Version = '0.5.4'
0
   end
0
 end
...
51
52
53
 
 
 
 
 
 
 
 
 
 
54
55
56
...
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
0
@@ -51,6 +51,16 @@ module WWW
0
         parse_html if @body
0
       end
0
 
0
+ # Fetch the title of the page
0
+ def title
0
+ parse_html() unless @title
0
+ @title
0
+ end
0
+
0
+ def form(name)
0
+ forms.name(name).first
0
+ end
0
+
0
       private
0
     
0
       def parse_html
...
27
28
29
30
 
31
32
33
...
47
48
49
 
 
50
51
52
53
54
55
 
 
 
 
 
 
 
 
 
 
 
 
 
56
57
 
58
59
60
...
27
28
29
 
30
31
32
33
...
47
48
49
50
51
52
53
 
 
 
 
54
55
56
57
58
59
60
61
62
63
64
65
66
67
 
68
69
70
71
0
@@ -27,7 +27,7 @@ module WWW
0
 
0
       # Use this method to save the content of this object to filename
0
       def save_as(filename)
0
- ::File::open(filename, "w") { |f|
0
+ ::File::open(filename, "wb") { |f|
0
           f.write body
0
         }
0
       end
0
@@ -47,14 +47,25 @@ module WWW
0
     # agent.get('http://example.com/foo.pdf')
0
     #
0
     class FileSaver < File
0
+ attr_reader :filename
0
+
0
       def initialize(uri=nil, response=nil, body=nil, code=nil)
0
         @uri, @response, @body, @code = uri, response, body, code
0
- path = uri.path == '/' ? '/index.html' : uri.path
0
- path =~ /^(.*)\/([^\/]*)$/
0
- filename = $2
0
- path = "#{uri.host}#{$1}"
0
+ path = uri.path.empty? ? 'index.html' : uri.path.gsub(/^[\/]*/, '')
0
+ path += 'index.html' if path =~ /\/$/
0
+
0
+ split_path = path.split(/\//)
0
+ filename = split_path.length > 0 ? split_path.pop : 'index.html'
0
+ joined_path = split_path.join(::File::SEPARATOR)
0
+ path = if joined_path.empty?
0
+ uri.host
0
+ else
0
+ "#{uri.host}#{::File::SEPARATOR}#{joined_path}"
0
+ end
0
+
0
+ @filename = "#{path}#{::File::SEPARATOR}#{filename}"
0
         FileUtils.mkdir_p(path)
0
- save_as("#{path}/#{filename}")
0
+ save_as(@filename)
0
       end
0
     end
0
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
...
1
 
 
 
 
 
 
 
 
 
 
 
 
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0
@@ -1,14 +1,16 @@
0
 -----BEGIN CERTIFICATE-----
0
-MIICLzCCAZgCCQDS5ue63ULFQDANBgkqhkiG9w0BAQUFADBcMQswCQYDVQQGEwJV
0
-UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2VhdHRsZTESMBAGA1UE
0
-ChMJTWVjaGFuaXplMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMDYwNjI5MjEzMjIy
0
-WhcNMDYwNzI5MjEzMjIyWjBcMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
0
-Z3RvbjEQMA4GA1UEBxMHU2VhdHRsZTESMBAGA1UEChMJTWVjaGFuaXplMRIwEAYD
0
-VQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAL3qHTIQ
0
-32nJtgwvL86UYhO3W8WPAPWEmY1FgsAxboWDdoHr/klGCKuPXR5tUovymD26/G3Z
0
-yAN+ev7IJUIA6E++jCIQ9v7l22NOJyN/7bS9gNXFfKeNWJXSd0D6DWypgPURHVhi
0
-A4viRFYiv/Q1XDU/UxNTqu2/OoQo/KjWEGQvAgMBAAEwDQYJKoZIhvcNAQEFBQAD
0
-gYEAZhacqH+7sknkjnptrBkYjbmmlj8STYXwKs9+xWsUW1NSW01jT61e7qlqOTR3
0
-26tUms1aq4OTBovGSBboNKI2NqWSHD0stdudjPMyNj0eZBJVLlaYiS7/1AqV6fM/
0
-OGmX/Alaaa3fTytbuocHtQfm9ue18dTzabfIw2Wp6Hscm/Q=
0
+MIICmzCCAgQCCQDq2kM3TCIM0DANBgkqhkiG9w0BAQQFADCBkTELMAkGA1UEBhMC
0
+VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1NlYXR0bGUxEjAQBgNV
0
+BAoTCU1lY2hhbml6ZTESMBAGA1UECxMJTWVjaGFuaXplMQ4wDAYDVQQDEwVBYXJv
0
+bjEjMCEGCSqGSIb3DQEJARYUYWFyb25wQHJ1Ynlmb3JnZS5vcmcwHhcNMDYwODIz
0
+MDU0NTMwWhcNMDcwODIzMDU0NTMwWjCBkTELMAkGA1UEBhMCVVMxEzARBgNVBAgT
0
+Cldhc2hpbmd0b24xEDAOBgNVBAcTB1NlYXR0bGUxEjAQBgNVBAoTCU1lY2hhbml6
0
+ZTESMBAGA1UECxMJTWVjaGFuaXplMQ4wDAYDVQQDEwVBYXJvbjEjMCEGCSqGSIb3
0
+DQEJARYUYWFyb25wQHJ1Ynlmb3JnZS5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0A
0
+MIGJAoGBAKpnmI4Y4tBz3SJsHR28QoUr7IYxkqbD8qjqgIN0LXOslyFyiGzNKca5
0
+Ln2Au8CZLyCugt/vutjfV+Eq0bl0HwbKdo6HjP6RxmGX6EKWX7NOrsof+s96DhLm
0
+JaWWbtvqP8eyy9PNInKjX6n/7wsVetQutjhNy/cAkrh6UOsjyCGvAgMBAAEwDQYJ
0
+KoZIhvcNAQEEBQADgYEAGtqgxn1fh0X5MxDG1yMp5aGcZ6HhtEtlm5S0ZsRnMsqU
0
+Hh6Bd57+zUQ66XnLCbQN2cwNeeSoqtI16Ccc1I5cAhQnIZESMsPG21i1BnpEhKph
0
+HfNFNpWI/upT2EXNUM6Vx2Kk2aCw2ysrD2pHpsTo5bCOly00uK1ZkoJVQMTL4gU=
0
 -----END CERTIFICATE-----
...
1
2
3
4
5
6
7
8
9
10
 
 
 
 
 
 
 
 
 
 
11
...
1
 
 
 
 
 
 
 
 
 
2
3
4
5
6
7
8
9
10
11
12
0
@@ -1,11 +1,12 @@
0
 -----BEGIN CERTIFICATE REQUEST-----
0
-MIIBnDCCAQUCAQAwXDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
0
-EDAOBgNVBAcTB1NlYXR0bGUxEjAQBgNVBAoTCU1lY2hhbml6ZTESMBAGA1UEAxMJ
0
-bG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC96h0yEN9pybYM
0
-Ly/OlGITt1vFjwD1hJmNRYLAMW6Fg3aB6/5JRgirj10ebVKL8pg9uvxt2cgDfnr+
0
-yCVCAOhPvowiEPb+5dtjTicjf+20vYDVxXynjViV0ndA+g1sqYD1ER1YYgOL4kRW
0
-Ir/0NVw1P1MTU6rtvzqEKPyo1hBkLwIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEA
0
-BdaUO9CUvFe6RIXPxJfeHnU39SDrzXAgQ4zoi9EwbJO1rs/cid3qcF6sjGgtSLgF
0
-qJqpJplLa0wezecjHtDKAIwchNYrv+MrchWCKWlVtxYdCX6kjn796Tpjl0w7CUfm
0
-mYhE04+mqjhS3SMMCiIyxnM/zGDiMmxsxyhUF+WUppo=