Alex Rivera | Logout

What is the best way to get an empty temporary directory in Ruby on Rails?

Asked 2009-07-16T18:01:17.373
41

What is the best way to get a temporary directory with nothing in it using Ruby on Rails? I need the API to be cross-platform compatible. The stdlib tmpdir won't work.

Edit
Report

2 Answers

0

I started to tackle this by hijacking Tempfile, see below. It should clean itself up as Tempfile does, but doesn't always yet.. It's yet to delete files in the tempdir. Anyway I share this here, might be useful as a starting point.

require 'tempfile'
class Tempdir < Tempfile
  require 'tmpdir'
  def initialize(basename, tmpdir = Dir::tmpdir)
    super
    p = self.path
    File.delete(p)
    Dir.mkdir(p)
  end
  def unlink # copied from tempfile.rb
    # keep this order for thread safeness
    begin
      Dir.unlink(@tmpname) if File.exist?(@tmpname)
      @@cleanlist.delete(@tmpname)
      @data = @tmpname = nil
      ObjectSpace.undefine_finalizer(self)
    rescue Errno::EACCES
      # may not be able to unlink on Windows; just ignore
    end
  end  
end

This can be used the same way as Tempfile, eg:

 Tempdir.new('foo')

All methods on Tempfile , and in turn, File should work. Just briefly tested it, so no guarantees.

answered 2010-09-07T17:40:54.373
0

Update: gem install files, then

require "files"
dir = Files do
  file "hello.txt", "stuff"
end

See below for more examples.


Here's another solution, inspired by a few other answers. This one is suitable for inclusion in a test (e.g. rspec or spec_helper.rb). It makes a temporary dir based on the name of the including file, stores it in an instance variable so it persists for the duration of the test (but is not shared between tests), and deletes it on exit (or optionally doesn't, if you want to check its contents after the test run).

def temp_dir options = {:remove => true}
  @temp_dir ||= begin
    require 'tmpdir'
    require 'fileutils'
    called_from = File.basename caller.first.split(':').first, ".rb"
    path = File.join(Dir::tmpdir, "#{called_from}_#{Time.now.to_i}_#{rand(1000)}")
    Dir.mkdir(path)
    at_exit {FileUtils.rm_rf(path) if File.exists?(path)} if options[:remove]
    File.new path
  end
end

(You could also use Dir.mktmpdir (which has been around since Ruby 1.8.7) instead of Dir.mkdir but I find the API of that method confusing, not to mention the naming algorithm.)

Usage example (and another useful test method):

def write name, contents = "contents of #{name}"
  path = "#{temp_dir}/#{name}"
  File.open(path, "w") do |f|
    f.write contents
  end
  File.new path
end

describe "#write" do
  before do
    @hello = write "hello.txt"
    @goodbye = write "goodbye.txt", "farewell"
  end

  it "uses temp_dir" do
    File.dirname(@hello).should == temp_dir
    File.dirname(@goodbye).should == temp_dir
  end

  it "writes a default value" do
    File.read(@hello).should == "contents of hello.txt"
  end

  it "writes a given value" do
    # since write returns a File instance, we can call read on it
   
answered 2012-01-11T17:16:09.977

Your Answer