Module: Bundler::Plugin
| Relationships & Source Files | |
| Namespace Children | |
|
Modules:
| |
|
Classes:
| |
|
Exceptions:
| |
| Defined in: | lib/bundler/plugin/api.rb, lib/bundler/plugin.rb, lib/bundler/plugin/dsl.rb, lib/bundler/plugin/events.rb, lib/bundler/plugin/index.rb, lib/bundler/plugin/installer.rb, lib/bundler/plugin/source_list.rb, lib/bundler/plugin/unloaded_source.rb, lib/bundler/plugin/api/source.rb, lib/bundler/plugin/installer/git.rb, lib/bundler/plugin/installer/path.rb, lib/bundler/plugin/installer/rubygems.rb |
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
-
PLUGIN_FILE_NAME =
# File 'lib/bundler/plugin.rb', line 19"plugins.rb"
Class Method Summary
-
.add_command(command, cls)
mod_func
To be called via the
APIto register to handle a command. -
.add_hook(event, &block)
mod_func
To be called via the
APIto register a hooks and corresponding block that will be called to handle the hook. -
.add_source(source, cls)
mod_func
To be called via the
APIto register to handle a source plugin. -
.cache
mod_func
The cache directory for plugin stuffs.
-
.command?(command) ⇒ Boolean
mod_func
Checks if any plugin handles the command.
-
.exec_command(command, args)
mod_func
To be called from Cli class to pass the command and argument to appropriate plugin class.
- .from_lock(locked_opts) ⇒ API::Source mod_func
-
.gemfile_install(gemfile = nil, lockfile = nil, unlock = {}, &inline)
mod_func
Evaluates the Gemfile with a limited
DSLand installs the plugins specified by plugin method. -
.global_root
mod_func
The global directory root for all plugin related data.
-
.hook(event, *args, &arg_blk)
mod_func
Runs all the hooks that are registered for the passed event.
-
.index
mod_func
The index object used to store the details about the plugin.
-
.install(names, options)
mod_func
Installs a new plugin by the given name.
-
.installed?(plugin) ⇒ String?
mod_func
currently only intended for specs.
-
.list
mod_func
List installed plugins and commands.
-
.load_plugin(name)
mod_func
Executes the plugins.rb file.
- .loaded?(plugin) ⇒ true, false mod_func
- .local_root mod_func
-
.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.
- .reset! mod_func
-
.root
mod_func
The directory root for all plugin related data.
-
.save_plugin(name, spec, optional_plugin = false)
mod_func
Validates and registers a plugin.
-
.save_plugins(specs, optional_plugins = [])
mod_func
Post installation processing and registering with index.
- .source(name) ⇒ Class mod_func
-
.source?(name) ⇒ Boolean
mod_func
Checks if any plugin declares the source.
-
.source_plugin(name)
mod_func
Returns the plugin that handles the source
nameif any. -
.uninstall(names, options)
mod_func
Uninstalls plugins by the given names.
-
.validate_plugin!(plugin_path)
mod_func
Checks if the gem is good to be a plugin.
Class Method Details
.add_command(command, cls) (mod_func)
To be called via the Plugin::API to register to handle a command
# File 'lib/bundler/plugin.rb', line 185
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
# File 'lib/bundler/plugin.rb', line 247
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
.cache (mod_func)
The cache directory for plugin stuffs
# File 'lib/bundler/plugin.rb', line 180
def cache @cache ||= root.join("cache") end
.command?(command) ⇒ Boolean (mod_func)
Checks if any plugin handles the command
# File 'lib/bundler/plugin.rb', line 190
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
# File 'lib/bundler/plugin.rb', line 196
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) ⇒ API::Source (mod_func)
# File 'lib/bundler/plugin.rb', line 231
def from_lock(locked_opts) opts = locked_opts.merge("uri" => locked_opts["remote"]) # when reading the lockfile while doing the plugin-install-from-gemfile phase, # we need to ignore any plugin sources return UnloadedSource.new(opts) if @gemfile_parse # use an inert placeholder when the plugin handling this source is not # installed, so that the lockfile can still be parsed return UnloadedSource.new(opts) unless source?(locked_opts["type"]) src = source(locked_opts["type"]) src.new(opts) end
.gemfile_install(gemfile = nil, lockfile = nil, unlock = {}, &inline) (mod_func)
Evaluates the Gemfile with a limited Plugin::DSL and installs the plugins
specified by plugin method
# File 'lib/bundler/plugin.rb', line 113
def gemfile_install(gemfile = nil, lockfile = nil, unlock = {}, &inline) @gemfile_parse = true Bundler.configure builder = DSL.new if block_given? builder.instance_eval(&inline) else builder.eval_gemfile(gemfile) end builder.check_primary_source_safety plugins = builder.dependencies.map(&:name) return if plugins.empty? # skip the update if unlocking specific gems, but none of them are plugins # declared in the Gemfile if unlock.is_a?(Hash) && unlock[:gems] && !unlock[:gems].empty? && (unlock[:gems] & plugins).empty? unlock = {} end # resolve remotely when unlocking, so that plugins can be updated. # Definition#initialize consumes the unlock hash, so this must be decided # before building the definition. updating = unlock == true || (unlock.is_a?(Hash) && !unlock.empty?) definition = builder.to_definition(lockfile, unlock) installed_specs = Installer.new.install_definition(definition, updating) save_plugins installed_specs.slice(*plugins), builder.inferred_plugins rescue RuntimeError => e unless e.is_a?(GemfileError) Bundler.ui.error "Failed to install plugin: #{e.}\n #{e.backtrace[0]}" end raise ensure @gemfile_parse = false end
.global_root (mod_func)
The global directory root for all plugin related data
# File 'lib/bundler/plugin.rb', line 175
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.
# File 'lib/bundler/plugin.rb', line 260
def hook(event, *args, &arg_blk) return unless Bundler.settings[: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 (mod_func)
The index object used to store the details about the plugin
.install(names, options) (mod_func)
Installs a new plugin by the given name
# File 'lib/bundler/plugin.rb', line 48
def install(names, ) raise InvalidOption, "You cannot specify `--branch` and `--ref` at the same time." if ["branch"] && ["ref"] specs = Installer.new.install(names, ) save_plugins specs.slice(*names) 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
# File 'lib/bundler/plugin.rb', line 277
def installed?(plugin) (path = index.installed?(plugin)) && index.plugin_path(plugin).join(PLUGIN_FILE_NAME).file? && path end
.list (mod_func)
List installed plugins and commands
# File 'lib/bundler/plugin.rb', line 90
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
# File 'lib/bundler/plugin.rb', line 373
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.}" raise end
.loaded?(plugin) ⇒ true, false (mod_func)
# File 'lib/bundler/plugin.rb', line 284
def loaded?(plugin) @loaded_plugin_names.include?(plugin) end
.local_root (mod_func)
[ GitHub ]# File 'lib/bundler/plugin.rb', line 170
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.
# File 'lib/bundler/plugin.rb', line 337
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.}" end if optional_plugin && @sources.keys.any? {|s| source_plugin(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 31
def reset! instance_variables.each {|i| remove_instance_variable(i) } @sources = {} @commands = {} @hooks_by_event = Hash.new {|h, k| h[k] = [] } @loaded_plugin_names = [] @index = nil 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")
# File 'lib/bundler/plugin.rb', line 162
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.
# File 'lib/bundler/plugin.rb', line 318
def save_plugin(name, spec, optional_plugin = false) return if index.up_to_date?(spec) 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.})" end
.save_plugins(specs, optional_plugins = []) (mod_func)
Post installation processing and registering with index
# File 'lib/bundler/plugin.rb', line 293
def save_plugins(specs, optional_plugins = []) specs.each do |name, spec| save_plugin(name, spec, optional_plugins.include?(name)) end end
.source(name) ⇒ Class (mod_func)
# File 'lib/bundler/plugin.rb', line 220
def source(name) raise UnknownSourceError, "Source #{name} not found" unless source_plugin(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
# File 'lib/bundler/plugin.rb', line 210
def source?(name) !!source_plugin(name) end
.source_plugin(name) (mod_func)
Returns the plugin that handles the source name if any
# File 'lib/bundler/plugin.rb', line 215
def source_plugin(name) index.source_plugin(name.to_s) end
.uninstall(names, options) (mod_func)
Uninstalls plugins by the given names
# File 'lib/bundler/plugin.rb', line 64
def uninstall(names, ) if names.empty? && ![: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 [: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) (mod_func)
Checks if the gem is good to be a plugin
At present it only checks whether it contains plugins.rb file
# File 'lib/bundler/plugin.rb', line 305
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