在Ruby on Rails中获取空临时目录的最佳方法是什么?

时间:2009-07-16 18:01:17

标签: ruby-on-rails ruby

使用Ruby on Rails获取没有任何内容的临时目录的最佳方法是什么?我需要API与跨平台兼容。 stdlib tmpdir无效。

9 个答案:

答案 0 :(得分:48)

Dir对象有一个方法mktmpdir,它创建一个临时目录:

require 'tmpdir' # Not needed if you are using rails.

Dir.mktmpdir do |dir|
  puts "My new temp dir: #{dir}"
end

执行块后将删除临时目录。

答案 1 :(得分:20)

Ruby核心中的Dir#tmpdir函数(不是您链接到的stdlib)应该是跨平台的。

要使用此功能,您需要require 'tmpdir'

答案 2 :(得分:13)

我现在正在使用的普通aprox:

def in_tmpdir
  path = File.expand_path "#{Dir.tmpdir}/#{Time.now.to_i}#{rand(1000)}/"
  FileUtils.mkdir_p path
  yield path
ensure
  FileUtils.rm_rf( path ) if File.exists?( path )
end

因此,在您的代码中,您可以:

in_tmpdir do |tmpdir|
  puts "My tmp dir: #{tmpdir}"
  # work with files in the dir
end

当您的方法完成后,临时目录将自动删除

答案 3 :(得分:2)

require 'tmpdir' # not needed if you are loading Rails
tmp_dir = File.join(Dir::tmpdir, "my_app_#{Time.now.to_i}_#{rand(100)}")
Dir.mkdir(tmp_dir)

适合我。

答案 4 :(得分:1)

Ruby有Dir#mktmpdir,所以只需使用它。

require 'tempfile'
Dir.mktmpdir('prefix_unique_to_your_program') do |dir|
    ### your work here ###
end

请参阅http://www.ruby-doc.org/stdlib-1.9.3/libdoc/tmpdir/rdoc/Dir.html

或者使用流程和线程唯一的Tempfile临时文件构建自己的文件,所以只需使用它来构建一个快速的Tempdir。

require 'tempfile'
Tempfile.open('prefix_unique_to_your_program') do |tmp|
  tmp_dir = tmp.path + "_dir"
  begin
    FileUtils.mkdir_p(tmp_dir)

    ### your work here ###
  ensure
    FileUtils.rm_rf(tmp_dir)
  end
end

有关可选的后缀/前缀选项,请参阅http://www.ruby-doc.org/stdlib-1.9.3/libdoc/tempfile/rdoc/Tempfile.html

答案 5 :(得分:0)

我开始通过劫持Tempfile解决这个问题,见下文。 它应该像Tempfile一样清理自己,但并不总是...... 它还没有删除tempdir中的文件。 无论如何,我在这里分享这个,作为一个起点可能是有用的。

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

这可以与Tempfile相同的方式使用,例如:

 Tempdir.new('foo')

Tempfile上的所有方法,反过来,File应该有效。 只是简单地测试了它,所以没有保证。

答案 6 :(得分:0)

查看Ruby STemp库:http://ruby-stemp.rubyforge.org/rdoc/

如果您这样做:

dirname = STemp.mkdtemp("#{Dir.tmpdir}/directory-name-template-XXXXXXXX")

dirname将是一个字符串,指向一个保证以前不存在的目录。您可以定义目标名称的开头内容。 X被随机字符取代。

编辑:有人提到这对于他们在1.9上没有用,所以YMMV。

答案 7 :(得分:0)

更新:gem install files,然后

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

有关更多示例,请参阅下文。


这是另一个解决方案,受其他一些答案的启发。这个适合包含在测试中(例如rspec或spec_helper.rb)。它根据包含文件的名称创建一个临时目录,将其存储在实例变量中,以便在测试期间保持不变(但不在测试之间共享),并在退出时删除它(或者可选择不删除)如果你想在试运行后检查它的内容。)

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

(您也可以使用Dir.mktmpdir(自Ruby 1.8.7以来一直存在)而不是Dir.mkdir,但我发现该方法的API令人困惑,更不用说命名算法。)< / p>

用法示例(和另一种有用的测试方法):

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
    @goodbye.read.should == "farewell" 
  end
end

更新:我已经使用这段代码来启动我正在调用的files一个gem,它打算使用它来创建用于临时(例如单元测试)的目录和文件变得非常容易。请参阅https://github.com/alexch/fileshttps://rubygems.org/gems/files。例如:

require "files"

files = Files do     # creates a temporary directory inside Dir.tmpdir
  file "hello.txt"          # creates file "hello.txt" containing "contents of hello.txt"
  dir "web" do              # creates directory "web"
    file "snippet.html",    # creates file "web/snippet.html"...
      "<h1>Fix this!</h1>"  # ...containing "<h1>Fix this!</h1>"
    dir "img" do            # creates directory "web/img"
      file File.new("data/hello.png")            # containing a copy of hello.png
      file "hi.png", File.new("data/hello.png")  # and a copy of hello.png named hi.png
    end
  end
end                         # returns a string with the path to the directory

答案 8 :(得分:0)

您可以使用Dir.mktmpdir

使用块将在关闭时删除临时目录。

Dir.mktmpdir do |dir|
  File.open("#{dir}/foo", 'w') { |f| f.write('foo') }
end

或者如果您需要同时存在多个临时目录,例如

context 'when there are duplicate tasks' do
  it 'raises an DuplicateTask error' do
    begin
      tmp_dir1 = Dir.mktmpdir('foo')
      tmp_dir2 = Dir.mktmpdir('bar')

      File.new("#{tmp_dir1}/task_name", 'w+')
      File.new("#{tmp_dir2}/task_name", 'w+')

      expect { subject.filepath('task_name') }.to raise_error(TaskFinder::DuplicateTask)
    ensure
      FileUtils.remove_entry tmp_dir1
      FileUtils.remove_entry tmp_dir2
    end
  end
end

Dir.mktmpdirDir.tmpdir下创建一个临时目录(您需要require 'tmpdir'才能查看评估结果)。

如果您想使用自己的路径,Dir.mktmpdir如果给出非零值,则会选择第二个参数 tmpdir 。 E.g。

Dir.mktmpdir(nil, "/var/tmp") { |dir| "dir is '/var/tmp/d...'" }