When using Tempfile
Ruby is creating a file with a thread-safe and inter-process-safe name. I only need a file name in that way.
I was wondering if there is a more straight forward approach way than:
t = Tempfile.new(['fleischwurst', '.png'])
temp_path = t.path
t.close
t.unlink
Alternatively referred to as a foo file, a temporary file or temp file is a file created to hold information while a file's being created or modified. After the program is closed, the temporary file is deleted.
Techopedia Explains Temporary File In terms of backup purposes, Microsoft's Office applications are good examples of this. For example, Microsoft Word and Excel save a temporary file associated with the current open document that it points to after a computer has recovered from a crash or power outage.
To create and use a temporary fileThe application opens the user-provided source text file by using CreateFile. The application retrieves a temporary file path and file name by using the GetTempPath and GetTempFileName functions, and then uses CreateFile to create the temporary file.
C++ tmpfile()The tmpfile() function in C++ creates and opens a temporary file in binary read/write (wb+) mode with a unique auto-generated filename. The file will be automatically deleted when it is closed by the program(by executing fclose) or when the program terminates.
You could use Dir::Tmpname.create
. It figures out what temporary directory to use (unless you pass it a directory). It's a little ugly to use given that it expects a block:
require 'tmpdir' # => true Dir::Tmpname.create(['prefix-', '.ext']) {} # => "/tmp/prefix-20190827-1-87n9iu.ext" Dir::Tmpname.create(['prefix-', '.ext'], '/my/custom/directory') {} # => "/my/custom/directory/prefix-20190827-1-11x2u0h.ext"
The block is there for code to test if the file exists and raise an Errno::EEXIST
so that a new name can be generated with incrementing value appended on the end.
The solution implemented by Ruby on Rails is short and similar to the solution originally implemented in Ruby:
require 'tmpdir' # => true File.join(Dir.tmpdir, "YOUR_PREFIX-#{Time.now.strftime("%Y%m%d")}-#{$$}-#{rand(0x100000000).to_s(36)}-YOUR_SUFFIX") => "/tmp/YOUR_PREFIX-20190827-1-wyouwg-YOUR_SUFFIX" File.join(Dir.tmpdir, "YOUR_PREFIX-#{Time.now.strftime("%Y%m%d")}-#{$$}-#{rand(0x100000000).to_s(36)}-YOUR_SUFFIX") => "/tmp/YOUR_PREFIX-20190827-1-140far-YOUR_SUFFIX"
Dir::Tmpname.make_tmpname was removed in Ruby 2.5.0. Prior to Ruby 2.4.4 it could accept a directory path as a prefix, but as of Ruby 2.4.4, directory separators are removed.
Digging in tempfile.rb
you'll notice that Tempfile
includes Dir::Tmpname
. Inside you'll find make_tmpname
which does what you ask for.
require 'tmpdir' # => true File.join(Dir.tmpdir, Dir::Tmpname.make_tmpname("prefix-", nil)) # => "/tmp/prefix-20190827-1-dfhvld" File.join(Dir.tmpdir, Dir::Tmpname.make_tmpname(["prefix-", ".ext"], nil)) # => "/tmp/prefix-20190827-1-19zjck1.ext" File.join(Dir.tmpdir, Dir::Tmpname.make_tmpname(["prefix-", ".ext"], "suffix")) # => "/tmp/prefix-20190827-1-f5ipo7-suffix.ext"
Since Dir::Tmpname.make_tmpname
was removed in Ruby 2.5.0, this one falls back to using SecureRandom
:
require "tmpdir"
def generate_temp_filename(ext=".png")
filename = begin
Dir::Tmpname.make_tmpname(["x", ext], nil)
rescue NoMethodError
require "securerandom"
"#{SecureRandom.urlsafe_base64}#{ext}"
end
File.join(Dir.tmpdir, filename)
end
Since you only need the filename, what about using the SecureRandom for that:
require 'securerandom'
filename = "#{SecureRandom.hex(6)}.png" #=> "0f04dd94addf.png"
You can also use SecureRandom.alphanumeric
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With