module Boson # A library is a group of commands (Command objects) usually grouped together by a module. # Libraries are loaded from different sources depending on the library subclass. Default library # subclasses are FileLibrary, GemLibrary, RequireLibrary and ModuleLibrary. # # To create your own subclass you need to define what sources the subclass can handle with handles(). # If handles() returns true then the subclass is chosen to load. See Loader to see what instance methods # to override for a subclass. class Library include Loader class <<self #:stopdoc: attr_accessor :handle_blocks def handles(&block) (Library.handle_blocks ||= []) << [self,block] end #:startdoc: end # Public attributes for use outside of Boson. ATTRIBUTES = [:gems, :dependencies, :commands, :loaded, :module, :name, :namespace] attr_reader *(ATTRIBUTES + [:commands_hash, :library_file, :object_namespace]) # Private attribute for use within Boson. attr_reader :except, :no_alias_creation, :new_module, :new_commands # Optional namespace name for a library. When enabled defaults to a library's name. attr_writer :namespace # Creates a library object with a hash of attributes which must include a :name attribute. # Each hash pair maps directly to an instance variable and value. Defaults for attributes # are read from config[:libraries][@library_name][@attribute]. # # Attributes that can be configured: # * *:dependencies*: An array of libraries that this library depends on. A library won't load # unless its dependencies are loaded first. # * *:commands*: A hash or array of commands that belong to this library. A hash configures command attributes # for the given commands with command names pointing to their configs. See Command.new for a # command's configurable attributes. If an array, the commands are set for the given library, # overidding default command detection. # Example: # :commands=>{'commands'=>{:description=>'Lists commands', :alias=>'com'}} # * *:class_commands*: A hash of commands to create. Hash should map command names to any string of ruby code # that ends with a method call. # Example: # :class_commands=>{'spy'=>'Bond.spy', 'create'=>'Alias.manager.create'} # * *:force*: Boolean which forces a library to ignore when a library's methods are overriding existing ones. # Use with caution. Default is false. # * *:object_methods*: Boolean which detects any Object/Kernel methods created when loading a library and automatically # adds them to a library's commands. Default is true. # * *:namespace*: Boolean or string which namespaces a library. When true, the library is automatically namespaced # to the library's name. When a string, the library is namespaced to the string. Default is nil. To control the # namespacing of all libraries see Boson::Repo.config. def initialize(hash) @name = set_name hash.delete(:name) @loaded = false repo = set_repo @repo_dir = repo.dir @commands_hash = {} @commands = [] set_config (repo.config[:libraries][@name] || {}).merge(hash) set_command_aliases(repo.config[:command_aliases]) @namespace = true if Boson.repo.config[:auto_namespace] && @namespace.nil? && !Boson::Runner.default_libraries.include?(@module) @namespace = clean_name if @namespace end # A concise symbol version of a library type i.e. FileLibrary -> :file. def library_type str = self.class.to_s[/::(\w+)Library$/, 1] || 'library' str.downcase.to_sym end # The object a library uses for executing its commands. def namespace_object @namespace_object ||= @namespace ? Boson.invoke(@namespace) : Boson.main_object end #:stopdoc: # handles names under directories def clean_name @name[/\w+$/] end def set_name(name) name.to_s or raise ArgumentError, "New library missing required key :name" end def set_config(config) if (commands = config.delete(:commands)) if commands.is_a?(Array) @commands += commands @pre_defined_commands = true elsif commands.is_a?(Hash) @commands += commands.keys @commands_hash = Util.recursive_hash_merge commands, @commands_hash end end set_command_aliases config.delete(:command_aliases) if config[:command_aliases] set_attributes config, true end def set_command_aliases(command_aliases) (command_aliases || {}).each do |cmd, cmd_alias| @commands_hash[cmd] ||= {} @commands_hash[cmd][:alias] ||= cmd_alias end end def set_repo Boson.repo end def set_attributes(hash, force=false) hash.each {|k,v| instance_variable_set("@#{k}", v) if instance_variable_get("@#{k}").nil? || force } end def command_objects(names) Boson.commands.select {|e| names.include?(e.name) && e.lib == self.name } end def marshal_dump [@name, @commands, @gems, @module.to_s, @repo_dir] end def marshal_load(ary) @name, @commands, @gems, @module, @repo_dir = ary end #:startdoc: end end