gruner-smurftp 0.5.1
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/README.mkdn +59 -0
- data/VERSION.yml +4 -0
- data/bin/smurftp +30 -0
- data/lib/smurftp.rb +35 -0
- data/lib/smurftp/configuration.rb +45 -0
- data/lib/smurftp/shell.rb +268 -0
- data/lib/smurftp/templates/smurftp_config.yaml +10 -0
- data/lib/smurftp/templates/smurftp_multisite_config.yaml +41 -0
- data/lib/smurftp/version.rb +34 -0
- data/test/configuration_test.rb +29 -0
- data/test/shell_test.rb +91 -0
- metadata +66 -0
data/README.mkdn
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Smurftp
|
2
|
+
|
3
|
+
Smurftp is a command-line utility written in Ruby that searches a specified directory and creates a queue of recently modified files for quickly uploading to a remote server over FTP.
|
4
|
+
|
5
|
+
It was written for making web site edits where you might have a small group of files in multiple subdirectories that you want uploaded without having to manually go directory by directory.
|
6
|
+
|
7
|
+
## Install
|
8
|
+
|
9
|
+
$ gem sources -a http://gems.github.com
|
10
|
+
$ sudo gem install divineflame-smurftp
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
Start Smurftp with this command:
|
15
|
+
|
16
|
+
$ smurftp <directory or configuration file>
|
17
|
+
|
18
|
+
## Configuration
|
19
|
+
|
20
|
+
Smurftp requires a configuration file in YAML format that defines your FTP server and login information, as well as a local directory ('document_root').
|
21
|
+
|
22
|
+
When starting Smurftp, if you specify a directory, it looks for a 'smurftp_config.yaml' file in that directory. If the file is not found you'll be given the option for this file to be generated for you.
|
23
|
+
|
24
|
+
Alternatively, if you specify an existing configuration file (it doesn't require the 'smurftp_config.yaml' name) Smurftp will start by loading this file. Because the 'document_root' setting is defined in the configuration file, you can store the configuration file separately from your project files if you choose.
|
25
|
+
|
26
|
+
It's also possible to define multiple servers, sites, and login credentials in the same configuration file.
|
27
|
+
|
28
|
+
## File List
|
29
|
+
|
30
|
+
Smurftp will search the defined 'document_root' directory and list all files in all sub directories, ordered by modification date with newest files first. It skips any files defined in the configuration's 'exclusions' list.
|
31
|
+
|
32
|
+
### Sample Output
|
33
|
+
|
34
|
+
[1] foo.php
|
35
|
+
[2] images/bar.jpg
|
36
|
+
[3] includes/header.php
|
37
|
+
[4] images/logo.png
|
38
|
+
[5] yada.php
|
39
|
+
====================
|
40
|
+
smurftp>
|
41
|
+
|
42
|
+
At the `smurftp>` prompt enter the number identifier of the files you want uploaded. You can also enter a list or range of files.
|
43
|
+
|
44
|
+
### Example Commands
|
45
|
+
|
46
|
+
'1' uploads file [1]
|
47
|
+
'1-5' uploads files [1-5]
|
48
|
+
'1,2,4' uploads [1], [2], and [4]
|
49
|
+
'1-5,^4' uploads files [1-3], and [5], skipping file [4]
|
50
|
+
'1-5,!4' same as above
|
51
|
+
'all' uploads all listed files
|
52
|
+
|
53
|
+
To quit type `quit` or `exit` at the prompt. `e` or `q` will also quit.
|
54
|
+
|
55
|
+
## TODO
|
56
|
+
|
57
|
+
* support relative paths for :document_root
|
58
|
+
* add optional sftp
|
59
|
+
* automatically refresh file list
|
data/VERSION.yml
ADDED
data/bin/smurftp
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../lib/smurftp'
|
4
|
+
|
5
|
+
input = ARGV[0]
|
6
|
+
site = ARGV[1]
|
7
|
+
|
8
|
+
if !input
|
9
|
+
puts "Please specify a directory or configuration file to run smurftp from"
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
|
13
|
+
config_file = ''
|
14
|
+
|
15
|
+
if File.directory?(input)
|
16
|
+
directory = input
|
17
|
+
config_file = "#{input}/smurftp_config.yaml"
|
18
|
+
unless File.file?(config_file)
|
19
|
+
Smurftp::Configuration.generate_config_file(directory)
|
20
|
+
exit
|
21
|
+
end
|
22
|
+
elsif File.file?(input)
|
23
|
+
config_file = input
|
24
|
+
else
|
25
|
+
puts "Invalid directory or configuration file."
|
26
|
+
exit
|
27
|
+
end
|
28
|
+
|
29
|
+
smurftp = Smurftp::Shell.new(config_file, site)
|
30
|
+
smurftp.run()
|
data/lib/smurftp.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'find'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'net/ftp'
|
5
|
+
require 'readline'
|
6
|
+
# require 'rubygems'
|
7
|
+
# other dependencies
|
8
|
+
|
9
|
+
# a bit of monkey patching
|
10
|
+
|
11
|
+
class String
|
12
|
+
def to_regex!
|
13
|
+
return /#{self}/
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
class Hash
|
19
|
+
# Destructively convert all keys to symbols.
|
20
|
+
def symbolize_keys!
|
21
|
+
self.each do |key, value|
|
22
|
+
unless key.is_a?(Symbol)
|
23
|
+
self[key.to_sym] = self[key]
|
24
|
+
delete(key)
|
25
|
+
end
|
26
|
+
# recursively call this method on nested hashes
|
27
|
+
value.symbolize_keys! if value.class == Hash
|
28
|
+
end
|
29
|
+
self
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
%w(version configuration shell).each do |file|
|
34
|
+
require File.join(File.dirname(__FILE__), 'smurftp', file)
|
35
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Smurftp
|
2
|
+
class Configuration < Hash
|
3
|
+
|
4
|
+
def self.generate_config_file(dir)
|
5
|
+
# TODO ask before creating new file
|
6
|
+
# TODO fill out the config file by promption user for the info
|
7
|
+
templates_dir = File.dirname(__FILE__) + '/templates'
|
8
|
+
FileUtils.cp("#{templates_dir}/smurftp_config.yaml", "#{dir}/smurftp_config.yaml")
|
9
|
+
puts "No configuration file found. Creating new file."
|
10
|
+
puts "New configuration file created in #{dir}."
|
11
|
+
puts "Enter server and login info in this file and restart smurftp."
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def initialize(file, site=nil)
|
16
|
+
load_config_file(file)
|
17
|
+
if site # merge config settings with current site
|
18
|
+
tmp = self[site]
|
19
|
+
self.clear.merge! tmp
|
20
|
+
end
|
21
|
+
self.symbolize_keys!
|
22
|
+
validate
|
23
|
+
self[:exclusions] << file #exclude config file from upload if it's in the @base_dir
|
24
|
+
self[:queue_limit] ||= 15
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def load_config_file(file)
|
29
|
+
self.merge! YAML::load(File.open(file))
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def validate
|
34
|
+
%w[server server_root document_root login password].each do |setting|
|
35
|
+
unless self[setting.to_sym]
|
36
|
+
raise StandardError, "Error: \"#{setting}\" is missing from configuration file."
|
37
|
+
end
|
38
|
+
end
|
39
|
+
unless File.directory?(self[:document_root])
|
40
|
+
raise StandardError, "Error: \"#{self[:document_root]}\" specified in configuration file is not a valid directory."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
# Smurftp::Shell is used to create an interactive shell. It is invoked by the smurftp binary.
|
2
|
+
module Smurftp
|
3
|
+
class Shell
|
4
|
+
|
5
|
+
attr_reader :upload_queue, :file_list
|
6
|
+
|
7
|
+
def initialize(config_file, site=nil)
|
8
|
+
#Readline.basic_word_break_characters = ""
|
9
|
+
#Readline.completion_append_character = nil
|
10
|
+
@configuration = Smurftp::Configuration.new(config_file, site)
|
11
|
+
@base_dir = @configuration[:document_root]
|
12
|
+
@file_list = []
|
13
|
+
@upload_queue = []
|
14
|
+
@last_upload = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
# Run a single command.
|
19
|
+
def parse_command(cmd)
|
20
|
+
case cmd.downcase
|
21
|
+
when /^(a|all)$/
|
22
|
+
return lambda { upload_all }
|
23
|
+
when /\d+,/ # digit comma
|
24
|
+
files = parse_list(cmd)
|
25
|
+
command = lambda { upload }
|
26
|
+
return command, files
|
27
|
+
when /\d+(\.+|-)\d+/
|
28
|
+
files = parse_range(cmd)
|
29
|
+
command = lambda { upload }
|
30
|
+
return command, files
|
31
|
+
when /^\d+/
|
32
|
+
file = [cmd]
|
33
|
+
command = lambda { upload }
|
34
|
+
return command, file
|
35
|
+
# when /^(m|more)/: list_more_queued_files
|
36
|
+
when /^(r|refresh|l|ls|list)$/
|
37
|
+
command = lambda do
|
38
|
+
find_files
|
39
|
+
refresh_file_display
|
40
|
+
end
|
41
|
+
return command
|
42
|
+
else
|
43
|
+
return 'error'
|
44
|
+
# TODO needs error message as fallback
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# Run the interactive shell using readline.
|
50
|
+
def run
|
51
|
+
find_files
|
52
|
+
refresh_file_display
|
53
|
+
loop do
|
54
|
+
cmd = Readline.readline('smurftp> ')
|
55
|
+
finish if cmd.nil? or cmd =~ /^(e|exit|q|quit)$/
|
56
|
+
next if cmd == ""
|
57
|
+
Readline::HISTORY.push(cmd)
|
58
|
+
command, files = parse_command(cmd)
|
59
|
+
if files
|
60
|
+
add_files_to_queue(files)
|
61
|
+
end
|
62
|
+
command.call
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
##
|
68
|
+
# Adds single file to upload queue, ensuring
|
69
|
+
# no duplicate additions
|
70
|
+
|
71
|
+
def add_file_to_queue(str)
|
72
|
+
str.gsub!(/[^\d]/, '') #strip non-digit characters
|
73
|
+
file = str.to_i-1
|
74
|
+
@upload_queue << file unless @upload_queue.include?(file)
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def add_files_to_queue(files)
|
79
|
+
files.each {|f| add_file_to_queue(f)}
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
##
|
84
|
+
# Extract a list of comma separated values from a string.
|
85
|
+
# Look for ranges and expand them, look for exceptions
|
86
|
+
# and remove them from the returned list.
|
87
|
+
|
88
|
+
def parse_list(str)
|
89
|
+
file_list = []
|
90
|
+
exceptions = []
|
91
|
+
str.split(',').each do |s|
|
92
|
+
if s =~ /-/
|
93
|
+
file_list += parse_range(s)
|
94
|
+
elsif s =~ /(\^|!)\d/
|
95
|
+
s.gsub!(/[^\d]/, '') #strip non-digit characters
|
96
|
+
exceptions << s
|
97
|
+
else
|
98
|
+
file_list << s
|
99
|
+
end
|
100
|
+
end
|
101
|
+
return file_list - exceptions
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
##
|
106
|
+
# Extract a range of numbers from a string.
|
107
|
+
# Expand the range into an array that represents files
|
108
|
+
# in the displayed list, and returns said array.
|
109
|
+
|
110
|
+
def parse_range(str)
|
111
|
+
delimiters = str.split(/\.+|-+/)
|
112
|
+
r_start, r_end = delimiters[0], delimiters[1]
|
113
|
+
# TODO assumes even number pairs for creating a range
|
114
|
+
range = r_start.to_i..r_end.to_i
|
115
|
+
file_list = []
|
116
|
+
range.each do |n|
|
117
|
+
file_list << n.to_s
|
118
|
+
end
|
119
|
+
return file_list
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
def list_more_queued_files
|
124
|
+
# TODO
|
125
|
+
# not sure how this will work yet
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
##
|
130
|
+
# Format the output of the file list display by looping over
|
131
|
+
# @file_list and numbering each file up to the predefined queue limit.
|
132
|
+
|
133
|
+
def refresh_file_display
|
134
|
+
if @last_upload
|
135
|
+
puts 'Files changed since last upload:'
|
136
|
+
else
|
137
|
+
puts 'Recently modified files:'
|
138
|
+
end
|
139
|
+
|
140
|
+
file_count = 1
|
141
|
+
@file_list.each do |f|
|
142
|
+
unless file_count > @configuration[:queue_limit]
|
143
|
+
spacer = ' ' unless file_count > 9 #add space to even the file numbering column
|
144
|
+
puts "#{spacer}[#{file_count}] #{f[:base_name]}"
|
145
|
+
file_count += 1
|
146
|
+
else
|
147
|
+
remaining_files = @file_list.length - file_count
|
148
|
+
puts "(plus #{remaining_files} more)"
|
149
|
+
break
|
150
|
+
end
|
151
|
+
end
|
152
|
+
puts '===================='
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
##
|
157
|
+
# Find the files to process, ignoring temporary files, source
|
158
|
+
# configuration management files, etc., and add them to @file_list mapping
|
159
|
+
# filename to modification time.
|
160
|
+
|
161
|
+
def find_files
|
162
|
+
@file_list.clear
|
163
|
+
Find.find(@base_dir) do |f|
|
164
|
+
|
165
|
+
@configuration[:exclusions].each do |e|
|
166
|
+
Find.prune if f =~ e.to_regex!
|
167
|
+
# if e.class == Regexp
|
168
|
+
# Find.prune if f =~ e.to_regex!
|
169
|
+
# end
|
170
|
+
end
|
171
|
+
|
172
|
+
next if f =~ /(swp|~|rej|orig|bak|.git)$/ # temporary/patch files
|
173
|
+
next if f =~ /\/\.?#/ # Emacs autosave/cvs merge files
|
174
|
+
next if File.directory?(f) #skip directories
|
175
|
+
|
176
|
+
#TODO loop through exclusions that are regex objects
|
177
|
+
|
178
|
+
file_name = f.sub(/^\.\//, '')
|
179
|
+
mtime = File.stat(file_name).mtime
|
180
|
+
base_name = file_name.sub("#{@base_dir}/", '')
|
181
|
+
|
182
|
+
if @last_upload
|
183
|
+
if mtime > @last_upload
|
184
|
+
@file_list << {:name => file_name,
|
185
|
+
:base_name => base_name,
|
186
|
+
:mtime => mtime} rescue next
|
187
|
+
end
|
188
|
+
else #get all files, because we haven't uploaded yet
|
189
|
+
@file_list << {:name => file_name,
|
190
|
+
:base_name => base_name,
|
191
|
+
:mtime => mtime} rescue next
|
192
|
+
end
|
193
|
+
end
|
194
|
+
# sort list by mtime
|
195
|
+
@file_list.sort! { |x,y| y[:mtime] <=> x[:mtime] }
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
def upload
|
200
|
+
#TODO add timeout error handling
|
201
|
+
created_dirs = []
|
202
|
+
Net::FTP.open(@configuration[:server]) do |ftp|
|
203
|
+
ftp.login(@configuration[:login], @configuration[:password])
|
204
|
+
@upload_queue.each do |file_id|
|
205
|
+
file = @file_list[file_id]
|
206
|
+
|
207
|
+
dirs = parse_file_for_sub_dirs(file[:base_name])
|
208
|
+
dirs.each do |dir|
|
209
|
+
unless created_dirs.include? dir
|
210
|
+
begin
|
211
|
+
ftp.mkdir "#{@configuration[:server_root]}/#{dir}"
|
212
|
+
puts "created #{dir}..."
|
213
|
+
rescue Net::FTPPermError; end #ignore errors for existing dirs
|
214
|
+
created_dirs << dir
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
puts "uploading #{file[:base_name]}..."
|
219
|
+
ftp.put("#{file[:name]}", "#{@configuration[:server_root]}/#{file[:base_name]}")
|
220
|
+
# @file_list.delete_at file_id
|
221
|
+
# @upload_queue.delete file_id
|
222
|
+
end
|
223
|
+
end
|
224
|
+
@upload_queue.clear
|
225
|
+
puts "done"
|
226
|
+
@last_upload = Time.now
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
def upload_all
|
231
|
+
@file_list.length.times { |f| @upload_queue << f+1 }
|
232
|
+
upload
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
def parse_file_for_sub_dirs(file)
|
237
|
+
dirs = file.split(/\//)
|
238
|
+
return [] if dirs.length <= 1
|
239
|
+
dirs_expanded = []
|
240
|
+
|
241
|
+
while dirs.length > 1
|
242
|
+
dirs.pop
|
243
|
+
dirs_expanded << dirs.join('/')
|
244
|
+
end
|
245
|
+
|
246
|
+
return dirs_expanded.reverse
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
##
|
251
|
+
# Close the shell and exit the program with a cheesy message.
|
252
|
+
|
253
|
+
def finish
|
254
|
+
messages =
|
255
|
+
[
|
256
|
+
'Hasta La Vista, Baby!',
|
257
|
+
'Peace Out, Dawg!',
|
258
|
+
'Diggidy!',
|
259
|
+
'Up, up, and away!',
|
260
|
+
'Sally Forth Good Sir!'
|
261
|
+
]
|
262
|
+
random_msg = messages[rand(messages.length)]
|
263
|
+
puts random_msg
|
264
|
+
exit
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
268
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# smurftp multisite configuration
|
2
|
+
|
3
|
+
# multiple servers can be configured
|
4
|
+
# and shared across site settings
|
5
|
+
|
6
|
+
server1: &server1
|
7
|
+
server: ftp.yourserver.com
|
8
|
+
login: your_login
|
9
|
+
password: your_password
|
10
|
+
|
11
|
+
global_exclusions: &exclusions
|
12
|
+
exclusions:
|
13
|
+
- '_notes'
|
14
|
+
- 'resources'
|
15
|
+
- '/regex/'
|
16
|
+
|
17
|
+
# define multiple sites which can
|
18
|
+
# share server info and global_exclusions
|
19
|
+
# in addition to defining their own
|
20
|
+
|
21
|
+
site1:
|
22
|
+
<<: *server1
|
23
|
+
<<: *exclusions
|
24
|
+
server_root: 'web/'
|
25
|
+
document_root: '.'
|
26
|
+
|
27
|
+
site2:
|
28
|
+
<<: *server1
|
29
|
+
exclusions:
|
30
|
+
- 'psd'
|
31
|
+
- 'src'
|
32
|
+
server_root: 'web/'
|
33
|
+
document_root: '.'
|
34
|
+
|
35
|
+
site3:
|
36
|
+
server: ftp.anotherserver.com
|
37
|
+
login: site3_login
|
38
|
+
password: site3_password
|
39
|
+
server_root: 'web/'
|
40
|
+
document_root: '.'
|
41
|
+
<<: *exclusions
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Smurftp #:nodoc:
|
2
|
+
module VERSION #:nodoc:
|
3
|
+
MAJOR = 0
|
4
|
+
MINOR = 5
|
5
|
+
TINY = 0
|
6
|
+
|
7
|
+
STRING = [MAJOR, MINOR, TINY].join('.')
|
8
|
+
URLIFIED = STRING.tr('.', '_')
|
9
|
+
|
10
|
+
# requirements_met? can take a hash with :major, :minor, :tiny set or
|
11
|
+
# a string in the format "major.minor.tiny"
|
12
|
+
def self.requirements_met?(minimum_version = {})
|
13
|
+
major = minor = tiny = 0
|
14
|
+
if minimum_version.is_a?(Hash)
|
15
|
+
major = minimum_version[:major].to_i if minimum_version.has_key?(:major)
|
16
|
+
minor = minimum_version[:minor].to_i if minimum_version.has_key?(:minor)
|
17
|
+
tiny = minimum_version[:tiny].to_i if minimum_version.has_key?(:tiny)
|
18
|
+
else
|
19
|
+
major, minor, tiny = minimum_version.to_s.split('.').collect { |v| v.to_i }
|
20
|
+
end
|
21
|
+
met = false
|
22
|
+
if Smurftp::VERSION::MAJOR > major
|
23
|
+
met = true
|
24
|
+
elsif Smurftp::VERSION::MAJOR == major
|
25
|
+
if Smurftp::VERSION::MINOR > minor
|
26
|
+
met = true
|
27
|
+
elsif Smurftp::VERSION::MINOR == minor
|
28
|
+
met = Smurftp::VERSION::TINY >= tiny
|
29
|
+
end
|
30
|
+
end
|
31
|
+
met
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.dirname(__FILE__) + '/../lib/smurftp'
|
3
|
+
|
4
|
+
class SmurftpConfigurationTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@config_file = File.dirname(__FILE__) + '/../lib/smurftp/templates/smurftp_config.yaml'
|
7
|
+
@multisite_config_file = File.dirname(__FILE__) + '/../lib/smurftp/templates/smurftp_multisite_config.yaml'
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def test_hash_symbolize_keys
|
12
|
+
assert_equal({:yada => 'yada'}, {'yada' => 'yada'}.symbolize_keys!)
|
13
|
+
expected = {:yada => {:yada => {:yada => 'yada'}}}
|
14
|
+
sample = {'yada' => {'yada' => {'yada' => 'yada'}}}
|
15
|
+
assert_equal expected, sample.symbolize_keys!
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def test_configuration
|
20
|
+
config = Smurftp::Configuration.new(@config_file)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def test_multisite_configuration
|
25
|
+
config = Smurftp::Configuration.new(@multisite_config_file, 'site1')
|
26
|
+
puts config.inspect
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/test/shell_test.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.dirname(__FILE__) + '/../lib/smurftp'
|
3
|
+
|
4
|
+
class SmurftpShellTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@config = File.dirname(__FILE__) + '/../lib/smurftp/templates/smurftp_config.yaml'
|
7
|
+
@smurftp = Smurftp::Shell.new(@config)
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def test_should_parse_list
|
12
|
+
lists = [
|
13
|
+
['1,2,3',['1','2','3']],
|
14
|
+
['1-4,^3',['1','2','4']],
|
15
|
+
['1,2,3-6,^4',['1','2','3','5','6']],
|
16
|
+
['1,2,3-6,!4',['1','2','3','5','6']],
|
17
|
+
['^4,1-6',['1','2','3','5','6']]
|
18
|
+
].each do |input, expected|
|
19
|
+
assert_equal expected, @smurftp.parse_list(input)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def test_should_parse_range
|
25
|
+
assert_equal ['1','2','3','4'], @smurftp.parse_range('1-4')
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def test_should_parse_file_for_sub_dirs
|
30
|
+
file_paths = [
|
31
|
+
['one/two/three/file.txt',['one','one/two','one/two/three']],
|
32
|
+
['file.txt',[]]
|
33
|
+
].each do |file,expanded|
|
34
|
+
assert_equal expanded, @smurftp.parse_file_for_sub_dirs(file)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def test_should_add_files_to_queue
|
40
|
+
@smurftp.add_files_to_queue(['1','2','3','4'])
|
41
|
+
assert_equal [0,1,2,3], @smurftp.upload_queue
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def test_upload_queue_should_be_unique
|
46
|
+
@smurftp.add_files_to_queue(['1','2','3','4','2','3','1','1','1'])
|
47
|
+
assert_equal [0,1,2,3], @smurftp.upload_queue
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def test_parse_command_should_parse_input
|
52
|
+
input = @smurftp.parse_command('all')
|
53
|
+
assert_equal Proc, input.class
|
54
|
+
|
55
|
+
input = @smurftp.parse_command('a')
|
56
|
+
assert_equal Proc, input.class
|
57
|
+
|
58
|
+
input = @smurftp.parse_command('ardvark')
|
59
|
+
assert_equal 'error', input
|
60
|
+
|
61
|
+
input = @smurftp.parse_command('allin')
|
62
|
+
assert_equal 'error', input
|
63
|
+
|
64
|
+
cmd, files = @smurftp.parse_command('1')
|
65
|
+
assert_equal ['1'], files
|
66
|
+
|
67
|
+
cmd = @smurftp.parse_command(' 1 ')
|
68
|
+
assert_equal 'error', cmd
|
69
|
+
|
70
|
+
cmd, files = @smurftp.parse_command('1,2,3,4')
|
71
|
+
assert_equal ['1','2','3','4'], files
|
72
|
+
|
73
|
+
cmd, files = @smurftp.parse_command('1-4')
|
74
|
+
assert_equal ['1','2','3','4'], files
|
75
|
+
|
76
|
+
cmd, files = @smurftp.parse_command('1-4,^3')
|
77
|
+
assert_equal ['1','2','4'], files
|
78
|
+
|
79
|
+
cmd, files = @smurftp.parse_command('^3,1-4')
|
80
|
+
assert_equal ['1','2','4'], files
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def test_hash_symbolize_keys
|
85
|
+
assert_equal({:yada => 'yada'}, {'yada' => 'yada'}.symbolize_keys!)
|
86
|
+
expected = {:yada => {:yada => {:yada => 'yada'}}}
|
87
|
+
sample = {'yada' => {'yada' => {'yada' => 'yada'}}}
|
88
|
+
assert_equal expected, sample.symbolize_keys!
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gruner-smurftp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Gruner
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-05 00:00:00 -08:00
|
13
|
+
default_executable: smurftp
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Smurftp is a command-line utility that searches a specified directory and creates a queue of recently modified files for quickly uploading to a remote server over FTP.
|
17
|
+
email: [email protected]
|
18
|
+
executables:
|
19
|
+
- smurftp
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- VERSION.yml
|
26
|
+
- README.mkdn
|
27
|
+
- bin/smurftp
|
28
|
+
- lib/smurftp
|
29
|
+
- lib/smurftp/shell.rb
|
30
|
+
- lib/smurftp/version.rb
|
31
|
+
- lib/smurftp/templates
|
32
|
+
- lib/smurftp/templates/smurftp_multisite_config.yaml
|
33
|
+
- lib/smurftp/templates/smurftp_config.yaml
|
34
|
+
- lib/smurftp/configuration.rb
|
35
|
+
- lib/smurftp.rb
|
36
|
+
- test/shell_test.rb
|
37
|
+
- test/configuration_test.rb
|
38
|
+
has_rdoc: true
|
39
|
+
homepage: http://github.com/divineflame/smurftp
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options:
|
42
|
+
- --inline-source
|
43
|
+
- --charset=UTF-8
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "0"
|
51
|
+
version:
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
requirements: []
|
59
|
+
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.2.0
|
62
|
+
signing_key:
|
63
|
+
specification_version: 2
|
64
|
+
summary: Command-line utility for uploading recently modified files to a server
|
65
|
+
test_files: []
|
66
|
+
|