module Nucleon module Plugin class Base < Core def self.register_ids [ :plugin_name, :name ] end # All Plugin classes should directly or indirectly extend Base def initialize(namespace, plugin_type, provider, options) @actor = Nucleon.handle(self) config = Util::Data.clean(Config.ensure(options), false) name = Util::Data.ensure_value(config.delete(:plugin_name), config.delete(:name, provider)) @quiet = config.delete(:quiet, false) set_meta(config.delete(:meta, Config.new)) # No logging statements above this line!! super(config.import({ :logger => "#{namespace}->#{plugin_type}->#{plugin_provider}" }), {}, true, false, false) myself.plugin_name = name logger.debug("Normalizing #{namespace} #{plugin_type} plugin #{plugin_name}") normalize(false) @initialized = true end #--- def parallel_finalize remove_plugin end #--- def method_missing(method, *args, &block) return nil end #--- def remove_plugin # Implement in sub classes if needed for cleanup end #----------------------------------------------------------------------------- # Checks def quiet? @quiet end #----------------------------------------------------------------------------- # Property accessor / modifiers def myself @actor end alias_method :me, :myself #--- def quiet=quiet @quiet = quiet end #--- def meta return @meta end #--- def set_meta(meta) @meta = Config.ensure(meta) end #--- def plugin_namespace return meta.get(:namespace) end #--- def plugin_type return meta.get(:type) end #--- def plugin_provider return meta.get(:provider) end #--- def plugin_name return meta.get(:name) end def plugin_name=plugin_name meta.set(:name, string(plugin_name)) end #--- def plugin_directory return meta.get(:directory) end #--- def plugin_file return meta.get(:file) end #--- def plugin_instance_name return meta.get(:instance_name) end #--- def plugin_parent=parent meta.set(:parent, parent) if parent.is_a?(Nucleon::Plugin::Base) end def plugin_parent return meta.get(:parent) end #----------------------------------------------------------------------------- # Status codes def self.code Nucleon.code end def code self.class.code end def self.codes(*codes) Nucleon.codes(*codes) end def codes(*codes) self.class.codes(*codes) end #--- def status=status meta.set(:status, status) end def status meta.get(:status, code.unknown_status) end #----------------------------------------------------------------------------- # Plugin operations def normalize(reload) # Implement in sub classes end #----------------------------------------------------------------------------- # Extensions def hook_method(hook) "#{plugin_type}_#{plugin_provider}_#{hook}" end #--- def extension(hook, options = {}, &code) Nucleon.exec(hook_method(hook), Config.ensure(options).import({ :plugin => myself }), &code) end #--- def extended_config(type, options = {}) config = Nucleon.config(type, Config.ensure(options).import({ :plugin => myself })) config.delete(:plugin) config end #--- def extension_check(hook, options = {}) Nucleon.check(hook_method(hook), Config.ensure(options).import({ :plugin => myself })) end #--- def extension_set(hook, value, options = {}) Nucleon.value(hook_method(hook), value, Config.ensure(options).import({ :plugin => myself })) end #--- def extension_collect(hook, options = {}) Nucleon.collect(hook_method(hook), Config.ensure(options).import({ :plugin => myself })) end #----------------------------------------------------------------------------- # Input def ask(message, options = {}) ui.ask(message, options) end #--- def password(type, options = {}) ui.password(type, options) end #----------------------------------------------------------------------------- # Output def render_provider plugin_provider end protected :render_provider #--- def render_options export.merge({ :plugin_namespace => self.class.respond_to?(:namespace) ? self.class.namespace : plugin_namespace, :plugin_type => plugin_type, :plugin_provider => render_provider }) end protected :render_options #--- def render_message(message, options = {}) config = Config.ensure(options) use_prefix = true if config.delete(:i18n, true) Nucleon.namespaces.each do |namespace| if message =~ /^#{namespace.to_s.downcase}\./ use_prefix = false break end end if use_prefix plugin_namespace = self.class.namespace if self.class.respond_to?(:namespace) operation_id = config.has_key?(:operation) ? config[:operation] : '' prefix = "#{plugin_namespace}.#{plugin_type}.#{render_provider.to_s.gsub('_', '.')}." message = prefix + ( operation_id.empty? ? '' : "#{operation_id}." ) + message.sub(/^#{prefix}/, '') end message = I18n.t(message, Util::Data.merge([ Config.ensure(render_options).export, config.export ], true)) end message end protected :render_message #--- def render(data, options = {}) config = Config.ensure(options) if ! quiet? || config[:silent] translator = nil translator = Nucleon.translator({}, config[:format]) if config[:format] data = translator.generate(data) if translator ui.dump(data, options) unless data.strip.empty? || config[:silent] end data end #--- def info(message, options = {}) config = Config.new(options).import({ :operation => :info }) unless quiet? message = render_message(message, config) ui.info(message, config.export) end message end #--- def warn(message, options = {}) config = Config.new(options).import({ :operation => :warn }) unless quiet? message = render_message(message, config) ui.warn(message, config.export) end message end #--- def error(message, options = {}) config = Config.new(options).import({ :operation => :error }) unless quiet? message = render_message(message, config) ui.error(message, config.export) end message end #--- def success(message, options = {}) config = Config.new(options).import({ :operation => :success }) unless quiet? message = render_message(message, config) ui.success(message, config.export) end message end #--- def prefixed_message(type, prefix, message, options = {}) return unless [ :info, :warn, :error, :success ].include?(type.to_sym) send(type, prefix.to_s + render_message(message, Config.new(options).import({ :prefix => false, :operation => type.to_sym }).export), Config.new(options).import({ :i18n => false }).export) end #----------------------------------------------------------------------------- # Utilities def self.build_info(namespace, plugin_type, data) plugins = [] if data.is_a?(Hash) data = [ data ] end logger.debug("Building plugin list of #{plugin_type}") if data.is_a?(Array) data.each do |info| unless Util::Data.empty?(info) info = translate(info) if Util::Data.empty?(info[:provider]) info[:provider] = Nucleon.type_default(namespace, plugin_type) end plugins << info end end end return plugins end #--- def self.translate(data) logger.debug("Translating input data to internal plugin structure") return ( data.is_a?(Hash) ? symbol_map(data) : data ) end #--- def self.translate_reference(reference, editable = false) # ex: provider:::name if reference && reference.match(/^\s*([a-zA-Z0-9_-]+)(?::::)?(.*)?\s*$/) provider = $1 name = $2 logger.debug("Translating plugin reference: #{provider} #{name}") info = { :provider => provider, :name => name } logger.debug("Plugin reference info: #{info.inspect}") return info end nil end #--- def translate_reference(reference, editable = false) myself.class.translate_reference(reference, editable) end #--- def self.init_plugin_collection(*external_block_methods) logger.debug("Initializing plugin collection interface at #{Time.now}") include Parallel external_block_exec(*external_block_methods) include Mixin::Settings include Mixin::SubConfig extend Mixin::Macro::PluginInterface end #--- def safe_exec(return_result = true, &code) begin result = code.call return result if return_result return true rescue => error logger.error(error.inspect) logger.error(error.message) error(error.message, { :prefix => false, :i18n => false }) if error.message end return false end #--- def admin_exec(return_result = true, &block) if Nucleon.admin? safe_exec(return_result, &block) else warn("The #{plugin_provider} action must be run as a machine administrator", { :i18n => false }) myself.status = code.access_denied end end end end end