aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/lib/active_support/core_ext/file.rb24
-rw-r--r--activesupport/lib/active_support/core_ext/file/atomic.rb46
-rw-r--r--activesupport/test/core_ext/file_test.rb50
3 files changed, 94 insertions, 26 deletions
diff --git a/activesupport/lib/active_support/core_ext/file.rb b/activesupport/lib/active_support/core_ext/file.rb
index 45d93b220f..e03f8ac44e 100644
--- a/activesupport/lib/active_support/core_ext/file.rb
+++ b/activesupport/lib/active_support/core_ext/file.rb
@@ -1,21 +1,5 @@
-require 'tempfile'
+require 'active_support/core_ext/file/atomic'
-# Write to a file atomically. Useful for situations where you don't
-# want other processes or threads to see half-written files.
-#
-# File.atomic_write("important.file") do |file|
-# file.write("hello")
-# end
-#
-# If your temp directory is not on the same filesystem as the file you're
-# trying to write, you can provide a different temporary directory.
-#
-# File.atomic_write("/data/something.important", "/data/tmp") do |f|
-# file.write("hello")
-# end
-def File.atomic_write(file_name, temp_dir = Dir.tmpdir)
- temp_file = Tempfile.new(File.basename(file_name), temp_dir)
- yield temp_file
- temp_file.close
- File.rename(temp_file.path, file_name)
-end \ No newline at end of file
+class File #:nodoc:
+ extend ActiveSupport::CoreExtensions::File::Atomic
+end
diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb
new file mode 100644
index 0000000000..4d3cf5423f
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/file/atomic.rb
@@ -0,0 +1,46 @@
+require 'tempfile'
+
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module File #:nodoc:
+ module Atomic
+ # Write to a file atomically. Useful for situations where you don't
+ # want other processes or threads to see half-written files.
+ #
+ # File.atomic_write("important.file") do |file|
+ # file.write("hello")
+ # end
+ #
+ # If your temp directory is not on the same filesystem as the file you're
+ # trying to write, you can provide a different temporary directory.
+ #
+ # File.atomic_write("/data/something.important", "/data/tmp") do |f|
+ # file.write("hello")
+ # end
+ def atomic_write(file_name, temp_dir = Dir.tmpdir)
+ temp_file = Tempfile.new(basename(file_name), temp_dir)
+ yield temp_file
+ temp_file.close
+
+ begin
+ # Get original file permissions
+ old_stat = stat(file_name)
+ rescue Errno::ENOENT
+ # No old permissions, write a temp file to determine the defaults
+ check_name = ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}"
+ new(check_name, "w")
+ old_stat = stat(check_name)
+ unlink(check_name)
+ end
+
+ # Overwrite original file with temp file
+ rename(temp_file.path, file_name)
+
+ # Set correct permissions on new file
+ chown(old_stat.uid, old_stat.gid, file_name)
+ chmod(old_stat.mode, file_name)
+ end
+ end
+ end
+ end
+end
diff --git a/activesupport/test/core_ext/file_test.rb b/activesupport/test/core_ext/file_test.rb
index 5efe357e9f..63b470684f 100644
--- a/activesupport/test/core_ext/file_test.rb
+++ b/activesupport/test/core_ext/file_test.rb
@@ -1,9 +1,8 @@
require 'abstract_unit'
class AtomicWriteTest < Test::Unit::TestCase
-
def test_atomic_write_without_errors
- contents = "Atomic Text"
+ contents = "Atomic Text"
File.atomic_write(file_name, Dir.pwd) do |file|
file.write(contents)
assert !File.exist?(file_name)
@@ -13,7 +12,7 @@ class AtomicWriteTest < Test::Unit::TestCase
ensure
File.unlink(file_name) rescue nil
end
-
+
def test_atomic_write_doesnt_write_when_block_raises
File.atomic_write(file_name) do |file|
file.write("testing")
@@ -22,8 +21,47 @@ class AtomicWriteTest < Test::Unit::TestCase
rescue
assert !File.exist?(file_name)
end
-
- def file_name
- "atomic.file"
+
+ def test_atomic_write_preserves_file_permissions
+ contents = "Atomic Text"
+ File.open(file_name, "w", 0755) do |file|
+ file.write(contents)
+ assert File.exist?(file_name)
+ end
+ assert File.exist?(file_name)
+ assert_equal "100755", file_mode
+ assert_equal contents, File.read(file_name)
+
+ File.atomic_write(file_name, Dir.pwd) do |file|
+ file.write(contents)
+ assert File.exist?(file_name)
+ end
+ assert File.exist?(file_name)
+ assert_equal "100755", file_mode
+ assert_equal contents, File.read(file_name)
+ ensure
+ File.unlink(file_name) rescue nil
+ end
+
+ def test_atomic_write_preserves_default_file_permissions
+ contents = "Atomic Text"
+ File.atomic_write(file_name, Dir.pwd) do |file|
+ file.write(contents)
+ assert !File.exist?(file_name)
+ end
+ assert File.exist?(file_name)
+ assert_equal "100644", file_mode
+ assert_equal contents, File.read(file_name)
+ ensure
+ File.unlink(file_name) rescue nil
end
+
+ private
+ def file_name
+ "atomic.file"
+ end
+
+ def file_mode
+ sprintf("%o", File.stat(file_name).mode)
+ end
end