Warning: file_get_contents(https://raw.githubusercontent.com/Den1xxx/Filemanager/master/languages/ru.json): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found
in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 88
Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 215
Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 216
Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 217
Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 218
Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 219
Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 220
PK ! Ud d rubygems.rbnu [ # frozen_string_literal: true
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
require "rbconfig"
module Gem
VERSION = "3.5.22"
end
# Must be first since it unloads the prelude from 1.9.2
require_relative "rubygems/compatibility"
require_relative "rubygems/defaults"
require_relative "rubygems/deprecate"
require_relative "rubygems/errors"
##
# RubyGems is the Ruby standard for publishing and managing third party
# libraries.
#
# For user documentation, see:
#
# * gem help and gem help [command]
# * {RubyGems User Guide}[https://guides.rubygems.org/]
# * {Frequently Asked Questions}[https://guides.rubygems.org/faqs]
#
# For gem developer documentation see:
#
# * {Creating Gems}[https://guides.rubygems.org/make-your-own-gem]
# * Gem::Specification
# * Gem::Version for version dependency notes
#
# Further RubyGems documentation can be found at:
#
# * {RubyGems Guides}[https://guides.rubygems.org]
# * {RubyGems API}[https://www.rubydoc.info/github/rubygems/rubygems] (also available from
# gem server)
#
# == RubyGems Plugins
#
# RubyGems will load plugins in the latest version of each installed gem or
# $LOAD_PATH. Plugins must be named 'rubygems_plugin' (.rb, .so, etc) and
# placed at the root of your gem's #require_path. Plugins are installed at a
# special location and loaded on boot.
#
# For an example plugin, see the {Graph gem}[https://github.com/seattlerb/graph]
# which adds a gem graph command.
#
# == RubyGems Defaults, Packaging
#
# RubyGems defaults are stored in lib/rubygems/defaults.rb. If you're packaging
# RubyGems or implementing Ruby you can change RubyGems' defaults.
#
# For RubyGems packagers, provide lib/rubygems/defaults/operating_system.rb
# and override any defaults from lib/rubygems/defaults.rb.
#
# For Ruby implementers, provide lib/rubygems/defaults/#{RUBY_ENGINE}.rb and
# override any defaults from lib/rubygems/defaults.rb.
#
# If you need RubyGems to perform extra work on install or uninstall, your
# defaults override file can set pre/post install and uninstall hooks.
# See Gem::pre_install, Gem::pre_uninstall, Gem::post_install,
# Gem::post_uninstall.
#
# == Bugs
#
# You can submit bugs to the
# {RubyGems bug tracker}[https://github.com/rubygems/rubygems/issues]
# on GitHub
#
# == Credits
#
# RubyGems is currently maintained by Eric Hodel.
#
# RubyGems was originally developed at RubyConf 2003 by:
#
# * Rich Kilmer -- rich(at)infoether.com
# * Chad Fowler -- chad(at)chadfowler.com
# * David Black -- dblack(at)wobblini.net
# * Paul Brannan -- paul(at)atdesk.com
# * Jim Weirich -- jim(at)weirichhouse.org
#
# Contributors:
#
# * Gavin Sinclair -- gsinclair(at)soyabean.com.au
# * George Marrows -- george.marrows(at)ntlworld.com
# * Dick Davies -- rasputnik(at)hellooperator.net
# * Mauricio Fernandez -- batsman.geo(at)yahoo.com
# * Simon Strandgaard -- neoneye(at)adslhome.dk
# * Dave Glasser -- glasser(at)mit.edu
# * Paul Duncan -- pabs(at)pablotron.org
# * Ville Aine -- vaine(at)cs.helsinki.fi
# * Eric Hodel -- drbrain(at)segment7.net
# * Daniel Berger -- djberg96(at)gmail.com
# * Phil Hagelberg -- technomancy(at)gmail.com
# * Ryan Davis -- ryand-ruby(at)zenspider.com
# * Evan Phoenix -- evan(at)fallingsnow.net
# * Steve Klabnik -- steve(at)steveklabnik.com
#
# (If your name is missing, PLEASE let us know!)
#
# == License
#
# See {LICENSE.txt}[rdoc-ref:lib/rubygems/LICENSE.txt] for permissions.
#
# Thanks!
#
# -The RubyGems Team
module Gem
RUBYGEMS_DIR = __dir__
##
# An Array of Regexps that match windows Ruby platforms.
WIN_PATTERNS = [
/bccwin/i,
/cygwin/i,
/djgpp/i,
/mingw/i,
/mswin/i,
/wince/i,
].freeze
GEM_DEP_FILES = %w[
gem.deps.rb
gems.rb
Gemfile
Isolate
].freeze
##
# Subdirectories in a gem repository
REPOSITORY_SUBDIRECTORIES = %w[
build_info
cache
doc
extensions
gems
plugins
specifications
].freeze
##
# Subdirectories in a gem repository for default gems
REPOSITORY_DEFAULT_GEM_SUBDIRECTORIES = %w[
gems
specifications/default
].freeze
@@win_platform = nil
@configuration = nil
@gemdeps = nil
@loaded_specs = {}
LOADED_SPECS_MUTEX = Thread::Mutex.new
@path_to_default_spec_map = {}
@platforms = []
@ruby = nil
@ruby_api_version = nil
@sources = nil
@post_build_hooks ||= []
@post_install_hooks ||= []
@post_uninstall_hooks ||= []
@pre_uninstall_hooks ||= []
@pre_install_hooks ||= []
@pre_reset_hooks ||= []
@post_reset_hooks ||= []
@default_source_date_epoch = nil
@discover_gems_on_require = true
##
# Try to activate a gem containing +path+. Returns true if
# activation succeeded or wasn't needed because it was already
# activated. Returns false if it can't find the path in a gem.
def self.try_activate(path)
# finds the _latest_ version... regardless of loaded specs and their deps
# if another gem had a requirement that would mean we shouldn't
# activate the latest version, then either it would already be activated
# or if it was ambiguous (and thus unresolved) the code in our custom
# require will try to activate the more specific version.
spec = Gem::Specification.find_by_path path
return false unless spec
return true if spec.activated?
begin
spec.activate
rescue Gem::LoadError => e # this could fail due to gem dep collisions, go lax
spec_by_name = Gem::Specification.find_by_name(spec.name)
if spec_by_name.nil?
raise e
else
spec_by_name.activate
end
end
true
end
def self.needs
rs = Gem::RequestSet.new
yield rs
finish_resolve rs
end
def self.finish_resolve(request_set=Gem::RequestSet.new)
request_set.import Gem::Specification.unresolved_deps.values
request_set.import Gem.loaded_specs.values.map {|s| Gem::Dependency.new(s.name, s.version) }
request_set.resolve_current.each do |s|
s.full_spec.activate
end
end
##
# Find the full path to the executable for gem +name+. If the +exec_name+
# is not given, an exception will be raised, otherwise the
# specified executable's path is returned. +requirements+ allows
# you to specify specific gem versions.
def self.bin_path(name, exec_name = nil, *requirements)
requirements = Gem::Requirement.default if
requirements.empty?
find_spec_for_exe(name, exec_name, requirements).bin_file exec_name
end
def self.find_spec_for_exe(name, exec_name, requirements)
raise ArgumentError, "you must supply exec_name" unless exec_name
dep = Gem::Dependency.new name, requirements
loaded = Gem.loaded_specs[name]
return loaded if loaded && dep.matches_spec?(loaded)
specs = dep.matching_specs(true)
specs = specs.find_all do |spec|
spec.executables.include? exec_name
end if exec_name
unless spec = specs.first
msg = "can't find gem #{dep} with executable #{exec_name}"
raise Gem::GemNotFoundException, msg
end
spec
end
private_class_method :find_spec_for_exe
##
# Find the full path to the executable for gem +name+. If the +exec_name+
# is not given, an exception will be raised, otherwise the
# specified executable's path is returned. +requirements+ allows
# you to specify specific gem versions.
#
# A side effect of this method is that it will activate the gem that
# contains the executable.
#
# This method should *only* be used in bin stub files.
def self.activate_bin_path(name, exec_name = nil, *requirements) # :nodoc:
spec = find_spec_for_exe name, exec_name, requirements
Gem::LOADED_SPECS_MUTEX.synchronize do
spec.activate
finish_resolve
end
spec.bin_file exec_name
end
##
# The mode needed to read a file as straight binary.
def self.binary_mode
"rb"
end
##
# The path where gem executables are to be installed.
def self.bindir(install_dir=Gem.dir)
return File.join install_dir, "bin" unless
install_dir.to_s == Gem.default_dir.to_s
Gem.default_bindir
end
##
# The path were rubygems plugins are to be installed.
def self.plugindir(install_dir=Gem.dir)
File.join install_dir, "plugins"
end
##
# Reset the +dir+ and +path+ values. The next time +dir+ or +path+
# is requested, the values will be calculated from scratch. This is
# mainly used by the unit tests to provide test isolation.
def self.clear_paths
@paths = nil
@user_home = nil
Gem::Specification.reset
Gem::Security.reset if defined?(Gem::Security)
end
##
# The standard configuration object for gems.
def self.configuration
@configuration ||= Gem::ConfigFile.new []
end
##
# Use the given configuration object (which implements the ConfigFile
# protocol) as the standard configuration object.
def self.configuration=(config)
@configuration = config
end
##
# A Zlib::Deflate.deflate wrapper
def self.deflate(data)
require "zlib"
Zlib::Deflate.deflate data
end
# Retrieve the PathSupport object that RubyGems uses to
# lookup files.
def self.paths
@paths ||= Gem::PathSupport.new(ENV)
end
# Initialize the filesystem paths to use from +env+.
# +env+ is a hash-like object (typically ENV) that
# is queried for 'GEM_HOME', 'GEM_PATH', and 'GEM_SPEC_CACHE'
# Keys for the +env+ hash should be Strings, and values of the hash should
# be Strings or +nil+.
def self.paths=(env)
clear_paths
target = {}
env.each_pair do |k,v|
case k
when "GEM_HOME", "GEM_PATH", "GEM_SPEC_CACHE"
case v
when nil, String
target[k] = v
when Array
unless Gem::Deprecate.skip
warn <<-EOWARN
Array values in the parameter to `Gem.paths=` are deprecated.
Please use a String or nil.
An Array (#{env.inspect}) was passed in from #{caller[3]}
EOWARN
end
target[k] = v.join File::PATH_SEPARATOR
end
else
target[k] = v
end
end
@paths = Gem::PathSupport.new ENV.to_hash.merge(target)
Gem::Specification.dirs = @paths.path
end
##
# The path where gems are to be installed.
def self.dir
paths.home
end
def self.path
paths.path
end
def self.spec_cache_dir
paths.spec_cache_dir
end
##
# Quietly ensure the Gem directory +dir+ contains all the proper
# subdirectories. If we can't create a directory due to a permission
# problem, then we will silently continue.
#
# If +mode+ is given, missing directories are created with this mode.
#
# World-writable directories will never be created.
def self.ensure_gem_subdirectories(dir = Gem.dir, mode = nil)
ensure_subdirectories(dir, mode, REPOSITORY_SUBDIRECTORIES)
end
##
# Quietly ensure the Gem directory +dir+ contains all the proper
# subdirectories for handling default gems. If we can't create a
# directory due to a permission problem, then we will silently continue.
#
# If +mode+ is given, missing directories are created with this mode.
#
# World-writable directories will never be created.
def self.ensure_default_gem_subdirectories(dir = Gem.dir, mode = nil)
ensure_subdirectories(dir, mode, REPOSITORY_DEFAULT_GEM_SUBDIRECTORIES)
end
def self.ensure_subdirectories(dir, mode, subdirs) # :nodoc:
old_umask = File.umask
File.umask old_umask | 0o002
options = {}
options[:mode] = mode if mode
subdirs.each do |name|
subdir = File.join dir, name
next if File.exist? subdir
require "fileutils"
begin
FileUtils.mkdir_p subdir, **options
rescue SystemCallError
end
end
ensure
File.umask old_umask
end
##
# The extension API version of ruby. This includes the static vs non-static
# distinction as extensions cannot be shared between the two.
def self.extension_api_version # :nodoc:
if RbConfig::CONFIG["ENABLE_SHARED"] == "no"
"#{ruby_api_version}-static"
else
ruby_api_version
end
end
##
# Returns a list of paths matching +glob+ that can be used by a gem to pick
# up features from other gems. For example:
#
# Gem.find_files('rdoc/discover').each do |path| load path end
#
# if +check_load_path+ is true (the default), then find_files also searches
# $LOAD_PATH for files as well as gems.
#
# Note that find_files will return all files even if they are from different
# versions of the same gem. See also find_latest_files
def self.find_files(glob, check_load_path=true)
files = []
files = find_files_from_load_path glob if check_load_path
gem_specifications = @gemdeps ? Gem.loaded_specs.values : Gem::Specification.stubs
files.concat gem_specifications.map {|spec|
spec.matches_for_glob("#{glob}#{Gem.suffix_pattern}")
}.flatten
# $LOAD_PATH might contain duplicate entries or reference
# the spec dirs directly, so we prune.
files.uniq! if check_load_path
files
end
def self.find_files_from_load_path(glob) # :nodoc:
glob_with_suffixes = "#{glob}#{Gem.suffix_pattern}"
$LOAD_PATH.map do |load_path|
Gem::Util.glob_files_in_dir(glob_with_suffixes, load_path)
end.flatten.select {|file| File.file? file }
end
##
# Returns a list of paths matching +glob+ from the latest gems that can be
# used by a gem to pick up features from other gems. For example:
#
# Gem.find_latest_files('rdoc/discover').each do |path| load path end
#
# if +check_load_path+ is true (the default), then find_latest_files also
# searches $LOAD_PATH for files as well as gems.
#
# Unlike find_files, find_latest_files will return only files from the
# latest version of a gem.
def self.find_latest_files(glob, check_load_path=true)
files = []
files = find_files_from_load_path glob if check_load_path
files.concat Gem::Specification.latest_specs(true).map {|spec|
spec.matches_for_glob("#{glob}#{Gem.suffix_pattern}")
}.flatten
# $LOAD_PATH might contain duplicate entries or reference
# the spec dirs directly, so we prune.
files.uniq! if check_load_path
files
end
##
# Top level install helper method. Allows you to install gems interactively:
#
# % irb
# >> Gem.install "minitest"
# Fetching: minitest-5.14.0.gem (100%)
# => [#]
def self.install(name, version = Gem::Requirement.default, *options)
require_relative "rubygems/dependency_installer"
inst = Gem::DependencyInstaller.new(*options)
inst.install name, version
inst.installed_gems
end
##
# Get the default RubyGems API host. This is normally
# https://rubygems.org.
def self.host
@host ||= Gem::DEFAULT_HOST
end
## Set the default RubyGems API host.
def self.host=(host)
@host = host
end
##
# The index to insert activated gem paths into the $LOAD_PATH. The activated
# gem's paths are inserted before site lib directory by default.
def self.load_path_insert_index
$LOAD_PATH.each_with_index do |path, i|
return i if path.instance_variable_defined?(:@gem_prelude_index)
end
index = $LOAD_PATH.index RbConfig::CONFIG["sitelibdir"]
index || 0
end
##
# The number of paths in the +$LOAD_PATH+ from activated gems. Used to
# prioritize +-I+ and ENV['RUBYLIB'] entries during +require+.
def self.activated_gem_paths
@activated_gem_paths ||= 0
end
##
# Add a list of paths to the $LOAD_PATH at the proper place.
def self.add_to_load_path(*paths)
@activated_gem_paths = activated_gem_paths + paths.size
# gem directories must come after -I and ENV['RUBYLIB']
$LOAD_PATH.insert(Gem.load_path_insert_index, *paths)
end
@yaml_loaded = false
##
# Loads YAML, preferring Psych
def self.load_yaml
return if @yaml_loaded
require "psych"
require_relative "rubygems/psych_tree"
require_relative "rubygems/safe_yaml"
@yaml_loaded = true
end
@safe_marshal_loaded = false
def self.load_safe_marshal
return if @safe_marshal_loaded
require_relative "rubygems/safe_marshal"
@safe_marshal_loaded = true
end
##
# The file name and line number of the caller of the caller of this method.
#
# +depth+ is how many layers up the call stack it should go.
#
# e.g.,
#
# def a; Gem.location_of_caller; end
# a #=> ["x.rb", 2] # (it'll vary depending on file name and line number)
#
# def b; c; end
# def c; Gem.location_of_caller(2); end
# b #=> ["x.rb", 6] # (it'll vary depending on file name and line number)
def self.location_of_caller(depth = 1)
caller[depth] =~ /(.*?):(\d+).*?$/i
file = $1
lineno = $2.to_i
[file, lineno]
end
##
# The version of the Marshal format for your Ruby.
def self.marshal_version
"#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
end
##
# Set array of platforms this RubyGems supports (primarily for testing).
def self.platforms=(platforms)
@platforms = platforms
end
##
# Array of platforms this RubyGems supports.
def self.platforms
@platforms ||= []
if @platforms.empty?
@platforms = [Gem::Platform::RUBY, Gem::Platform.local]
end
@platforms
end
##
# Adds a post-build hook that will be passed an Gem::Installer instance
# when Gem::Installer#install is called. The hook is called after the gem
# has been extracted and extensions have been built but before the
# executables or gemspec has been written. If the hook returns +false+ then
# the gem's files will be removed and the install will be aborted.
def self.post_build(&hook)
@post_build_hooks << hook
end
##
# Adds a post-install hook that will be passed an Gem::Installer instance
# when Gem::Installer#install is called
def self.post_install(&hook)
@post_install_hooks << hook
end
##
# Adds a post-installs hook that will be passed a Gem::DependencyInstaller
# and a list of installed specifications when
# Gem::DependencyInstaller#install is complete
def self.done_installing(&hook)
@done_installing_hooks << hook
end
##
# Adds a hook that will get run after Gem::Specification.reset is
# run.
def self.post_reset(&hook)
@post_reset_hooks << hook
end
##
# Adds a post-uninstall hook that will be passed a Gem::Uninstaller instance
# and the spec that was uninstalled when Gem::Uninstaller#uninstall is
# called
def self.post_uninstall(&hook)
@post_uninstall_hooks << hook
end
##
# Adds a pre-install hook that will be passed an Gem::Installer instance
# when Gem::Installer#install is called. If the hook returns +false+ then
# the install will be aborted.
def self.pre_install(&hook)
@pre_install_hooks << hook
end
##
# Adds a hook that will get run before Gem::Specification.reset is
# run.
def self.pre_reset(&hook)
@pre_reset_hooks << hook
end
##
# Adds a pre-uninstall hook that will be passed an Gem::Uninstaller instance
# and the spec that will be uninstalled when Gem::Uninstaller#uninstall is
# called
def self.pre_uninstall(&hook)
@pre_uninstall_hooks << hook
end
##
# The directory prefix this RubyGems was installed at. If your
# prefix is in a standard location (ie, rubygems is installed where
# you'd expect it to be), then prefix returns nil.
def self.prefix
prefix = File.dirname RUBYGEMS_DIR
if prefix != File.expand_path(RbConfig::CONFIG["sitelibdir"]) &&
prefix != File.expand_path(RbConfig::CONFIG["libdir"]) &&
File.basename(RUBYGEMS_DIR) == "lib"
prefix
end
end
##
# Refresh available gems from disk.
def self.refresh
Gem::Specification.reset
end
##
# Safely read a file in binary mode on all platforms.
def self.read_binary(path)
File.binread(path)
end
##
# Safely write a file in binary mode on all platforms.
def self.write_binary(path, data)
File.binwrite(path, data)
rescue Errno::ENOSPC
# If we ran out of space but the file exists, it's *guaranteed* to be corrupted.
File.delete(path) if File.exist?(path)
raise
end
##
# Open a file with given flags
def self.open_file(path, flags, &block)
File.open(path, flags, &block)
end
##
# Open a file with given flags, and protect access with a file lock
def self.open_file_with_lock(path, &block)
file_lock = "#{path}.lock"
open_file_with_flock(file_lock, &block)
ensure
FileUtils.rm_f file_lock
end
##
# Open a file with given flags, and protect access with flock
def self.open_file_with_flock(path, &block)
# read-write mode is used rather than read-only in order to support NFS
mode = IO::RDWR | IO::APPEND | IO::CREAT | IO::BINARY
mode |= IO::SHARE_DELETE if IO.const_defined?(:SHARE_DELETE)
File.open(path, mode) do |io|
begin
io.flock(File::LOCK_EX)
rescue Errno::ENOSYS, Errno::ENOTSUP
end
yield io
end
end
##
# The path to the running Ruby interpreter.
def self.ruby
if @ruby.nil?
@ruby = RbConfig.ruby
@ruby = "\"#{@ruby}\"" if /\s/.match?(@ruby)
end
@ruby
end
##
# Returns a String containing the API compatibility version of Ruby
def self.ruby_api_version
@ruby_api_version ||= RbConfig::CONFIG["ruby_version"].dup
end
def self.env_requirement(gem_name)
@env_requirements_by_name ||= {}
@env_requirements_by_name[gem_name] ||= begin
req = ENV["GEM_REQUIREMENT_#{gem_name.upcase}"] || ">= 0"
Gem::Requirement.create(req)
end
end
post_reset { @env_requirements_by_name = {} }
##
# Returns the latest release-version specification for the gem +name+.
def self.latest_spec_for(name)
dependency = Gem::Dependency.new name
fetcher = Gem::SpecFetcher.fetcher
spec_tuples, = fetcher.spec_for_dependency dependency
spec, = spec_tuples.last
spec
end
##
# Returns the latest release version of RubyGems.
def self.latest_rubygems_version
latest_version_for("rubygems-update") ||
raise("Can't find 'rubygems-update' in any repo. Check `gem source list`.")
end
##
# Returns the version of the latest release-version of gem +name+
def self.latest_version_for(name)
latest_spec_for(name)&.version
end
##
# A Gem::Version for the currently running Ruby.
def self.ruby_version
return @ruby_version if defined? @ruby_version
version = RUBY_VERSION.dup
if RUBY_PATCHLEVEL == -1
if RUBY_ENGINE == "ruby"
desc = RUBY_DESCRIPTION[/\Aruby #{Regexp.quote(RUBY_VERSION)}([^ ]+) /, 1]
else
desc = RUBY_DESCRIPTION[/\A#{RUBY_ENGINE} #{Regexp.quote(RUBY_ENGINE_VERSION)} \(#{RUBY_VERSION}([^ ]+)\) /, 1]
end
version << ".#{desc}" if desc
end
@ruby_version = Gem::Version.new version
end
##
# A Gem::Version for the currently running RubyGems
def self.rubygems_version
return @rubygems_version if defined? @rubygems_version
@rubygems_version = Gem::Version.new Gem::VERSION
end
##
# Returns an Array of sources to fetch remote gems from. Uses
# default_sources if the sources list is empty.
def self.sources
source_list = configuration.sources || default_sources
@sources ||= Gem::SourceList.from(source_list)
end
##
# Need to be able to set the sources without calling
# Gem.sources.replace since that would cause an infinite loop.
#
# DOC: This comment is not documentation about the method itself, it's
# more of a code comment about the implementation.
def self.sources=(new_sources)
if !new_sources
@sources = nil
else
@sources = Gem::SourceList.from(new_sources)
end
end
##
# Glob pattern for require-able path suffixes.
def self.suffix_pattern
@suffix_pattern ||= "{#{suffixes.join(",")}}"
end
##
# Regexp for require-able path suffixes.
def self.suffix_regexp
@suffix_regexp ||= /#{Regexp.union(suffixes)}\z/
end
##
# Glob pattern for require-able plugin suffixes.
def self.plugin_suffix_pattern
@plugin_suffix_pattern ||= "_plugin#{suffix_pattern}"
end
##
# Regexp for require-able plugin suffixes.
def self.plugin_suffix_regexp
@plugin_suffix_regexp ||= /_plugin#{suffix_regexp}\z/
end
##
# Suffixes for require-able paths.
def self.suffixes
@suffixes ||= ["",
".rb",
*%w[DLEXT DLEXT2].map do |key|
val = RbConfig::CONFIG[key]
next unless val && !val.empty?
".#{val}"
end].compact.uniq
end
##
# Suffixes for dynamic library require-able paths.
def self.dynamic_library_suffixes
@dynamic_library_suffixes ||= suffixes - [".rb"]
end
##
# Prints the amount of time the supplied block takes to run using the debug
# UI output.
def self.time(msg, width = 0, display = Gem.configuration.verbose)
now = Time.now
value = yield
elapsed = Time.now - now
ui.say format("%2$*1$s: %3$3.3fs", -width, msg, elapsed) if display
value
end
##
# Lazily loads DefaultUserInteraction and returns the default UI.
def self.ui
require_relative "rubygems/user_interaction"
Gem::DefaultUserInteraction.ui
end
##
# Use the +home+ and +paths+ values for Gem.dir and Gem.path. Used mainly
# by the unit tests to provide environment isolation.
def self.use_paths(home, *paths)
paths.flatten!
paths.compact!
hash = { "GEM_HOME" => home, "GEM_PATH" => paths.empty? ? home : paths.join(File::PATH_SEPARATOR) }
hash.delete_if {|_, v| v.nil? }
self.paths = hash
end
##
# Is this a windows platform?
def self.win_platform?
if @@win_platform.nil?
ruby_platform = RbConfig::CONFIG["host_os"]
@@win_platform = !WIN_PATTERNS.find {|r| ruby_platform =~ r }.nil?
end
@@win_platform
end
##
# Is this a java platform?
def self.java_platform?
RUBY_PLATFORM == "java"
end
##
# Is this platform Solaris?
def self.solaris_platform?
RUBY_PLATFORM.include?("solaris")
end
##
# Is this platform FreeBSD
def self.freebsd_platform?
RbConfig::CONFIG["host_os"].to_s.include?("bsd")
end
##
# Load +plugins+ as Ruby files
def self.load_plugin_files(plugins) # :nodoc:
plugins.each do |plugin|
# Skip older versions of the GemCutter plugin: Its commands are in
# RubyGems proper now.
next if /gemcutter-0\.[0-3]/.match?(plugin)
begin
load plugin
rescue ScriptError, StandardError => e
details = "#{plugin.inspect}: #{e.message} (#{e.class})"
warn "Error loading RubyGems plugin #{details}"
end
end
end
##
# Find rubygems plugin files in the standard location and load them
def self.load_plugins
Gem.path.each do |gem_path|
load_plugin_files Gem::Util.glob_files_in_dir("*#{Gem.plugin_suffix_pattern}", plugindir(gem_path))
end
end
##
# Find all 'rubygems_plugin' files in $LOAD_PATH and load them
def self.load_env_plugins
load_plugin_files find_files_from_load_path("rubygems_plugin")
end
##
# Looks for a gem dependency file at +path+ and activates the gems in the
# file if found. If the file is not found an ArgumentError is raised.
#
# If +path+ is not given the RUBYGEMS_GEMDEPS environment variable is used,
# but if no file is found no exception is raised.
#
# If '-' is given for +path+ RubyGems searches up from the current working
# directory for gem dependency files (gem.deps.rb, Gemfile, Isolate) and
# activates the gems in the first one found.
#
# You can run this automatically when rubygems starts. To enable, set
# the RUBYGEMS_GEMDEPS environment variable to either the path
# of your gem dependencies file or "-" to auto-discover in parent
# directories.
#
# NOTE: Enabling automatic discovery on multiuser systems can lead to
# execution of arbitrary code when used from directories outside your
# control.
def self.use_gemdeps(path = nil)
raise_exception = path
path ||= ENV["RUBYGEMS_GEMDEPS"]
return unless path
path = path.dup
if path == "-"
Gem::Util.traverse_parents Dir.pwd do |directory|
dep_file = GEM_DEP_FILES.find {|f| File.file?(f) }
next unless dep_file
path = File.join directory, dep_file
break
end
end
unless File.file? path
return unless raise_exception
raise ArgumentError, "Unable to find gem dependencies file at #{path}"
end
ENV["BUNDLE_GEMFILE"] ||= File.expand_path(path)
require_relative "rubygems/user_interaction"
require "bundler"
begin
Gem::DefaultUserInteraction.use_ui(ui) do
Bundler.ui.silence do
@gemdeps = Bundler.setup
end
ensure
Gem::DefaultUserInteraction.ui.close
end
rescue Bundler::BundlerError => e
warn e.message
warn "You may need to `bundle install` to install missing gems"
warn ""
end
end
##
# If the SOURCE_DATE_EPOCH environment variable is set, returns it's value.
# Otherwise, returns the time that +Gem.source_date_epoch_string+ was
# first called in the same format as SOURCE_DATE_EPOCH.
#
# NOTE(@duckinator): The implementation is a tad weird because we want to:
# 1. Make builds reproducible by default, by having this function always
# return the same result during a given run.
# 2. Allow changing ENV['SOURCE_DATE_EPOCH'] at runtime, since multiple
# tests that set this variable will be run in a single process.
#
# If you simplify this function and a lot of tests fail, that is likely
# due to #2 above.
#
# Details on SOURCE_DATE_EPOCH:
# https://reproducible-builds.org/specs/source-date-epoch/
def self.source_date_epoch_string
# The value used if $SOURCE_DATE_EPOCH is not set.
@default_source_date_epoch ||= Time.now.to_i.to_s
specified_epoch = ENV["SOURCE_DATE_EPOCH"]
# If it's empty or just whitespace, treat it like it wasn't set at all.
specified_epoch = nil if !specified_epoch.nil? && specified_epoch.strip.empty?
epoch = specified_epoch || @default_source_date_epoch
epoch.strip
end
##
# Returns the value of Gem.source_date_epoch_string, as a Time object.
#
# This is used throughout RubyGems for enabling reproducible builds.
def self.source_date_epoch
Time.at(source_date_epoch_string.to_i).utc.freeze
end
# FIX: Almost everywhere else we use the `def self.` way of defining class
# methods, and then we switch over to `class << self` here. Pick one or the
# other.
class << self
##
# RubyGems distributors (like operating system package managers) can
# disable RubyGems update by setting this to error message printed to
# end-users on gem update --system instead of actual update.
attr_accessor :disable_system_update_message
##
# Whether RubyGems should enhance builtin `require` to automatically
# check whether the path required is present in installed gems, and
# automatically activate them and add them to `$LOAD_PATH`.
attr_accessor :discover_gems_on_require
##
# Hash of loaded Gem::Specification keyed by name
attr_reader :loaded_specs
##
# GemDependencyAPI object, which is set when .use_gemdeps is called.
# This contains all the information from the Gemfile.
attr_reader :gemdeps
##
# Register a Gem::Specification for default gem.
#
# Two formats for the specification are supported:
#
# * MRI 2.0 style, where spec.files contains unprefixed require names.
# The spec's filenames will be registered as-is.
# * New style, where spec.files contains files prefixed with paths
# from spec.require_paths. The prefixes are stripped before
# registering the spec's filenames. Unprefixed files are omitted.
#
def register_default_spec(spec)
extended_require_paths = spec.require_paths.map {|f| f + "/" }
new_format = extended_require_paths.any? {|path| spec.files.any? {|f| f.start_with? path } }
if new_format
prefix_group = extended_require_paths.join("|")
prefix_pattern = /^(#{prefix_group})/
end
spec.files.each do |file|
if new_format
file = file.sub(prefix_pattern, "")
next unless $~
end
spec.activate if already_loaded?(file)
@path_to_default_spec_map[file] = spec
@path_to_default_spec_map[file.sub(suffix_regexp, "")] = spec
end
end
##
# Find a Gem::Specification of default gem from +path+
def find_default_spec(path)
@path_to_default_spec_map[path]
end
##
# Find an unresolved Gem::Specification of default gem from +path+
def find_unresolved_default_spec(path)
default_spec = @path_to_default_spec_map[path]
default_spec if default_spec && loaded_specs[default_spec.name] != default_spec
end
##
# Clear default gem related variables. It is for test
def clear_default_specs
@path_to_default_spec_map.clear
end
##
# The list of hooks to be run after Gem::Installer#install extracts files
# and builds extensions
attr_reader :post_build_hooks
##
# The list of hooks to be run after Gem::Installer#install completes
# installation
attr_reader :post_install_hooks
##
# The list of hooks to be run after Gem::DependencyInstaller installs a
# set of gems
attr_reader :done_installing_hooks
##
# The list of hooks to be run after Gem::Specification.reset is run.
attr_reader :post_reset_hooks
##
# The list of hooks to be run after Gem::Uninstaller#uninstall completes
# installation
attr_reader :post_uninstall_hooks
##
# The list of hooks to be run before Gem::Installer#install does any work
attr_reader :pre_install_hooks
##
# The list of hooks to be run before Gem::Specification.reset is run.
attr_reader :pre_reset_hooks
##
# The list of hooks to be run before Gem::Uninstaller#uninstall does any
# work
attr_reader :pre_uninstall_hooks
private
def already_loaded?(file)
$LOADED_FEATURES.any? do |feature_path|
feature_path.end_with?(file) && default_gem_load_paths.any? {|load_path_entry| feature_path == "#{load_path_entry}/#{file}" }
end
end
def default_gem_load_paths
@default_gem_load_paths ||= $LOAD_PATH[load_path_insert_index..-1].map do |lp|
expanded = File.expand_path(lp)
next expanded unless File.exist?(expanded)
File.realpath(expanded)
end
end
end
##
# Location of Marshal quick gemspecs on remote repositories
MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/".freeze
autoload :ConfigFile, File.expand_path("rubygems/config_file", __dir__)
autoload :CIDetector, File.expand_path("rubygems/ci_detector", __dir__)
autoload :Dependency, File.expand_path("rubygems/dependency", __dir__)
autoload :DependencyList, File.expand_path("rubygems/dependency_list", __dir__)
autoload :Installer, File.expand_path("rubygems/installer", __dir__)
autoload :Licenses, File.expand_path("rubygems/util/licenses", __dir__)
autoload :NameTuple, File.expand_path("rubygems/name_tuple", __dir__)
autoload :PathSupport, File.expand_path("rubygems/path_support", __dir__)
autoload :RequestSet, File.expand_path("rubygems/request_set", __dir__)
autoload :Requirement, File.expand_path("rubygems/requirement", __dir__)
autoload :Resolver, File.expand_path("rubygems/resolver", __dir__)
autoload :Source, File.expand_path("rubygems/source", __dir__)
autoload :SourceList, File.expand_path("rubygems/source_list", __dir__)
autoload :SpecFetcher, File.expand_path("rubygems/spec_fetcher", __dir__)
autoload :SpecificationPolicy, File.expand_path("rubygems/specification_policy", __dir__)
autoload :Util, File.expand_path("rubygems/util", __dir__)
autoload :Version, File.expand_path("rubygems/version", __dir__)
end
require_relative "rubygems/exceptions"
require_relative "rubygems/specification"
# REFACTOR: This should be pulled out into some kind of hacks file.
begin
##
# Defaults the operating system (or packager) wants to provide for RubyGems.
require "rubygems/defaults/operating_system"
rescue LoadError
# Ignored
rescue StandardError => e
path = e.backtrace_locations.reverse.find {|l| l.path.end_with?("rubygems/defaults/operating_system.rb") }.path
msg = "#{e.message}\n" \
"Loading the #{path} file caused an error. " \
"This file is owned by your OS, not by rubygems upstream. " \
"Please find out which OS package this file belongs to and follow the guidelines from your OS to report " \
"the problem and ask for help."
raise e.class, msg
end
begin
##
# Defaults the Ruby implementation wants to provide for RubyGems
require "rubygems/defaults/#{RUBY_ENGINE}"
rescue LoadError
end
# TruffleRuby >= 24 defines REUSE_AS_BINARY_ON_TRUFFLERUBY in defaults/truffleruby.
# However, TruffleRuby < 24 defines REUSE_AS_BINARY_ON_TRUFFLERUBY directly in its copy
# of lib/rubygems/platform.rb, so it is not defined if RubyGems is updated (gem update --system).
# Instead, we define it here in that case, similar to bundler/lib/bundler/rubygems_ext.rb.
# We must define it here and not in platform.rb because platform.rb is loaded before defaults/truffleruby.
class Gem::Platform
if RUBY_ENGINE == "truffleruby" && !defined?(REUSE_AS_BINARY_ON_TRUFFLERUBY)
REUSE_AS_BINARY_ON_TRUFFLERUBY = %w[libv8 libv8-node sorbet-static].freeze
end
end
##
# Loads the default specs.
Gem::Specification.load_defaults
require_relative "rubygems/core_ext/kernel_gem"
path = File.join(__dir__, "rubygems/core_ext/kernel_require.rb")
# When https://bugs.ruby-lang.org/issues/17259 is available, there is no need to override Kernel#warn
if RUBY_ENGINE == "truffleruby" ||
RUBY_ENGINE == "ruby"
file = ""
else
require_relative "rubygems/core_ext/kernel_warn"
file = path
end
eval File.read(path), nil, file
require ENV["BUNDLER_SETUP"] if ENV["BUNDLER_SETUP"] && !defined?(Bundler)
PK ! 5{y&- &- rubygems/request_set.rbnu [ # frozen_string_literal: true
require_relative "vendored_tsort"
##
# A RequestSet groups a request to activate a set of dependencies.
#
# nokogiri = Gem::Dependency.new 'nokogiri', '~> 1.6'
# pg = Gem::Dependency.new 'pg', '~> 0.14'
#
# set = Gem::RequestSet.new nokogiri, pg
#
# requests = set.resolve
#
# p requests.map { |r| r.full_name }
# #=> ["nokogiri-1.6.0", "mini_portile-0.5.1", "pg-0.17.0"]
class Gem::RequestSet
include Gem::TSort
##
# Array of gems to install even if already installed
attr_accessor :always_install
attr_reader :dependencies
attr_accessor :development
##
# Errors fetching gems during resolution.
attr_reader :errors
##
# Set to true if you want to install only direct development dependencies.
attr_accessor :development_shallow
##
# The set of git gems imported via load_gemdeps.
attr_reader :git_set # :nodoc:
##
# When true, dependency resolution is not performed, only the requested gems
# are installed.
attr_accessor :ignore_dependencies
attr_reader :install_dir # :nodoc:
##
# If true, allow dependencies to match prerelease gems.
attr_accessor :prerelease
##
# When false no remote sets are used for resolving gems.
attr_accessor :remote
attr_reader :resolver # :nodoc:
##
# Sets used for resolution
attr_reader :sets # :nodoc:
##
# Treat missing dependencies as silent errors
attr_accessor :soft_missing
##
# The set of vendor gems imported via load_gemdeps.
attr_reader :vendor_set # :nodoc:
##
# The set of source gems imported via load_gemdeps.
attr_reader :source_set
##
# Creates a RequestSet for a list of Gem::Dependency objects, +deps+. You
# can then #resolve and #install the resolved list of dependencies.
#
# nokogiri = Gem::Dependency.new 'nokogiri', '~> 1.6'
# pg = Gem::Dependency.new 'pg', '~> 0.14'
#
# set = Gem::RequestSet.new nokogiri, pg
def initialize(*deps)
@dependencies = deps
@always_install = []
@conservative = false
@dependency_names = {}
@development = false
@development_shallow = false
@errors = []
@git_set = nil
@ignore_dependencies = false
@install_dir = Gem.dir
@prerelease = false
@remote = true
@requests = []
@sets = []
@soft_missing = false
@sorted_requests = nil
@specs = nil
@vendor_set = nil
@source_set = nil
yield self if block_given?
end
##
# Declare that a gem of name +name+ with +reqs+ requirements is needed.
def gem(name, *reqs)
if dep = @dependency_names[name]
dep.requirement.concat reqs
else
dep = Gem::Dependency.new name, *reqs
@dependency_names[name] = dep
@dependencies << dep
end
end
##
# Add +deps+ Gem::Dependency objects to the set.
def import(deps)
@dependencies.concat deps
end
##
# Installs gems for this RequestSet using the Gem::Installer +options+.
#
# If a +block+ is given an activation +request+ and +installer+ are yielded.
# The +installer+ will be +nil+ if a gem matching the request was already
# installed.
def install(options, &block) # :yields: request, installer
if dir = options[:install_dir]
requests = install_into dir, false, options, &block
return requests
end
@prerelease = options[:prerelease]
requests = []
download_queue = Thread::Queue.new
# Create a thread-safe list of gems to download
sorted_requests.each do |req|
download_queue << req
end
# Create N threads in a pool, have them download all the gems
threads = Array.new(Gem.configuration.concurrent_downloads) do
# When a thread pops this item, it knows to stop running. The symbol
# is queued here so that there will be one symbol per thread.
download_queue << :stop
Thread.new do
# The pop method will block waiting for items, so the only way
# to stop a thread from running is to provide a final item that
# means the thread should stop.
while req = download_queue.pop
break if req == :stop
req.spec.download options unless req.installed?
end
end
end
# Wait for all the downloads to finish before continuing
threads.each(&:value)
# Install requested gems after they have been downloaded
sorted_requests.each do |req|
if req.installed?
req.spec.spec.build_extensions
if @always_install.none? {|spec| spec == req.spec.spec }
yield req, nil if block_given?
next
end
end
spec =
begin
req.spec.install options do |installer|
yield req, installer if block_given?
end
rescue Gem::RuntimeRequirementNotMetError => e
suggestion = "There are no versions of #{req.request} compatible with your Ruby & RubyGems"
suggestion += ". Maybe try installing an older version of the gem you're looking for?" unless @always_install.include?(req.spec.spec)
e.suggestion = suggestion
raise
end
requests << spec
end
return requests if options[:gemdeps]
install_hooks requests, options
requests
end
##
# Installs from the gem dependencies files in the +:gemdeps+ option in
# +options+, yielding to the +block+ as in #install.
#
# If +:without_groups+ is given in the +options+, those groups in the gem
# dependencies file are not used. See Gem::Installer for other +options+.
def install_from_gemdeps(options, &block)
gemdeps = options[:gemdeps]
@install_dir = options[:install_dir] || Gem.dir
@prerelease = options[:prerelease]
@remote = options[:domain] != :local
@conservative = true if options[:conservative]
gem_deps_api = load_gemdeps gemdeps, options[:without_groups], true
resolve
if options[:explain]
puts "Gems to install:"
sorted_requests.each do |spec|
puts " #{spec.full_name}"
end
if Gem.configuration.really_verbose
@resolver.stats.display
end
else
installed = install options, &block
if options.fetch :lock, true
lockfile =
Gem::RequestSet::Lockfile.build self, gemdeps, gem_deps_api.dependencies
lockfile.write
end
installed
end
end
def install_into(dir, force = true, options = {})
gem_home = ENV["GEM_HOME"]
ENV["GEM_HOME"] = dir
existing = force ? [] : specs_in(dir)
existing.delete_if {|s| @always_install.include? s }
dir = File.expand_path dir
installed = []
options[:development] = false
options[:install_dir] = dir
options[:only_install_dir] = true
@prerelease = options[:prerelease]
sorted_requests.each do |request|
spec = request.spec
if existing.find {|s| s.full_name == spec.full_name }
yield request, nil if block_given?
next
end
spec.install options do |installer|
yield request, installer if block_given?
end
installed << request
end
install_hooks installed, options
installed
ensure
ENV["GEM_HOME"] = gem_home
end
##
# Call hooks on installed gems
def install_hooks(requests, options)
specs = requests.map do |request|
case request
when Gem::Resolver::ActivationRequest then
request.spec.spec
else
request
end
end
require_relative "dependency_installer"
inst = Gem::DependencyInstaller.new options
inst.installed_gems.replace specs
Gem.done_installing_hooks.each do |hook|
hook.call inst, specs
end unless Gem.done_installing_hooks.empty?
end
##
# Load a dependency management file.
def load_gemdeps(path, without_groups = [], installing = false)
@git_set = Gem::Resolver::GitSet.new
@vendor_set = Gem::Resolver::VendorSet.new
@source_set = Gem::Resolver::SourceSet.new
@git_set.root_dir = @install_dir
lock_file = "#{File.expand_path(path)}.lock"
begin
tokenizer = Gem::RequestSet::Lockfile::Tokenizer.from_file lock_file
parser = tokenizer.make_parser self, []
parser.parse
rescue Errno::ENOENT
end
gf = Gem::RequestSet::GemDependencyAPI.new self, path
gf.installing = installing
gf.without_groups = without_groups if without_groups
gf.load
end
def pretty_print(q) # :nodoc:
q.group 2, "[RequestSet:", "]" do
q.breakable
if @remote
q.text "remote"
q.breakable
end
if @prerelease
q.text "prerelease"
q.breakable
end
if @development_shallow
q.text "shallow development"
q.breakable
elsif @development
q.text "development"
q.breakable
end
if @soft_missing
q.text "soft missing"
end
q.group 2, "[dependencies:", "]" do
q.breakable
@dependencies.map do |dep|
q.text dep.to_s
q.breakable
end
end
q.breakable
q.text "sets:"
q.breakable
q.pp @sets.map(&:class)
end
end
##
# Resolve the requested dependencies and return an Array of Specification
# objects to be activated.
def resolve(set = Gem::Resolver::BestSet.new)
@sets << set
@sets << @git_set
@sets << @vendor_set
@sets << @source_set
set = Gem::Resolver.compose_sets(*@sets)
set.remote = @remote
set.prerelease = @prerelease
resolver = Gem::Resolver.new @dependencies, set
resolver.development = @development
resolver.development_shallow = @development_shallow
resolver.ignore_dependencies = @ignore_dependencies
resolver.soft_missing = @soft_missing
if @conservative
installed_gems = {}
Gem::Specification.find_all do |spec|
(installed_gems[spec.name] ||= []) << spec
end
resolver.skip_gems = installed_gems
end
@resolver = resolver
@requests = resolver.resolve
@errors = set.errors
@requests
end
##
# Resolve the requested dependencies against the gems available via Gem.path
# and return an Array of Specification objects to be activated.
def resolve_current
resolve Gem::Resolver::CurrentSet.new
end
def sorted_requests
@sorted_requests ||= strongly_connected_components.flatten
end
def specs
@specs ||= @requests.map(&:full_spec)
end
def specs_in(dir)
Gem::Util.glob_files_in_dir("*.gemspec", File.join(dir, "specifications")).map do |g|
Gem::Specification.load g
end
end
def tsort_each_node(&block) # :nodoc:
@requests.each(&block)
end
def tsort_each_child(node) # :nodoc:
node.spec.dependencies.each do |dep|
next if dep.type == :development && !@development
match = @requests.find do |r|
dep.match?(r.spec.name, r.spec.version, r.spec.is_a?(Gem::Resolver::InstalledSpecification) || @prerelease)
end
unless match
next if dep.type == :development && @development_shallow
next if @soft_missing
raise Gem::DependencyError,
"Unresolved dependency found during sorting - #{dep} (requested by #{node.spec.full_name})"
end
yield match
end
end
end
require_relative "request_set/gem_dependency_api"
require_relative "request_set/lockfile"
require_relative "request_set/lockfile/tokenizer"
PK ! }3 3 rubygems/version.rbnu [ # frozen_string_literal: true
require_relative "deprecate"
##
# The Version class processes string versions into comparable
# values. A version string should normally be a series of numbers
# separated by periods. Each part (digits separated by periods) is
# considered its own number, and these are used for sorting. So for
# instance, 3.10 sorts higher than 3.2 because ten is greater than
# two.
#
# If any part contains letters (currently only a-z are supported) then
# that version is considered prerelease. Versions with a prerelease
# part in the Nth part sort less than versions with N-1
# parts. Prerelease parts are sorted alphabetically using the normal
# Ruby string sorting rules. If a prerelease part contains both
# letters and numbers, it will be broken into multiple parts to
# provide expected sort behavior (1.0.a10 becomes 1.0.a.10, and is
# greater than 1.0.a9).
#
# Prereleases sort between real releases (newest to oldest):
#
# 1. 1.0
# 2. 1.0.b1
# 3. 1.0.a.2
# 4. 0.9
#
# If you want to specify a version restriction that includes both prereleases
# and regular releases of the 1.x series this is the best way:
#
# s.add_dependency 'example', '>= 1.0.0.a', '< 2.0.0'
#
# == How Software Changes
#
# Users expect to be able to specify a version constraint that gives them
# some reasonable expectation that new versions of a library will work with
# their software if the version constraint is true, and not work with their
# software if the version constraint is false. In other words, the perfect
# system will accept all compatible versions of the library and reject all
# incompatible versions.
#
# Libraries change in 3 ways (well, more than 3, but stay focused here!).
#
# 1. The change may be an implementation detail only and have no effect on
# the client software.
# 2. The change may add new features, but do so in a way that client software
# written to an earlier version is still compatible.
# 3. The change may change the public interface of the library in such a way
# that old software is no longer compatible.
#
# Some examples are appropriate at this point. Suppose I have a Stack class
# that supports a push and a pop method.
#
# === Examples of Category 1 changes:
#
# * Switch from an array based implementation to a linked-list based
# implementation.
# * Provide an automatic (and transparent) backing store for large stacks.
#
# === Examples of Category 2 changes might be:
#
# * Add a depth method to return the current depth of the stack.
# * Add a top method that returns the current top of stack (without
# changing the stack).
# * Change push so that it returns the item pushed (previously it
# had no usable return value).
#
# === Examples of Category 3 changes might be:
#
# * Changes pop so that it no longer returns a value (you must use
# top to get the top of the stack).
# * Rename the methods to push_item and pop_item.
#
# == RubyGems Rational Versioning
#
# * Versions shall be represented by three non-negative integers, separated
# by periods (e.g. 3.1.4). The first integers is the "major" version
# number, the second integer is the "minor" version number, and the third
# integer is the "build" number.
#
# * A category 1 change (implementation detail) will increment the build
# number.
#
# * A category 2 change (backwards compatible) will increment the minor
# version number and reset the build number.
#
# * A category 3 change (incompatible) will increment the major build number
# and reset the minor and build numbers.
#
# * Any "public" release of a gem should have a different version. Normally
# that means incrementing the build number. This means a developer can
# generate builds all day long, but as soon as they make a public release,
# the version must be updated.
#
# === Examples
#
# Let's work through a project lifecycle using our Stack example from above.
#
# Version 0.0.1:: The initial Stack class is release.
# Version 0.0.2:: Switched to a linked=list implementation because it is
# cooler.
# Version 0.1.0:: Added a depth method.
# Version 1.0.0:: Added top and made pop return nil
# (pop used to return the old top item).
# Version 1.1.0:: push now returns the value pushed (it used it
# return nil).
# Version 1.1.1:: Fixed a bug in the linked list implementation.
# Version 1.1.2:: Fixed a bug introduced in the last fix.
#
# Client A needs a stack with basic push/pop capability. They write to the
# original interface (no top), so their version constraint looks like:
#
# gem 'stack', '>= 0.0'
#
# Essentially, any version is OK with Client A. An incompatible change to
# the library will cause them grief, but they are willing to take the chance
# (we call Client A optimistic).
#
# Client B is just like Client A except for two things: (1) They use the
# depth method and (2) they are worried about future
# incompatibilities, so they write their version constraint like this:
#
# gem 'stack', '~> 0.1'
#
# The depth method was introduced in version 0.1.0, so that version
# or anything later is fine, as long as the version stays below version 1.0
# where incompatibilities are introduced. We call Client B pessimistic
# because they are worried about incompatible future changes (it is OK to be
# pessimistic!).
#
# == Preventing Version Catastrophe:
#
# From: https://www.zenspider.com/ruby/2008/10/rubygems-how-to-preventing-catastrophe.html
#
# Let's say you're depending on the fnord gem version 2.y.z. If you
# specify your dependency as ">= 2.0.0" then, you're good, right? What
# happens if fnord 3.0 comes out and it isn't backwards compatible
# with 2.y.z? Your stuff will break as a result of using ">=". The
# better route is to specify your dependency with an "approximate" version
# specifier ("~>"). They're a tad confusing, so here is how the dependency
# specifiers work:
#
# Specification From ... To (exclusive)
# ">= 3.0" 3.0 ... ∞
# "~> 3.0" 3.0 ... 4.0
# "~> 3.0.0" 3.0.0 ... 3.1
# "~> 3.5" 3.5 ... 4.0
# "~> 3.5.0" 3.5.0 ... 3.6
# "~> 3" 3.0 ... 4.0
#
# For the last example, single-digit versions are automatically extended with
# a zero to give a sensible result.
class Gem::Version
include Comparable
VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?' # :nodoc:
ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/ # :nodoc:
##
# A string representation of this Version.
def version
@version
end
alias_method :to_s, :version
##
# True if the +version+ string matches RubyGems' requirements.
def self.correct?(version)
nil_versions_are_discouraged! if version.nil?
ANCHORED_VERSION_PATTERN.match?(version.to_s)
end
##
# Factory method to create a Version object. Input may be a Version
# or a String. Intended to simplify client code.
#
# ver1 = Version.create('1.3.17') # -> (Version object)
# ver2 = Version.create(ver1) # -> (ver1)
# ver3 = Version.create(nil) # -> nil
def self.create(input)
if self === input # check yourself before you wreck yourself
input
elsif input.nil?
nil_versions_are_discouraged!
nil
else
new input
end
end
@@all = {}
@@bump = {}
@@release = {}
def self.new(version) # :nodoc:
return super unless self == Gem::Version
@@all[version] ||= super
end
def self.nil_versions_are_discouraged!
unless Gem::Deprecate.skip
warn "nil versions are discouraged and will be deprecated in Rubygems 4"
end
end
private_class_method :nil_versions_are_discouraged!
##
# Constructs a Version from the +version+ string. A version string is a
# series of digits or ASCII letters separated by dots.
def initialize(version)
unless self.class.correct?(version)
raise ArgumentError, "Malformed version number string #{version}"
end
# If version is an empty string convert it to 0
version = 0 if version.is_a?(String) && /\A\s*\Z/.match?(version)
@version = version.to_s
# optimization to avoid allocation when given an integer, since we know
# it's to_s won't have any spaces or dashes
unless version.is_a?(Integer)
@version = @version.strip
@version.gsub!("-",".pre.")
end
@version = -@version
@segments = nil
end
##
# Return a new version object where the next to the last revision
# number is one greater (e.g., 5.3.1 => 5.4).
#
# Pre-release (alpha) parts, e.g, 5.3.1.b.2 => 5.4, are ignored.
def bump
@@bump[self] ||= begin
segments = self.segments
segments.pop while segments.any? {|s| String === s }
segments.pop if segments.size > 1
segments[-1] = segments[-1].succ
self.class.new segments.join(".")
end
end
##
# A Version is only eql? to another version if it's specified to the
# same precision. Version "1.0" is not the same as version "1".
def eql?(other)
self.class === other && @version == other.version
end
def hash # :nodoc:
canonical_segments.hash
end
def init_with(coder) # :nodoc:
yaml_initialize coder.tag, coder.map
end
def inspect # :nodoc:
"#<#{self.class} #{version.inspect}>"
end
##
# Dump only the raw version string, not the complete object. It's a
# string for backwards (RubyGems 1.3.5 and earlier) compatibility.
def marshal_dump
[@version]
end
##
# Load custom marshal format. It's a string for backwards (RubyGems
# 1.3.5 and earlier) compatibility.
def marshal_load(array)
initialize array[0]
end
def yaml_initialize(tag, map) # :nodoc:
@version = -map["version"]
@segments = nil
@hash = nil
end
def to_yaml_properties # :nodoc:
["@version"]
end
def encode_with(coder) # :nodoc:
coder.add "version", @version
end
##
# A version is considered a prerelease if it contains a letter.
def prerelease?
unless instance_variable_defined? :@prerelease
@prerelease = /[a-zA-Z]/.match?(version)
end
@prerelease
end
def pretty_print(q) # :nodoc:
q.text "Gem::Version.new(#{version.inspect})"
end
##
# The release for this version (e.g. 1.2.0.a -> 1.2.0).
# Non-prerelease versions return themselves.
def release
@@release[self] ||= if prerelease?
segments = self.segments
segments.pop while segments.any? {|s| String === s }
self.class.new segments.join(".")
else
self
end
end
def segments # :nodoc:
_segments.dup
end
##
# A recommended version for use with a ~> Requirement.
def approximate_recommendation
segments = self.segments
segments.pop while segments.any? {|s| String === s }
segments.pop while segments.size > 2
segments.push 0 while segments.size < 2
recommendation = "~> #{segments.join(".")}"
recommendation += ".a" if prerelease?
recommendation
end
##
# Compares this version with +other+ returning -1, 0, or 1 if the
# other version is larger, the same, or smaller than this
# one. Attempts to compare to something that's not a
# Gem::Version or a valid version String return +nil+.
def <=>(other)
return self <=> self.class.new(other) if (String === other) && self.class.correct?(other)
return unless Gem::Version === other
return 0 if @version == other.version || canonical_segments == other.canonical_segments
lhsegments = canonical_segments
rhsegments = other.canonical_segments
lhsize = lhsegments.size
rhsize = rhsegments.size
limit = (lhsize > rhsize ? lhsize : rhsize) - 1
i = 0
while i <= limit
lhs = lhsegments[i] || 0
rhs = rhsegments[i] || 0
i += 1
next if lhs == rhs
return -1 if String === lhs && Numeric === rhs
return 1 if Numeric === lhs && String === rhs
return lhs <=> rhs
end
0
end
# remove trailing zeros segments before first letter or at the end of the version
def canonical_segments
@canonical_segments ||= begin
# remove trailing 0 segments, using dot or letter as anchor
# may leave a trailing dot which will be ignored by partition_segments
canonical_version = @version.sub(/(?<=[a-zA-Z.])[.0]+\z/, "")
# remove 0 segments before the first letter in a prerelease version
canonical_version.sub!(/(?<=\.|\A)[0.]+(?=[a-zA-Z])/, "") if prerelease?
partition_segments(canonical_version)
end
end
def freeze
prerelease?
_segments
canonical_segments
super
end
protected
def _segments
# segments is lazy so it can pick up version values that come from
# old marshaled versions, which don't go through marshal_load.
# since this version object is cached in @@all, its @segments should be frozen
@segments ||= partition_segments(@version)
end
def partition_segments(ver)
ver.scan(/\d+|[a-z]+/i).map! do |s|
/\A\d/.match?(s) ? s.to_i : -s
end.freeze
end
end
PK ! t
B B rubygems/text.rbnu [ # frozen_string_literal: true
##
# A collection of text-wrangling methods
module Gem::Text
##
# Remove any non-printable characters and make the text suitable for
# printing.
def clean_text(text)
text.gsub(/[\000-\b\v-\f\016-\037\177]/, ".")
end
def truncate_text(text, description, max_length = 100_000)
raise ArgumentError, "max_length must be positive" unless max_length > 0
return text if text.size <= max_length
"Truncating #{description} to #{max_length.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse} characters:\n" + text[0, max_length]
end
##
# Wraps +text+ to +wrap+ characters and optionally indents by +indent+
# characters
def format_text(text, wrap, indent=0)
result = []
work = clean_text(text)
while work.length > wrap do
if work =~ /^(.{0,#{wrap}})[ \n]/
result << $1.rstrip
work.slice!(0, $&.length)
else
result << work.slice!(0, wrap)
end
end
result << work if work.length.nonzero?
result.join("\n").gsub(/^/, " " * indent)
end
def min3(a, b, c) # :nodoc:
if a < b && a < c
a
elsif b < c
b
else
c
end
end
# Returns a value representing the "cost" of transforming str1 into str2
# Vendored version of DidYouMean::Levenshtein.distance from the ruby/did_you_mean gem @ 1.4.0
# https://github.com/ruby/did_you_mean/blob/2ddf39b874808685965dbc47d344cf6c7651807c/lib/did_you_mean/levenshtein.rb#L7-L37
def levenshtein_distance(str1, str2)
n = str1.length
m = str2.length
return m if n.zero?
return n if m.zero?
d = (0..m).to_a
x = nil
# to avoid duplicating an enumerable object, create it outside of the loop
str2_codepoints = str2.codepoints
str1.each_codepoint.with_index(1) do |char1, i|
j = 0
while j < m
cost = char1 == str2_codepoints[j] ? 0 : 1
x = min3(
d[j + 1] + 1, # insertion
i + 1, # deletion
d[j] + cost # substitution
)
d[j] = i
i = x
j += 1
end
d[m] = x
end
x
end
end
PK ! Z_ $ rubygems/request/connection_pools.rbnu [ # frozen_string_literal: true
class Gem::Request::ConnectionPools # :nodoc:
@client = Gem::Net::HTTP
class << self
attr_accessor :client
end
def initialize(proxy_uri, cert_files)
@proxy_uri = proxy_uri
@cert_files = cert_files
@pools = {}
@pool_mutex = Thread::Mutex.new
end
def pool_for(uri)
http_args = net_http_args(uri, @proxy_uri)
key = http_args + [https?(uri)]
@pool_mutex.synchronize do
@pools[key] ||=
if https? uri
Gem::Request::HTTPSPool.new(http_args, @cert_files, @proxy_uri)
else
Gem::Request::HTTPPool.new(http_args, @cert_files, @proxy_uri)
end
end
end
def close_all
@pools.each_value(&:close_all)
end
private
##
# Returns list of no_proxy entries (if any) from the environment
def get_no_proxy_from_env
env_no_proxy = ENV["no_proxy"] || ENV["NO_PROXY"]
return [] if env_no_proxy.nil? || env_no_proxy.empty?
env_no_proxy.split(/\s*,\s*/)
end
def https?(uri)
uri.scheme.casecmp("https").zero?
end
def no_proxy?(host, env_no_proxy)
host = host.downcase
env_no_proxy.any? do |pattern|
env_no_proxy_pattern = pattern.downcase.dup
# Remove dot in front of pattern for wildcard matching
env_no_proxy_pattern[0] = "" if env_no_proxy_pattern[0] == "."
host_tokens = host.split(".")
pattern_tokens = env_no_proxy_pattern.split(".")
intersection = (host_tokens - pattern_tokens) | (pattern_tokens - host_tokens)
# When we do the split into tokens we miss a dot character, so add it back if we need it
missing_dot = intersection.length > 0 ? 1 : 0
start = intersection.join(".").size + missing_dot
no_proxy_host = host[start..-1]
env_no_proxy_pattern == no_proxy_host
end
end
def net_http_args(uri, proxy_uri)
hostname = uri.hostname
net_http_args = [hostname, uri.port]
no_proxy = get_no_proxy_from_env
if proxy_uri && !no_proxy?(hostname, no_proxy)
proxy_hostname = proxy_uri.respond_to?(:hostname) ? proxy_uri.hostname : proxy_uri.host
net_http_args + [
proxy_hostname,
proxy_uri.port,
Gem::UriFormatter.new(proxy_uri.user).unescape,
Gem::UriFormatter.new(proxy_uri.password).unescape,
]
elsif no_proxy? hostname, no_proxy
net_http_args + [nil, nil]
else
net_http_args
end
end
end
PK ! fޟ rubygems/request/https_pool.rbnu [ # frozen_string_literal: true
class Gem::Request::HTTPSPool < Gem::Request::HTTPPool # :nodoc:
private
def setup_connection(connection)
Gem::Request.configure_connection_for_https(connection, @cert_files)
super
end
end
PK ! @ rubygems/request/http_pool.rbnu [ # frozen_string_literal: true
##
# A connection "pool" that only manages one connection for now. Provides
# thread safe `checkout` and `checkin` methods. The pool consists of one
# connection that corresponds to `http_args`. This class is private, do not
# use it.
class Gem::Request::HTTPPool # :nodoc:
attr_reader :cert_files, :proxy_uri
def initialize(http_args, cert_files, proxy_uri)
@http_args = http_args
@cert_files = cert_files
@proxy_uri = proxy_uri
@queue = Thread::SizedQueue.new 1
@queue << nil
end
def checkout
@queue.pop || make_connection
end
def checkin(connection)
@queue.push connection
end
def close_all
until @queue.empty?
if (connection = @queue.pop(true)) && connection.started?
connection.finish
end
end
@queue.push(nil)
end
private
def make_connection
setup_connection Gem::Request::ConnectionPools.client.new(*@http_args)
end
def setup_connection(connection)
connection.start
connection
end
end
PK ! *g! g! rubygems/platform.rbnu [ # frozen_string_literal: true
require_relative "deprecate"
##
# Available list of platforms for targeting Gem installations.
#
# See `gem help platform` for information on platform matching.
class Gem::Platform
@local = nil
attr_accessor :cpu, :os, :version
def self.local
@local ||= begin
arch = RbConfig::CONFIG["arch"]
arch = "#{arch}_60" if /mswin(?:32|64)$/.match?(arch)
new(arch)
end
end
def self.match(platform)
match_platforms?(platform, Gem.platforms)
end
class << self
extend Gem::Deprecate
rubygems_deprecate :match, "Gem::Platform.match_spec? or match_gem?"
end
def self.match_platforms?(platform, platforms)
platform = Gem::Platform.new(platform) unless platform.is_a?(Gem::Platform)
platforms.any? do |local_platform|
platform.nil? ||
local_platform == platform ||
(local_platform != Gem::Platform::RUBY && platform =~ local_platform)
end
end
private_class_method :match_platforms?
def self.match_spec?(spec)
match_gem?(spec.platform, spec.name)
end
if RUBY_ENGINE == "truffleruby"
def self.match_gem?(platform, gem_name)
raise "Not a string: #{gem_name.inspect}" unless String === gem_name
if REUSE_AS_BINARY_ON_TRUFFLERUBY.include?(gem_name)
match_platforms?(platform, [Gem::Platform::RUBY, Gem::Platform.local])
else
match_platforms?(platform, Gem.platforms)
end
end
else
def self.match_gem?(platform, gem_name)
match_platforms?(platform, Gem.platforms)
end
end
def self.sort_priority(platform)
platform == Gem::Platform::RUBY ? -1 : 1
end
def self.installable?(spec)
if spec.respond_to? :installable_platform?
spec.installable_platform?
else
match_spec? spec
end
end
def self.new(arch) # :nodoc:
case arch
when Gem::Platform::CURRENT then
Gem::Platform.local
when Gem::Platform::RUBY, nil, "" then
Gem::Platform::RUBY
else
super
end
end
def initialize(arch)
case arch
when Array then
@cpu, @os, @version = arch
when String then
arch = arch.split "-"
if arch.length > 2 && !arch.last.match?(/\d+(\.\d+)?$/) # reassemble x86-linux-{libc}
extra = arch.pop
arch.last << "-#{extra}"
end
cpu = arch.shift
@cpu = case cpu
when /i\d86/ then "x86"
else cpu
end
if arch.length == 2 && arch.last.match?(/^\d+(\.\d+)?$/) # for command-line
@os, @version = arch
return
end
os, = arch
if os.nil?
@cpu = nil
os = cpu
end # legacy jruby
@os, @version = case os
when /aix(\d+)?/ then ["aix", $1]
when /cygwin/ then ["cygwin", nil]
when /darwin(\d+)?/ then ["darwin", $1]
when /^macruby$/ then ["macruby", nil]
when /freebsd(\d+)?/ then ["freebsd", $1]
when /^java$/, /^jruby$/ then ["java", nil]
when /^java([\d.]*)/ then ["java", $1]
when /^dalvik(\d+)?$/ then ["dalvik", $1]
when /^dotnet$/ then ["dotnet", nil]
when /^dotnet([\d.]*)/ then ["dotnet", $1]
when /linux-?(\w+)?/ then ["linux", $1]
when /mingw32/ then ["mingw32", nil]
when /mingw-?(\w+)?/ then ["mingw", $1]
when /(mswin\d+)(\_(\d+))?/ then
os = $1
version = $3
@cpu = "x86" if @cpu.nil? && os =~ /32$/
[os, version]
when /netbsdelf/ then ["netbsdelf", nil]
when /openbsd(\d+\.\d+)?/ then ["openbsd", $1]
when /solaris(\d+\.\d+)?/ then ["solaris", $1]
when /wasi/ then ["wasi", nil]
# test
when /^(\w+_platform)(\d+)?/ then [$1, $2]
else ["unknown", nil]
end
when Gem::Platform then
@cpu = arch.cpu
@os = arch.os
@version = arch.version
else
raise ArgumentError, "invalid argument #{arch.inspect}"
end
end
def to_a
[@cpu, @os, @version]
end
def to_s
to_a.compact.join "-"
end
##
# Is +other+ equal to this platform? Two platforms are equal if they have
# the same CPU, OS and version.
def ==(other)
self.class === other && to_a == other.to_a
end
alias_method :eql?, :==
def hash # :nodoc:
to_a.hash
end
##
# Does +other+ match this platform? Two platforms match if they have the
# same CPU, or either has a CPU of 'universal', they have the same OS, and
# they have the same version, or either one has no version
#
# Additionally, the platform will match if the local CPU is 'arm' and the
# other CPU starts with "armv" (for generic 32-bit ARM family support).
#
# Of note, this method is not commutative. Indeed the OS 'linux' has a
# special case: the version is the libc name, yet while "no version" stands
# as a wildcard for a binary gem platform (as for other OSes), for the
# runtime platform "no version" stands for 'gnu'. To be able to distinguish
# these, the method receiver is the gem platform, while the argument is
# the runtime platform.
#
#--
# NOTE: Until it can be removed, changes to this method must also be reflected in `bundler/lib/bundler/rubygems_ext.rb`
def ===(other)
return nil unless Gem::Platform === other
# universal-mingw32 matches x64-mingw-ucrt
return true if (@cpu == "universal" || other.cpu == "universal") &&
@os.start_with?("mingw") && other.os.start_with?("mingw")
# cpu
([nil,"universal"].include?(@cpu) || [nil, "universal"].include?(other.cpu) || @cpu == other.cpu ||
(@cpu == "arm" && other.cpu.start_with?("armv"))) &&
# os
@os == other.os &&
# version
(
(@os != "linux" && (@version.nil? || other.version.nil?)) ||
(@os == "linux" && (normalized_linux_version == other.normalized_linux_version || ["musl#{@version}", "musleabi#{@version}", "musleabihf#{@version}"].include?(other.version))) ||
@version == other.version
)
end
#--
# NOTE: Until it can be removed, changes to this method must also be reflected in `bundler/lib/bundler/rubygems_ext.rb`
def normalized_linux_version
return nil unless @version
without_gnu_nor_abi_modifiers = @version.sub(/\Agnu/, "").sub(/eabi(hf)?\Z/, "")
return nil if without_gnu_nor_abi_modifiers.empty?
without_gnu_nor_abi_modifiers
end
##
# Does +other+ match this platform? If +other+ is a String it will be
# converted to a Gem::Platform first. See #=== for matching rules.
def =~(other)
case other
when Gem::Platform then # nop
when String then
# This data is from http://gems.rubyforge.org/gems/yaml on 19 Aug 2007
other = case other
when /^i686-darwin(\d)/ then ["x86", "darwin", $1]
when /^i\d86-linux/ then ["x86", "linux", nil]
when "java", "jruby" then [nil, "java", nil]
when /^dalvik(\d+)?$/ then [nil, "dalvik", $1]
when /dotnet(\-(\d+\.\d+))?/ then ["universal","dotnet", $2]
when /mswin32(\_(\d+))?/ then ["x86", "mswin32", $2]
when /mswin64(\_(\d+))?/ then ["x64", "mswin64", $2]
when "powerpc-darwin" then ["powerpc", "darwin", nil]
when /powerpc-darwin(\d)/ then ["powerpc", "darwin", $1]
when /sparc-solaris2.8/ then ["sparc", "solaris", "2.8"]
when /universal-darwin(\d)/ then ["universal", "darwin", $1]
else other
end
other = Gem::Platform.new other
else
return nil
end
self === other
end
##
# A pure-Ruby gem that may use Gem::Specification#extensions to build
# binary files.
RUBY = "ruby"
##
# A platform-specific gem that is built for the packaging Ruby's platform.
# This will be replaced with Gem::Platform::local.
CURRENT = "current"
end
PK ! 8p! ! rubygems/query_utils.rbnu [ # frozen_string_literal: true
require_relative "local_remote_options"
require_relative "spec_fetcher"
require_relative "version_option"
require_relative "text"
module Gem::QueryUtils
include Gem::Text
include Gem::LocalRemoteOptions
include Gem::VersionOption
def add_query_options
add_option("-i", "--[no-]installed",
"Check for installed gem") do |value, options|
options[:installed] = value
end
add_option("-I", "Equivalent to --no-installed") do |_value, options|
options[:installed] = false
end
add_version_option command, "for use with --installed"
add_option("-d", "--[no-]details",
"Display detailed information of gem(s)") do |value, options|
options[:details] = value
end
add_option("--[no-]versions",
"Display only gem names") do |value, options|
options[:versions] = value
options[:details] = false unless value
end
add_option("-a", "--all",
"Display all gem versions") do |value, options|
options[:all] = value
end
add_option("-e", "--exact",
"Name of gem(s) to query on matches the",
"provided STRING") do |value, options|
options[:exact] = value
end
add_option("--[no-]prerelease",
"Display prerelease versions") do |value, options|
options[:prerelease] = value
end
add_local_remote_options
end
def defaults_str # :nodoc:
"--local --no-details --versions --no-installed"
end
def execute
gem_names = if args.empty?
[options[:name]]
else
options[:exact] ? args.map {|arg| /\A#{Regexp.escape(arg)}\Z/ } : args.map {|arg| /#{arg}/i }
end
terminate_interaction(check_installed_gems(gem_names)) if check_installed_gems?
gem_names.each {|n| show_gems(n) }
end
private
def check_installed_gems(gem_names)
exit_code = 0
if args.empty? && !gem_name?
alert_error "You must specify a gem name"
exit_code = 4
elsif gem_names.count > 1
alert_error "You must specify only ONE gem!"
exit_code = 4
else
installed = installed?(gem_names.first, options[:version])
installed = !installed unless options[:installed]
say(installed)
exit_code = 1 unless installed
end
exit_code
end
def check_installed_gems?
!options[:installed].nil?
end
def gem_name?
!options[:name].nil?
end
def prerelease
options[:prerelease]
end
def show_prereleases?
prerelease.nil? || prerelease
end
def args
options[:args].to_a
end
def display_header(type)
if (ui.outs.tty? && Gem.configuration.verbose) || both?
say
say "*** #{type} GEMS ***"
say
end
end
# Guts of original execute
def show_gems(name)
show_local_gems(name) if local?
show_remote_gems(name) if remote?
end
def show_local_gems(name, req = Gem::Requirement.default)
display_header("LOCAL")
specs = Gem::Specification.find_all do |s|
name_matches = name ? s.name =~ name : true
version_matches = show_prereleases? || !s.version.prerelease?
name_matches && version_matches
end.uniq(&:full_name)
spec_tuples = specs.map do |spec|
[spec.name_tuple, spec]
end
output_query_results(spec_tuples)
end
def show_remote_gems(name)
display_header("REMOTE")
fetcher = Gem::SpecFetcher.fetcher
spec_tuples = if name.nil?
fetcher.detect(specs_type) { true }
else
fetcher.detect(specs_type) do |name_tuple|
name === name_tuple.name && options[:version].satisfied_by?(name_tuple.version)
end
end
output_query_results(spec_tuples)
end
def specs_type
if options[:all] || options[:version].specific?
if options[:prerelease]
:complete
else
:released
end
elsif options[:prerelease]
:prerelease
else
:latest
end
end
##
# Check if gem +name+ version +version+ is installed.
def installed?(name, req = Gem::Requirement.default)
Gem::Specification.any? {|s| s.name =~ name && req =~ s.version }
end
def output_query_results(spec_tuples)
output = []
versions = Hash.new {|h,name| h[name] = [] }
spec_tuples.each do |spec_tuple, source|
versions[spec_tuple.name] << [spec_tuple, source]
end
versions = versions.sort_by do |(n,_),_|
n.downcase
end
output_versions output, versions
say output.join(options[:details] ? "\n\n" : "\n")
end
def output_versions(output, versions)
versions.each do |_gem_name, matching_tuples|
matching_tuples = matching_tuples.sort_by {|n,_| n.version }.reverse
platforms = Hash.new {|h,version| h[version] = [] }
matching_tuples.each do |n, _|
platforms[n.version] << n.platform if n.platform
end
seen = {}
matching_tuples.delete_if do |n,_|
if seen[n.version]
true
else
seen[n.version] = true
false
end
end
output << clean_text(make_entry(matching_tuples, platforms))
end
end
def entry_details(entry, detail_tuple, specs, platforms)
return unless options[:details]
name_tuple, spec = detail_tuple
spec = spec.fetch_spec(name_tuple)if spec.respond_to?(:fetch_spec)
entry << "\n"
spec_platforms entry, platforms
spec_authors entry, spec
spec_homepage entry, spec
spec_license entry, spec
spec_loaded_from entry, spec, specs
spec_summary entry, spec
end
def entry_versions(entry, name_tuples, platforms, specs)
return unless options[:versions]
list =
if platforms.empty? || options[:details]
name_tuples.map(&:version).uniq
else
platforms.sort.reverse.map do |version, pls|
out = version.to_s
if options[:domain] == :local
default = specs.any? do |s|
!s.is_a?(Gem::Source) && s.version == version && s.default_gem?
end
out = "default: #{out}" if default
end
if pls != [Gem::Platform::RUBY]
platform_list = [pls.delete(Gem::Platform::RUBY), *pls.sort].compact
out = platform_list.unshift(out).join(" ")
end
out
end
end
entry << " (#{list.join ", "})"
end
def make_entry(entry_tuples, platforms)
detail_tuple = entry_tuples.first
name_tuples, specs = entry_tuples.flatten.partition do |item|
Gem::NameTuple === item
end
entry = [name_tuples.first.name]
entry_versions(entry, name_tuples, platforms, specs)
entry_details(entry, detail_tuple, specs, platforms)
entry.join
end
def spec_authors(entry, spec)
authors = "Author#{spec.authors.length > 1 ? "s" : ""}: ".dup
authors << spec.authors.join(", ")
entry << format_text(authors, 68, 4)
end
def spec_homepage(entry, spec)
return if spec.homepage.nil? || spec.homepage.empty?
entry << "\n" << format_text("Homepage: #{spec.homepage}", 68, 4)
end
def spec_license(entry, spec)
return if spec.license.nil? || spec.license.empty?
licenses = "License#{spec.licenses.length > 1 ? "s" : ""}: ".dup
licenses << spec.licenses.join(", ")
entry << "\n" << format_text(licenses, 68, 4)
end
def spec_loaded_from(entry, spec, specs)
return unless spec.loaded_from
if specs.length == 1
default = spec.default_gem? ? " (default)" : nil
entry << "\n" << " Installed at#{default}: #{spec.base_dir}"
else
label = "Installed at"
specs.each do |s|
version = s.version.to_s
default = ", default" if s.default_gem?
entry << "\n" << " #{label} (#{version}#{default}): #{s.base_dir}"
label = " " * label.length
end
end
end
def spec_platforms(entry, platforms)
non_ruby = platforms.any? do |_, pls|
pls.any? {|pl| pl != Gem::Platform::RUBY }
end
return unless non_ruby
if platforms.length == 1
title = platforms.values.length == 1 ? "Platform" : "Platforms"
entry << " #{title}: #{platforms.values.sort.join(", ")}\n"
else
entry << " Platforms:\n"
sorted_platforms = platforms.sort
sorted_platforms.each do |version, pls|
label = " #{version}: "
data = format_text pls.sort.join(", "), 68, label.length
data[0, label.length] = label
entry << data << "\n"
end
end
end
def spec_summary(entry, spec)
summary = truncate_text(spec.summary, "the summary for #{spec.full_name}")
entry << "\n\n" << format_text(summary, 68, 4)
end
end
PK ! B rubygems/errors.rbnu [ # frozen_string_literal: true
#--
# This file contains all the various exceptions and other errors that are used
# inside of RubyGems.
#
# DOC: Confirm _all_
#++
module Gem
##
# Raised when RubyGems is unable to load or activate a gem. Contains the
# name and version requirements of the gem that either conflicts with
# already activated gems or that RubyGems is otherwise unable to activate.
class LoadError < ::LoadError
# Name of gem
attr_accessor :name
# Version requirement of gem
attr_accessor :requirement
end
##
# Raised when trying to activate a gem, and that gem does not exist on the
# system. Instead of rescuing from this class, make sure to rescue from the
# superclass Gem::LoadError to catch all types of load errors.
class MissingSpecError < Gem::LoadError
def initialize(name, requirement, extra_message=nil)
@name = name
@requirement = requirement
@extra_message = extra_message
super(message)
end
def message # :nodoc:
build_message +
"Checked in 'GEM_PATH=#{Gem.path.join(File::PATH_SEPARATOR)}' #{@extra_message}, execute `gem env` for more information"
end
private
def build_message
total = Gem::Specification.stubs.size
"Could not find '#{name}' (#{requirement}) among #{total} total gem(s)\n"
end
end
##
# Raised when trying to activate a gem, and the gem exists on the system, but
# not the requested version. Instead of rescuing from this class, make sure to
# rescue from the superclass Gem::LoadError to catch all types of load errors.
class MissingSpecVersionError < MissingSpecError
attr_reader :specs
def initialize(name, requirement, specs)
@specs = specs
super(name, requirement)
end
private
def build_message
names = specs.map(&:full_name)
"Could not find '#{name}' (#{requirement}) - did find: [#{names.join ","}]\n"
end
end
# Raised when there are conflicting gem specs loaded
class ConflictError < LoadError
##
# A Hash mapping conflicting specifications to the dependencies that
# caused the conflict
attr_reader :conflicts
##
# The specification that had the conflict
attr_reader :target
def initialize(target, conflicts)
@target = target
@conflicts = conflicts
@name = target.name
reason = conflicts.map do |act, dependencies|
"#{act.full_name} conflicts with #{dependencies.join(", ")}"
end.join ", "
# TODO: improve message by saying who activated `con`
super("Unable to activate #{target.full_name}, because #{reason}")
end
end
class ErrorReason; end
# Generated when trying to lookup a gem to indicate that the gem
# was found, but that it isn't usable on the current platform.
#
# fetch and install read these and report them to the user to aid
# in figuring out why a gem couldn't be installed.
#
class PlatformMismatch < ErrorReason
##
# the name of the gem
attr_reader :name
##
# the version
attr_reader :version
##
# The platforms that are mismatched
attr_reader :platforms
def initialize(name, version)
@name = name
@version = version
@platforms = []
end
##
# append a platform to the list of mismatched platforms.
#
# Platforms are added via this instead of injected via the constructor
# so that we can loop over a list of mismatches and just add them rather
# than perform some kind of calculation mismatch summary before creation.
def add_platform(platform)
@platforms << platform
end
##
# A wordy description of the error.
def wordy
format("Found %s (%s), but was for platform%s %s", @name, @version, @platforms.size == 1 ? "" : "s", @platforms.join(" ,"))
end
end
##
# An error that indicates we weren't able to fetch some
# data from a source
class SourceFetchProblem < ErrorReason
##
# Creates a new SourceFetchProblem for the given +source+ and +error+.
def initialize(source, error)
@source = source
@error = error
end
##
# The source that had the fetch problem.
attr_reader :source
##
# The fetch error which is an Exception subclass.
attr_reader :error
##
# An English description of the error.
def wordy
"Unable to download data from #{Gem::Uri.redact(@source.uri)} - #{@error.message}"
end
##
# The "exception" alias allows you to call raise on a SourceFetchProblem.
alias_method :exception, :error
end
end
PK ! (ղ, , rubygems/gemcutter_utilities.rbnu [ # frozen_string_literal: true
require_relative "remote_fetcher"
require_relative "text"
require_relative "gemcutter_utilities/webauthn_listener"
require_relative "gemcutter_utilities/webauthn_poller"
##
# Utility methods for using the RubyGems API.
module Gem::GemcutterUtilities
ERROR_CODE = 1
API_SCOPES = [:index_rubygems, :push_rubygem, :yank_rubygem, :add_owner, :remove_owner, :access_webhooks].freeze
EXCLUSIVELY_API_SCOPES = [:show_dashboard].freeze
include Gem::Text
attr_writer :host
attr_writer :scope
##
# Add the --key option
def add_key_option
add_option("-k", "--key KEYNAME", Symbol,
"Use the given API key",
"from #{Gem.configuration.credentials_path}") do |value,options|
options[:key] = value
end
end
##
# Add the --otp option
def add_otp_option
add_option("--otp CODE",
"Digit code for multifactor authentication",
"You can also use the environment variable GEM_HOST_OTP_CODE") do |value, options|
options[:otp] = value
end
end
##
# The API key from the command options or from the user's configuration.
def api_key
if ENV["GEM_HOST_API_KEY"]
ENV["GEM_HOST_API_KEY"]
elsif options[:key]
verify_api_key options[:key]
elsif Gem.configuration.api_keys.key?(host)
Gem.configuration.api_keys[host]
else
Gem.configuration.rubygems_api_key
end
end
##
# The OTP code from the command options or from the user's configuration.
def otp
options[:otp] || ENV["GEM_HOST_OTP_CODE"]
end
##
# The host to connect to either from the RUBYGEMS_HOST environment variable
# or from the user's configuration
def host
configured_host = Gem.host unless
Gem.configuration.disable_default_gem_server
@host ||=
begin
env_rubygems_host = ENV["RUBYGEMS_HOST"]
env_rubygems_host = nil if env_rubygems_host&.empty?
env_rubygems_host || configured_host
end
end
##
# Creates an RubyGems API to +host+ and +path+ with the given HTTP +method+.
#
# If +allowed_push_host+ metadata is present, then it will only allow that host.
def rubygems_api_request(method, path, host = nil, allowed_push_host = nil, scope: nil, credentials: {}, &block)
require_relative "vendored_net_http"
self.host = host if host
unless self.host
alert_error "You must specify a gem server"
terminate_interaction(ERROR_CODE)
end
if allowed_push_host
allowed_host_uri = Gem::URI.parse(allowed_push_host)
host_uri = Gem::URI.parse(self.host)
unless (host_uri.scheme == allowed_host_uri.scheme) && (host_uri.host == allowed_host_uri.host)
alert_error "#{self.host.inspect} is not allowed by the gemspec, which only allows #{allowed_push_host.inspect}"
terminate_interaction(ERROR_CODE)
end
end
uri = Gem::URI.parse "#{self.host}/#{path}"
response = request_with_otp(method, uri, &block)
if mfa_unauthorized?(response)
fetch_otp(credentials)
response = request_with_otp(method, uri, &block)
end
if api_key_forbidden?(response)
update_scope(scope)
request_with_otp(method, uri, &block)
else
response
end
end
def mfa_unauthorized?(response)
response.is_a?(Gem::Net::HTTPUnauthorized) && response.body.start_with?("You have enabled multifactor authentication")
end
def update_scope(scope)
sign_in_host = host
pretty_host = pretty_host(sign_in_host)
update_scope_params = { scope => true }
say "The existing key doesn't have access of #{scope} on #{pretty_host}. Please sign in to update access."
identifier = ask "Username/email: "
password = ask_for_password " Password: "
response = rubygems_api_request(:put, "api/v1/api_key",
sign_in_host, scope: scope) do |request|
request.basic_auth identifier, password
request["OTP"] = otp if otp
request.body = Gem::URI.encode_www_form({ api_key: api_key }.merge(update_scope_params))
end
with_response response do |_resp|
say "Added #{scope} scope to the existing API key"
end
end
##
# Signs in with the RubyGems API at +sign_in_host+ and sets the rubygems API
# key.
def sign_in(sign_in_host = nil, scope: nil)
sign_in_host ||= host
return if api_key
pretty_host = pretty_host(sign_in_host)
say "Enter your #{pretty_host} credentials."
say "Don't have an account yet? " \
"Create one at #{sign_in_host}/sign_up"
identifier = ask "Username/email: "
password = ask_for_password " Password: "
say "\n"
key_name = get_key_name(scope)
scope_params = get_scope_params(scope)
profile = get_user_profile(identifier, password)
mfa_params = get_mfa_params(profile)
all_params = scope_params.merge(mfa_params)
warning = profile["warning"]
credentials = { identifier: identifier, password: password }
say "#{warning}\n" if warning
response = rubygems_api_request(:post, "api/v1/api_key",
sign_in_host, credentials: credentials, scope: scope) do |request|
request.basic_auth identifier, password
request["OTP"] = otp if otp
request.body = Gem::URI.encode_www_form({ name: key_name }.merge(all_params))
end
with_response response do |resp|
say "Signed in with API key: #{key_name}."
set_api_key host, resp.body
end
end
##
# Retrieves the pre-configured API key +key+ or terminates interaction with
# an error.
def verify_api_key(key)
if Gem.configuration.api_keys.key? key
Gem.configuration.api_keys[key]
else
alert_error "No such API key. Please add it to your configuration (done automatically on initial `gem push`)."
terminate_interaction(ERROR_CODE)
end
end
##
# If +response+ is an HTTP Success (2XX) response, yields the response if a
# block was given or shows the response body to the user.
#
# If the response was not successful, shows an error to the user including
# the +error_prefix+ and the response body. If the response was a permanent redirect,
# shows an error to the user including the redirect location.
def with_response(response, error_prefix = nil)
case response
when Gem::Net::HTTPSuccess then
if block_given?
yield response
else
say clean_text(response.body)
end
when Gem::Net::HTTPPermanentRedirect, Gem::Net::HTTPRedirection then
message = "The request has redirected permanently to #{response["location"]}. Please check your defined push host URL."
message = "#{error_prefix}: #{message}" if error_prefix
say clean_text(message)
terminate_interaction(ERROR_CODE)
else
message = response.body
message = "#{error_prefix}: #{message}" if error_prefix
say clean_text(message)
terminate_interaction(ERROR_CODE)
end
end
##
# Returns true when the user has enabled multifactor authentication from
# +response+ text and no otp provided by options.
def set_api_key(host, key)
if default_host?
Gem.configuration.rubygems_api_key = key
else
Gem.configuration.set_api_key host, key
end
end
private
def request_with_otp(method, uri, &block)
request_method = Gem::Net::HTTP.const_get method.to_s.capitalize
Gem::RemoteFetcher.fetcher.request(uri, request_method) do |req|
req["OTP"] = otp if otp
block.call(req)
end
end
def fetch_otp(credentials)
options[:otp] = if webauthn_url = webauthn_verification_url(credentials)
server = TCPServer.new 0
port = server.addr[1].to_s
url_with_port = "#{webauthn_url}?port=#{port}"
say "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option."
threads = [WebauthnListener.listener_thread(host, server), WebauthnPoller.poll_thread(options, host, webauthn_url, credentials)]
otp_thread = wait_for_otp_thread(*threads)
threads.each(&:join)
if error = otp_thread[:error]
alert_error error.message
terminate_interaction(1)
end
say "You are verified with a security device. You may close the browser window."
otp_thread[:otp]
else
say "You have enabled multi-factor authentication. Please enter OTP code."
ask "Code: "
end
end
def wait_for_otp_thread(*threads)
loop do
threads.each do |otp_thread|
return otp_thread unless otp_thread.alive?
end
sleep 0.1
end
ensure
threads.each(&:exit)
end
def webauthn_verification_url(credentials)
response = rubygems_api_request(:post, "api/v1/webauthn_verification") do |request|
if credentials.empty?
request.add_field "Authorization", api_key
else
request.basic_auth credentials[:identifier], credentials[:password]
end
end
response.is_a?(Gem::Net::HTTPSuccess) ? response.body : nil
end
def pretty_host(host)
if default_host?
"RubyGems.org"
else
host
end
end
def get_scope_params(scope)
scope_params = { index_rubygems: true }
if scope
scope_params = { scope => true }
else
say "The default access scope is:"
scope_params.each do |k, _v|
say " #{k}: y"
end
say "\n"
customise = ask_yes_no("Do you want to customise scopes?", false)
if customise
EXCLUSIVELY_API_SCOPES.each do |excl_scope|
selected = ask_yes_no("#{excl_scope} (exclusive scope, answering yes will not prompt for other scopes)", false)
next unless selected
return { excl_scope => true }
end
scope_params = {}
API_SCOPES.each do |s|
selected = ask_yes_no(s.to_s, false)
scope_params[s] = true if selected
end
end
say "\n"
end
scope_params
end
def default_host?
host == Gem::DEFAULT_HOST
end
def get_user_profile(identifier, password)
return {} unless default_host?
response = rubygems_api_request(:get, "api/v1/profile/me.yaml") do |request|
request.basic_auth identifier, password
end
with_response response do |resp|
Gem::ConfigFile.load_with_rubygems_config_hash(clean_text(resp.body))
end
end
def get_mfa_params(profile)
mfa_level = profile["mfa"]
params = {}
if ["ui_only", "ui_and_gem_signin"].include?(mfa_level)
selected = ask_yes_no("Would you like to enable MFA for this key? (strongly recommended)")
params["mfa"] = true if selected
end
params
end
def get_key_name(scope)
hostname = Socket.gethostname || "unknown-host"
user = ENV["USER"] || ENV["USERNAME"] || "unknown-user"
ts = Time.now.strftime("%Y%m%d%H%M%S")
default_key_name = "#{hostname}-#{user}-#{ts}"
key_name = ask "API Key name [#{default_key_name}]: " unless scope
if key_name.nil? || key_name.empty?
default_key_name
else
key_name
end
end
def api_key_forbidden?(response)
response.is_a?(Gem::Net::HTTPForbidden) && response.body.start_with?("The API key doesn't have access")
end
end
PK ! <$' ' rubygems/dependency_installer.rbnu [ # frozen_string_literal: true
require_relative "../rubygems"
require_relative "dependency_list"
require_relative "package"
require_relative "installer"
require_relative "spec_fetcher"
require_relative "user_interaction"
require_relative "available_set"
require_relative "deprecate"
##
# Installs a gem along with all its dependencies from local and remote gems.
class Gem::DependencyInstaller
include Gem::UserInteraction
extend Gem::Deprecate
DEFAULT_OPTIONS = { # :nodoc:
env_shebang: false,
document: %w[ri],
domain: :both, # HACK: dup
force: false,
format_executable: false, # HACK: dup
ignore_dependencies: false,
prerelease: false,
security_policy: nil, # HACK: NoSecurity requires OpenSSL. AlmostNo? Low?
wrappers: true,
build_args: nil,
build_docs_in_background: false,
install_as_default: false,
}.freeze
##
# Documentation types. For use by the Gem.done_installing hook
attr_reader :document
##
# Errors from SpecFetcher while searching for remote specifications
attr_reader :errors
##
# List of gems installed by #install in alphabetic order
attr_reader :installed_gems
##
# Creates a new installer instance.
#
# Options are:
# :cache_dir:: Alternate repository path to store .gem files in.
# :domain:: :local, :remote, or :both. :local only searches gems in the
# current directory. :remote searches only gems in Gem::sources.
# :both searches both.
# :env_shebang:: See Gem::Installer::new.
# :force:: See Gem::Installer#install.
# :format_executable:: See Gem::Installer#initialize.
# :ignore_dependencies:: Don't install any dependencies.
# :install_dir:: See Gem::Installer#install.
# :prerelease:: Allow prerelease versions. See #install.
# :security_policy:: See Gem::Installer::new and Gem::Security.
# :user_install:: See Gem::Installer.new
# :wrappers:: See Gem::Installer::new
# :build_args:: See Gem::Installer::new
def initialize(options = {})
@only_install_dir = !options[:install_dir].nil?
@install_dir = options[:install_dir] || Gem.dir
@build_root = options[:build_root]
options = DEFAULT_OPTIONS.merge options
@bin_dir = options[:bin_dir]
@dev_shallow = options[:dev_shallow]
@development = options[:development]
@document = options[:document]
@domain = options[:domain]
@env_shebang = options[:env_shebang]
@force = options[:force]
@format_executable = options[:format_executable]
@ignore_dependencies = options[:ignore_dependencies]
@prerelease = options[:prerelease]
@security_policy = options[:security_policy]
@user_install = options[:user_install]
@wrappers = options[:wrappers]
@build_args = options[:build_args]
@build_docs_in_background = options[:build_docs_in_background]
@install_as_default = options[:install_as_default]
@dir_mode = options[:dir_mode]
@data_mode = options[:data_mode]
@prog_mode = options[:prog_mode]
# Indicates that we should not try to update any deps unless
# we absolutely must.
@minimal_deps = options[:minimal_deps]
@available = nil
@installed_gems = []
@toplevel_specs = nil
@cache_dir = options[:cache_dir] || @install_dir
@errors = []
end
##
# Indicated, based on the requested domain, if local
# gems should be considered.
def consider_local?
@domain == :both || @domain == :local
end
##
# Indicated, based on the requested domain, if remote
# gems should be considered.
def consider_remote?
@domain == :both || @domain == :remote
end
##
# Returns a list of pairs of gemspecs and source_uris that match
# Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources)
# sources. Gems are sorted with newer gems preferred over older gems, and
# local gems preferred over remote gems.
def find_gems_with_sources(dep, best_only=false) # :nodoc:
set = Gem::AvailableSet.new
if consider_local?
sl = Gem::Source::Local.new
if spec = sl.find_gem(dep.name)
if dep.matches_spec? spec
set.add spec, sl
end
end
end
if consider_remote?
begin
# This is pulled from #spec_for_dependency to allow
# us to filter tuples before fetching specs.
tuples, errors = Gem::SpecFetcher.fetcher.search_for_dependency dep
if best_only && !tuples.empty?
tuples.sort! do |a,b|
if b[0].version == a[0].version
if b[0].platform != Gem::Platform::RUBY
1
else
-1
end
else
b[0].version <=> a[0].version
end
end
tuples = [tuples.first]
end
specs = []
tuples.each do |tup, source|
spec = source.fetch_spec(tup)
rescue Gem::RemoteFetcher::FetchError => e
errors << Gem::SourceFetchProblem.new(source, e)
else
specs << [spec, source]
end
if @errors
@errors += errors
else
@errors = errors
end
set << specs
rescue Gem::RemoteFetcher::FetchError => e
# FIX if there is a problem talking to the network, we either need to always tell
# the user (no really_verbose) or fail hard, not silently tell them that we just
# couldn't find their requested gem.
verbose do
"Error fetching remote data:\t\t#{e.message}\n" \
"Falling back to local-only install"
end
@domain = :local
end
end
set
end
rubygems_deprecate :find_gems_with_sources
def in_background(what) # :nodoc:
fork_happened = false
if @build_docs_in_background && Process.respond_to?(:fork)
begin
Process.fork do
yield
end
fork_happened = true
say "#{what} in a background process."
rescue NotImplementedError
end
end
yield unless fork_happened
end
##
# Installs the gem +dep_or_name+ and all its dependencies. Returns an Array
# of installed gem specifications.
#
# If the +:prerelease+ option is set and there is a prerelease for
# +dep_or_name+ the prerelease version will be installed.
#
# Unless explicitly specified as a prerelease dependency, prerelease gems
# that +dep_or_name+ depend on will not be installed.
#
# If c-1.a depends on b-1 and a-1.a and there is a gem b-1.a available then
# c-1.a, b-1 and a-1.a will be installed. b-1.a will need to be installed
# separately.
def install(dep_or_name, version = Gem::Requirement.default)
request_set = resolve_dependencies dep_or_name, version
@installed_gems = []
options = {
bin_dir: @bin_dir,
build_args: @build_args,
document: @document,
env_shebang: @env_shebang,
force: @force,
format_executable: @format_executable,
ignore_dependencies: @ignore_dependencies,
prerelease: @prerelease,
security_policy: @security_policy,
user_install: @user_install,
wrappers: @wrappers,
build_root: @build_root,
install_as_default: @install_as_default,
dir_mode: @dir_mode,
data_mode: @data_mode,
prog_mode: @prog_mode,
}
options[:install_dir] = @install_dir if @only_install_dir
request_set.install options do |_, installer|
@installed_gems << installer.spec if installer
end
@installed_gems.sort!
# Since this is currently only called for docs, we can be lazy and just say
# it's documentation. Ideally the hook adder could decide whether to be in
# the background or not, and what to call it.
in_background "Installing documentation" do
Gem.done_installing_hooks.each do |hook|
hook.call self, @installed_gems
end
end unless Gem.done_installing_hooks.empty?
@installed_gems
end
def install_development_deps # :nodoc:
if @development && @dev_shallow
:shallow
elsif @development
:all
else
:none
end
end
def resolve_dependencies(dep_or_name, version) # :nodoc:
request_set = Gem::RequestSet.new
request_set.development = @development
request_set.development_shallow = @dev_shallow
request_set.soft_missing = @force
request_set.prerelease = @prerelease
installer_set = Gem::Resolver::InstallerSet.new @domain
installer_set.ignore_installed = (@minimal_deps == false) || @only_install_dir
installer_set.force = @force
if consider_local?
if dep_or_name =~ /\.gem$/ && File.file?(dep_or_name)
src = Gem::Source::SpecificFile.new dep_or_name
installer_set.add_local dep_or_name, src.spec, src
version = src.spec.version if version == Gem::Requirement.default
elsif dep_or_name =~ /\.gem$/ # rubocop:disable Performance/RegexpMatch
Dir[dep_or_name].each do |name|
src = Gem::Source::SpecificFile.new name
installer_set.add_local dep_or_name, src.spec, src
rescue Gem::Package::FormatError
end
# else This is a dependency. InstallerSet handles this case
end
end
dependency =
if spec = installer_set.local?(dep_or_name)
installer_set.remote = nil if spec.dependencies.none?
Gem::Dependency.new spec.name, version
elsif String === dep_or_name
Gem::Dependency.new dep_or_name, version
else
dep_or_name
end
dependency.prerelease = @prerelease
request_set.import [dependency]
installer_set.add_always_install dependency
request_set.always_install = installer_set.always_install
request_set.remote = installer_set.consider_remote?
if @ignore_dependencies
installer_set.ignore_dependencies = true
request_set.ignore_dependencies = true
request_set.soft_missing = true
end
request_set.resolve installer_set
@errors.concat request_set.errors
request_set
end
end
PK ! t