Подтвердить что ты не робот

Каков наилучший способ получить пустую временную директорию в Ruby on Rails?

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

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

Ответ 3

Общий 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

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

Ответ 4

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)

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

Ответ 5

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

Ответ 6

Посмотрите библиотеку Ruby STemp: http://ruby-stemp.rubyforge.org/rdoc/

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

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

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

EDIT: кто-то сказал, что это не сработало для них на 1.9, поэтому YMMV.

Ответ 7

Обновление: 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

Обновление: я использовал этот код для запуска gem, который я вызываю files, который намеревается сделать его очень простым для создания каталогов и файлов для временного использования (например, unit test). См. 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

Ответ 8

Ruby имеет Dir # mktmpdir, поэтому просто используйте это.

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

См. http://www.ruby-doc.org/stdlib-1.9.3/libdoc/tmpdir/rdoc/Dir.html

Или создайте свой собственный, используя Tempfile 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 для дополнительных опций суффикса/префикса.

Ответ 9

Вы можете использовать Dir.mktmpdir.

Использование блока избавится от временного каталога при его закрытии.

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

Или если вам нужно несколько директорий temp, чтобы существовать одновременно, например

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...'" }