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.

WARNING: Using ‘cd’ and :noop together can be dangerous!

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.



Classes and Modules

Class Tempster::Messager


DEFAULT_PREFIX = "tempster"
ARMOR_CHARACTERS = ["A".."Z","a".."z","0".."9"].collect{|r| r.to_a}.join

Public Class methods

Returns a string of random characters.


# File lib/tempster.rb, line 146
  def self._armor_string(length=DEFAULT_ARMOR_LENGTH)

Creates a temporary file.


# File lib/tempster.rb, line 151
  def self.mktemp(opts={}, &block)
    tempster({:kind => :file}.merge(opts), &block)

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)


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)


  • :kind — Create a :file or :directory, required.
  • :prefix — String to prefix the temporary name with, defaults to "tempster".
  • :suffix — String to suffix the temporary name with, defaults is no suffix.
  • :name — Same as :prefix
  • :dir — Base directory to create temporary entries in, uses system-wide temporary directory (e.g., /tmp) by default.
  • :cd — Change into the newly directory created using ch within the block, and then switch back to the previous directory. Only used when a block is given and the :kind is :directory. Default is false. See WARNING at the top of this class‘s documentation!
  • :noop — no-operation mode, pretends to do actions without actually creating or deleting temporary entries. Default is false. WARNING: See WARNING at the top of this class‘s documentation!
  • :verbose — Print status messages about creating and deleting the temporary entries. Default is false.
  • :delete — Delete the temporary entries when exiting block. Default is true when given a block, false otherwise. If you don‘t use a block, you‘re responsible for deleting the entries yourself.
  • :tries — Number of tries to create a temporary entry, usually it‘ll succeed on the first try. Default is 10.
  • :armor — Length of armor to append to the prefix. These are random characters padding out the temporary entry names to prevent them from using existing files. If you have a very short armor, you‘re likely to get a collision and the algorithm will have to try again for the specified number of tries.
  • :message_callback — lambda called when there‘s a message, e.g., lambda{|message| puts message}, regardless of :verbose state. By default :messaging is nil and messages are printed to STDOUT only when :verbose is true.
  • :message_prefix — String to put in front of messages, e.g., "# "


# File lib/tempster.rb, line 60
  def self.tempster(opts={}, &block)
    kind = opts.delete(:kind) or raise"'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
      when :directory
        raise"unknown kind: #{kind}")

    raise"can only use 'delete' option with block") if delete and not block
    raise"can only use 'cd' with directories and blocks") if cd and (not dir or not block)
    raise"unknown extra options: #{opts.inspect}") unless opts.empty?

    messager =, message_callback, message_prefix)

    path = nil
    success = false
    for i in 1..tries
        name = prefix+"_"+_armor_string(armor)
        name << suffix if suffix
        path = File.join(dir, name)

        unless noop
          case kind
          when :file
  , File::RDWR|File::CREAT|File::EXCL).close
            File.chmod(mode, path)
          when :directory
            Dir.mkdir(path, mode)
            raise"unknown kind: #{kind}")
        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
      rescue Errno::EEXIST
        # Try again
    raise"couldn't create temporary #{kind}, ") unless success
    if block
      previous = Dir.pwd if cd
        if cd
          Dir.chdir(path) unless noop
          messager.puts("pushd #{path}")
      rescue Exception => e
        # Re-throw exception after cleaning up
        raise e
        if cd
          Dir.chdir(previous) unless noop
          messager.puts("popd # => #{previous}")
        if delete
          FileUtils.rm_rf(path) unless noop
          messager.puts("rm -rf #{path}")
      return true
      return path