commander 5.0.0 → 6.0.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/.github/workflows/ci.yml +67 -0
- data/.gitignore +0 -1
- data/.rubocop.yml +1 -4
- data/.rubocop_todo.yml +46 -42
- data/.ruby-version +1 -0
- data/Gemfile +10 -0
- data/History.rdoc +7 -0
- data/README.md +7 -32
- data/commander.gemspec +3 -8
- data/lib/commander/blank.rb +9 -0
- data/lib/commander/command.rb +26 -11
- data/lib/commander/configure.rb +30 -2
- data/lib/commander/core_ext/array.rb +3 -1
- data/lib/commander/core_ext/object.rb +3 -1
- data/lib/commander/delegates.rb +10 -0
- data/lib/commander/help_formatters/base.rb +11 -7
- data/lib/commander/help_formatters/terminal.rb +13 -5
- data/lib/commander/help_formatters/terminal_compact.rb +8 -5
- data/lib/commander/help_formatters.rb +28 -0
- data/lib/commander/methods.rb +12 -0
- data/lib/commander/platform.rb +6 -0
- data/lib/commander/runner.rb +17 -38
- data/lib/commander/user_interaction.rb +30 -59
- data/lib/commander/version.rb +1 -1
- metadata +12 -90
- data/.travis.yml +0 -12
- data/Manifest +0 -38
- data/spec/command_spec.rb +0 -198
- data/spec/configure_spec.rb +0 -39
- data/spec/core_ext/array_spec.rb +0 -20
- data/spec/core_ext/object_spec.rb +0 -21
- data/spec/help_formatters/terminal_compact_spec.rb +0 -71
- data/spec/help_formatters/terminal_spec.rb +0 -69
- data/spec/methods_spec.rb +0 -63
- data/spec/runner_spec.rb +0 -761
- data/spec/spec_helper.rb +0 -89
- data/spec/ui_spec.rb +0 -32
|
@@ -4,13 +4,16 @@ require 'erb'
|
|
|
4
4
|
|
|
5
5
|
module Commander
|
|
6
6
|
module HelpFormatter
|
|
7
|
+
##
|
|
8
|
+
# A condensed variant of HelpFormatter::Terminal, using the ERB
|
|
9
|
+
# templates in the neighboring +terminal_compact/+ directory. Enable
|
|
10
|
+
# it with <tt>program :help_formatter, :compact</tt>.
|
|
7
11
|
class TerminalCompact < Terminal
|
|
12
|
+
##
|
|
13
|
+
# Compile the ERB template named _name_ (+:help+ or
|
|
14
|
+
# +:command_help+) from this formatter's template directory.
|
|
8
15
|
def template(name)
|
|
9
|
-
|
|
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
|
|
16
|
+
ERB.new(File.read(File.join(File.dirname(__FILE__), 'terminal_compact', "#{name}.erb")), trim_mode: '-')
|
|
14
17
|
end
|
|
15
18
|
end
|
|
16
19
|
end
|
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Commander
|
|
4
|
+
##
|
|
5
|
+
# = Help Formatter
|
|
6
|
+
#
|
|
7
|
+
# Commander's help formatters control the output when
|
|
8
|
+
# either the help command, or --help switch are called.
|
|
9
|
+
# The default formatter is Commander::HelpFormatter::Terminal.
|
|
4
10
|
module HelpFormatter
|
|
5
11
|
autoload :Base, 'commander/help_formatters/base'
|
|
6
12
|
autoload :Terminal, 'commander/help_formatters/terminal'
|
|
7
13
|
autoload :TerminalCompact, 'commander/help_formatters/terminal_compact'
|
|
8
14
|
|
|
15
|
+
##
|
|
16
|
+
# Exposes _target_'s binding to an ERB template, letting the template
|
|
17
|
+
# reference _target_'s instance variables and private methods as if
|
|
18
|
+
# they were local variables/bare method calls. Subclasses can inject
|
|
19
|
+
# additional template-local variables by overriding #decorate_binding.
|
|
9
20
|
class Context
|
|
10
21
|
def initialize(target)
|
|
11
22
|
@target = target
|
|
12
23
|
end
|
|
13
24
|
|
|
25
|
+
##
|
|
26
|
+
# The (possibly decorated) binding of _target_, suitable for
|
|
27
|
+
# passing to ERB#result.
|
|
14
28
|
def get_binding
|
|
15
29
|
@target.instance_eval { binding }.tap do |bind|
|
|
16
30
|
decorate_binding(bind)
|
|
@@ -22,20 +36,31 @@ module Commander
|
|
|
22
36
|
end
|
|
23
37
|
end
|
|
24
38
|
|
|
39
|
+
##
|
|
40
|
+
# A Context for rendering the global help template (see
|
|
41
|
+
# HelpFormatter::Terminal#render), decorating the target Runner's
|
|
42
|
+
# binding with +max_command_length+/+max_aliases_length+ locals so
|
|
43
|
+
# the template can align its columns.
|
|
25
44
|
class ProgramContext < Context
|
|
26
45
|
def decorate_binding(bind)
|
|
27
46
|
bind.eval("max_command_length = #{max_command_length(bind)}")
|
|
28
47
|
bind.eval("max_aliases_length = #{max_aliases_length(bind)}")
|
|
29
48
|
end
|
|
30
49
|
|
|
50
|
+
##
|
|
51
|
+
# Length, in characters, of the longest registered command name.
|
|
31
52
|
def max_command_length(bind)
|
|
32
53
|
max_key_length(bind.eval('@commands'))
|
|
33
54
|
end
|
|
34
55
|
|
|
56
|
+
##
|
|
57
|
+
# Length, in characters, of the longest registered command alias.
|
|
35
58
|
def max_aliases_length(bind)
|
|
36
59
|
max_key_length(bind.eval('@aliases'))
|
|
37
60
|
end
|
|
38
61
|
|
|
62
|
+
##
|
|
63
|
+
# Length of the longest key in _hash_, or _default_ when _hash_ is empty.
|
|
39
64
|
def max_key_length(hash, default = 20)
|
|
40
65
|
longest = hash.keys.max_by(&:size)
|
|
41
66
|
longest ? longest.size : default
|
|
@@ -44,6 +69,9 @@ module Commander
|
|
|
44
69
|
|
|
45
70
|
module_function
|
|
46
71
|
|
|
72
|
+
##
|
|
73
|
+
# Indent each line of _text_ (after the first) by _amount_ spaces.
|
|
74
|
+
# Used by the help templates to align multi-line descriptions.
|
|
47
75
|
def indent(amount, text)
|
|
48
76
|
text.to_s.gsub("\n", "\n#{' ' * amount}")
|
|
49
77
|
end
|
data/lib/commander/methods.rb
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Commander
|
|
4
|
+
##
|
|
5
|
+
# = Methods
|
|
6
|
+
#
|
|
7
|
+
# The primary entry point for adding Commander's DSL to an object.
|
|
8
|
+
# Bundles Commander::UI (terminal output/prompting helpers),
|
|
9
|
+
# Commander::UI::AskForClass (the +ask_for_*+ family of methods), and
|
|
10
|
+
# Commander::Delegates (the +command+/+program+/+run!+ DSL) into a
|
|
11
|
+
# single mixin. <tt>require 'commander/import'</tt> includes this into
|
|
12
|
+
# the top-level Object; embedders typically include it directly instead.
|
|
13
|
+
#
|
|
14
|
+
# When connected to a sufficiently wide terminal, also configures
|
|
15
|
+
# HighLine to wrap output five columns short of the terminal width.
|
|
4
16
|
module Methods
|
|
5
17
|
include Commander::UI
|
|
6
18
|
include Commander::UI::AskForClass
|
data/lib/commander/platform.rb
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Commander
|
|
4
|
+
##
|
|
5
|
+
# = Platform
|
|
6
|
+
#
|
|
7
|
+
# Small helpers for detecting the running Ruby engine.
|
|
4
8
|
module Platform
|
|
9
|
+
##
|
|
10
|
+
# Whether the current process is running under JRuby.
|
|
5
11
|
def self.jruby?
|
|
6
12
|
defined?(RUBY_ENGINE) && (RUBY_ENGINE == 'jruby')
|
|
7
13
|
end
|
data/lib/commander/runner.rb
CHANGED
|
@@ -3,13 +3,23 @@
|
|
|
3
3
|
require 'optparse'
|
|
4
4
|
|
|
5
5
|
module Commander
|
|
6
|
+
##
|
|
7
|
+
# = Runner
|
|
8
|
+
#
|
|
9
|
+
# The engine behind Commander's DSL: holds the registered commands,
|
|
10
|
+
# global options, and program metadata, parses ARGV, and dispatches to
|
|
11
|
+
# the matched Commander::Command. Accessed as a singleton
|
|
12
|
+
# (Runner.instance) by Commander::Delegates so that the top-level
|
|
13
|
+
# +command+/+program+/+run!+ methods all operate on the same runner.
|
|
6
14
|
class Runner
|
|
7
15
|
#--
|
|
8
16
|
# Exceptions
|
|
9
17
|
#++
|
|
10
18
|
|
|
19
|
+
# Raised when required program information (e.g. :version) is missing.
|
|
11
20
|
class CommandError < StandardError; end
|
|
12
21
|
|
|
22
|
+
# Raised when no command matches the given arguments.
|
|
13
23
|
class InvalidCommandError < CommandError; end
|
|
14
24
|
|
|
15
25
|
attr_reader :commands, :options, :help_formatter_aliases
|
|
@@ -17,7 +27,6 @@ module Commander
|
|
|
17
27
|
##
|
|
18
28
|
# Initialize a new command runner. Optionally
|
|
19
29
|
# supplying _args_ for mocking, or arbitrary usage.
|
|
20
|
-
|
|
21
30
|
def initialize(args = ARGV)
|
|
22
31
|
@args, @commands, @aliases, @options = args, {}, {}, []
|
|
23
32
|
@help_formatter_aliases = help_formatter_alias_defaults
|
|
@@ -29,14 +38,12 @@ module Commander
|
|
|
29
38
|
|
|
30
39
|
##
|
|
31
40
|
# Return singleton Runner instance.
|
|
32
|
-
|
|
33
41
|
def self.instance
|
|
34
42
|
@instance ||= new
|
|
35
43
|
end
|
|
36
44
|
|
|
37
45
|
##
|
|
38
46
|
# Run command parsing and execution process.
|
|
39
|
-
|
|
40
47
|
def run!
|
|
41
48
|
trace = @always_trace || false
|
|
42
49
|
require_program :version, :description
|
|
@@ -78,14 +85,12 @@ module Commander
|
|
|
78
85
|
|
|
79
86
|
##
|
|
80
87
|
# Return program version.
|
|
81
|
-
|
|
82
88
|
def version
|
|
83
89
|
format('%s %s', program(:name), program(:version))
|
|
84
90
|
end
|
|
85
91
|
|
|
86
92
|
##
|
|
87
93
|
# Enable tracing on all executions (bypasses --trace)
|
|
88
|
-
|
|
89
94
|
def always_trace!
|
|
90
95
|
@always_trace = true
|
|
91
96
|
@never_trace = false
|
|
@@ -93,7 +98,6 @@ module Commander
|
|
|
93
98
|
|
|
94
99
|
##
|
|
95
100
|
# Hide the trace option from the help menus and don't add it as a global option
|
|
96
|
-
|
|
97
101
|
def never_trace!
|
|
98
102
|
@never_trace = true
|
|
99
103
|
@always_trace = false
|
|
@@ -127,7 +131,6 @@ module Commander
|
|
|
127
131
|
# :help_paging Flag for toggling help paging
|
|
128
132
|
# :int_message Message to display when interrupted (CTRL + C)
|
|
129
133
|
#
|
|
130
|
-
|
|
131
134
|
def program(key, *args, &block)
|
|
132
135
|
if key == :help && !args.empty?
|
|
133
136
|
@program[:help] ||= {}
|
|
@@ -138,7 +141,7 @@ module Commander
|
|
|
138
141
|
@program[key] = block
|
|
139
142
|
else
|
|
140
143
|
unless args.empty?
|
|
141
|
-
@program[key] = args.
|
|
144
|
+
@program[key] = args.length == 1 ? args[0] : args
|
|
142
145
|
end
|
|
143
146
|
@program[key]
|
|
144
147
|
end
|
|
@@ -157,7 +160,6 @@ module Commander
|
|
|
157
160
|
# end
|
|
158
161
|
# end
|
|
159
162
|
#
|
|
160
|
-
|
|
161
163
|
def command(name, &block)
|
|
162
164
|
yield add_command(Commander::Command.new(name)) if block
|
|
163
165
|
@commands[name.to_s]
|
|
@@ -166,7 +168,6 @@ module Commander
|
|
|
166
168
|
##
|
|
167
169
|
# Add a global option; follows the same syntax as Command#option
|
|
168
170
|
# This would be used for switches such as --version, --trace, etc.
|
|
169
|
-
|
|
170
171
|
def global_option(*args, &block)
|
|
171
172
|
switches, description = Runner.separate_switches_from_description(*args)
|
|
172
173
|
@options << {
|
|
@@ -180,7 +181,6 @@ module Commander
|
|
|
180
181
|
##
|
|
181
182
|
# Alias command _name_ with _alias_name_. Optionally _args_ may be passed
|
|
182
183
|
# as if they were being passed straight to the original command via the command-line.
|
|
183
|
-
|
|
184
184
|
def alias_command(alias_name, name, *args)
|
|
185
185
|
@commands[alias_name.to_s] = command name
|
|
186
186
|
@aliases[alias_name.to_s] = args
|
|
@@ -189,37 +189,32 @@ module Commander
|
|
|
189
189
|
##
|
|
190
190
|
# Default command _name_ to be used when no other
|
|
191
191
|
# command is found in the arguments.
|
|
192
|
-
|
|
193
192
|
def default_command(name)
|
|
194
193
|
@default_command = name
|
|
195
194
|
end
|
|
196
195
|
|
|
197
196
|
##
|
|
198
197
|
# Add a command object to this runner.
|
|
199
|
-
|
|
200
198
|
def add_command(command)
|
|
201
199
|
@commands[command.name] = command
|
|
202
200
|
end
|
|
203
201
|
|
|
204
202
|
##
|
|
205
203
|
# Check if command _name_ is an alias.
|
|
206
|
-
|
|
207
204
|
def alias?(name)
|
|
208
205
|
@aliases.include? name.to_s
|
|
209
206
|
end
|
|
210
207
|
|
|
211
208
|
##
|
|
212
209
|
# Check if a command _name_ exists.
|
|
213
|
-
|
|
214
210
|
def command_exists?(name)
|
|
215
211
|
@commands[name.to_s]
|
|
216
212
|
end
|
|
217
213
|
|
|
218
|
-
|
|
214
|
+
# :stopdoc:
|
|
219
215
|
|
|
220
216
|
##
|
|
221
217
|
# Get active command within arguments passed to this runner.
|
|
222
|
-
|
|
223
218
|
def active_command
|
|
224
219
|
@active_command ||= command(command_name_from_args)
|
|
225
220
|
end
|
|
@@ -228,14 +223,12 @@ module Commander
|
|
|
228
223
|
# Attempts to locate a command name from within the arguments.
|
|
229
224
|
# Supports multi-word commands, using the largest possible match.
|
|
230
225
|
# Returns the default command, if no valid commands found in the args.
|
|
231
|
-
|
|
232
226
|
def command_name_from_args
|
|
233
|
-
@command_name_from_args ||=
|
|
227
|
+
@command_name_from_args ||= longest_valid_command_name_from(@args) || @default_command
|
|
234
228
|
end
|
|
235
229
|
|
|
236
230
|
##
|
|
237
231
|
# Returns array of valid command names found within _args_.
|
|
238
|
-
|
|
239
232
|
def valid_command_names_from(*args)
|
|
240
233
|
remove_global_options options, args
|
|
241
234
|
arg_string = args.delete_if { |value| value =~ /^-/ }.join ' '
|
|
@@ -244,14 +237,12 @@ module Commander
|
|
|
244
237
|
|
|
245
238
|
##
|
|
246
239
|
# Help formatter instance.
|
|
247
|
-
|
|
248
240
|
def help_formatter
|
|
249
241
|
@help_formatter ||= program(:help_formatter).new self
|
|
250
242
|
end
|
|
251
243
|
|
|
252
244
|
##
|
|
253
245
|
# Return arguments without the command name.
|
|
254
|
-
|
|
255
246
|
def args_without_command_name
|
|
256
247
|
removed = []
|
|
257
248
|
parts = command_name_from_args.split rescue []
|
|
@@ -262,7 +253,6 @@ module Commander
|
|
|
262
253
|
|
|
263
254
|
##
|
|
264
255
|
# Returns hash of help formatter alias defaults.
|
|
265
|
-
|
|
266
256
|
def help_formatter_alias_defaults
|
|
267
257
|
{
|
|
268
258
|
compact: HelpFormatter::TerminalCompact,
|
|
@@ -271,7 +261,6 @@ module Commander
|
|
|
271
261
|
|
|
272
262
|
##
|
|
273
263
|
# Returns hash of program defaults.
|
|
274
|
-
|
|
275
264
|
def program_defaults
|
|
276
265
|
{
|
|
277
266
|
help_formatter: HelpFormatter::Terminal,
|
|
@@ -283,7 +272,6 @@ module Commander
|
|
|
283
272
|
##
|
|
284
273
|
# Creates default commands such as 'help' which is
|
|
285
274
|
# essentially the same as using the --help switch.
|
|
286
|
-
|
|
287
275
|
def create_default_commands
|
|
288
276
|
command :help do |c|
|
|
289
277
|
c.syntax = 'commander help [command]'
|
|
@@ -309,7 +297,6 @@ module Commander
|
|
|
309
297
|
|
|
310
298
|
##
|
|
311
299
|
# Raises InvalidCommandError when a _command_ is not found.
|
|
312
|
-
|
|
313
300
|
def require_valid_command(command = active_command)
|
|
314
301
|
fail InvalidCommandError, 'invalid command', caller if command.nil?
|
|
315
302
|
end
|
|
@@ -318,7 +305,6 @@ module Commander
|
|
|
318
305
|
# Removes global _options_ from _args_. This prevents an invalid
|
|
319
306
|
# option error from occurring when options are parsed
|
|
320
307
|
# again for the command.
|
|
321
|
-
|
|
322
308
|
def remove_global_options(options, args)
|
|
323
309
|
options.each do |option|
|
|
324
310
|
switches = option[:switches]
|
|
@@ -358,8 +344,8 @@ module Commander
|
|
|
358
344
|
def expand_optionally_negative_switches(switches)
|
|
359
345
|
switches.reduce([]) do |memo, val|
|
|
360
346
|
if val =~ /\[no-\]/
|
|
361
|
-
memo << val.gsub(
|
|
362
|
-
memo << val.gsub(
|
|
347
|
+
memo << val.gsub('[no-]', '')
|
|
348
|
+
memo << val.gsub('[no-]', 'no-')
|
|
363
349
|
else
|
|
364
350
|
memo << val
|
|
365
351
|
end
|
|
@@ -368,7 +354,6 @@ module Commander
|
|
|
368
354
|
|
|
369
355
|
##
|
|
370
356
|
# Parse global command options.
|
|
371
|
-
|
|
372
357
|
def parse_global_options
|
|
373
358
|
parser = options.inject(OptionParser.new) do |options, option|
|
|
374
359
|
options.on(*option[:args], &global_option_proc(option[:switches], &option[:proc]))
|
|
@@ -389,7 +374,6 @@ module Commander
|
|
|
389
374
|
# This functionality works whether a block is present for the global
|
|
390
375
|
# option or not, so simple switches such as --verbose can be used
|
|
391
376
|
# without a block, and used throughout all commands.
|
|
392
|
-
|
|
393
377
|
def global_option_proc(switches, &block)
|
|
394
378
|
lambda do |value|
|
|
395
379
|
unless active_command.nil?
|
|
@@ -401,7 +385,6 @@ module Commander
|
|
|
401
385
|
|
|
402
386
|
##
|
|
403
387
|
# Raises a CommandError when the program any of the _keys_ are not present, or empty.
|
|
404
|
-
|
|
405
388
|
def require_program(*keys)
|
|
406
389
|
keys.each do |key|
|
|
407
390
|
fail CommandError, "program #{key} required" if program(key).nil? || program(key).empty?
|
|
@@ -410,7 +393,6 @@ module Commander
|
|
|
410
393
|
|
|
411
394
|
##
|
|
412
395
|
# Return switches and description separated from the _args_ passed.
|
|
413
|
-
|
|
414
396
|
def self.separate_switches_from_description(*args)
|
|
415
397
|
switches = args.find_all { |arg| arg.to_s =~ /^-/ }
|
|
416
398
|
description = args.last if args.last.is_a?(String) && !args.last.match(/^-/)
|
|
@@ -428,14 +410,12 @@ module Commander
|
|
|
428
410
|
# --file FILE # => :file
|
|
429
411
|
# --list of,things # => :list
|
|
430
412
|
#
|
|
431
|
-
|
|
432
413
|
def self.switch_to_sym(switch)
|
|
433
|
-
switch.scan(/[
|
|
414
|
+
switch.scan(/[-\]](\w+)/).join('_').to_sym rescue nil
|
|
434
415
|
end
|
|
435
416
|
|
|
436
417
|
##
|
|
437
418
|
# Run the active command.
|
|
438
|
-
|
|
439
419
|
def run_active_command
|
|
440
420
|
require_valid_command
|
|
441
421
|
if alias? command_name_from_args
|
|
@@ -445,7 +425,7 @@ module Commander
|
|
|
445
425
|
end
|
|
446
426
|
end
|
|
447
427
|
|
|
448
|
-
def say(*args)
|
|
428
|
+
def say(*args) # :nodoc:
|
|
449
429
|
HighLine.default_instance.say(*args)
|
|
450
430
|
end
|
|
451
431
|
|
|
@@ -454,7 +434,6 @@ module Commander
|
|
|
454
434
|
##
|
|
455
435
|
# Attempts to locate a command name from within the provided arguments.
|
|
456
436
|
# Supports multi-word commands, using the largest possible match.
|
|
457
|
-
|
|
458
437
|
def longest_valid_command_name_from(args)
|
|
459
438
|
valid_command_names_from(*args.dup).max
|
|
460
439
|
end
|