module Commands
Module containing definitions for client commands.
Constants
- CAT
Print the contents of remote files.
- CD
Change the remote working directory.
- CP
Copy remote files.
- DEBUG
Execute arbitrary code.
- EXEC
Execute shell command.
- EXIT
Terminate the session.
- FORGET
Clear the cache.
- GET
Download remote files.
- HELP
List commands, or print information about a specific command.
- HISTORY
Get remote file revisions.
- LCD
Change the local working directory.
- LS
List remote files.
- MEDIA
Get temporary links to remote files.
- MKDIR
Create a remote directory.
- MV
Move/rename remote files.
- NAMES
Array
of all command names.- PUT
Upload a local file.
- RESTORE
Restore a remove file to a previous version.
- RM
Remove remote files.
- RMDIR
Remove remote directories.
- SEARCH
Search for remote files.
- SHARE
Get permanent links to remote files.
- UsageError
Exception indicating that a client command was given the wrong number of arguments.
Public Class Methods
Parse and execute a line of user input in the given context.
# File lib/droxi/commands.rb, line 608 def self.exec(input, client, state) unless input.empty? input.sub!(/^!/, 'exec ') tokens = Text.tokenize(input) cmd = tokens.first args = tokens.drop(1) try_command(cmd, args, client, state) end end
Return an Array
of all command names.
# File lib/droxi/commands.rb, line 599 def self.names symbols = constants.select { |sym| const_get(sym).is_a?(Command) } symbols.map { |sym| sym.to_s.downcase } end
Private Class Methods
If the remote working directory does not exist, move up the directory tree until at a real location.
# File lib/droxi/commands.rb, line 739 def self.check_pwd(state) (state.pwd = File.dirname(state.pwd)) until state.metadata(state.pwd) end
Attempts to upload a file to the server in chunks, displaying progress.
# File lib/droxi/commands.rb, line 775 def self.chunked_upload(client, to_path, file, quiet, tries) uploader = DropboxClient::ChunkedUploader.new(client, file, file.size) thread = quiet ? nil : Thread.new { monitor_upload(uploader, to_path) } tries = -1 if tries == 0 loop_upload(uploader, thread, tries) success = (uploader.offset == uploader.total_size) data = uploader.finish(to_path) if thread thread.join print "\r" + (' ' * (18 + to_path.rpartition('/')[2].size)) + "\r" end [data, success] end
Copies or moves a file.
# File lib/droxi/commands.rb, line 697 def self.copy_move(method, args, flags, client, state) from_path, to_path = args.map { |p| state.resolve_path(p) } try_and_handle(DropboxError) do overwrite(to_path, client, state) if flags.include?('-f') metadata = client.send(method, from_path, to_path) state.cache.remove(from_path) if method == :file_move state.cache.add(metadata) puts "#{args.first} -> #{args[1]}" end end
Execute a 'mv' or 'cp' operation depending on arguments given.
# File lib/droxi/commands.rb, line 709 def self.cp_mv(client, state, args, cmd, usage) flags = extract_flags(usage, args, '-f' => 0) sources = expand(state, args.take(args.size - 1), true, true, cmd) method = (cmd == 'cp') ? :file_copy : :file_move dest = state.resolve_path(args.last) if sources.size == 1 && !state.directory?(dest) copy_move(method, [sources.first, args.last], flags, client, state) else cp_mv_to_dir(args, flags, client, state, cmd) end end
Copies or moves files into a directory.
# File lib/droxi/commands.rb, line 723 def self.cp_mv_to_dir(args, flags, client, state, cmd) sources = expand(state, args.take(args.size - 1), true, false, cmd) method = (cmd == 'cp') ? :file_copy : :file_move if state.metadata(state.resolve_path(args.last)) sources.each do |source| to_path = args.last.chomp('/') + '/' + File.basename(source) copy_move(method, [source, to_path], flags, client, state) end else warn "#{cmd}: #{args.last}: no such directory" state.exit_status = 1 end end
Return an Array
of paths from an Array
of globs, printing error messages if output
is true.
# File lib/droxi/commands.rb, line 678 def self.expand(state, paths, preserve_root, output, cmd) state.expand_patterns(paths, preserve_root).map do |item| if item.is_a?(GlobError) warn "#{cmd}: #{item}: no such file or directory" if output state.exit_status = 1 nil else item end end.compact end
Removes a flag and its arugments from the Array
and returns an Array
of the flag and its arguments. Prints warnings if the given flag is invalid.
# File lib/droxi/commands.rb, line 762 def self.extract_flag(usage, args, flags, arg, index) num_args = flags[arg] if num_args.nil? fail UsageError, usage else if index + num_args < args.size return (num_args + 1).times.map { args.delete_at(index) } end fail UsageError, usage end end
Removes flags (e.g. -f) from the Array
and returns an Array
of the removed flags. Prints warnings if the flags are not in the given String
of valid flags (e.g. '-rf').
# File lib/droxi/commands.rb, line 746 def self.extract_flags(usage, args, flags) extracted = [] index = 0 while index < args.size arg = args[index] extracted_flags = arg[/^-\w/] ? extract_flag(usage, args, flags, arg, index) : nil extracted += extracted_flags unless extracted_flags.nil? index += 1 if extracted_flags.nil? || extracted_flags.empty? end args.delete_if { |a| a[/^-\w/] } extracted end
Yield lines of output for the ls command executed on the given file paths and names.
# File lib/droxi/commands.rb, line 657 def self.list(state, paths, names, long) if long paths.zip(names).each { |path, name| yield long_info(state, path, name) } else Text.table(names).each { |line| yield line } end end
Return a String
of information about a remote file for ls -l.
# File lib/droxi/commands.rb, line 646 def self.long_info(state, path, name) meta = state.metadata(state.resolve_path(path), false) is_dir = meta['is_dir'] ? 'd' : '-' size = meta['size'].sub(/ (.)B/, '\1').sub(' bytes', '').rjust(7) mtime = Time.parse(meta['modified']) format_str = (mtime.year == Time.now.year) ? '%b %e %H:%M' : '%b %e %Y' "#{is_dir} #{size} #{mtime.strftime(format_str)} #{name}" end
Continuously try to upload until successful or interrupted.
# File lib/droxi/commands.rb, line 790 def self.loop_upload(uploader, monitor_thread, tries) while tries != 0 && uploader.offset < uploader.total_size begin uploader.upload(1024 * 1024) rescue DropboxError => error puts "\n" + error.to_s --tries end end rescue Interrupt => error monitor_thread.kill if monitor_thread raise error end
Displays real-time progress for the a being uploaded.
# File lib/droxi/commands.rb, line 805 def self.monitor_upload(uploader, to_path) filename = to_path.rpartition('/')[2] loop do percent = 100.0 * uploader.offset / uploader.total_size printf("\rUploading %s: %.1f%%", filename, percent) break if uploader.offset == uploader.total_size sleep 1 end end
# File lib/droxi/commands.rb, line 690 def self.overwrite(path, client, state) return unless state.metadata(path) client.file_delete(path) state.cache.remove(path) end
Run a command in the system shell and yield lines of output.
# File lib/droxi/commands.rb, line 666 def self.shell(cmd) IO.popen(cmd) do |pipe| pipe.each_line { |line| yield line.chomp if block_given? } end rescue Interrupt yield '' rescue Errno::ENOENT => error yield error.to_s if block_given? end
Attempt to run the associated block, handling the given type of Exception
by issuing a warning using its String
representation.
# File lib/droxi/commands.rb, line 622 def self.try_and_handle(exception_class) yield rescue exception_class => error warn error end
Run a command with the given name, or print an error message if usage is incorrect or no such command exists.
# File lib/droxi/commands.rb, line 630 def self.try_command(command_name, args, client, state) if NAMES.include?(command_name) begin command = const_get(command_name.upcase.to_sym) command.exec(client, state, *args) { |line| puts line } rescue UsageError => error warn "Usage: #{error}" state.exit_status = 1 end else warn "droxi: #{command_name}: command not found" state.exit_status = 1 end end