cri 2.2.1 → 2.3.0

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