commander 4.4.6 → 4.6.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/.rubocop.yml +26 -6
- data/.rubocop_todo.yml +4 -4
- data/.travis.yml +7 -8
- data/Gemfile +2 -0
- data/History.rdoc +25 -0
- data/README.md +6 -6
- data/Rakefile +3 -1
- data/bin/commander +2 -1
- data/commander.gemspec +13 -10
- data/lib/commander.rb +2 -0
- data/lib/commander/blank.rb +2 -0
- data/lib/commander/command.rb +10 -5
- data/lib/commander/configure.rb +2 -0
- data/lib/commander/core_ext.rb +2 -0
- data/lib/commander/core_ext/array.rb +3 -1
- data/lib/commander/core_ext/object.rb +2 -0
- data/lib/commander/delegates.rb +3 -1
- data/lib/commander/help_formatters.rb +3 -1
- data/lib/commander/help_formatters/base.rb +2 -0
- data/lib/commander/help_formatters/terminal.rb +7 -1
- data/lib/commander/help_formatters/terminal/command_help.erb +6 -6
- data/lib/commander/help_formatters/terminal/help.erb +7 -7
- data/lib/commander/help_formatters/terminal_compact.rb +7 -1
- data/lib/commander/import.rb +2 -0
- data/lib/commander/methods.rb +4 -2
- data/lib/commander/platform.rb +2 -0
- data/lib/commander/runner.rb +44 -36
- data/lib/commander/user_interaction.rb +27 -21
- data/lib/commander/version.rb +3 -1
- data/spec/command_spec.rb +29 -0
- data/spec/configure_spec.rb +2 -0
- data/spec/core_ext/array_spec.rb +3 -1
- data/spec/core_ext/object_spec.rb +2 -0
- data/spec/help_formatters/terminal_compact_spec.rb +2 -0
- data/spec/help_formatters/terminal_spec.rb +2 -0
- data/spec/methods_spec.rb +5 -3
- data/spec/runner_spec.rb +123 -8
- data/spec/spec_helper.rb +15 -4
- data/spec/ui_spec.rb +4 -2
- metadata +34 -30
@@ -1,34 +1,34 @@
|
|
1
|
-
<%=
|
1
|
+
<%= HighLine.default_instance.color "NAME", :bold %>:
|
2
2
|
|
3
3
|
<%= program :name %>
|
4
4
|
|
5
|
-
<%=
|
5
|
+
<%= HighLine.default_instance.color "DESCRIPTION", :bold %>:
|
6
6
|
|
7
7
|
<%= Commander::HelpFormatter.indent 4, program(:description) %>
|
8
8
|
|
9
|
-
<%=
|
9
|
+
<%= HighLine.default_instance.color "COMMANDS", :bold %>:
|
10
10
|
<% for name, command in @commands.sort -%>
|
11
11
|
<% unless alias? name %>
|
12
12
|
<%= "%-#{max_command_length}s %s" % [command.name, command.summary || command.description] -%>
|
13
13
|
<% end -%>
|
14
14
|
<% end %>
|
15
15
|
<% unless @aliases.empty? %>
|
16
|
-
<%=
|
16
|
+
<%= HighLine.default_instance.color "ALIASES", :bold %>:
|
17
17
|
<% for alias_name, args in @aliases.sort %>
|
18
18
|
<%= "%-#{max_aliases_length}s %s %s" % [alias_name, command(alias_name).name, args.join(' ')] -%>
|
19
19
|
<% end %>
|
20
20
|
<% end %>
|
21
21
|
<% unless @options.empty? -%>
|
22
|
-
<%=
|
22
|
+
<%= HighLine.default_instance.color "GLOBAL OPTIONS", :bold %>:
|
23
23
|
<% for option in @options -%>
|
24
24
|
|
25
|
-
<%= option[:switches].join ', ' %>
|
25
|
+
<%= option[:switches].join ', ' %>
|
26
26
|
<%= option[:description] %>
|
27
27
|
<% end -%>
|
28
28
|
<% end -%>
|
29
29
|
<% if program :help -%>
|
30
30
|
<% for title, body in program(:help) %>
|
31
|
-
<%=
|
31
|
+
<%= HighLine.default_instance.color title.to_s.upcase, :bold %>:
|
32
32
|
|
33
33
|
<%= body %>
|
34
34
|
<% end -%>
|
@@ -1,10 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'erb'
|
2
4
|
|
3
5
|
module Commander
|
4
6
|
module HelpFormatter
|
5
7
|
class TerminalCompact < Terminal
|
6
8
|
def template(name)
|
7
|
-
|
9
|
+
if RUBY_VERSION < '2.6'
|
10
|
+
ERB.new(File.read(File.join(File.dirname(__FILE__), 'terminal_compact', "#{name}.erb")), nil, '-')
|
11
|
+
else
|
12
|
+
ERB.new(File.read(File.join(File.dirname(__FILE__), 'terminal_compact', "#{name}.erb")), trim_mode: '-')
|
13
|
+
end
|
8
14
|
end
|
9
15
|
end
|
10
16
|
end
|
data/lib/commander/import.rb
CHANGED
data/lib/commander/methods.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Commander
|
2
4
|
module Methods
|
3
5
|
include Commander::UI
|
4
6
|
include Commander::UI::AskForClass
|
5
7
|
include Commander::Delegates
|
6
8
|
|
7
|
-
if $stdin.tty? && (cols =
|
8
|
-
|
9
|
+
if $stdin.tty? && (cols = HighLine.default_instance.output_cols) >= 40
|
10
|
+
HighLine.default_instance.wrap_at = cols - 5
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
data/lib/commander/platform.rb
CHANGED
data/lib/commander/runner.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'optparse'
|
2
4
|
|
3
5
|
module Commander
|
@@ -7,22 +9,10 @@ module Commander
|
|
7
9
|
#++
|
8
10
|
|
9
11
|
class CommandError < StandardError; end
|
10
|
-
class InvalidCommandError < CommandError; end
|
11
|
-
|
12
|
-
##
|
13
|
-
# Array of commands.
|
14
|
-
|
15
|
-
attr_reader :commands
|
16
12
|
|
17
|
-
|
18
|
-
# Global options.
|
19
|
-
|
20
|
-
attr_reader :options
|
21
|
-
|
22
|
-
##
|
23
|
-
# Hash of help formatter aliases.
|
13
|
+
class InvalidCommandError < CommandError; end
|
24
14
|
|
25
|
-
attr_reader :help_formatter_aliases
|
15
|
+
attr_reader :commands, :options, :help_formatter_aliases
|
26
16
|
|
27
17
|
##
|
28
18
|
# Initialize a new command runner. Optionally
|
@@ -41,7 +31,7 @@ module Commander
|
|
41
31
|
# Return singleton Runner instance.
|
42
32
|
|
43
33
|
def self.instance
|
44
|
-
@
|
34
|
+
@instance ||= new
|
45
35
|
end
|
46
36
|
|
47
37
|
##
|
@@ -76,7 +66,7 @@ module Commander
|
|
76
66
|
OptionParser::InvalidArgument,
|
77
67
|
OptionParser::MissingArgument => e
|
78
68
|
abort e.to_s
|
79
|
-
rescue => e
|
69
|
+
rescue StandardError => e
|
80
70
|
if @never_trace
|
81
71
|
abort "error: #{e}."
|
82
72
|
else
|
@@ -231,21 +221,23 @@ module Commander
|
|
231
221
|
# Get active command within arguments passed to this runner.
|
232
222
|
|
233
223
|
def active_command
|
234
|
-
@
|
224
|
+
@active_command ||= command(command_name_from_args)
|
235
225
|
end
|
236
226
|
|
237
227
|
##
|
238
228
|
# Attempts to locate a command name from within the arguments.
|
239
229
|
# Supports multi-word commands, using the largest possible match.
|
230
|
+
# Returns the default command, if no valid commands found in the args.
|
240
231
|
|
241
232
|
def command_name_from_args
|
242
|
-
@
|
233
|
+
@command_name_from_args ||= (longest_valid_command_name_from(@args) || @default_command)
|
243
234
|
end
|
244
235
|
|
245
236
|
##
|
246
237
|
# Returns array of valid command names found within _args_.
|
247
238
|
|
248
239
|
def valid_command_names_from(*args)
|
240
|
+
remove_global_options options, args
|
249
241
|
arg_string = args.delete_if { |value| value =~ /^-/ }.join ' '
|
250
242
|
commands.keys.find_all { |name| name if arg_string =~ /^#{name}\b/ }
|
251
243
|
end
|
@@ -254,7 +246,7 @@ module Commander
|
|
254
246
|
# Help formatter instance.
|
255
247
|
|
256
248
|
def help_formatter
|
257
|
-
@
|
249
|
+
@help_formatter ||= program(:help_formatter).new self
|
258
250
|
end
|
259
251
|
|
260
252
|
##
|
@@ -303,7 +295,7 @@ module Commander
|
|
303
295
|
if args.empty?
|
304
296
|
say help_formatter.render
|
305
297
|
else
|
306
|
-
command = command
|
298
|
+
command = command(longest_valid_command_name_from(args))
|
307
299
|
begin
|
308
300
|
require_valid_command command
|
309
301
|
rescue InvalidCommandError => e
|
@@ -328,26 +320,32 @@ module Commander
|
|
328
320
|
# again for the command.
|
329
321
|
|
330
322
|
def remove_global_options(options, args)
|
331
|
-
# TODO: refactor with flipflop, please TJ ! have time to refactor me !
|
332
323
|
options.each do |option|
|
333
|
-
switches = option[:switches]
|
324
|
+
switches = option[:switches]
|
334
325
|
next if switches.empty?
|
335
326
|
|
336
|
-
|
337
|
-
switches.map! { |s| s[0, s.index('=') || s.index(' ') || s.length] }
|
338
|
-
end
|
339
|
-
|
327
|
+
option_takes_argument = switches.any? { |s| s =~ /[ =]/ }
|
340
328
|
switches = expand_optionally_negative_switches(switches)
|
341
329
|
|
342
|
-
|
343
|
-
args.delete_if do |
|
344
|
-
if
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
330
|
+
option_argument_needs_removal = false
|
331
|
+
args.delete_if do |token|
|
332
|
+
break if token == '--'
|
333
|
+
|
334
|
+
# Use just the portion of the token before the = when
|
335
|
+
# comparing switches.
|
336
|
+
index_of_equals = token.index('=') if option_takes_argument
|
337
|
+
token = token[0, index_of_equals] if index_of_equals
|
338
|
+
token_contains_option_argument = !index_of_equals.nil?
|
339
|
+
|
340
|
+
if switches.any? { |s| s[0, token.length] == token }
|
341
|
+
option_argument_needs_removal =
|
342
|
+
option_takes_argument && !token_contains_option_argument
|
343
|
+
true
|
344
|
+
elsif option_argument_needs_removal && token !~ /^-/
|
345
|
+
option_argument_needs_removal = false
|
346
|
+
true
|
349
347
|
else
|
350
|
-
|
348
|
+
option_argument_needs_removal = false
|
351
349
|
false
|
352
350
|
end
|
353
351
|
end
|
@@ -395,7 +393,7 @@ module Commander
|
|
395
393
|
def global_option_proc(switches, &block)
|
396
394
|
lambda do |value|
|
397
395
|
unless active_command.nil?
|
398
|
-
active_command.
|
396
|
+
active_command.global_options << [Runner.switch_to_sym(switches.last), value]
|
399
397
|
end
|
400
398
|
yield value if block && !value.nil?
|
401
399
|
end
|
@@ -448,7 +446,17 @@ module Commander
|
|
448
446
|
end
|
449
447
|
|
450
448
|
def say(*args) #:nodoc:
|
451
|
-
|
449
|
+
HighLine.default_instance.say(*args)
|
450
|
+
end
|
451
|
+
|
452
|
+
private
|
453
|
+
|
454
|
+
##
|
455
|
+
# Attempts to locate a command name from within the provided arguments.
|
456
|
+
# Supports multi-word commands, using the largest possible match.
|
457
|
+
|
458
|
+
def longest_valid_command_name_from(args)
|
459
|
+
valid_command_names_from(*args.dup).max
|
452
460
|
end
|
453
461
|
end
|
454
462
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'tempfile'
|
2
4
|
require 'shellwords'
|
3
5
|
|
@@ -66,7 +68,7 @@ module Commander
|
|
66
68
|
|
67
69
|
def say_ok(*args)
|
68
70
|
args.each do |arg|
|
69
|
-
say
|
71
|
+
say HighLine.default_instance.color(arg, :green)
|
70
72
|
end
|
71
73
|
end
|
72
74
|
|
@@ -80,7 +82,7 @@ module Commander
|
|
80
82
|
|
81
83
|
def say_warning(*args)
|
82
84
|
args.each do |arg|
|
83
|
-
say
|
85
|
+
say HighLine.default_instance.color(arg, :yellow)
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
@@ -94,7 +96,7 @@ module Commander
|
|
94
96
|
|
95
97
|
def say_error(*args)
|
96
98
|
args.each do |arg|
|
97
|
-
say
|
99
|
+
say HighLine.default_instance.color(arg, :red)
|
98
100
|
end
|
99
101
|
end
|
100
102
|
|
@@ -113,7 +115,7 @@ module Commander
|
|
113
115
|
# * highligh: on_<color>
|
114
116
|
|
115
117
|
def color(*args)
|
116
|
-
say
|
118
|
+
say HighLine.default_instance.color(*args)
|
117
119
|
end
|
118
120
|
|
119
121
|
##
|
@@ -218,20 +220,16 @@ module Commander
|
|
218
220
|
#
|
219
221
|
|
220
222
|
def io(input = nil, output = nil, &block)
|
223
|
+
orig_stdin, orig_stdout = $stdin, $stdout
|
221
224
|
$stdin = File.new(input) if input
|
222
225
|
$stdout = File.new(output, 'r+') if output
|
223
226
|
return unless block
|
227
|
+
|
224
228
|
yield
|
229
|
+
$stdin, $stdout = orig_stdin, orig_stdout
|
225
230
|
reset_io
|
226
231
|
end
|
227
232
|
|
228
|
-
##
|
229
|
-
# Reset IO to initial constant streams.
|
230
|
-
|
231
|
-
def reset_io
|
232
|
-
$stdin, $stdout = STDIN, STDOUT
|
233
|
-
end
|
234
|
-
|
235
233
|
##
|
236
234
|
# Find an editor available in path. Optionally supply the _preferred_
|
237
235
|
# editor. Returns the name as a string, nil if none is available.
|
@@ -274,6 +272,7 @@ module Commander
|
|
274
272
|
def enable_paging
|
275
273
|
return unless $stdout.tty?
|
276
274
|
return unless Process.respond_to? :fork
|
275
|
+
|
277
276
|
read, write = IO.pipe
|
278
277
|
|
279
278
|
# Kernel.fork is not supported on all platforms and configurations.
|
@@ -324,12 +323,12 @@ module Commander
|
|
324
323
|
# Implements ask_for_CLASS methods.
|
325
324
|
|
326
325
|
module AskForClass
|
327
|
-
DEPRECATED_CONSTANTS = [
|
326
|
+
DEPRECATED_CONSTANTS = %i[Config TimeoutError MissingSourceFile NIL TRUE FALSE Fixnum Bignum Data].freeze
|
328
327
|
|
329
328
|
# define methods for common classes
|
330
329
|
[Float, Integer, String, Symbol, Regexp, Array, File, Pathname].each do |klass|
|
331
330
|
define_method "ask_for_#{klass.to_s.downcase}" do |prompt|
|
332
|
-
|
331
|
+
HighLine.default_instance.ask(prompt, klass)
|
333
332
|
end
|
334
333
|
end
|
335
334
|
|
@@ -338,20 +337,26 @@ module Commander
|
|
338
337
|
if arguments.count != 1
|
339
338
|
fail ArgumentError, "wrong number of arguments (given #{arguments.count}, expected 1)"
|
340
339
|
end
|
340
|
+
|
341
341
|
prompt = arguments.first
|
342
342
|
requested_class = Regexp.last_match[1]
|
343
343
|
|
344
344
|
# All Classes that respond to #parse
|
345
345
|
# Ignore constants that trigger deprecation warnings
|
346
346
|
available_classes = (Object.constants - DEPRECATED_CONSTANTS).map do |const|
|
347
|
-
|
348
|
-
|
349
|
-
|
347
|
+
begin
|
348
|
+
Object.const_get(const)
|
349
|
+
rescue RuntimeError
|
350
|
+
# Rescue errors in Ruby 3 for SortedSet:
|
351
|
+
# The `SortedSet` class has been extracted from the `set` library.
|
352
|
+
end
|
353
|
+
end.compact.select do |const|
|
354
|
+
const.instance_of?(Class) && const.respond_to?(:parse)
|
350
355
|
end
|
351
356
|
|
352
357
|
klass = available_classes.find { |k| k.to_s.downcase == requested_class }
|
353
358
|
if klass
|
354
|
-
|
359
|
+
HighLine.default_instance.ask(prompt, klass)
|
355
360
|
else
|
356
361
|
super
|
357
362
|
end
|
@@ -498,7 +503,7 @@ module Commander
|
|
498
503
|
steps_remaining: steps_remaining,
|
499
504
|
total_steps: @total_steps,
|
500
505
|
time_elapsed: format('%0.2fs', time_elapsed),
|
501
|
-
time_remaining: @step
|
506
|
+
time_remaining: @step.positive? ? format('%0.2fs', time_remaining) : '',
|
502
507
|
}.merge! @tokens
|
503
508
|
end
|
504
509
|
|
@@ -507,11 +512,12 @@ module Commander
|
|
507
512
|
|
508
513
|
def show
|
509
514
|
return if finished?
|
515
|
+
|
510
516
|
erase_line
|
511
517
|
if completed?
|
512
|
-
|
518
|
+
HighLine.default_instance.say UI.replace_tokens(@complete_message, generate_tokens) if @complete_message.is_a? String
|
513
519
|
else
|
514
|
-
|
520
|
+
HighLine.default_instance.say UI.replace_tokens(@format, generate_tokens) << ' '
|
515
521
|
end
|
516
522
|
end
|
517
523
|
|
@@ -544,7 +550,7 @@ module Commander
|
|
544
550
|
|
545
551
|
def erase_line
|
546
552
|
# highline does not expose the output stream
|
547
|
-
|
553
|
+
HighLine.default_instance.instance_variable_get('@output').print "\r\e[K"
|
548
554
|
end
|
549
555
|
end
|
550
556
|
end
|
data/lib/commander/version.rb
CHANGED
data/spec/command_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Commander::Command do
|
@@ -164,6 +166,33 @@ describe Commander::Command do
|
|
164
166
|
end
|
165
167
|
@command.run '--interval', '15'
|
166
168
|
end
|
169
|
+
|
170
|
+
describe 'given a global option' do
|
171
|
+
before do
|
172
|
+
@command.global_options << [:global_option, 'gvalue']
|
173
|
+
end
|
174
|
+
|
175
|
+
describe 'and no command specific arguments' do
|
176
|
+
it 'provides the global option to the command action' do
|
177
|
+
@command.when_called { |_, options| expect(options.global_option).to eq('gvalue') }
|
178
|
+
@command.run
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe 'and a command specific option' do
|
183
|
+
it 'provides the global option to the command action' do
|
184
|
+
@command.when_called { |_, options| expect(options.global_option).to eq('gvalue') }
|
185
|
+
@command.run '--verbose'
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe 'and a command specific argument' do
|
190
|
+
it 'provides the global option to the command action' do
|
191
|
+
@command.when_called { |_, options| expect(options.global_option).to eq('gvalue') }
|
192
|
+
@command.run 'argument'
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
167
196
|
end
|
168
197
|
end
|
169
198
|
end
|