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

exec(input, client, state) click to toggle source

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
names() click to toggle source

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

check_pwd(state) click to toggle source

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
chunked_upload(client, to_path, file, quiet, tries) click to toggle source

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
copy_move(method, args, flags, client, state) click to toggle source

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
cp_mv(client, state, args, cmd, usage) click to toggle source

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
cp_mv_to_dir(args, flags, client, state, cmd) click to toggle source

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
expand(state, paths, preserve_root, output, cmd) click to toggle source

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
extract_flag(usage, args, flags, arg, index) click to toggle source

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
extract_flags(usage, args, flags) click to toggle source

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
list(state, paths, names, long) { |long_info(state, path, name)| ... } click to toggle source

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
long_info(state, path, name) click to toggle source

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
loop_upload(uploader, monitor_thread, tries) click to toggle source

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
monitor_upload(uploader, to_path) click to toggle source

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
overwrite(path, client, state) click to toggle source
# 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
shell(cmd) { |chomp| ... } click to toggle source

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
try_and_handle(exception_class) { || ... } click to toggle source

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
try_command(command_name, args, client, state) click to toggle source

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