diff options
-rw-r--r-- | lib/bundler/cli.rb | 4 | ||||
-rw-r--r-- | lib/bundler/cli/install.rb | 2 | ||||
-rw-r--r-- | lib/bundler/plugin.rb | 41 | ||||
-rw-r--r-- | lib/bundler/plugin/api.rb | 31 | ||||
-rw-r--r-- | lib/bundler/plugin/index.rb | 30 |
5 files changed, 98 insertions, 10 deletions
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 75d4581e12..7cde242804 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -82,6 +82,10 @@ module Bundler end def self.handle_no_command_error(command, has_namespace = $thor_runner) + if Bundler::Plugin.command? command + return Bundler::Plugin.exec_command(command, ARGV[1..-1]) + end + return super unless command_path = Bundler.which("bundler-#{command}") Kernel.exec(command_path, *ARGV[1..-1]) diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb index efde762e5a..0589b87d26 100644 --- a/lib/bundler/cli/install.rb +++ b/lib/bundler/cli/install.rb @@ -96,7 +96,7 @@ module Bundler # rubygems plugins sometimes hook into the gem install process Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins) - Bundler::Plugin.eval_gemfile(Bundler.default_gemfile) + Plugin.eval_gemfile(Bundler.default_gemfile) definition = Bundler.definition definition.validate_ruby! diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb index e4ab928735..4bf398e0eb 100644 --- a/lib/bundler/plugin.rb +++ b/lib/bundler/plugin.rb @@ -2,11 +2,16 @@ module Bundler class Plugin + autoload :Api, "bundler/plugin/api" autoload :Dsl, "bundler/plugin/dsl" autoload :Index, "bundler/plugin/index" autoload :Installer, "bundler/plugin/installer" autoload :SourceList, "bundler/plugin/source_list" + PLUGIN_FILE_NAME = "plugin.rb".freeze + + @commands = {} + class << self # Installs a new plugin by the given name # @@ -60,6 +65,22 @@ module Bundler @cache ||= root.join("cache") end + def add_command(command, cls) + @commands[command] = cls + end + + def command?(command) + index.command? command + end + + def exec_command(command, args) + raise "Command #{command} not found" unless command? command + + load_plugin index.command_plugin(command) unless @commands.key? command + + @commands[command].new.exec(command, args) + end + private # Checks if the gem is good to be a plugin @@ -70,8 +91,8 @@ module Bundler # # @raise [Error] if plugin.rb file is not found def validate_plugin!(plugin_path) - plugin_file = plugin_path.join("plugin.rb") - raise "plugin.rb was not found in the plugin!" unless plugin_file.file? + plugin_file = plugin_path.join(PLUGIN_FILE_NAME) + raise "#{PLUGIN_FILE_NAME} was not found in the plugin!" unless plugin_file.file? end # Runs the plugin.rb file, records the plugin actions it registers for and @@ -80,9 +101,21 @@ module Bundler # @param [String] name the name of the plugin # @param [Pathname] path the path where the plugin is installed at def register_plugin(name, path) - require path.join("plugin.rb") # this shall latter be used to find the actions the plugin performs + commands = @commands + + @commands = {} + + require path.join(PLUGIN_FILE_NAME) # this shall latter be used to find the actions the plugin performs + + index.register_plugin name, path.to_s, @commands.keys + ensure + @commands = commands + end + + def load_plugin(name) + path = index.plugin_path(name) - index.register_plugin name, path.to_s + load path.join(PLUGIN_FILE_NAME) end end end diff --git a/lib/bundler/plugin/api.rb b/lib/bundler/plugin/api.rb new file mode 100644 index 0000000000..267b448cc3 --- /dev/null +++ b/lib/bundler/plugin/api.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Bundler + # 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. + class Plugin::Api + def self.command(command) + Plugin.add_command command, self + end + + # The cache dir to be used by the plugins for persistance storage + # + # @return [Pathname] path of the cache dir + def cache + Plugin.cache.join("plugins") + end + + # A tmp dir to be used by plugins + # Note: Its desirable to explicitly remove the dir after use + # + # @param [String] name unique for the plugin or the purpose + # @return [Pathname] object for the new directory created + def tmp(name) + Bundler.tmp(File.join(["plugin", name])) + end + end +end diff --git a/lib/bundler/plugin/index.rb b/lib/bundler/plugin/index.rb index 0fc70af27a..5d8e896f30 100644 --- a/lib/bundler/plugin/index.rb +++ b/lib/bundler/plugin/index.rb @@ -7,7 +7,8 @@ module Bundler # now a stub class). class Plugin::Index def initialize - @plugin_sources = {} + @plugin_paths = {} + @commands = {} load_index end @@ -17,8 +18,13 @@ module Bundler # # @param [String] name of the plugin to be registered # @param [String] path where the plugin is installed - def register_plugin(name, path) - @plugin_sources[name] = path + # @param [Array<String>] commands that are handled by the plugin + def register_plugin(name, path, commands) + @plugin_paths[name] = path + + common = commands & @commands.keys + raise "Command(s) #{common.join(", ")} are already registered" if common.any? + commands.each{|c| @commands[c] = name } save_index end @@ -28,6 +34,18 @@ module Bundler Plugin.root.join("index") end + def plugin_path(name) + Pathname.new @plugin_paths[name] + end + + def command?(command) + @commands.key? command + end + + def command_plugin(command) + @commands[command] + end + private # Reads the index file from the directory and initializes the instance variables. @@ -36,7 +54,8 @@ module Bundler valid_file = index_f && index_f.exist? && !index_f.size.zero? break unless valid_file index = YAML.load_file(index_f) - @plugin_sources = index["plugin_sources"] + @plugin_paths = index["plugin_paths"] + @commands = index["commands"] end end @@ -44,7 +63,8 @@ module Bundler # variables in YAML format. (The instance variables are supposed to be only String key value pairs) def save_index index = { - "plugin_sources" => @plugin_sources, + "plugin_paths" => @plugin_paths, + "commands" => @commands, } SharedHelpers.filesystem_access(index_file) do |index_f| |