class Nucleon::Manager
Plugin
manager¶ ↑
The Nucleon::Manager
class defines a manager for defined plugin types, loaded, metadata, and active instances.
One of the primary functions of the Nucleon
library is to provide a very flexible extensible architectural base for Ruby applications needing ready made modularity. To fulfill our objectives, the Nucleon
library defines plugin managers managed as a global multition.
These managers should be able to fail gracefully and recover to the state they left off if a plugin provider crashes. To accomplish this, each manager is a Celluloid actor that manages a globally defined environment (also within a multition). This environment contains all of the plugins and providers that they manager has registered and loaded.
See also:
Attributes
- Symbol
-
Plugin
manager identifier
Nucleon::Util::Logger
-
Instance logger
Public Class Methods
Return a specified plugin manager instance
-
Parameters
- String, Symbol
-
name Name of the plugin manager (actor id)
- Boolean
-
reset Whether or not to reinitialize the manager
-
Returns
Nucleon::Manager
, Celluloid::Actor-
Returns an interface to manage plugins
-
Errors
See:
# File lib/core/manager.rb 78 def self.connection(name = :core, reset = false) 79 Nucleon.manager(@@supervisors, name, self, reset) 80 end
Accessor for the global plugin manager environments (mostly for testing purposes)
# File lib/core/manager.rb 57 def self.environments 58 @@environments 59 end
Initialize a new Nucleon
environment
IMORTANT: The environment constructor should accept no parameters!
-
Parameters
- String, Symbol
-
actor_id Name of the plugin manager
- Boolean
-
reset Whether or not to reinitialize the manager
-
Returns
- Void
-
This method does not return a value
-
Errors
See also:
# File lib/core/manager.rb 99 def initialize(actor_id, reset) 100 @logger = Nucleon.logger 101 @actor_id = actor_id.to_sym 102 103 @provider_cache = {} 104 105 if reset || ! @@environments[@actor_id] 106 @@environments[@actor_id] = Environment.new 107 end 108 end
Accessor for the global supervisors (mostly for testing purposes)
# File lib/core/manager.rb 43 def self.supervisors 44 @@supervisors 45 end
Public Instance Methods
Return active plugins for namespaces, plugin types, providers if specified
-
Parameters
-
Returns
- nil,
Hash
<Symbol|Symbol|Symbol|Symbol|Nucleon::Plugin::Base> -
Returns all plugin instances if no parameters given
- nil,
- nil,
Hash
<Symbol|Symbol|Symbol|Nucleon::Plugin::Base> -
Returns namespace plugin instances if only namespace given
- nil,
- nil,
Hash
<Symbol|Symbol|Nucleon::Plugin::Base> -
Returns plugin type instances if namespace and plugin type given
- nil,
- nil,
Hash
<Symbol|Nucleon::Plugin::Base> -
Returns provider instances if namespace, plugin type, and provider given
- nil,
-
Errors
See:
# File lib/core/manager.rb 400 def active_plugins(namespace = nil, plugin_type = nil, provider = nil) 401 @@environments[@actor_id].active_plugins(namespace, plugin_type, provider) 402 end
Autoload all of the defined plugins
-
Parameters
-
Returns
- Void
-
This method does not return a value
-
Errors
See:
See also:
# File lib/core/manager.rb 506 def autoload 507 logger.info("Autoloading registered plugins at #{Time.now}") 508 509 @@environments[@actor_id].autoload do |namespace, plugin_type, provider, plugin| 510 logger.debug("Autoloading provider #{provider} at #{plugin[:directory]}") 511 512 # Make sure extensions are listening from the time they are loaded 513 if plugin[:namespace] == :nucleon && plugin_type == :extension 514 logger.debug("Creating #{plugin_type} #{provider}") 515 516 # Create a persistent instance 517 load(plugin[:namespace], :extension, provider, { :name => provider }) 518 end 519 end 520 end
# File lib/core/manager.rb 717 def check(method, options = {}) 718 config = Config.ensure(options) 719 720 logger.info("Checking extension #{method}") 721 722 success = exec(method, config.import({ :extension_type => :check })) do |op, data| 723 if op == :reduce 724 ! data.values.include?(false) 725 else 726 data ? true : false 727 end 728 end 729 730 success = success.nil? || success ? true : false 731 732 logger.debug("Extension #{method} check result: #{success.inspect}") 733 success 734 end
Return a fully formed class name as a machine usable constant
-
Parameters
- String, Symbol, Array
-
name Class name components
- String, Symbol
-
separator Class component separator (default ‘::’)
-
Returns
- Class Constant
-
Returns class constant for fully formed class name of given components
-
Errors
See:
# File lib/core/manager.rb 826 def class_const(name, separator = '::') 827 @@environments[@actor_id].class_const(name, separator) 828 end
Return a fully formed class name as a string
-
Parameters
- String, Symbol, Array
-
name Class name components
- String, Symbol
-
separator Class component separator (default ‘::’)
- Boolean
-
want_array Whether or not to return array of final components or string version
-
Returns
- String
-
Returns fully rendered class name as string unless want_array is true
- Array
-
Returns array of final class components if want_array is true
-
Errors
See:
# File lib/core/manager.rb 808 def class_name(name, separator = '::', want_array = false) 809 @@environments[@actor_id].class_name(name, separator, want_array) 810 end
# File lib/core/manager.rb 751 def collect(method, options = {}) 752 config = Config.ensure(options) 753 values = [] 754 755 logger.info("Collecting extension #{method} values") 756 757 exec(method, config.import({ :extension_type => :collect })) do |op, data| 758 if op == :process 759 values << data unless data.nil? 760 end 761 end 762 values = values.flatten 763 764 logger.debug("Extension #{method} collected values: #{values.inspect}") 765 values 766 end
# File lib/core/manager.rb 698 def config(type, options = {}) 699 config = Config.ensure(options) 700 701 logger.info("Generating #{type} extended configuration") 702 703 exec("#{type}_config", Config.new(config.export, {}, true, false)) do |op, data| 704 if op == :reduce 705 data.each do |provider, result| 706 config.import(result) 707 end 708 nil 709 else 710 data 711 end 712 end 713 config.delete(:extension_type) 714 config 715 end
# File lib/core/manager.rb 603 def create(namespace, plugin_type, provider, options = {}) 604 @@environments[@actor_id].create_plugin(namespace, plugin_type, provider, options) do |type_info, plugin_options| 605 logger.info("Creating new plugin #{provider} #{plugin_type}") 606 translate(type_info, plugin_options) 607 end 608 end
Define a new plugin provider of a specified plugin type.
-
Parameters
- String, Symbol
-
namespace Namespace that contains plugin types
- String, Symbol
-
plugin_type
Plugin
type name to fetch default provider
- String
-
base_path Base load path of the plugin provider
- String
-
file File that contains the provider definition
-
Returns
Nucleon::Manager
, Celluloid::Actor-
Returns reference to self for compound operations
-
Errors
-
Yields
See:
# File lib/core/manager.rb 357 def define_plugin(namespace, plugin_type, base_path, file, &code) # :yields: data 358 @@environments[@actor_id].define_plugin(namespace, plugin_type, base_path, file, &code) 359 myself 360 end
Define a new plugin type in a specified namespace.
-
Parameters
- String, Symbol
-
namespace Namespace that contains plugin types
- String, Symbol
-
plugin_type
Plugin
type name within namespace
- String, Symbol
-
default_provider Default provider
-
Returns
Nucleon::Manager
, Celluloid::Actor-
Returns reference to self for compound operations
-
Errors
See:
# File lib/core/manager.rb 237 def define_type(namespace, plugin_type, default_provider) 238 @@environments[@actor_id].define_plugin_type(namespace, plugin_type, default_provider) 239 myself 240 end
Define one or more new plugin types in a specified namespace.
-
Parameters
-
Returns
Nucleon::Manager
, Celluloid::Actor-
Returns reference to self for compound operations
-
Errors
See:
# File lib/core/manager.rb 256 def define_types(namespace, type_info) 257 @@environments[@actor_id].define_plugin_types(namespace, type_info) 258 end
*****************************************************************************
Extension hook execution
# File lib/core/manager.rb 658 def exec(method, options = {}) 659 results = nil 660 661 logger.info("Executing extension hook #{Nucleon.blue(method)} at #{Nucleon.green(Time.now.to_s)}") 662 663 extensions = Util::Data.clone(active_plugins(:nucleon, :extension)) 664 665 extensions.each do |name, plugin| 666 provider = plugin.plugin_provider 667 result = nil 668 669 logger.info("Checking extension #{provider}") 670 671 if plugin.respond_to?(method) 672 results = {} if results.nil? 673 674 result = plugin.send(method, options) 675 logger.info("Completed hook #{method} at #{Time.now}") 676 677 if block_given? 678 results[provider] = yield(:process, result) 679 logger.debug("Processed extension result into: #{results[provider].inspect}") 680 end 681 682 if results[provider].nil? 683 logger.debug("Setting extension result to: #{result.inspect}") 684 results[provider] = result 685 end 686 end 687 end 688 689 if ! results.nil? && block_given? 690 results = yield(:reduce, results) 691 logger.debug("Reducing extension results to: #{results.inspect}") 692 else 693 logger.debug("Final extension results: #{results.inspect}") 694 end 695 results 696 end
Return a plugin instance by name if it exists
-
Parameters
-
Returns
- nil,
Nucleon::Plugin::Base
-
Returns a plugin instance of name specified if it exists
- nil,
-
Errors
See:
# File lib/core/manager.rb 626 def get(namespace, plugin_type, plugin_name) 627 @@environments[@actor_id].get_plugin(namespace, plugin_type, plugin_name) 628 end
# File lib/core/manager.rb 559 def load(namespace, plugin_type, provider = nil, options = {}) 560 default_provider = type_default(namespace, plugin_type) 561 562 # Allow options to override provider 563 config = Config.ensure(options) 564 provider = config.delete(:provider, provider) 565 provider = default_provider unless provider 566 567 provider_data = Util::Data.merge([ config.export, { 568 :namespace => namespace, 569 :type => plugin_type 570 }]) 571 provider_id = Nucleon.sha1(Util::Data.merge([ provider_data, { :provider => provider }])) 572 573 if @provider_cache.has_key?(provider_id) 574 provider = @provider_cache[provider_id] 575 else 576 provider = value(:manager_plugin_provider, provider, provider_data) 577 @provider_cache[provider_id] = provider 578 end 579 580 load_base(namespace, plugin_type, provider, config) 581 end
*****************************************************************************
Plugin workflow
# File lib/core/manager.rb 526 def load_base(namespace, plugin_type, provider, options = {}) 527 logger.info("Fetching plugin #{namespace} #{plugin_type} provider #{provider} at #{Time.now}") 528 529 type_info = loaded_plugin(namespace, plugin_type, provider) 530 options = translate_type(type_info, options) 531 config = Config.ensure(options) 532 533 provider = config.get(:provider, provider) 534 name = config.get(:name, nil) 535 ensure_new = config.get(:new, false) 536 537 if name 538 logger.debug("Looking up existing instance of #{name}") 539 540 if existing_instance = get(namespace, plugin_type, name) 541 unless ensure_new 542 config.delete(:new) 543 544 config.export.each do |property_name, value| 545 unless [ :name, :meta ].include?(property_name) 546 existing_instance[property_name] = value 547 end 548 end 549 existing_instance.normalize(true) 550 551 logger.debug("Using existing instance of #{plugin_type}, #{name}") 552 return existing_instance 553 end 554 end 555 end 556 create(namespace, plugin_type, provider, config) 557 end
# File lib/core/manager.rb 583 def load_multiple(namespace, plugin_type, data, build_hash = false, keep_array = false) 584 logger.info("Fetching multiple plugins of #{plugin_type} at #{Time.now}") 585 586 group = ( build_hash ? {} : [] ) 587 klass = plugin_class(namespace, plugin_type) 588 data = klass.build_info(namespace, plugin_type, data) if klass.respond_to?(:build_info) 589 590 data.each do |options| 591 if plugin = load(namespace, plugin_type, options[:provider], options) 592 if build_hash 593 group[plugin.plugin_name] = plugin 594 else 595 group << plugin 596 end 597 end 598 end 599 return group.shift if ! build_hash && group.length == 1 && ! keep_array 600 group 601 end
Return the load information for a specified plugin provider if it exists
-
Parameters
-
Returns
- nil,
Hash
<Symbol|ANY> -
Returns provider load information if provider exists, nil otherwise
- nil,
-
Errors
See:
# File lib/core/manager.rb 311 def loaded_plugin(namespace, plugin_type, provider) 312 @@environments[@actor_id].loaded_plugin(namespace, plugin_type, provider) 313 end
Return the load information for namespaces, plugin types, providers if it exists
-
Parameters
-
Returns
- nil,
Hash
<Symbol|Symbol|Symbol|Symbol|ANY> -
Returns all load information if no parameters given
- nil,
- nil,
Hash
<Symbol|Symbol|Symbol|ANY> -
Returns namespace load information if only namespace given
- nil,
- nil,
Hash
<Symbol|Symbol|ANY> -
Returns plugin type load information if namespace and plugin type given
- nil,
- nil,
Hash
<Symbol|ANY> -
Returns provider load information if namespace, plugin type, and provider given
- nil,
-
Errors
See:
# File lib/core/manager.rb 333 def loaded_plugins(namespace = nil, plugin_type = nil, provider = nil) 334 @@environments[@actor_id].loaded_plugins(namespace, plugin_type, provider) 335 end
Return a reference to self
This is needed so we can wrap instances in Celluloid actor proxies. Using self directly causes a lot of headaches.
-
Parameters
-
Returns
Nucleon::Manager
, Celluloid::Actor-
Returns a reference to this manager instance
-
Errors
See also:
# File lib/core/manager.rb 162 def myself 163 Nucleon.handle(self) 164 end
Return all of the defined namespaces in the plugin environment.
-
Parameters
-
Returns
- Array<Symbol>
-
Array of defined plugin namespaces
-
Errors
See:
# File lib/core/manager.rb 201 def namespaces 202 @@environments[@actor_id].namespaces 203 end
Perform any cleanup operations during manager shutdown
This only runs when in parallel mode.
-
Parameters
-
Returns
- Void
-
This method does not return a value
-
Errors
See also:
# File lib/core/manager.rb 125 def parallel_finalize 126 active_plugins.each do |namespace, namespace_plugins| 127 namespace_plugins.each do |plugin_type, type_plugins| 128 type_plugins.each do |instance_name, plugin| 129 remove(plugin) 130 end 131 end 132 end 133 end
Return a class constant representing a base plugin class generated from namespace and plugin_type.
-
Parameters
-
Returns
- String
-
Returns a class constant representing the plugin namespace and type
-
Errors
See:
# File lib/core/manager.rb 844 def plugin_class(namespace, plugin_type) 845 @@environments[@actor_id].plugin_class(namespace, plugin_type) 846 end
Check if a specified plugin provider has been loaded
-
Parameters
-
Returns
- Boolean
-
Returns true if plugin provider has been loaded, false otherwise
-
Errors
See:
# File lib/core/manager.rb 377 def plugin_has_provider?(namespace, plugin_type, provider) 378 @@environments[@actor_id].plugin_has_provider?(namespace, plugin_type, provider) 379 end
Return a class constant representing a plugin provider class generated from namespace, plugin_type, and provider name.
The provider name can be entered as an array if it is included in sub modules.
-
Parameters
-
Returns
- String
-
Returns a class constant representing the plugin provider
-
Errors
See also:
-
sanitize_class
# File lib/core/manager.rb 867 def provider_class(namespace, plugin_type, provider) 868 @@environments[@actor_id].provider_class(namespace, plugin_type, provider) 869 end
# File lib/core/manager.rb 452 def register(base_path, &code) 453 namespaces.each do |namespace| 454 namespace_path = File.join(base_path, namespace.to_s) 455 register_namespace(namespace, namespace_path, &code) 456 end 457 end
*****************************************************************************
Plugin registration / initialization
# File lib/core/manager.rb 407 def reload(core = false, loaded = [], &code) 408 logger.info("Loading Nucleon plugins at #{Time.now}") 409 410 if core 411 Celluloid.logger = logger if Nucleon.parallel? 412 413 define_types :nucleon, { 414 :extension => nil, # Core 415 :action => :project_update, # Core 416 :project => :git, # Core 417 :command => :bash, # Core 418 :event => :regex, # Utility 419 :template => :json, # Utility 420 :translator => :json # Utility 421 } 422 end 423 424 # Allow block level namespace and type registration 425 code.call(:define, myself) if code 426 427 load_plugins(core, loaded, &code) 428 logger.info("Finished loading Nucleon plugins at #{Time.now}") 429 end
# File lib/core/manager.rb 646 def remove(plugin) 647 begin # TODO: Figure out what do do about the plugin proxy being terminated before respond_to? method called. 648 if plugin && plugin.respond_to?(:plugin_type) 649 remove_by_name(plugin.plugin_namespace, plugin.plugin_type, plugin.plugin_instance_name) 650 end 651 rescue 652 end 653 end
# File lib/core/manager.rb 630 def remove_by_name(namespace, plugin_type, plugin_instance_name) 631 active_instances = active_plugins(namespace, plugin_type) 632 633 if active_instances.has_key?(plugin_instance_name) 634 @@environments[@actor_id].remove_plugin(namespace, plugin_type, plugin_instance_name) do |plugin| 635 logger.debug("Removing #{plugin_type} #{plugin_instance_name}") 636 637 if plugin.respond_to?(:terminate) # For Celluloid plugins 638 plugin.terminate 639 else 640 plugin.remove_plugin 641 end 642 end 643 end 644 end
Return true as a test method for checking for running manager
This method should always return true and is only really called internally to perform a system check when running in parallel mode.
-
Parameters
-
Returns
Nucleon::Manager
, Celluloid::Actor-
Returns a reference to this manager instance
-
Errors
See also:
# File lib/core/manager.rb 182 def test_connection 183 true 184 end
# File lib/core/manager.rb 781 def translate(type_info, options) 782 if type_info 783 klass = type_info[:class] 784 785 logger.debug("Executing option translation for: #{klass.inspect}") 786 787 options = klass.send(:translate, options) if klass.respond_to?(:translate) 788 end 789 options 790 end
*****************************************************************************
Utilities
# File lib/core/manager.rb 771 def translate_type(type_info, options) 772 if type_info 773 klass = plugin_class(type_info[:namespace], type_info[:type]) 774 logger.debug("Executing option translation for: #{klass.inspect}") 775 776 options = klass.send(:translate, options) if klass.respond_to?(:translate) 777 end 778 options 779 end
Return the default provider currently registered for a plugin type
-
Parameters
- String, Symbol
-
namespace Namespace that contains plugin types
- String, Symbol
-
plugin_type
Plugin
type name to fetch default provider
-
Returns
- nil, Symbol
-
Returns default provider if plugin type exists, nil otherwise
-
Errors
See:
# File lib/core/manager.rb 292 def type_default(namespace, plugin_type) 293 @@environments[@actor_id].plugin_type_default(namespace, plugin_type) 294 end
Check if a specified plugin type has been defined
-
Parameters
- String, Symbol
-
namespace Namespace that contains plugin types
- String, Symbol
-
plugin_type
Plugin
type name to check within namespace
-
Returns
- Boolean
-
Returns true if plugin type exists, false otherwise
-
Errors
See:
-
Nucleon::Environment#plugin_type_defined
# File lib/core/manager.rb 274 def type_defined?(namespace, plugin_type) 275 @@environments[@actor_id].plugin_type_defined?(namespace, plugin_type) 276 end
Return all of the defined plugin types in a plugin namespace.
-
Parameters
- String, Symbol
-
namespace Namespace that contains plugin types
-
Returns
- Array<Symbol>
-
Array of defined plugin types
-
Errors
See:
# File lib/core/manager.rb 218 def types(namespace) 219 @@environments[@actor_id].plugin_types(namespace) 220 end
# File lib/core/manager.rb 736 def value(method, value, options = {}) 737 config = Config.ensure(options) 738 739 logger.info("Setting extension #{method} value") 740 741 exec(method, config.import({ :value => value, :extension_type => :value })) do |op, data| 742 if op == :process 743 value = data unless data.nil? 744 end 745 end 746 747 logger.debug("Extension #{method} retrieved value: #{value.inspect}") 748 value 749 end
Protected Instance Methods
# File lib/core/manager.rb 431 def load_plugins(core = false, loaded = [], &code) 432 if core 433 # Register core plugins 434 logger.info("Initializing core plugins at #{Time.now}") 435 register(File.join(File.dirname(__FILE__), '..')) 436 end 437 438 # Register external Gem defined plugins 439 Gems.register(true, Util::Data.array(loaded)) 440 441 # Register any other extension plugins 442 exec(:register_plugins) 443 444 # Catch any block level requests before autoloading 445 code.call(:load, myself) if code 446 447 # Autoload all registered plugins 448 autoload 449 end
# File lib/core/manager.rb 459 def register_namespace(namespace, base_path, &code) 460 if File.directory?(base_path) 461 logger.info("Loading files from #{base_path} at #{Time.now}") 462 463 Dir.glob(File.join(base_path, '*.rb')).each do |file| 464 logger.debug("Loading file: #{file}") 465 require file 466 end 467 468 logger.info("Loading directories from #{base_path} at #{Time.now}") 469 Dir.entries(base_path).each do |path| 470 unless path.match(/^\.\.?$/) 471 register_type(namespace, base_path, path, &code) if type_defined?(namespace, path) 472 end 473 end 474 end 475 end
# File lib/core/manager.rb 478 def register_type(namespace, base_path, plugin_type, &code) 479 base_directory = File.join(base_path, plugin_type.to_s) 480 481 if File.directory?(base_directory) 482 logger.info("Registering #{base_directory} at #{Time.now}") 483 484 Dir.glob(File.join(base_directory, '**', '*.rb')).each do |file| 485 define_plugin(namespace, plugin_type, base_directory, file, &code) 486 end 487 end 488 end