模块 Gem

RubyGems 是发布和管理第三方库的 Ruby 标准。

有关用户文档,请参见

有关 gem 开发人员文档,请参见

更多 RubyGems 文档可以在以下位置找到

RubyGems 插件

RubyGems 将加载每个已安装 gem 或 $LOAD_PATH 的最新版本中的插件。插件必须命名为“rubygems_plugin”(.rb,.so 等),并放置在 gem 的 require_path 的根目录中。插件安装在特殊位置,并在启动时加载。

有关示例插件,请参见Graph gem,它添加了 gem graph 命令。

RubyGems 默认值,打包

RubyGems 默认值存储在 lib/rubygems/defaults.rb 中。如果您正在打包 RubyGems 或实现 Ruby,则可以更改 RubyGems 的默认值。

对于 RubyGems 打包者,请提供 lib/rubygems/defaults/operating_system.rb 并覆盖 lib/rubygems/defaults.rb 中的任何默认值。

对于 Ruby 实现者,请提供 lib/rubygems/defaults/#{RUBY_ENGINE}.rb 并覆盖 lib/rubygems/defaults.rb 中的任何默认值。

如果需要 RubyGems 在安装或卸载时执行额外的工作,则默认值覆盖文件可以设置安装前/后和卸载挂钩。请参见Gem::pre_installGem::pre_uninstallGem::post_installGem::post_uninstall

错误

您可以将错误提交到 GitHub 上的RubyGems 错误跟踪器

贡献者

RubyGems 目前由 Eric Hodel 维护。

RubyGems 最初由以下人员在 RubyConf 2003 上开发:

  • 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

贡献者

  • 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

(如果您的名字遗漏了,请务必告知我们!)

许可证

有关权限,请参见LICENSE.txt

谢谢!

-RubyGems 团队

提供了 3 种方法来声明某项内容何时将被弃用。

+deprecate(name, repl, year, month)+

Indicate something may be removed on/after a certain date.

+rubygems_deprecate(name, replacement=:none)+

Indicate something will be removed in the next major RubyGems version,
and (optionally) a replacement for it.

rubygems_deprecate_command:

Indicate a RubyGems command (in +lib/rubygems/commands/*.rb+) will be
removed in the next RubyGems version.

还提供了 skip_during 来临时关闭弃用警告。此方法旨在在测试套件中使用,这样,如果您需要确保 stderr 为空,则弃用警告不会导致测试失败。

deprecaterubygems_deprecate 的用法示例

class Legacy
  def self.some_class_method
    # ...
  end

  def some_instance_method
    # ...
  end

  def some_old_method
    # ...
  end

  extend Gem::Deprecate
  deprecate :some_instance_method, "X.z", 2011, 4
  rubygems_deprecate :some_old_method, "Modern#some_new_method"

  class << self
    extend Gem::Deprecate
    deprecate :some_class_method, :none, 2011, 4
  end
end

rubygems_deprecate_command 的用法示例

class Gem::Commands::QueryCommand < Gem::Command
  extend Gem::Deprecate
  rubygems_deprecate_command

  # ...
end

skip_during 的用法示例

class TestSomething < Gem::Testcase
  def test_some_thing_with_deprecations
    Gem::Deprecate.skip_during do
      actual_stdout, actual_stderr = capture_output do
        Gem.something_deprecated
      end
      assert_empty actual_stdout
      assert_equal(expected, actual_stderr)
    end
  end
end

常量

DEFAULT_HOST
GEM_DEP_FILES
LOADED_SPECS_MUTEX
MARSHAL_SPEC_DIR

远程存储库上 Marshal 快速 gemspec 的位置

RDoc
REPOSITORY_DEFAULT_GEM_SUBDIRECTORIES

默认 gem 的 gem 存储库中的子目录

REPOSITORY_SUBDIRECTORIES

gem 存储库中的子目录

RUBYGEMS_DIR
VERSION
WIN_PATTERNS

与 Windows Ruby 平台匹配的正则表达式数组。

属性

disable_system_update_message[RW]

RubyGems 分发者(如操作系统软件包管理器)可以通过将此属性设置为错误消息(而不是实际更新),在 gem update --system 上打印给最终用户,从而禁用 RubyGems 更新。

discover_gems_on_require[RW]

RubyGems 是否应增强内置的“require”,以自动检查所要求的路径是否存在于已安装的 gem 中,并自动激活它们并将其添加到 `$LOAD_PATH`。

done_installing_hooks[R]

Gem::DependencyInstaller安装一组 gem 后要运行的挂钩列表

gemdeps[R]

GemDependencyAPI 对象,在调用 .use_gemdeps 时设置。其中包含来自 Gemfile 的所有信息。

loaded_specs[R]

按名称键入的加载的Gem::Specification的哈希

post_build_hooks[R]

Gem::Installer#install提取文件并构建扩展后要运行的挂钩列表

post_install_hooks[R]

Gem::Installer#install完成安装后要运行的挂钩列表

post_reset_hooks[R]

在运行Gem::Specification.reset后要运行的挂钩列表。

post_uninstall_hooks[R]

Gem::Uninstaller#uninstall完成安装后要运行的挂钩列表

pre_install_hooks[R]

Gem::Installer#install执行任何工作之前要运行的挂钩列表

pre_reset_hooks[R]

在运行Gem::Specification.reset之前要运行的挂钩列表。

pre_uninstall_hooks[R]

Gem::Uninstaller#uninstall执行任何工作之前要运行的挂钩列表

公共类方法

URI(uri) 点击以切换源

返回从给定 uri 派生的 Gem::URI 对象,该对象可以是 Gem::URI 字符串或现有的 Gem::URI 对象

# Returns a new Gem::URI.
uri = Gem::URI('http://github.com/ruby/ruby')
# => #<Gem::URI::HTTP http://github.com/ruby/ruby>
# Returns the given Gem::URI.
Gem::URI(uri)
# => #<Gem::URI::HTTP http://github.com/ruby/ruby>
# File rubygems/vendor/uri/lib/uri/common.rb, line 865
def URI(uri)
  if uri.is_a?(Gem::URI::Generic)
    uri
  elsif uri = String.try_convert(uri)
    Gem::URI.parse(uri)
  else
    raise ArgumentError,
      "bad argument (expected Gem::URI object or Gem::URI string)"
  end
end
activated_gem_paths() 点击以切换源

来自已激活 gem 的 +$LOAD_PATH+ 中的路径数。用于在 require 期间优先处理 -IENV['RUBYLIB'] 条目。

# File rubygems.rb, line 592
def self.activated_gem_paths
  @activated_gem_paths ||= 0
end
add_to_load_path(*paths) 点击以切换源

将路径列表添加到 $LOAD_PATH 中的适当位置。

# File rubygems.rb, line 599
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
bin_path(name, exec_name = nil, *requirements) 点击以切换源

查找 gem name 的可执行文件的完整路径。如果未提供 exec_name,则会引发异常,否则将返回指定可执行文件的路径。requirements 允许您指定特定的 gem 版本。

# File rubygems.rb, line 238
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
binary_mode() 点击切换源代码

以纯二进制方式读取文件所需的模式。

# File rubygems.rb, line 292
def self.binary_mode
  "rb"
end
bindir(install_dir=Gem.dir) 点击切换源代码

gem 可执行文件要安装到的路径。

# File rubygems.rb, line 299
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
cache_home() 点击切换源代码

用户缓存目录的标准位置的路径。

# File rubygems/defaults.rb, line 147
def self.cache_home
  @cache_home ||= ENV["XDG_CACHE_HOME"] || File.join(Gem.user_home, ".cache")
end
clear_default_specs() 点击切换源代码

清除默认的 gem 相关变量。用于测试。

# File rubygems.rb, line 1270
def clear_default_specs
  @path_to_default_spec_map.clear
end
clear_paths() 点击切换源代码

重置 dirpath 的值。下次请求 dirpath 时,将从头开始计算这些值。这主要用于单元测试以提供测试隔离。

# File rubygems.rb, line 317
def self.clear_paths
  @paths         = nil
  @user_home     = nil
  Gem::Specification.reset
  Gem::Security.reset if defined?(Gem::Security)
end
config_file() 点击切换源代码

用户的 .gemrc 文件的标准位置的路径。

# File rubygems/defaults.rb, line 133
def self.config_file
  @config_file ||= find_config_file
end
config_home() 点击切换源代码

用户的配置目录的标准位置的路径。

# File rubygems/defaults.rb, line 114
def self.config_home
  @config_home ||= ENV["XDG_CONFIG_HOME"] || File.join(Gem.user_home, ".config")
end
configuration() 点击切换源代码

gems 的标准配置对象。

# File rubygems.rb, line 327
def self.configuration
  @configuration ||= Gem::ConfigFile.new []
end
configuration=(config) 点击切换源代码

使用给定的配置对象(实现 ConfigFile 协议)作为标准配置对象。

# File rubygems.rb, line 335
def self.configuration=(config)
  @configuration = config
end
data_home() 点击切换源代码

用户数据目录的标准位置的路径。

# File rubygems/defaults.rb, line 154
def self.data_home
  @data_home ||= ENV["XDG_DATA_HOME"] || File.join(Gem.user_home, ".local", "share")
end
default_bindir() 点击切换源代码

二进制文件的默认目录。

# File rubygems/defaults.rb, line 204
def self.default_bindir
  RbConfig::CONFIG["bindir"]
end
default_cert_path() 点击切换源代码

默认签名证书链路径。

# File rubygems/defaults.rb, line 228
def self.default_cert_path
  default_cert_path = File.join Gem.user_home, ".gem", "gem-public_cert.pem"

  unless File.exist?(default_cert_path)
    default_cert_path = File.join Gem.data_home, "gem", "gem-public_cert.pem"
  end

  default_cert_path
end
default_dir() 点击切换源代码

如果环境中未指定备用值,则要使用的默认主目录路径。

# File rubygems/defaults.rb, line 37
def self.default_dir
  @default_dir ||= File.join(RbConfig::CONFIG["rubylibprefix"], "gems", RbConfig::CONFIG["ruby_version"])
end
default_exec_format() 点击切换源代码

从 Ruby 的安装名称推断其 --program-prefix 和 --program-suffix。

# File rubygems/defaults.rb, line 186
def self.default_exec_format
  exec_format = begin
                  RbConfig::CONFIG["ruby_install_name"].sub("ruby", "%s")
                rescue StandardError
                  "%s"
                end

  unless exec_format.include?("%s")
    raise Gem::Exception,
      "[BUG] invalid exec_format #{exec_format.inspect}, no %s"
  end

  exec_format
end
default_ext_dir_for(base_dir) 点击切换源代码

返回指定 RubyGems 基本目录的二进制扩展目录,如果无法确定此类目录,则返回 nil。

默认情况下,二进制扩展与它们的 Ruby 对应项并排存在,因此返回 nil。

# File rubygems/defaults.rb, line 48
def self.default_ext_dir_for(base_dir)
  nil
end
default_key_path() 点击切换源代码

默认签名密钥路径。

# File rubygems/defaults.rb, line 215
def self.default_key_path
  default_key_path = File.join Gem.user_home, ".gem", "gem-private_key.pem"

  unless File.exist?(default_key_path)
    default_key_path = File.join Gem.data_home, "gem", "gem-private_key.pem"
  end

  default_key_path
end
default_path() 点击切换源代码

默认 gem 加载路径。

# File rubygems/defaults.rb, line 175
def self.default_path
  path = []
  path << user_dir if user_home && File.exist?(user_home)
  path << default_dir
  path << vendor_dir if vendor_dir && File.directory?(vendor_dir)
  path
end
default_rubygems_dirs() 点击切换源代码

RubyGems 的 .rb 文件和 bin 文件安装的路径。

# File rubygems/defaults.rb, line 55
def self.default_rubygems_dirs
  nil # default to standard layout
end
default_sources() 点击切换源代码

RubyGems 附带的默认源的数组。

# File rubygems/defaults.rb, line 15
def self.default_sources
  %w[https://rubygems.org.cn/]
end
default_spec_cache_dir() 点击切换源代码

如果环境中未指定备用值,则要使用的默认 spec 目录路径。

# File rubygems/defaults.rb, line 23
def self.default_spec_cache_dir
  default_spec_cache_dir = File.join Gem.user_home, ".gem", "specs"

  unless File.exist?(default_spec_cache_dir)
    default_spec_cache_dir = File.join Gem.cache_home, "gem", "specs"
  end

  default_spec_cache_dir
end
default_specifications_dir() 点击切换源代码

默认 gem 的 specification 文件路径。

# File rubygems/defaults.rb, line 62
def self.default_specifications_dir
  @default_specifications_dir ||= File.join(Gem.default_dir, "specifications", "default")
end
deflate(data) 点击切换源代码

一个 Zlib::Deflate.deflate 包装器。

# File rubygems.rb, line 342
def self.deflate(data)
  require "zlib"
  Zlib::Deflate.deflate data
end
dir() 点击切换源代码

gems 要安装到的路径。

# File rubygems.rb, line 390
def self.dir
  paths.home
end
done_installing(&hook) 点击切换源代码

添加一个安装后钩子,当 Gem::DependencyInstaller#install 完成时,该钩子将传递一个 Gem::DependencyInstaller 和已安装的 specifications 列表。

# File rubygems.rb, line 703
def self.done_installing(&hook)
  @done_installing_hooks << hook
end
dynamic_library_suffixes() 点击切换源代码

动态库可 require 路径的后缀。

# File rubygems.rb, line 977
def self.dynamic_library_suffixes
  @dynamic_library_suffixes ||= suffixes - [".rb"]
end
ensure_default_gem_subdirectories(dir = Gem.dir, mode = nil) 点击切换源代码

静默地确保 Gem 目录 dir 包含处理默认 gems 的所有适当子目录。如果由于权限问题而无法创建目录,我们将静默地继续。

如果给定了 mode,则使用此模式创建缺少的目录。

永远不会创建全局可写的目录。

# File rubygems.rb, line 441
def self.ensure_default_gem_subdirectories(dir = Gem.dir, mode = nil)
  ensure_subdirectories(dir, mode, REPOSITORY_DEFAULT_GEM_SUBDIRECTORIES)
end
ensure_gem_subdirectories(dir = Gem.dir, mode = nil) 点击切换源代码

静默地确保 Gem 目录 dir 包含所有适当的子目录。如果由于权限问题而无法创建目录,我们将静默地继续。

如果给定了 mode,则使用此模式创建缺少的目录。

永远不会创建全局可写的目录。

# File rubygems.rb, line 428
def self.ensure_gem_subdirectories(dir = Gem.dir, mode = nil)
  ensure_subdirectories(dir, mode, REPOSITORY_SUBDIRECTORIES)
end
env_requirement(gem_name) 点击切换源代码
# File rubygems.rb, line 845
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
find_config_file() 点击切换源代码

查找用户的配置文件。

# File rubygems/defaults.rb, line 121
def self.find_config_file
  gemrc = File.join Gem.user_home, ".gemrc"
  if File.exist? gemrc
    gemrc
  else
    File.join Gem.config_home, "gem", "gemrc"
  end
end
find_default_spec(path) 点击切换源代码

path 查找默认 gem 的 Gem::Specification

# File rubygems.rb, line 1255
def find_default_spec(path)
  @path_to_default_spec_map[path]
end
find_files(glob, check_load_path=true) 点击切换源代码

返回与 glob 匹配的路径列表,gem 可以使用这些路径从其他 gems 中获取功能。例如

Gem.find_files('rdoc/discover').each do |path| load path end

如果 check_load_path 为 true(默认值),则 find_files 也会在 $LOAD_PATH 中搜索文件以及 gems。

请注意,即使文件来自同一 gem 的不同版本,find_files 也会返回所有文件。另请参见 find_latest_files

# File rubygems.rb, line 492
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.flat_map {|spec|
    spec.matches_for_glob("#{glob}#{Gem.suffix_pattern}")
  }

  # $LOAD_PATH might contain duplicate entries or reference
  # the spec dirs directly, so we prune.
  files.uniq! if check_load_path

  files
end
find_latest_files(glob, check_load_path=true) 点击切换源代码

返回与 glob 匹配的路径列表,这些路径来自最新的 gem,gem 可以使用它们从其他 gem 中获取功能。例如

Gem.find_latest_files('rdoc/discover').each do |path| load path end

如果 check_load_path 为 true(默认值),则 find_latest_files 也会在 $LOAD_PATH 中搜索文件以及 gems。

find_files 不同,find_latest_files 将仅返回来自 gem 的最新版本的文件。

# File rubygems.rb, line 529
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).flat_map {|spec|
    spec.matches_for_glob("#{glob}#{Gem.suffix_pattern}")
  }

  # $LOAD_PATH might contain duplicate entries or reference
  # the spec dirs directly, so we prune.
  files.uniq! if check_load_path

  files
end
find_unresolved_default_spec(path) 点击切换源代码

path 查找默认 gem 的未解析 Gem::Specification

# File rubygems.rb, line 1262
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
finish_resolve(request_set=Gem::RequestSet.new) 点击切换源代码
# File rubygems.rb, line 223
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
freebsd_platform?() 点击切换源代码

此平台是否为 FreeBSD。

# File rubygems.rb, line 1047
def self.freebsd_platform?
  RbConfig::CONFIG["host_os"].to_s.include?("bsd")
end
host() 点击切换源代码

获取默认的 RubyGems API 主机。这通常是 https://rubygems.org.cn

# File rubygems.rb, line 564
def self.host
  @host ||= Gem::DEFAULT_HOST
end
host=(host) 点击切换源代码

设置默认的 RubyGems API 主机。

# File rubygems.rb, line 570
def self.host=(host)
  @host = host
end
install(name, version = Gem::Requirement.default, *options) 点击切换源代码

顶级安装帮助方法。允许您以交互方式安装 gems。

% irb
>> Gem.install "minitest"
Fetching: minitest-5.14.0.gem (100%)
=> [#<Gem::Specification:0x1013b4528 @name="minitest", ...>]
# File rubygems.rb, line 553
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
java_platform?() 点击切换源代码

这是一个 java 平台吗?

# File rubygems.rb, line 1033
def self.java_platform?
  RUBY_PLATFORM == "java"
end
latest_rubygems_version() 点击切换源代码

返回 RubyGems 的最新发行版本。

# File rubygems.rb, line 870
def self.latest_rubygems_version
  latest_version_for("rubygems-update") ||
    raise("Can't find 'rubygems-update' in any repo. Check `gem source list`.")
end
latest_spec_for(name) 点击切换源代码

返回 gem name 的最新发行版本 specification。

# File rubygems.rb, line 857
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
latest_version_for(name) 点击切换源代码

返回 gem name 的最新发行版本的版本。

# File rubygems.rb, line 878
def self.latest_version_for(name)
  latest_spec_for(name)&.version
end
load_env_plugins() 点击切换源代码

查找 $LOAD_PATH 中的所有“rubygems_plugin”文件并加载它们。

# File rubygems.rb, line 1082
def self.load_env_plugins
  load_plugin_files find_files_from_load_path("rubygems_plugin")
end
load_path_insert_index() 点击切换源代码

将激活的 gem 路径插入 $LOAD_PATH 的索引。默认情况下,激活的 gem 的路径插入在 site lib 目录之前。

# File rubygems.rb, line 578
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
load_plugins() 点击切换源代码

在标准位置查找 rubygems 插件文件并加载它们。

# File rubygems.rb, line 1073
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
load_safe_marshal() 点击切换源代码
# File rubygems.rb, line 624
def self.load_safe_marshal
  return if @safe_marshal_loaded

  require_relative "rubygems/safe_marshal"

  @safe_marshal_loaded = true
end
load_yaml() 点击切换源代码

加载 YAML,首选 Psych。

# File rubygems.rb, line 611
def self.load_yaml
  return if @yaml_loaded

  require "psych"
  require_relative "rubygems/psych_tree"

  require_relative "rubygems/safe_yaml"

  @yaml_loaded = true
end
location_of_caller(depth = 1) 点击切换源代码

此方法的调用者的调用者的文件名和行号。

depth 是应该向上调用堆栈的层数。

例如,

def a; Gem.location_of_caller; end a #=> [“x.rb”, 2] #(它会因文件名和行号而异)

def b; c; end def c; Gem.location_of_caller(2); end b #=> [“x.rb”, 6] #(它会因文件名和行号而异)

# File rubygems.rb, line 646
def self.location_of_caller(depth = 1)
  caller[depth] =~ /(.*?):(\d+).*?$/i
  file = $1
  lineno = $2.to_i

  [file, lineno]
end
marshal_version() 点击切换源代码

Ruby 的 Marshal 格式的版本。

# File rubygems.rb, line 657
def self.marshal_version
  "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
end
needs() { |rs| ... } 点击切换源代码
# File rubygems.rb, line 215
def self.needs
  rs = Gem::RequestSet.new

  yield rs

  finish_resolve rs
end
open_file(path, flags, &block) 点击切换源代码

使用给定标志打开文件。

# File rubygems.rb, line 793
def self.open_file(path, flags, &block)
  File.open(path, flags, &block)
end
open_file_with_flock(path) { |io| ... } 点击切换源代码

使用给定标志打开文件,并使用 flock 保护访问。

# File rubygems.rb, line 811
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
open_file_with_lock(path, &block) 点击切换源代码

使用给定标志打开文件,并使用文件锁保护访问。

# File rubygems.rb, line 800
def self.open_file_with_lock(path, &block)
  file_lock = "#{path}.lock"
  open_file_with_flock(file_lock, &block)
ensure
  require "fileutils"
  FileUtils.rm_f file_lock
end
operating_system_defaults() 点击以切换源代码

为 Ruby 打包器提供的 gem 命令的默认选项。

此处的选项应结构化为一个字符串数组,其中键为 “gem” 命令名称,值为默认选项字符串。

示例

def self.operating_system_defaults

{
    'install' => '--no-rdoc --no-ri --env-shebang',
    'update' => '--no-rdoc --no-ri --env-shebang'
}

end

# File rubygems/defaults.rb, line 286
def self.operating_system_defaults
  {}
end
path() 点击以切换源代码
# File rubygems.rb, line 394
def self.path
  paths.path
end
path_separator() 点击以切换源代码

如何分割字符串 Gem 路径。可为特殊的平台重写。

# File rubygems/defaults.rb, line 168
def self.path_separator
  File::PATH_SEPARATOR
end
paths() 点击以切换源代码

检索 RubyGems 用于查找文件的 PathSupport 对象。

# File rubygems.rb, line 350
def self.paths
  @paths ||= Gem::PathSupport.new(ENV)
end
paths=(env) 点击以切换源代码

env 初始化要使用的文件系统路径。env 是一个类似哈希的对象(通常是 ENV),它查询 ‘GEM_HOME’、‘GEM_PATH’ 和 ‘GEM_SPEC_CACHE’ 键。 env 哈希的键应为字符串,哈希的值应为字符串或 nil

# File rubygems.rb, line 360
  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
platform_defaults() 点击以切换源代码

为 Ruby 实现者提供的 gem 命令的默认选项。

此处的选项应结构化为一个字符串数组,其中键为 “gem” 命令名称,值为默认选项字符串。

示例

def self.platform_defaults

{
    'install' => '--no-rdoc --no-ri --env-shebang',
    'update' => '--no-rdoc --no-ri --env-shebang'
}

end

# File rubygems/defaults.rb, line 305
def self.platform_defaults
  {}
end
platforms() 点击以切换源代码

此 RubyGems 支持的平台数组。

# File rubygems.rb, line 671
def self.platforms
  @platforms ||= []
  if @platforms.empty?
    @platforms = [Gem::Platform::RUBY, Gem::Platform.local]
  end
  @platforms
end
platforms=(platforms) 点击以切换源代码

设置此 RubyGems 支持的平台数组(主要用于测试)。

# File rubygems.rb, line 664
def self.platforms=(platforms)
  @platforms = platforms
end
plugin_suffix_pattern() 点击以切换源代码

可 require 的插件后缀的 Glob 模式。

# File rubygems.rb, line 950
def self.plugin_suffix_pattern
  @plugin_suffix_pattern ||= "_plugin#{suffix_pattern}"
end
plugin_suffix_regexp() 点击以切换源代码

可 require 的插件后缀的正则表达式。

# File rubygems.rb, line 957
def self.plugin_suffix_regexp
  @plugin_suffix_regexp ||= /_plugin#{suffix_regexp}\z/
end
plugindir(install_dir=Gem.dir) 点击以切换源代码

rubygems 插件要安装的路径。

# File rubygems.rb, line 308
def self.plugindir(install_dir=Gem.dir)
  File.join install_dir, "plugins"
end
post_build(&hook) 点击以切换源代码

添加一个 post-build 钩子,当 Gem::Installer#install 被调用时,该钩子将被传递一个 Gem::Installer 实例。钩子在 gem 解压和扩展构建之后,但在可执行文件或 gemspec 写入之前被调用。如果钩子返回 false,则 gem 的文件将被删除,并且安装将中止。

# File rubygems.rb, line 686
def self.post_build(&hook)
  @post_build_hooks << hook
end
post_install(&hook) 点击以切换源代码

添加一个 post-install 钩子,当 Gem::Installer#install 被调用时,该钩子将被传递一个 Gem::Installer 实例

# File rubygems.rb, line 694
def self.post_install(&hook)
  @post_install_hooks << hook
end
post_reset(&hook) 点击以切换源代码

添加一个在 Gem::Specification.reset 运行后运行的钩子。

# File rubygems.rb, line 711
def self.post_reset(&hook)
  @post_reset_hooks << hook
end
post_uninstall(&hook) 点击以切换源代码

添加一个 post-uninstall 钩子,当 Gem::Uninstaller#uninstall 被调用时,该钩子将被传递一个 Gem::Uninstaller 实例和卸载的 spec。

# File rubygems.rb, line 720
def self.post_uninstall(&hook)
  @post_uninstall_hooks << hook
end
pre_install(&hook) 点击以切换源代码

添加一个 pre-install 钩子,当 Gem::Installer#install 被调用时,该钩子将被传递一个 Gem::Installer 实例。如果钩子返回 false,则安装将中止。

# File rubygems.rb, line 729
def self.pre_install(&hook)
  @pre_install_hooks << hook
end
pre_reset(&hook) 点击以切换源代码

添加一个在 Gem::Specification.reset 运行之前运行的钩子。

# File rubygems.rb, line 737
def self.pre_reset(&hook)
  @pre_reset_hooks << hook
end
pre_uninstall(&hook) 点击以切换源代码

添加一个 pre-uninstall 钩子,当 Gem::Uninstaller#uninstall 被调用时,该钩子将被传递一个 Gem::Uninstaller 实例和将要卸载的 spec。

# File rubygems.rb, line 746
def self.pre_uninstall(&hook)
  @pre_uninstall_hooks << hook
end
prefix() 点击以切换源代码

此 RubyGems 安装的目录前缀。如果您的前缀位于标准位置(即,rubygems 安装在您期望的位置),则 prefix 返回 nil。

# File rubygems.rb, line 755
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
rdoc_hooks_defined_via_plugin?() 点击以切换源代码

返回 RDoc 是否通过 RubyGems 插件定义了自己的安装钩子。一旦没有支持的 Ruby 版本附带旧于 6.9.0 的 RDoc,就可以删除此项以及受其保护的任何内容。

# File rubygems/rdoc.rb, line 15
def self.rdoc_hooks_defined_via_plugin?
  Gem::Version.new(::RDoc::VERSION) >= Gem::Version.new("6.9.0")
end
read_binary(path) 点击以切换源代码

在所有平台上以二进制模式安全地读取文件。

# File rubygems.rb, line 775
def self.read_binary(path)
  File.binread(path)
end
refresh() 点击以切换源代码

从磁盘刷新可用的 gem。

# File rubygems.rb, line 768
def self.refresh
  Gem::Specification.reset
end
register_default_spec(spec) 点击以切换源代码

为默认 gem 注册一个 Gem::Specification

支持两种规格格式

  • MRI 2.0 样式,其中 spec.files 包含未加前缀的 require 名称。spec 的文件名将按原样注册。

  • 新样式,其中 spec.files 包含以 spec.require_paths 中的路径为前缀的文件。在注册 spec 的文件名之前,会去除前缀。未加前缀的文件将被忽略。

# File rubygems.rb, line 1230
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
ruby() 点击以切换源代码

正在运行的 Ruby 解释器的路径。

# File rubygems.rb, line 828
def self.ruby
  if @ruby.nil?
    @ruby = RbConfig.ruby

    @ruby = "\"#{@ruby}\"" if /\s/.match?(@ruby)
  end

  @ruby
end
ruby_api_version() 点击以切换源代码

返回一个包含 Ruby 的 API 兼容性版本的字符串

# File rubygems.rb, line 841
def self.ruby_api_version
  @ruby_api_version ||= target_rbconfig["ruby_version"].dup
end
ruby_engine() 点击以切换源代码
# File rubygems/defaults.rb, line 208
def self.ruby_engine
  RUBY_ENGINE
end
ruby_version() 点击以切换源代码

当前正在运行的 Ruby 的 Gem::Version

# File rubygems.rb, line 885
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
rubygems_version() 点击以切换源代码

当前正在运行的 RubyGems 的 Gem::Version

# File rubygems.rb, line 904
def self.rubygems_version
  return @rubygems_version if defined? @rubygems_version
  @rubygems_version = Gem::Version.new Gem::VERSION
end
set_target_rbconfig(rbconfig_path) 点击以切换源代码
# File rubygems.rb, line 412
def self.set_target_rbconfig(rbconfig_path)
  @target_rbconfig = Gem::TargetRbConfig.from_path(rbconfig_path)
  Gem::Platform.local(refresh: true)
  Gem.platforms << Gem::Platform.local unless Gem.platforms.include? Gem::Platform.local
  @target_rbconfig
end
solaris_platform?() 点击以切换源代码

此平台是 Solaris 吗?

# File rubygems.rb, line 1040
def self.solaris_platform?
  RUBY_PLATFORM.include?("solaris")
end
source_date_epoch() 点击以切换源代码

返回 Gem.source_date_epoch_string 的值,作为 Time 对象。

这在整个 RubyGems 中用于实现可重现的构建。

# File rubygems.rb, line 1185
def self.source_date_epoch
  Time.at(source_date_epoch_string.to_i).utc.freeze
end
source_date_epoch_string() 点击以切换源代码

如果设置了 SOURCE_DATE_EPOCH 环境变量,则返回其值。否则,返回首次调用 Gem.source_date_epoch_string 的时间,格式与 SOURCE_DATE_EPOCH 相同。

注意(@duckinator):此实现有点奇怪,因为我们希望

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.

如果您简化此函数,并且很多测试失败,则可能是由于上面的 #2 引起的。

有关 SOURCE_DATE_EPOCH 的详细信息:reproducible-builds.org/specs/source-date-epoch/

# File rubygems.rb, line 1166
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
sources() 点击以切换源代码

返回用于从远程获取 gem 的源的数组。如果源列表为空,则使用 default_sources

# File rubygems.rb, line 913
def self.sources
  source_list = configuration.sources || default_sources
  @sources ||= Gem::SourceList.from(source_list)
end
sources=(new_sources) 点击以切换源代码

需要能够在不调用 Gem.sources.replace 的情况下设置源,因为这将导致无限循环。

DOC:此注释不是关于方法本身的文档,它更像是关于实现的注释。

# File rubygems.rb, line 925
def self.sources=(new_sources)
  if !new_sources
    @sources = nil
  else
    @sources = Gem::SourceList.from(new_sources)
  end
end
spec_cache_dir() 点击以切换源代码
# File rubygems.rb, line 398
def self.spec_cache_dir
  paths.spec_cache_dir
end
state_file() 点击以切换源代码

用户状态文件的标准位置的路径。

# File rubygems/defaults.rb, line 140
def self.state_file
  @state_file ||= File.join(Gem.state_home, "gem", "last_update_check")
end
state_home() 点击以切换源代码

用户状态目录的标准位置的路径。

# File rubygems/defaults.rb, line 161
def self.state_home
  @state_home ||= ENV["XDG_STATE_HOME"] || File.join(Gem.user_home, ".local", "state")
end
suffix_pattern() 点击以切换源代码

可 require 的路径后缀的 Glob 模式。

# File rubygems.rb, line 936
def self.suffix_pattern
  @suffix_pattern ||= "{#{suffixes.join(",")}}"
end
suffix_regexp() 点击以切换源代码

可 require 的路径后缀的正则表达式。

# File rubygems.rb, line 943
def self.suffix_regexp
  @suffix_regexp ||= /#{Regexp.union(suffixes)}\z/
end
suffixes() 点击以切换源代码

可 require 的路径的后缀。

# File rubygems.rb, line 964
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
target_rbconfig() 点击以切换源代码

部署目标平台的 RbConfig 对象。

这通常与运行平台相同,但如果您正在进行交叉编译,则可能有所不同。

# File rubygems.rb, line 408
def self.target_rbconfig
  @target_rbconfig || Gem::TargetRbConfig.for_running_ruby
end
time(msg, width = 0, display = Gem.configuration.verbose) { || ... } 点击以切换源代码

使用调试 UI 输出打印提供的代码块运行所需的时间量。

# File rubygems.rb, line 985
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
try_activate(path) 点击以切换源代码

尝试激活包含 path 的 gem。如果激活成功或不需要激活(因为它已激活),则返回 true。如果在 gem 中找不到该路径,则返回 false。

# File rubygems.rb, line 190
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
ui() 点击以切换源代码

惰性加载 DefaultUserInteraction 并返回默认 UI。

# File rubygems.rb, line 1000
def self.ui
  require_relative "rubygems/user_interaction"

  Gem::DefaultUserInteraction.ui
end
use_gemdeps(path = nil) 点击以切换源代码

path 中查找 gem 依赖项文件,如果找到,则激活文件中的 gem。如果找不到文件,则会引发 ArgumentError。

如果未提供 path,则使用 RUBYGEMS_GEMDEPS 环境变量,但如果未找到文件,则不会引发异常。

如果为 path 提供了 '-',则 RubyGems 从当前工作目录向上搜索 gem 依赖项文件(gem.deps.rb、Gemfile、Isolate)并激活找到的第一个文件中的 gem。

您可以在 rubygems 启动时自动运行此操作。要启用,请将 RUBYGEMS_GEMDEPS 环境变量设置为您的 gem 依赖项文件的路径,或设置为 “-” 以在父目录中自动发现。

注意:在多用户系统上启用自动发现可能会导致在不受您控制的目录中使用时执行任意代码。

# File rubygems.rb, line 1106
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
use_paths(home, *paths) 点击以切换源代码

使用 Gem.dirGem.pathhomepaths 值。主要由单元测试使用,以提供环境隔离。

# File rubygems.rb, line 1010
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
user_dir() 点击切换源代码

用户主目录下 gems 的路径

# File rubygems/defaults.rb, line 103
def self.user_dir
  gem_dir = File.join(Gem.user_home, ".gem")
  gem_dir = File.join(Gem.data_home, "gem") unless File.exist?(gem_dir)
  parts = [gem_dir, ruby_engine]
  parts << RbConfig::CONFIG["ruby_version"] unless RbConfig::CONFIG["ruby_version"].empty?
  File.join parts
end
user_home() 点击切换源代码

用户的主目录。

# File rubygems/defaults.rb, line 96
def self.user_home
  @user_home ||= find_home
end
win_platform?() 点击切换源代码

这是一个 Windows 平台吗?

# File rubygems.rb, line 1021
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
write_binary(path, data) 点击切换源代码

在所有平台上以二进制模式安全地写入文件。

# File rubygems.rb, line 782
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

私有类方法

already_loaded?(file) 点击切换源代码
# File rubygems.rb, line 1321
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
default_gem_load_paths() 点击切换源代码
# File rubygems.rb, line 1327
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
find_home() 点击切换源代码

查找用户的主目录。

# File rubygems/defaults.rb, line 81
def self.find_home
  Dir.home.dup
rescue StandardError
  if Gem.win_platform?
    File.expand_path File.join(ENV["HOMEDRIVE"] || ENV["SystemDrive"], "/")
  else
    File.expand_path "/"
  end
end
find_spec_for_exe(name, exec_name, requirements) 点击切换源代码
# File rubygems.rb, line 245
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

私有实例方法

URI(uri) 点击以切换源

返回从给定 uri 派生的 Gem::URI 对象,该对象可以是 Gem::URI 字符串或现有的 Gem::URI 对象

# Returns a new Gem::URI.
uri = Gem::URI('http://github.com/ruby/ruby')
# => #<Gem::URI::HTTP http://github.com/ruby/ruby>
# Returns the given Gem::URI.
Gem::URI(uri)
# => #<Gem::URI::HTTP http://github.com/ruby/ruby>
# File rubygems/vendor/uri/lib/uri/common.rb, line 865
def URI(uri)
  if uri.is_a?(Gem::URI::Generic)
    uri
  elsif uri = String.try_convert(uri)
    Gem::URI.parse(uri)
  else
    raise ArgumentError,
      "bad argument (expected Gem::URI object or Gem::URI string)"
  end
end