class Consoler::Application
Consoler
application
@example A simple application
# create a application app = Consoler::Application.new description: 'A simple app' # define a command app.build 'target [--clean]' do |target, clean| # clean contains a boolean clean_up if clean # target contains a string build_project target end app.run(['build', 'production', '--clean']) # this does not match, nothing is executed and the usage message is printed app.run(['deploy', 'production'])
Public Class Methods
Create a consoler application
@param options [Hash] Options
for the application @option options [String] :description The description for the application (optional)
# File lib/consoler/application.rb, line 30 def initialize(options = {}) @description = options[:description] @commands = [] end
Public Instance Methods
Register a command for this app
@param command_name [Symbol] Name of the command @param input [String, Consoler::Application] Options
definition or a complete subapp @yield […] Executed when the action is matched with parameters based on your options @return [nil]
# File lib/consoler/application.rb, line 51 def method_missing(command_name, input = nil, &block) action = nil options_def = '' unless block.nil? action = block options_def = input if !options_def.nil? && !options_def.instance_of?(String) raise 'Invalid options' end end if input.instance_of? Consoler::Application action = input options_def = '' end if action.nil? raise 'Invalid subapp/block' end command = command_name.to_s _add_command(command, options_def, action) nil end
Accept all method_missing
call
We use the name as a command name, thus we accept all names
@param _method_name [String] Name of the method @param _include_private [bool] Name of the method
# File lib/consoler/application.rb, line 41 def respond_to_missing?(_method_name, _include_private = false) true end
Run the application with a list of arguments
@param args [Array] Arguments
@param disable_usage_message [Boolean] Disable the usage message when nothing it matched @return [mixed] Result of your matched command, nil
otherwise
# File lib/consoler/application.rb, line 85 def run(args = ARGV, disable_usage_message = false) # TODO: signal handling of some kind? result, matched = _run(args.dup) if !matched && !disable_usage_message usage end result end
Show the usage message
Contains all commands and options, including subapps
# File lib/consoler/application.rb, line 100 def usage puts "#{@description}\n\n" unless @description.nil? puts 'Usage:' _commands_usage $PROGRAM_NAME end
Protected Instance Methods
Print the usage message for this command
@param [String] prefix A prefix for the command from a parent app @return [Consoler::Application]
# File lib/consoler/application.rb, line 165 def _commands_usage(prefix = '') @commands.each do |command| # print the usage message of a subapp with a prefix from the current command if command.action.instance_of?(Consoler::Application) command.action._commands_usage "#{prefix} #{command.command}" else print " #{prefix} #{command.command}" if command.options.size print " #{command.options.to_definition}" end unless command.options.description.nil? print " -- #{command.options.description}" end print "\n" end end self end
Run the app
@param [Array<String>] args Arguments
@return [(mixed, Boolean)] Result of the command, and, did the args match a command at all
# File lib/consoler/application.rb, line 113 def _run(args) arg = args.shift return [nil, false] if arg.nil? arguments = Consoler::Arguments.new args exact_matches = [] partial_matches = [] @commands.each do |command| if command.command == arg exact_matches.push command elsif command.command.start_with? arg partial_matches.push command end end # we only allow a single partial match to prevent ambiguity partial_match = if partial_matches.size == 1 partial_matches[0] end unless exact_matches.empty? && partial_match.nil? matches = exact_matches matches.push partial_match unless partial_match.nil? matches.each do |command| # the matched command contains a subapp, run subapp with the same # arguments (excluding the arg that matched this command) if command.action.instance_of?(Consoler::Application) result, matched = command.action._run(args) if matched return result, true end else match = arguments.match command.options next if match.nil? return _dispatch(command.action, match), true end end end [nil, false] end
Private Instance Methods
Add a command
@param [String] command Command
name @param [String] options_def Definition of options @param [Proc, Consoler::Application] action Action or subapp @return [Consoler::Application]
# File lib/consoler/application.rb, line 196 def _add_command(command, options_def, action) @commands.push( Consoler::Command.new( command: command, options: Consoler::Options.new(options_def), action: action, ) ) self end
Execute an action with argument match info
@param [Proc] action Action @param [Hash] match Argument match information
# File lib/consoler/application.rb, line 212 def _dispatch(action, match) # match parameter names to indices of match information arguments = action.parameters.map do |parameter| parameter_name = parameter[1].to_s if match.key? parameter_name match[parameter_name] else # check for the normalized name of every match to see # if it fits the parameter name match.each do |name, value| normalized_name = _normalize name if parameter_name == normalized_name break value end end end end action.call(*arguments) end
Normalize a name to be used as a variable name
@param [String] name Name @return [String] Normalized name
# File lib/consoler/application.rb, line 239 def _normalize(name) # maybe do something more, maybe not.. ruby does allow for # some weird stuff to be used as a variable name. the user # should use some common sense. and, other things might # also be an syntax error, like starting with a number. # this normalization is more of a comvenience than anything # else name.tr('-', '_') end