模块 Bundler::Plugin

这是代表我们打算提供给插件使用的 API 的接口类。

为了使插件独立于 Bundler 内部,它们应将其交互限制为仅此类的方法。这将使它们在某些内部更改时不会中断。

目前,我们将 Bundler 类中定义的方法委派给自身。因此,此类充当缓冲区。

如果 Bundler 类中存在与先前行为不兼容的更改,或者在其他方面需要时,我们可以重新实现(或实现)该方法以保持兼容性。

要使用它,该类可以继承此类或直接使用它。有关两种使用类型的示例,请参考文件“spec/plugins/command.rb”

要在不继承的情况下使用它,您必须创建此对象才能使用这些函数(除了诸如 command、source 和 hooks 之类的声明函数)。

管理安装了哪些插件及其来源。这也应该映射哪个插件执行什么操作(目前这些功能尚未实现,因此此类现在是一个存根类)。

处理插件在适当目录中的安装。

此类应该是现有 gem 安装基础设施的包装器,但目前它自己处理所有事情,因为 Source 的子类(例如 Source::RubyGems)严重依赖 Gemfile。

在解析 Gemfile 时要使用的 SourceList 对象,设置要与 Source 类一起使用的适当选项以进行插件安装

常量

PLUGIN_FILE_NAME

公共实例方法

add_command(command, cls) 单击以切换源

通过 API 调用以注册来处理命令

# File bundler/plugin.rb, line 160
def add_command(command, cls)
  @commands[command] = cls
end
add_hook(event, &block) 单击以切换源

通过 API 调用以注册钩子和相应的块,该块将被调用以处理钩子

# File bundler/plugin.rb, line 209
def add_hook(event, &block)
  unless Events.defined_event?(event)
    raise ArgumentError, "Event '#{event}' not defined in Bundler::Plugin::Events"
  end
  @hooks_by_event[event.to_s] << block
end
add_source(source, cls) 单击以切换源

通过 API 调用以注册来处理源插件

# File bundler/plugin.rb, line 180
def add_source(source, cls)
  @sources[source] = cls
end
cache() 单击以切换源

插件内容的缓存目录

# File bundler/plugin.rb, line 155
def cache
  @cache ||= root.join("cache")
end
command?(command) 单击以切换源

检查是否有任何插件处理该命令

# File bundler/plugin.rb, line 165
def command?(command)
  !index.command_plugin(command).nil?
end
exec_command(command, args) 单击以切换源

从 Cli 类调用以将命令和参数传递给适当的插件类

# File bundler/plugin.rb, line 171
def exec_command(command, args)
  raise UndefinedCommandError, "Command `#{command}` not found" unless command? command

  load_plugin index.command_plugin(command) unless @commands.key? command

  @commands[command].new.exec(command, args)
end
from_lock(locked_opts) 单击以切换源

@param [Hash] 锁定文件中存在的选项 @return [API::Source] 处理该来源的类的实例

type passed in locked_opts
# File bundler/plugin.rb, line 201
def from_lock(locked_opts)
  src = source(locked_opts["type"])

  src.new(locked_opts.merge("uri" => locked_opts["remote"]))
end
gemfile_install(gemfile = nil, &inline) 单击以切换源

使用有限的 DSL 评估 Gemfile,并安装由 plugin 方法指定的插件

@param [Pathname] gemfile 路径 @param [Proc] 可以为(内联)Gemfile 求值的块

# File bundler/plugin.rb, line 103
def gemfile_install(gemfile = nil, &inline)
  Bundler.settings.temporary(frozen: false, deployment: false) do
    builder = DSL.new
    if block_given?
      builder.instance_eval(&inline)
    else
      builder.eval_gemfile(gemfile)
    end
    builder.check_primary_source_safety
    definition = builder.to_definition(nil, true)

    return if definition.dependencies.empty?

    plugins = definition.dependencies.map(&:name).reject {|p| index.installed? p }
    installed_specs = Installer.new.install_definition(definition)

    save_plugins plugins, installed_specs, builder.inferred_plugins
  end
rescue RuntimeError => e
  unless e.is_a?(GemfileError)
    Bundler.ui.error "Failed to install plugin: #{e.message}\n  #{e.backtrace[0]}"
  end
  raise
end
global_root() 单击以切换源

所有插件相关数据的全局目录根

# File bundler/plugin.rb, line 150
def global_root
  Bundler.user_bundle_path("plugin")
end
hook(event, *args, &arg_blk) 单击以切换源

运行为传递的事件注册的所有钩子

它将传递的参数和块传递给使用 api 注册的块。

@param [String] 事件

# File bundler/plugin.rb, line 222
def hook(event, *args, &arg_blk)
  return unless Bundler.feature_flag.plugins?
  unless Events.defined_event?(event)
    raise ArgumentError, "Event '#{event}' not defined in Bundler::Plugin::Events"
  end

  plugins = index.hook_plugins(event)
  return unless plugins.any?

  plugins.each {|name| load_plugin(name) }

  @hooks_by_event[event].each {|blk| blk.call(*args, &arg_blk) }
end
index() 单击以切换源

用于存储有关插件详细信息的索引对象

# File bundler/plugin.rb, line 129
def index
  @index ||= Index.new
end
install(names, options) 单击以切换源

通过给定的名称安装新插件

@param [Array<String>] 要安装的插件名称 @param [Hash] 描述中描述的各种参数。

Refer to cli/plugin for available options
# File bundler/plugin.rb, line 38
def install(names, options)
  raise InvalidOption, "You cannot specify `--branch` and `--ref` at the same time." if options["branch"] && options["ref"]

  specs = Installer.new.install(names, options)

  save_plugins names, specs
rescue PluginError
  specs_to_delete = specs.select {|k, _v| names.include?(k) && !index.commands.values.include?(k) }
  specs_to_delete.each_value {|spec| Bundler.rm_rf(spec.full_gem_path) }

  raise
end
installed?(plugin) 单击以切换源

目前仅用于规范

@return [String, nil] 安装路径

# File bundler/plugin.rb, line 239
def installed?(plugin)
  Index.new.installed?(plugin)
end
list() 单击以切换源

列出已安装的插件和命令

# File bundler/plugin.rb, line 80
def list
  installed_plugins = index.installed_plugins
  if installed_plugins.any?
    output = String.new
    installed_plugins.each do |plugin|
      output << "#{plugin}\n"
      output << "-----\n"
      index.plugin_commands(plugin).each do |command|
        output << "  #{command}\n"
      end
      output << "\n"
    end
  else
    output = "No plugins installed"
  end
  Bundler.ui.info output
end
load_plugin(name) 单击以切换源

执行 plugins.rb 文件

@param [String] 插件名称

# File bundler/plugin.rb, line 336
    def load_plugin(name)
      return unless name && !name.empty?
      return if loaded?(name)

      # Need to ensure before this that plugin root where the rest of gems
      # are installed to be on load path to support plugin deps. Currently not
      # done to avoid conflicts
      path = index.plugin_path(name)

      paths = index.load_paths(name)
      invalid_paths = paths.reject {|p| File.directory?(p) }

      if invalid_paths.any?
        Bundler.ui.warn <<~MESSAGE
          The following plugin paths don't exist: #{invalid_paths.join(", ")}.

          This can happen if the plugin was installed with a different version of Ruby that has since been uninstalled.

          If you would like to reinstall the plugin, run:

          bundler plugin uninstall #{name} && bundler plugin install #{name}

          Continuing without installing plugin #{name}.
        MESSAGE

        return
      end

      Gem.add_to_load_path(*paths)

      load path.join(PLUGIN_FILE_NAME)

      @loaded_plugin_names << name
    rescue RuntimeError => e
      Bundler.ui.error "Failed loading plugin #{name}: #{e.message}"
      raise
    end
loaded?(plugin) 单击以切换源

@return [true, false] 插件是否已加载

# File bundler/plugin.rb, line 244
def loaded?(plugin)
  @loaded_plugin_names.include?(plugin)
end
local_root() 单击以切换源
# File bundler/plugin.rb, line 145
def local_root
  Bundler.app_config_path.join("plugin")
end
register_plugin(name, spec, optional_plugin = false) 单击以切换源

在隔离的命名空间中运行 plugins.rb 文件,记录它注册的插件操作,然后将数据传递给索引以进行存储。

@param [String] 插件名称 @param [Specification] 已安装插件的规范 @param [Boolean] optional_plugin,如果存在冲突则删除

other plugin (used for default source plugins)

@raise [MalformattedPlugin] 如果 plugins.rb 引发任何错误

# File bundler/plugin.rb, line 300
def register_plugin(name, spec, optional_plugin = false)
  commands = @commands
  sources = @sources
  hooks = @hooks_by_event

  @commands = {}
  @sources = {}
  @hooks_by_event = Hash.new {|h, k| h[k] = [] }

  load_paths = spec.load_paths
  Gem.add_to_load_path(*load_paths)
  path = Pathname.new spec.full_gem_path

  begin
    load path.join(PLUGIN_FILE_NAME), true
  rescue StandardError => e
    raise MalformattedPlugin, "#{e.class}: #{e.message}"
  end

  if optional_plugin && @sources.keys.any? {|s| source? s }
    Bundler.rm_rf(path)
    false
  else
    index.register_plugin(name, path.to_s, load_paths, @commands.keys,
      @sources.keys, @hooks_by_event.keys)
    true
  end
ensure
  @commands = commands
  @sources = sources
  @hooks_by_event = hooks
end
reset!() 单击以切换源
# File bundler/plugin.rb, line 22
def reset!
  instance_variables.each {|i| remove_instance_variable(i) }

  @sources = {}
  @commands = {}
  @hooks_by_event = Hash.new {|h, k| h[k] = [] }
  @loaded_plugin_names = []
end
root() 单击以切换源

所有插件相关数据的目录根

如果在应用程序中运行,则指向本地根,在 app_config_path 中。否则,指向全局根,在 Bundler.user_bundle_path(“plugin”) 中

# File bundler/plugin.rb, line 137
def root
  @root ||= if SharedHelpers.in_bundle?
    local_root
  else
    global_root
  end
end
save_plugin(name, spec, optional_plugin = false) 单击以切换源

验证并注册插件。

@param [String] 插件名称 @param [Specification] 已安装插件的规范 @param [Boolean] optional_plugin,如果存在冲突则删除

other plugin (used for default source plugins)

@raise [PluginInstallError] 如果验证或注册引发任何错误

# File bundler/plugin.rb, line 283
def save_plugin(name, spec, optional_plugin = false)
  validate_plugin! Pathname.new(spec.full_gem_path)
  installed = register_plugin(name, spec, optional_plugin)
  Bundler.ui.info "Installed plugin #{name}" if installed
rescue PluginError => e
  raise PluginInstallError, "Failed to install plugin `#{spec.name}`, due to #{e.class} (#{e.message})"
end
save_plugins(plugins, specs, optional_plugins = []) 单击以切换源

安装后处理并使用索引注册

@param [Array<String>] 要安装的插件列表 @param [Hash] 插件的规范映射到安装路径(目前它们

contain all the installed specs, including plugins)

@param [Array<String>] 可以忽略的推断源插件的名称

# File bundler/plugin.rb, line 254
def save_plugins(plugins, specs, optional_plugins = [])
  plugins.each do |name|
    next if index.installed?(name)

    spec = specs[name]

    save_plugin(name, spec, optional_plugins.include?(name))
  end
end
source(name) 单击以切换源

@return [Class] 处理该来源的类。该类包括 API::Source

# File bundler/plugin.rb, line 190
def source(name)
  raise UnknownSourceError, "Source #{name} not found" unless source? name

  load_plugin(index.source_plugin(name)) unless @sources.key? name

  @sources[name]
end
source?(name) 单击以切换源

检查是否有任何插件声明了该来源

# File bundler/plugin.rb, line 185
def source?(name)
  !index.source_plugin(name.to_s).nil?
end
uninstall(names, options) 单击以切换源

通过给定的名称卸载插件

@param [Array<String>] 要卸载的插件名称

# File bundler/plugin.rb, line 54
def uninstall(names, options)
  if names.empty? && !options[:all]
    Bundler.ui.error "No plugins to uninstall. Specify at least 1 plugin to uninstall.\n"\
      "Use --all option to uninstall all the installed plugins."
    return
  end

  names = index.installed_plugins if options[:all]
  if names.any?
    names.each do |name|
      if index.installed?(name)
        path = index.plugin_path(name).to_s
        Bundler.rm_rf(path) if index.installed_in_plugin_root?(name)
        index.unregister_plugin(name)
        Bundler.ui.info "Uninstalled plugin #{name}"
      else
        Bundler.ui.error "Plugin #{name} is not installed \n"
      end
    end
  else
    Bundler.ui.info "No plugins installed"
  end
end
validate_plugin!(plugin_path) 单击以切换源

检查该 gem 是否适合作为插件

目前,它仅检查它是否包含 plugins.rb 文件

@param [Pathname] 插件安装路径 @raise [MalformattedPlugin] 如果未找到 plugins.rb 文件

# File bundler/plugin.rb, line 270
def validate_plugin!(plugin_path)
  plugin_file = plugin_path.join(PLUGIN_FILE_NAME)
  raise MalformattedPlugin, "#{PLUGIN_FILE_NAME} was not found in the plugin." unless plugin_file.file?
end