lib/boson/manager.rb in boson-0.4.0 vs lib/boson/manager.rb in boson-1.0.0
- old
+ new
@@ -1,169 +1,165 @@
module Boson
- # Base class for library loading errors. Raised mostly in Boson::Loader and rescued by Boson::Manager.
+ # Base class for library loading errors. Raised mostly in Boson::Loader and
+ # rescued by Boson::Manager.
class LoaderError < StandardError; end
- # Raised when a library's append_features returns false.
- class AppendFeaturesFalseError < StandardError; end
# Handles loading of libraries and commands.
class Manager
- class <<self
- attr_accessor :failed_libraries
+ # Loads a library or an array of libraries with options. Manager loads the
+ # first library subclass to return true for Library#handles. Any options
+ # that aren't listed here are passed as library attributes to the libraries
+ # (see Library.new)
+ #
+ # @param [Hash] options
+ # @option options [Boolean] :verbose Prints each library's loaded status
+ # along with more verbose errors. Default is false.
+ # @example Manager.load MyRunner
+ def self.load(libraries, options={})
+ instance.load(libraries, options)
+ end
- # Loads a library or an array of libraries with options. Manager loads the first library subclass
- # to meet a library subclass' criteria in this order: ModuleLibrary, FileLibrary, GemLibrary, RequireLibrary.
- # ==== Examples:
- # Manager.load 'my_commands' -> Loads a FileLibrary object from ~/.boson/commands/my_commands.rb
- # Manager.load 'method_lister' -> Loads a GemLibrary object which requires the method_lister gem
- # Any options that aren't listed here are passed as library attributes to the libraries (see Library.new)
- # ==== Options:
- # [:verbose] Boolean to print each library's loaded status along with more verbose errors. Default is false.
- def load(libraries, options={})
- Array(libraries).map {|e|
- (@library = load_once(e, options)) ? after_load : false
- }.all?
- end
+ class <<self; attr_accessor :instance; end
- #:stopdoc:
- def failed_libraries
- @failed_libraries ||= []
- end
+ def self.instance
+ @instance ||= new
+ end
- def add_library(lib)
- Boson.libraries.delete(Boson.library(lib.name))
- Boson.libraries << lib
- end
+ # Adds a library to Boson.libraries
+ def self.add_library(lib)
+ Boson.libraries.delete(Boson.library(lib.name))
+ Boson.libraries << lib
+ end
- def loaded?(lib_name)
- ((lib = Boson.library(lib_name)) && lib.loaded) ? true : false
- end
+ # Given a library name, determines if it's loaded
+ def self.loaded?(lib_name)
+ ((lib = Boson.library(lib_name)) && lib.loaded) ? true : false
+ end
- def rescue_load_action(library, load_method)
- yield
- rescue AppendFeaturesFalseError
- warn "DEBUG: Library #{library} didn't load due to append_features" if Runner.debug
- rescue LoaderError=>e
- FileLibrary.reset_file_cache(library.to_s)
- failed_libraries << library
- $stderr.puts "Unable to #{load_method} library #{library}. Reason: #{e.message}"
- rescue StandardError, SyntaxError, LoadError =>e
- FileLibrary.reset_file_cache(library.to_s)
- failed_libraries << library
- message = "Unable to #{load_method} library #{library}. Reason: #{$!}"
- if Runner.debug
- message += "\n" + e.backtrace.map {|e| " " + e }.join("\n")
- elsif @options[:verbose]
- message += "\n" + e.backtrace.slice(0,3).map {|e| " " + e }.join("\n")
- end
- $stderr.puts message
- ensure
- Inspector.disable if Inspector.enabled
- end
+ attr_accessor :failed_libraries, :verbose
+ def initialize
+ @failed_libraries = []
+ end
- def load_once(source, options={})
- @options = options
- rescue_load_action(source, :load) do
- lib = loader_create(source)
- if loaded?(lib.name)
- $stderr.puts "Library #{lib.name} already exists." if options[:verbose] && !options[:dependency]
- false
- else
- if lib.load { load_dependencies(lib, options) }
- lib
- else
- $stderr.puts "Library #{lib.name} did not load successfully." if !options[:dependency]
- $stderr.puts " "+lib.inspect if Runner.debug
- false
- end
- end
- end
- end
+ # Loads libraries
+ def load(libraries, options={})
+ Array(libraries).map {|e|
+ (@library = load_once(e, options)) ? after_load : false
+ }.all?
+ end
- def lib_dependencies
- @lib_dependencies ||= {}
- end
+ # Adds a library to the failed list
+ def add_failed_library(library)
+ failed_libraries << library
+ end
- def load_dependencies(lib, options={})
- lib_dependencies[lib] = Array(lib.dependencies).map do |e|
- next if loaded?(e)
- load_once(e, options.merge(:dependency=>true)) ||
- raise(LoaderError, "Can't load dependency #{e}")
- end.compact
- end
+ # Called after a library is loaded
+ def after_load
+ create_commands(@library)
+ self.class.add_library(@library)
+ puts "Loaded library #{@library.name}" if verbose
+ during_after_load
+ true
+ end
- def loader_create(source)
- lib_class = Library.handle_blocks.find {|k,v| v.call(source) } or raise(LoaderError, "Library #{source} not found.")
- lib_class[0].new(@options.merge(:name=>source))
+ # Redefines commands
+ def redefine_commands(lib, commands)
+ option_commands = lib.command_objects(commands).select(&:option_command?)
+ accepted, rejected = option_commands.partition {|e|
+ e.args(lib) || e.arg_size }
+ if verbose && rejected.size > 0
+ puts "Following commands cannot have options until their arguments " +
+ "are configured: " + rejected.map {|e| e.name}.join(', ')
end
+ accepted.each {|cmd| Scientist.redefine_command(lib.namespace_object, cmd) }
+ end
- def after_load
- create_commands(@library)
- add_library(@library)
- puts "Loaded library #{@library.name}" if @options[:verbose]
- (lib_dependencies[@library] || []).each do |e|
- create_commands(e)
- add_library(e)
- puts "Loaded library dependency #{e.name}" if @options[:verbose]
- end
- true
- end
+ module API
+ # Method hook for loading dependencies or anything else before loading
+ # a library
+ def load_dependencies(lib, options); end
- def before_create_commands(lib)
- lib.is_a?(FileLibrary) && lib.module && Inspector.add_method_data_to_library(lib)
- end
+ # Method hook in middle of after_load
+ def during_after_load; end
- def create_commands(lib, commands=lib.commands)
- before_create_commands(lib)
- commands.each {|e| Boson.commands << Command.create(e, lib)}
- create_command_aliases(lib, commands) if commands.size > 0 && !lib.no_alias_creation
- redefine_commands(lib, commands)
- end
-
- def redefine_commands(lib, commands)
- option_commands = lib.command_objects(commands).select {|e| e.option_command? }
- accepted, rejected = option_commands.partition {|e| e.args(lib) || e.arg_size }
- if @options[:verbose] && rejected.size > 0
- puts "Following commands cannot have options until their arguments are configured: " +
- rejected.map {|e| e.name}.join(', ')
+ # Method hook called before create_commands
+ def before_create_commands(lib)
+ if lib.is_a?(RunnerLibrary) && lib.module
+ Inspector.add_method_data_to_library(lib)
end
- accepted.each {|cmd| Scientist.redefine_command(lib.namespace_object, cmd) }
end
- def create_command_aliases(lib, commands)
- lib.module ? prep_and_create_instance_aliases(commands, lib.module) : check_for_uncreated_aliases(lib, commands)
- end
+ # Method hook called after create_commands
+ def after_create_commands(lib, commands); end
- def prep_and_create_instance_aliases(commands, lib_module)
- aliases_hash = {}
- select_commands = Boson.commands.select {|e| commands.include?(e.name)}
- select_commands.each do |e|
- if e.alias
- aliases_hash[lib_module.to_s] ||= {}
- aliases_hash[lib_module.to_s][e.name] = e.alias
+ # Handles an error from a load action
+ def handle_load_action_error(library, load_method, err)
+ case err
+ when LoaderError
+ add_failed_library library
+ warn "Unable to #{load_method} library #{library}. Reason: #{err.message}"
+ else
+ add_failed_library library
+ message = "Unable to #{load_method} library #{library}. Reason: #{err}"
+ if Boson.debug
+ message << "\n" + err.backtrace.map {|e| " " + e }.join("\n")
+ elsif verbose
+ message << "\n" + err.backtrace.slice(0,3).map {|e| " " + e }.join("\n")
end
+ warn message
end
- create_instance_aliases(aliases_hash)
end
+ end
+ include API
- def create_instance_aliases(aliases_hash)
- Alias.manager.create_aliases(:instance_method, aliases_hash)
- end
+ private
+ def call_load_action(library, load_method)
+ yield
+ rescue StandardError, SyntaxError, LoadError => err
+ handle_load_action_error(library, load_method, err)
+ ensure
+ Inspector.disable if Inspector.enabled
+ end
- def create_class_aliases(mod, class_commands)
- class_commands.dup.each {|k,v|
- if v.is_a?(Array)
- class_commands.delete(k).each {|e| class_commands[e] = "#{k}.#{e}"}
+ def load_once(source, options={})
+ self.verbose = options[:verbose]
+
+ call_load_action(source, :load) do
+ lib = loader_create(source, options)
+ if self.class.loaded?(lib.name)
+ if verbose && !options[:dependency]
+ warn "Library #{lib.name} already exists."
end
- }
- Alias.manager.create_aliases(:any_to_instance_method, mod.to_s=>class_commands.invert)
+ false
+ else
+ actual_load_once lib, options
+ end
end
+ end
- def check_for_uncreated_aliases(lib, commands)
- return if lib.is_a?(GemLibrary)
- if (found_commands = Boson.commands.select {|e| commands.include?(e.name)}) && found_commands.find {|e| e.alias }
- $stderr.puts "No aliases created for library #{lib.name} because it has no module"
+ def actual_load_once(lib, options)
+ if lib.load { load_dependencies(lib, options) }
+ lib
+ else
+ if !options[:dependency]
+ warn "Library #{lib.name} did not load successfully."
end
+ warn " "+lib.inspect if Boson.debug
+ false
end
- #:startdoc:
+ end
+
+ def loader_create(source, options)
+ options = options.dup.tap {|h| h.delete(:verbose) }
+ lib_class = Library.handle_blocks.find {|k,v| v.call(source) } or
+ raise(LoaderError, "Library #{source} not found.")
+ lib_class[0].new(options.merge(name: source))
+ end
+
+ def create_commands(lib, commands=lib.commands)
+ before_create_commands(lib)
+ commands.each {|e| Boson.commands << Command.create(e, lib)}
+ after_create_commands(lib, commands)
+ redefine_commands(lib, commands)
end
end
end