ftputils 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +12 -0
- data/VERSION +1 -1
- data/ftputils.gemspec +67 -0
- data/lib/ftputils.rb +120 -86
- data/lib/ftputils/ftpconnection.rb +25 -19
- data/spec/ftpconnection_spec.rb +12 -0
- data/spec/ftputils_spec.rb +175 -1
- data/spec/spec_helper.rb +0 -6
- metadata +7 -6
- data/.gitignore +0 -21
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.
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
37
|
+
filename = FTPFile.basename(src)
|
25
38
|
|
26
|
-
|
27
|
-
|
28
|
-
|
39
|
+
if File.directory? dest
|
40
|
+
dest += "/#{filename}"
|
41
|
+
end
|
29
42
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
49
|
+
dest_path = FTPFile.relative_path(dest)
|
37
50
|
|
38
|
-
|
39
|
-
|
40
|
-
|
51
|
+
if FTPFile.directory?(dest)
|
52
|
+
dest_path += "/#{File.basename(src)}"
|
53
|
+
end
|
41
54
|
|
42
|
-
|
43
|
-
|
55
|
+
connection = FTPConnection.connect(dest)
|
56
|
+
connection.chdir FTPFile.dirname(dest)
|
44
57
|
|
45
|
-
begin
|
46
58
|
connection.putbinaryfile src, dest_path, 1024
|
47
|
-
|
48
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
74
|
-
if
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
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
|
-
|
92
|
-
|
113
|
+
timeout(timeout_period) do
|
114
|
+
if ftp_url?(path)
|
115
|
+
connection = FTPConnection.connect(path)
|
93
116
|
|
94
|
-
|
95
|
-
|
96
|
-
|
117
|
+
subdirs = FTPFile.relative_path(path).split(/\//)
|
118
|
+
subdirs.each do |subdir|
|
119
|
+
next if subdir == ""
|
97
120
|
|
98
|
-
|
99
|
-
|
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
|
-
|
108
|
-
|
109
|
-
if
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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
|
-
|
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
|
-
|
134
|
-
if
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
-
|
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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
data/spec/ftpconnection_spec.rb
CHANGED
@@ -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
|
data/spec/ftputils_spec.rb
CHANGED
@@ -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
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
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:
|
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
|
-
- .
|
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
|
-
|
81
|
+
rdoc_options: []
|
82
|
+
|
82
83
|
require_paths:
|
83
84
|
- lib
|
84
85
|
required_ruby_version: !ruby/object:Gem::Requirement
|