diff --git a/lib/tempfile.rb b/lib/tempfile.rb index 9f4ed8f..8dbe53d 100644 --- a/lib/tempfile.rb +++ b/lib/tempfile.rb @@ -4,7 +4,6 @@ # $Id$ # -require 'delegate' require 'tmpdir' require 'thread' @@ -78,9 +77,11 @@ require 'thread' # Tempfile itself however may not be entirely thread-safe. If you access the # same Tempfile object from multiple threads then you should protect it with a # mutex. -class Tempfile < DelegateClass(File) +class Tempfile < File include Dir::Tmpname + @@tmpmap = {} + # call-seq: # new(basename, [tmpdir = Dir.tmpdir], [options]) # @@ -143,33 +144,26 @@ class Tempfile < DelegateClass(File) else opts = perm end - @data[1] = @tmpfile = File.open(tmpname, mode, opts) + @data[1] = @tmpfile = super(tmpname, mode, opts) @data[0] = @tmpname = tmpname @mode = mode & ~(File::CREAT|File::EXCL) perm or opts.freeze @opts = opts end + @@tmpmap[@tmpname] = 1 + @data[2] = @@tmpmap + end - super(@tmpfile) + def dup + @@tmpmap[@tmpname] += 1 + super end # Opens or reopens the file with mode "r+". def open - @tmpfile.close if @tmpfile - @tmpfile = File.open(@tmpname, @mode, @opts) - @data[1] = @tmpfile - __setobj__(@tmpfile) - end - - def _close # :nodoc: - begin - @tmpfile.close if @tmpfile - ensure - @tmpfile = nil - @data[1] = nil if @data - end + opts = @opts.is_a?(Hash) ? @opts : nil + @tmpfile = reopen(@tmpname, @mode, opts) end - protected :_close # Closes the file. If +unlink_now+ is true, then the file will be unlinked # (deleted) after closing. Of course, you can choose to later call #unlink @@ -178,19 +172,23 @@ class Tempfile < DelegateClass(File) # If you don't explicitly unlink the temporary file, the removal # will be delayed until the object is finalized. def close(unlink_now=false) + begin + super() if @tmpfile + ensure + @tmpfile = nil + @data[1] = nil if @data + end if unlink_now - close! - else - _close + unlink + ObjectSpace.undefine_finalizer(self) end + nil end # Closes and unlinks (deletes) the file. Has the same effect as called # close(true). def close! - _close - unlink - ObjectSpace.undefine_finalizer(self) + close(true) end # Unlinks (deletes) the file from the filesystem. One should always unlink @@ -271,13 +269,13 @@ class Tempfile < DelegateClass(File) def call(*args) return if @pid != $$ - path, tmpfile = *@data + path, tmpfile, tmpmap = *@data STDERR.print "removing ", path, "..." if $DEBUG tmpfile.close if tmpfile - if path + if path && tmpmap.has_key?(path) && (tmpmap[path] -= 1) == 0 begin File.unlink(path) rescue Errno::ENOENT @@ -285,6 +283,9 @@ class Tempfile < DelegateClass(File) end STDERR.print "done\n" if $DEBUG + + ensure + tmpmap.delete(path) end end # :startdoc: diff --git a/test/test_tempfile.rb b/test/test_tempfile.rb index 1462a98..b7cf77e 100644 --- a/test/test_tempfile.rb +++ b/test/test_tempfile.rb @@ -304,5 +304,19 @@ puts Tempfile.new('foo').path assert_equal(0600, t.stat.mode & 0777) end end -end + def test_dup + t = tempfile("foo") + t_dup = t.dup + assert(t_dup.is_a?(File)) + end + + def test_duped_finalizer + t = Tempfile.new("foo") + path = t.path + t_dup = t.dup + t = nil + GC.start + assert(File.exist?(path)) + end +end