downspout 0.2.7 → 0.2.8

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.7
1
+ 0.2.8
@@ -15,17 +15,16 @@ module Downspout
15
15
  end
16
16
 
17
17
  =begin rdoc
18
- Download a file from a given URL to a given Path on the local system
19
- The path is optional and will default to a generated temporary file
18
+ Download a file from a given URL. The path is optional and will default to a generated temporary file.
20
19
  =end
21
- def self.download_url_to_path( some_url, some_path = nil )
22
- $logger.debug("downspout | download_url_to_path | URL : #{some_url}")
23
- $logger.debug("downspout | download_url_to_path | Download Path : #{some_path}")
20
+ def self.fetch_url( some_url, some_path = nil )
21
+ $logger.debug("downspout | fetch_url | URL : #{some_url}")
22
+ $logger.debug("downspout | fetch_url | Download Path : #{some_path}")
24
23
 
25
24
  begin
26
25
  d = Downspout::Downloader.new( :url => some_url, :path => some_path )
27
26
  rescue Exception => e
28
- $logger.error("downspout | download_url_to_path | Exception : '#{e}'")
27
+ $logger.error("downspout | fetch_url | Exception : '#{e}'")
29
28
  return nil if e.class == Downspout::UnsupportedScheme
30
29
  raise e
31
30
  end
@@ -33,19 +32,16 @@ module Downspout
33
32
  fetched = d.download!
34
33
 
35
34
  if !(fetched) then
36
- $logger.error("downspout | download_url_to_path | Fetch Failed : #{d.url} ")
35
+ $logger.error("downspout | fetch_url | Fetch Failed : #{d.url} ")
37
36
  return nil
38
37
  end
39
38
 
40
- $logger.debug("downspout | download_url_to_path | Local File : #{d.path} ")
39
+ $logger.debug("downspout | fetch_url | Local File : #{d.path} ")
41
40
  return d
42
41
  end
43
42
 
44
- =begin rdoc
45
- Convenience method for downloading a file from an URL without specifying a path for storage.
46
- =end
47
- def self.fetch_url( the_url )
48
- return self.download_url_to_path( the_url )
43
+ def self.download_url_to_path( the_url, the_path ) #:nodoc:
44
+ return self.fetch_url( the_url, the_path )
49
45
  end
50
46
 
51
47
  =begin rdoc
@@ -8,6 +8,8 @@ module Downspout
8
8
  @@curb_allowed = true
9
9
  @@curb_enabled = true
10
10
  @@prefix = 'downspout'
11
+ @@max_redirects = 2
12
+ @@ssl_verification = true
11
13
 
12
14
  def self.tmp_dir
13
15
  return @@tmp_dir
@@ -29,6 +31,14 @@ module Downspout
29
31
  return @@credentials
30
32
  end
31
33
 
34
+ def self.max_redirects
35
+ @@max_redirects
36
+ end
37
+
38
+ def self.max_redirects=( num )
39
+ @@max_redirects = num
40
+ end
41
+
32
42
  def self.network_enabled?
33
43
  return @@network_enabled
34
44
  end
@@ -42,6 +52,10 @@ module Downspout
42
52
  @@network_enabled = true
43
53
  end
44
54
 
55
+ def self.ssl_verification?
56
+ @@ssl_verification
57
+ end
58
+
45
59
  def self.curb_available?
46
60
  begin
47
61
  require 'curb'
@@ -23,6 +23,7 @@ module Downspout
23
23
  @response_headers = {}
24
24
  @started_at = nil
25
25
  @finished_at = nil
26
+ @redirected_url = nil
26
27
 
27
28
  if options.respond_to?(:keys) then
28
29
  options.each do |key, value|
@@ -58,7 +59,7 @@ module Downspout
58
59
  if !(@path.nil?) then
59
60
  @basename = File.basename( @path )
60
61
  else
61
- if !(@uri.path.nil? || @uri.path.empty? || uri.path == '/')
62
+ if !(@uri.path.nil? || @uri.path.empty? || @uri.path == '/')
62
63
  @basename = File.basename( @uri.path )
63
64
  else
64
65
  $logger.debug("downspout | downloader | basename | Bad URI path")
@@ -197,7 +198,7 @@ module Downspout
197
198
  $logger.debug("downspout | downloader | net_http_download | Downloading #{@url} ...")
198
199
 
199
200
  begin
200
- response = net_http_fetch( @url , 1)
201
+ response = net_http_fetch( @url )
201
202
  open( @path, "wb" ) do |file|
202
203
  file.write(response.body)
203
204
  end
@@ -230,27 +231,38 @@ module Downspout
230
231
  return true
231
232
  end
232
233
 
233
- def net_http_fetch( url_str, limit = 10 )
234
- $logger.debug("downspout | downloader | net_http_fetch | URL: #{url_str}, Redirects: #{limit}.")
234
+ def net_http_fetch( url_str, redirects = 0 )
235
+ $logger.debug("downspout | downloader | net_http_fetch | URL: #{url_str}, Redirects: #{redirects}.")
236
+
235
237
  raise Downspout::BadURL, 'URL is missing' if url_str.nil?
236
- raise Downspout::ExcessiveRedirects, 'HTTP redirect too deep' if limit == 0
238
+
239
+ if redirects > Downspout::Config.max_redirects then
240
+ raise Downspout::ExcessiveRedirects, 'HTTP redirect too deep'
241
+ end
237
242
 
238
243
  u = URI.parse( url_str )
244
+
245
+ http = Net::HTTP.new( u.host, u.port )
246
+
247
+ if (u.scheme == "https") then
248
+ http.use_ssl = true
249
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if !(Downspout::Config.ssl_verification?)
250
+ end
239
251
 
240
- my_request = Net::HTTP::Get.new( "#{u.path}?#{u.query}" )
252
+ my_request = Net::HTTP::Get.new( u.request_uri )
241
253
 
242
254
  # TODO : implement credentials for downloads via net_http_fetch
243
255
  my_request.basic_auth 'account', 'p4ssw0rd'
244
256
 
245
- @response = Net::HTTP.start( u.host, u.port ) do |http|
246
- http.request( my_request )
247
- end
257
+ @response = http.request( my_request )
248
258
 
249
259
  case @response
250
260
  when Net::HTTPSuccess
251
261
  @response
252
262
  when Net::HTTPRedirection
253
- net_http_fetch( @response['location'], limit - 1 )
263
+ @redirected_url = @response['location']
264
+ # TODO : use the new location to update the file name / extension when unknown
265
+ net_http_fetch( @redirected_url, redirects + 1 )
254
266
  else
255
267
  $logger.error("downspout | downloader | net_http_fetch | Response : #{@response}")
256
268
  @response.error!
@@ -261,10 +273,10 @@ module Downspout
261
273
  $logger.debug("downspout | downloader | curb_http_download | Downloading #{@url} ...")
262
274
 
263
275
  begin
264
- curb = Curl::Easy.download( @url, @path) {|c| c.follow_location=true; c.max_redirects=1;}
265
- rescue Curl::Err::HostResolutionError
276
+ curb = Curl::Easy.download( @url, @path) {|c| c.follow_location=true; c.max_redirects = Downspout::Config.max_redirects;}
277
+ rescue Curl::Err::HostResolutionError => dns_err
266
278
  $logger.error("downspout | downloader | curb_http_download | Curb/Curl DNS Error | #{@uri.host}")
267
- return false
279
+ raise dns_err
268
280
  end
269
281
 
270
282
  $logger.debug("downspout | downloader | curb_http_download | Response Code : #{curb.response_code}")
@@ -277,6 +289,12 @@ module Downspout
277
289
  # populate the response headers from curb header string
278
290
  parse_headers_from_string!( curb.header_str )
279
291
 
292
+ ultimate_url = curb_last_location( curb.header_str )
293
+ if !( ultimate_url == @url ) then
294
+ # re-directed
295
+ @redirected_url = ultimate_url
296
+ end
297
+
280
298
  # populate a 'proxy' HTTPResponse object with the Curb data...
281
299
  hr_klass = Net::HTTPResponse.send('response_class', curb.response_code.to_s)
282
300
  $logger.debug("downspout | downloader | curb_http_download | Response Type : #{hr_klass.name}")
@@ -293,22 +311,42 @@ module Downspout
293
311
  return true
294
312
  end
295
313
 
314
+ def curb_last_location( header_string )
315
+ matches = header_string.scan(/Location\:\s?(.*)\W/)
316
+
317
+ return nil if matches.nil?
318
+ return nil if (matches.class == Array) && (matches.last.nil?)
319
+
320
+ result = matches.last.first.strip
321
+ $logger.error("downspout | downloader | curb_last_location | #{result}")
322
+ return result
323
+ end
324
+
296
325
  def parse_headers_from_string!( header_str )
326
+ # $logger.debug("downspout | downloader | parse_headers_from_string! | Header String : #{header_str}")
297
327
  header_hash = {}
298
- http_hash = {}
328
+
299
329
  headers = header_str.split("\r\n")
300
-
301
330
  http_info = headers[0]
331
+
332
+ http_hash = {}
302
333
  http_hash[:header] = http_info
303
334
  http_hash[:version] = http_info.split(" ")[0].match("HTTP/([0-9\.]+)")[1]
304
335
  http_hash[:code] = (http_info.split("\r\n")[0].split(" ")[1]).to_i
305
336
  http_hash[:message] = http_info.split("\r\n")[0].split(" ")[2]
306
337
 
307
338
  header_hash["HTTP"] = http_hash
308
-
309
- headers[1..-1].each do |line|
310
- header_name, header_value = line.match(/([\w\-\s]+)\:\s?(.*)/)[1..2]
311
- header_hash[header_name] = header_value
339
+
340
+ headers[1..-1].each do |line|
341
+ next if line.nil? || line.empty?
342
+ begin
343
+ matches = line.match(/([\w\-\s]+)\:\s?(.*)/)
344
+ next if matches.nil? || matches.size < 3
345
+ header_name, header_value = matches[1..2]
346
+ header_hash[header_name] = header_value
347
+ rescue Exception => e
348
+ $logger.warn("downspout | downloader | parse_headers_from_string! | #{line}, Exception : #{e}")
349
+ end
312
350
  end
313
351
 
314
352
  @response_headers = header_hash
@@ -318,20 +356,22 @@ module Downspout
318
356
  result = nil
319
357
 
320
358
  result = file_name_from_content_disposition
359
+ result = file_name_from_redirect if result.nil?
321
360
  result = file_name_from_content_type if result.nil?
322
361
 
323
362
  return result
324
363
  end
325
364
 
326
-
327
365
  # Extracts filename from Content-Disposition Header per RFC 2183
328
366
  # "http://tools.ietf.org/html/rfc2183"
329
367
  def file_name_from_content_disposition
330
-
331
368
  file_name = nil
332
369
 
333
- cd_key = response_headers.keys.select{|k| k =~ /content-disposition/i }.first
370
+ cd_key = response_headers.keys.select{|k| k =~ /content-disposition/i }.first # TODO: better to use the last?
334
371
 
372
+ $logger.debug("downspout | downloader | file_name_from_content_disposition | cd key : #{cd_key}")
373
+ return nil if cd_key.nil?
374
+
335
375
  if cd_key then
336
376
  disposition = @response_headers[cd_key]
337
377
  if disposition then
@@ -357,6 +397,19 @@ module Downspout
357
397
  $logger.debug("downspout | downloader | file_name_from_content_type | #{file_name}")
358
398
  return file_name
359
399
  end
400
+
401
+ def file_name_from_redirect
402
+ return nil if @redirected_url.nil?
403
+
404
+ my_uri = URI::parse( @redirected_url )
405
+
406
+ if !(my_uri.path.nil? || my_uri.path.empty? || my_uri.path == '/')
407
+ return File.basename( my_uri.path )
408
+ else
409
+ $logger.debug("downspout | downloader | basename | Bad URI path")
410
+ return nil
411
+ end
412
+ end
360
413
 
361
414
  end
362
415
 
@@ -1,10 +1,10 @@
1
1
  if !(defined?( $logger )) then
2
- if defined?( RAILS_DEFAULT_LOGGER ) then
3
- $logger = RAILS_DEFAULT_LOGGER
2
+ if ( defined?( Rails ) && !(Rails.logger.nil?) ) then
3
+ $logger = Rails.logger
4
4
  else
5
5
  require 'logger'
6
6
  $logger = Logger.new( STDERR )
7
- $logger.level = Logger::INFO
7
+ $logger.level = Logger::WARN
8
8
  end
9
9
  end
10
10
 
data/lib/downspout.rb CHANGED
@@ -4,6 +4,7 @@ require 'rubygems'
4
4
  require 'fileutils'
5
5
  require 'uri'
6
6
  require 'net/http'
7
+ require 'net/https'
7
8
  require 'net/ftp'
8
9
 
9
10
  # add this directory to the path...
@@ -17,26 +17,40 @@ class DownspoutTest < Test::Unit::TestCase
17
17
 
18
18
  $test_ws.mount(TestServlet.path, TestServlet)
19
19
 
20
+ n_deep_proc = Proc.new { |req, resp|
21
+ resp.body = '3-deep redirector proc mounted on #{req.script_name}'
22
+
23
+ num = req.query['n'].to_i
24
+
25
+ if num > 1 then
26
+ resp.set_redirect( HTTPStatus::MovedPermanently, "/deep/?n=#{num - 1}")
27
+ else
28
+ resp.set_redirect( HTTPStatus::MovedPermanently, '/READ_ME.rdoc')
29
+ end
30
+ }
31
+
32
+ $test_ws.mount('/deep', HTTPServlet::ProcHandler.new(n_deep_proc) )
33
+
20
34
  two_deep_proc = Proc.new { |req, resp|
21
35
  resp.body = '2-deep redirector proc mounted on #{req.script_name}'
22
- resp.set_redirect( HTTPStatus::MovedPermanently, '/one/deep')
36
+ resp.set_redirect( HTTPStatus::MovedPermanently, '/deep/one')
23
37
  }
24
38
 
25
- $test_ws.mount('/two/deep/', HTTPServlet::ProcHandler.new(two_deep_proc) )
39
+ $test_ws.mount('/deep/two', HTTPServlet::ProcHandler.new(two_deep_proc) )
26
40
 
27
41
  redir_proc = Proc.new { |req, resp|
28
42
  resp.body = 'redirector proc mounted on #{req.script_name}'
29
43
  resp.set_redirect( HTTPStatus::MovedPermanently, '/READ_ME.rdoc')
30
44
  }
31
45
 
32
- $test_ws.mount('/one/deep/', HTTPServlet::ProcHandler.new(redir_proc) )
46
+ $test_ws.mount('/deep/one', HTTPServlet::ProcHandler.new(redir_proc) )
33
47
 
34
48
  $test_ws.mount("/images", HTTPServlet::FileHandler,
35
49
  File.join( Test::App.root, "test", "fixtures"), {:FancyIndexing => true} )
36
50
 
37
51
  $test_ws_thread = Thread.new { $test_ws.start }
38
52
  end
39
-
53
+
40
54
  def test_download_rdoc_from_servlet
41
55
  some_url = $test_read_me_url
42
56
 
@@ -69,10 +83,12 @@ class DownspoutTest < Test::Unit::TestCase
69
83
  end
70
84
 
71
85
  should "fail with Curb error in case of excessive redirects" do
72
- two_deep_url = "http://127.0.0.1:8899/two/deep?curby"
86
+ assert_equal 2, Downspout::Config.max_redirects
87
+
88
+ too_deep_url = "http://127.0.0.1:8899/deep?n=3&test=curb"
73
89
 
74
90
  assert_raise Curl::Err::TooManyRedirectsError do
75
- dl = Downspout.fetch_url( two_deep_url )
91
+ dl = Downspout.fetch_url( too_deep_url )
76
92
  end
77
93
  end
78
94
 
@@ -82,12 +98,13 @@ class DownspoutTest < Test::Unit::TestCase
82
98
  end
83
99
 
84
100
  should "fail with Downspout error in case of excessive redirects" do
85
- two_deep_url = "http://127.0.0.1:8899/two/deep?no-curb"
101
+ assert_equal 2, Downspout::Config.max_redirects
102
+
103
+ too_deep_url = "http://127.0.0.1:8899/deep/?n=3&test=no-curb"
86
104
 
87
105
  assert_raise Downspout::ExcessiveRedirects do
88
- dl = Downspout.fetch_url( two_deep_url )
106
+ dl = Downspout.fetch_url( too_deep_url )
89
107
  end
90
-
91
108
  end
92
109
 
93
110
  teardown do
@@ -102,10 +119,12 @@ class DownspoutTest < Test::Unit::TestCase
102
119
  end
103
120
 
104
121
  should "fail due to excessive redirects" do
105
- @obj = Downspout::Downloader.new( :url => "http://127.0.0.1:8899/two/deep?over-draft" )
122
+ @obj = Downspout::Downloader.new( :url => "http://127.0.0.1:8899/deep/?n=2&test=over-draft" )
106
123
 
107
124
  assert_raise Downspout::ExcessiveRedirects do
108
- @obj.send('net_http_fetch', @obj.url, 1 ) # uses send to bypass Curb and force Net::HTTP
125
+ # use send to bypass Curb and force Net::HTTP
126
+ # also pass 1 so that the second redirect is too much
127
+ @obj.send('net_http_fetch', @obj.url, 1 )
109
128
  end
110
129
  end
111
130
 
@@ -113,7 +132,8 @@ class DownspoutTest < Test::Unit::TestCase
113
132
  @obj = Downspout::Downloader.new( :url => "http://fu.man.chu/deep/nested/resource?over-draft" )
114
133
 
115
134
  assert_raise SocketError do
116
- @obj.send('net_http_fetch', @obj.url) # uses send to bypass Curb and force Net::HTTP
135
+ # uses send to bypass Curb and force Net::HTTP
136
+ @obj.send('net_http_fetch', @obj.url)
117
137
  end
118
138
  end
119
139
 
@@ -125,3 +145,4 @@ class DownspoutTest < Test::Unit::TestCase
125
145
  end
126
146
 
127
147
  end
148
+
@@ -0,0 +1,54 @@
1
+ require 'test_helper'
2
+
3
+ class FocusedTest < Test::Unit::TestCase
4
+ context "Downspout" do
5
+ context "Focus" do
6
+ setup do
7
+ @expected_url = "https://download.github.com/rails-rails-v2.3.11-0-gb0c3d45.tar.gz"
8
+ @header_str = "HTTP/1.1 302 Found
9
+ Server: nginx/0.7.67
10
+ Date: Mon, 18 Apr 2011 01:53:16 GMT
11
+ Content-Type: text/html; charset=utf-8
12
+ Connection: keep-alive
13
+ Status: 302 Found
14
+ Location: https://nodeload.github.com/rails/rails/tarball/v2.3.11
15
+ X-Runtime: 3ms
16
+ Content-Length: 121
17
+ Set-Cookie: _gh_sess=BAh7BzoRbG9jYWxlX2d1ZXNzMCIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7AA%3D%3D--719def2ab2d99283383e374327fcdbb716c45c12; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT; secure; HttpOnly
18
+ Cache-Control: no-cache
19
+ Strict-Transport-Security: max-age=2592000
20
+
21
+ HTTP/1.1 302 Moved Temporarily
22
+ Server: nginx/0.7.67
23
+ Date: Mon, 18 Apr 2011 01:53:17 GMT
24
+ Transfer-Encoding: chunked
25
+ Connection: keep-alive
26
+ Location: https://download.github.com/rails-rails-v2.3.11-0-gb0c3d45.tar.gz
27
+
28
+ HTTP/1.1 200 OK
29
+ Server: nginx/0.7.67
30
+ Date: Mon, 18 Apr 2011 01:53:17 GMT
31
+ Content-Type: application/octet-stream
32
+ Content-Length: 3416081
33
+ Last-Modified: Mon, 18 Apr 2011 01:45:25 GMT
34
+ Connection: keep-alive
35
+ Accept-Ranges: bytes"
36
+ end
37
+
38
+ should "grep last location header" do
39
+ last_location = @header_str.scan(/Location\:\s?(.*)\W/).last.first
40
+
41
+ assert_equal @expected_url, last_location
42
+ end
43
+
44
+ should "match last_location method" do
45
+ @obj = Downspout::Downloader.new( :url => "http://bad.url/path" )
46
+ last_location = @obj.send('curb_last_location', @header_str )
47
+
48
+ assert_equal String, last_location.class
49
+ assert_equal @expected_url, last_location
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -4,28 +4,56 @@ class ConfigTest < Test::Unit::TestCase
4
4
  context "Downspout" do
5
5
  context "Config" do
6
6
 
7
- should "respond to tmp_dir" do
8
- assert Downspout::Config.respond_to?(:tmp_dir)
7
+ should "respond to max_redirects" do
8
+ assert Downspout::Config.respond_to?(:max_redirects)
9
9
  end
10
10
 
11
- should "default to '/tmp/downloads/' for Downspout::Config#tmp_dir" do
12
- assert_equal "/tmp/downloads/", Downspout::Config.tmp_dir
11
+ should "respond to tmp_dir" do
12
+ assert Downspout::Config.respond_to?(:tmp_dir)
13
13
  end
14
14
 
15
15
  should "respond to Network Enabled" do
16
16
  assert Downspout::Config.respond_to?("network_enabled?")
17
17
  end
18
18
 
19
- should "default to Network Enabled" do
20
- assert Downspout::Config.network_enabled?
21
- end
22
-
23
19
  should "respond to Disable Networking" do
24
20
  assert Downspout::Config.respond_to?("disable_networking!")
25
21
  end
26
22
 
23
+ should "respond to SSL Verification" do
24
+ assert Downspout::Config.respond_to?("ssl_verification?")
25
+ end
26
+
27
+ context "defaults" do
28
+
29
+ should "use '/tmp/downloads/' for Downspout::Config#tmp_dir" do
30
+ assert_equal "/tmp/downloads/", Downspout::Config.tmp_dir
31
+ end
32
+
33
+ should "be 2 redirect max" do
34
+ assert_equal 2, Downspout::Config.max_redirects
35
+ end
36
+
37
+ should "be Network Enabled" do
38
+ assert Downspout::Config.network_enabled?
39
+ end
40
+
41
+ should "use 'downspout' Default Prefix" do
42
+ assert_equal "downspout", Downspout::Config.default_prefix
43
+ end
44
+
45
+ should "use Curb library if available" do
46
+ assert Downspout::Config.curb_available?
47
+ assert Downspout::Config.enable_curb!
48
+ end
49
+
50
+ should "use SSL Verification" do
51
+ assert Downspout::Config.ssl_verification?
52
+ end
53
+
54
+ end
55
+
27
56
  should "allow setting Default Prefix" do
28
- assert_equal "downspout", Downspout::Config.default_prefix
29
57
  Downspout::Config.default_prefix = "fu-manchu"
30
58
  assert_equal "fu-manchu", Downspout::Config.default_prefix
31
59
  end
@@ -44,17 +72,19 @@ class ConfigTest < Test::Unit::TestCase
44
72
  assert Downspout::Config.curb_available?
45
73
  end
46
74
 
47
- should "enable Curb if library is available" do
48
- assert Downspout::Config.curb_available?
49
- assert Downspout::Config.enable_curb!
50
- end
51
-
52
75
  should "support enabling network operations" do
53
76
  assert !(Downspout::Config.network_enabled?)
54
77
  assert Downspout::Config.enable_networking!
55
78
  assert Downspout::Config.network_enabled?
56
79
  end
57
80
 
81
+ should "allow customization of redirect maximum" do
82
+ Downspout::Config.max_redirects = 3
83
+ assert_equal 3, Downspout::Config.max_redirects
84
+ Downspout::Config.max_redirects = 2
85
+ assert_equal 2, Downspout::Config.max_redirects
86
+ end
87
+
58
88
  context "Host-Based Credentials" do
59
89
  should "respond to Credentials" do
60
90
  assert Downspout::Config.respond_to?(:credentials)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: downspout
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 7
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 7
10
- version: 0.2.7
9
+ - 8
10
+ version: 0.2.8
11
11
  platform: ruby
12
12
  authors:
13
13
  - Phi.Sanders
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-04-15 00:00:00 -04:00
18
+ date: 2011-04-17 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -122,6 +122,7 @@ files:
122
122
  - test/downspout_test.rb
123
123
  - test/fixtures/faux_headers.txt
124
124
  - test/fixtures/ruby.png
125
+ - test/focused_test.rb
125
126
  - test/servlet.rb
126
127
  - test/test_helper.rb
127
128
  - test/test_logger.rb
@@ -168,6 +169,7 @@ specification_version: 3
168
169
  summary: Downspout is an easy-to-use ruby library for downloading files from URLs.
169
170
  test_files:
170
171
  - test/downspout_test.rb
172
+ - test/focused_test.rb
171
173
  - test/servlet.rb
172
174
  - test/test_helper.rb
173
175
  - test/test_logger.rb