class CLAide::Command::PluginManager
Handles plugin related logic logic for the `Command` class.
Plugins are loaded the first time a command run and are identified by the prefix specified in the command class. Plugins must adopt the following conventions:
-
Support being loaded by a file located under the
`lib/#{plugin_prefix}_plugin` relative path.
-
Be stored in a folder named after the plugin.
Public Class Methods
@return [Array<Specification>] The RubyGems specifications for the
installed plugins that match the given `plugin_prefix`.
# File lib/claide/command/plugin_manager.rb, line 45 def self.installed_specifications_for_prefix(plugin_prefix) loaded_plugins[plugin_prefix] || plugin_gems_for_prefix(plugin_prefix).map(&:first) end
@return [Array<Gem::Specification>] Loads plugins via RubyGems looking
for files named after the `PLUGIN_PREFIX_plugin` and returns the specifications of the gems loaded successfully. Plugins are required safely.
# File lib/claide/command/plugin_manager.rb, line 28 def self.load_plugins(plugin_prefix) loaded_plugins[plugin_prefix] ||= plugin_gems_for_prefix(plugin_prefix).map do |spec, paths| spec if safe_activate_and_require(spec, paths) end.compact end
@return [Hash<String,Gem::Specification>] The loaded plugins,
grouped by plugin prefix.
# File lib/claide/command/plugin_manager.rb, line 19 def self.loaded_plugins @loaded_plugins ||= {} end
@return [Array<[Gem::Specification, Array<String>]>]
Returns an array of tuples containing the specifications and plugin files to require for a given plugin prefix.
# File lib/claide/command/plugin_manager.rb, line 72 def self.plugin_gems_for_prefix(prefix) glob = "#{prefix}_plugin#{Gem.suffix_pattern}" Gem::Specification.latest_specs(true).map do |spec| matches = spec.matches_for_glob(glob) [spec, matches] unless matches.empty? end.compact end
@return [Array<String>] The list of the plugins whose root path appears
in the backtrace of an exception.
@param [Exception] exception
The exception to analyze.
# File lib/claide/command/plugin_manager.rb, line 56 def self.plugins_involved_in_exception(exception) specifications.select do |gemspec| exception.backtrace.any? do |line| full_require_paths_for(gemspec).any? do |plugin_path| line.include?(plugin_path) end end end.map(&:name) end
Activates the given spec and requires the given paths. If any exception occurs it is caught and an informative message is printed.
@param [Gem::Specification] spec
The spec to be activated.
@param [String] paths
The paths to require.
@return [Bool] Whether activation and requiring succeeded.
# File lib/claide/command/plugin_manager.rb, line 92 def self.safe_activate_and_require(spec, paths) spec.activate paths.each { |path| require(path) } true rescue Exception => exception # rubocop:disable RescueException message = "\n---------------------------------------------" message << "\nError loading the plugin `#{spec.full_name}`.\n" message << "\n#{exception.class} - #{exception.message}" message << "\n#{exception.backtrace.join("\n")}" message << "\n---------------------------------------------\n" warn message.ansi.yellow false end
@return [Array<Specification>] The RubyGems specifications for the
loaded plugins.
# File lib/claide/command/plugin_manager.rb, line 38 def self.specifications loaded_plugins.values.flatten.uniq end
Private Class Methods
# File lib/claide/command/plugin_manager.rb, line 106 def self.full_require_paths_for(gemspec) if gemspec.respond_to?(:full_require_paths) return gemspec.full_require_paths end # RubyGems < 2.2 gemspec.require_paths.map do |require_path| if require_path.include?(gemspec.full_gem_path) require_path else File.join(gemspec.full_gem_path, require_path) end end end