ftp-ext 0.1.1 → 0.1.3

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 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.