Class: Gem::Indexer
Relationships & Source Files | |
Super Chains via Extension / Inclusion / Inheritance | |
Instance Chain:
|
|
Inherits: | Object |
Defined in: | lib/rubygems/indexer.rb |
Overview
Top level class for building the gem repository index.
Class Method Summary
-
.new(directory, options = {}) ⇒ Indexer
constructor
Create an indexer that will index the gems in #directory.
Instance Attribute Summary
-
#build_modern
rw
Build indexes for RubyGems 1.2.0 and newer when true.
-
#dest_directory
readonly
Index install location.
-
#dest_latest_specs_index
readonly
Latest specs index install location.
-
#dest_prerelease_specs_index
readonly
Prerelease specs index install location.
-
#dest_specs_index
readonly
Specs index install location.
-
#directory
readonly
Index build directory.
DefaultUserInteraction - Included
Instance Method Summary
-
#abbreviate(spec)
Abbreviate the spec for downloading.
-
#build_indicies
Build various indicies.
-
#build_marshal_gemspecs
Builds Marshal quick index gemspecs.
-
#build_modern_index(index, file, name)
Build a single index for RubyGems 1.2 and newer.
-
#build_modern_indicies
Builds indicies for RubyGems 1.2 and newer.
-
#compact_specs(specs)
Compacts Marshal output for the specs index data source by using identical objects as much as possible.
-
#compress(filename, extension)
Compress
filename
withextension
. -
#compress_indicies
Compresses indicies on disk.
-
#gem_file_list
List of gem file names to index.
-
#generate_index
Builds and installs indicies.
-
#gzip(filename)
Zlib::GzipWriter
wrapper that gzipsfilename
on disk. -
#install_indicies
Install generated indicies into the destination directory.
-
#make_temp_directories
Make directories for index generation.
- #map_gems_to_specs(gems)
-
#paranoid(path, extension)
Ensure path and path with
extension
are identical. -
#sanitize(spec)
Sanitize the descriptive fields in the spec.
-
#sanitize_string(string)
Sanitize a single string.
-
#update_index
Perform an in-place update of the repository from newly added gems.
-
#update_specs_index(index, source, dest)
Combines specs in
index
andsource
then writes out a new copy todest
.
UserInteraction - Included
#alert | Displays an alert |
#alert_error | Displays an error |
#alert_warning | Displays a warning |
#ask | Asks a |
#ask_for_password | Asks for a password with a |
#ask_yes_no | Asks a yes or no |
#choose_from_list | Asks the user to answer |
#say | Displays the given |
#terminate_interaction | Terminates the RubyGems process with the given |
#verbose | Calls |
DefaultUserInteraction - Included
Constructor Details
.new(directory, options = {}) ⇒ Indexer
Create an indexer that will index the gems in #directory.
# File 'lib/rubygems/indexer.rb', line 51
def initialize(directory, = {}) require 'fileutils' require 'tmpdir' require 'zlib' unless defined?(Builder::XChar) then raise "Gem::Indexer requires that the XML Builder library be installed:" + "\n\tgem install builder" end = { :build_modern => true }.merge @build_modern = [:build_modern] @dest_directory = directory @directory = File.join(Dir.tmpdir, "gem_generate_index_#{$$}") marshal_name = "Marshal.#{Gem.marshal_version}" @master_index = File.join @directory, 'yaml' @marshal_index = File.join @directory, marshal_name @quick_dir = File.join @directory, 'quick' @quick_marshal_dir = File.join @quick_dir, marshal_name @quick_marshal_dir_base = File.join "quick", marshal_name # FIX: UGH @quick_index = File.join @quick_dir, 'index' @latest_index = File.join @quick_dir, 'latest_index' @specs_index = File.join @directory, "specs.#{Gem.marshal_version}" @latest_specs_index = File.join(@directory, "latest_specs.#{Gem.marshal_version}") @prerelease_specs_index = File.join(@directory, "prerelease_specs.#{Gem.marshal_version}") @dest_specs_index = File.join(@dest_directory, "specs.#{Gem.marshal_version}") @dest_latest_specs_index = File.join(@dest_directory, "latest_specs.#{Gem.marshal_version}") @dest_prerelease_specs_index = File.join(@dest_directory, "prerelease_specs.#{Gem.marshal_version}") @files = [] end
Instance Attribute Details
#build_modern (rw)
Build indexes for RubyGems 1.2.0 and newer when true
# File 'lib/rubygems/indexer.rb', line 21
attr_accessor :build_modern
#dest_directory (readonly)
Index install location
# File 'lib/rubygems/indexer.rb', line 26
attr_reader :dest_directory
#dest_latest_specs_index (readonly)
Latest specs index install location
# File 'lib/rubygems/indexer.rb', line 36
attr_reader :dest_latest_specs_index
#dest_prerelease_specs_index (readonly)
Prerelease specs index install location
# File 'lib/rubygems/indexer.rb', line 41
attr_reader :dest_prerelease_specs_index
#dest_specs_index (readonly)
Specs index install location
# File 'lib/rubygems/indexer.rb', line 31
attr_reader :dest_specs_index
#directory (readonly)
Index build directory
# File 'lib/rubygems/indexer.rb', line 46
attr_reader :directory
Instance Method Details
#abbreviate(spec)
Abbreviate the spec for downloading. Abbreviated specs are only used for searching, downloading and related activities and do not need deployment specific information (e.g. list of files). So we abbreviate the spec, making it much smaller for quicker downloads.
# File 'lib/rubygems/indexer.rb', line 103
def abbreviate(spec) spec.files = [] spec.test_files = [] spec. = [] spec.extra_rdoc_files = [] spec.cert_chain = [] spec end
#build_indicies
Build various indicies
# File 'lib/rubygems/indexer.rb', line 115
def build_indicies Gem::Specification.dirs = [] Gem::Specification.add_specs(*map_gems_to_specs(gem_file_list)) build_marshal_gemspecs build_modern_indicies if @build_modern compress_indicies end
#build_marshal_gemspecs
Builds Marshal quick index gemspecs.
# File 'lib/rubygems/indexer.rb', line 128
def build_marshal_gemspecs count = Gem::Specification.count { |s| not s.default_gem? } progress = ui.progress_reporter count, "Generating Marshal quick index gemspecs for #{count} gems", "Complete" files = [] Gem.time 'Generated Marshal quick index gemspecs' do Gem::Specification.each do |spec| next if spec.default_gem? spec_file_name = "#{spec.original_name}.gemspec.rz" marshal_name = File.join @quick_marshal_dir, spec_file_name marshal_zipped = Gem.deflate Marshal.dump(spec) open marshal_name, 'wb' do |io| io.write marshal_zipped end files << marshal_name progress.updated spec.original_name end progress.done end @files << @quick_marshal_dir files end
#build_modern_index(index, file, name)
Build a single index for RubyGems 1.2 and newer
# File 'lib/rubygems/indexer.rb', line 161
def build_modern_index(index, file, name) say "Generating #{name} index" Gem.time "Generated #{name} index" do open(file, 'wb') do |io| specs = index.map do |*spec| # We have to splat here because latest_specs is an array, while the # others are hashes. spec = spec.flatten.last platform = spec.original_platform # win32-api-1.0.4-x86-mswin32-60 unless String === platform then alert_warning "Skipping invalid platform in gem: #{spec.full_name}" next end platform = Gem::Platform::RUBY if platform.nil? or platform.empty? [spec.name, spec.version, platform] end specs = compact_specs(specs) Marshal.dump(specs, io) end end end
#build_modern_indicies
Builds indicies for RubyGems 1.2 and newer. Handles full, latest, prerelease
# File 'lib/rubygems/indexer.rb', line 191
def build_modern_indicies specs = Gem::Specification.reject { |s| s.default_gem? } prerelease, released = specs.partition { |s| s.version.prerelease? } latest_specs = Gem::Specification.latest_specs.reject { |s| s.default_gem? } build_modern_index(released.sort, @specs_index, 'specs') build_modern_index(latest_specs.sort, @latest_specs_index, 'latest specs') build_modern_index(prerelease.sort, @prerelease_specs_index, 'prerelease specs') @files += [@specs_index, "#{@specs_index}.gz", @latest_specs_index, "#{@latest_specs_index}.gz", @prerelease_specs_index, "#{@prerelease_specs_index}.gz"] end
#compact_specs(specs)
Compacts Marshal output for the specs index data source by using identical objects as much as possible.
# File 'lib/rubygems/indexer.rb', line 271
def compact_specs(specs) names = {} versions = {} platforms = {} specs.map do |(name, version, platform)| names[name] = name unless names.include? name versions[version] = version unless versions.include? version platforms[platform] = platform unless platforms.include? platform [names[name], versions[version], platforms[platform]] end end
#compress(filename, extension)
Compress filename
with extension
.
# File 'lib/rubygems/indexer.rb', line 288
def compress(filename, extension) data = Gem.read_binary filename zipped = Gem.deflate data open "#{filename}.#{extension}", 'wb' do |io| io.write zipped end end
#compress_indicies
Compresses indicies on disk
#gem_file_list
List of gem file names to index.
# File 'lib/rubygems/indexer.rb', line 301
def gem_file_list Dir[File.join(@dest_directory, "gems", '*.gem')] end
#generate_index
Builds and installs indicies.
# File 'lib/rubygems/indexer.rb', line 308
def generate_index make_temp_directories build_indicies install_indicies rescue SignalException ensure FileUtils.rm_rf @directory end
#gzip(filename)
Zlib::GzipWriter
wrapper that gzips filename
on disk.
# File 'lib/rubygems/indexer.rb', line 320
def gzip(filename) Zlib::GzipWriter.open "#{filename}.gz" do |io| io.write Gem.read_binary(filename) end end
#install_indicies
Install generated indicies into the destination directory.
# File 'lib/rubygems/indexer.rb', line 329
def install_indicies verbose = Gem.configuration.really_verbose say "Moving index into production dir #{@dest_directory}" if verbose files = @files files.delete @quick_marshal_dir if files.include? @quick_dir if files.include? @quick_marshal_dir and not files.include? @quick_dir then files.delete @quick_marshal_dir dst_name = File.join(@dest_directory, @quick_marshal_dir_base) FileUtils.mkdir_p File.dirname(dst_name), :verbose => verbose FileUtils.rm_rf dst_name, :verbose => verbose FileUtils.mv(@quick_marshal_dir, dst_name, :verbose => verbose, :force => true) end files = files.map do |path| path.sub(/^#{Regexp.escape @directory}\/?/, '') # HACK? end files.each do |file| src_name = File.join @directory, file dst_name = File.join @dest_directory, file FileUtils.rm_rf dst_name, :verbose => verbose FileUtils.mv(src_name, @dest_directory, :verbose => verbose, :force => true) end end
#make_temp_directories
Make directories for index generation
# File 'lib/rubygems/indexer.rb', line 365
def make_temp_directories FileUtils.rm_rf @directory FileUtils.mkdir_p @directory, :mode => 0700 FileUtils.mkdir_p @quick_marshal_dir end
#map_gems_to_specs(gems)
[ GitHub ]# File 'lib/rubygems/indexer.rb', line 213
def map_gems_to_specs gems gems.map { |gemfile| if File.size(gemfile) == 0 then alert_warning "Skipping zero-length gem: #{gemfile}" next end begin spec = Gem::Package.new(gemfile).spec spec.loaded_from = gemfile # HACK: fuck this shit - borks all tests that use pl1 # if File.basename(gemfile, ".gem") != spec.original_name then # exp = spec.full_name # exp << " (#{spec.original_name})" if # spec.original_name != spec.full_name # msg = "Skipping misnamed gem: #{gemfile} should be named #{exp}" # alert_warning msg # next # end abbreviate spec sanitize spec spec rescue SignalException alert_error "Received signal, exiting" raise rescue Exception => e msg = ["Unable to process #{gemfile}", "#{e.} (#{e.class})", "\t#{e.backtrace.join "\n\t"}"].join("\n") alert_error msg end }.compact end
#paranoid(path, extension)
Ensure Gem.path and path with extension
are identical.
# File 'lib/rubygems/indexer.rb', line 374
def paranoid(path, extension) data = Gem.read_binary path compressed_data = Gem.read_binary "#{path}.#{extension}" unless data == Gem.inflate(compressed_data) then raise "Compressed file #{compressed_path} does not match uncompressed file #{path}" end end
#sanitize(spec)
Sanitize the descriptive fields in the spec. Sometimes non-ASCII characters will garble the site index. Non-ASCII characters will be replaced by their XML entity equivalent.
# File 'lib/rubygems/indexer.rb', line 388
def sanitize(spec) spec.summary = sanitize_string(spec.summary) spec.description = sanitize_string(spec.description) spec. = sanitize_string(spec. ) spec. = spec. .collect { |a| sanitize_string(a) } spec end
#sanitize_string(string)
Sanitize a single string.
# File 'lib/rubygems/indexer.rb', line 400
def sanitize_string(string) return string unless string # HACK the #to_s is in here because RSpec has an Array of Arrays of # Strings for authors. Need a way to disallow bad values on gemspec # generation. (Probably won't happen.) string = string.to_s begin Builder::XChar.encode string rescue NameError, NoMethodError string.to_xs end end
#update_index
Perform an in-place update of the repository from newly added gems.
# File 'lib/rubygems/indexer.rb', line 418
def update_index make_temp_directories specs_mtime = File.stat(@dest_specs_index).mtime newest_mtime = Time.at 0 updated_gems = gem_file_list.select do |gem| gem_mtime = File.stat(gem).mtime newest_mtime = gem_mtime if gem_mtime > newest_mtime gem_mtime >= specs_mtime end if updated_gems.empty? then say 'No new gems' terminate_interaction 0 end specs = map_gems_to_specs updated_gems prerelease, released = specs.partition { |s| s.version.prerelease? } Gem::Specification.dirs = [] Gem::Specification.add_specs(*specs) files = build_marshal_gemspecs Gem.time 'Updated indexes' do update_specs_index released, @dest_specs_index, @specs_index update_specs_index released, @dest_latest_specs_index, @latest_specs_index update_specs_index(prerelease, @dest_prerelease_specs_index, @prerelease_specs_index) end compress_indicies verbose = Gem.configuration.really_verbose say "Updating production dir #{@dest_directory}" if verbose files << @specs_index files << "#{@specs_index}.gz" files << @latest_specs_index files << "#{@latest_specs_index}.gz" files << @prerelease_specs_index files << "#{@prerelease_specs_index}.gz" files = files.map do |path| path.sub(/^#{Regexp.escape @directory}\/?/, '') # HACK? end files.each do |file| src_name = File.join @directory, file dst_name = File.join @dest_directory, file # REFACTOR: duped above FileUtils.mv src_name, dst_name, :verbose => verbose, :force => true File.utime newest_mtime, newest_mtime, dst_name end end
#update_specs_index(index, source, dest)
Combines specs in index
and source
then writes out a new copy to dest
. For a latest index, does not ensure the new file is minimal.
# File 'lib/rubygems/indexer.rb', line 483
def update_specs_index(index, source, dest) specs_index = Marshal.load Gem.read_binary(source) index.each do |spec| platform = spec.original_platform platform = Gem::Platform::RUBY if platform.nil? or platform.empty? specs_index << [spec.name, spec.version, platform] end specs_index = compact_specs specs_index.uniq.sort open dest, 'wb' do |io| Marshal.dump specs_index, io end end