module Bundler::Plugin

def add_command(command, cls)

To be called via the API to register to handle a command
def add_command(command, cls)
  @commands[command] = cls
end

def add_hook(event, &block)

will be called to handle the hook
To be called via the API to register a hooks and corresponding block that
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

def add_source(source, cls)

To be called via the API to register to handle a source plugin
def add_source(source, cls)
  @sources[source] = cls
end

def cache

The cache directory for plugin stuffs
def cache
  @cache ||= root.join("cache")
end

def command?(command)

Checks if any plugin handles the command
def command?(command)
  !index.command_plugin(command).nil?
end

def exec_command(command, args)

appropriate plugin class
To be called from Cli class to pass the command and argument to
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

def gemfile_install(gemfile = nil, &inline)

Parameters:
  • block (Proc) -- that can be evaluated for (inline) Gemfile
  • gemfile (Pathname) -- path
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

def global_root

The global directory root for all plugin related data
def global_root
  Bundler.user_bundle_path("plugin")
end

def hook(event, *args, &arg_blk)

Parameters:
  • event (String) --
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 - @loaded_plugin_names).each {|name| load_plugin(name) }
  @hooks_by_event[event].each {|blk| blk.call(*args, &arg_blk) }
end

def index

The index object used to store the details about the plugin
def index
  @index ||= Index.new
end

def install(names, options)

Parameters:
  • options (Hash) -- various parameters as described in description.
  • names (Array) -- the name of plugin to be installed
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

def installed?(plugin)

Returns:
  • (String, nil) - installed path
def installed?(plugin)
  Index.new.installed?(plugin)
end

def list


List installed plugins and commands
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

def load_plugin(name)

Parameters:
  • name (String) -- of the plugin
def load_plugin(name)
  return unless name && !name.empty?
  # 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)
  Bundler.rubygems.add_to_load_path(index.load_paths(name))
  load path.join(PLUGIN_FILE_NAME)
  @loaded_plugin_names << name
rescue RuntimeError => e
  Bundler.ui.error "Failed loading plugin #{name}: #{e.message}"
  raise
end

def local_root

def local_root
  Bundler.app_config_path.join("plugin")
end

def register_plugin(name, spec, optional_plugin = false)

Raises:
  • (MalformattedPlugin) - if plugins.rb raises any error

Parameters:
  • optional_plugin, (Boolean) -- removed if there is conflict with any
  • spec (Specification) -- of installed plugin
  • name (String) -- the name of the plugin
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
  Bundler.rubygems.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

def reset!

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

def root

Otherwise, points to global root, in Bundler.user_bundle_path("plugin")
If run in an app, points to local root, in app_config_path

The directory root for all plugin related data
def root
  @root ||= if SharedHelpers.in_bundle?
    local_root
  else
    global_root
  end
end

def save_plugin(name, spec, optional_plugin = false)

Raises:
  • (PluginInstallError) - if validation or registration raises any error

Parameters:
  • optional_plugin, (Boolean) -- removed if there is conflict with any
  • spec (Specification) -- of installed plugin
  • name (String) -- the name of the plugin
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

def save_plugins(plugins, specs, optional_plugins = [])

Parameters:
  • names (Array) -- of inferred source plugins that can be ignored
  • specs (Hash) -- of plugins mapped to installation path (currently they
  • plugins (Array) -- list to be installed
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

def source(name)

Returns:
  • (Class) - that handles the source. The class includes API::Source
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

def source?(name)

Checks if any plugin declares the source
def source?(name)
  !index.source_plugin(name.to_s).nil?
end

def source_from_lock(locked_opts)

Returns:
  • (API::Source) - the instance of the class that handles the source

Parameters:
  • The (Hash) -- options that are present in the lock file
def source_from_lock(locked_opts)
  src = source(locked_opts["type"])
  src.new(locked_opts.merge("uri" => locked_opts["remote"]))
end

def uninstall(names, options)

Parameters:
  • names (Array) -- the names of plugins to be uninstalled
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)
        Bundler.rm_rf(index.plugin_path(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

def validate_plugin!(plugin_path)

Raises:
  • (MalformattedPlugin) - if plugins.rb file is not found

Parameters:
  • plugin_path (Pathname) -- the path plugin is installed at
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