# frozen_string_literal: true module Doing # Plugin handling module Plugins class << self def user_home @user_home ||= Util.user_home end def plugins @plugins ||= { import: {}, export: {} } end ## # Load plugins from plugins folder # def load_plugins(add_dir = nil) plugins_path(add_dir).each do |plugin_search_path| Dir.glob(File.join(plugin_search_path, '**', '*.rb')).sort.each do |plugin| require plugin end end plugins end # Public: Setup the plugin search path # # Returns an Array of plugin search paths def plugins_path(add_dir = nil) paths = Array(File.join(File.dirname(__FILE__), 'plugins')) paths << File.join(add_dir) if add_dir paths.map { |d| File.expand_path(d) } end ## # Register a plugin # # param: +[String|Array]+ title The name of the plugin (can be an array of names) # # param: +type+ The plugin type (:import, :export) # # param: +klass+ The class responding to :render or :import # # # returns: Success boolean # def register(title, type, klass) type = validate_plugin(title, type, klass) return unless type if title.is_a?(Array) title.each { |t| register(t, type, klass) } return end settings = if klass.respond_to? :settings klass.settings else { trigger: title.normalize_trigger, config: {} } end plugins[type] ||= {} plugins[type][title] = { trigger: settings[:trigger].normalize_trigger || title.normalize_trigger, class: klass, templates: settings[:templates] || nil, config: settings[:config] || {} } return unless ENV['DOING_PLUGIN_DEBUG'] Doing.logger.debug('Plugin Manager:', "Registered #{type} plugin \"#{title}\"") end def validate_plugin(title, type, klass) type = valid_type(type) if type == :import && !klass.respond_to?(:import) raise Errors::PluginUncallable.new('Import plugins must respond to :import', type: type, plugin: title) end if type == :export && !klass.respond_to?(:render) raise Errors::PluginUncallable.new('Export plugins must respond to :render', type: type, plugin: title) end type end def valid_type(type, default: nil) type ||= default t = type.to_s type = case t when /^i(m(p(o(r(t)?)?)?)?)?$/ :import when /^e(x(p(o(r(t)?)?)?)?)?$/ :export else raise Errors::InvalidPluginType, 'Invalid plugin type' end type.to_sym end ## ## List available plugins to stdout ## ## @param options { type, separator } ## def list_plugins(options = {}) separator = options[:column] ? "\n" : "\t" type = options[:type].nil? || options[:type] =~ /all/i ? 'all' : valid_type(options[:type]) case type when :import puts plugin_names(type: :import, separator: separator) when :export puts plugin_names(type: :export, separator: separator) else print 'Import plugins: ' puts plugin_names(type: :import, separator: ', ') print 'Export plugins: ' puts plugin_names(type: :export, separator: ', ') end end ## ## Return array of available plugin names ## ## @param type Plugin type (:import, :export) ## ## @return [Array<String>] plugin names ## def available_plugins(type: :export) type = valid_type(type) plugins[type].keys.sort end ## ## Return string version of plugin names ## ## @param type Plugin type (:import, :export) ## @param separator The separator to join names with ## ## @return [String] Plugin names ## def plugin_names(type: :export, separator: '|') type = valid_type(type) available_plugins(type: type).join(separator) end ## ## Return a regular expression of all ## plugin triggers for type ## ## @param type The type :import or :export ## def plugin_regex(type: :export) type = valid_type(type) pattern = [] plugins[type].each do |_, options| pattern << options[:trigger].normalize_trigger end Regexp.new("^(?:#{pattern.join('|')})$", true) end def plugin_templates(type: :export) type = valid_type(type) templates = [] plugs = plugins[type].clone plugs.delete_if { |_t, o| o[:templates].nil? }.each do |_, options| options[:templates].each do |t| templates << t[:name] end end templates end def template_regex(type: :export) type = valid_type(type) pattern = [] plugs = plugins[type].clone plugs.delete_if { |_t, o| o[:templates].nil? }.each do |_, options| options[:templates].each do |t| pattern << t[:trigger].normalize_trigger end end Regexp.new("^(?:#{pattern.join('|')})$", true) end def template_for_trigger(trigger, type: :export) type = valid_type(type) plugs = plugins[type].clone plugs.delete_if { |_t, o| o[:templates].nil? }.each do |_, options| options[:templates].each do |t| return options[:class].template(trigger) if trigger =~ /^(?:#{t[:trigger].normalize_trigger})$/ end end raise Errors::InvalidArgument, "No template type matched \"#{trigger}\"" end end end end