<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,5 +1,7 @@
 1.0.5
 * Added erb: a file task that creates a file form an ERB template.
+* The write and update file methods now perform atomic writes (create
+temporary file, rename to desired path).
 
 1.0.4 (2009-11-4)
 * When used with -U option and no other argument, necktie command updates the</diff>
      <filename>CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -208,4 +208,8 @@ Includes Session, created by Ara T. Howard and released under the Ruby License
 http://raa.ruby-lang.org/project/session
 http://www.codeforpeople.com/lib/ruby/session
 
+Atomic file write adapted from Rails.
+
+ERB task adapted from Luke Bayes http://gist.github.com/215270.
+
 Developed for managing EC2 instances for http://apartly.com </diff>
      <filename>README.rdoc</filename>
    </modified>
    <modified>
      <diff>@@ -1,43 +1,69 @@
-module Necktie::Files
-  # Return the contents of the file (same as File.read).
-  def read(name)
-    File.read(name)
-  end
+require &quot;tempfile&quot;
 
-  # Writes contents to a new file, or overwrites existing file.
-  # Takes string as second argument, or yields to block. For example:
-  #   write &quot;/etc/mailname&quot;, &quot;example.com&quot;
-  #   write(&quot;/var/run/bowtie.pid&quot;) { Process.pid }
-  def write(name, contents = nil)
-    contents ||= yield
-    File.open name, &quot;w&quot; do |f|
-      f.write contents
+module Necktie
+  module Files
+    # Return the contents of the file (same as File.read).
+    def read(name)
+      File.read(name)
     end
-  end
 
-  # Append contents to a file, creating it if necessary.
-  # Takes string as second argument, or yields to block. For example:
-  #   append &quot;/etc/fstab&quot;, &quot;/dev/sdh /vol xfs\n&quot; unless read(&quot;/etc/fstab&quot;)[&quot;/dev/sdh &quot;]
-  def append(name, contents = nil)
-    contents ||= yield
-    File.open name, &quot;a&quot; do |f|
-      f.write contents
+    # Writes contents to a new file, or overwrites existing file.  Takes string
+    # as second argument, or yields to block. For example:
+    #
+    #   write &quot;/etc/mailname&quot;, &quot;example.com&quot;
+    #   write(&quot;/var/run/bowtie.pid&quot;) { Process.pid }
+    # 
+    # This method performs an atomic write using TMPDIR or (/tmp) as the
+    # temporary directory and renaming the file over to the new location.
+    # Ownership and premission are retained if replacing existing file.
+    def write(name, contents = nil)
+      contents ||= yield
+      temp = Tempfile.new(File.basename(name))
+      temp.write contents
+      temp.close
+
+      begin
+        # Get original file permissions
+        stat = File.stat(name)
+      rescue Errno::ENOENT
+        # No old permissions, write a temp file to determine the defaults
+        stat_check = File.join(File.dirname(name), &quot;.permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}&quot;)
+        File.open(stat_check, &quot;w&quot;) { }
+        stat = File.stat(stat_check)
+        File.unlink stat_check
+      end
+      
+      # Overwrite original file with temp file
+      File.rename(temp.path, name)
+
+      # Set correct permissions on new file
+      File.chown stat.uid, stat.gid, name
+      File.chmod stat.mode, name
+    end
+
+    # Append contents to a file, creating it if necessary.  Takes string as
+    # second argument, or yields to block. For example:
+    #   append &quot;/etc/fstab&quot;, &quot;/dev/sdh /vol xfs\n&quot; unless read(&quot;/etc/fstab&quot;)[&quot;/dev/sdh &quot;]
+    def append(name, contents = nil)
+      contents ||= yield
+      File.open name, &quot;a&quot; do |f|
+        f.write contents
+      end
     end
-  end
 
-  # Updates a file: read contents, substitue and write it back.
-  # Takes two arguments for substitution, or yields to block.
-  # These two are equivalent:
-  #   update &quot;/etc/memcached.conf&quot;, /^-l 127.0.0.1/, &quot;-l 0.0.0.0&quot;
-  #   update(&quot;/etc/memcached.conf&quot;) { |s| s.sub(/^-l 127.0.0.1/, &quot;-l 0.0.0.0&quot;) }
-  def update(name, from = nil, to = nil)
-    contents = File.read(name)
-    if from &amp;&amp; to
-      contents = contents.sub(from, to)
-    else
-      contents = yield(contents)
+    # Updates a file: read contents, substitue and write it back.  Takes two
+    # arguments for substitution, or yields to block.  These two are equivalent:
+    #   update &quot;/etc/memcached.conf&quot;, /^-l 127.0.0.1/, &quot;-l 0.0.0.0&quot;
+    #   update(&quot;/etc/memcached.conf&quot;) { |s| s.sub(/^-l 127.0.0.1/, &quot;-l 0.0.0.0&quot;) }
+    def update(name, from = nil, to = nil)
+      contents = File.read(name)
+      if from &amp;&amp; to
+        contents = contents.sub(from, to)
+      else
+        contents = yield(contents)
+      end
+      write name, contents
     end
-    write name, contents
   end
 end
 </diff>
      <filename>lib/necktie/files.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,20 +1,22 @@
 require &quot;rubygems/dependency_installer&quot;
 
-module Necktie::Gems
-  # Installs the specified gem, if not already installed. First argument is the
-  # name of the gem, or file containing the gem. Second argument is version requirement.
-  # For example:
-  #   install_gem &quot;unicorn&quot;, &quot;~&gt;0.93&quot;
-  #
-  #   Dir[&quot;gems/*.gem&quot;].each do |gem|
-  #     install_gem gem
-  #   end
-  def install_gem(name, version = nil)
-    installer = Gem::DependencyInstaller.new
-    spec = installer.find_spec_by_name_and_version(name, version).first.first
-    if Gem::SourceIndex.from_installed_gems.find_name(spec.name, spec.version).empty?
-      puts &quot; ** Installing the gem #{spec.name} #{spec.version}&quot;
-      installer.install name, version
+module Necktie
+  module Gems
+    # Installs the specified gem, if not already installed. First argument is the
+    # name of the gem, or file containing the gem. Second argument is version requirement.
+    # For example:
+    #   install_gem &quot;unicorn&quot;, &quot;~&gt;0.93&quot;
+    #
+    #   Dir[&quot;gems/*.gem&quot;].each do |gem|
+    #     install_gem gem
+    #   end
+    def install_gem(name, version = nil)
+      installer = Gem::DependencyInstaller.new
+      spec = installer.find_spec_by_name_and_version(name, version).first.first
+      if Gem::SourceIndex.from_installed_gems.find_name(spec.name, spec.version).empty?
+        puts &quot; ** Installing the gem #{spec.name} #{spec.version}&quot;
+        installer.install name, version
+      end
     end
   end
 end</diff>
      <filename>lib/necktie/gems.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>7b875ed0098af3c76a6d790df74df2787cd1637f</id>
    </parent>
  </parents>
  <author>
    <name>Assaf Arkin</name>
    <email>assaf@labnotes.org</email>
  </author>
  <url>http://github.com/assaf/necktie/commit/111de47350fae6ef067586ec3978b8a2ec320da3</url>
  <id>111de47350fae6ef067586ec3978b8a2ec320da3</id>
  <committed-date>2009-11-06T12:17:41-08:00</committed-date>
  <authored-date>2009-11-06T12:17:41-08:00</authored-date>
  <message>The write and update file methods now perform atomic writes (create
temporary file, rename to desired path).</message>
  <tree>0ff600b4520342e3a466175d5826fdc6319a06c8</tree>
  <committer>
    <name>Assaf Arkin</name>
    <email>assaf@labnotes.org</email>
  </committer>
</commit>
