ftputils 0.2.0 → 0.2.1

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/CHANGELOG.rdoc ADDED
@@ -0,0 +1,12 @@
1
+ = Change Log
2
+
3
+ == 0.2.1
4
+
5
+ * Add timeouts to FTPUtils and FTPConnection methods. Defaults to 5 minutes,
6
+ but can be set with FTPUtils.timeout_period=(seconds).
7
+
8
+ == 0.2.0
9
+
10
+ * Major internal refactoring, pulling FTPConnection out of FTPURI
11
+ * Add FTPFile, which implements a subset of the File API for FTP URIs
12
+ * Add FTPUtils.ls
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.1
data/ftputils.gemspec ADDED
@@ -0,0 +1,67 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ftputils}
8
+ s.version = "0.2.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Bruz Marzolf"]
12
+ s.date = %q{2011-06-20}
13
+ s.description = %q{Implements a subset of the methods in FileUtils for FTP URIs, falling back on FileUtils when a path doesn't look like FTP}
14
+ s.email = %q{bmarzolf@systemsbiology.org}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "CHANGELOG.rdoc",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "ftputils.gemspec",
27
+ "lib/ftputils.rb",
28
+ "lib/ftputils/ext/class.rb",
29
+ "lib/ftputils/ftpconnection.rb",
30
+ "lib/ftputils/ftpfile.rb",
31
+ "lib/ftputils/ftpuri.rb",
32
+ "spec/ftpconnection_spec.rb",
33
+ "spec/ftpfile_spec.rb",
34
+ "spec/ftpuri_spec.rb",
35
+ "spec/ftputils_spec.rb",
36
+ "spec/spec.opts",
37
+ "spec/spec_helper.rb"
38
+ ]
39
+ s.homepage = %q{http://github.com/bmarzolf/ftputils}
40
+ s.require_paths = ["lib"]
41
+ s.rubygems_version = %q{1.3.6}
42
+ s.summary = %q{Like FileUtils for FTP}
43
+ s.test_files = [
44
+ "spec/ftpconnection_spec.rb",
45
+ "spec/ftpfile_spec.rb",
46
+ "spec/ftpuri_spec.rb",
47
+ "spec/ftputils_spec.rb",
48
+ "spec/spec_helper.rb"
49
+ ]
50
+
51
+ if s.respond_to? :specification_version then
52
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
53
+ s.specification_version = 3
54
+
55
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
56
+ s.add_runtime_dependency(%q<ftpfxp>, [">= 0.0.4"])
57
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
58
+ else
59
+ s.add_dependency(%q<ftpfxp>, [">= 0.0.4"])
60
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
61
+ end
62
+ else
63
+ s.add_dependency(%q<ftpfxp>, [">= 0.0.4"])
64
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
65
+ end
66
+ end
67
+
data/lib/ftputils.rb CHANGED
@@ -7,61 +7,79 @@ end
7
7
 
8
8
  require 'fileutils'
9
9
  require 'open-uri'
10
+ require 'timeout'
10
11
 
11
12
  class FTPUtils
12
13
 
14
+ @@timeout_period = 300
15
+
16
+ def self.timeout_period
17
+ # default to a 5 minute timeout
18
+ @@timeout_period || 300
19
+ end
20
+
21
+ def self.timeout_period=(seconds)
22
+ @@timeout_period = seconds
23
+ end
24
+
13
25
  def self.cp(src, dest, options = {})
14
- # handle all combinations of copying to/from FTP and local files
15
- case [ftp_url?(src), ftp_url?(dest)]
16
- when [true, true]
17
- raise "src should be a filename, not a directory" if FTPFile.directory?(src)
26
+ timeout(timeout_period) do
27
+ # handle all combinations of copying to/from FTP and local files
28
+ case [ftp_url?(src), ftp_url?(dest)]
29
+ when [true, true]
30
+ raise "src should be a filename, not a directory" if FTPFile.directory?(src)
18
31
 
19
- dest_path = FTPFile.dirname(dest) + "/" + ( FTPFile.basename(dest) || FTPFile.basename(src) )
20
- FTPConnection.connect(src).fxpto(FTPConnection.connect(dest), dest_path, FTPFile.relative_path(src))
21
- when [true, false]
22
- raise "src should be a filename, not a directory" if FTPFile.directory?(src)
32
+ dest_path = FTPFile.dirname(dest) + "/" + ( FTPFile.basename(dest) || FTPFile.basename(src) )
33
+ FTPConnection.connect(src).fxpto(FTPConnection.connect(dest), dest_path, FTPFile.relative_path(src))
34
+ when [true, false]
35
+ raise "src should be a filename, not a directory" if FTPFile.directory?(src)
23
36
 
24
- filename = FTPFile.basename(src)
37
+ filename = FTPFile.basename(src)
25
38
 
26
- if File.directory? dest
27
- dest += "/#{filename}"
28
- end
39
+ if File.directory? dest
40
+ dest += "/#{filename}"
41
+ end
29
42
 
30
- connection = FTPConnection.connect(src)
31
- connection.chdir FTPFile.dirname(src)
32
- connection.getbinaryfile filename, dest, 1024
33
- when [false, true]
34
- raise "src should be a filename, not a directory" if File.directory? src
43
+ connection = FTPConnection.connect(src)
44
+ connection.chdir FTPFile.dirname(src)
45
+ connection.getbinaryfile filename, dest, 1024
46
+ when [false, true]
47
+ raise "src should be a filename, not a directory" if File.directory? src
35
48
 
36
- dest_path = FTPFile.relative_path(dest)
49
+ dest_path = FTPFile.relative_path(dest)
37
50
 
38
- if FTPFile.directory?(dest)
39
- dest_path += "/#{File.basename(src)}"
40
- end
51
+ if FTPFile.directory?(dest)
52
+ dest_path += "/#{File.basename(src)}"
53
+ end
41
54
 
42
- connection = FTPConnection.connect(dest)
43
- connection.chdir FTPFile.dirname(dest)
55
+ connection = FTPConnection.connect(dest)
56
+ connection.chdir FTPFile.dirname(dest)
44
57
 
45
- begin
46
58
  connection.putbinaryfile src, dest_path, 1024
47
- rescue Net::FTPPermError
48
- raise "Unable to copy #{src} to #{dest_path}, possibly due to FTP server permissions"
59
+ when [false, false]
60
+ FileUtils.cp src, dest, options
49
61
  end
50
- when [false, false]
51
- FileUtils.cp src, dest, options
52
62
  end
63
+ rescue Net::FTPPermError
64
+ raise "Unable to copy #{src} to #{dest}, possibly due to FTP server permissions"
65
+ rescue Timeout::Error
66
+ raise "Copying #{src} to #{dest} timed out after #{timeout_period} seconds"
53
67
  end
54
68
 
55
69
  def self.rm(path)
56
- if ftp_url?(path)
57
- raise "Can't use FTPUtils.rm on directories. Instead use FTPUtils.rm_r" if FTPFile.directory?(path)
58
-
59
- connection = FTPConnection.connect(path)
60
- connection.chdir FTPFile.dirname(path)
61
- connection.delete FTPFile.basename(path)
62
- else
63
- FileUtils.rm path
70
+ timeout(timeout_period) do
71
+ if ftp_url?(path)
72
+ raise "Can't use FTPUtils.rm on directories. Instead use FTPUtils.rm_r" if FTPFile.directory?(path)
73
+
74
+ connection = FTPConnection.connect(path)
75
+ connection.chdir FTPFile.dirname(path)
76
+ connection.delete FTPFile.basename(path)
77
+ else
78
+ FileUtils.rm path
79
+ end
64
80
  end
81
+ rescue Timeout::Error
82
+ raise "Removing #{path} timed out after #{timeout_period} seconds"
65
83
  end
66
84
 
67
85
  def self.mv(src, dest, options = {})
@@ -70,78 +88,94 @@ class FTPUtils
70
88
  end
71
89
 
72
90
  def self.rm_r(path)
73
- if ftp_url?(path)
74
- if FTPFile.directory?(path)
75
- connection = FTPConnection.connect(path)
76
- connection.chdir FTPFile.relative_path(path)
77
-
78
- files = connection.nlst
79
- files.each {|file| rm_r "#{path}/#{file}"}
80
-
81
- connection.rmdir FTPFile.relative_path(path)
91
+ timeout(timeout_period) do
92
+ if ftp_url?(path)
93
+ if FTPFile.directory?(path)
94
+ connection = FTPConnection.connect(path)
95
+ connection.chdir FTPFile.relative_path(path)
96
+
97
+ files = connection.nlst
98
+ files.each {|file| rm_r "#{path}/#{file}"}
99
+
100
+ connection.rmdir FTPFile.relative_path(path)
101
+ else
102
+ rm(path)
103
+ end
82
104
  else
83
- rm(path)
105
+ FileUtils.rm_r path
84
106
  end
85
- else
86
- FileUtils.rm_r path
87
107
  end
108
+ rescue Timeout::Error
109
+ raise "Removing #{path} timed out after #{timeout_period} seconds"
88
110
  end
89
111
 
90
112
  def self.mkdir_p(path)
91
- if ftp_url?(path)
92
- connection = FTPConnection.connect(path)
113
+ timeout(timeout_period) do
114
+ if ftp_url?(path)
115
+ connection = FTPConnection.connect(path)
93
116
 
94
- subdirs = FTPFile.relative_path(path).split(/\//)
95
- subdirs.each do |subdir|
96
- next if subdir == ""
117
+ subdirs = FTPFile.relative_path(path).split(/\//)
118
+ subdirs.each do |subdir|
119
+ next if subdir == ""
97
120
 
98
- connection.mkdir subdir
99
- connection.chdir subdir
121
+ connection.mkdir subdir
122
+ connection.chdir subdir
123
+ end
124
+ else
125
+ FileUtils.mkdir_p path
100
126
  end
101
- else
102
- FileUtils.mkdir_p path
103
127
  end
128
+ rescue Timeout::Error
129
+ raise "Creating #{path} timed out after #{timeout_period} seconds"
104
130
  end
105
131
 
106
132
  def self.cp_r(src, dest, options = {})
107
- # handle all combinations of copying to/from FTP and local files
108
- if ftp_url?(src)
109
- if FTPFile.directory?(src)
110
- mkdir_p dest
111
-
112
- connection = FTPConnection.connect(src)
113
- files = connection.nlst
114
- files.each {|file| cp_r "#{src}/#{file}", "#{dest}/#{file}", options}
115
- else
116
- cp(src, dest, options)
117
- end
118
- elsif ftp_url?(dest)
119
- if FTPFile.directory?(dest)
120
- mkdir_p dest
121
-
122
- files = Dir.entries(src)
123
- files.each {|file| cp_r "#{src}/#{file}", "#{dest}/#{file}", options}
133
+ timeout(timeout_period) do
134
+ # handle all combinations of copying to/from FTP and local files
135
+ if ftp_url?(src)
136
+ if FTPFile.directory?(src)
137
+ mkdir_p dest
138
+
139
+ connection = FTPConnection.connect(src)
140
+ files = connection.nlst
141
+ files.each {|file| cp_r "#{src}/#{file}", "#{dest}/#{file}", options}
142
+ else
143
+ cp(src, dest, options)
144
+ end
145
+ elsif ftp_url?(dest)
146
+ if FTPFile.directory?(dest)
147
+ mkdir_p dest
148
+
149
+ files = Dir.entries(src)
150
+ files.each {|file| cp_r "#{src}/#{file}", "#{dest}/#{file}", options}
151
+ else
152
+ cp(src, dest, options)
153
+ end
124
154
  else
125
- cp(src, dest, options)
155
+ FileUtils.cp_r src, dest
126
156
  end
127
- else
128
- FileUtils.cp_r src, dest
129
157
  end
158
+ rescue Timeout::Error
159
+ raise "Copying #{src} to #{dest} timed out after #{timeout_period} seconds"
130
160
  end
131
161
 
132
162
  def self.ls(path)
133
- if ftp_url?(path)
134
- if FTPFile.directory?(path)
135
- connection = FTPConnection.connect(path)
136
- connection.chdir FTPFile.relative_path(path)
137
-
138
- return connection.nlst
163
+ timeout(timeout_period) do
164
+ if ftp_url?(path)
165
+ if FTPFile.directory?(path)
166
+ connection = FTPConnection.connect(path)
167
+ connection.chdir FTPFile.relative_path(path)
168
+
169
+ return connection.nlst
170
+ else
171
+ nil
172
+ end
139
173
  else
140
- nil
174
+ Dir.entries path
141
175
  end
142
- else
143
- Dir.entries path
144
176
  end
177
+ rescue Timeout::Error
178
+ raise "Listing #{path} timed out after #{timeout_period} seconds"
145
179
  end
146
180
 
147
181
  private
@@ -1,29 +1,35 @@
1
+ require 'timeout'
2
+
1
3
  class FTPUtils
2
4
  class FTPConnection
3
5
  cattr_accessor :connections
4
6
 
5
7
  def self.connect(uri)
6
- if uri.match(/^ftp:\/\/(.*?):(.*?)@(.*?)(\/.*)*$/)
7
- username = $1
8
- password = $2
9
- host = $3
10
-
11
- connection = self.establish_connection(host, username, password)
12
-
13
- # need to reset to the top directory since connections are cached and
14
- # could have been left elsewhere. this also provides a way to see if the
15
- # connection has expired and needs to be reloaded
16
- begin
17
- connection.chdir "/"
18
- rescue Net::FTPTempError
19
- connection = self.establish_connection(host, username, password, true)
20
- retry
8
+ timeout(FTPUtils.timeout_period) do
9
+ if uri.match(/^ftp:\/\/(.*?):(.*?)@(.*?)(\/.*)*$/)
10
+ username = $1
11
+ password = $2
12
+ host = $3
13
+
14
+ connection = self.establish_connection(host, username, password)
15
+
16
+ # need to reset to the top directory since connections are cached and
17
+ # could have been left elsewhere. this also provides a way to see if the
18
+ # connection has expired and needs to be reloaded
19
+ begin
20
+ connection.chdir "/"
21
+ rescue Net::FTPTempError
22
+ connection = self.establish_connection(host, username, password, true)
23
+ retry
24
+ end
25
+
26
+ return connection
27
+ else
28
+ raise "Invalid FTP URL provided: #{uri}"
21
29
  end
22
-
23
- return connection
24
- else
25
- raise "Invalid FTP URL provided: #{uri}"
26
30
  end
31
+ rescue Timeout::Error
32
+ raise "Connecting to #{uri} timed out after #{FTPUtils.timeout_period} seconds"
27
33
  end
28
34
 
29
35
  def self.clear_connection_cache
@@ -52,5 +52,17 @@ describe FTPUtils::FTPConnection do
52
52
  FTPUtils::FTPConnection.connect("ftp://admin:test@myhost/path/to/file.txt")
53
53
  end.should raise_error(Net::FTPPermError)
54
54
  end
55
+
56
+ it "should raise an error if connecting takes too long" do
57
+ FTPUtils.timeout_period = 0.1
58
+
59
+ Net::FTPFXP.should_receive(:new) do
60
+ sleep 1
61
+ end
62
+
63
+ lambda do
64
+ FTPUtils::FTPConnection.connect("ftp://admin:test@myhost/path/to/file.txt")
65
+ end.should raise_error("Connecting to ftp://admin:test@myhost/path/to/file.txt timed out after 0.1 seconds")
66
+ end
55
67
  end
56
68
  end
@@ -76,6 +76,33 @@ describe "FTPUtils" do
76
76
  FTPUtils.cp "ftp://admin:test@host1/subdir1", "ftp://admin:test@host2/subdir2"
77
77
  end.should raise_error("src should be a filename, not a directory")
78
78
  end
79
+
80
+ it "should time out a transfer that takes too long" do
81
+ FTPUtils.timeout_period = 0.1
82
+
83
+ FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/file1.txt").
84
+ and_return(false)
85
+ FTPUtils::FTPFile.should_receive(:dirname).with("ftp://admin:test@host2/file2.txt").
86
+ and_return("/")
87
+ FTPUtils::FTPFile.should_receive(:basename).with("ftp://admin:test@host2/file2.txt").
88
+ and_return("file2.txt")
89
+
90
+ mock_src_connection = mock(FTPUtils::FTPConnection)
91
+ FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/file1.txt").
92
+ and_return(mock_src_connection)
93
+ mock_dest_connection = mock(FTPUtils::FTPConnection)
94
+ FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host2/file2.txt").
95
+ and_return(mock_dest_connection)
96
+ FTPUtils::FTPFile.should_receive(:relative_path).with("ftp://admin:test@host1/file1.txt").
97
+ and_return("/file1.txt")
98
+ mock_src_connection.should_receive(:fxpto).with(mock_dest_connection, "//file2.txt", "/file1.txt") do
99
+ sleep 1
100
+ end
101
+
102
+ lambda do
103
+ FTPUtils.cp "ftp://admin:test@host1/file1.txt", "ftp://admin:test@host2/file2.txt"
104
+ end.should raise_error("Copying ftp://admin:test@host1/file1.txt to ftp://admin:test@host2/file2.txt timed out after 0.1 seconds")
105
+ end
79
106
  end
80
107
 
81
108
  describe "from FTP to the local filesystem" do
@@ -119,6 +146,32 @@ describe "FTPUtils" do
119
146
  FTPUtils.cp "ftp://admin:test@host1/subdir1/file1.txt", "/home/me"
120
147
  end
121
148
 
149
+ it "should time out a transfer that takes too long" do
150
+ FTPUtils.timeout_period = 0.1
151
+
152
+ FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/file1.txt").
153
+ and_return(false)
154
+
155
+ FTPUtils::FTPFile.should_receive(:basename).with("ftp://admin:test@host1/subdir1/file1.txt").
156
+ and_return("file1.txt")
157
+
158
+ File.should_receive(:directory?).with("/home/me/file2.txt").and_return(false)
159
+
160
+ mock_src_connection = mock(FTPUtils::FTPConnection)
161
+ FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1/file1.txt").
162
+ and_return(mock_src_connection)
163
+ FTPUtils::FTPFile.should_receive(:dirname).with("ftp://admin:test@host1/subdir1/file1.txt").
164
+ and_return("/subdir1")
165
+ mock_src_connection.should_receive(:chdir).with("/subdir1")
166
+ mock_src_connection.should_receive(:getbinaryfile).with("file1.txt", "/home/me/file2.txt", 1024) do
167
+ sleep 1
168
+ end
169
+
170
+ lambda do
171
+ FTPUtils.cp "ftp://admin:test@host1/subdir1/file1.txt", "/home/me/file2.txt"
172
+ end.should raise_error("Copying ftp://admin:test@host1/subdir1/file1.txt to /home/me/file2.txt timed out after 0.1 seconds")
173
+ end
174
+
122
175
  it "should raise an error if the source URI is a directory" do
123
176
  FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1").
124
177
  and_return(true)
@@ -179,7 +232,29 @@ describe "FTPUtils" do
179
232
 
180
233
  lambda do
181
234
  FTPUtils.cp "/home/me/file1.txt", "ftp://admin:test@host2/file2.txt"
182
- end.should raise_error("Unable to copy /home/me/file1.txt to /file2.txt, possibly due to FTP server permissions")
235
+ end.should raise_error("Unable to copy /home/me/file1.txt to ftp://admin:test@host2/file2.txt, possibly due to FTP server permissions")
236
+ end
237
+
238
+ it "should time out a transfer that takes too long" do
239
+ FTPUtils.timeout_period = 0.1
240
+
241
+ File.should_receive(:directory?).with("/home/me/file1.txt").and_return(false)
242
+ FTPUtils::FTPFile.should_receive(:relative_path).with("ftp://admin:test@host2/file2.txt").
243
+ and_return("/file2.txt")
244
+ FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host2/file2.txt").
245
+ and_return(false)
246
+ mock_dest_connection = mock(FTPUtils::FTPConnection)
247
+ FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host2/file2.txt").
248
+ and_return(mock_dest_connection)
249
+ FTPUtils::FTPFile.should_receive(:dirname).with("ftp://admin:test@host2/file2.txt").and_return("/")
250
+ mock_dest_connection.should_receive(:chdir).with("/")
251
+ mock_dest_connection.should_receive(:putbinaryfile).with("/home/me/file1.txt", "/file2.txt", 1024) do
252
+ sleep 1
253
+ end
254
+
255
+ lambda do
256
+ FTPUtils.cp "/home/me/file1.txt", "ftp://admin:test@host2/file2.txt"
257
+ end.should raise_error("Copying /home/me/file1.txt to ftp://admin:test@host2/file2.txt timed out after 0.1 seconds")
183
258
  end
184
259
 
185
260
  it "should raise an error if the source file is a directory" do
@@ -228,6 +303,25 @@ describe "FTPUtils" do
228
303
 
229
304
  FTPUtils.rm "file.txt"
230
305
  end
306
+
307
+ it "should time out a removal that takes too long" do
308
+ FTPUtils.timeout_period = 0.1
309
+
310
+ FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/file.txt").
311
+ and_return(false)
312
+ mock_connection = mock(FTPUtils::FTPConnection)
313
+ FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1/file.txt").
314
+ and_return(mock_connection)
315
+ FTPUtils::FTPFile.should_receive(:dirname).with("ftp://admin:test@host1/subdir1/file.txt").and_return("/subdir1")
316
+ mock_connection.should_receive(:chdir).with("/subdir1")
317
+ mock_connection.should_receive(:delete).with("file.txt") do
318
+ sleep 1
319
+ end
320
+
321
+ lambda do
322
+ FTPUtils.rm "ftp://admin:test@host1/subdir1/file.txt"
323
+ end.should raise_error("Removing ftp://admin:test@host1/subdir1/file.txt timed out after 0.1 seconds")
324
+ end
231
325
  end
232
326
 
233
327
  describe "removing recursively" do
@@ -275,6 +369,43 @@ describe "FTPUtils" do
275
369
 
276
370
  FTPUtils.rm_r "dir"
277
371
  end
372
+
373
+ it "should time out a removal that takes too long" do
374
+ FTPUtils.timeout_period = 0.1
375
+
376
+ FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1").
377
+ and_return(true)
378
+ mock_connection = mock(FTPUtils::FTPConnection)
379
+ FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1").
380
+ and_return(mock_connection)
381
+ mock_connection.should_receive(:chdir).with("/subdir1")
382
+ mock_connection.should_receive(:nlst).and_return( ["subdir2", "file.txt"] )
383
+
384
+ FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/subdir2").
385
+ and_return(true)
386
+ mock_subdir_connection = mock(FTPUtils::FTPConnection)
387
+ FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1/subdir2").
388
+ and_return(mock_subdir_connection)
389
+ mock_subdir_connection.should_receive(:chdir).with("/subdir1/subdir2")
390
+ mock_subdir_connection.should_receive(:nlst).and_return( [] )
391
+ FTPUtils::FTPFile.should_receive(:relative_path).with("ftp://admin:test@host1/subdir1/subdir2").twice.
392
+ and_return("/subdir1/subdir2")
393
+ mock_subdir_connection.should_receive(:rmdir).with("/subdir1/subdir2")
394
+
395
+ FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/file.txt").
396
+ and_return(false)
397
+ FTPUtils.should_receive(:rm).with("ftp://admin:test@host1/subdir1/file.txt")
398
+
399
+ FTPUtils::FTPFile.should_receive(:relative_path).with("ftp://admin:test@host1/subdir1").twice.
400
+ and_return("/subdir1")
401
+ mock_connection.should_receive(:rmdir).with("/subdir1") do
402
+ sleep 1
403
+ end
404
+
405
+ lambda do
406
+ FTPUtils.rm_r "ftp://admin:test@host1/subdir1"
407
+ end.should raise_error("Removing ftp://admin:test@host1/subdir1 timed out after 0.1 seconds")
408
+ end
278
409
  end
279
410
 
280
411
  describe "create a directory and all its parents" do
@@ -299,6 +430,25 @@ describe "FTPUtils" do
299
430
 
300
431
  FTPUtils.mkdir_p("subdir1/subdir2")
301
432
  end
433
+
434
+ it "should time out a creation that takes too long" do
435
+ FTPUtils.timeout_period = 0.1
436
+
437
+ mock_connection = mock(FTPUtils::FTPConnection)
438
+ FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1/subdir2").
439
+ and_return(mock_connection)
440
+
441
+ FTPUtils::FTPFile.should_receive(:relative_path).with("ftp://admin:test@host1/subdir1/subdir2").
442
+ and_return("/subdir1/subdir2")
443
+
444
+ mock_connection.should_receive(:mkdir).with("subdir1") do
445
+ sleep 1
446
+ end
447
+
448
+ lambda do
449
+ FTPUtils.mkdir_p "ftp://admin:test@host1/subdir1/subdir2"
450
+ end.should raise_error("Creating ftp://admin:test@host1/subdir1/subdir2 timed out after 0.1 seconds")
451
+ end
302
452
  end
303
453
 
304
454
  describe "copying recursively" do
@@ -375,6 +525,18 @@ describe "FTPUtils" do
375
525
 
376
526
  FTPUtils.cp_r "/home/me/subdir1", "/home/me/subdir2"
377
527
  end
528
+
529
+ it "should time out a copy that takes too long" do
530
+ FTPUtils.timeout_period = 0.1
531
+
532
+ FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1") do
533
+ sleep 1
534
+ end
535
+
536
+ lambda do
537
+ FTPUtils.cp_r "ftp://admin:test@host1/subdir1", "ftp://admin:test@host2/subdir2"
538
+ end.should raise_error("Copying ftp://admin:test@host1/subdir1 to ftp://admin:test@host2/subdir2 timed out after 0.1 seconds")
539
+ end
378
540
  end
379
541
 
380
542
  describe "listing the entries in a directory" do
@@ -402,5 +564,17 @@ describe "FTPUtils" do
402
564
 
403
565
  FTPUtils.ls("/home/me/subdir1")
404
566
  end
567
+
568
+ it "should time out a directory listing that takes too long" do
569
+ FTPUtils.timeout_period = 0.1
570
+
571
+ FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1") do
572
+ sleep 1
573
+ end
574
+
575
+ lambda do
576
+ FTPUtils.ls("ftp://admin:test@host1/subdir1")
577
+ end.should raise_error("Listing ftp://admin:test@host1/subdir1 timed out after 0.1 seconds")
578
+ end
405
579
  end
406
580
  end
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,3 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
2
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
3
  require 'ftputils'
4
- require 'spec'
5
- require 'spec/autorun'
6
-
7
- Spec::Runner.configure do |config|
8
-
9
- end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 0
9
- version: 0.2.0
8
+ - 1
9
+ version: 0.2.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Bruz Marzolf
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-06-04 00:00:00 -07:00
17
+ date: 2011-06-20 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -56,11 +56,12 @@ extra_rdoc_files:
56
56
  - README.rdoc
57
57
  files:
58
58
  - .document
59
- - .gitignore
59
+ - CHANGELOG.rdoc
60
60
  - LICENSE
61
61
  - README.rdoc
62
62
  - Rakefile
63
63
  - VERSION
64
+ - ftputils.gemspec
64
65
  - lib/ftputils.rb
65
66
  - lib/ftputils/ext/class.rb
66
67
  - lib/ftputils/ftpconnection.rb
@@ -77,8 +78,8 @@ homepage: http://github.com/bmarzolf/ftputils
77
78
  licenses: []
78
79
 
79
80
  post_install_message:
80
- rdoc_options:
81
- - --charset=UTF-8
81
+ rdoc_options: []
82
+
82
83
  require_paths:
83
84
  - lib
84
85
  required_ruby_version: !ruby/object:Gem::Requirement
data/.gitignore DELETED
@@ -1,21 +0,0 @@
1
- ## MAC OS
2
- .DS_Store
3
-
4
- ## TEXTMATE
5
- *.tmproj
6
- tmtags
7
-
8
- ## EMACS
9
- *~
10
- \#*
11
- .\#*
12
-
13
- ## VIM
14
- *.swp
15
-
16
- ## PROJECT::GENERAL
17
- coverage
18
- rdoc
19
- pkg
20
-
21
- ## PROJECT::SPECIFIC