ftputils 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
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
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
@@ -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
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'ftputils'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
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