Каков наилучший способ получить временный каталог, в котором нет ничего с помощью Ruby on Rails? Мне нужен API для кросс-платформенной совместимости. Stdlib tmpdir не будет работать.
Каков наилучший способ получить пустую временную директорию в Ruby on Rails?
Ответ 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...'" }