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 CHANGED
@@ -1,6 +1,12 @@
1
1
  Cri News
2
2
  ========
3
3
 
4
+ 2.3.0
5
+ -----
6
+
7
+ * Added colors
8
+ * Added support for marking commands as hidden
9
+
4
10
  2.2.1
5
11
  -----
6
12
 
@@ -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
@@ -17,7 +17,7 @@ module Cri
17
17
  end
18
18
 
19
19
  # The current Cri version.
20
- VERSION = '2.2.1'
20
+ VERSION = '2.3.0'
21
21
 
22
22
  autoload 'Command', 'cri/command'
23
23
  autoload 'CommandDSL', 'cri/command_dsl'
@@ -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
- full_usage = path[1..-1].map { |c| c.name + ' ' }.join + usage
303
- text << "usage: #{full_usage}\n"
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 << "aliases: #{aliases.join(' ')}\n"
310
- end
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.commands.empty?
334
+ unless self.subcommands.empty?
326
335
  text << "\n"
327
- text << (self.supercommand ? 'subcommands' : 'commands') << ":\n"
336
+ text << (self.supercommand ? 'subcommands' : 'commands').formatted_as_title
328
337
  text << "\n"
329
- length = self.commands.inject(0) { |m,c| [ m, c.name.size ].max }
330
- self.commands.sort_by { |cmd| cmd.name }.each do |cmd|
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(&:+).inject(0) { |m,o| [ m, o[:long].size ].max }
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}:\n"
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 %s\n",
382
+ " -%1s --%-#{length+4}s",
351
383
  opt_def[:short],
352
- opt_def[:long],
353
- opt_def[:desc])
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
 
@@ -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
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
- option :h, :help, 'show help for this command' do |value|
4
- puts self.help
3
+ flag :h, :help, 'show help for this command' do |value, cmd|
4
+ puts cmd.help
5
5
  exit 0
6
6
  end
7
7
 
@@ -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
@@ -13,7 +13,7 @@ class Cri::TestCase < MiniTest::Unit::TestCase
13
13
  end
14
14
 
15
15
  def capture_io_while(&block)
16
- orig_io = capture_io
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
@@ -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 'dunno whatever'
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
- assert_match /^usage: super sub \[options\]/, help
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.2.1
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-03-17 00:00:00.000000000 Z
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.17
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