diff options
-rw-r--r-- | activesupport/CHANGELOG | 2 | ||||
-rw-r--r-- | activesupport/lib/active_support/core_ext/file.rb | 21 | ||||
-rw-r--r-- | activesupport/test/core_ext/file_test.rb | 29 |
3 files changed, 52 insertions, 0 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 622ddf1a94..09ea8159a7 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Add File.atomic_write, allows you to write large files in an atomic manner, preventing users from seeing half written files. [Koz] + * Allow users to provide custom formatters to Logger. [aeden] * Hash#to_query CGI-escapes its keys. [Jeremy Kemper] diff --git a/activesupport/lib/active_support/core_ext/file.rb b/activesupport/lib/active_support/core_ext/file.rb new file mode 100644 index 0000000000..cd43be37e3 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/file.rb @@ -0,0 +1,21 @@ +require 'tempfile' + +# 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.imporant", "/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 diff --git a/activesupport/test/core_ext/file_test.rb b/activesupport/test/core_ext/file_test.rb new file mode 100644 index 0000000000..a7a8324125 --- /dev/null +++ b/activesupport/test/core_ext/file_test.rb @@ -0,0 +1,29 @@ +require File.dirname(__FILE__) + '/../abstract_unit' + +class AtomicWriteTest < Test::Unit::TestCase + + def test_atomic_write_without_errors + contents = "Atomic Text" + File.atomic_write(file_name) do |file| + file.write(contents) + assert !File.exists?(file_name) + end + assert File.exists?(file_name) + assert_equal contents, File.read(file_name) + ensure + File.unlink(file_name) + end + + def test_atomic_write_doesnt_write_when_block_raises + File.atomic_write(file_name) do |file| + file.write("testing") + raise "something bad" + end + rescue + assert !File.exists?(file_name) + end + + def file_name + "atomic.file" + end +end |