mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1363 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
		
			
				
	
	
		
			156 lines
		
	
	
	
		
			2.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			156 lines
		
	
	
	
		
			2.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
#
 | 
						|
# How to use:
 | 
						|
#
 | 
						|
# db = PStore.new("/tmp/foo")
 | 
						|
# db.transaction do
 | 
						|
#   p db.roots
 | 
						|
#   ary = db["root"] = [1,2,3,4]
 | 
						|
#   ary[0] = [1,1.5]
 | 
						|
# end
 | 
						|
 | 
						|
# db.transaction do
 | 
						|
#   p db["root"]
 | 
						|
# end
 | 
						|
 | 
						|
require "ftools"
 | 
						|
require "md5"
 | 
						|
 | 
						|
class PStore
 | 
						|
  class Error < StandardError
 | 
						|
  end
 | 
						|
 | 
						|
  def initialize(file)
 | 
						|
    dir = File::dirname(file)
 | 
						|
    unless File::directory? dir
 | 
						|
      raise PStore::Error, format("directory %s does not exist", dir)
 | 
						|
    end
 | 
						|
    unless File::writable? dir
 | 
						|
      raise PStore::Error, format("directory %s not writable", dir)
 | 
						|
    end
 | 
						|
    if File::exist? file and not File::readable? file
 | 
						|
      raise PStore::Error, format("file %s not readable", file)
 | 
						|
    end
 | 
						|
    @transaction = false
 | 
						|
    @filename = file
 | 
						|
    @abort = false
 | 
						|
  end
 | 
						|
 | 
						|
  def in_transaction
 | 
						|
    raise PStore::Error, "not in transaction" unless @transaction
 | 
						|
  end
 | 
						|
  private :in_transaction
 | 
						|
 | 
						|
  def [](name)
 | 
						|
    in_transaction
 | 
						|
    unless @table.key? name
 | 
						|
      raise PStore::Error, format("undefined root name `%s'", name)
 | 
						|
    end
 | 
						|
    @table[name]
 | 
						|
  end
 | 
						|
  def []=(name, value)
 | 
						|
    in_transaction
 | 
						|
    @table[name] = value
 | 
						|
  end
 | 
						|
  def delete(name)
 | 
						|
    in_transaction
 | 
						|
    @table.delete name
 | 
						|
  end
 | 
						|
 | 
						|
  def roots
 | 
						|
    in_transaction
 | 
						|
    @table.keys
 | 
						|
  end
 | 
						|
  def root?(name)
 | 
						|
    in_transaction
 | 
						|
    @table.key? name
 | 
						|
  end
 | 
						|
  def path
 | 
						|
    @filename
 | 
						|
  end
 | 
						|
 | 
						|
  def commit
 | 
						|
    in_transaction
 | 
						|
    @abort = false
 | 
						|
    throw :pstore_abort_transaction
 | 
						|
  end
 | 
						|
  def abort
 | 
						|
    in_transaction
 | 
						|
    @abort = true
 | 
						|
    throw :pstore_abort_transaction
 | 
						|
  end
 | 
						|
 | 
						|
  def transaction
 | 
						|
    raise PStore::Error, "nested transaction" if @transaction
 | 
						|
    begin
 | 
						|
      @transaction = true
 | 
						|
      value = nil
 | 
						|
      backup = @filename+"~"
 | 
						|
      begin
 | 
						|
	file = File::open(@filename, "r+")
 | 
						|
	orig = true
 | 
						|
      rescue Errno::ENOENT
 | 
						|
	file = File::open(@filename, "w+")
 | 
						|
      end
 | 
						|
      file.flock(File::LOCK_EX)
 | 
						|
      if orig
 | 
						|
	content = file.read
 | 
						|
	@table = Marshal::load(content)
 | 
						|
	size = content.size
 | 
						|
	md5 = MD5.new(content).digest
 | 
						|
	content = nil		# unreference huge data
 | 
						|
      else
 | 
						|
	@table = {}
 | 
						|
      end
 | 
						|
      begin
 | 
						|
	catch(:pstore_abort_transaction) do
 | 
						|
	  value = yield(self)
 | 
						|
	end
 | 
						|
      rescue Exception
 | 
						|
	@abort = true
 | 
						|
	raise
 | 
						|
      ensure
 | 
						|
	unless @abort
 | 
						|
	  file.rewind
 | 
						|
	  content = Marshal::dump(@table)
 | 
						|
	  if !md5 || size != content.size || md5 != MD5.new(content).digest
 | 
						|
	    File::copy @filename, backup
 | 
						|
	    begin
 | 
						|
	      file.write(content)
 | 
						|
	      file.truncate(file.pos)
 | 
						|
	      content = nil		# unreference huge data
 | 
						|
	    rescue
 | 
						|
	      File::rename backup, @filename if File::exist?(backup)
 | 
						|
	      raise
 | 
						|
	    end
 | 
						|
	  end
 | 
						|
	end
 | 
						|
	@abort = false
 | 
						|
      end
 | 
						|
    ensure
 | 
						|
      @table = nil
 | 
						|
      @transaction = false
 | 
						|
      file.close
 | 
						|
    end
 | 
						|
    value
 | 
						|
  end
 | 
						|
end
 | 
						|
 | 
						|
if __FILE__ == $0
 | 
						|
  db = PStore.new("/tmp/foo")
 | 
						|
  db.transaction do
 | 
						|
    p db.roots
 | 
						|
    ary = db["root"] = [1,2,3,4]
 | 
						|
    ary[1] = [1,1.5]
 | 
						|
  end
 | 
						|
 | 
						|
  1000.times do
 | 
						|
    db.transaction do
 | 
						|
      db["root"][0] += 1
 | 
						|
      p db["root"][0]
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  db.transaction do
 | 
						|
    p db["root"]
 | 
						|
  end
 | 
						|
end
 |