Creating a temporary file gives you an empty file with a random name inside your O.S. (Operating System) temporary folder.
This file gets deleted automatically.
How can you do this in Ruby?
Like this:
require 'tempfile' Tempfile.create { |f| f << "abc\n" }
Where f
is your file & <<
writes to it.
This is built into Ruby so you don’t have to install any gems.
Ok.
That’s simple enough, but you may still have questions.
Like:
- When is the file deleted exactly?
- Why can’t I read back from my temp file?
- Are tempfiles guaranteed to be unique?
I’m going to answer these questions & a few more so you can develop a deeper understanding!
What’s The Difference Between New & Create?
You’ll notice that Tempfile
has two methods for creating files.
One is new
, and the other is create
.
What’s the difference?
According to the documentation, new
creates a Tempfile
object (as you would expect), but create
gives you a File
object.
I don’t think that really matters, because Tempfile
delegates to File
.
The real difference is that create
accepts a block.
But new
doesn’t.
Try this:
Tempfile.new {}
You’ll get this helpful warning:
# warning: Tempfile.new doesn't call the given block.
You can use a block with create
to ensure that your temporary file gets deleted after the block ends.
This brings up the following question…
When is A TempFile Deleted?
You can control when the file is deleted, by deleting it yourself like a regular file (delete
method), or using create
with a block.
Automatic deleting works when:
- Your programs ends
- Your file gets “garbage collected”, in other words, the file is removed from memory by an algorithm to free up space
The later can only happen if you aren’t holding any references to the file.
Like a variable.
This is what I mean:
t = Tempfile.new
When t
goes out of scope, then the temporary file can be removed.
If you’re using Linux, a very interesting tool to watch which files are being created & deleted in real time is inotify-tools.
Try this command:
inotifywait /tmp -m --format "%w %e %f %T" --timefmt "%H:%m:%S"
Then run some Ruby code that creates files.
Example:
ruby -rtempfile -e "def c; t = Tempfile.new('testing'); end; c; sleep 10"
You’ll see this:
/tmp/ CREATE testing20190506-11391-1wqcng0 14:51:48 /tmp/ OPEN testing20190506-11391-1wqcng0 14:51:48 /tmp/ CLOSE_WRITE,CLOSE testing20190506-11391-1wqcng0 14:51:58 /tmp/ DELETE testing20190506-11391-1wqcng0 14:51:58
Why Can’t You Read Back From Your Temp File?
If you try to read back from one of your temporary files you’ll get an empty string.
Example:
Tempfile.create { |f| f << "abc\n"; f.read } # ""
Why is that?
It turns out that Files
are IO
objects.
IO objects have a position pointer & when you write to a file this position advances.
So if you want to read, you have to rewind this pointer.
Like this:
Tempfile.create { |f| f << "abc\n"; f.rewind; f.read } # "abc\n"
You may also need to flush your file contents.
Example:
temp = Tempfile.new temp << "1" temp << "2" temp.flush
Are Tempfiles Really Unique?
Tempfile creates a unique filename & sets the permission mode to 600, which means that only the user who created this file can read it.
But can there ever be a duplicated name?
The documentation says this:
“Tempfile’s filename picking method is both thread-safe and inter-process-safe: it guarantees that no other threads or processes will pick the same filename.”
And the description for new
also says that it may raise an error if it can’t find a unique filename.
It’s also good to know that you can set a prefix for your files.
Like this:
Tempfile.new("banana-").path # "/tmp/banana-20190426-25403-1cm7sjt"
This decreases the possibility of a name collision.
Opening A Tempfile in Binary Mode
If you’re working with images, music, or anything else than plain text you may want to set your file mode to binary.
The binary file mode will stop line-ending conversion.
As a result, data will be more “raw” & you’ll avoid breaking parts of the binary file.
Binary mode is not enabled by default:
temp = Tempfile.new temp.binmode? # false
You can enable it like this:
temp = Tempfile.new temp.binmode temp.binmode? # true
This also works with the create
method:
Tempfile.create do |f| f.binmode f << "bacon\n" end
Summary
You’ve learned about temporary files in Ruby using the Tempfile
class!
Now it’s your turn to practice.
Thanks for reading 🙂
Nice Article! The only thing that is missing is when we might use temp files. I’ve been programming for quite a while and still haven’t use them directly.
you might get to use this if you ever use chef
Tempfile might be what I’ve been looking for. Tokara, my rails app has a model with dependent properties. I instantiate the object using 3 forms. This particular model doesn’t have a table, so after validations I have to hide the data from form1 inside form2 and so on. Tempfile could be used to hold data from form1 and form2. When form3 data is validated I can then initialize the object by retrieving the data in Tempfile.
Temporary files can be used for intermediate tasks (like compressing, downloading, editing) or as some sort of cache.
Look inside your O.S. temporary folder (
/tmp
in Linux) to see what kind of files are there.It’s one more tool to have in your toolbox 🙂
Can you explain a bit about
flush
?Hi Rostyslav,
Thanks for your question!
Flush is a method on IO objects that will tell the Operating System to write the contents to disk.
This is a thing because both Ruby & your O.S. buffer write operations for performance reasons. Calling
flush
forces this buffer to empty & the actual write operation to happen.Hope that helps 🙂
Hey Jesus,
what are the main differences between a StringIO and a Tempfile? Thank you for all your amazing blogs. They help so much and they are beautifully designed.
Hey Ben,
great question!
They are both very similar, the main difference is that StringIO will keep everything in memory & Tempfile will use the file system.
What does that mean in practice?
It means that StringIO is going to be faster (benchmark to be sure), but it’s going to be limited by your RAM.