
# Opens a file as a string and writes back the string to the file at
# the end of the block.
#
# Returns the number of written bytes or +nil+ if the file wasn't
# modified.
#
# Note that the file will even be written back in case the block
# raises an exception.
#
# Mode can either be "b" or "+" and specifies to open the file in
# binary mode (no mapping of the plattform's newlines to "\n" is
# done) or to append to it.
#
# == Usage
#
#   # Reverse contents of "message"
#   File.open_as_string("message") { |str| str.reverse! }
#
#   # Replace "foo" by "bar" in "binary"
#   File.open_as_string("binary", "b") { |str| str.gsub!("foo", "bar") }
#
#--
# TODO Should it be in-place modification like this? Or would it be better
# if whatever the block returns is written to the file instead?
#++

def File.open_as_string(name, mode = "")

  unless block_given?
    raise(ArgumentError, "Need to supply block to File.open_as_string")
  end

  if mode.is_a?(Numeric) then
    flag, mode = mode, ""
    mode += "b" if flag & File::Constants::BINARY != 0
    mode += "+" if flag & File::Constants::APPEND != 0
  else
    mode.delete!("^b+")
  end

  str = File.open(name, "r#{mode}") { |file| file.read } #rescue ""
  old_str = str.clone

  begin
    yield str
  ensure
    if old_str != str then
      File.open(name, "w#{mode}") { |file| file.write(str) }
    end
  end

end



#  _____         _
# |_   _|__  ___| |_
#   | |/ _ \/ __| __|
#   | |  __/\__ \ |_
#   |_|\___||___/\__|
#
=begin test

  require 'test/unit'

  # mock File class

  class File
    def self.mock_content( content )
      @mock_content = content
    end

    def self.open( name, mode, &blk )
      blk.call( self )
    end
    def self.read( fname=nil )
      @mock_content.clone
    end
    def self.write( str )
      @mock_content = str
    end
  end


  class TC_OPEN_AS_STRING < Test::Unit::TestCase

    def test_open_as_string
      f = "not a real file"
      t = 'This is a test'
      File.mock_content( t )
      File.open_as_string(f) { |s| s.reverse! }
      s = File.read(f)
      assert_equal( t.reverse, s )
    end

  end

=end
