Class: Bundler::CompactIndexClient::CacheFile
| Relationships & Source Files | |
| Namespace Children | |
| Exceptions: | |
| Inherits: | Object | 
| Defined in: | lib/bundler/compact_index_client/cache_file.rb | 
Overview
write cache files in a way that is robust to concurrent modifications if digests are given, the checksums will be verified
Constant Summary
- 
    DEFAULT_FILE_MODE =
    private
    
 # File 'lib/bundler/compact_index_client/cache_file.rb', line 110o644
Class Method Summary
- 
    
      .copy(path, &block)  
    
    Initialize with a copy of the original file, then yield the instance. 
- .new(original_path, &block) ⇒ CacheFile constructor
- 
    
      .write(path, data, digests = nil)  
    
    Write data to a temp file, then replace the original file with it verifying the digests if given. 
Instance Attribute Summary
- 
    
      #digests=(expected_digests)  
    
    rw
    set the digests that will be verified at the end. 
- #digests? ⇒ Boolean rw
- #original_path readonly
- #path readonly
Instance Method Summary
- 
    
      #append(data)  
    
    Returns false without appending when no digests since appending is too error prone to do without digests. 
- 
    
      #close  
    
    Remove the temp file without replacing the original file. 
- 
    
      #commit  
    
    Replace the original file with the temp file without verifying digests. 
- #commit!
- 
    
      #initialize_digests(keys = nil)  
    
    initialize the digests using SUPPORTED_DIGESTS, or a subset based on keys. 
- 
    
      #open(write_mode = "wb", perm = @perm, &block)  
    
    Open the temp file for writing, reusing original permissions, yielding the IO object. 
- 
    
      #reset_digests  
    
    reset the digests so they don’t contain any previously read data. 
- #size
- 
    
      #verify  
    
    Verify the digests, returning true on match, false on mismatch. 
- #write(data)
Constructor Details
    .new(original_path, &block)  ⇒ CacheFile 
  
# File 'lib/bundler/compact_index_client/cache_file.rb', line 49
def initialize(original_path, &block) @original_path = original_path @perm = original_path.file? ? original_path.stat.mode : DEFAULT_FILE_MODE @path = original_path.sub(/$/, ".#{$$}.tmp") return unless block_given? begin yield self ensure close end end
Class Method Details
.copy(path, &block)
Initialize with a copy of the original file, then yield the instance.
# File 'lib/bundler/compact_index_client/cache_file.rb', line 24
def self.copy(path, &block) new(path) do |file| file.initialize_digests SharedHelpers.filesystem_access(path, :read) do path.open("rb") do |s| file.open {|f| IO.copy_stream(s, f) } end end yield file end end
.write(path, data, digests = nil)
Write data to a temp file, then replace the original file with it verifying the digests if given.
Instance Attribute Details
#digests=(expected_digests) (rw)
set the digests that will be verified at the end
# File 'lib/bundler/compact_index_client/cache_file.rb', line 77
def digests=(expected_digests) @expected_digests = expected_digests if @expected_digests.nil? @digests = nil elsif @digests @digests = @digests.slice(*@expected_digests.keys) else initialize_digests(@expected_digests.keys) end end
    #digests?  ⇒ Boolean  (rw)
  
  [ GitHub ]
# File 'lib/bundler/compact_index_client/cache_file.rb', line 89
def digests? @digests&.any? end
#original_path (readonly)
[ GitHub ]# File 'lib/bundler/compact_index_client/cache_file.rb', line 47
attr_reader :original_path, :path
#path (readonly)
[ GitHub ]# File 'lib/bundler/compact_index_client/cache_file.rb', line 47
attr_reader :original_path, :path
Instance Method Details
#append(data)
Returns false without appending when no digests since appending is too error prone to do without digests.
#close
Remove the temp file without replacing the original file. The file is permanently closed.
# File 'lib/bundler/compact_index_client/cache_file.rb', line 141
def close return if @closed FileUtils.remove_file(path) if @path&.file? @closed = true end
#commit
Replace the original file with the temp file without verifying digests. The file is permanently closed.
# File 'lib/bundler/compact_index_client/cache_file.rb', line 131
def commit raise ClosedError, "Cannot commit closed file" if @closed SharedHelpers.filesystem_access(original_path, :write) do FileUtils.mv(path, original_path) end @closed = true end
#commit!
[ GitHub ]# File 'lib/bundler/compact_index_client/cache_file.rb', line 116
def commit! verify || raise(DigestMismatchError.new(@base64digests, @expected_digests)) commit end
#initialize_digests(keys = nil)
initialize the digests using SUPPORTED_DIGESTS, or a subset based on keys.
# File 'lib/bundler/compact_index_client/cache_file.rb', line 66
def initialize_digests(keys = nil) @digests = keys ? SUPPORTED_DIGESTS.slice(*keys) : SUPPORTED_DIGESTS.dup @digests.transform_values! {|algo_class| SharedHelpers.digest(algo_class).new } end
#open(write_mode = "wb", perm = @perm, &block)
Open the temp file for writing, reusing original permissions, yielding the IO object.
# File 'lib/bundler/compact_index_client/cache_file.rb', line 94
def open(write_mode = "wb", perm = @perm, &block) raise ClosedError, "Cannot reopen closed file" if @closed SharedHelpers.filesystem_access(path, :write) do path.open(write_mode, perm) do |f| yield digests? ? Gem::Package::DigestIO.new(f, @digests) : f end end end
#reset_digests
reset the digests so they don’t contain any previously read data
# File 'lib/bundler/compact_index_client/cache_file.rb', line 72
def reset_digests @digests&.each_value(&:reset) end
#size
[ GitHub ]# File 'lib/bundler/compact_index_client/cache_file.rb', line 61
def size path.size end
#verify
Verify the digests, returning true on match, false on mismatch.
# File 'lib/bundler/compact_index_client/cache_file.rb', line 122
def verify return true unless @expected_digests && digests? @base64digests = @digests.transform_values!(&:base64digest) @digests = nil @base64digests.all? {|algo, digest| @expected_digests[algo] == digest } end
#write(data)
[ GitHub ]# File 'lib/bundler/compact_index_client/cache_file.rb', line 110
def write(data) reset_digests open {|f| f.write data } commit! end