Search code examples
rubyhandledelete-file

In ruby, how to rename a text file, keep same handle and delete the old file


I am using a constant (FLOG) as a handle to write to my log. At a given point, I have to use a temporary log, and later append that content to the regular log, all that with the same handle, which is used through a bunch of methods.
My test program is below. After closing the handle 'FLOG' associated with the temp log, when I re-assign FLOG to the new log, this somehow re-opens the temp log, and I can't delete it. Is there a way to make sure that the old temp file stays close (so I can delete it)

# Pre-existing log:
final_log = "final_#{Time.now.strftime("%Y%m%d")}.txt"

#Writing something in it
File.open(final_log, "w+") { |file| file.write("This is the final log: #{final_log}\n") }

# temp log:
temp_log = "temp_#{Time.now.strftime("%Y%m%d")}.txt"
FLOG = File.new(temp_log, "w+")

# write some stuff in temp_log
FLOG.puts "Writing in temp_log named #{temp_log}"

# closing handle for temp_log
FLOG.close  

# avoid constant reuse warning:
Object.send(:remove_const,'FLOG') if Object.const_defined?('FLOG')

# need to append temp_log content to final_log with handle FLOG 
FLOG = File.open(final_log, "a+") 

# appending old temp log to new log 
File.open(temp_log, "r").readlines.each do |line|
    puts "appending...  #{line}"
    FLOG.puts "appending...  #{line}"               
end

# closing handle    
FLOG.close

# this tells me that 'temp_log' is somehow re-opened:
ObjectSpace.each_object(File) { |f| puts("3: #{temp_log} is open") if f.path == temp_log && !f.closed? }

File.delete(temp_log)       # Cant do that:
# test_file2.rb:35:in `delete': Permission denied - temp_20150324.txt (Errno::EACCES)

Solution

  • If you're going to use a temp file, use tempfile

    require 'tempfile'
    
    # Pre-existing log:
    final_log = "final_#{Time.now.strftime("%Y%m%d")}.txt"
    
    #Writing something in it
    File.open(final_log, "w+") { |file| file.write("This is the final log: #{final_log}\n") }
    
    # give the tempfile a meaningful prefix
    temp_log = Tempfile.new('foobar')
    begin
      $flog = temp_log
    
      # write some stuff in temp_log
      $flog.puts "Writing in temp_log named #{temp_log.path}"
    
      # need to append temp_log content to final_log with handle $flog
      $flog = File.open(final_log, "a+")
    
      # reopen temp_log for reading, append to new log
      temp_log.open.readlines.each do |line|
        puts "appending...  #{line}"
          $flog.puts "appending...  #{line}"
      end
    
      # closing handle
      $flog.close
    ensure
      # delete temp_log
      temp_log.unlink
    end
    

    And while globals are generally bad, hacking a constant so that you can use it like a global is worse.