diff options
-rw-r--r-- | lib/tempfile.rb | 72 | ||||
-rw-r--r-- | test/test_tempfile.rb | 16 |
2 files changed, 70 insertions, 18 deletions
diff --git a/lib/tempfile.rb b/lib/tempfile.rb index bf57bb68d9..ca8fa4bf7e 100644 --- a/lib/tempfile.rb +++ b/lib/tempfile.rb @@ -152,26 +152,49 @@ class Tempfile < DelegateClass(File) @unlinked = false @mode = mode|File::RDWR|File::CREAT|File::EXCL + @finalizer_obj = Object.new + tmpfile = nil ::Dir::Tmpname.create(basename, tmpdir, **options) do |tmpname, n, opts| opts[:perm] = 0600 - @tmpfile = File.open(tmpname, @mode, **opts) + tmpfile = File.open(tmpname, @mode, **opts) @opts = opts.freeze end - ObjectSpace.define_finalizer(self, Remover.new(@tmpfile)) + ObjectSpace.define_finalizer(@finalizer_obj, Remover.new(tmpfile.path)) + ObjectSpace.define_finalizer(self, Closer.new(tmpfile)) - super(@tmpfile) + super(tmpfile) + end + + def initialize_dup(other) + initialize_copy_iv(other) + super(other) + ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) + end + + def initialize_clone(other) + initialize_copy_iv(other) + super(other) + ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) + end + + private def initialize_copy_iv(other) + @unlinked = other.unlinked + @mode = other.mode + @opts = other.opts + @finalizer_obj = other.finalizer_obj end # Opens or reopens the file with mode "r+". def open _close + ObjectSpace.undefine_finalizer(self) mode = @mode & ~(File::CREAT|File::EXCL) - @tmpfile = File.open(@tmpfile.path, mode, **@opts) - __setobj__(@tmpfile) + __setobj__(File.open(__getobj__.path, mode, **@opts)) + ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) end def _close # :nodoc: - @tmpfile.close + __getobj__.close end protected :_close @@ -228,13 +251,13 @@ class Tempfile < DelegateClass(File) def unlink return if @unlinked begin - File.unlink(@tmpfile.path) + File.unlink(__getobj__.path) rescue Errno::ENOENT rescue Errno::EACCES # may not be able to unlink on Windows; just ignore return end - ObjectSpace.undefine_finalizer(self) + ObjectSpace.undefine_finalizer(@finalizer_obj) @unlinked = true end alias delete unlink @@ -242,43 +265,56 @@ class Tempfile < DelegateClass(File) # Returns the full path name of the temporary file. # This will be nil if #unlink has been called. def path - @unlinked ? nil : @tmpfile.path + @unlinked ? nil : __getobj__.path end # Returns the size of the temporary file. As a side effect, the IO # buffer is flushed before determining the size. def size - if [email protected]? - @tmpfile.size # File#size calls rb_io_flush_raw() + if !__getobj__.closed? + __getobj__.size # File#size calls rb_io_flush_raw() else - File.size(@tmpfile.path) + File.size(__getobj__.path) end end alias length size # :stopdoc: def inspect - if @tmpfile.closed? + if __getobj__.closed? "#<#{self.class}:#{path} (closed)>" else "#<#{self.class}:#{path}>" end end - class Remover # :nodoc: + protected + + attr_reader :unlinked, :mode, :opts, :finalizer_obj + + class Closer # :nodoc: def initialize(tmpfile) - @pid = Process.pid @tmpfile = tmpfile end def call(*args) + @tmpfile.close + end + end + + class Remover # :nodoc: + def initialize(path) + @pid = Process.pid + @path = path + end + + def call(*args) return if @pid != Process.pid - $stderr.puts "removing #{@tmpfile.path}..." if $DEBUG + $stderr.puts "removing #{@path}..." if $DEBUG - @tmpfile.close begin - File.unlink(@tmpfile.path) + File.unlink(@path) rescue Errno::ENOENT end diff --git a/test/test_tempfile.rb b/test/test_tempfile.rb index 87c1241df8..d5d684b016 100644 --- a/test/test_tempfile.rb +++ b/test/test_tempfile.rb @@ -63,6 +63,22 @@ class TestTempfile < Test::Unit::TestCase assert_match(/\.txt$/, File.basename(t.path)) end + def test_dup + t = tempfile + t2 = t.dup + t2.close + assert_equal true, t2.closed? + assert_equal false, t.closed? + end + + def test_clone + t = tempfile + t2 = t.clone + t2.close + assert_equal true, t2.closed? + assert_equal false, t.closed? + end + def test_unlink t = tempfile("foo") path = t.path |