module Conify::Command
Constants
- CMD_BLACKLIST
Public Instance Methods
# File lib/conify/command.rb, line 170 def command_description(command_info_module) command_info_module.const_defined?('DESCRIPTION') ? command_info_module::DESCRIPTION : '' end
Feturn an array of all command file paths, with the exception of abstract_command.rb
# File lib/conify/command.rb, line 267 def command_file_paths abstract_file = File.join(File.dirname(__FILE__), 'command', 'abstract_command.rb') Dir[File.join(File.dirname(__FILE__), 'command', '*.rb')] - [abstract_file] end
# File lib/conify/command.rb, line 166 def command_valid_args(command_info_module) command_info_module.const_defined?('VALID_ARGS') ? command_info_module::VALID_ARGS : [] end
# File lib/conify/command.rb, line 272 def commands @@commands ||= {} end
Create a commands map to respond to `conify help` with.
# File lib/conify/command.rb, line 196 def create_commands_map # Require all the ruby command files command_file_paths.each do |file| require file # Get basename for the file without the extension basename = get_basename_from_file(file) # Camelcase the basename to be the klass name klass_name = camelize(basename) # return the command klass for this klass_name command_klass = Conify::Command.const_get(klass_name) # For each of the user-defined methods inside this class, create a command for it manually_added_methods(command_klass).each { |method| register_command(basename, method.to_s, command_klass, global: basename == 'global') } end end
# File lib/conify/command.rb, line 120 def error_no_command error([ "`#{@current_cmd}` is not a conify command.", "Type `conify help` for a list of available commands." ].compact.join("\n")) end
Create a command file path from the name of a command
# File lib/conify/command.rb, line 111 def file_for_command(command) File.join(File.dirname(__FILE__), 'command', "#{command}.rb") end
Finds file/method for command
# File lib/conify/command.rb, line 14 def find_command(cmd, args = []) @current_cmd = cmd @current_args = args respond_with_help if seeking_help? respond_with_version if seeking_version? # Separate out primary/secondary commands based on if command was namespaced # e.g. `conify services vs. conify services:add` primary_cmd, secondary_cmd = @current_cmd.split(':') # Get the command file path (string) for the primary command primary_cmd_file = file_for_command(primary_cmd) # If the primary command has it's own file, require it primary_cmd_file_exists = File.exists?(primary_cmd_file) require primary_cmd_file if primary_cmd_file_exists # If a secondary command exists, the primary_cmd_file must be where our command method lies if !secondary_cmd.nil? error_no_command if !primary_cmd_file_exists # Get command_klass for file path. Example response --> Conify::Command::Services command_klass = klass_for_file(primary_cmd_file) # Error out if the command klass doesn't have a method named <secondary_cmd> error_no_command if !klass_has_method?(command_klass, secondary_cmd) run(command_klass, secondary_cmd) # If there's no secondary command, there are 2 options for where the command method could be (in order of priority): # (1) Inside the primary command file as the 'index' method # (2) Inside the global command file, as a method named <primary_cmd> else # Store lambda for later try_global = lambda { require 'conify/command/global' command_klass = Conify::Command::Global error_no_command if !klass_has_method?(command_klass, primary_cmd) run(command_klass, primary_cmd) } # Number 1 above. If primary_cmd file exists, call the index method on it if it exists. # If index method doens't exist, check to see if method is a global command. if primary_cmd_file_exists # Get command_klass for file path. Example response --> Conify::Command::Services command_klass = klass_for_file(primary_cmd_file) klass_has_method?(command_klass, 'index') ? run(command_klass, 'index') : try_global.call # Number 2 above. Check to see if method is a global command inside command/global.rb else try_global.call end end end
Return just the basename for a file, no extensions.
# File lib/conify/command.rb, line 261 def get_basename_from_file(file) basename = Pathname.new(file).basename.to_s basename[0..(basename.rindex('.') - 1)] end
Respond to the user in the instance of invalid arguments.
# File lib/conify/command.rb, line 152 def handle_invalid_args(command_info_module) if !@invalid_args.empty? message = 'Invalid argument' message += 's' if @invalid_args.length > 1 args = @invalid_args.map { |arg| "\"#{arg}\"" }.join(', ') puts " ! #{message}: #{args}" else puts " ! Invalid command usage" end respond_with_command_help(command_info_module) end
Get a command klass back from a file path: Example I/O: 'command/bundles' –> Conify::Command::Bundles
# File lib/conify/command.rb, line 99 def klass_for_file(file) # Get basename for the file without the extension basename = get_basename_from_file(file) # Camelcase the basename to be the klass name klass_name = camelize(basename) # return the command klass for this klass_name Conify::Command.const_get(klass_name) end
Check to see if user-defined method exists on a klass
# File lib/conify/command.rb, line 116 def klass_has_method?(klass, method) manually_added_methods(klass).include?(method.to_sym) end
register a command's info to the @@commands map - utilized when calling `conify help`
# File lib/conify/command.rb, line 277 def register_command(basename, action, command_class, global: false) command = global ? action : (action == 'index' ? basename : "#{basename}:#{action}") command_info_module = command_class::CommandInfo.const_get(camelize(action)) commands[command] = { description: command_description(command_info_module) } end
Respond to command-specific help
# File lib/conify/command.rb, line 240 def respond_with_command_help(command_info_module) help = "\nValid Command Formats:\n\n" command_valid_args(command_info_module).each { |format| help += "# conify #{@current_cmd} #{format.join(' ')}\n" } puts "#{help}\n" end
# File lib/conify/command.rb, line 179 def respond_with_help create_commands_map header = [ 'Usage: conify COMMAND [command-specific-arguments]', 'Type "conify COMMAND --help" for more details about each command', 'Commands:' ].join("\n\n") commands_info = usage_info(commands) puts "\n#{header}" puts "\n#{commands_info}\n\n" exit(0) end
# File lib/conify/command.rb, line 255 def respond_with_version display "conify #{Conify::VERSION}" exit(0) end
Call a method on a klass with certain arguments. Will validate arguments first before calling method.
# File lib/conify/command.rb, line 73 def run(klass, method) # Get the command info for this method on this klass command_info_module = klass::CommandInfo.const_get(camelize(method)) # If seeking help for this command with --help or -h if seeking_command_help?(@current_args) puts "\nPurpose: #{command_description(command_info_module)}\n" # respond with command-specific help respond_with_command_help(command_info_module) return end # get the valid arguments defined for this comand valid_args = command_valid_args(command_info_module) if !valid_args?(valid_args) handle_invalid_args(command_info_module) return end klass.new(@current_args.dup).send(method) end
Seeking command-specific help. e.g. `conify bundles –help`
# File lib/conify/command.rb, line 235 def seeking_command_help?(args) args.include?('-h') || args.include?('--help') end
stdin is `conify help` or `conify -h`
# File lib/conify/command.rb, line 175 def seeking_help? @current_args.length == 0 && (@current_cmd.empty? || ['help', '--help', '-h'].include?(@current_cmd)) end
stdin is `conify –version` or `conify -v`
# File lib/conify/command.rb, line 251 def seeking_version? @current_args.length == 0 && (@current_cmd == '--version' || @current_cmd == '-v') end
Format a map of commands into help output format
# File lib/conify/command.rb, line 218 def usage_info(map) keys = map.keys commands_column_width = keys.max_by(&:length).length + 1 commands_column_width += 2 if commands_column_width < 12 # iterate through each of the commands, create an array # of strings in a `<command> # <description>` format. Sort # them alphabetically, and then join them with new lines. keys.map { |key| command = " #{key}" command += (' ' * (commands_column_width - key.length + 1)) command += "# #{map[key][:description]}" command }.sort_by{ |k| k.downcase }.join("\n") end
Check if passed-in arguments are valid for a specific format
# File lib/conify/command.rb, line 128 def valid_args?(accepted_arg_formats) valid_args = false accepted_arg_formats.each { |format| # if no arguments exist, and no arguments is an accepted format, args are valid. if format.empty? && @current_args.empty? valid_args = true else passed_in_args = @current_args.clone format.each_with_index { |arg, i| passed_in_args[i] = arg if CMD_BLACKLIST.include?(arg) } @invalid_args = passed_in_args - format - [nil] valid_args = true if passed_in_args == format end } valid_args end