Peter Alfvin
Peter Alfvin

Reputation: 29439

Is there any way to make a Ruby temporary file permanent?

If I've created a temporary file through Tempfile, is there any way aside from copying it to another file that I can make it "permanent"? I'd like to avoid it getting deleted when the associated Tempfile instance is garbage collected or the process is terminated.

On a related point, is there a way to leverage the Tempfile mechanism (or use a similar mechanism) to get a "new" filename without having to create a file at that name?

Upvotes: 34

Views: 12891

Answers (3)

Jose Paez
Jose Paez

Reputation: 857

In my case I tried using ObjectSpace.undefine_finalizer(t) and it failed for me (I assume maybe due to the garbage collector once a request finishes, just taking everything from the tmp folder regardless). So I just did something simple and effective, move it outside of the tmp folder

  t = Tempfile.new("test")

  # Extra validation, check if new path is empty & clear it 
  FileUtils.rm(new_path) if File.file?(new_path)

  # Persisting the file
  FileUtils.mv(t.path, new_path)

Your are good to go! 👍🏼 (if you check on that new folder that file should be there for good)

Remember to clean that up later, possibly like what I did in the prevalidation

Upvotes: 0

Linuxios
Linuxios

Reputation: 35788

Not really. For the question itself, see this:

ObjectSpace.undefine_finalizer(tmpfile)

The Tempfile library uses Ruby ObjectSpace finalizers to automatically delete itself on garbage collection. By using the above line you can remove the Tempfile's ability to delete itself if you don't delete it. So, for example:

$ irb
2.0.0p0 :001 > require "tempfile"
 => true 
2.0.0p0 :002 > t = Tempfile.new("test")
 => #<Tempfile:/tmp/test20140122-6655-80p4b7> 
2.0.0p0 :003 > t.write("Hi!")
 => 3 
2.0.0p0 :004 > ObjectSpace.undefine_finalizer(t)
 => #<Tempfile:/tmp/test20140122-6655-80p4b7> 
2.0.0p0 :005 > exit
$ cat /tmp/test20140122-6655-80p4b7
Hi!
$ 

There's something else to be aware of though. Tempfile will use system temporary file directories like /tmp that the OS automatically cleans out every once in a while (for example on every boot). Because of this, even if you "persist" the file, you either need to be OK with it disappearing, or move it to a directory that doesn't get cleaned out by default, like /var/tmp (the Linux directory for persistant temporary files).


As for your second question, try this code from here:

Dir::Tmpname.create('your_application_prefix') { |path| puts path }

It requires a require "tmpdir".

Upvotes: 28

Patrick Oscity
Patrick Oscity

Reputation: 54734

I think the simplest solution may be to monkey patch the Tmpfile class to add a persist method. This method takes a filename where the temporary file will be moved to. Additionally, it removes the finalizer so that the temporary file will not be deleted at exit.

require 'tempfile'
require 'fileutils'

class Tempfile
  def persist(filename)
    FileUtils.mv(self.path, filename)
    ObjectSpace.undefine_finalizer(self)
  end
end

file = Tempfile.new('tmp')
file.write('hello world')
file.close
file.persist('hello.txt')

Running this program will create a persistent file ./hello.txt by moving the original temporary file instead of copying it.

Upvotes: 13

Related Questions