How to Create Temporary Files in Ruby

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 🙂

8 thoughts on “How to Create Temporary Files in Ruby”

  1. 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.

    • 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 🙂

    • 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 🙂

  2. 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.

Comments are closed.