module Doing::Plugins

Plugin handling

Public Class Methods

available_plugins(type: :export) click to toggle source

Return array of available plugin names

@param type [Symbol] Plugin type (:import, :export)

@return [Array] Array of plugin names (String)

# File lib/doing/plugin_manager.rb, line 182
def available_plugins(type: :export)
  type = valid_type(type)
  plugins[type].keys.sort
end
list_plugins(options = {}) click to toggle source

List available plugins to stdout

@param options [Hash] additional options

@option options :column [Boolean] display results in a single column @option options :type [String] Plugin type: all, import, or export

# File lib/doing/plugin_manager.rb, line 158
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
load_plugins(add_dir = nil) click to toggle source

Load plugins from plugins folder

# File lib/doing/plugin_manager.rb, line 28
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

  Gem.find_latest_files('doing-plugin-*', true).sort.each do |plugin|
    load plugin
  end

  # Gem.path.each do |path|
  #   $LOAD_PATH.unshift path
  #   Dir.glob(File.join(path, 'gems', 'doing-plugin-*', 'lib', '*.rb')).sort.each do |plugin|
  #     require plugin.sub(%r{#{path}/gems/(.*?)-[\d.]+$}, '\1')
  #   end
  # end

  plugins
end
plugin_names(type: :export, separator: '|') click to toggle source

Return string version of plugin names

@param type Plugin type (:import, :export) @param separator The separator to join names with

@return [String] Plugin names joined with separator

# File lib/doing/plugin_manager.rb, line 195
def plugin_names(type: :export, separator: '|')
  type = valid_type(type)
  available_plugins(type: type).join(separator)
end
plugin_regex(type: :export) click to toggle source

Return a regular expression of all plugin triggers for type

@param type [Symbol] The type :import or :export

@return [Regexp] regular expression

# File lib/doing/plugin_manager.rb, line 209
def plugin_regex(type: :export)
  type = valid_type(type)
  pattern = []
  plugins[type].each do |_, options|
    pattern << options[:trigger].normalize_trigger
  end
  Regexp.new("^(?:#{pattern.sort.uniq.join('|')})$", true)
end
plugin_templates(type: :export) click to toggle source

Return array of available template names

@param type [Symbol] Plugin type (:import, :export)

@return [Array] Array of template names (String)

# File lib/doing/plugin_manager.rb, line 226
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|
      out = t[:name]
      out += " (#{t[:format]})" if t.key?(:format)
      templates << out
    end
  end

  templates.sort.uniq
end
plugins() click to toggle source

Storage for registered plugins. Hash with :import and :export keys containing hashes of available plugins.

@return [Hash] registered plugins

# File lib/doing/plugin_manager.rb, line 18
def plugins
  @plugins ||= {
    import: {},
    export: {}
  }
end
plugins_path(add_dir = nil) click to toggle source

Setup the plugin search path

@param add_dir [String] optional additional path to include

@return [Array] Returns an Array of plugin search paths

# File lib/doing/plugin_manager.rb, line 56
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(title, type, klass) click to toggle source

Register a plugin

@param title [String|Array] The name of the plugin (can be an array of names) @param type [Symbol] The plugin type (:import, :export) @param klass [Class] The class responding to :render or :import

@return [Boolean] Success boolean

# File lib/doing/plugin_manager.rb, line 74
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
template_for_trigger(trigger, type: :export, save_to: nil) click to toggle source

Find and return the appropriate template for a trigger string. Outputs a string that can be written out to the terminal for redirection

@param trigger [String] The trigger to test @param type [Symbol] the plugin type (:import, :export) @param save_to [String] if a path is specified, write the template to that path. Nil for STDOUT

@return [String] string content of template for trigger

# File lib/doing/plugin_manager.rb, line 276
def template_for_trigger(trigger, type: :export, save_to: nil)
  plugins[valid_type(type)].clone.delete_if { |_t, o| o[:templates].nil? }.each do |_, options|
    options[:templates].each do |t|
      next unless trigger =~ /^(?:#{t[:trigger].normalize_trigger})$/

      tpl = options[:class].template(trigger)
      return tpl unless save_to

      raise PluginException.new('No default filename defined', :export, t[:name]) unless t.key?(:filename)

      return save_template(tpl, save_to, t[:filename])
    end
  end
  raise Errors::InvalidArgument, "No template type matched \"#{trigger}\""
end
template_regex(type: :export) click to toggle source

Return a regular expression of all template triggers for type

@param type [Symbol] The type :import or :export

@return [Regexp] regular expression

# File lib/doing/plugin_manager.rb, line 250
def template_regex(type: :export)
  type = valid_type(type)
  pattern = []
  plugs = plugins[type].clone
  plugs.delete_if { |_, 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
user_home() click to toggle source

Return the user’s home directory

# File lib/doing/plugin_manager.rb, line 8
def user_home
  @user_home ||= Util.user_home
end
valid_type(type, default: nil) click to toggle source

Converts a partial symbol to a valid plugin type, e.g. :imp => :import

@param type [Symbol] the symbol to test @param default [Symbol] fallback value

@return [Symbol] :import or :export

# File lib/doing/plugin_manager.rb, line 135
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.new('Invalid plugin type', 'unrecognized')
         end

  type.to_sym
end
validate_plugin(title, type, klass) click to toggle source

Verifies that a plugin is properly configured with necessary methods for its type. If the plugin fails validation, a PluginUncallable exception will be raised.

@param title [String] The title @param type [Symbol] type, :import or :export @param klass [Class] Plugin class

# File lib/doing/plugin_manager.rb, line 113
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, title)
  end

  if type == :export && !klass.respond_to?(:render)
    raise Errors::PluginUncallable.new('Export plugins must respond to :render', type, title)
  end

  type
end

Private Class Methods

save_template(tpl, dir, filename) click to toggle source
# File lib/doing/plugin_manager.rb, line 294
def save_template(tpl, dir, filename)
  dir = File.expand_path(dir)
  FileUtils.mkdir_p(dir) unless File.exist?(dir)
  raise DoingRuntimeError, "Path #{dir} exists but is not a directory" unless File.directory?(dir)

  file = File.join(dir, filename)
  File.open(file, 'w') do |f|
    f.puts(tpl)
    Doing.logger.warn('File update:', "Template written to #{file}")
  end
end