aboutsummaryrefslogtreecommitdiffstats
path: root/lib/rubygems/source_info_cache.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rubygems/source_info_cache.rb')
-rw-r--r--lib/rubygems/source_info_cache.rb312
1 files changed, 217 insertions, 95 deletions
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