Как лучше всего получить временный каталог, в котором ничего нет, с помощью Ruby on Rails? Мне нужно, чтобы API был кроссплатформенным. Стандартная библиотека tmpdir не будет работать.

38
Mike 16 Июл 2009 в 22:01

9 ответов

Лучший ответ

Функция Dir#tmpdir в ядре Ruby (а не stdlib, с которой вы связались) должна быть кроссплатформенной.

Чтобы использовать эту функцию, вам необходимо require 'tmpdir'.

20
georgebrock 18 Авг 2012 в 10:14

У объекта Dir есть метод mktmpdir, который создает временный каталог:

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

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

Временный каталог будет удален после выполнения блока.

57
Justin Tanner 25 Янв 2017 в 01:29

Общий пример, который я использую сейчас:

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

Временный каталог будет удален автоматически, когда ваш метод будет завершен.

13
Simon B. 3 Июл 2013 в 10:21

У 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
adamkonrad 21 Июн 2018 в 20:02
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)

Работает для меня.

2
fguillen 24 Авг 2011 в 14:43

Вы можете использовать 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.mktmpdir создает временный каталог в Dir.tmpdir (вам нужно require 'tmpdir', чтобы увидеть, что это означает).

Если вы хотите использовать свой собственный путь, Dir.mktmpdir принимает необязательный второй аргумент tmpdir , если задано значение, отличное от nil. Например.

Dir.mktmpdir(nil, "/var/tmp") { |dir| "dir is '/var/tmp/d...'" }
2
Dennis 25 Апр 2016 в 11:27

Я начал бороться с этим, взломав 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 должны работать. Просто протестировал его вкратце, так что никаких гарантий.

0
inger 7 Сен 2010 в 18:12

Обновление: 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 этого метода запутанным, не говоря уже об алгоритме именования.)

Пример использования (и еще один полезный метод тестирования):

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, который призван упростить создание каталогов и файлов для временного (например, модульного тестирования) использования. См. https://github.com/alexch/files и https://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
0
AlexChaffee 13 Янв 2012 в 21:17

Ознакомьтесь с библиотекой Ruby STemp: http://ruby-stemp.rubyforge.org/rdoc/

Если вы сделаете что-то вроде этого:

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

Dirname будет строкой, указывающей на каталог, который ранее гарантированно не существовал. Вы можете определить, с чего вы хотите, чтобы имя каталога начиналось. X заменяются случайными символами.

РЕДАКТИРОВАТЬ: кто-то упомянул, что это не сработало для них на 1.9, поэтому YMMV.

0
JT. 4 Дек 2012 в 19:20