commander-openflighthpc 2.0.1 → 2.2.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 +45 -138
- data/commander-openflighthpc.gemspec +2 -1
- data/lib/commander.rb +3 -4
- data/lib/commander/cli.rb +15 -76
- data/lib/commander/command.rb +81 -176
- data/lib/commander/error_handler.rb +62 -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 +14 -6
- data/lib/commander/help_formatters/terminal/help.erb +12 -4
- data/lib/commander/runner.rb +92 -130
- data/lib/commander/version.rb +1 -1
- metadata +21 -46
- 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,62 @@
|
|
1
|
+
require 'paint'
|
2
|
+
|
3
|
+
module Commander
|
4
|
+
##
|
5
|
+
# Internal error class to delay rendering help text
|
6
|
+
# This is required as the help command points directly to stdout
|
7
|
+
# In general this has a bit of a code smell to it, and should
|
8
|
+
# not be used publicly
|
9
|
+
class InternalCallableError < StandardError
|
10
|
+
attr_accessor :callable
|
11
|
+
|
12
|
+
def initialize(msg = nil, &block)
|
13
|
+
super(msg)
|
14
|
+
self.callable = block
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
callable.call if callable
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
ErrorHandler = Struct.new(:program_name, :trace) do
|
23
|
+
def parse_trace(*raw_args)
|
24
|
+
# Do not modify the original array
|
25
|
+
args = raw_args.dup
|
26
|
+
|
27
|
+
# Determines if there is a --trace flag before a --
|
28
|
+
trace_index = args.index do |a|
|
29
|
+
if a == '--trace'
|
30
|
+
self.trace = true
|
31
|
+
elsif a == '--'
|
32
|
+
break
|
33
|
+
else
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Removes the --trace flag if required
|
39
|
+
args.tap { |a| a.delete_at(trace_index) if trace_index }
|
40
|
+
end
|
41
|
+
|
42
|
+
def start
|
43
|
+
yield(self) if block_given?
|
44
|
+
rescue => e
|
45
|
+
$stderr.puts e.full_message if trace
|
46
|
+
|
47
|
+
error_msg = "#{Paint[program_name, '#2794d8']}: #{Paint[e.to_s, :red, :bright]}"
|
48
|
+
exit_code = e.respond_to?(:exit_code) ? e.exit_code.to_i : 1
|
49
|
+
case e
|
50
|
+
when InternalCallableError
|
51
|
+
# See: https://shapeshed.com/unix-exit-codes/
|
52
|
+
exit_code = 126
|
53
|
+
$stderr.puts error_msg
|
54
|
+
e.call
|
55
|
+
else
|
56
|
+
$stderr.puts error_msg
|
57
|
+
end
|
58
|
+
exit(exit_code)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
@@ -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,22 @@
|
|
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
|
+
elsif meta = option.config[:meta]
|
33
|
+
meta
|
34
|
+
else
|
35
|
+
option.key.upcase
|
36
|
+
end
|
37
|
+
-%>
|
38
|
+
<%= option.flags.join ', ' %> <%= tag %>
|
39
|
+
<%= Commander::HelpFormatter.indent 8, option.desc %><% if option.default_value %>
|
40
|
+
<%= $terminal.color "Default", :bold %>: <%= option.default_value %><% end %>
|
33
41
|
<% end -%>
|
34
42
|
<% end -%>
|
35
43
|
|
@@ -28,12 +28,20 @@
|
|
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
|
+
elsif meta = option.config[:meta]
|
38
|
+
meta
|
39
|
+
else
|
40
|
+
option.key.upcase
|
41
|
+
end
|
42
|
+
-%>
|
43
|
+
<%= global.flags.join ', ' %> <%= tag %>
|
44
|
+
<%= Commander::HelpFormatter.indent 8, global.desc %>
|
37
45
|
<% end -%>
|
38
46
|
<% end -%>
|
39
47
|
<% if program :help -%>
|
data/lib/commander/runner.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'ostruct'
|
2
2
|
|
3
3
|
module Commander
|
4
4
|
class Runner
|
@@ -14,9 +14,8 @@ module Commander
|
|
14
14
|
attr_reader :commands
|
15
15
|
|
16
16
|
##
|
17
|
-
#
|
18
|
-
|
19
|
-
attr_reader :options
|
17
|
+
# The global Slop Options
|
18
|
+
attr_reader :global_slop
|
20
19
|
|
21
20
|
##
|
22
21
|
# Hash of help formatter aliases.
|
@@ -33,8 +32,8 @@ module Commander
|
|
33
32
|
|
34
33
|
def initialize(*inputs)
|
35
34
|
@program, @commands, @default_command, \
|
36
|
-
@
|
37
|
-
|
35
|
+
@global_slop, @aliases, @args = inputs.map(&:dup)
|
36
|
+
|
38
37
|
@commands['help'] ||= Command.new('help').tap do |c|
|
39
38
|
c.syntax = "#{program(:name)} help [command]"
|
40
39
|
c.description = 'Display global or [command] help documentation'
|
@@ -49,15 +48,58 @@ module Commander
|
|
49
48
|
|
50
49
|
##
|
51
50
|
# Run command parsing and execution process
|
52
|
-
|
51
|
+
INBUILT_ERRORS = [
|
52
|
+
OptionParser::InvalidOption,
|
53
|
+
Command::CommandUsageError,
|
54
|
+
InvalidCommandError
|
55
|
+
]
|
53
56
|
|
54
57
|
def run
|
55
58
|
require_program :version, :description
|
56
59
|
|
57
|
-
|
58
|
-
|
60
|
+
# Determine where the arguments/ options start
|
61
|
+
remaining_args = if alias? command_name_from_args
|
62
|
+
@aliases[command_name_from_args.to_s] + args_without_command_name
|
63
|
+
else
|
64
|
+
args_without_command_name
|
65
|
+
end
|
59
66
|
|
60
|
-
|
67
|
+
# Combines the global and command options into a single parser
|
68
|
+
global_opts = global_slop.options
|
69
|
+
command_opts = active_command? ? active_command.slop.options : []
|
70
|
+
opts = [*global_opts, *command_opts]
|
71
|
+
parser = Slop::Parser.new(opts)
|
72
|
+
|
73
|
+
# Parsers the arguments/opts and fetches the config
|
74
|
+
parser.parse(remaining_args)
|
75
|
+
opts = OpenStruct.new parser.parse(remaining_args).to_h
|
76
|
+
remaining_args = parser.arguments
|
77
|
+
config = program(:config).dup
|
78
|
+
|
79
|
+
if opts.version
|
80
|
+
# Return the version
|
81
|
+
say version
|
82
|
+
exit 0
|
83
|
+
elsif opts.help && active_command?
|
84
|
+
# Return help for the active_command
|
85
|
+
run_help_command([active_command!.name])
|
86
|
+
elsif active_command?
|
87
|
+
# Run the active_command
|
88
|
+
active_command.run!(remaining_args, opts, config)
|
89
|
+
else
|
90
|
+
# Return generic help
|
91
|
+
run_help_command('')
|
92
|
+
end
|
93
|
+
rescue => original
|
94
|
+
error = original
|
95
|
+
if INBUILT_ERRORS.include?(error.class)
|
96
|
+
error = InternalCallableError.new(error.message) do
|
97
|
+
$stderr.puts "\nUsage:\n\n"
|
98
|
+
name = active_command? ? active_command.name : :error
|
99
|
+
run_help_command([name])
|
100
|
+
end
|
101
|
+
end
|
102
|
+
raise error
|
61
103
|
end
|
62
104
|
|
63
105
|
##
|
@@ -100,20 +142,39 @@ module Commander
|
|
100
142
|
@aliases.include? name.to_s
|
101
143
|
end
|
102
144
|
|
103
|
-
##
|
104
|
-
# Check if a command _name_ exists.
|
105
|
-
|
106
|
-
def command_exists?(name)
|
107
|
-
@commands[name.to_s]
|
108
|
-
end
|
109
|
-
|
110
145
|
#:stopdoc:
|
111
146
|
|
112
147
|
##
|
113
148
|
# Get active command within arguments passed to this runner.
|
149
|
+
# It will try an run the default if arguments have been provided
|
150
|
+
# It can not run a default command that is flags-only
|
151
|
+
# This is to provide consistent behaviour to --help
|
152
|
+
#
|
114
153
|
|
115
154
|
def active_command
|
116
|
-
@__active_command ||=
|
155
|
+
@__active_command ||= begin
|
156
|
+
if named_command = command(command_name_from_args)
|
157
|
+
named_command
|
158
|
+
elsif default_command? && flagless_args_string.length > 0
|
159
|
+
default_command
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def active_command!
|
165
|
+
active_command.tap { |c| require_valid_command(c) }
|
166
|
+
end
|
167
|
+
|
168
|
+
def active_command?
|
169
|
+
active_command ? true : false
|
170
|
+
end
|
171
|
+
|
172
|
+
def default_command
|
173
|
+
@__default_command ||= command(@default_command)
|
174
|
+
end
|
175
|
+
|
176
|
+
def default_command?
|
177
|
+
default_command ? true : false
|
117
178
|
end
|
118
179
|
|
119
180
|
##
|
@@ -121,15 +182,20 @@ module Commander
|
|
121
182
|
# Supports multi-word commands, using the largest possible match.
|
122
183
|
|
123
184
|
def command_name_from_args
|
124
|
-
@__command_name_from_args ||=
|
185
|
+
@__command_name_from_args ||= valid_command_names_from(*@args.dup).sort.last
|
186
|
+
end
|
187
|
+
|
188
|
+
def flagless_args_string
|
189
|
+
@flagless_args_string ||= @args.reject { |value| value =~ /^-/ }.join ' '
|
125
190
|
end
|
126
191
|
|
127
192
|
##
|
128
193
|
# Returns array of valid command names found within _args_.
|
129
194
|
|
130
195
|
def valid_command_names_from(*args)
|
131
|
-
|
132
|
-
|
196
|
+
commands.keys.find_all do |name|
|
197
|
+
name if flagless_args_string =~ /^#{name}(?![[:graph:]])/
|
198
|
+
end
|
133
199
|
end
|
134
200
|
|
135
201
|
##
|
@@ -145,7 +211,7 @@ module Commander
|
|
145
211
|
def args_without_command_name
|
146
212
|
removed = []
|
147
213
|
parts = command_name_from_args.split rescue []
|
148
|
-
@args.
|
214
|
+
@args.reject do |arg|
|
149
215
|
removed << arg if parts.include?(arg) && !removed.include?(arg)
|
150
216
|
end
|
151
217
|
end
|
@@ -155,37 +221,17 @@ module Commander
|
|
155
221
|
|
156
222
|
def help_formatter_alias_defaults
|
157
223
|
{
|
158
|
-
compact: HelpFormatter::TerminalCompact,
|
159
224
|
}
|
160
225
|
end
|
161
226
|
|
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
227
|
##
|
181
228
|
# Creates default commands such as 'help' which is
|
182
229
|
# essentially the same as using the --help switch.
|
183
230
|
def run_help_command(args)
|
184
231
|
UI.enable_paging if program(:help_paging)
|
185
|
-
@help_commands = @commands.
|
232
|
+
@help_commands = @commands.reject { |_, v| v.hidden(false) }.to_h
|
186
233
|
if args.empty? || args[0] == :error
|
187
|
-
@help_options =
|
188
|
-
@help_commands.reject! { |k, v| !!v.hidden }
|
234
|
+
@help_options = []
|
189
235
|
old_wrap = $terminal.wrap_at
|
190
236
|
$terminal.wrap_at = nil
|
191
237
|
program(:nobanner, true) if args[0] == :error
|
@@ -194,54 +240,17 @@ module Commander
|
|
194
240
|
else
|
195
241
|
command = command args.join(' ')
|
196
242
|
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
|
243
|
+
say help_formatter.render_command(command)
|
203
244
|
end
|
204
245
|
end
|
205
246
|
|
206
247
|
##
|
207
248
|
# Raises InvalidCommandError when a _command_ is not found.
|
208
249
|
|
209
|
-
def require_valid_command(command
|
250
|
+
def require_valid_command(command)
|
210
251
|
fail InvalidCommandError, 'invalid command', caller if command.nil?
|
211
252
|
end
|
212
253
|
|
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
254
|
# expand switches of the style '--[no-]blah' into both their
|
246
255
|
# '--blah' and '--no-blah' variants, so that they can be
|
247
256
|
# properly detected and removed
|
@@ -256,41 +265,6 @@ module Commander
|
|
256
265
|
end
|
257
266
|
end
|
258
267
|
|
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
268
|
##
|
295
269
|
# Raises a CommandError when the program any of the _keys_ are not present, or empty.
|
296
270
|
|
@@ -325,18 +299,6 @@ module Commander
|
|
325
299
|
switch.scan(/[\-\]](\w+)/).join('_').to_sym rescue nil
|
326
300
|
end
|
327
301
|
|
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
302
|
def say(*args) #:nodoc:
|
341
303
|
$terminal.say(*args)
|
342
304
|
end
|