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 CHANGED
@@ -1,4 +1,4 @@
1
- README.rdoc
1
+ README.mdown
2
2
  lib/**/*.rb
3
3
  bin/*
4
4
  features/**/*.feature
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Zach Pendleton
1
+ Copyright (c) 2010 Zach Pendleton
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -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 &mdash; <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.
@@ -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.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-10-12}
13
- s.description = %q{ftp-ext adds two methods (sync_dir and put_dir) to Net::FTP to copy and sync whole folders over FTP.}
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.rdoc",
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.add_dependency(%q<shoulda>, [">= 2.11.3"])
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.add_dependency(%q<shoulda>, [">= 2.11.3"])
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
@@ -1,56 +1,147 @@
1
- %w{rubygems net/ftp ptools}.each do |lib|
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
- def put_dir(local_folder, remote_folder = '.')
9
- rmdir(File.join(remote_folder, local_folder)) if file_exists? File.join(remote_folder, local_folder)
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
- Dir.chdir(local_folder)
12
- chdir(remote_folder)
13
-
14
- mkdir(File.basename(local_folder))
15
- chdir(File.basename(local_folder))
16
-
17
- Dir.foreach('.') do |f|
18
- if File.file?(f)
19
- File.binary?(f) ? putbinaryfile(f) : puttextfile(f)
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(File.path(f))
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 sync_dir(local_folder, remote_folder = '.')
30
- mkdir(remote_folder) if !file_exists?(remote_folder)
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
- Dir.chdir(local_folder)
33
- chdir(remote_folder)
87
+ rmdir(dir)
88
+ puts "rm -rf #{dir}" if verbose
34
89
 
35
- Dir.foreach('.') do |f|
36
- if File.file?(f)
37
- if !file_exists?(f) || mtime(f) < File.mtime(f)
38
- File.binary?(f) ? putbinaryfile(f) : puttextfile(f)
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
- mkdir(File.basename(f)) if !file_exists?(f)
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
- protected
51
- def file_exists?(file)
52
- list = nlst
53
- nlst.include?(file)
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
@@ -1,5 +1,19 @@
1
1
  require 'helper'
2
2
 
3
- class TestFtpExt < Test::Unit::TestCase
4
-
5
- end
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: 25
4
+ hash: 29
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 1
10
- version: 0.1.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-10-12 00:00:00 -06:00
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: ftp-ext adds two methods (sync_dir and put_dir) to Net::FTP to copy and sync whole folders over FTP.
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.rdoc
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
@@ -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.