/tools/Ruby/lib/ruby/site_ruby/1.8/rubygems/source_info_cache.rb

http://github.com/agross/netopenspace · Ruby · 395 lines · 212 code · 94 blank · 89 comment · 19 complexity · da32563681f17616728d6d1d373bc810 MD5 · raw file

  1. require 'fileutils'
  2. require 'rubygems'
  3. require 'rubygems/source_info_cache_entry'
  4. require 'rubygems/user_interaction'
  5. ##
  6. # SourceInfoCache stores a copy of the gem index for each gem source.
  7. #
  8. # There are two possible cache locations, the system cache and the user cache:
  9. # * The system cache is preferred if it is writable or can be created.
  10. # * The user cache is used otherwise
  11. #
  12. # Once a cache is selected, it will be used for all operations.
  13. # SourceInfoCache will not switch between cache files dynamically.
  14. #
  15. # Cache data is a Hash mapping a source URI to a SourceInfoCacheEntry.
  16. #
  17. #--
  18. # To keep things straight, this is how the cache objects all fit together:
  19. #
  20. # Gem::SourceInfoCache
  21. # @cache_data = {
  22. # source_uri => Gem::SourceInfoCacheEntry
  23. # @size = source index size
  24. # @source_index = Gem::SourceIndex
  25. # ...
  26. # }
  27. class Gem::SourceInfoCache
  28. include Gem::UserInteraction
  29. ##
  30. # The singleton Gem::SourceInfoCache. If +all+ is true, a full refresh will
  31. # be performed if the singleton instance is being initialized.
  32. def self.cache(all = false)
  33. return @cache if @cache
  34. @cache = new
  35. @cache.refresh all if Gem.configuration.update_sources
  36. @cache
  37. end
  38. def self.cache_data
  39. cache.cache_data
  40. end
  41. ##
  42. # The name of the system cache file.
  43. def self.latest_system_cache_file
  44. File.join File.dirname(system_cache_file),
  45. "latest_#{File.basename system_cache_file}"
  46. end
  47. ##
  48. # The name of the latest user cache file.
  49. def self.latest_user_cache_file
  50. File.join File.dirname(user_cache_file),
  51. "latest_#{File.basename user_cache_file}"
  52. end
  53. ##
  54. # Reset all singletons, discarding any changes.
  55. def self.reset
  56. @cache = nil
  57. @system_cache_file = nil
  58. @user_cache_file = nil
  59. end
  60. ##
  61. # Search all source indexes. See Gem::SourceInfoCache#search.
  62. def self.search(*args)
  63. cache.search(*args)
  64. end
  65. ##
  66. # Search all source indexes returning the source_uri. See
  67. # Gem::SourceInfoCache#search_with_source.
  68. def self.search_with_source(*args)
  69. cache.search_with_source(*args)
  70. end
  71. ##
  72. # The name of the system cache file. (class method)
  73. def self.system_cache_file
  74. @system_cache_file ||= Gem.default_system_source_cache_dir
  75. end
  76. ##
  77. # The name of the user cache file.
  78. def self.user_cache_file
  79. @user_cache_file ||=
  80. ENV['GEMCACHE'] || Gem.default_user_source_cache_dir
  81. end
  82. def initialize # :nodoc:
  83. @cache_data = nil
  84. @cache_file = nil
  85. @dirty = false
  86. @only_latest = true
  87. end
  88. ##
  89. # The most recent cache data.
  90. def cache_data
  91. return @cache_data if @cache_data
  92. cache_file # HACK writable check
  93. @only_latest = true
  94. @cache_data = read_cache_data latest_cache_file
  95. @cache_data
  96. end
  97. ##
  98. # The name of the cache file.
  99. def cache_file
  100. return @cache_file if @cache_file
  101. @cache_file = (try_file(system_cache_file) or
  102. try_file(user_cache_file) or
  103. raise "unable to locate a writable cache file")
  104. end
  105. ##
  106. # Write the cache to a local file (if it is dirty).
  107. def flush
  108. write_cache if @dirty
  109. @dirty = false
  110. end
  111. def latest_cache_data
  112. latest_cache_data = {}
  113. cache_data.each do |repo, sice|
  114. latest = sice.source_index.latest_specs
  115. new_si = Gem::SourceIndex.new
  116. new_si.add_specs(*latest)
  117. latest_sice = Gem::SourceInfoCacheEntry.new new_si, sice.size
  118. latest_cache_data[repo] = latest_sice
  119. end
  120. latest_cache_data
  121. end
  122. ##
  123. # The name of the latest cache file.
  124. def latest_cache_file
  125. File.join File.dirname(cache_file), "latest_#{File.basename cache_file}"
  126. end
  127. ##
  128. # The name of the latest system cache file.
  129. def latest_system_cache_file
  130. self.class.latest_system_cache_file
  131. end
  132. ##
  133. # The name of the latest user cache file.
  134. def latest_user_cache_file
  135. self.class.latest_user_cache_file
  136. end
  137. ##
  138. # Merges the complete cache file into this Gem::SourceInfoCache.
  139. def read_all_cache_data
  140. if @only_latest then
  141. @only_latest = false
  142. all_data = read_cache_data cache_file
  143. cache_data.update all_data do |source_uri, latest_sice, all_sice|
  144. all_sice.source_index.gems.update latest_sice.source_index.gems
  145. Gem::SourceInfoCacheEntry.new all_sice.source_index, latest_sice.size
  146. end
  147. begin
  148. refresh true
  149. rescue Gem::RemoteFetcher::FetchError
  150. end
  151. end
  152. end
  153. ##
  154. # Reads cached data from +file+.
  155. def read_cache_data(file)
  156. # Marshal loads 30-40% faster from a String, and 2MB on 20061116 is small
  157. data = open file, 'rb' do |fp| fp.read end
  158. cache_data = Marshal.load data
  159. cache_data.each do |url, sice|
  160. next unless sice.is_a?(Hash)
  161. update
  162. cache = sice['cache']
  163. size = sice['size']
  164. if cache.is_a?(Gem::SourceIndex) and size.is_a?(Numeric) then
  165. new_sice = Gem::SourceInfoCacheEntry.new cache, size
  166. cache_data[url] = new_sice
  167. else # irreperable, force refetch.
  168. reset_cache_for url, cache_data
  169. end
  170. end
  171. cache_data
  172. rescue Errno::ENOENT
  173. {}
  174. rescue => e
  175. if Gem.configuration.really_verbose then
  176. say "Exception during cache_data handling: #{e.class} - #{e}"
  177. say "Cache file was: #{file}"
  178. say "\t#{e.backtrace.join "\n\t"}"
  179. end
  180. {}
  181. end
  182. ##
  183. # Refreshes each source in the cache from its repository. If +all+ is
  184. # false, only latest gems are updated.
  185. def refresh(all)
  186. Gem.sources.each do |source_uri|
  187. cache_entry = cache_data[source_uri]
  188. if cache_entry.nil? then
  189. cache_entry = Gem::SourceInfoCacheEntry.new nil, 0
  190. cache_data[source_uri] = cache_entry
  191. end
  192. update if cache_entry.refresh source_uri, all
  193. end
  194. flush
  195. end
  196. def reset_cache_for(url, cache_data)
  197. say "Reseting cache for #{url}" if Gem.configuration.really_verbose
  198. sice = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0
  199. sice.refresh url, false # HACK may be unnecessary, see ::cache and #refresh
  200. cache_data[url] = sice
  201. cache_data
  202. end
  203. def reset_cache_data
  204. @cache_data = nil
  205. @only_latest = true
  206. end
  207. ##
  208. # Force cache file to be reset, useful for integration testing of rubygems
  209. def reset_cache_file
  210. @cache_file = nil
  211. end
  212. ##
  213. # Searches all source indexes. See Gem::SourceIndex#search for details on
  214. # +pattern+ and +platform_only+. If +all+ is set to true, the full index
  215. # will be loaded before searching.
  216. def search(pattern, platform_only = false, all = false)
  217. read_all_cache_data if all
  218. cache_data.map do |source_uri, sic_entry|
  219. next unless Gem.sources.include? source_uri
  220. # TODO - Remove this gunk after 2008/11
  221. unless pattern.kind_of? Gem::Dependency then
  222. pattern = Gem::Dependency.new pattern, Gem::Requirement.default
  223. end
  224. sic_entry.source_index.search pattern, platform_only
  225. end.flatten.compact
  226. end
  227. ##
  228. # Searches all source indexes for +pattern+. If +only_platform+ is true,
  229. # only gems matching Gem.platforms will be selected. Returns an Array of
  230. # pairs containing the Gem::Specification found and the source_uri it was
  231. # found at.
  232. def search_with_source(pattern, only_platform = false, all = false)
  233. read_all_cache_data if all
  234. results = []
  235. cache_data.map do |source_uri, sic_entry|
  236. next unless Gem.sources.include? source_uri
  237. # TODO - Remove this gunk after 2008/11
  238. unless pattern.kind_of?(Gem::Dependency)
  239. pattern = Gem::Dependency.new(pattern, Gem::Requirement.default)
  240. end
  241. sic_entry.source_index.search(pattern, only_platform).each do |spec|
  242. results << [spec, source_uri]
  243. end
  244. end
  245. results
  246. end
  247. ##
  248. # Set the source info cache data directly. This is mainly used for unit
  249. # testing when we don't want to read a file system to grab the cached source
  250. # index information. The +hash+ should map a source URL into a
  251. # SourceInfoCacheEntry.
  252. def set_cache_data(hash)
  253. @cache_data = hash
  254. update
  255. end
  256. ##
  257. # The name of the system cache file.
  258. def system_cache_file
  259. self.class.system_cache_file
  260. end
  261. ##
  262. # Determine if +path+ is a candidate for a cache file. Returns +path+ if
  263. # it is, nil if not.
  264. def try_file(path)
  265. return path if File.writable? path
  266. return nil if File.exist? path
  267. dir = File.dirname path
  268. unless File.exist? dir then
  269. begin
  270. FileUtils.mkdir_p dir
  271. rescue RuntimeError, SystemCallError
  272. return nil
  273. end
  274. end
  275. return path if File.writable? dir
  276. nil
  277. end
  278. ##
  279. # Mark the cache as updated (i.e. dirty).
  280. def update
  281. @dirty = true
  282. end
  283. ##
  284. # The name of the user cache file.
  285. def user_cache_file
  286. self.class.user_cache_file
  287. end
  288. ##
  289. # Write data to the proper cache files.
  290. def write_cache
  291. if not File.exist?(cache_file) or not @only_latest then
  292. open cache_file, 'wb' do |io|
  293. io.write Marshal.dump(cache_data)
  294. end
  295. end
  296. open latest_cache_file, 'wb' do |io|
  297. io.write Marshal.dump(latest_cache_data)
  298. end
  299. end
  300. reset
  301. end