commander-openflighthpc 2.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|