It rather seems that you're making *your* life too hard.
Probably...
Why do you need at_exit for this - especially if you want to be
compatible to File.open?
def File.open_tentative_output_file(tmp, final, mode = "w", &b)
raise "Need a block" unless b
File.open(tmp, mode, &b)
File.rename tmp, final
end
So the recommendation is that any "successful program completion"
cleanup be coerced into
the execute a block then cleanup construct cited above.
Enforcing the the block is required is the key. I was hoping to relax
that requirement but
you can't have everything.
That might do the trick...
BTW: here was my previous implementation... actually called OutputFile
# Setup ABORT detection preamble
$ABORTING=true
def exit_program *args
$ABORTING=false
__original_exit__(*args)
end
.....
alias :exit :exit_program
class OutputFile
def initialize filename,options={},&block
@filename,@options,@block=filename.dup,options.dup,block
@mode=options[:mode] || 'w'
unless append?
# TODO: Nice if we could validate valid filename here
instead of at "open?" time
# TODO: support :nosuperceed, :superceed
File.send(@options[:backup] ? :backup : :delete,@filename)
if File.exists? @filename
@work_filename=Filename.work filename
end
if block_given?
yield self
close
else
# TODO: Support suppression of autoclose on fatal_error
at_exit{close unless $ABORTING}
end
@file
end
def puts *args
open?
@file.puts(*args)
end
def print *args
open?
@file.puts(*args)
end
def close
if @file
@file.close
# TODO: what to do if rename fails for any reason
File.rename @work_filename,@filename if @work_filename
end
@file=nil
end
private
def open? # Defer open until first require output request
return if @file
# TODO: support wait on busy
@file=File.open(@work_filename || @filename,@mode)
end
def append?
/a/i.match(@mode)
end
end
def OutputFile *args,&block
OutputFile.new(*args,&block)
end