module Boson # Raised if a library has methods which conflict with existing methods class MethodConflictError < LoaderError MESSAGE = "The following commands conflict with existing commands: %s" def initialize(conflicts) super MESSAGE % conflicts.join(', ') end end # This module is mixed into Library to give it load() functionality. When # creating your own Library subclass, you should at least override # load_source_and_set_module. module Loader # Loads a library and its dependencies and returns true if library loads # correctly. def load load_source_and_set_module module_callbacks if @module yield if block_given? # load dependencies detect_additions { load_commands } if load_commands? set_library_commands loaded_correctly? && (@loaded = true) end # Method hook at the beginning of #load. This method should load the source # and set instance variables necessary to make a library valid i.e. @module. def load_source_and_set_module; end # Method hook for @module before loading def module_callbacks; end # Determines if load_commands should be called def load_commands? @module end # Wraps around module loading for unexpected additions def detect_additions(options={}, &block) Util.detect(options, &block).tap do |detected| @commands.concat detected[:methods].map(&:to_s) end end # Prepares for command loading, loads commands and rescues certain errors. def load_commands @module = @module ? Util.constantize(@module) : Util.create_module(Boson::Commands, clean_name) before_load_commands check_for_method_conflicts unless @force actual_load_commands rescue MethodConflictError => err handle_method_conflict_error err end # Boolean which indicates if library loaded correctly. def loaded_correctly? !!@module end # Method hook for @module after it's been included def after_include; end # called when MethodConflictError is rescued def handle_method_conflict_error(err) raise err end # Method hook called after @module has been created def before_load_commands; end # Actually includes module and its commands def actual_load_commands include_in_universe end # Returns array of method conflicts def method_conflicts (@module.instance_methods + @module.private_instance_methods) & (Boson.main_object.methods + Boson.main_object.private_methods) end # Handles setting and cleaning @commands def set_library_commands clean_library_commands end # Cleans @commands from set_library_commands def clean_library_commands aliases = @commands_hash.select {|k,v| @commands.include?(k) }. map {|k,v| v[:alias] }.compact @commands -= aliases @commands.uniq! end private def include_in_universe(lib_module=@module) Boson::Universe.send :include, lib_module after_include Boson::Universe.send :extend_object, Boson.main_object end def check_for_method_conflicts conflicts = method_conflicts raise MethodConflictError.new(conflicts) unless conflicts.empty? end end end