class Gem::CompactIndexClient::CacheFile
write cache files in a way that is robust to concurrent modifications if digests are given, the checksums will be verified
Constants
- DEFAULT_FILE_MODE
Attributes
Public Class Methods
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 24 def self.copy(path, &block) new(path) do |file| file.initialize_digests path.open("rb") do |s| file.open {|f| IO.copy_stream(s, f) } end yield file end end
Initialize with a copy of the original file, then yield the instance.
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 47 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
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 37 def self.write(path, data, digests = nil) return unless data new(path) do |file| file.digests = digests file.write(data) end end
Write data to a temp file, then replace the original file with it verifying the digests if given.
Public Instance Methods
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 100 def append(data) return false unless digests? open("a") {|f| f.write data } verify && commit end
Returns false without appending when no digests since appending is too error prone to do without digests.
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 135 def close return if @closed FileUtils.remove_file(path) if @path&.file? @closed = true end
Remove the temp file without replacing the original file. The file is permanently closed.
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 127 def commit raise ClosedError, "Cannot commit closed file" if @closed FileUtils.mv(path, original_path) @closed = true end
Replace the original file with the temp file without verifying digests. The file is permanently closed.
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 112 def commit! verify || raise(DigestMismatchError.new(@base64digests, @expected_digests)) commit end
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 75 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
set the digests that will be verified at the end
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 87 def digests? @digests&.any? end
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 64 def initialize_digests(keys = nil) @digests = keys ? SUPPORTED_DIGESTS.slice(*keys) : SUPPORTED_DIGESTS.dup @digests.transform_values! {|algo_class| Digest(algo_class).new } end
initialize the digests using CompactIndexClient::SUPPORTED_DIGESTS, or a subset based on keys.
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 92 def open(write_mode = "wb", perm = @perm, &block) raise ClosedError, "Cannot reopen closed file" if @closed path.open(write_mode, perm) do |f| yield digests? ? Gem::Package::DigestIO.new(f, @digests) : f end end
Open the temp file for writing, reusing original permissions, yielding the IO object.
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 70 def reset_digests @digests&.each_value(&:reset) end
reset the digests so they donβt contain any previously read data
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 59 def size path.size end
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 118 def verify return true unless @expected_digests && digests? @base64digests = @digests.transform_values!(&:base64digest) @digests = nil @base64digests.all? {|algo, digest| @expected_digests[algo] == digest } end
Verify the digests, returning true on match, false on mismatch.
Source
# File lib/rubygems/compact_index_client/cache_file.rb, line 106 def write(data) reset_digests open {|f| f.write data } commit! end