Class: Commander::Runner
- Inherits:
-
Object
- Object
- Commander::Runner
- Defined in:
- lib/MrMurano/commands/completion.rb,
lib/MrMurano/ReCommander.rb
Overview
rubocop:disable Style/ClassAndModuleChildren
"Use nested module/class definitions instead of compact style."
except that nested style (class [::]Commander\nclass Runner)
does not work.
Constant Summary collapse
- SHELL_TYPES =
The shells for which we have completion templates.
i[bash zsh].freeze
Instance Attribute Summary collapse
-
#command_exit ⇒ Object
Returns the value of attribute command_exit.
Instance Method Summary collapse
- #args_without_command_name ⇒ Object
- #cleanup_args_simple_command_help ⇒ Object
-
#cmdMaxDepth ⇒ Object
Get maximum depth of sub-commands.
-
#cmdTree ⇒ Object
Get a tree of all commands and sub commands.
-
#cmdTreeB ⇒ Object
Alternate tree of sub-commands.
- #end_of_options_hack ⇒ Object
- #find_exact_match_maybe(arg, arg_opts) ⇒ Object
- #find_matching_options(arg) ⇒ Object
- #fix_args_for_help ⇒ Object
-
#flatswitches(option) ⇒ Object
Change the ‘–[no-]foo’ switch into ‘–no-foo’ and ‘–foo’.
-
#help_hack ⇒ Object
2017-08-22: Commander’s help infrastructure is either really weak, or we did something elsewhere that seriously cripples it.
- #help_opts ⇒ Object
-
#must_be_sane_option!(arg_opts) ⇒ Object
Returns true if next arg is input to current –option.
- #must_not_option_passed_first! ⇒ Object
- #old_args_without_command_name ⇒ Object
- #old_parse_global_options ⇒ Object
- #old_remove_global_options ⇒ Object
-
#old_run_active_command ⇒ Object
run_active_command is called by commander-rb’s at_exit hook.
-
#optionDesc(option) ⇒ Object
truncate the description of an option.
- #parse_global_options ⇒ Object
- #parse_global_options_real ⇒ Object
- #remove_global_options(options, args) ⇒ Object
- #run_active_command ⇒ Object
- #show_alias_help_maybe! ⇒ Object
-
#takesArg(option, yes = '=', no = '') ⇒ Object
If the switches take an argument, return =.
-
#trim_options_from_args ⇒ Object
If there are options in addition to –help, then Commander runs the command! So remove all options.
-
#verify_solutions_unmanaged! ⇒ Object
(lb): I’m not a huge fan of mingling business logic with our Commander monkey patch, but it’s a lot more readable that having every command make the call!.
- #vers_opts ⇒ Object
Instance Attribute Details
#command_exit ⇒ Object
Returns the value of attribute command_exit.
78 79 80 |
# File 'lib/MrMurano/ReCommander.rb', line 78 def command_exit @command_exit end |
Instance Method Details
#args_without_command_name ⇒ Object
137 138 139 140 141 |
# File 'lib/MrMurano/ReCommander.rb', line 137 def args_without_command_name args_sans = old_args_without_command_name args_sans += ['--'] + @positional unless @positional.empty? args_sans end |
#cleanup_args_simple_command_help ⇒ Object
322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/MrMurano/ReCommander.rb', line 322 def cleanup_args_simple_command_help # This is a single-word command, e.g., 'link', not 'link list', # as in `murano link --help`, not `murano link list --help`. # Positional parameters break Commander. E.g., # $ murano --help config application.id # invalid command. Use --help for more information # so remove any remaining --options and use the first term. @args -= help_opts @args = @args[0..0] # Add back in the --help if the command is not a subcommand help. @args.push('--help') unless active_command.subcmdgrouphelp end |
#cmdMaxDepth ⇒ Object
Get maximum depth of sub-commands.
78 79 80 81 82 83 84 85 |
# File 'lib/MrMurano/commands/completion.rb', line 78 def cmdMaxDepth depth = 0 @commands.keys.sort.each do |name| levels = name.split depth = levels.count if levels.count > depth end depth end |
#cmdTree ⇒ Object
Get a tree of all commands and sub commands
62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/MrMurano/commands/completion.rb', line 62 def cmdTree tree = {} @commands.sort.each do |name, cmd| levels = name.split pos = tree levels.each do |step| pos[step] = {} unless pos.key? step pos = pos[step] end pos["\0cmd"] = cmd end tree end |
#cmdTreeB ⇒ Object
Alternate tree of sub-commands.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/MrMurano/commands/completion.rb', line 89 def cmdTreeB tree = {} @commands.sort.each do |name, cmd| levels = name.split tree[levels.join(' ')] = { cmd: cmd } # load parent. left = levels[0..-2] right = levels[-1] key = left.join(' ') tree[key] = {} unless tree.key? key if tree[key].key?(:subs) tree[key][:subs] << right else tree[key][:subs] = [right] end end tree end |
#end_of_options_hack ⇒ Object
178 179 180 181 182 183 184 |
# File 'lib/MrMurano/ReCommander.rb', line 178 def @positional = [] i_positional = @args.index('--') return if i_positional.nil? @positional = @args[i_positional + 1..-1] @args = @args[0, i_positional] end |
#find_exact_match_maybe(arg, arg_opts) ⇒ Object
287 288 289 290 291 292 293 294 295 |
# File 'lib/MrMurano/ReCommander.rb', line 287 def find_exact_match_maybe(arg, arg_opts) return arg_opts unless arg =~ /^--/ exact = arg_opts.select do |opt| opt[:switches].include?(arg) || opt[:switches].any? do |sw| sw.start_with?(arg + ' ') end end exact.empty? && exact || arg_opts end |
#find_matching_options(arg) ⇒ Object
275 276 277 278 279 280 281 282 283 284 285 |
# File 'lib/MrMurano/ReCommander.rb', line 275 def (arg) .select do |opt| if arg =~ /^-[^-]/ # Single char abbrev: look for exact match. opt[:switches].include?(arg) else # Long switch: Commander matches abbrevs of longs... opt[:switches].any? { |oarg| oarg =~ /^#{arg}/ } end end end |
#fix_args_for_help ⇒ Object
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/MrMurano/ReCommander.rb', line 210 def fix_args_for_help # If `murano --help` is specified, let rb-commander handle it. # But if `murano command --help` is specified, don't let cmdr # handle it, otherwise it just shows the command description, # but not any of the subcommands (our SubCmdGroupContext code). # Note: not checking help_opts here, which includes 'help', because # 'help' might really be a command argument (e.g., a solution name). # Note: `murano -v`'s active_command is 'help'. do_help = (@args & %w[-h --help]).any? || active_command.name == 'help' if do_help @purargs = @args - help_opts return do_help if active_command.name == 'help' # Any command other than `murano help` or `murano --help`. return do_help if !do_help || active_command.name.include?(' ') # Command is not --help, or it's help for a single-word command. cleanup_args_simple_command_help do_help end |
#flatswitches(option) ⇒ Object
Change the ‘–[no-]foo’ switch into ‘–no-foo’ and ‘–foo’
31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/MrMurano/commands/completion.rb', line 31 def flatswitches(option) # if there is a --[no-]foo format, break that into two switches. switches = option[:switches].map do |switch| switch = switch.sub(/\s.*$/, '') # drop argument spec if exists. if switch =~ /\[no-\]/ [switch.sub(/\[no-\]/, ''), switch.gsub(/[\[\]]/, '')] else switch end end switches.flatten end |
#help_hack ⇒ Object
2017-08-22: Commander’s help infrastructure is either really weak, or we did something elsewhere that seriously cripples it. In any case, this fixes all its quirks.
189 190 191 192 193 |
# File 'lib/MrMurano/ReCommander.rb', line 189 def help_hack do_help = fix_args_for_help show_alias_help_maybe! if do_help do_help end |
#help_opts ⇒ Object
195 196 197 |
# File 'lib/MrMurano/ReCommander.rb', line 195 def help_opts %w[-h --help help].freeze end |
#must_be_sane_option!(arg_opts) ⇒ Object
Returns true if next arg is input to current –option.
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
# File 'lib/MrMurano/ReCommander.rb', line 298 def must_be_sane_option!(arg_opts) if arg_opts.length > 1 # MAYBE/2017-08-23: Always do this check, not just for help. ambig = MrMurano::Verbose.fancy_ticks(arg) match = arg_opts.map do |opt| opt[:switches].map { |sw| MrMurano::Verbose.fancy_ticks(sw) }.join('|') end match = match.flatten match[-1] = "and #{match[-1]}" if match.length > 1 match = match.join(', ') MrMurano::Verbose.error("Ambiguous option: #{ambig} matches: #{match}") exit 2 elsif arg_opts.length == 1 # See if this option (the only option) has input. arg_opts.first[:switches].any? do |opt| # There may be a space in a --long option, e.g., # '--config KEY=VAL', which means an argument follows. opt.start_with?('--') && opt.include?(' ') end else false end end |
#must_not_option_passed_first! ⇒ Object
203 204 205 206 207 208 |
# File 'lib/MrMurano/ReCommander.rb', line 203 def must_not_option_passed_first! return unless @args[0].to_s.start_with? '-' return if %w[-h --help -v --version].include? @args[0] MrMurano::Verbose.warning('Usage: murano <sub-cmd> [<options>]') exit 1 end |
#old_args_without_command_name ⇒ Object
136 |
# File 'lib/MrMurano/ReCommander.rb', line 136 alias old_args_without_command_name args_without_command_name |
#old_parse_global_options ⇒ Object
151 |
# File 'lib/MrMurano/ReCommander.rb', line 151 alias |
#old_remove_global_options ⇒ Object
143 |
# File 'lib/MrMurano/ReCommander.rb', line 143 alias |
#old_run_active_command ⇒ Object
run_active_command is called by commander-rb’s at_exit hook. We override – monkey patch – it to do other stuff.
82 |
# File 'lib/MrMurano/ReCommander.rb', line 82 alias old_run_active_command run_active_command |
#optionDesc(option) ⇒ Object
truncate the description of an option
56 57 58 |
# File 'lib/MrMurano/commands/completion.rb', line 56 def optionDesc(option) option[:description].sub(/\n.*$/, '') end |
#parse_global_options ⇒ Object
152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/MrMurano/ReCommander.rb', line 152 def # User can specify, e.g., status.options, to set options for specific commands. defopts = ($cfg["#{active_command.name}.options"] || '').split @args.push(*defopts) # Check if they are passing an option first must_not_option_passed_first! do_help = help_hack $cfg.validate_cmd_business_and_project(active_command) unless do_help end |
#parse_global_options_real ⇒ Object
164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/MrMurano/ReCommander.rb', line 164 def rescue OptionParser::MissingArgument => err if err..start_with?('missing argument:') puts err. else err_msg = MrMurano::Verbose.fancy_ticks(err.) MrMurano::Verbose.error( "There was a problem interpreting the options: #{err_msg}" ) end exit 2 end |
#remove_global_options(options, args) ⇒ Object
144 145 146 147 148 149 |
# File 'lib/MrMurano/ReCommander.rb', line 144 def (, args) # Look for sole '--' argument, which signals start of positionals. i_positional = args.index('--') args = args[0, i_positional] unless i_positional.nil? (, args) end |
#run_active_command ⇒ Object
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/MrMurano/ReCommander.rb', line 83 def run_active_command exit @command_exit if defined?(@command_exit) && @command_exit $cfg.must_be_valid_values! section = active_command.name hooked = MrMurano::Hooked.new(section) hooked.check_run_pre_hook verify_solutions_unmanaged! begin old_run_active_command rescue LocalJumpError => _err # This happens when you `return` from a command, since # commands are blocks, and returning from a block would # really mean returning from the thing running the block, # which would be bad. So Ruby barfs instead. return rescue OptionParser::InvalidArgument => err MrMurano::Verbose.whirly_stop MrMurano::Verbose.error err. exit 1 rescue OptionParser::InvalidOption => err MrMurano::Verbose.whirly_stop MrMurano::Verbose.error err. MrMurano::Verbose.error 'invalid command' if section == 'help' exit 1 rescue OptionParser::MissingArgument => err MrMurano::Verbose.whirly_stop MrMurano::Verbose.error err. exit 1 rescue OptionParser::NeedlessArgument => err MrMurano::Verbose.whirly_stop MrMurano::Verbose.error err. pattern = /^needless argument: --(?<arg>[_a-zA-Z0-9]+)=(?<val>.*)/ md = pattern.match(err.) unless md.nil? puts %(Try the option without the equals sign, e.g.,) puts %( --#{md[:arg]} "#{md[:val]}") end exit 1 rescue MrMurano::ConfigError => err # Clear whirly if it was running. MrMurano::Verbose.whirly_stop MrMurano::Verbose.error err. exit 1 rescue StandardError => _err raise end hooked.check_run_post_hook end |
#show_alias_help_maybe! ⇒ Object
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/MrMurano/ReCommander.rb', line 335 def show_alias_help_maybe! return unless alias?(command_name_from_args) || (active_command.name == 'help' && @args.length > 1) # Why, oh why, Commander, do you flake out on aliases? # E.g., # $ murano product --help # invalid command. Use --help for more information # Though it sometimes work, like with: # $ murano --help product device enable # but only because Commander shows help for the 'device' command. # I.e., this doesn't work: `murano product push --help` # So we'll just roll our own help for aliases! @args -= help_opts cli_cmd = MrMurano::Verbose.fancy_ticks(@purargs.join(' ')) if active_command.name == 'help' arg_cmd = @args.join(' ') else arg_cmd = command_name_from_args end mur_msg = '' if @aliases[arg_cmd].nil? matches = @aliases.keys.find_all { |key| key.start_with?('arg_cmd') } matches = @aliases.keys.find_all { |key| key.start_with?(@args[0]) } if matches.empty? unless matches.empty? matches = matches.map { |match| MrMurano::Verbose.fancy_ticks(match) } matches = matches.sort.join(', ') mur_msg = %(The #{cli_cmd} command includes: #{matches}) end else mur_cmd = [] mur_cmd += [active_command.name] if active_command.name != 'help' mur_cmd += @aliases[arg_cmd] unless @aliases[arg_cmd].empty? mur_cmd = mur_cmd.join(' ') #mur_cmd = active_command.name if mur_cmd.empty? mur_cmd = MrMurano::Verbose.fancy_ticks(mur_cmd) mur_msg = %(The #{cli_cmd} command is really: #{mur_cmd}) end return if mur_msg.empty? puts mur_msg exit 0 end |
#takesArg(option, yes = '=', no = '') ⇒ Object
If the switches take an argument, return =
46 47 48 49 50 51 52 |
# File 'lib/MrMurano/commands/completion.rb', line 46 def takesArg(option, yes='=', no='') if option[:switches].select { |switch| switch =~ /\s\S+$/ }.empty? no else yes end end |
#trim_options_from_args ⇒ Object
If there are options in addition to –help, then Commander runs the command! So remove all options.
E.g., this shows the help for usage:
$ murano --help usage
but if a flag is added, the command gets run, e.g.,
$ murano usage --id 1234 --help
runs the usage command.
(lb): I walked the code and it looks like when Commander tries to validate the args, it doesn’t like the –id flag (maybe because the “help” command is being used to validate, and it does not define any options?). Then it removes the –id switch (after the –help switch was previously removed) and tries the command again (in gems/commander-4.4.3/lib/commander/runner.rb, look for the comment, “Remove the offending args and retry”). So in the example given above, ‘murano usage –id 1234 –help`, both the `–id` flag and `–help` flag are moved from @args, and then `murano usage 1234` is executed (and args are not validated by Commander but merely passed to the command action, so `usage` gets args=). Whadda wreck.
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/MrMurano/ReCommander.rb', line 253 def reject_next = false @args.reject! do |arg| if reject_next reject_next = false true elsif ( arg.start_with?('-') && !help_opts.include?(arg) && !vers_opts.include?(arg) ) # See if the next argument should also be consumed. arg_opts = (arg) arg_opts = find_exact_match_maybe(arg, arg_opts) reject_next = must_be_sane_option!(arg_opts) true else false end end end |
#verify_solutions_unmanaged! ⇒ Object
(lb): I’m not a huge fan of mingling business logic with our Commander monkey patch, but it’s a lot more readable that having every command make the call!
379 380 381 382 383 384 385 386 387 388 389 390 391 |
# File 'lib/MrMurano/ReCommander.rb', line 379 def verify_solutions_unmanaged! return if $cfg['tool.skip-managed'] # (lb): All @exosite.com employees are welcome behind the curtain. if $cfg['user.name'] && $cfg['user.name'].end_with?('@exosite.com') MrMurano::Verbose.verbose( "Welcome behind the curtain, #{$cfg['user.name']}!" ) return end # FIXME/LATER: (lb): The Element Author should also be allowed in. return unless active_command.must_not_be_managed MrMurano::Business.must_not_be_managed! end |
#vers_opts ⇒ Object
199 200 201 |
# File 'lib/MrMurano/ReCommander.rb', line 199 def vers_opts %w[-v --version].freeze end |