1 module ActiveSupport #:nodoc:
2 module CoreExtensions #:nodoc:
3 module File #:nodoc:
4 module Atomic
5 # Write to a file atomically. Useful for situations where you don't
6 # want other processes or threads to see half-written files.
7 #
8 # File.atomic_write("important.file") do |file|
9 # file.write("hello")
10 # end
11 #
12 # If your temp directory is not on the same filesystem as the file you're
13 # trying to write, you can provide a different temporary directory.
14 #
15 # File.atomic_write("/data/something.important", "/data/tmp") do |f|
16 # file.write("hello")
17 # end
18 def atomic_write(file_name, temp_dir = Dir.tmpdir)
19 require 'tempfile' unless defined?(Tempfile)
20 require 'fileutils' unless defined?(FileUtils)
21
22 temp_file = Tempfile.new(basename(file_name), temp_dir)
23 yield temp_file
24 temp_file.close
25
26 begin
27 # Get original file permissions
28 old_stat = stat(file_name)
29 rescue Errno::ENOENT
30 # No old permissions, write a temp file to determine the defaults
31 check_name = join(dirname(file_name), ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}")
32 open(check_name, "w") { }
33 old_stat = stat(check_name)
34 unlink(check_name)
35 end
36
37 # Overwrite original file with temp file
38 FileUtils.mv(temp_file.path, file_name)
39
40 # Set correct permissions on new file
41 chown(old_stat.uid, old_stat.gid, file_name)
42 chmod(old_stat.mode, file_name)
43 end
44 end
45 end
46 end
47 end