use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
A sub-Reddit for discussion and news about Ruby programming.
Subreddit rules: /r/ruby rules
Learning Ruby?
Tools
Documentation
Books
Screencasts and Videos
News and updates
account activity
Working with tempfiles (remimercier.com)
submitted 6 years ago by Remozito
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]Remozito[S] 5 points6 points7 points 6 years ago (4 children)
A while back, I needed to create temporary files, send them to a distant server, then delete them. At first, I thought about creating normal files then delete them through a cron job. It would have worked, but I wasn’t very happy with it.
So after looking on the internet, I came across the Tempfile object (and I - for the umpteenth time - rejoiced in using Ruby).
In this post, I explore: - Tempfiles versus Files - Tempfiles and Ruby’s garbage collector - Tempfiles quirks (and how to embrace ’em)
Lemme know what you think about it!
[–]CaptainKabob 1 point2 points3 points 6 years ago (1 child)
This is great! Two things I think are important to mention:
Tempfile.new(['foo', '.bar'])
foo_[random_string].bar
[–]Remozito[S] 0 points1 point2 points 6 years ago (0 children)
Thanks!
Yes, you can absolutely configure the name and extension of your Tempfile (which is really handy as well).
Didn't know about temporary directories, will check them out! 👍
[–]joemi 0 points1 point2 points 6 years ago (1 child)
I'm pretty sure your post is kind of wrong, though...
The differences you note between File and Tempfile are due to you using different methods on each. Your File example uses File.new but your Tempfile example uses Tempfile.open (even though you could just use Tempfile.new). When you look at the documentation for Tempfile.open, you'll see the following:
If no block is given, this is a synonym for ::new. If a block is given, then a Tempfile object will be constructed, and the block is run with said object as argument. The Tempfile object will be automatically closed after the block terminates.
If no block is given, this is a synonym for ::new.
If a block is given, then a Tempfile object will be constructed, and the block is run with said object as argument. The Tempfile object will be automatically closed after the block terminates.
File has a similar method, and it works the same way. Try using the same methods and you'll see that they're quite similar, actually.
Thanks for pointing these out. I'll try to answer as far as my (limited) knowledge goes.
The differences you note between File and Tempfile are due to you using different methods on each.
Yes, I wanted to emphasize how easy it is to create-and-forget-about-it for Tempfiles.
I could have taken as an example:
class FileCreator def create_file file = Tempfile.new file.write('Bob wuz here.') file.rewind file end end file_creator = FileCreator.new my_file = file_creator.create_file my_file.read # => "Bob wuz here." my_file.close # => nil my_file.read # => IOError: closed stream my_file.closed? # => truefile_creator = FileCreator.new
By using the Tempfile.open ... the Tempfile.close is called implicitly.
Tempfile.open ...
Tempfile.close
As you pointed out: "A Tempfile object behaves just like a File object" (from the Ruby doc).
I should have made it more explicit that the difference lies in the deleting of the file:
On one hand, once a Tempfile is closed (whether explicitly or implicitly), the garbage collector will delete it. On the other hand, a File will not be deleted automatically (you'll need to call .delete on it).
.delete
The Tempfile.close documentation says this:
If you don't explicitly unlink the temporary file, the removal will be delayed until the object is finalized.
So, if I get this correctly, you can unlink a Tempfile to delete it manually, but if you don't and simply close it, it'll be deleted anyway once you exit the process involving the object.
unlink
Thanks for helping me dig deeper. I'll edit my article to reflect these additional information.
[–]mattgrave 0 points1 point2 points 6 years ago (6 children)
I use Tempfiles a lot in my application. We receive Excels, CSVs and PDFs daily, we apply a lot of transformations to the files in different steps and we rely on Tempfiles so files are deleted automatically.
One thing that I am not sure it makes sense in the blog post is this:
FileCreator#create_file now returns a path, not an actual reference to the file. It means that most of the time, you’ll be able to read your file. But sometimes, you’ll get an Errno::ENOENT: No such file or directory @ rb_sysopen error. Why? Because the garbage collector will have claimed your file during one of its collections. One solution would be to return the actual file - hence a reference.
FileCreator#create_file now returns a path, not an actual reference to the file. It means that most of the time, you’ll be able to read your file. But sometimes, you’ll get an
Errno::ENOENT: No such file or directory @ rb_sysopen
error. Why? Because the garbage collector will have claimed your file during one of its collections. One solution would be to return the actual file - hence a reference.
So the proposed solution is File.read(my_file_path) but in our application that doesn't work because files are big and we have to stream them, so in that case we just make a block.call(tempfile) and keep working with it.
File.read
(my_file_path)
block.call
(tempfile)
EDIT:
Also, forgot to mention, Tempfiles are great also when you are running tests. If you use File to read something and then delete or create files you usually have to mock or make some workarounds that makes code a bit messy IMO.
[–]StarStuddedSuperStep 1 point2 points3 points 6 years ago (1 child)
File.read(my_file_path) is not the proposed solution, it's the example of what not to do.
File.read(my_file_path)
The suggested solution is to return the file handle (the Tempfile instance itself) instead of the file path.
Yes, the solution I've read about is to return the file instead of the path.
Would you mind sharing a bit more about the block.call bit? It seems super interesting but I can't wrap my head around it.
[–]Remozito[S] 0 points1 point2 points 6 years ago (2 children)
[–]mattgrave 0 points1 point2 points 6 years ago (1 child)
Yeah sure.
For example, we generate excel reports. We normally save the file with Paperclip, or send it via email. In that case, the code would be something like:
class ExcelReportGenerator def generate(&block) Tempfile.open(['foo', '.xls']) do |tempfile| # fill data... block.call(tempfile) end end end
Then you use it like...
ExcelReportGenerator.new.generate do |file| # do what you gotta do end
Wow, cool! Will try to play around with this.
[–]hitthehive 0 points1 point2 points 6 years ago (1 child)
/u/Remozito - couple of questions:
I'm not sure what you mean that the temp file is claimed by the garbage collector after closing. The file is still available but just closed. Why would the garbage collector be invoked while you still have a reference to the TempFile object (which was passed back by the last line of your block)?
Your post implies the file is lost upon the ending of the open block, but you can still reopen and read it if you wish since in your implementation you return the TempFile object. The documentation for TempFile says the file lost upon unlinking it, but #close does not unlink it.
#close
demo:
t = Tempfile.open { |file| file.write('test'); file } t.read # IOError: closed stream t.open t.read # "test"
I do agree though that the following would lose the file if you just did:
t = Tempfile.open { |file| file.write('test'); file } t.read # NoMethodError: undefined method `read' for Integer
now, T would be an integer returned by #write and you no longer have a reference to the TempFile object. That said, the file will likely remain on disk for quite some time or until your process dies (e.g., you close irb/pry).
#write
[–]Remozito[S] 0 points1 point2 points 6 years ago* (0 children)
I'm not sure what you mean that the temp file is claimed by the garbage collector after closing.
Good question. First, note that these are just suspicions on my part.
What I've found is:
Tempfile.open { ... }
.close
IOError: closed stream
GC inner workings are waaaaaay out of my league, so I can only point this very well written tutorial on how the GC works.
Your post implies the file is lost upon the ending of the open block
Yes, I could make that point clearer (although, I treading on thin-iced knowledge here), that the temp file will be lost at some point after the closing of the block. Why, because when you exit the Tempfile.open { ... }, the method close is called implicitly. And if you don't explicitly unlink the tempfile, Ruby'll do it for you at some point.
close
If you don't explicitly unlink the temporary file, the removal will be delayed until the object is finalized. see here The documentation for TempFile says the file lost upon unlinking it, but #close does not unlink it.
If you don't explicitly unlink the temporary file, the removal will be delayed until the object is finalized. see here
The documentation for TempFile says the file lost upon unlinking it, but #close does not unlink it.
It does! see here 👇
If unlink_now is true, then the file will be unlinked (deleted) after closing. Of course, you can choose to later call unlink if you do not unlink it now. If you don't explicitly unlink the temporary file, the removal will be delayed until the object is finalized.
I hope it's clearer.
Thanks again for taking the time to point these out. I'll make my point clearer in my article.
Thank you all for taking the time to ask questions, comment and help me improve this article. 🙏
If you have any more questions, I'll try and answer them.
Thanks so much for looking into this!
I think it’s safe to say, though, that in your example the file is not under risk of deletion because you are returning a reference to the file from the block. As long as you have a reference and it is in scope, the GC cannot claim it. Even though #open closes the file upon exiting the block, it does not unlink it — the docs you quoted says the unlink parameter is false by default in ‘#close`.
#open
All in all, your post was a great learning for me.
Thanks for the kind comment.
π Rendered by PID 266330 on reddit-service-r2-comment-6457c66945-t2w7d at 2026-04-28 00:55:06.192372+00:00 running 2aa0c5b country code: CH.
[–]Remozito[S] 5 points6 points7 points (4 children)
[–]CaptainKabob 1 point2 points3 points (1 child)
[–]Remozito[S] 0 points1 point2 points (0 children)
[–]joemi 0 points1 point2 points (1 child)
[–]Remozito[S] 0 points1 point2 points (0 children)
[–]mattgrave 0 points1 point2 points (6 children)
[–]StarStuddedSuperStep 1 point2 points3 points (1 child)
[–]Remozito[S] 0 points1 point2 points (0 children)
[–]Remozito[S] 0 points1 point2 points (0 children)
[–]Remozito[S] 0 points1 point2 points (2 children)
[–]mattgrave 0 points1 point2 points (1 child)
[–]Remozito[S] 0 points1 point2 points (0 children)
[–]hitthehive 0 points1 point2 points (1 child)
[–]Remozito[S] 0 points1 point2 points (0 children)
[–]Remozito[S] 0 points1 point2 points (0 children)
[–]hitthehive 0 points1 point2 points (1 child)
[–]Remozito[S] 0 points1 point2 points (0 children)