ftp-ext 0.1.1 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +1 -1
- data/LICENSE +1 -1
- data/README.mdown +66 -0
- data/ftp-ext.gemspec +8 -9
- data/lib/ftp-ext.rb +125 -34
- data/test/test_ftp-ext.rb +17 -3
- metadata +7 -8
- data/README.rdoc +0 -22
data/.document
CHANGED
data/LICENSE
CHANGED
data/README.mdown
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
FTPExt
|
2
|
+
======
|
3
|
+
A simple gem for copying, synching, and deleting entire folders over FTP. Adds
|
4
|
+
two new public methods to Net::FTP — <code>put\_dir</code> and
|
5
|
+
<code>rmrf\_dir</code>. <code>put\_dir</code> takes a hash of arguments:
|
6
|
+
|
7
|
+
* local - (_string_) The relative or absolute path to the local directory to
|
8
|
+
be copied.
|
9
|
+
* remote - (_string_) The relative or absolute remote path that the local
|
10
|
+
directory will be copied to.
|
11
|
+
* verbose - (_bool_: defaults to false) Will output progress if set to true.
|
12
|
+
* erase - (_bool_: defaults to false) If set to true, <code>put_dir</code>
|
13
|
+
will overwrite any existing directories (default behavior is to update
|
14
|
+
changed files only).
|
15
|
+
* exclude - (_array_) A list of any files in local directory to be excluded.
|
16
|
+
All file paths in <code>exclude</code> should be relative to <code>local
|
17
|
+
</code>
|
18
|
+
|
19
|
+
<code>rmrf\_dir</code> takes two arguments: the path to the remote directory
|
20
|
+
to be deleted, and a verbose boolean flag (defaulted to false).
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
To use FTPExt, require it and use Net::FTP as normal. Because FTPExt requires
|
24
|
+
Net::FTP for you, you don't need to include it separately.
|
25
|
+
|
26
|
+
require 'rubygems'
|
27
|
+
require 'ftp-ext'
|
28
|
+
|
29
|
+
ftp = Net::FTP.new('ftp/server/address')
|
30
|
+
ftp.login('username', 'password')
|
31
|
+
|
32
|
+
ftp.put_dir(:local => 'path/to/local', :remote => 'path/to/remote')
|
33
|
+
|
34
|
+
For example, imagine I have a local directory,
|
35
|
+
<code>/Users/zpendleton/code</code>, that I want to upload on my server to the
|
36
|
+
<code>/home/zpendleton</code> directory. I'd run the following command:
|
37
|
+
|
38
|
+
ftp.put_dir('/Users/zpendleton/code', '/home/zpendleton/code')
|
39
|
+
|
40
|
+
If the <code>code</code> directory already exists in
|
41
|
+
<code>/home/zpendleton</code>, it will be updated (the script checks the
|
42
|
+
last modified timestamps of all files). If the <code>code</code> directory
|
43
|
+
doesn't exist, it will be created. To overwrite the <code>code</code>
|
44
|
+
directory on the server, set the <code>:erase</code> option in
|
45
|
+
<code>put\_dir</code> to <code>true</code>.
|
46
|
+
|
47
|
+
To later delete the <code>/home/zpendleton/code</code> directory (in verbose
|
48
|
+
mode), you would use:
|
49
|
+
|
50
|
+
ftp.rmrf_dir('/home/zpendleton/code', true)
|
51
|
+
|
52
|
+
To leave verbose mode turned off, you would just omit the second parameter.
|
53
|
+
|
54
|
+
## Contributing
|
55
|
+
1. Fork the project.
|
56
|
+
2. Create a branch (<code>git checkout -b my_edit</code>).
|
57
|
+
3. Commit your changes (<code>git commit -am "Added feature"</code>).
|
58
|
+
4. Push to the branch (<code>git push origin my_edit</code>).
|
59
|
+
5. Create an [issue](http://github.com/zpendleton/ftp-ext/issues) with a link
|
60
|
+
to your branch.
|
61
|
+
6. Kick it old school to [this](http://www.youtube.com/watch?v=jEa1BYBgeQI)
|
62
|
+
while you wait.
|
63
|
+
|
64
|
+
## Copyright
|
65
|
+
|
66
|
+
Copyright (c) 2010 Zach Pendleton. See LICENSE for details.
|
data/ftp-ext.gemspec
CHANGED
@@ -5,22 +5,21 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{ftp-ext}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Zach Pendleton"]
|
12
|
-
s.date = %q{2010-
|
13
|
-
s.description = %q{
|
12
|
+
s.date = %q{2010-12-30}
|
13
|
+
s.description = %q{FTPExt adds two methods (put_dir and rmrf_dir) to Net::FTP to copy, sync, and delete whole folders over FTP.}
|
14
14
|
s.email = %q{zachpendleton@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
|
-
"LICENSE"
|
17
|
-
"README.rdoc"
|
16
|
+
"LICENSE"
|
18
17
|
]
|
19
18
|
s.files = [
|
20
19
|
".document",
|
21
20
|
".gitignore",
|
22
21
|
"LICENSE",
|
23
|
-
"README.
|
22
|
+
"README.mdown",
|
24
23
|
"Rakefile",
|
25
24
|
"VERSION",
|
26
25
|
"ftp-ext.gemspec",
|
@@ -32,7 +31,7 @@ Gem::Specification.new do |s|
|
|
32
31
|
s.rdoc_options = ["--charset=UTF-8"]
|
33
32
|
s.require_paths = ["lib"]
|
34
33
|
s.rubygems_version = %q{1.3.7}
|
35
|
-
s.summary = %q{Folder copying methods for Net::FTP}
|
34
|
+
s.summary = %q{Folder copying and deletion methods for Net::FTP}
|
36
35
|
s.test_files = [
|
37
36
|
"test/helper.rb",
|
38
37
|
"test/test_ftp-ext.rb"
|
@@ -46,11 +45,11 @@ Gem::Specification.new do |s|
|
|
46
45
|
s.add_development_dependency(%q<shoulda>, [">= 2.11.3"])
|
47
46
|
s.add_dependency(%q<ptools>, [">= 1.1.9"])
|
48
47
|
else
|
49
|
-
s.
|
48
|
+
s.add_development_dependency(%q<shoulda>, [">= 2.11.3"])
|
50
49
|
s.add_dependency(%q<ptools>, [">= 1.1.9"])
|
51
50
|
end
|
52
51
|
else
|
53
|
-
s.
|
52
|
+
s.add_development_dependency(%q<shoulda>, [">= 2.11.3"])
|
54
53
|
s.add_dependency(%q<ptools>, [">= 1.1.9"])
|
55
54
|
end
|
56
55
|
end
|
data/lib/ftp-ext.rb
CHANGED
@@ -1,56 +1,147 @@
|
|
1
|
-
%w
|
1
|
+
%w(rubygems net/ftp ptools).each do |lib|
|
2
2
|
require lib
|
3
3
|
end
|
4
4
|
|
5
5
|
module Net
|
6
6
|
class FTP
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
def put_dir(opts = {})
|
8
|
+
# Set default options
|
9
|
+
options = {
|
10
|
+
:local => ".",
|
11
|
+
:remote => ".",
|
12
|
+
:verbose => false,
|
13
|
+
:erase => false,
|
14
|
+
:exclude => []
|
15
|
+
}.merge!(opts)
|
10
16
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
# Format options[:exclude]
|
18
|
+
unless options[:preformatted]
|
19
|
+
options[:exclude].map! { |f| File.join(options[:local], f) }
|
20
|
+
options[:preformatted] = true
|
21
|
+
end
|
22
|
+
|
23
|
+
# Check for existence of directory on remote server
|
24
|
+
# and create it if it isn't there. If it does exist,
|
25
|
+
# call sync_dir instead.
|
26
|
+
if !remote_file_exists?(options[:remote])
|
27
|
+
file_segments = options[:remote].split(File::SEPARATOR)
|
28
|
+
|
29
|
+
# Make directory and all missing parent directories
|
30
|
+
file_segments.length.times do |n|
|
31
|
+
fpath = File.join(file_segments[0..n])
|
32
|
+
|
33
|
+
unless remote_file_exists?(fpath) || fpath == ""
|
34
|
+
puts "mkdir #{fpath}" if options[:verbose] == true
|
35
|
+
mkdir(fpath)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
else
|
39
|
+
options[:erase] ? (rmrf_dir(options[:remote], options[:verbose]); put_dir(options)) : sync_dir(options)
|
40
|
+
return
|
41
|
+
end
|
42
|
+
|
43
|
+
# Upload files recursively
|
44
|
+
Dir.foreach(options[:local]) do |f|
|
45
|
+
local = File.join(options[:local], f)
|
46
|
+
remote = File.join(options[:remote], f)
|
47
|
+
|
48
|
+
(puts "excluding #{local}"; next) if options[:exclude].include?(local)
|
49
|
+
|
50
|
+
if File.file?(local)
|
51
|
+
puts "cp #{remote}" if options[:verbose] == true
|
52
|
+
File.binary?(local) ? putbinaryfile(local, remote) : puttextfile(local, remote)
|
53
|
+
|
54
|
+
# ignore . and .., but upload other directories
|
20
55
|
elsif !f.match(/^\.+$/)
|
21
|
-
put_dir(
|
56
|
+
put_dir(options.merge({ :local => local, :remote => remote }))
|
22
57
|
end
|
58
|
+
|
23
59
|
end
|
24
|
-
|
25
|
-
Dir.chdir('..')
|
26
|
-
chdir('..')
|
60
|
+
|
27
61
|
end
|
28
|
-
|
29
|
-
def
|
30
|
-
|
62
|
+
|
63
|
+
def rmrf_dir(dir, verbose = false)
|
64
|
+
return if !remote_file_exists?(dir)
|
65
|
+
|
66
|
+
# Get file list from the server and begin deleting files
|
67
|
+
begin
|
68
|
+
flist = nlst(dir)
|
69
|
+
|
70
|
+
flist.each do |f|
|
71
|
+
# Ignore . and ..
|
72
|
+
next if f.match(/^\.+$/)
|
73
|
+
|
74
|
+
# Try to delete the file. If we fail then it's a directory
|
75
|
+
begin
|
76
|
+
delete(f)
|
77
|
+
puts "rm #{f}" if verbose
|
78
|
+
rescue Exception => e
|
79
|
+
rmrf_dir(f, verbose)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
rescue Exception => e
|
84
|
+
# If the directory is empty, error silently and continue execution
|
85
|
+
end
|
31
86
|
|
32
|
-
|
33
|
-
|
87
|
+
rmdir(dir)
|
88
|
+
puts "rm -rf #{dir}" if verbose
|
34
89
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
90
|
+
end
|
91
|
+
|
92
|
+
protected
|
93
|
+
|
94
|
+
def sync_dir(options = {})
|
95
|
+
# Loop through each directory and compare/update the files
|
96
|
+
Dir.foreach(options[:local]) do |f|
|
97
|
+
local = File.join(options[:local], f)
|
98
|
+
remote = File.join(options[:remote], f)
|
99
|
+
|
100
|
+
(puts "excluding #{local}" if options[:verbose] == true; next) if options[:exclude].include?(local)
|
101
|
+
|
102
|
+
if File.file?(local)
|
103
|
+
if !remote_file_exists?(remote) || mtime(remote) < File.mtime(local)
|
104
|
+
puts "updating #{remote}" if options[:verbose] == true
|
105
|
+
File.binary?(local) ? putbinaryfile(local, remote) : puttextfile(local, remote)
|
106
|
+
|
107
|
+
else
|
108
|
+
puts "skipping #{remote}" if options[:verbose] == true
|
39
109
|
end
|
40
110
|
elsif !f.match(/^\.+$/)
|
41
|
-
|
42
|
-
sync_dir(File.path(f), f)
|
111
|
+
put_dir(options.merge(:local => local, :remote => remote))
|
43
112
|
end
|
113
|
+
|
44
114
|
end
|
45
115
|
|
46
|
-
Dir.chdir('..')
|
47
|
-
chdir('..')
|
48
116
|
end
|
49
117
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
118
|
+
def remote_file_exists?(file)
|
119
|
+
fpath = file.split(File::SEPARATOR)
|
120
|
+
|
121
|
+
# If we're only one directory deep, don't try to join anything
|
122
|
+
if fpath.length == 1
|
123
|
+
# starting at file system root?
|
124
|
+
if file.match(/^#{File::SEPARATOR}/)
|
125
|
+
return nlst(File::SEPARATOR).include?(file)
|
126
|
+
# starting relative to FTP dir
|
127
|
+
else
|
128
|
+
return nlst.include?(file)
|
129
|
+
end
|
130
|
+
# Nested directories
|
131
|
+
else
|
132
|
+
fname = fpath.pop
|
133
|
+
fpath.map! { |v| v == "" ? File::SEPARATOR : v }
|
134
|
+
fpath = fpath.join(File::SEPARATOR)
|
135
|
+
|
136
|
+
# Wrap return in begin..rescue because nlst errors on empty directory
|
137
|
+
begin
|
138
|
+
return nlst(fpath).include?(File.join(fpath, fname))
|
139
|
+
rescue Exception => e
|
140
|
+
return false if e.message.match("450")
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
54
145
|
end
|
55
146
|
|
56
147
|
end
|
data/test/test_ftp-ext.rb
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
class
|
4
|
-
|
5
|
-
|
3
|
+
class FTPExtTest < Test::Unit::TestCase
|
4
|
+
context "FTPExt should" do
|
5
|
+
|
6
|
+
setup do
|
7
|
+
@src = 'htdocs'
|
8
|
+
end
|
9
|
+
|
10
|
+
should "upload a directory to a linux machine" do
|
11
|
+
ftp = Net::FTP.new('localhost')
|
12
|
+
ftp.login('testuser', 'testpass')
|
13
|
+
|
14
|
+
ftp.put_dir(@src)
|
15
|
+
puts ftp.nlst
|
16
|
+
assert true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ftp-ext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 29
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 3
|
10
|
+
version: 0.1.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Zach Pendleton
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-12-30 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -50,7 +50,7 @@ dependencies:
|
|
50
50
|
version: 1.1.9
|
51
51
|
type: :runtime
|
52
52
|
version_requirements: *id002
|
53
|
-
description:
|
53
|
+
description: FTPExt adds two methods (put_dir and rmrf_dir) to Net::FTP to copy, sync, and delete whole folders over FTP.
|
54
54
|
email: zachpendleton@gmail.com
|
55
55
|
executables: []
|
56
56
|
|
@@ -58,12 +58,11 @@ extensions: []
|
|
58
58
|
|
59
59
|
extra_rdoc_files:
|
60
60
|
- LICENSE
|
61
|
-
- README.rdoc
|
62
61
|
files:
|
63
62
|
- .document
|
64
63
|
- .gitignore
|
65
64
|
- LICENSE
|
66
|
-
- README.
|
65
|
+
- README.mdown
|
67
66
|
- Rakefile
|
68
67
|
- VERSION
|
69
68
|
- ftp-ext.gemspec
|
@@ -103,7 +102,7 @@ rubyforge_project:
|
|
103
102
|
rubygems_version: 1.3.7
|
104
103
|
signing_key:
|
105
104
|
specification_version: 3
|
106
|
-
summary: Folder copying methods for Net::FTP
|
105
|
+
summary: Folder copying and deletion methods for Net::FTP
|
107
106
|
test_files:
|
108
107
|
- test/helper.rb
|
109
108
|
- test/test_ftp-ext.rb
|
data/README.rdoc
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
= ftp-ext
|
2
|
-
|
3
|
-
A simple gem for pushing and syncing entire folders over FTP. Adds two new
|
4
|
-
public methods to Net::FTP -- put_dir and sync_dir. Both take two arguments:
|
5
|
-
the local folder to be pushed, and the remote folder to be synced to.
|
6
|
-
|
7
|
-
Right now it's missing any error checking, but is useful for basic operations
|
8
|
-
in controlled environments.
|
9
|
-
|
10
|
-
== Note on Patches/Pull Requests
|
11
|
-
|
12
|
-
* Fork the project.
|
13
|
-
* Make your feature addition or bug fix.
|
14
|
-
* Add tests for it. This is important so I don't break it in a
|
15
|
-
future version unintentionally.
|
16
|
-
* Commit, do not mess with rakefile, version, or history.
|
17
|
-
(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)
|
18
|
-
* Send me a pull request. Bonus points for topic branches.
|
19
|
-
|
20
|
-
== Copyright
|
21
|
-
|
22
|
-
Copyright (c) 2010 Zach Pendleton. See LICENSE for details.
|