Module: Tmptation::SafeDeletable

Included in:
TmpDir, TmpFile
Defined in:
lib/tmptation.rb

Overview

Adds a #safe_delete method that will delete the object’s associated path (either #path or #to_s, if it exists) only if it lives within the system’s temporary directory (as defined by Dir.tmpdir)

Examples:


path = Pathname.new('~/Documents')
path.extend(SafeDeletable)

path.to_s #=> '~/Documents'
path.safe_delete #=> raises UnsafeDelete

# however:

path = Pathname(Dir.mktmpdir).expand_path

Dir.tmpdir  #=> /var/folders/l8/l8EJIxZoHGGj+y1RvV0r6U+++TM/-Tmp-/
path.to_s   #=> /var/folders/l8/l8EJIxZoHGGj+y1RvV0r6U+++TM/-Tmp-/20101103-94996-1iywsjo

path.exist? #=> true
path.safe_delete
path.exist? #=> false

Constant Summary collapse

UnsafeDelete =
Class.new(RuntimeError)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.path_for(obj) ⇒ Object



36
37
38
39
40
41
42
43
44
45
# File 'lib/tmptation.rb', line 36

def self.path_for(obj)
  path = obj.respond_to?(:path) ? obj.path : obj.to_s
  path = Pathname(path).expand_path

  unless safe?(path)
    raise UnsafeDelete.new("refusing to remove non-tmp directory '#{path}'")
  end

  path
end

.safe?(path) ⇒ Boolean

Whether ‘path` lives under `Dir.tmpdir`

Returns:

  • (Boolean)


48
49
50
# File 'lib/tmptation.rb', line 48

def self.safe?(path)
  !!path.to_s.match(/^#{Regexp.escape(Dir.tmpdir)}/)
end

Instance Method Details

#safe_deleteObject

Delete ‘#path` or `#to_s` if it exists, and only if it lives under `Dir.tmpdir`. If the path is a directory, it is deleted recursively.



57
58
59
60
61
# File 'lib/tmptation.rb', line 57

def safe_delete
  FileUtils.remove_entry_secure(SafeDeletable.path_for(self).to_s)
rescue Errno::ENOENT
  # noop
end

#safe_delete_contentsObject

Same as ‘#safe_delete`, but only deletes the contents of the directory, i.e. files and subdirectories



69
70
71
# File 'lib/tmptation.rb', line 69

def safe_delete_contents
  SafeDeletable.path_for(self).children.each {|entry| FileUtils.remove_entry_secure(entry) }
end