Feature #7148 ยป patch.diff
| lib/tempfile.rb | ||
|---|---|---|
|
# $Id$
|
||
|
#
|
||
|
require 'delegate'
|
||
|
require 'tmpdir'
|
||
|
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])
|
||
|
#
|
||
| ... | ... | |
|
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
|
||
| ... | ... | |
|
# 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
|
||
|
# <tt>close(true)</tt>.
|
||
|
def close!
|
||
|
_close
|
||
|
unlink
|
||
|
ObjectSpace.undefine_finalizer(self)
|
||
|
close(true)
|
||
|
end
|
||
|
# Unlinks (deletes) the file from the filesystem. One should always unlink
|
||
| ... | ... | |
|
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
|
||
| ... | ... | |
|
end
|
||
|
STDERR.print "done\n" if $DEBUG
|
||
|
ensure
|
||
|
tmpmap.delete(path)
|
||
|
end
|
||
|
end
|
||
|
# :startdoc:
|
||
| test/test_tempfile.rb | ||
|---|---|---|
|
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
|
||