Class | Tempster |
In: |
lib/tempster.rb
|
Parent: | Object |
Tempster is a pure-Ruby library for creating temporary files and directories. Unlike other tools, it can create both temporary files and directories, and is designed to be secure, thread-safe, easy-to-use, powerful thanks to user-configurable options, and user-friendly thanks to good defaults so you don‘t need to provide any arguments.
Why write another library for this? The Tempfile standard library provides no way to create temporary directories and always deletes the files it creates, even if you want to keep them. The MkTemp gem is insecure and fails on collisions. Linux "mktemp" works fine but is platform-specific. Therefore, I had to write something.
Tempster will only pretend to make directories in :noop (no-operation) mode. In :noop mode, it will also only pretend to change into the directory when using :cd or mktempdircd.
This can be disastrous if you‘re executing non-AutomateIt commands (e.g. system) that use relative paths and expect to be run inside the newly-created temporary directory because the chdir didn‘t actually happen.
Read previews.txt for instructions on how to write code that can be safely previewed.
DEFAULT_PREFIX | = | "tempster" |
DEFAULT_FILE_MODE | = | 0600 |
DEFAULT_DIRECTORY_MODE | = | 0700 |
DEFAULT_ARMOR_LENGTH | = | 10 |
ARMOR_CHARACTERS | = | ["A".."Z","a".."z","0".."9"].collect{|r| r.to_a}.join |
Returns a string of random characters.
# File lib/tempster.rb, line 146 def self._armor_string(length=DEFAULT_ARMOR_LENGTH) (1..length).collect{ARMOR_CHARACTERS[rand(ARMOR_CHARACTERS.size)]}.pack("C*") end
Creates a temporary file.
# File lib/tempster.rb, line 151 def self.mktemp(opts={}, &block) tempster({:kind => :file}.merge(opts), &block) end
Creates a temporary directory.
WARNING: See WARNING text at the top of this class‘s documentation!
# File lib/tempster.rb, line 158 def self.mktempdir(opts={}, &block) tempster({:kind => :directory}.merge(opts), &block) end
Creates a temporary directory and changes into it using chdir. This is a shortcut for using mktempdir with the :cd => true option.
WARNING: See WARNING text at the top of this class‘s documentation!
# File lib/tempster.rb, line 167 def self.mktempdircd(opts={}, &block) tempster({:kind => :directory, :cd => true}.merge(opts), &block) end
Options:
# File lib/tempster.rb, line 60 def self.tempster(opts={}, &block) kind = opts.delete(:kind) or raise ArgumentError.new("'kind' option not specified") prefix = opts.delete(:prefix) || opts.delete(:name) || DEFAULT_PREFIX suffix = opts.delete(:suffix) || nil dir = opts.delete(:dir) || Dir.tmpdir cd = opts.delete(:cd) || false noop = opts.delete(:noop) || false verbose = opts.delete(:verbose) || false delete = opts.delete(:delete) || block ? true : false tries = opts.delete(:tries) || 10 armor = opts.delete(:armor) || DEFAULT_ARMOR_LENGTH message_callback = opts.delete(:message_callback) || nil message_prefix = opts.delete(:message_prefix) || "" mode = opts.delete(:mode) || \ case kind when :file DEFAULT_FILE_MODE when :directory DEFAULT_DIRECTORY_MODE else raise ArgumentError.new("unknown kind: #{kind}") end raise ArgumentError.new("can only use 'delete' option with block") if delete and not block raise ArgumentError.new("can only use 'cd' with directories and blocks") if cd and (not dir or not block) raise ArgumentError.new("unknown extra options: #{opts.inspect}") unless opts.empty? messager = Messager.new(verbose, message_callback, message_prefix) path = nil success = false for i in 1..tries begin name = prefix+"_"+_armor_string(armor) name << suffix if suffix path = File.join(dir, name) unless noop case kind when :file File.open(path, File::RDWR|File::CREAT|File::EXCL).close File.chmod(mode, path) when :directory Dir.mkdir(path, mode) else raise ArgumentError.new("unknown kind: #{kind}") end end message = "mktempster --mode=0%o --kind=%s --dir=%s --prefix=%s" % [mode, kind, dir, prefix] message << (" --suffix=%s" % suffix) if suffix message << (" # => %s" % path) if block success = true break rescue Errno::EEXIST # Try again end end raise IOError.new("couldn't create temporary #{kind}, ") unless success if block previous = Dir.pwd if cd begin if cd Dir.chdir(path) unless noop messager.puts("pushd #{path}") end block.call(path) rescue Exception => e # Re-throw exception after cleaning up raise e ensure if cd Dir.chdir(previous) unless noop messager.puts("popd # => #{previous}") end if delete FileUtils.rm_rf(path) unless noop messager.puts("rm -rf #{path}") end end return true else return path end end