commander-openflighthpc 2.0.2 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -87
- data/commander-openflighthpc.gemspec +1 -0
- data/lib/commander.rb +3 -4
- data/lib/commander/cli.rb +15 -76
- data/lib/commander/command.rb +89 -175
- data/lib/commander/error_handler.rb +72 -0
- data/lib/commander/help_formatters.rb +0 -4
- data/lib/commander/help_formatters/terminal.rb +0 -5
- data/lib/commander/help_formatters/terminal/command_help.erb +12 -6
- data/lib/commander/help_formatters/terminal/help.erb +10 -4
- data/lib/commander/runner.rb +98 -129
- data/lib/commander/version.rb +1 -1
- metadata +19 -44
- data/bin/commander +0 -104
- data/lib/commander/blank.rb +0 -7
- data/lib/commander/core_ext.rb +0 -2
- data/lib/commander/core_ext/array.rb +0 -24
- data/lib/commander/core_ext/object.rb +0 -8
- data/lib/commander/help_formatters/terminal/subcommand_help.erb +0 -23
- data/lib/commander/help_formatters/terminal_compact.rb +0 -11
- data/lib/commander/help_formatters/terminal_compact/command_help.erb +0 -26
- data/lib/commander/help_formatters/terminal_compact/help.erb +0 -29
- data/lib/commander/help_formatters/terminal_compact/subcommand_help.erb +0 -15
- data/lib/commander/import.rb +0 -5
- data/lib/commander/patches/decimal-integer.rb +0 -17
- data/lib/commander/patches/help_formatter_binding.rb +0 -15
- data/lib/commander/patches/implicit-short-tags.rb +0 -75
- data/lib/commander/patches/priority_sort.rb +0 -21
- data/lib/commander/patches/validate_inputs.rb +0 -79
- data/lib/commander/platform.rb +0 -7
- data/spec/command_spec.rb +0 -157
- data/spec/configure_spec.rb +0 -37
- data/spec/core_ext/array_spec.rb +0 -18
- data/spec/core_ext/object_spec.rb +0 -19
- data/spec/help_formatters/terminal_compact_spec.rb +0 -69
- data/spec/help_formatters/terminal_spec.rb +0 -67
- data/spec/methods_spec.rb +0 -61
- data/spec/patches/validate_inputs_spec.rb +0 -84
- data/spec/runner_spec.rb +0 -672
- data/spec/spec_helper.rb +0 -79
- data/spec/ui_spec.rb +0 -30
@@ -0,0 +1,72 @@
|
|
1
|
+
module Commander
|
2
|
+
##
|
3
|
+
# Internal error class to delay rendering help text
|
4
|
+
# This is required as the help command pints directly to stdout
|
5
|
+
# In general this has a bit of a code smell to it, and should
|
6
|
+
# not be used publicly
|
7
|
+
class InternalCallableError < StandardError
|
8
|
+
attr_accessor :callable
|
9
|
+
|
10
|
+
def initialize(msg = nil, &block)
|
11
|
+
super(msg)
|
12
|
+
self.callable = block
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
callable.call if callable
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
INTERRUPT_MSG = 'Received Interrupt!'
|
21
|
+
|
22
|
+
def self.traceable_error_handler(*args)
|
23
|
+
# Determines if there is a --trace flag before a --
|
24
|
+
trace_index = args.index do |a|
|
25
|
+
if a == '--trace'
|
26
|
+
true
|
27
|
+
elsif a == '--'
|
28
|
+
break
|
29
|
+
else
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Removes the --trace flag if required
|
35
|
+
new_args = args.dup
|
36
|
+
new_args.delete_at(trace_index) if trace_index
|
37
|
+
|
38
|
+
# Start the actual error handler
|
39
|
+
error_handler(!!trace_index) do
|
40
|
+
yield(new_args) if block_given?
|
41
|
+
end
|
42
|
+
|
43
|
+
rescue Interrupt
|
44
|
+
# Start Rescuing Interrupt Immediately
|
45
|
+
$stderr.puts INTERRUPT_MSG
|
46
|
+
exit 130
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.error_handler(trace = false)
|
50
|
+
yield if block_given?
|
51
|
+
rescue StandardError, Interrupt => e
|
52
|
+
$stderr.puts e.full_message if trace
|
53
|
+
|
54
|
+
error_msg = e.message
|
55
|
+
exit_code = e.respond_to?(:exit_code) ? e.exit_code.to_i : 1
|
56
|
+
case e
|
57
|
+
when InternalCallableError
|
58
|
+
# See: https://shapeshed.com/unix-exit-codes/
|
59
|
+
exit_code = 126
|
60
|
+
$stderr.puts error_msg
|
61
|
+
e.call
|
62
|
+
when Interrupt
|
63
|
+
$stderr.puts INTERRUPT_MSG
|
64
|
+
# See: https://shapeshed.com/unix-exit-codes/
|
65
|
+
exit_code = 130
|
66
|
+
else
|
67
|
+
$stderr.puts error_msg
|
68
|
+
end
|
69
|
+
exit(exit_code)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
@@ -2,16 +2,12 @@ module Commander
|
|
2
2
|
module HelpFormatter
|
3
3
|
autoload :Base, 'commander/help_formatters/base'
|
4
4
|
autoload :Terminal, 'commander/help_formatters/terminal'
|
5
|
-
autoload :TerminalCompact, 'commander/help_formatters/terminal_compact'
|
6
5
|
|
7
6
|
class Context
|
8
7
|
def initialize(target)
|
9
8
|
@target = target
|
10
9
|
end
|
11
10
|
|
12
|
-
# NOTE: `get_binding` has been stubbed! This version will be ignored
|
13
|
-
# See patch for actual version
|
14
|
-
prepend Patches::HelpFormatterBinding
|
15
11
|
def get_binding
|
16
12
|
@target.instance_eval { binding }.tap do |bind|
|
17
13
|
decorate_binding(bind)
|
@@ -11,11 +11,6 @@ module Commander
|
|
11
11
|
template(:command_help).result(Context.new(command).get_binding)
|
12
12
|
end
|
13
13
|
|
14
|
-
def render_subcommand(command)
|
15
|
-
bind = ProgramContext.new(@runner).get_binding({cmd: command})
|
16
|
-
template(:subcommand_help).result(bind)
|
17
|
-
end
|
18
|
-
|
19
14
|
def template(name)
|
20
15
|
ERB.new(File.read(File.join(File.dirname(__FILE__), 'terminal', "#{name}.erb")), nil, '-')
|
21
16
|
end
|
@@ -22,14 +22,20 @@
|
|
22
22
|
<%= command %>
|
23
23
|
<% end -%>
|
24
24
|
<% end -%>
|
25
|
-
<% unless
|
25
|
+
<% unless slop.options.empty? -%>
|
26
26
|
|
27
27
|
<%= $terminal.color "OPTIONS", :bold %>:
|
28
|
-
<%
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
<% slop.options.each do |option| -%>
|
29
|
+
|
30
|
+
<% tag = if [Slop::BoolOption, Slop::NullOption].include?(option.class)
|
31
|
+
nil
|
32
|
+
else
|
33
|
+
option.key.upcase
|
34
|
+
end
|
35
|
+
-%>
|
36
|
+
<%= option.flags.join ', ' %> <%= tag %>
|
37
|
+
<%= Commander::HelpFormatter.indent 8, option.desc %><% if option.default_value %>
|
38
|
+
<%= $terminal.color "Default", :bold %>: <%= option.default_value %><% end %>
|
33
39
|
<% end -%>
|
34
40
|
<% end -%>
|
35
41
|
|
@@ -28,12 +28,18 @@
|
|
28
28
|
<%= "%-#{max_aliases_length}s %s %s" % [alias_name, command(alias_name).name, args.join(' ')] -%>
|
29
29
|
<% end %>
|
30
30
|
<% end %>
|
31
|
-
<% unless
|
31
|
+
<% unless global_slop.options.empty? -%>
|
32
32
|
<%= $terminal.color "GLOBAL OPTIONS", :bold %>:
|
33
|
-
<%
|
33
|
+
<% global_slop.options.each do |global| -%>
|
34
34
|
|
35
|
-
|
36
|
-
|
35
|
+
<% tag = if [Slop::BoolOption, Slop::NullOption]
|
36
|
+
nil
|
37
|
+
else
|
38
|
+
option.key.upcase
|
39
|
+
end
|
40
|
+
-%>
|
41
|
+
<%= global.flags.join ', ' %> <%= tag %>
|
42
|
+
<%= global.desc %>
|
37
43
|
<% end -%>
|
38
44
|
<% end -%>
|
39
45
|
<% if program :help -%>
|
data/lib/commander/runner.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'paint'
|
2
|
+
require 'ostruct'
|
2
3
|
|
3
4
|
module Commander
|
4
5
|
class Runner
|
@@ -14,9 +15,8 @@ module Commander
|
|
14
15
|
attr_reader :commands
|
15
16
|
|
16
17
|
##
|
17
|
-
#
|
18
|
-
|
19
|
-
attr_reader :options
|
18
|
+
# The global Slop Options
|
19
|
+
attr_reader :global_slop
|
20
20
|
|
21
21
|
##
|
22
22
|
# Hash of help formatter aliases.
|
@@ -33,8 +33,8 @@ module Commander
|
|
33
33
|
|
34
34
|
def initialize(*inputs)
|
35
35
|
@program, @commands, @default_command, \
|
36
|
-
@
|
37
|
-
|
36
|
+
@global_slop, @aliases, @args = inputs.map(&:dup)
|
37
|
+
|
38
38
|
@commands['help'] ||= Command.new('help').tap do |c|
|
39
39
|
c.syntax = "#{program(:name)} help [command]"
|
40
40
|
c.description = 'Display global or [command] help documentation'
|
@@ -49,15 +49,64 @@ module Commander
|
|
49
49
|
|
50
50
|
##
|
51
51
|
# Run command parsing and execution process
|
52
|
-
|
52
|
+
INBUILT_ERRORS = [
|
53
|
+
OptionParser::InvalidOption,
|
54
|
+
Command::CommandUsageError,
|
55
|
+
InvalidCommandError
|
56
|
+
]
|
53
57
|
|
54
58
|
def run
|
55
59
|
require_program :version, :description
|
56
60
|
|
57
|
-
|
58
|
-
|
61
|
+
# Determine where the arguments/ options start
|
62
|
+
remaining_args = if alias? command_name_from_args
|
63
|
+
@aliases[command_name_from_args.to_s] + args_without_command_name
|
64
|
+
else
|
65
|
+
args_without_command_name
|
66
|
+
end
|
67
|
+
|
68
|
+
# Parses the global slop options
|
69
|
+
global_parser = Slop::Parser.new(global_slop, suppress_errors: true)
|
70
|
+
global_opts = global_parser.parse(remaining_args)
|
71
|
+
remaining_args = global_parser.arguments
|
72
|
+
|
73
|
+
# Parse the command slop options
|
74
|
+
if active_command?
|
75
|
+
local_parser = Slop::Parser.new(active_command.slop)
|
76
|
+
local_opts = local_parser.parse(remaining_args)
|
77
|
+
remaining_args = local_parser.arguments
|
78
|
+
end
|
59
79
|
|
60
|
-
|
80
|
+
# Format the config and opts
|
81
|
+
config = program(:config).dup
|
82
|
+
opts = OpenStruct.new global_opts.to_h.merge(local_opts.to_h)
|
83
|
+
|
84
|
+
if opts.version
|
85
|
+
# Return the version
|
86
|
+
say version
|
87
|
+
exit 0
|
88
|
+
elsif opts.help && active_command?
|
89
|
+
# Return help for the active_command
|
90
|
+
run_help_command([active_command!.name])
|
91
|
+
elsif active_command?
|
92
|
+
# Run the active_command
|
93
|
+
active_command.run!(remaining_args, opts, config)
|
94
|
+
else
|
95
|
+
# Return generic help
|
96
|
+
run_help_command('')
|
97
|
+
end
|
98
|
+
rescue => e
|
99
|
+
msg = "#{Paint[program(:name), '#2794d8']}: #{Paint[e.to_s, :red, :bright]}"
|
100
|
+
new_error = e.exception(msg)
|
101
|
+
|
102
|
+
if INBUILT_ERRORS.include?(new_error.class)
|
103
|
+
new_error = InternalCallableError.new(e.message) do
|
104
|
+
$stderr.puts "\nUsage:\n\n"
|
105
|
+
name = active_command? ? active_command.name : :error
|
106
|
+
run_help_command([name])
|
107
|
+
end
|
108
|
+
end
|
109
|
+
raise new_error
|
61
110
|
end
|
62
111
|
|
63
112
|
##
|
@@ -100,20 +149,39 @@ module Commander
|
|
100
149
|
@aliases.include? name.to_s
|
101
150
|
end
|
102
151
|
|
103
|
-
##
|
104
|
-
# Check if a command _name_ exists.
|
105
|
-
|
106
|
-
def command_exists?(name)
|
107
|
-
@commands[name.to_s]
|
108
|
-
end
|
109
|
-
|
110
152
|
#:stopdoc:
|
111
153
|
|
112
154
|
##
|
113
155
|
# Get active command within arguments passed to this runner.
|
156
|
+
# It will try an run the default if arguments have been provided
|
157
|
+
# It can not run a default command that is flags-only
|
158
|
+
# This is to provide consistent behaviour to --help
|
159
|
+
#
|
114
160
|
|
115
161
|
def active_command
|
116
|
-
@__active_command ||=
|
162
|
+
@__active_command ||= begin
|
163
|
+
if named_command = command(command_name_from_args)
|
164
|
+
named_command
|
165
|
+
elsif default_command? && flagless_args_string.length > 0
|
166
|
+
default_command
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def active_command!
|
172
|
+
active_command.tap { |c| require_valid_command(c) }
|
173
|
+
end
|
174
|
+
|
175
|
+
def active_command?
|
176
|
+
active_command ? true : false
|
177
|
+
end
|
178
|
+
|
179
|
+
def default_command
|
180
|
+
@__default_command ||= command(@default_command)
|
181
|
+
end
|
182
|
+
|
183
|
+
def default_command?
|
184
|
+
default_command ? true : false
|
117
185
|
end
|
118
186
|
|
119
187
|
##
|
@@ -121,15 +189,20 @@ module Commander
|
|
121
189
|
# Supports multi-word commands, using the largest possible match.
|
122
190
|
|
123
191
|
def command_name_from_args
|
124
|
-
@__command_name_from_args ||=
|
192
|
+
@__command_name_from_args ||= valid_command_names_from(*@args.dup).sort.last
|
193
|
+
end
|
194
|
+
|
195
|
+
def flagless_args_string
|
196
|
+
@flagless_args_string ||= @args.reject { |value| value =~ /^-/ }.join ' '
|
125
197
|
end
|
126
198
|
|
127
199
|
##
|
128
200
|
# Returns array of valid command names found within _args_.
|
129
201
|
|
130
202
|
def valid_command_names_from(*args)
|
131
|
-
|
132
|
-
|
203
|
+
commands.keys.find_all do |name|
|
204
|
+
name if flagless_args_string =~ /^#{name}\b/
|
205
|
+
end
|
133
206
|
end
|
134
207
|
|
135
208
|
##
|
@@ -145,7 +218,7 @@ module Commander
|
|
145
218
|
def args_without_command_name
|
146
219
|
removed = []
|
147
220
|
parts = command_name_from_args.split rescue []
|
148
|
-
@args.
|
221
|
+
@args.reject do |arg|
|
149
222
|
removed << arg if parts.include?(arg) && !removed.include?(arg)
|
150
223
|
end
|
151
224
|
end
|
@@ -155,37 +228,17 @@ module Commander
|
|
155
228
|
|
156
229
|
def help_formatter_alias_defaults
|
157
230
|
{
|
158
|
-
compact: HelpFormatter::TerminalCompact,
|
159
231
|
}
|
160
232
|
end
|
161
233
|
|
162
|
-
##
|
163
|
-
# Limit commands to those which are subcommands of the one that is active
|
164
|
-
def limit_commands_to_subcommands(command)
|
165
|
-
commands.select! do |other_sym, _|
|
166
|
-
other = other_sym.to_s
|
167
|
-
# Do not match sub-sub commands (matches for a second space)
|
168
|
-
if /\A#{command.name}\s.*\s/.match?(other)
|
169
|
-
false
|
170
|
-
# Do match regular sub commands
|
171
|
-
elsif /\A#{command.name}\s/.match?(other)
|
172
|
-
true
|
173
|
-
# Do not match any other commands
|
174
|
-
else
|
175
|
-
false
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
234
|
##
|
181
235
|
# Creates default commands such as 'help' which is
|
182
236
|
# essentially the same as using the --help switch.
|
183
237
|
def run_help_command(args)
|
184
238
|
UI.enable_paging if program(:help_paging)
|
185
|
-
@help_commands = @commands.
|
239
|
+
@help_commands = @commands.reject { |_, v| v.hidden(false) }.to_h
|
186
240
|
if args.empty? || args[0] == :error
|
187
|
-
@help_options =
|
188
|
-
@help_commands.reject! { |k, v| !!v.hidden }
|
241
|
+
@help_options = []
|
189
242
|
old_wrap = $terminal.wrap_at
|
190
243
|
$terminal.wrap_at = nil
|
191
244
|
program(:nobanner, true) if args[0] == :error
|
@@ -194,54 +247,17 @@ module Commander
|
|
194
247
|
else
|
195
248
|
command = command args.join(' ')
|
196
249
|
require_valid_command command
|
197
|
-
|
198
|
-
limit_commands_to_subcommands(command)
|
199
|
-
say help_formatter.render_subcommand(command)
|
200
|
-
else
|
201
|
-
say help_formatter.render_command(command)
|
202
|
-
end
|
250
|
+
say help_formatter.render_command(command)
|
203
251
|
end
|
204
252
|
end
|
205
253
|
|
206
254
|
##
|
207
255
|
# Raises InvalidCommandError when a _command_ is not found.
|
208
256
|
|
209
|
-
def require_valid_command(command
|
257
|
+
def require_valid_command(command)
|
210
258
|
fail InvalidCommandError, 'invalid command', caller if command.nil?
|
211
259
|
end
|
212
260
|
|
213
|
-
##
|
214
|
-
# Removes global _options_ from _args_. This prevents an invalid
|
215
|
-
# option error from occurring when options are parsed
|
216
|
-
# again for the command.
|
217
|
-
|
218
|
-
def remove_global_options(options, args)
|
219
|
-
# TODO: refactor with flipflop, please TJ ! have time to refactor me !
|
220
|
-
options.each do |option|
|
221
|
-
switches = option[:switches].dup
|
222
|
-
next if switches.empty?
|
223
|
-
|
224
|
-
if (switch_has_arg = switches.any? { |s| s =~ /[ =]/ })
|
225
|
-
switches.map! { |s| s[0, s.index('=') || s.index(' ') || s.length] }
|
226
|
-
end
|
227
|
-
|
228
|
-
switches = expand_optionally_negative_switches(switches)
|
229
|
-
|
230
|
-
past_switch, arg_removed = false, false
|
231
|
-
args.delete_if do |arg|
|
232
|
-
if switches.any? { |s| s == arg }
|
233
|
-
arg_removed = !switch_has_arg
|
234
|
-
past_switch = true
|
235
|
-
elsif past_switch && !arg_removed && arg !~ /^-/
|
236
|
-
arg_removed = true
|
237
|
-
else
|
238
|
-
arg_removed = true
|
239
|
-
false
|
240
|
-
end
|
241
|
-
end
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
261
|
# expand switches of the style '--[no-]blah' into both their
|
246
262
|
# '--blah' and '--no-blah' variants, so that they can be
|
247
263
|
# properly detected and removed
|
@@ -256,41 +272,6 @@ module Commander
|
|
256
272
|
end
|
257
273
|
end
|
258
274
|
|
259
|
-
##
|
260
|
-
# Parse global command options.
|
261
|
-
|
262
|
-
def parse_global_options
|
263
|
-
parser = options.inject(OptionParser.new) do |options, option|
|
264
|
-
options.on(*option[:args], &global_option_proc(option[:switches], &option[:proc]))
|
265
|
-
end
|
266
|
-
|
267
|
-
options = @args.dup
|
268
|
-
begin
|
269
|
-
parser.parse!(options)
|
270
|
-
rescue OptionParser::InvalidOption => e
|
271
|
-
# Remove the offending args and retry.
|
272
|
-
options = options.reject { |o| e.args.include?(o) }
|
273
|
-
retry
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
##
|
278
|
-
# Returns a proc allowing for commands to inherit global options.
|
279
|
-
# This functionality works whether a block is present for the global
|
280
|
-
# option or not, so simple switches such as --verbose can be used
|
281
|
-
# without a block, and used throughout all commands.
|
282
|
-
|
283
|
-
def global_option_proc(switches, &block)
|
284
|
-
lambda do |value|
|
285
|
-
unless active_command.nil?
|
286
|
-
active_command.proxy_options << [Runner.switch_to_sym(switches.last), value]
|
287
|
-
end
|
288
|
-
if block && !value.nil?
|
289
|
-
instance_exec(value, &block)
|
290
|
-
end
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
275
|
##
|
295
276
|
# Raises a CommandError when the program any of the _keys_ are not present, or empty.
|
296
277
|
|
@@ -325,18 +306,6 @@ module Commander
|
|
325
306
|
switch.scan(/[\-\]](\w+)/).join('_').to_sym rescue nil
|
326
307
|
end
|
327
308
|
|
328
|
-
##
|
329
|
-
# Run the active command.
|
330
|
-
|
331
|
-
def run_active_command
|
332
|
-
require_valid_command
|
333
|
-
if alias? command_name_from_args
|
334
|
-
active_command.run(*(@aliases[command_name_from_args.to_s] + args_without_command_name))
|
335
|
-
else
|
336
|
-
active_command.run(*args_without_command_name)
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
309
|
def say(*args) #:nodoc:
|
341
310
|
$terminal.say(*args)
|
342
311
|
end
|