class Gem::SourceIndex

def ==(other) # :nodoc:

:nodoc:
def ==(other) # :nodoc:
  self.class === other && @gems == other.gems
end

def add_spec(gem_spec, name = gem_spec.full_name)

def add_spec(gem_spec, name = gem_spec.full_name)
  # No idea why, but the Indexer wants to insert them using original_name
  # instead of full_name. So we make it an optional arg.
  @gems[name] = gem_spec
end

def add_specs(*gem_specs)

def add_specs(*gem_specs)
  gem_specs.each do |spec|
    add_spec spec
  end
end

def all_gems

TODO: remove method
def all_gems
  @gems
end

def dump

def dump
  Marshal.dump(self)
end

def each(&block) # :yields: gem.full_name, gem

:yields: gem.full_name, gem
def each(&block) # :yields: gem.full_name, gem
  @gems.each(&block)
end

def find_name(gem_name, requirement = Gem::Requirement.default)

def find_name(gem_name, requirement = Gem::Requirement.default)
  dep = Gem::Dependency.new gem_name, requirement
  search dep
end

def from_gems_in(*spec_dirs)

def from_gems_in(*spec_dirs)
  source_index = new
  source_index.spec_dirs = spec_dirs
  source_index.refresh!
end

def from_installed_gems(*deprecated)

def from_installed_gems(*deprecated)
  if deprecated.empty?
    from_gems_in(*installed_spec_directories)
  else
    from_gems_in(*deprecated) # HACK: warn
  end
end

def gem_signature(gem_full_name)

def gem_signature(gem_full_name)
  require 'digest'
  Digest::SHA256.new.hexdigest(@gems[gem_full_name].to_yaml).to_s
end

def index_signature

def index_signature
  require 'digest'
  Digest::SHA256.new.hexdigest(@gems.keys.sort.join(',')).to_s
end

def initialize(specifications = {})

def initialize(specifications = {})
  @gems = {}
  specifications.each {|_full_name, spec| add_spec spec }
  @spec_dirs = nil
end

def installed_spec_directories

def installed_spec_directories
  Gem.path.collect {|dir| File.join(dir, "specifications") }
end

def latest_specs(include_prerelease = false)

def latest_specs(include_prerelease = false)
  result = Hash.new {|h, k| h[k] = [] }
  latest = {}
  sort.each do |_, spec|
    name = spec.name
    curr_ver = spec.version
    prev_ver = latest.key?(name) ? latest[name].version : nil
    next if !include_prerelease && curr_ver.prerelease?
    next unless prev_ver.nil? || curr_ver >= prev_ver ||
                latest[name].platform != Gem::Platform::RUBY
    if prev_ver.nil? ||
       (curr_ver > prev_ver && spec.platform == Gem::Platform::RUBY)
      result[name].clear
      latest[name] = spec
    end
    if spec.platform != Gem::Platform::RUBY
      result[name].delete_if do |result_spec|
        result_spec.platform == spec.platform
      end
    end
    result[name] << spec
  end
  # TODO: why is this a hash while @gems is an array? Seems like
  # structural similarity would be good.
  result.values.flatten
end

def load_gems_in(*spec_dirs)

def load_gems_in(*spec_dirs)
  @gems.clear
  spec_dirs.reverse_each do |spec_dir|
    spec_files = Dir.glob File.join(spec_dir, '*.gemspec')
    spec_files.each do |spec_file|
      gemspec = Gem::Specification.load spec_file
      add_spec gemspec if gemspec
    end
  end
  self
end

def load_specification(file_name)

def load_specification(file_name)
  Gem::Specification.load file_name
end

def outdated

def outdated
  outdateds = []
  latest_specs.each do |local|
    dependency = Gem::Dependency.new local.name, ">= #{local.version}"
    fetcher = Gem::SpecFetcher.fetcher
    remotes = fetcher.find_matching dependency
    remotes = remotes.map {|(_, version, _), _| version }
    latest = remotes.sort.last
    outdateds << local.name if latest && local.version < latest
  end
  outdateds
end

def prerelease_gems

def prerelease_gems
  @gems.reject {|_name, gem| !gem.version.prerelease? }
end

def prerelease_specs

def prerelease_specs
  prerelease_gems.values
end

def refresh!

def refresh!
  raise 'source index not created from disk' if @spec_dirs.nil?
  load_gems_in(*@spec_dirs)
end

def released_gems

def released_gems
  @gems.reject {|_name, gem| gem.version.prerelease? }
end

def released_specs

def released_specs
  released_gems.values
end

def remove_spec(full_name)

def remove_spec(full_name)
  @gems.delete full_name
end

def search(gem_pattern, platform_only = false)

def search(gem_pattern, platform_only = false)
  requirement = nil
  only_platform = false
  # TODO: Remove support and warning for legacy arguments after 2008/11
  unless Gem::Dependency === gem_pattern
    warn "#{Gem.location_of_caller.join ':'}:Warning: Gem::SourceIndex#search support for #{gem_pattern.class} patterns is deprecated, use #find_name"
  end
  case gem_pattern
  when Regexp then
    requirement = platform_only || Gem::Requirement.default
  when Gem::Dependency then
    only_platform = platform_only
    requirement = gem_pattern.requirement
    gem_pattern = if Regexp === gem_pattern.name
                    gem_pattern.name
                  elsif gem_pattern.name.empty?
                    //
                  else
                    /^#{Regexp.escape gem_pattern.name}$/
                  end
  else
    requirement = platform_only || Gem::Requirement.default
    gem_pattern = /#{gem_pattern}/i
  end
  unless Gem::Requirement === requirement
    requirement = Gem::Requirement.create requirement
  end
  specs = all_gems.values.select do |spec|
    spec.name =~ gem_pattern &&
      requirement.satisfied_by?(spec.version)
  end
  if only_platform
    specs = specs.select do |spec|
      Gem::Platform.match spec.platform
    end
  end
  specs.sort_by(&:sort_obj)
end

def size

def size
  @gems.size
end

def specification(full_name)

def specification(full_name)
  @gems[full_name]
end