cri 2.2.1 → 2.3.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.
- data/NEWS.md +6 -0
- data/cri.gemspec +2 -0
- data/lib/cri.rb +1 -1
- data/lib/cri/command.rb +56 -24
- data/lib/cri/command_dsl.rb +10 -0
- data/lib/cri/commands/basic_help.rb +6 -2
- data/lib/cri/commands/basic_root.rb +2 -2
- data/lib/cri/core_ext/string.rb +19 -0
- data/test/helper.rb +4 -1
- data/test/test_basic_root.rb +17 -0
- data/test/test_command.rb +51 -2
- metadata +21 -4
data/NEWS.md
CHANGED
data/cri.gemspec
CHANGED
@@ -18,6 +18,8 @@ Gem::Specification.new do |s|
|
|
18
18
|
[ 'cri.gemspec', '.gemtest' ]
|
19
19
|
s.require_paths = [ 'lib' ]
|
20
20
|
|
21
|
+
s.add_dependency('colored', '>= 1.2')
|
22
|
+
|
21
23
|
s.rdoc_options = [ '--main', 'README.md' ]
|
22
24
|
s.extra_rdoc_files = [ 'ChangeLog', 'LICENSE', 'README.md', 'NEWS.md' ]
|
23
25
|
end
|
data/lib/cri.rb
CHANGED
data/lib/cri/command.rb
CHANGED
@@ -70,6 +70,11 @@ module Cri
|
|
70
70
|
# supercommands’ names.
|
71
71
|
attr_accessor :usage
|
72
72
|
|
73
|
+
# @return [Boolean] true if the command is hidden (e.g. because it is
|
74
|
+
# deprecated), false otherwise
|
75
|
+
attr_accessor :hidden
|
76
|
+
alias_method :hidden?, :hidden
|
77
|
+
|
73
78
|
# @return [Array<Hash>] The list of option definitions
|
74
79
|
attr_accessor :option_definitions
|
75
80
|
|
@@ -292,46 +297,73 @@ module Cri
|
|
292
297
|
end
|
293
298
|
|
294
299
|
# @return [String] The help text for this command
|
295
|
-
def help
|
300
|
+
def help(params={})
|
301
|
+
is_verbose = params.fetch(:verbose, false)
|
302
|
+
|
296
303
|
text = ''
|
297
304
|
|
305
|
+
# Append name and summary
|
306
|
+
if summary
|
307
|
+
text << "name".formatted_as_title << "\n"
|
308
|
+
text << " #{name.formatted_as_command} - #{summary}" << "\n"
|
309
|
+
unless aliases.empty?
|
310
|
+
text << " aliases: " << aliases.map { |a| a.formatted_as_command }.join(' ') << "\n"
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
298
314
|
# Append usage
|
299
315
|
if usage
|
300
316
|
path = [ self.supercommand ]
|
301
317
|
path.unshift(path[0].supercommand) until path[0].nil?
|
302
|
-
|
303
|
-
|
304
|
-
end
|
318
|
+
formatted_usage = usage.gsub(/^([^\s]+)/) { |m| m.formatted_as_command }
|
319
|
+
full_usage = path[1..-1].map { |c| c.name.formatted_as_command + ' ' }.join + formatted_usage
|
305
320
|
|
306
|
-
# Append aliases
|
307
|
-
unless aliases.empty?
|
308
321
|
text << "\n"
|
309
|
-
text << "
|
310
|
-
|
311
|
-
|
312
|
-
# Append short description
|
313
|
-
if summary
|
314
|
-
text << "\n"
|
315
|
-
text << summary + "\n"
|
322
|
+
text << "usage".formatted_as_title << "\n"
|
323
|
+
text << full_usage.wrap_and_indent(78, 4) << "\n"
|
316
324
|
end
|
317
325
|
|
318
326
|
# Append long description
|
319
327
|
if description
|
320
328
|
text << "\n"
|
329
|
+
text << "description".formatted_as_title << "\n"
|
321
330
|
text << description.wrap_and_indent(78, 4) + "\n"
|
322
331
|
end
|
323
332
|
|
324
333
|
# Append subcommands
|
325
|
-
unless self.
|
334
|
+
unless self.subcommands.empty?
|
326
335
|
text << "\n"
|
327
|
-
text << (self.supercommand ? 'subcommands' : 'commands')
|
336
|
+
text << (self.supercommand ? 'subcommands' : 'commands').formatted_as_title
|
328
337
|
text << "\n"
|
329
|
-
|
330
|
-
self.
|
338
|
+
|
339
|
+
visible_cmds, invisible_cmds = self.subcommands.partition { |c| !c.hidden? }
|
340
|
+
|
341
|
+
commands_for_length = is_verbose ? self.subcommands : visible_cmds
|
342
|
+
length = commands_for_length.map { |c| c.name.formatted_as_command.size }.max
|
343
|
+
|
344
|
+
# Visible
|
345
|
+
visible_cmds.sort_by { |cmd| cmd.name }.each do |cmd|
|
331
346
|
text << sprintf(" %-#{length+4}s %s\n",
|
332
|
-
cmd.name,
|
347
|
+
cmd.name.formatted_as_command,
|
333
348
|
cmd.summary)
|
334
349
|
end
|
350
|
+
|
351
|
+
# Invisible
|
352
|
+
if is_verbose
|
353
|
+
invisible_cmds.sort_by { |cmd| cmd.name }.each do |cmd|
|
354
|
+
text << sprintf(" %-#{length+4}s %s\n",
|
355
|
+
cmd.name.formatted_as_command,
|
356
|
+
cmd.summary)
|
357
|
+
end
|
358
|
+
else
|
359
|
+
case invisible_cmds.size
|
360
|
+
when 0
|
361
|
+
when 1
|
362
|
+
text << " (1 hidden command ommitted; show it with --verbose)\n"
|
363
|
+
else
|
364
|
+
text << " (#{invisible_cmds.size} hidden commands ommitted; show them with --verbose)\n"
|
365
|
+
end
|
366
|
+
end
|
335
367
|
end
|
336
368
|
|
337
369
|
# Append options
|
@@ -339,23 +371,23 @@ module Cri
|
|
339
371
|
if self.supercommand
|
340
372
|
groups["options for #{self.supercommand.name}"] = self.supercommand.global_option_definitions
|
341
373
|
end
|
342
|
-
length = groups.values.inject(&:+).
|
374
|
+
length = groups.values.inject(&:+).map { |o| o[:long].size }.max
|
343
375
|
groups.each_pair do |name, defs|
|
344
376
|
unless defs.empty?
|
345
377
|
text << "\n"
|
346
|
-
text << "#{name}
|
378
|
+
text << "#{name}".formatted_as_title
|
347
379
|
text << "\n"
|
348
380
|
defs.sort { |x,y| x[:long] <=> y[:long] }.each do |opt_def|
|
349
381
|
text << sprintf(
|
350
|
-
" -%1s --%-#{length+4}s
|
382
|
+
" -%1s --%-#{length+4}s",
|
351
383
|
opt_def[:short],
|
352
|
-
opt_def[:long]
|
353
|
-
|
384
|
+
opt_def[:long]).formatted_as_option
|
385
|
+
|
386
|
+
text << opt_def[:desc] << "\n"
|
354
387
|
end
|
355
388
|
end
|
356
389
|
end
|
357
390
|
|
358
|
-
# Return text
|
359
391
|
text
|
360
392
|
end
|
361
393
|
|
data/lib/cri/command_dsl.rb
CHANGED
@@ -79,6 +79,16 @@ module Cri
|
|
79
79
|
@command.usage = arg
|
80
80
|
end
|
81
81
|
|
82
|
+
# Marks the command as hidden. Hidden commands do not show up in the list of
|
83
|
+
# subcommands of the parent command, unless --verbose is passed (or
|
84
|
+
# `:verbose => true` is passed to the {Cri::Command#help} method). This can
|
85
|
+
# be used to mark commands as deprecated.
|
86
|
+
#
|
87
|
+
# @return [void]
|
88
|
+
def be_hidden
|
89
|
+
@command.hidden = true
|
90
|
+
end
|
91
|
+
|
82
92
|
# Adds a new option to the command. If a block is given, it will be
|
83
93
|
# executed when the option is successfully parsed.
|
84
94
|
#
|
@@ -10,16 +10,20 @@ commandline options. When a command is given, a command description as well as
|
|
10
10
|
command-specific commandline options are shown.
|
11
11
|
EOS
|
12
12
|
|
13
|
+
flag :v, :verbose, 'show more detailed help'
|
14
|
+
|
13
15
|
run do |opts, args, cmd|
|
14
16
|
if cmd.supercommand.nil?
|
15
17
|
raise NoHelpAvailableError,
|
16
18
|
"No help available because the help command has no supercommand"
|
17
19
|
end
|
18
20
|
|
21
|
+
is_verbose = opts.fetch(:verbose, false)
|
22
|
+
|
19
23
|
if args.empty?
|
20
|
-
puts cmd.supercommand.help
|
24
|
+
puts cmd.supercommand.help(:verbose => is_verbose)
|
21
25
|
elsif args.size == 1
|
22
|
-
puts cmd.supercommand.command_named(args[0]).help
|
26
|
+
puts cmd.supercommand.command_named(args[0]).help(:verbose => is_verbose)
|
23
27
|
else
|
24
28
|
$stderr.puts cmd.usage
|
25
29
|
exit 1
|
data/lib/cri/core_ext/string.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
require 'colored'
|
2
3
|
|
3
4
|
module Cri::CoreExtensions
|
4
5
|
|
@@ -59,6 +60,24 @@ module Cri::CoreExtensions
|
|
59
60
|
end.join("\n\n")
|
60
61
|
end
|
61
62
|
|
63
|
+
# @return [String] The string, formatted to be used as a title in a section
|
64
|
+
# in the help
|
65
|
+
def formatted_as_title
|
66
|
+
self.upcase.red.bold
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [String] The string, formatted to be used as the name of a command
|
70
|
+
# in the help
|
71
|
+
def formatted_as_command
|
72
|
+
self.green
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [String] The string, formatted to be used as an option definition
|
76
|
+
# of a command in the help
|
77
|
+
def formatted_as_option
|
78
|
+
self.yellow
|
79
|
+
end
|
80
|
+
|
62
81
|
end
|
63
82
|
|
64
83
|
end
|
data/test/helper.rb
CHANGED
@@ -13,7 +13,7 @@ class Cri::TestCase < MiniTest::Unit::TestCase
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def capture_io_while(&block)
|
16
|
-
|
16
|
+
orig_io = capture_io
|
17
17
|
block.call
|
18
18
|
[ $stdout.string, $stderr.string ]
|
19
19
|
ensure
|
@@ -42,3 +42,6 @@ private
|
|
42
42
|
end
|
43
43
|
|
44
44
|
end
|
45
|
+
|
46
|
+
# Unexpected system exit is unexpected
|
47
|
+
::MiniTest::Unit::TestCase::PASSTHROUGH_EXCEPTIONS.delete(SystemExit)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Cri::BasicRootTestCase < Cri::TestCase
|
4
|
+
|
5
|
+
def test_run_with_help
|
6
|
+
cmd = Cri::Command.new_basic_root
|
7
|
+
|
8
|
+
stdout, stderr = capture_io_while do
|
9
|
+
assert_raises SystemExit do
|
10
|
+
cmd.run(%w( -h ))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
assert stdout =~ /COMMANDS.*\n.*help.*show help/
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/test/test_command.rb
CHANGED
@@ -5,7 +5,7 @@ class Cri::CommandTestCase < Cri::TestCase
|
|
5
5
|
def simple_cmd
|
6
6
|
Cri::Command.define do
|
7
7
|
name 'moo'
|
8
|
-
usage '
|
8
|
+
usage 'moo [options] arg1 arg2 ...'
|
9
9
|
summary 'does stuff'
|
10
10
|
description 'This command does a lot of stuff.'
|
11
11
|
|
@@ -256,7 +256,7 @@ class Cri::CommandTestCase < Cri::TestCase
|
|
256
256
|
def test_help_nested
|
257
257
|
help = nested_cmd.subcommands.find { |cmd| cmd.name == 'sub' }.help
|
258
258
|
|
259
|
-
|
259
|
+
assert help.include?("USAGE\e[0m\e[0m\n \e[32msuper\e[0m \e[32msub\e[0m [options]\n")
|
260
260
|
end
|
261
261
|
|
262
262
|
def test_help_for_bare_cmd
|
@@ -346,4 +346,53 @@ class Cri::CommandTestCase < Cri::TestCase
|
|
346
346
|
assert_match /mycommand.rb/, error.backtrace.join("\n")
|
347
347
|
end
|
348
348
|
|
349
|
+
def test_hidden_commands_single
|
350
|
+
cmd = nested_cmd
|
351
|
+
subcmd = simple_cmd
|
352
|
+
cmd.add_command subcmd
|
353
|
+
subcmd.modify do |c|
|
354
|
+
c.name 'old-and-deprecated'
|
355
|
+
c.summary 'does stuff the ancient, totally deprecated way'
|
356
|
+
c.be_hidden
|
357
|
+
end
|
358
|
+
|
359
|
+
refute cmd.help.include?('hidden commands ommitted')
|
360
|
+
assert cmd.help.include?('hidden command ommitted')
|
361
|
+
refute cmd.help.include?('old-and-deprecated')
|
362
|
+
|
363
|
+
refute cmd.help(:verbose => true).include?('hidden commands ommitted')
|
364
|
+
refute cmd.help(:verbose => true).include?('hidden command ommitted')
|
365
|
+
assert cmd.help(:verbose => true).include?('old-and-deprecated')
|
366
|
+
end
|
367
|
+
|
368
|
+
def test_hidden_commands_multiple
|
369
|
+
cmd = nested_cmd
|
370
|
+
|
371
|
+
subcmd = simple_cmd
|
372
|
+
cmd.add_command subcmd
|
373
|
+
subcmd.modify do |c|
|
374
|
+
c.name 'old-and-deprecated'
|
375
|
+
c.summary 'does stuff the old, deprecated way'
|
376
|
+
c.be_hidden
|
377
|
+
end
|
378
|
+
|
379
|
+
subcmd = simple_cmd
|
380
|
+
cmd.add_command subcmd
|
381
|
+
subcmd.modify do |c|
|
382
|
+
c.name 'ancient-and-deprecated'
|
383
|
+
c.summary 'does stuff the ancient, reallydeprecated way'
|
384
|
+
c.be_hidden
|
385
|
+
end
|
386
|
+
|
387
|
+
assert cmd.help.include?('hidden commands ommitted')
|
388
|
+
refute cmd.help.include?('hidden command ommitted')
|
389
|
+
refute cmd.help.include?('old-and-deprecated')
|
390
|
+
refute cmd.help.include?('ancient-and-deprecated')
|
391
|
+
|
392
|
+
refute cmd.help(:verbose => true).include?('hidden commands ommitted')
|
393
|
+
refute cmd.help(:verbose => true).include?('hidden command ommitted')
|
394
|
+
assert cmd.help(:verbose => true).include?('old-and-deprecated')
|
395
|
+
assert cmd.help(:verbose => true).include?('ancient-and-deprecated')
|
396
|
+
end
|
397
|
+
|
349
398
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cri
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,24 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
13
|
-
dependencies:
|
12
|
+
date: 2012-06-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: colored
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.2'
|
14
30
|
description: Cri allows building easy-to-use commandline interfaces with support for
|
15
31
|
subcommands.
|
16
32
|
email: denis.defreyne@stoneship.org
|
@@ -39,6 +55,7 @@ files:
|
|
39
55
|
- test/helper.rb
|
40
56
|
- test/test_base.rb
|
41
57
|
- test/test_basic_help.rb
|
58
|
+
- test/test_basic_root.rb
|
42
59
|
- test/test_command.rb
|
43
60
|
- test/test_command_dsl.rb
|
44
61
|
- test/test_core_ext.rb
|
@@ -67,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
84
|
version: '0'
|
68
85
|
requirements: []
|
69
86
|
rubyforge_project:
|
70
|
-
rubygems_version: 1.8.
|
87
|
+
rubygems_version: 1.8.24
|
71
88
|
signing_key:
|
72
89
|
specification_version: 3
|
73
90
|
summary: a library for building easy-to-use commandline tools
|