Module: Can
- Defined in:
- lib/can.rb,
lib/can/info.rb,
lib/can/list.rb,
lib/can/empty.rb,
lib/can/trash.rb,
lib/can/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.2.0'
- 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'] }.freeze
- 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'] }.freeze
- ALL_FLAGS =
MODES.merge(OPTIONS).freeze
Class Method Summary collapse
- .can(argv) ⇒ Object
- .cli ⇒ Object
- .empty ⇒ Object
- .info ⇒ Object
- .init_dirs ⇒ Object
- .list ⇒ Object
- .trash ⇒ Object
- .untrash ⇒ Object
Class Method Details
.can(argv) ⇒ Object
33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/can.rb', line 33 def self.can(argv) @options, @argv = ArgParse.init_args(argv) mode = ArgParse.mode @options init_dirs send mode $exit = EXIT_SUCCESS if @options.include?(:force) end |
.cli ⇒ Object
28 29 30 |
# File 'lib/can.rb', line 28 def self.cli can ARGV end |
.empty ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/can/empty.rb', line 4 def self.empty # Remove everything in the files and info directory if ARGV.empty? FileUtils.rm_r Dir.glob("#{HOME_TRASH_INFO_DIRECTORY}/*"), secure: true FileUtils.rm_r Dir.glob("#{HOME_TRASH_FILES_DIRECTORY}/*"), secure: true else @argv.each do |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 end |
.info ⇒ Object
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 30 31 32 |
# File 'lib/can/info.rb', line 4 def self.info # Fails with a fatal error even with --force, intended # behavior. if @argv.empty? Error.fatal 'missing operand' else @argv.each_with_index do |file, i| trashinfo_filename = "#{file}.trashinfo" trashinfo_path = File.join(HOME_TRASH_INFO_DIRECTORY, trashinfo_filename) unless 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 end |
.init_dirs ⇒ Object
23 24 25 26 |
# File 'lib/can.rb', line 23 def self.init_dirs FileUtils.mkpath HOME_TRASH_FILES_DIRECTORY FileUtils.mkpath HOME_TRASH_INFO_DIRECTORY end |
.list ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/can/list.rb', line 4 def self.list # Given no args, show every trashed file if @argv.empty? 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 do |file| regex =~ file end ) else raise StandardError, "can: mode --list expects 0 to 1 arguments, given #{@argv.length}" end end |
.trash ⇒ Object
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 |
# File 'lib/can/trash.rb', line 25 def self.trash Error.fatal 'missing operand' if @argv.empty? && !@options.include?(:force) @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. unless File.exist?(path) Error.nonfatal "cannot trash '#{path}': No such file or directory" unless @options.include? :force 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) && !File.symlink?(path) Error.nonfatal "cannot remove '#{path}': Is a directory" unless @options.include? :recursive 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`. next if @options.include?(:prompt) && !(HighLine.agree "can: remove file '#{path}'?") 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
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 30 |
# File 'lib/can/untrash.rb', line 4 def self.untrash @argv.each do |filename| file_path = File.join(HOME_TRASH_FILES_DIRECTORY, filename) unless File.exist? file_path unless @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] 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 |