diff options
Diffstat (limited to 'lib/tempfile.rb')
| -rw-r--r-- | lib/tempfile.rb | 86 |
1 files changed, 52 insertions, 34 deletions
diff --git a/lib/tempfile.rb b/lib/tempfile.rb index 51c105d591..cd512bb1c5 100644 --- a/lib/tempfile.rb +++ b/lib/tempfile.rb @@ -29,7 +29,7 @@ require 'tmpdir' # require 'tempfile' # # # Tempfile.create with a block -# # The filename are choosen automatically. +# # The filename are chosen automatically. # # (You can specify the prefix and suffix of the filename by an optional argument.) # Tempfile.create {|f| # f.puts "foo" @@ -157,7 +157,7 @@ require 'tmpdir' class Tempfile < DelegateClass(File) # The version - VERSION = "0.2.1" + VERSION = "0.3.1" # Creates a file in the underlying file system; # returns a new \Tempfile object based on that file. @@ -221,45 +221,47 @@ 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) @opts = opts.freeze end - ObjectSpace.define_finalizer(@finalizer_obj, Remover.new(tmpfile.path)) - ObjectSpace.define_finalizer(self, Closer.new(tmpfile)) super(tmpfile) + + @finalizer_manager = FinalizerManager.new(__getobj__.path) + @finalizer_manager.register(self, __getobj__) end def initialize_dup(other) # :nodoc: initialize_copy_iv(other) super(other) - ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) + @finalizer_manager.register(self, __getobj__) end def initialize_clone(other) # :nodoc: initialize_copy_iv(other) super(other) - ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) + @finalizer_manager.register(self, __getobj__) end private def initialize_copy_iv(other) # :nodoc: @unlinked = other.unlinked @mode = other.mode @opts = other.opts - @finalizer_obj = other.finalizer_obj + @finalizer_manager = other.finalizer_manager end # Opens or reopens the file with mode "r+". def open _close - ObjectSpace.undefine_finalizer(self) + mode = @mode & ~(File::CREAT|File::EXCL) __setobj__(File.open(__getobj__.path, mode, **@opts)) - ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) + + @finalizer_manager.register(self, __getobj__) + __getobj__ end @@ -327,7 +329,9 @@ class Tempfile < DelegateClass(File) # may not be able to unlink on Windows; just ignore return end - ObjectSpace.undefine_finalizer(@finalizer_obj) + + @finalizer_manager.unlinked = true + @unlinked = true end alias delete unlink @@ -361,35 +365,35 @@ class Tempfile < DelegateClass(File) protected - attr_reader :unlinked, :mode, :opts, :finalizer_obj + attr_reader :unlinked, :mode, :opts, :finalizer_manager - class Closer # :nodoc: - def initialize(tmpfile) - @tmpfile = tmpfile - end - - def call(*args) - @tmpfile.close - end - end + class FinalizerManager # :nodoc: + attr_accessor :unlinked - class Remover # :nodoc: def initialize(path) - @pid = Process.pid + @open_files = {} @path = path + @pid = Process.pid + @unlinked = false end - def call(*args) - return if @pid != Process.pid + def register(obj, file) + ObjectSpace.undefine_finalizer(obj) + ObjectSpace.define_finalizer(obj, self) + @open_files[obj.object_id] = file + end - $stderr.puts "removing #{@path}..." if $DEBUG + def call(object_id) + @open_files.delete(object_id).close - begin - File.unlink(@path) - rescue Errno::ENOENT + if @open_files.empty? && !@unlinked && Process.pid == @pid + $stderr.puts "removing #{@path}..." if $DEBUG + begin + File.unlink(@path) + rescue Errno::ENOENT + end + $stderr.puts "done" if $DEBUG end - - $stderr.puts "done" if $DEBUG end end @@ -546,8 +550,8 @@ end # # Implementation note: # -# The keyword argument +anonymous=true+ is implemented using FILE_SHARE_DELETE on Windows. -# O_TMPFILE is used on Linux. +# The keyword argument <tt>anonymous=true</tt> is implemented using +FILE_SHARE_DELETE+ on Windows. +# +O_TMPFILE+ is used on Linux. # # Related: Tempfile.new. # @@ -560,6 +564,8 @@ def Tempfile.create(basename="", tmpdir=nil, mode: 0, anonymous: false, **option end class << Tempfile +# :stopdoc: + private def create_with_filename(basename="", tmpdir=nil, mode: 0, **options) tmpfile = nil Dir::Tmpname.create(basename, tmpdir, **options) do |tmpname, n, opts| @@ -589,6 +595,16 @@ private def create_with_filename(basename="", tmpdir=nil, mode: 0, **options) end end +if RUBY_VERSION < "3.2" + module PathAttr # :nodoc: + attr_reader :path + + def self.set_path(file, path) + file.extend(self).instance_variable_set(:@path, path) + end + end +end + private def create_anonymous(basename="", tmpdir=nil, mode: 0, **options, &block) tmpfile = nil tmpdir = Dir.tmpdir() if tmpdir.nil? @@ -604,12 +620,14 @@ private def create_anonymous(basename="", tmpdir=nil, mode: 0, **options, &block mode |= File::SHARE_DELETE | File::BINARY # Windows needs them to unlink the opened file. tmpfile = create_with_filename(basename, tmpdir, mode: mode, **options) File.unlink(tmpfile.path) + tmppath = tmpfile.path end path = File.join(tmpdir, '') - if tmpfile.path != path + unless tmppath == path # clear path. tmpfile.autoclose = false tmpfile = File.new(tmpfile.fileno, mode: File::RDWR, path: path) + PathAttr.set_path(tmpfile, path) if defined?(PathAttr) end if block begin |
