ftputils 0.1.0
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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/lib/ftputils/ext/class.rb +48 -0
- data/lib/ftputils/ftpconnection.rb +50 -0
- data/lib/ftputils/ftpfile.rb +106 -0
- data/lib/ftputils/ftpuri.rb +25 -0
- data/lib/ftputils.rb +152 -0
- data/spec/ftpconnection_spec.rb +56 -0
- data/spec/ftpfile_spec.rb +177 -0
- data/spec/ftpuri_spec.rb +25 -0
- data/spec/ftputils_spec.rb +387 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- metadata +110 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Bruz Marzolf
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= ftputils
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 Bruz Marzolf. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "ftputils"
|
8
|
+
gem.summary = %Q{Like FileUtils for FTP}
|
9
|
+
gem.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}
|
10
|
+
gem.email = "bmarzolf@systemsbiology.org"
|
11
|
+
gem.homepage = "http://github.com/bmarzolf/ftputils"
|
12
|
+
gem.authors = ["Bruz Marzolf"]
|
13
|
+
gem.add_dependency "ftpfxp", ">= 0.0.4"
|
14
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'spec/rake/spectask'
|
23
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
24
|
+
spec.libs << 'lib' << 'spec'
|
25
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
26
|
+
end
|
27
|
+
|
28
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
29
|
+
spec.libs << 'lib' << 'spec'
|
30
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
31
|
+
spec.rcov = true
|
32
|
+
end
|
33
|
+
|
34
|
+
task :spec => :check_dependencies
|
35
|
+
|
36
|
+
task :default => :spec
|
37
|
+
|
38
|
+
require 'rake/rdoctask'
|
39
|
+
Rake::RDocTask.new do |rdoc|
|
40
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
41
|
+
|
42
|
+
rdoc.rdoc_dir = 'rdoc'
|
43
|
+
rdoc.title = "ftputils #{version}"
|
44
|
+
rdoc.rdoc_files.include('README*')
|
45
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
46
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Class
|
2
|
+
def cattr_reader(*syms)
|
3
|
+
syms.flatten.each do |sym|
|
4
|
+
next if sym.is_a?(Hash)
|
5
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
6
|
+
unless defined? @@#{sym}
|
7
|
+
@@#{sym} = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.#{sym}
|
11
|
+
@@#{sym}
|
12
|
+
end
|
13
|
+
|
14
|
+
def #{sym}
|
15
|
+
@@#{sym}
|
16
|
+
end
|
17
|
+
EOS
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def cattr_writer(*syms)
|
22
|
+
options = syms.last.is_a?(Hash) ? syms.pop : {}
|
23
|
+
syms.flatten.each do |sym|
|
24
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
25
|
+
unless defined? @@#{sym}
|
26
|
+
@@#{sym} = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.#{sym}=(obj)
|
30
|
+
@@#{sym} = obj
|
31
|
+
end
|
32
|
+
|
33
|
+
#{"
|
34
|
+
def #{sym}=(obj)
|
35
|
+
@@#{sym} = obj
|
36
|
+
end
|
37
|
+
" unless options[:instance_writer] == false }
|
38
|
+
EOS
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def cattr_accessor(*syms)
|
43
|
+
cattr_reader(*syms)
|
44
|
+
cattr_writer(*syms)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class FTPUtils
|
2
|
+
class FTPConnection
|
3
|
+
cattr_accessor :connections
|
4
|
+
|
5
|
+
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
|
21
|
+
end
|
22
|
+
|
23
|
+
return connection
|
24
|
+
else
|
25
|
+
raise "Invalid FTP URL provided: #{uri}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.clear_connection_cache
|
30
|
+
self.connections = Hash.new
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def self.establish_connection(host, username, password, reload = false)
|
36
|
+
self.connections ||= Hash.new
|
37
|
+
|
38
|
+
return connections[host] if connections[host] && !reload
|
39
|
+
|
40
|
+
connection = Net::FTPFXP.new
|
41
|
+
connection.passive = true
|
42
|
+
connection.connect(host)
|
43
|
+
connection.login(username, password) if username && password
|
44
|
+
|
45
|
+
connections[host] = connection
|
46
|
+
|
47
|
+
return connection
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
class FTPUtils
|
2
|
+
class FTPFile
|
3
|
+
|
4
|
+
def self.basename(path, suffix=nil)
|
5
|
+
if ftp_uri = FTPUtils::FTPURI.parse(path)
|
6
|
+
if suffix
|
7
|
+
return ftp_uri.filename.gsub!(/#{suffix}\Z/,'')
|
8
|
+
else
|
9
|
+
return ftp_uri.filename
|
10
|
+
end
|
11
|
+
else
|
12
|
+
if suffix
|
13
|
+
return File.basename(path, suffix)
|
14
|
+
else
|
15
|
+
return File.basename(path)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.directory?(path)
|
21
|
+
if ftp_uri = FTPUtils::FTPURI.parse(path)
|
22
|
+
begin
|
23
|
+
connection = FTPUtils::FTPConnection.connect(path)
|
24
|
+
connection.chdir(ftp_uri.path)
|
25
|
+
|
26
|
+
return true
|
27
|
+
rescue Net::FTPPermError
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
else
|
31
|
+
return File.directory? path
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.dirname(path)
|
36
|
+
if ftp_uri = FTPUtils::FTPURI.parse(path)
|
37
|
+
return ftp_uri.dirname
|
38
|
+
else
|
39
|
+
return File.dirname(path)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.exists?(path)
|
44
|
+
if ftp_uri = FTPUtils::FTPURI.parse(path)
|
45
|
+
connection = FTPUtils::FTPConnection.connect(path)
|
46
|
+
connection.chdir ftp_uri.dirname
|
47
|
+
if connection.size(ftp_uri.filename) > 0
|
48
|
+
return true
|
49
|
+
else
|
50
|
+
return false
|
51
|
+
end
|
52
|
+
else
|
53
|
+
return File.exists?(path)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.expand_path(path)
|
58
|
+
if ftp_uri = FTPUtils::FTPURI.parse(path)
|
59
|
+
return path
|
60
|
+
else
|
61
|
+
return File.expand_path(path)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.file?(path)
|
66
|
+
if ftp_uri = FTPUtils::FTPURI.parse(path)
|
67
|
+
connection = FTPUtils::FTPConnection.connect(path)
|
68
|
+
connection.chdir(ftp_uri.dirname)
|
69
|
+
|
70
|
+
begin
|
71
|
+
connection.size(ftp_uri.filename)
|
72
|
+
return true
|
73
|
+
rescue Net::FTPPermError
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
else
|
77
|
+
return File.file? path
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.mtime(path)
|
82
|
+
if ftp_uri = FTPUtils::FTPURI.parse(path)
|
83
|
+
connection = FTPUtils::FTPConnection.connect(path)
|
84
|
+
connection.chdir(ftp_uri.dirname)
|
85
|
+
|
86
|
+
return connection.mtime(ftp_uri.filename)
|
87
|
+
else
|
88
|
+
return File.mtime path
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.relative_path(path)
|
93
|
+
if ftp_uri = FTPUtils::FTPURI.parse(path)
|
94
|
+
return ftp_uri.path
|
95
|
+
else
|
96
|
+
return nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def self.ftp_url?(str)
|
103
|
+
str.match(/^ftp:\/\//i) ? true : false
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class FTPUtils
|
2
|
+
class FTPURI
|
3
|
+
attr_accessor :dirname, :filename, :path
|
4
|
+
|
5
|
+
def self.parse(uri)
|
6
|
+
if uri.match(/^ftp:\/\/.*?:.*?@.*?(\/.*)*$/)
|
7
|
+
path = $1 || "/"
|
8
|
+
parts = path.split(/\//)
|
9
|
+
filename = parts[-1]
|
10
|
+
dirname = parts[0..-2].join("\/")
|
11
|
+
dirname = "/" if dirname.empty?
|
12
|
+
|
13
|
+
ftp_uri = FTPURI.new(dirname, filename, path)
|
14
|
+
else
|
15
|
+
return nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(dirname, filename, path)
|
20
|
+
self.dirname = dirname
|
21
|
+
self.filename = filename
|
22
|
+
self.path = path
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/ftputils.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
begin
|
2
|
+
require 'ftpfxp'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
require 'ftpfxp'
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'fileutils'
|
9
|
+
require 'open-uri'
|
10
|
+
|
11
|
+
class FTPUtils
|
12
|
+
def self.cp(src, dest, options = {})
|
13
|
+
# handle all combinations of copying to/from FTP and local files
|
14
|
+
case [ftp_url?(src), ftp_url?(dest)]
|
15
|
+
when [true, true]
|
16
|
+
raise "src should be a filename, not a directory" if FTPFile.directory?(src)
|
17
|
+
|
18
|
+
dest_path = FTPFile.dirname(dest) + "/" + ( FTPFile.basename(dest) || FTPFile.basename(src) )
|
19
|
+
FTPConnection.connect(src).fxpto(FTPConnection.connect(dest), dest_path, FTPFile.relative_path(src))
|
20
|
+
when [true, false]
|
21
|
+
raise "src should be a filename, not a directory" if FTPFile.directory?(src)
|
22
|
+
|
23
|
+
filename = FTPFile.basename(src)
|
24
|
+
|
25
|
+
if File.directory? dest
|
26
|
+
dest += "/#{filename}"
|
27
|
+
end
|
28
|
+
|
29
|
+
connection = FTPConnection.connect(src)
|
30
|
+
connection.chdir FTPFile.dirname(src)
|
31
|
+
connection.getbinaryfile filename, dest, 1024
|
32
|
+
when [false, true]
|
33
|
+
raise "src should be a filename, not a directory" if File.directory? src
|
34
|
+
|
35
|
+
dest_path = FTPFile.relative_path(dest)
|
36
|
+
|
37
|
+
if FTPFile.directory?(dest)
|
38
|
+
dest_path += "/#{File.basename(src)}"
|
39
|
+
end
|
40
|
+
|
41
|
+
connection = FTPConnection.connect(dest)
|
42
|
+
connection.chdir FTPFile.dirname(dest)
|
43
|
+
connection.putbinaryfile src, dest_path, 1024
|
44
|
+
when [false, false]
|
45
|
+
FileUtils.cp src, dest, options
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.rm(path)
|
50
|
+
if ftp_url?(path)
|
51
|
+
raise "Can't use FTPUtils.rm on directories. Instead use FTPUtils.rm_r" if FTPFile.directory?(path)
|
52
|
+
|
53
|
+
connection = FTPConnection.connect(path)
|
54
|
+
connection.chdir FTPFile.dirname(path)
|
55
|
+
connection.delete FTPFile.basename(path)
|
56
|
+
else
|
57
|
+
FileUtils.rm path
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.mv(src, dest, options = {})
|
62
|
+
cp(src, dest, options)
|
63
|
+
rm(src)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.rm_r(path)
|
67
|
+
if ftp_url?(path)
|
68
|
+
if FTPFile.directory?(path)
|
69
|
+
connection = FTPConnection.connect(path)
|
70
|
+
connection.chdir FTPFile.relative_path(path)
|
71
|
+
|
72
|
+
files = connection.nlst
|
73
|
+
files.each {|file| rm_r "#{path}/#{file}"}
|
74
|
+
|
75
|
+
connection.rmdir FTPFile.relative_path(path)
|
76
|
+
else
|
77
|
+
rm(path)
|
78
|
+
end
|
79
|
+
else
|
80
|
+
FileUtils.rm_r path
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.mkdir_p(path)
|
85
|
+
if ftp_url?(path)
|
86
|
+
connection = FTPConnection.connect(path)
|
87
|
+
|
88
|
+
subdirs = FTPFile.relative_path(path).split(/\//)
|
89
|
+
subdirs.each do |subdir|
|
90
|
+
next if subdir == ""
|
91
|
+
|
92
|
+
connection.mkdir subdir
|
93
|
+
connection.chdir subdir
|
94
|
+
end
|
95
|
+
else
|
96
|
+
FileUtils.mkdir_p path
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.cp_r(src, dest, options = {})
|
101
|
+
# handle all combinations of copying to/from FTP and local files
|
102
|
+
if ftp_url?(src)
|
103
|
+
if FTPFile.directory?(src)
|
104
|
+
mkdir_p dest
|
105
|
+
|
106
|
+
connection = FTPConnection.connect(src)
|
107
|
+
files = connection.nlst
|
108
|
+
files.each {|file| cp_r "#{src}/#{file}", "#{dest}/#{file}", options}
|
109
|
+
else
|
110
|
+
cp(src, dest, options)
|
111
|
+
end
|
112
|
+
elsif ftp_url?(dest)
|
113
|
+
if FTPFile.directory?(dest)
|
114
|
+
mkdir_p dest
|
115
|
+
|
116
|
+
files = Dir.entries(src)
|
117
|
+
files.each {|file| cp_r "#{src}/#{file}", "#{dest}/#{file}", options}
|
118
|
+
else
|
119
|
+
cp(src, dest, options)
|
120
|
+
end
|
121
|
+
else
|
122
|
+
FileUtils.cp_r src, dest
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.ls(path)
|
127
|
+
if ftp_url?(path)
|
128
|
+
if FTPFile.directory?(path)
|
129
|
+
connection = FTPConnection.connect(path)
|
130
|
+
connection.chdir FTPFile.relative_path(path)
|
131
|
+
|
132
|
+
return connection.nlst
|
133
|
+
else
|
134
|
+
nil
|
135
|
+
end
|
136
|
+
else
|
137
|
+
Dir.entries path
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def self.ftp_url?(str)
|
144
|
+
str.match(/^ftp:\/\//i) ? true : false
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
require 'ftputils/ext/class'
|
150
|
+
require 'ftputils/ftpconnection'
|
151
|
+
require 'ftputils/ftpuri'
|
152
|
+
require 'ftputils/ftpfile'
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe FTPUtils::FTPConnection do
|
4
|
+
before(:each) do
|
5
|
+
FTPUtils::FTPConnection.clear_connection_cache
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "initializing a new FTP connection" do
|
9
|
+
it "should raise an error if it doesn't look like an FTP connection" do
|
10
|
+
lambda do
|
11
|
+
FTPUtils::FTPConnection.connect("path/to/file.txt")
|
12
|
+
end.should raise_error("Invalid FTP URL provided: path/to/file.txt")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should establish a connection to a directory" do
|
16
|
+
mock_connection = mock(Net::FTPFXP)
|
17
|
+
Net::FTPFXP.should_receive(:new).and_return(mock_connection)
|
18
|
+
mock_connection.should_receive(:"passive=").with(true)
|
19
|
+
mock_connection.should_receive(:connect).with("myhost")
|
20
|
+
mock_connection.should_receive(:login).with("admin","test")
|
21
|
+
mock_connection.should_receive(:chdir).with("/")
|
22
|
+
|
23
|
+
FTPUtils::FTPConnection.connect("ftp://admin:test@myhost/path/to/directory")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should establish a connection to a host without a directory name" do
|
27
|
+
mock_connection = mock(Net::FTPFXP)
|
28
|
+
Net::FTPFXP.should_receive(:new).and_return(mock_connection)
|
29
|
+
mock_connection.should_receive(:"passive=").with(true)
|
30
|
+
mock_connection.should_receive(:connect).with("myhost")
|
31
|
+
mock_connection.should_receive(:login).with("admin","test")
|
32
|
+
mock_connection.should_receive(:chdir).with("/")
|
33
|
+
|
34
|
+
FTPUtils::FTPConnection.connect("ftp://admin:test@myhost")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should attempt to reconnect once if there is an error" do
|
38
|
+
mock_connection = mock(Net::FTPFXP)
|
39
|
+
Net::FTPFXP.should_receive(:new).and_return(mock_connection)
|
40
|
+
mock_connection.should_receive(:"passive=").with(true)
|
41
|
+
mock_connection.should_receive(:connect).with("myhost")
|
42
|
+
mock_connection.should_receive(:login).with("admin","test")
|
43
|
+
mock_connection.should_receive(:chdir).with("/").and_raise(Net::FTPTempError)
|
44
|
+
mock_connection_2 = mock(Net::FTPFXP)
|
45
|
+
Net::FTPFXP.should_receive(:new).and_return(mock_connection_2)
|
46
|
+
mock_connection_2.should_receive(:"passive=").with(true)
|
47
|
+
mock_connection_2.should_receive(:connect).with("myhost")
|
48
|
+
mock_connection_2.should_receive(:login).with("admin","test")
|
49
|
+
mock_connection_2.should_receive(:chdir).with("/").and_raise(Net::FTPPermError)
|
50
|
+
|
51
|
+
lambda do
|
52
|
+
FTPUtils::FTPConnection.connect("ftp://admin:test@myhost/path/to/file.txt")
|
53
|
+
end.should raise_error(Net::FTPPermError)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe FTPUtils::FTPURI do
|
4
|
+
describe "checking to see if a path is a directory" do
|
5
|
+
it "should be true when the path is an FTP directory" do
|
6
|
+
mock_uri = mock(FTPUtils::FTPURI, :path => "/path/to/directory")
|
7
|
+
FTPUtils::FTPURI.should_receive(:parse).with("ftp://admin:test@myhost/path/to/directory").and_return(mock_uri)
|
8
|
+
mock_connection = mock(FTPUtils::FTPConnection)
|
9
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@myhost/path/to/directory").and_return(mock_connection)
|
10
|
+
mock_connection.should_receive(:chdir).with("/path/to/directory")
|
11
|
+
|
12
|
+
FTPUtils::FTPFile.directory?("ftp://admin:test@myhost/path/to/directory").should be_true
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should be false when the path is an FTP file" do
|
16
|
+
mock_uri = mock(FTPUtils::FTPURI, :path => "/path/to/file.txt")
|
17
|
+
FTPUtils::FTPURI.should_receive(:parse).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_uri)
|
18
|
+
mock_connection = mock(FTPUtils::FTPConnection)
|
19
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_connection)
|
20
|
+
mock_connection.should_receive(:chdir).with("/path/to/file.txt").and_raise(Net::FTPPermError)
|
21
|
+
|
22
|
+
FTPUtils::FTPFile.directory?("ftp://admin:test@myhost/path/to/file.txt").should be_false
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should check the path using File.directory? if it doesn't look like an FTP URI" do
|
26
|
+
FTPUtils::FTPURI.should_receive(:parse).with("/path/to/file.txt").and_return(nil)
|
27
|
+
File.should_receive(:directory?).with("/path/to/file.txt")
|
28
|
+
|
29
|
+
FTPUtils::FTPFile.directory?("/path/to/file.txt")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "providing the directory name of a path" do
|
34
|
+
it "should provide the directory name of an FTP URI" do
|
35
|
+
mock_uri = mock(FTPUtils::FTPURI, :dirname => "/path/to")
|
36
|
+
FTPUtils::FTPURI.should_receive(:parse).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_uri)
|
37
|
+
|
38
|
+
FTPUtils::FTPFile.dirname("ftp://admin:test@myhost/path/to/file.txt").should == "/path/to"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should provide the directory name of a non-FTP path" do
|
42
|
+
FTPUtils::FTPURI.should_receive(:parse).with("/path/to/file.txt").and_return(nil)
|
43
|
+
File.should_receive(:dirname).with("/path/to/file.txt").and_return("/path/to")
|
44
|
+
|
45
|
+
FTPUtils::FTPFile.dirname("/path/to/file.txt").should == "/path/to"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "checking to see if a file exists" do
|
50
|
+
it "should be true when an FTP URI has size greater than 0" do
|
51
|
+
mock_uri = mock(FTPUtils::FTPURI, :path => "/path/to/file.txt", :dirname => "/path/to", :filename => "file.txt")
|
52
|
+
FTPUtils::FTPURI.should_receive(:parse).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_uri)
|
53
|
+
mock_connection = mock(FTPUtils::FTPConnection)
|
54
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_connection)
|
55
|
+
mock_connection.should_receive(:chdir).with("/path/to")
|
56
|
+
mock_connection.should_receive(:size).with("file.txt").and_return(100)
|
57
|
+
|
58
|
+
FTPUtils::FTPFile.exists?("ftp://admin:test@myhost/path/to/file.txt").should be_true
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should be false when an FTP URI has size less than 0" do
|
62
|
+
mock_uri = mock(FTPUtils::FTPURI, :path => "/path/to/file.txt", :dirname => "/path/to", :filename => "file.txt")
|
63
|
+
FTPUtils::FTPURI.should_receive(:parse).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_uri)
|
64
|
+
mock_connection = mock(FTPUtils::FTPConnection)
|
65
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_connection)
|
66
|
+
mock_connection.should_receive(:chdir).with("/path/to")
|
67
|
+
mock_connection.should_receive(:size).with("file.txt").and_return(-1)
|
68
|
+
|
69
|
+
FTPUtils::FTPFile.exists?("ftp://admin:test@myhost/path/to/file.txt").should be_false
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should pass the path to File.exists? if it's not an FTP URI" do
|
73
|
+
FTPUtils::FTPURI.should_receive(:parse).with("/path/to/file.txt").and_return(nil)
|
74
|
+
File.should_receive(:exists?)
|
75
|
+
|
76
|
+
FTPUtils::FTPFile.exists?("/path/to/file.txt")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "providing the basename" do
|
81
|
+
it "should provide the basename of an FTP URI" do
|
82
|
+
mock_uri = mock(FTPUtils::FTPURI, :filename => "file.txt")
|
83
|
+
FTPUtils::FTPURI.should_receive(:parse).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_uri)
|
84
|
+
|
85
|
+
FTPUtils::FTPFile.basename("ftp://admin:test@myhost/path/to/file.txt").should == "file.txt"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should provide the basename of a non-FTP path" do
|
89
|
+
FTPUtils::FTPURI.should_receive(:parse).with("/path/to/file.txt").and_return(nil)
|
90
|
+
File.should_receive(:basename).with("/path/to/file.txt")
|
91
|
+
|
92
|
+
FTPUtils::FTPFile.basename("/path/to/file.txt")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "providing the relative path" do
|
97
|
+
it "should provide the relative path for an FTP URI" do
|
98
|
+
mock_uri = mock(FTPUtils::FTPURI, :path => "/path/to")
|
99
|
+
FTPUtils::FTPURI.should_receive(:parse).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_uri)
|
100
|
+
|
101
|
+
FTPUtils::FTPFile.relative_path("ftp://admin:test@myhost/path/to/file.txt").should == "/path/to"
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should provide the relative path for a non-FTP path" do
|
105
|
+
FTPUtils::FTPURI.should_receive(:parse).with("/path/to/file.txt").and_return(nil)
|
106
|
+
|
107
|
+
FTPUtils::FTPFile.relative_path("/path/to/file.txt").should be_nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "determining whether a path is a file" do
|
112
|
+
it "should be true for an FTP URI of a file" do
|
113
|
+
mock_uri = mock(FTPUtils::FTPURI, :dirname => "/path/to", :filename => "file.txt")
|
114
|
+
FTPUtils::FTPURI.should_receive(:parse).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_uri)
|
115
|
+
mock_connection = mock(FTPUtils::FTPConnection)
|
116
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_connection)
|
117
|
+
mock_connection.should_receive(:chdir).with("/path/to")
|
118
|
+
mock_connection.should_receive(:size).with("file.txt").and_return(100)
|
119
|
+
|
120
|
+
FTPUtils::FTPFile.file?("ftp://admin:test@myhost/path/to/file.txt").should be_true
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should be false for an FTP URI of a directory" do
|
124
|
+
mock_uri = mock(FTPUtils::FTPURI, :dirname => "/path/to", :filename => "directory")
|
125
|
+
FTPUtils::FTPURI.should_receive(:parse).with("ftp://admin:test@myhost/path/to/directory").and_return(mock_uri)
|
126
|
+
mock_connection = mock(FTPUtils::FTPConnection)
|
127
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@myhost/path/to/directory").and_return(mock_connection)
|
128
|
+
mock_connection.should_receive(:chdir).with("/path/to")
|
129
|
+
mock_connection.should_receive(:size).with("directory").and_raise(Net::FTPPermError)
|
130
|
+
|
131
|
+
FTPUtils::FTPFile.file?("ftp://admin:test@myhost/path/to/directory").should be_false
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should use File.directory? if the path doesn't look like an FTP URI" do
|
135
|
+
FTPUtils::FTPURI.should_receive(:parse).with("/path/to/file.txt").and_return(nil)
|
136
|
+
File.should_receive(:file?).with("/path/to/file.txt")
|
137
|
+
|
138
|
+
FTPUtils::FTPFile.file?("/path/to/file.txt")
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "determining the modification time of a file" do
|
143
|
+
it "should find the modification of an FTP URI" do
|
144
|
+
mock_uri = mock(FTPUtils::FTPURI, :dirname => "/path/to", :filename => "file.txt")
|
145
|
+
FTPUtils::FTPURI.should_receive(:parse).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_uri)
|
146
|
+
mock_connection = mock(FTPUtils::FTPConnection)
|
147
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_connection)
|
148
|
+
mock_connection.should_receive(:chdir).with("/path/to")
|
149
|
+
mock_connection.should_receive(:mtime).with("file.txt").and_return("10:10")
|
150
|
+
|
151
|
+
FTPUtils::FTPFile.mtime("ftp://admin:test@myhost/path/to/file.txt").should == "10:10"
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should use File.mtime if the path is not an FTP URI" do
|
155
|
+
FTPUtils::FTPURI.should_receive(:parse).with("/path/to/file.txt").and_return(nil)
|
156
|
+
File.should_receive(:mtime).with("/path/to/file.txt")
|
157
|
+
|
158
|
+
FTPUtils::FTPFile.mtime("/path/to/file.txt")
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "expanding the path" do
|
163
|
+
it "should do nothing to the path if it's an FTP URI" do
|
164
|
+
mock_uri = mock(FTPUtils::FTPURI, :dirname => "/path/to", :filename => "file.txt")
|
165
|
+
FTPUtils::FTPURI.should_receive(:parse).with("ftp://admin:test@myhost/path/to/file.txt").and_return(mock_uri)
|
166
|
+
|
167
|
+
FTPUtils::FTPFile.expand_path("ftp://admin:test@myhost/path/to/file.txt").should == "ftp://admin:test@myhost/path/to/file.txt"
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should use File.expand_path if it's not an FTP URI" do
|
171
|
+
FTPUtils::FTPURI.should_receive(:parse).with("/path/to/file.txt").and_return(nil)
|
172
|
+
File.should_receive(:expand_path).with("/path/to/file.txt")
|
173
|
+
|
174
|
+
FTPUtils::FTPFile.expand_path("/path/to/file.txt")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
data/spec/ftpuri_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe FTPUtils::FTPURI do
|
4
|
+
it "should create a new URI if a valid FTP directory is provided" do
|
5
|
+
uri = FTPUtils::FTPURI.parse("ftp://admin:test@myhost/path/to/directory")
|
6
|
+
uri.dirname.should == "/path/to"
|
7
|
+
uri.filename.should == "directory"
|
8
|
+
uri.path.should == "/path/to/directory"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should create a new URI if just the hostname is provided" do
|
12
|
+
uri = FTPUtils::FTPURI.parse("ftp://admin:test@myhost")
|
13
|
+
uri.dirname.should == "/"
|
14
|
+
uri.filename.should be_nil
|
15
|
+
uri.path.should == "/"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should create a new URI if a valid FTP file is provided" do
|
19
|
+
uri = FTPUtils::FTPURI.parse("ftp://admin:test@myhost/path/to/file.txt")
|
20
|
+
uri.dirname.should == "/path/to"
|
21
|
+
uri.filename.should == "file.txt"
|
22
|
+
uri.path.should == "/path/to/file.txt"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,387 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "FTPUtils" do
|
4
|
+
describe "copying a file" do
|
5
|
+
describe "from FTP to FTP" do
|
6
|
+
it "should work if a file URIs are specified for the source and destination" do
|
7
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/file1.txt").
|
8
|
+
and_return(false)
|
9
|
+
FTPUtils::FTPFile.should_receive(:dirname).with("ftp://admin:test@host2/file2.txt").
|
10
|
+
and_return("/")
|
11
|
+
FTPUtils::FTPFile.should_receive(:basename).with("ftp://admin:test@host2/file2.txt").
|
12
|
+
and_return("file2.txt")
|
13
|
+
|
14
|
+
mock_src_connection = mock(FTPUtils::FTPConnection)
|
15
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/file1.txt").
|
16
|
+
and_return(mock_src_connection)
|
17
|
+
mock_dest_connection = mock(FTPUtils::FTPConnection)
|
18
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host2/file2.txt").
|
19
|
+
and_return(mock_dest_connection)
|
20
|
+
FTPUtils::FTPFile.should_receive(:relative_path).with("ftp://admin:test@host1/file1.txt").
|
21
|
+
and_return("/file1.txt")
|
22
|
+
mock_src_connection.should_receive(:fxpto).with(mock_dest_connection, "//file2.txt", "/file1.txt")
|
23
|
+
|
24
|
+
FTPUtils.cp "ftp://admin:test@host1/file1.txt", "ftp://admin:test@host2/file2.txt"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should work if a file URI with subdirnames are specified for the source and destination" do
|
28
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/file1.txt").
|
29
|
+
and_return(false)
|
30
|
+
FTPUtils::FTPFile.should_receive(:dirname).with("ftp://admin:test@host2/subdir2/file2.txt").
|
31
|
+
and_return("/subdir2")
|
32
|
+
FTPUtils::FTPFile.should_receive(:basename).with("ftp://admin:test@host2/subdir2/file2.txt").
|
33
|
+
and_return("file2.txt")
|
34
|
+
|
35
|
+
mock_src_connection = mock(FTPUtils::FTPConnection)
|
36
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1/file1.txt").
|
37
|
+
and_return(mock_src_connection)
|
38
|
+
mock_dest_connection = mock(FTPUtils::FTPConnection)
|
39
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host2/subdir2/file2.txt").
|
40
|
+
and_return(mock_dest_connection)
|
41
|
+
FTPUtils::FTPFile.should_receive(:relative_path).with("ftp://admin:test@host1/subdir1/file1.txt").
|
42
|
+
and_return("/subdir1/file1.txt")
|
43
|
+
mock_src_connection.should_receive(:fxpto).with(mock_dest_connection, "/subdir2/file2.txt", "/subdir1/file1.txt")
|
44
|
+
|
45
|
+
FTPUtils.cp "ftp://admin:test@host1/subdir1/file1.txt", "ftp://admin:test@host2/subdir2/file2.txt"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should work if a file URI is specified for the source and a directory for the destination" do
|
49
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/file1.txt").
|
50
|
+
and_return(false)
|
51
|
+
FTPUtils::FTPFile.should_receive(:dirname).with("ftp://admin:test@host2/subdir2").
|
52
|
+
and_return("/subdir2")
|
53
|
+
FTPUtils::FTPFile.should_receive(:basename).with("ftp://admin:test@host2/subdir2").
|
54
|
+
and_return(nil)
|
55
|
+
FTPUtils::FTPFile.should_receive(:basename).with("ftp://admin:test@host1/subdir1/file1.txt").
|
56
|
+
and_return("file1.txt")
|
57
|
+
|
58
|
+
mock_src_connection = mock(FTPUtils::FTPConnection)
|
59
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1/file1.txt").
|
60
|
+
and_return(mock_src_connection)
|
61
|
+
mock_dest_connection = mock(FTPUtils::FTPConnection)
|
62
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host2/subdir2").
|
63
|
+
and_return(mock_dest_connection)
|
64
|
+
FTPUtils::FTPFile.should_receive(:relative_path).with("ftp://admin:test@host1/subdir1/file1.txt").
|
65
|
+
and_return("/subdir1/file1.txt")
|
66
|
+
mock_src_connection.should_receive(:fxpto).with(mock_dest_connection, "/subdir2/file1.txt", "/subdir1/file1.txt")
|
67
|
+
|
68
|
+
FTPUtils.cp "ftp://admin:test@host1/subdir1/file1.txt", "ftp://admin:test@host2/subdir2"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should raise an error if the source URI is a directory" do
|
72
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1").
|
73
|
+
and_return(true)
|
74
|
+
|
75
|
+
lambda do
|
76
|
+
FTPUtils.cp "ftp://admin:test@host1/subdir1", "ftp://admin:test@host2/subdir2"
|
77
|
+
end.should raise_error("src should be a filename, not a directory")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "from FTP to the local filesystem" do
|
82
|
+
it "should work if the destination filename is given" do
|
83
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/file1.txt").
|
84
|
+
and_return(false)
|
85
|
+
|
86
|
+
FTPUtils::FTPFile.should_receive(:basename).with("ftp://admin:test@host1/subdir1/file1.txt").
|
87
|
+
and_return("file1.txt")
|
88
|
+
|
89
|
+
File.should_receive(:directory?).with("/home/me/file2.txt").and_return(false)
|
90
|
+
|
91
|
+
mock_src_connection = mock(FTPUtils::FTPConnection)
|
92
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1/file1.txt").
|
93
|
+
and_return(mock_src_connection)
|
94
|
+
FTPUtils::FTPFile.should_receive(:dirname).with("ftp://admin:test@host1/subdir1/file1.txt").
|
95
|
+
and_return("/subdir1")
|
96
|
+
mock_src_connection.should_receive(:chdir).with("/subdir1")
|
97
|
+
mock_src_connection.should_receive(:getbinaryfile).with("file1.txt", "/home/me/file2.txt", 1024)
|
98
|
+
|
99
|
+
FTPUtils.cp "ftp://admin:test@host1/subdir1/file1.txt", "/home/me/file2.txt"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should work if the destination directory is given" do
|
103
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/file1.txt").
|
104
|
+
and_return(false)
|
105
|
+
|
106
|
+
FTPUtils::FTPFile.should_receive(:basename).with("ftp://admin:test@host1/subdir1/file1.txt").
|
107
|
+
and_return("file1.txt")
|
108
|
+
|
109
|
+
File.should_receive(:directory?).with("/home/me").and_return(true)
|
110
|
+
|
111
|
+
mock_src_connection = mock(FTPUtils::FTPConnection)
|
112
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1/file1.txt").
|
113
|
+
and_return(mock_src_connection)
|
114
|
+
FTPUtils::FTPFile.should_receive(:dirname).with("ftp://admin:test@host1/subdir1/file1.txt").
|
115
|
+
and_return("/subdir1")
|
116
|
+
mock_src_connection.should_receive(:chdir).with("/subdir1")
|
117
|
+
mock_src_connection.should_receive(:getbinaryfile).with("file1.txt", "/home/me/file1.txt", 1024)
|
118
|
+
|
119
|
+
FTPUtils.cp "ftp://admin:test@host1/subdir1/file1.txt", "/home/me"
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should raise an error if the source URI is a directory" do
|
123
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1").
|
124
|
+
and_return(true)
|
125
|
+
|
126
|
+
lambda do
|
127
|
+
FTPUtils.cp "ftp://admin:test@host1/subdir1", "/home/me"
|
128
|
+
end.should raise_error("src should be a filename, not a directory")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "from local filesystem to FTP" do
|
133
|
+
it "should work if the source is a file and the destination is a file" do
|
134
|
+
File.should_receive(:directory?).with("/home/me/file1.txt").and_return(false)
|
135
|
+
FTPUtils::FTPFile.should_receive(:relative_path).with("ftp://admin:test@host2/file2.txt").
|
136
|
+
and_return("/file2.txt")
|
137
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host2/file2.txt").
|
138
|
+
and_return(false)
|
139
|
+
mock_dest_connection = mock(FTPUtils::FTPConnection)
|
140
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host2/file2.txt").
|
141
|
+
and_return(mock_dest_connection)
|
142
|
+
FTPUtils::FTPFile.should_receive(:dirname).with("ftp://admin:test@host2/file2.txt").and_return("/")
|
143
|
+
mock_dest_connection.should_receive(:chdir).with("/")
|
144
|
+
mock_dest_connection.should_receive(:putbinaryfile).with("/home/me/file1.txt", "/file2.txt", 1024)
|
145
|
+
|
146
|
+
FTPUtils.cp "/home/me/file1.txt", "ftp://admin:test@host2/file2.txt"
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should work if the source is a file and the destination is a directory" do
|
150
|
+
File.should_receive(:directory?).with("/home/me/file1.txt").and_return(false)
|
151
|
+
FTPUtils::FTPFile.should_receive(:relative_path).with("ftp://admin:test@host2").
|
152
|
+
and_return("")
|
153
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host2").
|
154
|
+
and_return(true)
|
155
|
+
File.should_receive(:basename).with("/home/me/file1.txt").and_return("file1.txt")
|
156
|
+
mock_dest_connection = mock(FTPUtils::FTPConnection)
|
157
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host2").
|
158
|
+
and_return(mock_dest_connection)
|
159
|
+
FTPUtils::FTPFile.should_receive(:dirname).with("ftp://admin:test@host2").and_return("/")
|
160
|
+
mock_dest_connection.should_receive(:chdir).with("/")
|
161
|
+
mock_dest_connection.should_receive(:putbinaryfile).with("/home/me/file1.txt", "/file1.txt", 1024)
|
162
|
+
|
163
|
+
FTPUtils.cp "/home/me/file1.txt", "ftp://admin:test@host2"
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should raise an error if the source file is a directory" do
|
167
|
+
File.should_receive(:directory?).with("/home/me").and_return(true)
|
168
|
+
|
169
|
+
lambda do
|
170
|
+
FTPUtils.cp "/home/me", "ftp://admin:test@host1/subdir1"
|
171
|
+
end.should raise_error("src should be a filename, not a directory")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "from local filesystem to local filesystem" do
|
176
|
+
it "should fall back on FileUtils.cp" do
|
177
|
+
FileUtils.should_receive(:cp).with("file1.txt", "file2.txt", {})
|
178
|
+
|
179
|
+
FTPUtils.cp "file1.txt", "file2.txt"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "removing a file" do
|
185
|
+
it "should remove an FTP directory" do
|
186
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/file.txt").
|
187
|
+
and_return(false)
|
188
|
+
mock_connection = mock(FTPUtils::FTPConnection)
|
189
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1/file.txt").
|
190
|
+
and_return(mock_connection)
|
191
|
+
FTPUtils::FTPFile.should_receive(:dirname).with("ftp://admin:test@host1/subdir1/file.txt").and_return("/subdir1")
|
192
|
+
mock_connection.should_receive(:chdir).with("/subdir1")
|
193
|
+
mock_connection.should_receive(:delete).with("file.txt")
|
194
|
+
|
195
|
+
FTPUtils.rm "ftp://admin:test@host1/subdir1/file.txt"
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should raise an error if an FTP directory is provided" do
|
199
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1").
|
200
|
+
and_return(true)
|
201
|
+
|
202
|
+
lambda do
|
203
|
+
FTPUtils.rm "ftp://admin:test@host1/subdir1"
|
204
|
+
end.should raise_error("Can't use FTPUtils.rm on directories. Instead use FTPUtils.rm_r")
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should fall back on FileUtils.rm if an FTP URI is not provided" do
|
208
|
+
FileUtils.should_receive(:rm).with("file.txt")
|
209
|
+
|
210
|
+
FTPUtils.rm "file.txt"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe "removing recursively" do
|
215
|
+
it "should remove a directory with nested dirnames and files" do
|
216
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1").
|
217
|
+
and_return(true)
|
218
|
+
mock_connection = mock(FTPUtils::FTPConnection)
|
219
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1").
|
220
|
+
and_return(mock_connection)
|
221
|
+
mock_connection.should_receive(:chdir).with("/subdir1")
|
222
|
+
mock_connection.should_receive(:nlst).and_return( ["subdir2", "file.txt"] )
|
223
|
+
|
224
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/subdir2").
|
225
|
+
and_return(true)
|
226
|
+
mock_subdir_connection = mock(FTPUtils::FTPConnection)
|
227
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1/subdir2").
|
228
|
+
and_return(mock_subdir_connection)
|
229
|
+
mock_subdir_connection.should_receive(:chdir).with("/subdir1/subdir2")
|
230
|
+
mock_subdir_connection.should_receive(:nlst).and_return( [] )
|
231
|
+
FTPUtils::FTPFile.should_receive(:relative_path).with("ftp://admin:test@host1/subdir1/subdir2").twice.
|
232
|
+
and_return("/subdir1/subdir2")
|
233
|
+
mock_subdir_connection.should_receive(:rmdir).with("/subdir1/subdir2")
|
234
|
+
|
235
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/file.txt").
|
236
|
+
and_return(false)
|
237
|
+
FTPUtils.should_receive(:rm).with("ftp://admin:test@host1/subdir1/file.txt")
|
238
|
+
|
239
|
+
FTPUtils::FTPFile.should_receive(:relative_path).with("ftp://admin:test@host1/subdir1").twice.
|
240
|
+
and_return("/subdir1")
|
241
|
+
mock_connection.should_receive(:rmdir).with("/subdir1")
|
242
|
+
|
243
|
+
FTPUtils.rm_r "ftp://admin:test@host1/subdir1"
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should remove a file" do
|
247
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/file.txt").
|
248
|
+
and_return(false)
|
249
|
+
FTPUtils.should_receive(:rm).with("ftp://admin:test@host1/subdir1/file.txt")
|
250
|
+
|
251
|
+
FTPUtils.rm_r "ftp://admin:test@host1/subdir1/file.txt"
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should fall back on FileUtils.rm_r if a non-FTP URI is provided" do
|
255
|
+
FileUtils.should_receive(:rm_r).with("dir")
|
256
|
+
|
257
|
+
FTPUtils.rm_r "dir"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe "create a directory and all its parents" do
|
262
|
+
it "should work on an FTP URI" do
|
263
|
+
mock_connection = mock(FTPUtils::FTPConnection)
|
264
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1/subdir2").
|
265
|
+
and_return(mock_connection)
|
266
|
+
|
267
|
+
FTPUtils::FTPFile.should_receive(:relative_path).with("ftp://admin:test@host1/subdir1/subdir2").
|
268
|
+
and_return("/subdir1/subdir2")
|
269
|
+
|
270
|
+
mock_connection.should_receive(:mkdir).with("subdir1")
|
271
|
+
mock_connection.should_receive(:chdir).with("subdir1")
|
272
|
+
mock_connection.should_receive(:mkdir).with("subdir2")
|
273
|
+
mock_connection.should_receive(:chdir).with("subdir2")
|
274
|
+
|
275
|
+
FTPUtils.mkdir_p "ftp://admin:test@host1/subdir1/subdir2"
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should fall back on FileUtils for a non-FTP URI" do
|
279
|
+
FileUtils.should_receive(:mkdir_p).with("subdir1/subdir2")
|
280
|
+
|
281
|
+
FTPUtils.mkdir_p("subdir1/subdir2")
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "copying recursively" do
|
286
|
+
it "should copy an FTP directory to another FTP directory" do
|
287
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1").
|
288
|
+
and_return(true)
|
289
|
+
FTPUtils.should_receive(:mkdir_p).with("ftp://admin:test@host2/subdir2")
|
290
|
+
mock_src_connection = mock(FTPUtils::FTPConnection)
|
291
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1").
|
292
|
+
and_return(mock_src_connection)
|
293
|
+
mock_src_connection.should_receive(:nlst).and_return( ["subdira", "file.txt"] )
|
294
|
+
|
295
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/subdira").
|
296
|
+
and_return(true)
|
297
|
+
FTPUtils.should_receive(:mkdir_p).with("ftp://admin:test@host2/subdir2/subdira")
|
298
|
+
mock_subdira_connection = mock(FTPUtils::FTPConnection)
|
299
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1/subdira").
|
300
|
+
and_return(mock_subdira_connection)
|
301
|
+
mock_subdira_connection.should_receive(:nlst).and_return([])
|
302
|
+
|
303
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/file.txt").
|
304
|
+
and_return(false)
|
305
|
+
FTPUtils.should_receive(:cp).with("ftp://admin:test@host1/subdir1/file.txt",
|
306
|
+
"ftp://admin:test@host2/subdir2/file.txt", {})
|
307
|
+
|
308
|
+
FTPUtils.cp_r "ftp://admin:test@host1/subdir1", "ftp://admin:test@host2/subdir2"
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should copy an FTP directory to a local directory" do
|
312
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1").
|
313
|
+
and_return(true)
|
314
|
+
FTPUtils.should_receive(:mkdir_p).with("/home/me/subdir2")
|
315
|
+
mock_src_connection = mock(FTPUtils::FTPConnection)
|
316
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1").
|
317
|
+
and_return(mock_src_connection)
|
318
|
+
mock_src_connection.should_receive(:nlst).and_return( ["subdira", "file.txt"] )
|
319
|
+
|
320
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/subdira").
|
321
|
+
and_return(true)
|
322
|
+
FTPUtils.should_receive(:mkdir_p).with("/home/me/subdir2/subdira")
|
323
|
+
mock_subdira_connection = mock(FTPUtils::FTPConnection)
|
324
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1/subdira").
|
325
|
+
and_return(mock_subdira_connection)
|
326
|
+
mock_subdira_connection.should_receive(:nlst).and_return([])
|
327
|
+
|
328
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1/file.txt").
|
329
|
+
and_return(false)
|
330
|
+
FTPUtils.should_receive(:cp).with("ftp://admin:test@host1/subdir1/file.txt",
|
331
|
+
"/home/me/subdir2/file.txt", {})
|
332
|
+
|
333
|
+
FTPUtils.cp_r "ftp://admin:test@host1/subdir1", "/home/me/subdir2"
|
334
|
+
end
|
335
|
+
|
336
|
+
it "should copy a local directory to an FTP directory" do
|
337
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host2/subdir2").
|
338
|
+
and_return(true)
|
339
|
+
FTPUtils.should_receive(:mkdir_p).with("ftp://admin:test@host2/subdir2")
|
340
|
+
Dir.should_receive(:entries).with("/home/me/subdir1").and_return( ["subdira", "file.txt"] )
|
341
|
+
|
342
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host2/subdir2/subdira").
|
343
|
+
and_return(true)
|
344
|
+
FTPUtils.should_receive(:mkdir_p).with("ftp://admin:test@host2/subdir2/subdira")
|
345
|
+
Dir.should_receive(:entries).with("/home/me/subdir1/subdira").and_return([])
|
346
|
+
|
347
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host2/subdir2/file.txt").
|
348
|
+
and_return(false)
|
349
|
+
FTPUtils.should_receive(:cp).with("/home/me/subdir1/file.txt", "ftp://admin:test@host2/subdir2/file.txt", {})
|
350
|
+
|
351
|
+
FTPUtils.cp_r "/home/me/subdir1", "ftp://admin:test@host2/subdir2"
|
352
|
+
end
|
353
|
+
|
354
|
+
it "should copy a local directory to another local directory" do
|
355
|
+
FileUtils.should_receive(:cp_r).with("/home/me/subdir1", "/home/me/subdir2")
|
356
|
+
|
357
|
+
FTPUtils.cp_r "/home/me/subdir1", "/home/me/subdir2"
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
describe "listing the entries in a directory" do
|
362
|
+
it "should return a list of entries in an FTP directory" do
|
363
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/subdir1").
|
364
|
+
and_return(true)
|
365
|
+
mock_connection = mock(FTPUtils::FTPConnection)
|
366
|
+
FTPUtils::FTPConnection.should_receive(:connect).with("ftp://admin:test@host1/subdir1").
|
367
|
+
and_return(mock_connection)
|
368
|
+
mock_connection.should_receive(:chdir).with("/subdir1")
|
369
|
+
mock_connection.should_receive(:nlst).and_return( ["subdir2", "file.txt"] )
|
370
|
+
|
371
|
+
FTPUtils.ls("ftp://admin:test@host1/subdir1").should == ["subdir2", "file.txt"]
|
372
|
+
end
|
373
|
+
|
374
|
+
it "should return nil for an FTP URI that isn't a directory" do
|
375
|
+
FTPUtils::FTPFile.should_receive(:directory?).with("ftp://admin:test@host1/file.txt").
|
376
|
+
and_return(false)
|
377
|
+
|
378
|
+
FTPUtils.ls("ftp://admin:test@host1/file.txt").should == nil
|
379
|
+
end
|
380
|
+
|
381
|
+
it "should use Dir.entries for a non-FTP URI" do
|
382
|
+
Dir.should_receive(:entries).with("/home/me/subdir1")
|
383
|
+
|
384
|
+
FTPUtils.ls("/home/me/subdir1")
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ftputils
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Bruz Marzolf
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-25 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: ftpfxp
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
- 0
|
30
|
+
- 4
|
31
|
+
version: 0.0.4
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rspec
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 1
|
43
|
+
- 2
|
44
|
+
- 9
|
45
|
+
version: 1.2.9
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
description: Implements a subset of the methods in FileUtils for FTP URIs, falling back on FileUtils when a path doesn't look like FTP
|
49
|
+
email: bmarzolf@systemsbiology.org
|
50
|
+
executables: []
|
51
|
+
|
52
|
+
extensions: []
|
53
|
+
|
54
|
+
extra_rdoc_files:
|
55
|
+
- LICENSE
|
56
|
+
- README.rdoc
|
57
|
+
files:
|
58
|
+
- .document
|
59
|
+
- .gitignore
|
60
|
+
- LICENSE
|
61
|
+
- README.rdoc
|
62
|
+
- Rakefile
|
63
|
+
- VERSION
|
64
|
+
- lib/ftputils.rb
|
65
|
+
- lib/ftputils/ext/class.rb
|
66
|
+
- lib/ftputils/ftpconnection.rb
|
67
|
+
- lib/ftputils/ftpfile.rb
|
68
|
+
- lib/ftputils/ftpuri.rb
|
69
|
+
- spec/ftpconnection_spec.rb
|
70
|
+
- spec/ftpfile_spec.rb
|
71
|
+
- spec/ftpuri_spec.rb
|
72
|
+
- spec/ftputils_spec.rb
|
73
|
+
- spec/spec.opts
|
74
|
+
- spec/spec_helper.rb
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: http://github.com/bmarzolf/ftputils
|
77
|
+
licenses: []
|
78
|
+
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options:
|
81
|
+
- --charset=UTF-8
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
version: "0"
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 1.3.6
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: Like FileUtils for FTP
|
105
|
+
test_files:
|
106
|
+
- spec/ftpconnection_spec.rb
|
107
|
+
- spec/ftpfile_spec.rb
|
108
|
+
- spec/ftpuri_spec.rb
|
109
|
+
- spec/ftputils_spec.rb
|
110
|
+
- spec/spec_helper.rb
|