class Gem::SpecificationRecord

Public Class Methods

Whether dir is an ABI-scoped spec dir (.../specifications/<ruby_abi>). Requires the parent to be named specifications to avoid false positives.

# File lib/rubygems/specification_record.rb, line 21
def self.abi_scoped_spec_dir?(dir)
  Gem::ContentAddress.valid_ruby_abi?(File.basename(dir)) &&
    File.basename(File.dirname(dir)) == "specifications"
end

Specification directories for each path: the flat specifications dir and the ABI-scoped +specifications/<ruby_abi>+ subdir.

# File lib/rubygems/specification_record.rb, line 9
def self.dirs_from(paths)
  paths.flat_map do |path|
    specifications = File.join(path, "specifications")

    [specifications, File.join(specifications, Gem.ruby_abi)]
  end
end

Ensure ABI-scoped subdirectories are present in dirs. No-op when dirs already includes them (e.g. MRI); expands flat-only dirs

# File lib/rubygems/specification_record.rb, line 39
def self.dirs_with_abi(dirs)
  dirs.flat_map do |dir|
    if File.basename(dir) == "specifications"
      [dir, File.join(dir, Gem.ruby_abi)]
    else
      dir
    end
  end.uniq
end
# File lib/rubygems/specification_record.rb, line 49
def self.from_path(path)
  new(dirs_from([path]))
end
# File lib/rubygems/specification_record.rb, line 53
def initialize(dirs)
  @all = nil
  @stubs = nil
  @stubs_by_name = {}
  @spec_with_requirable_file = {}
  @active_stub_with_requirable_file = {}

  @dirs = dirs
end

The spec dir for spec under base_dir: the ABI-scoped subdir for content-addressed specs, the flat specifications dir otherwise.

# File lib/rubygems/specification_record.rb, line 29
def self.specification_dir_for(spec, base_dir)
  dir = File.join(base_dir, "specifications")
  dir = File.join(dir, spec.ruby_abi) if Gem::ContentAddress.content_addressed?(spec)
  dir
end

Public Instance Methods

Adds spec to the record, keeping the collection properly sorted.

# File lib/rubygems/specification_record.rb, line 116
def add_spec(spec)
  return if all.include? spec

  all << spec
  stubs << spec
  (@stubs_by_name[spec.name] ||= []) << spec

  Gem::Specification._resort!(@stubs_by_name[spec.name])
  Gem::Specification._resort!(stubs)
end

Returns the list of all specifications in the record

# File lib/rubygems/specification_record.rb, line 70
def all
  @all ||= stubs.map(&:to_spec)
end

Sets the specs known by the record to specs.

# File lib/rubygems/specification_record.rb, line 139
def all=(specs)
  @stubs_by_name = specs.group_by(&:name)
  @all = @stubs = specs
end

Return full names of all specs in the record in sorted order.

# File lib/rubygems/specification_record.rb, line 147
def all_names
  all.map(&:full_name)
end

Enumerate every known spec.

# File lib/rubygems/specification_record.rb, line 156
def each
  return enum_for(:each) unless block_given?

  all.each do |x|
    yield x
  end
end

Return the best specification in the record that contains the file matching path, among those already activated.

# File lib/rubygems/specification_record.rb, line 224
def find_active_stub_by_path(path)
  stub = @active_stub_with_requirable_file[path] ||= stubs.find do |s|
    s.activated? && s.contains_requirable_file?(path)
  end || NOT_FOUND

  stub.this
end

Returns every spec in the record that matches name and optional requirements.

# File lib/rubygems/specification_record.rb, line 167
def find_all_by_name(name, *requirements)
  req = Gem::Requirement.create(*requirements)
  env_req = Gem.env_requirement(name)

  matches = stubs_for(name).find_all do |spec|
    req.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version)
  end.map(&:to_spec)

  if name == "bundler" && !req.specific?
    require_relative "bundler_version_finder"
    Gem::BundlerVersionFinder.prioritize!(matches)
  end

  matches
end

Return the best specification in the record that contains the file matching path.

# File lib/rubygems/specification_record.rb, line 186
def find_by_path(path)
  path = path.dup.freeze
  spec = @spec_with_requirable_file[path] ||= stubs.find do |s|
    s.contains_requirable_file? path
  end || NOT_FOUND

  spec.to_spec
end

Return the best specification in the record that contains the file matching path amongst the specs that are not activated.

# File lib/rubygems/specification_record.rb, line 212
def find_inactive_by_path(path)
  stub = stubs.find do |s|
    next if s.activated?
    s.contains_requirable_file? path
  end
  stub&.to_spec
end

Return the best specification that contains the file matching path amongst the specs that are not loaded. This method is different than find_inactive_by_path as it will filter out loaded specs by their name.

# File lib/rubygems/specification_record.rb, line 200
def find_unloaded_by_path(path)
  stub = stubs.find do |s|
    next if Gem.loaded_specs[s.name]
    s.contains_requirable_file? path
  end
  stub&.to_spec
end

Return the latest installed spec in the record for gem name.

# File lib/rubygems/specification_record.rb, line 243
def latest_spec_for(name)
  latest_specs(true).find {|installed_spec| installed_spec.name == name }
end

Return the latest specs in the record, optionally including prerelease specs if prerelease is true.

# File lib/rubygems/specification_record.rb, line 236
def latest_specs(prerelease)
  Gem::Specification._latest_specs stubs, prerelease
end

Removes spec from the record.

# File lib/rubygems/specification_record.rb, line 130
def remove_spec(spec)
  all.delete spec.to_spec
  stubs.delete spec
  (@stubs_by_name[spec.name] || []).delete spec
end

Returns a Gem::StubSpecification for every specification in the record

# File lib/rubygems/specification_record.rb, line 77
def stubs
  @stubs ||= begin
    pattern = "*.gemspec"
    stubs = stubs_for_pattern(pattern, false)

    @stubs_by_name = stubs.select {|s| Gem::Platform.match_spec? s }.group_by(&:name)
    stubs
  end
end

Returns a Gem::StubSpecification for every specification in the record named name only returns stubs that match Gem.platforms

# File lib/rubygems/specification_record.rb, line 91
def stubs_for(name)
  if @stubs
    @stubs_by_name[name] || []
  else
    @stubs_by_name[name] ||= stubs_for_pattern("#{name}-*.gemspec").select do |s|
      s.name == name
    end
  end
end

Finds stub specifications matching a pattern in the record, optionally filtering out specs not matching the current platform

# File lib/rubygems/specification_record.rb, line 105
def stubs_for_pattern(pattern, match_platform = true)
  installed_stubs = installed_stubs(pattern)
  installed_stubs.select! {|s| Gem::Platform.match_spec? s } if match_platform
  stubs = installed_stubs + Gem::Specification.default_stubs(pattern)
  Gem::Specification._resort!(stubs)
  stubs
end