Skip to Content
Course content

67: Tempfile for Temporary Files

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I was reviewing a pull request for a reporting tool that generated large CSV exports. The developer had implemented a simple logic: create a file called export.csv in a /tmp folder, write the data, and then upload it to S3. It worked perfectly on his machine. Then we hit production. Within an hour, users were reporting that their exports contained data from other customers. Because the filename was hardcoded, two concurrent requests were writing to the exact same file, stomping all over each other. It was a classic race condition that could have been avoided entirely with the right tool.

Automatic Naming and Uniqueness

When you need a place to stash data temporarily—maybe to pass a file to an external system command or to buffer a large dataset that doesn't fit in RAM—you should almost never pick the filename yourself. Ruby's Tempfile library handles this by generating a unique filename that is guaranteed not to collide with other processes.

When you initialize a Tempfile, Ruby creates a file in the system's temporary directory (usually /tmp on Unix) with a randomized name. You don't have to worry about File.exists? checks or appending timestamps to your filenames.

require 'tempfile'

# Create a tempfile. You can provide a prefix for easier debugging in /tmp
temp = Tempfile.new(['user_report', '.csv'])

begin
  temp.puts("User ID, Email")
  temp.puts("1, dev@example.com")
  temp.rewind # Important: move the pointer back to the start before reading

  puts "Temporary file created at: #{temp.path}"
  puts "Content: #{temp.read}"
ensure
  temp.close
  temp.unlink # Deletes the file from the filesystem
end

Managing the Lifecycle and Cleanup

One of the best things about Tempfile is that it attempts to clean up after itself. If you forget to call unlink, Ruby will eventually delete the file when the object is garbage collected. However, I strongly advise against relying on the garbage collector. In a high-traffic production app, relying on the GC can lead to your /tmp directory filling up faster than the GC can keep up, which can crash your entire server.

The safest pattern is to use a begin...ensure block. By calling unlink in the ensure block, you guarantee the file is deleted even if an exception is raised during processing. I've seen too many "disk space exhausted" alerts caused by developers who assumed the GC would handle their temporary files.

Handling Binary Data and System Calls

Often, we use Tempfile because we need to pass a file path to a shell command or a third-party library that doesn't accept strings. If you're dealing with images, PDFs, or encrypted blobs, remember to set the file to binary mode. By default, Tempfile opens in text mode, which can mangle your data on some operating systems.

require 'tempfile'

# Use binmode for non-text data
temp = Tempfile.new('image_buffer', '.jpg')
temp.binmode

begin
  # Imagine 'image_data' is a binary string from an API
  image_data = "\xFF\xD8\xFF\xE0\x00\x10JFIF" 
  temp.write(image_data)
  temp.flush # Ensure data is written to disk before the system call
  
  # Pass the path to an external tool (e.g., ImageMagick)
  system("identify #{temp.path}")
ensure
  temp.close
  temp.unlink
end

A quick tip: always call flush or close before passing the path to an external process. Otherwise, the external tool might try to read the file while the data is still sitting in Ruby's internal write buffer, resulting in a truncated or empty file.




📋 Practical Task

Building a Temporary Log Analysis Buffer

You need to create a script that simulates processing a large stream of log entries. Because the logs are too large to keep in a single array in memory, you will use a Tempfile to buffer them.

Write a script that does the following:

  • Creates a Tempfile with the prefix 'log_buffer'.
  • Iterates through an array of strings (simulate logs) and writes each one to the tempfile. Include at least five strings, some containing the word "ERROR" and some containing "INFO".
  • Rewinds the file and reads it back line-by-line to count how many lines contain the word "ERROR".
  • Prints the total error count and the path of the temporary file used.
  • Ensures the file is closed and unlinked regardless of whether the process succeeds or fails.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.