123456789_123456789_123456789_123456789_123456789_

Module: Bundler::Plugin

Overview

This is the interfacing class represents the API that we intend to provide the plugins to use.

For plugins to be independent of the ::Bundler internals they shall limit their interactions to methods of this class only. This will save them from breaking when some internal change.

Currently we are delegating the methods defined in ::Bundler class to itself. So, this class acts as a buffer.

If there is some change in the ::Bundler class that is incompatible to its previous behavior or if otherwise desired, we can reimplement(or implement) the method to preserve compatibility.

To use this, either the class can inherit this class or use it directly. For example of both types of use, refer the file spec/plugins/command.rb

To use it without inheriting, you will have to create an object of this to use the functions (except for declaration functions like command, source, and hooks).

Constant Summary

Class Method Summary

Class Method Details

.add_command(command, cls) (mod_func)

To be called via the Plugin::API to register to handle a command

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 159

def add_command(command, cls)
  @commands[command] = cls
end

.add_hook(event, &block) (mod_func)

To be called via the Plugin::API to register a hooks and corresponding block that will be called to handle the hook

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 208

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) (mod_func)

To be called via the Plugin::API to register to handle a source plugin

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 179

def add_source(source, cls)
  @sources[source] = cls
end

.cache (mod_func)

The cache directory for plugin stuffs

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 154

def cache
  @cache ||= root.join("cache")
end

.command?(command) ⇒ Boolean (mod_func)

Checks if any plugin handles the command

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 164

def command?(command)
  !index.command_plugin(command).nil?
end

.exec_command(command, args) (mod_func)

To be called from Cli class to pass the command and argument to appropriate plugin class

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 170

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

.gemfile_install(gemfile = nil, &inline) (mod_func)

Evaluates the Gemfile with a limited Plugin::DSL and installs the plugins specified by plugin method

Parameters:

  • gemfile (Pathname) (defaults to: nil)

    path

  • block (Proc)

    that can be evaluated for (inline) Gemfile

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 102

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 (mod_func)

The global directory root for all plugin related data

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 149

def global_root
  Bundler.user_bundle_path("plugin")
end

.hook(event, *args, &arg_blk) (mod_func)

Runs all the hooks that are registered for the passed event

It passes the passed arguments and block to the block registered with the api.

Parameters:

  • event (String)
[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 221

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

.index (mod_func)

The index object used to store the details about the plugin

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 128

def index
  @index ||= Index.new
end

.install(names, options) (mod_func)

Installs a new plugin by the given name

Parameters:

  • names (Array<String>)

    the name of plugin to be installed

  • options (Hash)

    various parameters as described in description. Refer to cli/plugin for available options

[ GitHub ]

  
# File 'lib/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) ⇒ String? (mod_func)

currently only intended for specs

Returns:

  • (String, nil)

    installed path

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 238

def installed?(plugin)
  Index.new.installed?(plugin)
end

.list (mod_func)

List installed plugins and commands

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 79

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) (mod_func)

Executes the plugins.rb file

Parameters:

  • name (String)

    of the plugin

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 330

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

.local_root (mod_func)

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 144

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

.register_plugin(name, spec, optional_plugin = false) (mod_func)

Runs the plugins.rb file in an isolated namespace, records the plugin actions it registers for and then passes the data to index to be stored.

Parameters:

  • name (String)

    the name of the plugin

  • spec (Specification)

    of installed plugin

  • optional_plugin, (Boolean)

    removed if there is conflict with any other plugin (used for default source plugins)

Raises:

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 294

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

.reset! (mod_func)

[ GitHub ]

  
# File 'lib/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 (mod_func)

The directory root for all plugin related data

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

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 136

def root
  @root ||= if SharedHelpers.in_bundle?
    local_root
  else
    global_root
  end
end

.save_plugin(name, spec, optional_plugin = false) (mod_func)

Validates and registers a plugin.

Parameters:

  • name (String)

    the name of the plugin

  • spec (Specification)

    of installed plugin

  • optional_plugin, (Boolean)

    removed if there is conflict with any other plugin (used for default source plugins)

Raises:

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 277

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 = []) (mod_func)

Post installation processing and registering with index

Parameters:

  • plugins (Array<String>)

    list to be installed

  • specs (Hash)

    of plugins mapped to installation path (currently they contain all the installed specs, including plugins)

  • names (Array<String>)

    of inferred source plugins that can be ignored

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 248

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) ⇒ Class (mod_func)

Returns:

Raises:

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 189

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) ⇒ Boolean (mod_func)

Checks if any plugin declares the source

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 184

def source?(name)
  !index.source_plugin(name.to_s).nil?
end

.source_from_lock(locked_opts) ⇒ API::Source (mod_func)

Parameters:

  • The (Hash)

    options that are present in the lock file

Returns:

  • (API::Source)

    the instance of the class that handles the source type passed in locked_opts

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 200

def source_from_lock(locked_opts)
  src = source(locked_opts["type"])

  src.new(locked_opts.merge("uri" => locked_opts["remote"]))
end

.uninstall(names, options) (mod_func)

Uninstalls plugins by the given names

Parameters:

  • names (Array<String>)

    the names of plugins to be uninstalled

[ GitHub ]

  
# File 'lib/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)
        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

.validate_plugin!(plugin_path) (mod_func)

Checks if the gem is good to be a plugin

At present it only checks whether it contains plugins.rb file

Parameters:

  • plugin_path (Pathname)

    the path plugin is installed at

Raises:

[ GitHub ]

  
# File 'lib/bundler/plugin.rb', line 264

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