commander 4.4.6 → 4.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|