From 8cc45aae947d453acca029e13eb64f3f5f0bf942 Mon Sep 17 00:00:00 2001 From: drbrain Date: Mon, 31 Mar 2008 22:40:06 +0000 Subject: Import RubyGems 1.1.0 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@15873 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/rubygems/source_info_cache.rb | 312 ++++++++++++++++++++++++++------------ 1 file changed, 217 insertions(+), 95 deletions(-) (limited to 'lib/rubygems/source_info_cache.rb') diff --git a/lib/rubygems/source_info_cache.rb b/lib/rubygems/source_info_cache.rb index c84868a5f5..9383f6362e 100644 --- a/lib/rubygems/source_info_cache.rb +++ b/lib/rubygems/source_info_cache.rb @@ -4,6 +4,7 @@ require 'rubygems' require 'rubygems/source_info_cache_entry' require 'rubygems/user_interaction' +## # SourceInfoCache stores a copy of the gem index for each gem source. # # There are two possible cache locations, the system cache and the user cache: @@ -25,7 +26,7 @@ require 'rubygems/user_interaction' # @source_index => Gem::SourceIndex # ... # } -# + class Gem::SourceInfoCache include Gem::UserInteraction @@ -37,7 +38,7 @@ class Gem::SourceInfoCache def self.cache return @cache if @cache @cache = new - @cache.refresh if Gem.configuration.update_sources + @cache.refresh false if Gem.configuration.update_sources @cache end @@ -45,86 +46,178 @@ class Gem::SourceInfoCache cache.cache_data end - # Search all source indexes for +pattern+. - def self.search(pattern, platform_only = false) - cache.search pattern, platform_only + ## + # The name of the system cache file. + + def self.latest_system_cache_file + File.join File.dirname(system_cache_file), + "latest_#{File.basename system_cache_file}" + end + + ## + # The name of the latest user cache file. + + def self.latest_user_cache_file + File.join File.dirname(user_cache_file), + "latest_#{File.basename user_cache_file}" + end + + ## + # Search all source indexes. See Gem::SourceInfoCache#search. + + def self.search(*args) + cache.search(*args) end - # Search all source indexes for +pattern+. Only returns gems matching - # Gem.platforms when +only_platform+ is true. See #search_with_source. - def self.search_with_source(pattern, only_platform = false) - cache.search_with_source(pattern, only_platform) + ## + # Search all source indexes returning the source_uri. See + # Gem::SourceInfoCache#search_with_source. + + def self.search_with_source(*args) + cache.search_with_source(*args) + end + + ## + # The name of the system cache file. (class method) + + def self.system_cache_file + @system_cache_file ||= Gem.default_system_source_cache_dir + end + + ## + # The name of the user cache file. + + def self.user_cache_file + @user_cache_file ||= + ENV['GEMCACHE'] || Gem.default_user_source_cache_dir end def initialize # :nodoc: @cache_data = nil @cache_file = nil @dirty = false + @only_latest = true end + ## # The most recent cache data. + def cache_data return @cache_data if @cache_data cache_file # HACK writable check - begin - # Marshal loads 30-40% faster from a String, and 2MB on 20061116 is small - data = File.open cache_file, 'rb' do |fp| fp.read end - @cache_data = Marshal.load data - - @cache_data.each do |url, sice| - next unless sice.is_a?(Hash) - update - cache = sice['cache'] - size = sice['size'] - if cache.is_a?(Gem::SourceIndex) and size.is_a?(Numeric) then - new_sice = Gem::SourceInfoCacheEntry.new cache, size - @cache_data[url] = new_sice - else # irreperable, force refetch. - reset_cache_for(url) - end - end - @cache_data - rescue => e - if Gem.configuration.really_verbose then - say "Exception during cache_data handling: #{ex.class} - #{ex}" - say "Cache file was: #{cache_file}" - say "\t#{e.backtrace.join "\n\t"}" - end - reset_cache_data - end - end - - def reset_cache_for(url) - say "Reseting cache for #{url}" if Gem.configuration.really_verbose + @only_latest = true - sice = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0 - sice.refresh url # HACK may be unnecessary, see ::cache and #refresh + @cache_data = read_cache_data latest_cache_file - @cache_data[url] = sice @cache_data end - def reset_cache_data - @cache_data = {} - end + ## + # The name of the cache file. - # The name of the cache file to be read def cache_file return @cache_file if @cache_file @cache_file = (try_file(system_cache_file) or try_file(user_cache_file) or raise "unable to locate a writable cache file") end + + ## + # Force cache file to be reset, useful for integration testing of rubygems + + def reset_cache_file + @cache_file = nil + end + ## # Write the cache to a local file (if it is dirty). + def flush write_cache if @dirty @dirty = false end - # Refreshes each source in the cache from its repository. - def refresh + def latest_cache_data + latest_cache_data = {} + + cache_data.each do |repo, sice| + latest = sice.source_index.latest_specs + + new_si = Gem::SourceIndex.new + new_si.add_specs(*latest) + + latest_sice = Gem::SourceInfoCacheEntry.new new_si, sice.size + latest_cache_data[repo] = latest_sice + end + + latest_cache_data + end + + ## + # The name of the latest cache file. + + def latest_cache_file + File.join File.dirname(cache_file), "latest_#{File.basename cache_file}" + end + + ## + # The name of the latest system cache file. + + def latest_system_cache_file + self.class.latest_system_cache_file + end + + ## + # The name of the latest user cache file. + + def latest_user_cache_file + self.class.latest_user_cache_file + end + + def read_all_cache_data + if @only_latest then + @only_latest = false + @cache_data = read_cache_data cache_file + end + end + + def read_cache_data(file) + # Marshal loads 30-40% faster from a String, and 2MB on 20061116 is small + data = open file, 'rb' do |fp| fp.read end + cache_data = Marshal.load data + + cache_data.each do |url, sice| + next unless sice.is_a?(Hash) + update + + cache = sice['cache'] + size = sice['size'] + + if cache.is_a?(Gem::SourceIndex) and size.is_a?(Numeric) then + new_sice = Gem::SourceInfoCacheEntry.new cache, size + cache_data[url] = new_sice + else # irreperable, force refetch. + reset_cache_for url, cache_data + end + end + + cache_data + rescue => e + if Gem.configuration.really_verbose then + say "Exception during cache_data handling: #{e.class} - #{e}" + say "Cache file was: #{file}" + say "\t#{e.backtrace.join "\n\t"}" + end + + {} + end + + ## + # Refreshes each source in the cache from its repository. If +all+ is + # false, only latest gems are updated. + + def refresh(all) Gem.sources.each do |source_uri| cache_entry = cache_data[source_uri] if cache_entry.nil? then @@ -132,14 +225,34 @@ class Gem::SourceInfoCache cache_data[source_uri] = cache_entry end - update if cache_entry.refresh source_uri + update if cache_entry.refresh source_uri, all end flush end - # Searches all source indexes for +pattern+. - def search(pattern, platform_only = false) + def reset_cache_for(url, cache_data) + say "Reseting cache for #{url}" if Gem.configuration.really_verbose + + sice = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0 + sice.refresh url, false # HACK may be unnecessary, see ::cache and #refresh + + cache_data[url] = sice + cache_data + end + + def reset_cache_data + @cache_data = nil + end + + ## + # Searches all source indexes. See Gem::SourceIndex#search for details on + # +pattern+ and +platform_only+. If +all+ is set to true, the full index + # will be loaded before searching. + + def search(pattern, platform_only = false, all = false) + read_all_cache_data if all + cache_data.map do |source_uri, sic_entry| next unless Gem.sources.include? source_uri sic_entry.source_index.search pattern, platform_only @@ -150,7 +263,9 @@ class Gem::SourceInfoCache # only gems matching Gem.platforms will be selected. Returns an Array of # pairs containing the Gem::Specification found and the source_uri it was # found at. - def search_with_source(pattern, only_platform = false) + def search_with_source(pattern, only_platform = false, all = false) + read_all_cache_data if all + results = [] cache_data.map do |source_uri, sic_entry| @@ -164,68 +279,75 @@ class Gem::SourceInfoCache results end - # Mark the cache as updated (i.e. dirty). - def update - @dirty = true + ## + # Set the source info cache data directly. This is mainly used for unit + # testing when we don't want to read a file system to grab the cached source + # index information. The +hash+ should map a source URL into a + # SourceInfoCacheEntry. + + def set_cache_data(hash) + @cache_data = hash + update end + ## # The name of the system cache file. + def system_cache_file self.class.system_cache_file end - # The name of the system cache file. (class method) - def self.system_cache_file - @system_cache_file ||= Gem.default_system_source_cache_dir + ## + # Determine if +path+ is a candidate for a cache file. Returns +path+ if + # it is, nil if not. + + def try_file(path) + return path if File.writable? path + return nil if File.exist? path + + dir = File.dirname path + + unless File.exist? dir then + begin + FileUtils.mkdir_p dir + rescue RuntimeError, SystemCallError + return nil + end + end + + if File.writable? dir then + open path, "wb" do |io| io.write Marshal.dump({}) end + return path + end + + nil + end + + ## + # Mark the cache as updated (i.e. dirty). + + def update + @dirty = true end + ## # The name of the user cache file. + def user_cache_file self.class.user_cache_file end - # The name of the user cache file. (class method) - def self.user_cache_file - @user_cache_file ||= - ENV['GEMCACHE'] || Gem.default_user_source_cache_dir - end - + ## # Write data to the proper cache. + def write_cache - open cache_file, "wb" do |f| - f.write Marshal.dump(cache_data) + open cache_file, 'wb' do |io| + io.write Marshal.dump(cache_data) end - end - # Set the source info cache data directly. This is mainly used for unit - # testing when we don't want to read a file system to grab the cached source - # index information. The +hash+ should map a source URL into a - # SourceInfoCacheEntry. - def set_cache_data(hash) - @cache_data = hash - update - end - - private - - # Determine if +fn+ is a candidate for a cache file. Return fn if - # it is. Return nil if it is not. - def try_file(fn) - return fn if File.writable?(fn) - return nil if File.exist?(fn) - dir = File.dirname(fn) - unless File.exist? dir then - begin - FileUtils.mkdir_p(dir) - rescue RuntimeError, SystemCallError - return nil - end + open latest_cache_file, 'wb' do |io| + io.write Marshal.dump(latest_cache_data) end - if File.writable?(dir) - File.open(fn, "wb") { |f| f << Marshal.dump({}) } - return fn - end - nil end end -- cgit v1.2.3