Module: Can
- Defined in:
- lib/can.rb,
lib/info.rb,
lib/list.rb,
lib/empty.rb,
lib/trash.rb,
lib/untrash.rb,
lib/can/version.rb,
lib/can/argparse.rb
Defined Under Namespace
Modules: ArgParse
Constant Summary collapse
- XDG_DATA_HOME_DEFAULT =
File.join(ENV['HOME'], '.local/share')
- XDG_DATA_HOME =
ENV['XDG_DATA_HOME'] || XDG_DATA_HOME_DEFAULT
- HOME_TRASH_DIRECTORY =
File.join(XDG_DATA_HOME, 'Trash')
- HOME_TRASH_INFO_DIRECTORY =
File.join(HOME_TRASH_DIRECTORY, 'info')
- HOME_TRASH_FILES_DIRECTORY =
File.join(HOME_TRASH_DIRECTORY, 'files')
- VERSION =
'0.1.1'- USAGE =
'Usage: can [OPTION] [FILE]...'- MODES =
{ :list => ['-l', '--list', 'list files in the trash'], :info => ['-n', '--info', 'see information about a trashed file'], :untrash => ['-u', '--untrash', 'restore a trashed file'], :empty => ['-e', '--empty', 'permanently remove a file from the trash; use with no arguments to empty entire trashcan'], }
- OPTIONS =
{ :force => ['-f', '--force', 'ignore nonexistent files and arguments, never prompt'], :prompt => ['-i', nil, 'prompt before every trashing'], :recursive => ['-r', '--recursive', 'trash directories and their contents recursively'] }
- ALL_FLAGS =
MODES.merge(OPTIONS)
Class Method Summary collapse
- .can ⇒ Object
- .empty ⇒ Object
-
.info ⇒ Object
TODO: Parse the .trashinfo files to make them more human readable.
- .init_dirs ⇒ Object
- .list ⇒ Object
- .trash ⇒ Object
- .untrash ⇒ Object
Class Method Details
.can ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/can.rb', line 26 def self.can ArgParse.init_args mode = ArgParse.get_mode self.init_dirs self.send mode if $options.include? :force $exit = EXIT_SUCCESS end end |
.empty ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/empty.rb', line 2 def self.empty # Remove everything in the files and info directory if ARGV.length == 0 FileUtils.rm_r Dir.glob("#{HOME_TRASH_INFO_DIRECTORY}/*"), secure: true FileUtils.rm_r Dir.glob("#{HOME_TRASH_FILES_DIRECTORY}/*"), secure: true else ARGV.map { |filename| trashinfo_filename = filename + '.trashinfo' file_path = File.join(HOME_TRASH_FILES_DIRECTORY, filename) trashinfo_file_path = File.join(HOME_TRASH_INFO_DIRECTORY, trashinfo_filename) FileUtils.remove_entry_secure file_path FileUtils.remove_entry_secure trashinfo_file_path } end end |
.info ⇒ Object
TODO: Parse the .trashinfo files to make them more human readable. Also, display the filename above the information with empty lines between consecutive info blocks.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/info.rb', line 5 def self.info # Fails with a fatal error even with --force, intended # behavior. if ARGV.length == 0 Error.fatal 'missing operand' else ARGV.each_with_index { |file, i| trashinfo_filename = file + '.trashinfo' trashinfo_path = File.join(HOME_TRASH_INFO_DIRECTORY, trashinfo_filename) if not File.exist? trashinfo_path Error.nonfatal "no such file in trashcan: '#{file}'" next end trashinfo = Trashinfo.parse(File.read trashinfo_path) # TODO: Checking if i is not zero every single # iteration is a little inefficient. Maybe there is a # better way to do this? puts if i != 0 puts <<~INFO #{file}: Path: #{trashinfo[:path]} Deletion Date: #{trashinfo[:deletion_date]} INFO } end end |
.init_dirs ⇒ Object
21 22 23 24 |
# File 'lib/can.rb', line 21 def self.init_dirs() FileUtils.mkpath HOME_TRASH_FILES_DIRECTORY FileUtils.mkpath HOME_TRASH_INFO_DIRECTORY end |
.list ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/list.rb', line 2 def self.list # Given no args, show every trashed file if ARGV.length == 0 puts Dir.children(HOME_TRASH_FILES_DIRECTORY) # Given a regex pattern as an arg, print trashed files # that fit elsif ARGV.length == 1 regex = Regexp.new(ARGV[0]) puts Dir.children(HOME_TRASH_FILES_DIRECTORY).select { |file| regex =~ file } else raise StandardError.new( "can: mode --list expects 0 to 1 arguments, given #{ARGV.length}" ) end end |
.trash ⇒ Object
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/trash.rb', line 24 def self.trash if ARGV.length == 0 and not $options.include? :force Error.fatal 'missing operand' end ARGV.each do |path| # TODO: If both `-f` and `-i` are used, can should # prompt if `-i` is used last. If `-f` is used last, # can should not prompt trashings. This follows the # behavior of rm. if not File.exist?(path) if not $options.include? :force Error.nonfatal "cannot trash '#{path}': No such file or directory" end next end # If --recursive is not used and a directory is given as an # argument, a non-zero error code should be returned # regardless if --force is used. if File.directory? path and not File.symlink? path if not $options.include? :recursive Error.nonfatal "cannot remove '#{path}': Is a directory" end next end # TODO: Highline.agree prints to stdout, when it should # print to stderr. It also uses `puts`, while this use # case should use `print`. if $options.include? :prompt unless HighLine.agree "can: remove file '#{path}'?" next end end filename = File.basename path trashinfo_string = Trashinfo.new path existing_trash_files = Dir.children HOME_TRASH_FILES_DIRECTORY # The File.basename function only strips the last # extension. These functions are needed to support files # with multiple extensions, like file.txt.bkp basename = strip_extensions(filename) exts = gather_extensions(filename) # Most implementations add a number as the first # extension to prevent file conflicts i = 0 while existing_trash_files.include?(filename) i += 1 filename = basename + ".#{i}" + exts end FileUtils.mv(path, File.join(HOME_TRASH_FILES_DIRECTORY, filename)) trashinfo_filename = filename + '.trashinfo' trashinfo_out_path = File.join(HOME_TRASH_INFO_DIRECTORY, trashinfo_filename) File.new(trashinfo_out_path, 'w').syswrite(trashinfo_string) end end |
.untrash ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/untrash.rb', line 2 def self.untrash ARGV.each do |filename| file_path = File.join(HOME_TRASH_FILES_DIRECTORY, filename) if not File.exist? file_path if not $options.include? :force Error.nonfatal "cannot untrash '#{filename}': No such file or directory in trash" end next end trashinfo_filename = filename + '.trashinfo' trashinfo_path = File.join(HOME_TRASH_INFO_DIRECTORY, trashinfo_filename) trashinfo = Trashinfo.parse(File.read trashinfo_path) original_path = trashinfo[:path] # TODO: Implement more thorough error handling if File.exist? original_path Error.nonfatal "cannot untrash '#{filename}' to '#{original_path}': File exists" next end # TODO: Make sure ctime, atime, mtime, do not change FileUtils.mv file_path, original_path FileUtils.rm trashinfo_path end end |