gli 2.10.0 → 2.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc8b2ac1abb69495f8096899fbb4da56b2e85d32
4
- data.tar.gz: ce4f47438626b06fbd4fcf4b714a5257b63a2ec5
3
+ metadata.gz: 362815ed2ee02ae4318a321eca52acb7175f802f
4
+ data.tar.gz: 27dadfccc2e537d4ae9824cf1a75075020391d6c
5
5
  SHA512:
6
- metadata.gz: 1d9e77187528594f30d47c0dca4cdef47a1dc01fbc35657e0bfec3441a20b1d37d87dccecc4015957bf78c1055ff4ce9a0cb0b728b1e97305b6c8936ba82f2c2
7
- data.tar.gz: 81eb28d1e7b564fed156a69fa0159bd4236bffec82d51bf1c5874c6d6a521eb8f24ce1ee3f156ff9445a504311b9deb83deb059ab27120c079b04e8578b286cf
6
+ metadata.gz: 261bf4ac97f86f4b2b1d68aba7cb7396da8c6e2651504dfd8ce1a736215cca382b723db02820165b69c71e74721b4ac4588c45cfcf7014596c80e1225845a504
7
+ data.tar.gz: 4a12460017ee29eb4dd7f97111ee5cbb8b5112cd44db7dd933c7edc069b3f8053e0245417e4fa3ffaaeb108b4f85384ab2a86bf0483b7811ad52dbdafb9d6110
@@ -17,6 +17,7 @@ Feature: The todo app has a nice user interface
17
17
 
18
18
  Scenario Outline: Getting Help for todo in general
19
19
  When I successfully run `todo <help>`
20
+ Then the exit status should be 0
20
21
  Then the output should contain:
21
22
  """
22
23
  NAME
@@ -195,8 +196,9 @@ Feature: The todo app has a nice user interface
195
196
  stored in your todo databases.
196
197
 
197
198
  COMMAND OPTIONS
198
- -l, --[no-]long - Show long form
199
- --required_flag=arg - (required, default: none)
199
+ -l, --[no-]long - Show long form
200
+ --required_flag=arg - (required, default: none)
201
+ --required_flag2=arg - (required, default: none)
200
202
 
201
203
  COMMANDS
202
204
  contexts - List contexts
@@ -238,8 +240,9 @@ Feature: The todo app has a nice user interface
238
240
  List a whole lot of things that you might be keeping track of in your overall todo list. This is your go-to place or finding all of the things that you might have stored in your todo databases.
239
241
 
240
242
  COMMAND OPTIONS
241
- -l, --[no-]long - Show long form
242
- --required_flag=arg - (required, default: none)
243
+ -l, --[no-]long - Show long form
244
+ --required_flag=arg - (required, default: none)
245
+ --required_flag2=arg - (required, default: none)
243
246
 
244
247
  COMMANDS
245
248
  contexts - List contexts
@@ -270,8 +273,9 @@ Feature: The todo app has a nice user interface
270
273
 
271
274
 
272
275
  COMMAND OPTIONS
273
- -l, --[no-]long - Show long form
274
- --required_flag=arg - (required, default: none)
276
+ -l, --[no-]long - Show long form
277
+ --required_flag=arg - (required, default: none)
278
+ --required_flag2=arg - (required, default: none)
275
279
 
276
280
  COMMANDS
277
281
  contexts - List contexts
@@ -294,8 +298,9 @@ Feature: The todo app has a nice user interface
294
298
  List a whole lot of things that you might be keeping track of in your overall todo list. This is your go-to place or finding all of the things that you might have stored in your todo databases.
295
299
 
296
300
  COMMAND OPTIONS
297
- -l, --[no-]long - Show long form
298
- --required_flag=arg - (required, default: none)
301
+ -l, --[no-]long - Show long form
302
+ --required_flag=arg - (required, default: none)
303
+ --required_flag2=arg - (required, default: none)
299
304
 
300
305
  COMMANDS
301
306
  contexts - List contexts
@@ -336,21 +341,27 @@ Feature: The todo app has a nice user interface
336
341
  SYNOPSIS
337
342
  todo [global options] create
338
343
  todo [global options] create contexts [context_name]
344
+ todo [global options] create relation_1-1 first second [name]
345
+ todo [global options] create relation_1-n first second[, second]* [name]
346
+ todo [global options] create relation_n-1 first[, first]* second [name]
339
347
  todo [global options] create tasks task_name[, task_name]*
340
348
 
341
349
  COMMANDS
342
- <default> - Makes a new task
343
- contexts - Make a new context
344
- tasks - Make a new task
350
+ <default> - Makes a new task
351
+ contexts - Make a new context
352
+ relation_1-1 -
353
+ relation_1-n -
354
+ relation_n-1 -
355
+ tasks - Make a new task
345
356
  """
346
357
  And the output should not contain "COMMAND OPTIONS"
347
358
 
348
359
  Scenario: Running list w/out subcommand performs list tasks by default
349
- When I successfully run `todo list --required_flag=blah boo yay`
360
+ When I successfully run `todo list --required_flag=blah --required_flag2=bleh boo yay`
350
361
  Then the output should contain "list tasks: boo,yay"
351
362
 
352
363
  Scenario: Running list w/out subcommand or any arguments performs list tasks by default
353
- When I successfully run `todo list --required_flag=blah`
364
+ When I successfully run `todo list --required_flag=blah --required_flag2=bleh`
354
365
  Then the output should contain "list tasks:"
355
366
 
356
367
  Scenario: Running chained commands works
@@ -479,3 +490,57 @@ Feature: The todo app has a nice user interface
479
490
  todo [global options] ls [command options] contexts [subcommand options]
480
491
  todo [global options] ls [command options] tasks [subcommand options]
481
492
  """
493
+
494
+ Scenario: We get a clear error message when a required argument is missing
495
+ Given a clean home directory
496
+ When I run `todo list`
497
+ Then the exit status should not be 0
498
+ And the stderr should contain "error: required_flag is required, required_flag2 is required"
499
+ And the output should contain:
500
+ """
501
+ NAME
502
+ list - List things, such as tasks or contexts
503
+
504
+ SYNOPSIS
505
+ todo [global options] list [command options] [tasks] [subcommand options]
506
+ todo [global options] list [command options] contexts [subcommand options]
507
+
508
+ DESCRIPTION
509
+ List a whole lot of things that you might be keeping track of in your
510
+ overall todo list.
511
+
512
+ This is your go-to place or finding all of the things that you might have
513
+ stored in your todo databases.
514
+
515
+ COMMAND OPTIONS
516
+ -l, --[no-]long - Show long form
517
+ --required_flag=arg - (required, default: none)
518
+ --required_flag2=arg - (required, default: none)
519
+
520
+ COMMANDS
521
+ contexts - List contexts
522
+ tasks - List tasks (default)
523
+ """
524
+
525
+ Scenario: Getting help on a root command with an arg_name outputs the argument description
526
+ When I run `todo help first`
527
+ And the stdout should contain:
528
+ """
529
+ NAME
530
+ first -
531
+
532
+ SYNOPSIS
533
+ todo [global options] first [argument]
534
+ """
535
+
536
+ Scenario: Getting help on a root command with an arg outputs the argument description
537
+ When I run `todo help second`
538
+ And the stdout should contain:
539
+ """
540
+ NAME
541
+ second -
542
+
543
+ SYNOPSIS
544
+ todo [global options] second [argument]
545
+ """
546
+
data/lib/gli.rb CHANGED
@@ -13,6 +13,7 @@ require 'gli/exceptions.rb'
13
13
  require 'gli/flag.rb'
14
14
  require 'gli/options.rb'
15
15
  require 'gli/switch.rb'
16
+ require 'gli/argument.rb'
16
17
  require 'gli/dsl.rb'
17
18
  require 'gli/version.rb'
18
19
  require 'gli/commands/help'
@@ -210,7 +210,7 @@ module GLI
210
210
  def handle_exception(ex,command)
211
211
  if regular_error_handling?(ex)
212
212
  output_error_message(ex)
213
- if ex.kind_of?(OptionParser::ParseError) || ex.kind_of?(BadCommandLine)
213
+ if ex.kind_of?(OptionParser::ParseError) || ex.kind_of?(BadCommandLine) || ex.kind_of?(RequestHelp)
214
214
  if commands[:help]
215
215
  command_for_help = command.nil? ? [] : command.name_for_help
216
216
  commands[:help].execute({},{},command_for_help)
@@ -220,7 +220,7 @@ module GLI
220
220
  stderr.puts "Custom error handler exited false, skipping normal error handling"
221
221
  end
222
222
 
223
- raise ex if ENV['GLI_DEBUG'] == 'true'
223
+ raise ex if ENV['GLI_DEBUG'] == 'true' && !ex.kind_of?(RequestHelp)
224
224
 
225
225
  ex.extend(GLI::StandardException)
226
226
  ex.exit_code
@@ -0,0 +1,20 @@
1
+ module GLI
2
+ class Argument #:nodoc:
3
+
4
+ attr_reader :name
5
+ attr_reader :options
6
+
7
+ def initialize(name,options = [])
8
+ @name = name
9
+ @options = options
10
+ end
11
+
12
+ def optional?
13
+ @options.include? :optional
14
+ end
15
+
16
+ def multiple?
17
+ @options.include? :multiple
18
+ end
19
+ end
20
+ end
data/lib/gli/command.rb CHANGED
@@ -49,6 +49,7 @@ module GLI
49
49
  super(options[:names],options[:description],options[:long_desc])
50
50
  @arguments_description = options[:arguments_name] || ''
51
51
  @arguments_options = Array(options[:arguments_options]).flatten
52
+ @arguments = options[:arguments] || []
52
53
  @skips_pre = options[:skips_pre]
53
54
  @skips_post = options[:skips_post]
54
55
  @skips_around = options[:skips_around]
@@ -25,6 +25,10 @@ module GLI
25
25
  @arguments_options
26
26
  end
27
27
 
28
+ def arguments
29
+ @arguments
30
+ end
31
+
28
32
  # If true, this command doesn't want the pre block run before it executes
29
33
  def skips_pre
30
34
  @skips_pre
@@ -3,7 +3,34 @@ module GLI
3
3
  module HelpModules
4
4
  # Handles wrapping text
5
5
  class ArgNameFormatter
6
- def format(arguments_description,arguments_options)
6
+ def format(arguments_description,arguments_options,arguments)
7
+ # Select which format to use: argname or arguments
8
+ # Priority to old way: argname
9
+ desc = format_argname(arguments_description, arguments_options)
10
+ desc = format_arguments(arguments) if desc.strip == ''
11
+ desc
12
+ end
13
+
14
+ def format_arguments(arguments)
15
+ return '' if arguments.empty?
16
+ desc = ""
17
+
18
+ # Go through the arguments, building the description string
19
+ arguments.each do |arg|
20
+ arg_desc = "#{arg.name}"
21
+ if arg.optional?
22
+ arg_desc = "[#{arg_desc}]"
23
+ end
24
+ if arg.multiple?
25
+ arg_desc = "#{arg_desc}[, #{arg_desc}]*"
26
+ end
27
+ desc = desc + " " + arg_desc
28
+ end
29
+
30
+ desc
31
+ end
32
+
33
+ def format_argname(arguments_description,arguments_options)
7
34
  return '' if String(arguments_description).strip == ''
8
35
  desc = arguments_description
9
36
  if arguments_options.include? :optional
@@ -11,7 +11,7 @@ module GLI
11
11
  def synopses_for_command(command)
12
12
  synopses = []
13
13
  one_line_usage = basic_usage(command)
14
- one_line_usage << command.arguments_description
14
+ one_line_usage << ArgNameFormatter.new.format(command.arguments_description,command.arguments_options,command.arguments).strip
15
15
  if command.commands.empty?
16
16
  synopses << one_line_usage
17
17
  else
@@ -73,7 +73,7 @@ module GLI
73
73
  usage << ' '
74
74
  usage << sub_options_doc
75
75
  end
76
- arg_name_doc = ArgNameFormatter.new.format(sub.arguments_description,sub.arguments_options).strip
76
+ arg_name_doc = ArgNameFormatter.new.format(sub.arguments_description,sub.arguments_options,sub.arguments).strip
77
77
  if arg_name_doc.length > 0
78
78
  usage << ' '
79
79
  usage << arg_name_doc
data/lib/gli/dsl.rb CHANGED
@@ -36,6 +36,26 @@ module GLI
36
36
  @next_arg_options = options
37
37
  end
38
38
 
39
+ # Describes one of the arguments of the next command
40
+ #
41
+ # +name+:: A String that *briefly* describes the argument given to the following command.
42
+ # +options+:: Symbol or array of symbols to annotate this argument. This doesn't affect parsing, just
43
+ # the help output. Values recognized are:
44
+ # +:optional+:: indicates this argument is optional; will format it with square brackets
45
+ # +:multiple+:: indicates multiple values are accepted; will format appropriately
46
+ #
47
+ # Example:
48
+ # arg :output
49
+ # arg :input, :multiple
50
+ # command :pack do ...
51
+ #
52
+ # Produces the synopsis:
53
+ # app.rb [global options] pack output input[, input]*
54
+ def arg(name, options=[])
55
+ @next_arguments ||= []
56
+ @next_arguments << Argument.new(name, Array(options).flatten)
57
+ end
58
+
39
59
  # set the default value of the next flag or switch
40
60
  #
41
61
  # +val+:: The default value to be used for the following flag if the user doesn't specify one
@@ -152,6 +172,7 @@ module GLI
152
172
  :description => @next_desc,
153
173
  :arguments_name => @next_arg_name,
154
174
  :arguments_options => @next_arg_options,
175
+ :arguments => @next_arguments,
155
176
  :long_desc => @next_long_desc,
156
177
  :skips_pre => @skips_pre,
157
178
  :skips_post => @skips_post,
@@ -178,6 +199,7 @@ module GLI
178
199
  yield command
179
200
  end
180
201
  clear_nexts
202
+ @next_arguments = []
181
203
  end
182
204
  alias :c :command
183
205
 
@@ -5,6 +5,21 @@ module GLI
5
5
  module StandardException
6
6
  def exit_code; 1; end
7
7
  end
8
+
9
+ # Hack to request help from within a command
10
+ # Will *not* be rethrown when GLI_DEBUG is ON
11
+ class RequestHelp < StandardError
12
+ include StandardException
13
+ def exit_code; 0; end
14
+
15
+ # The command for which the argument was unknown
16
+ attr_reader :command_in_context
17
+
18
+ def initialize(command_in_context)
19
+ @command_in_context = command_in_context
20
+ end
21
+ end
22
+
8
23
  # Indicates that the command line invocation was bad
9
24
  class BadCommandLine < StandardError
10
25
  include StandardException
@@ -44,6 +59,17 @@ module GLI
44
59
  end
45
60
  end
46
61
 
62
+ class MissingRequiredArgumentsException < BadCommandLine
63
+ # The command for which the argument was unknown
64
+ attr_reader :command_in_context
65
+ # +message+:: the error message to show the user
66
+ # +command+:: the command we were using to parse command-specific options
67
+ def initialize(message,command)
68
+ super(message)
69
+ @command_in_context = command
70
+ end
71
+ end
72
+
47
73
  # Indicates the bad command line was an unknown command argument
48
74
  class UnknownCommandArgument < CommandException
49
75
  end
@@ -37,23 +37,24 @@ module GLI
37
37
  end
38
38
  parsing_result.command = @command_finder.find_command(command_name)
39
39
  unless command_name == 'help'
40
- verify_required_options!(@flags,parsing_result.global_options)
40
+ verify_required_options!(@flags, parsing_result.command, parsing_result.global_options)
41
41
  end
42
42
  parsing_result
43
43
  end
44
44
 
45
45
  protected
46
46
 
47
- def verify_required_options!(flags,options)
47
+ def verify_required_options!(flags, command, options)
48
48
  missing_required_options = flags.values.
49
49
  select(&:required?).
50
50
  reject { |option|
51
51
  options[option.name] != nil
52
52
  }
53
53
  unless missing_required_options.empty?
54
- raise BadCommandLine, missing_required_options.map { |option|
54
+ missing_required_options.sort!
55
+ raise MissingRequiredArgumentsException.new(missing_required_options.map { |option|
55
56
  "#{option.name} is required"
56
- }.join(' ')
57
+ }.join(', '), command)
57
58
  end
58
59
  end
59
60
  end
@@ -86,7 +87,7 @@ module GLI
86
87
  command_finder = CommandFinder.new(command.commands,command.get_default_command)
87
88
  next_command_name = arguments.shift
88
89
 
89
- verify_required_options!(command.flags,parsed_command_options[command])
90
+ verify_required_options!(command.flags, command, parsed_command_options[command])
90
91
 
91
92
  begin
92
93
  command = command_finder.find_command(next_command_name)
@@ -138,7 +139,7 @@ module GLI
138
139
  subcommand,args = find_subcommand(command,parsing_result.arguments)
139
140
  parsing_result.command = subcommand
140
141
  parsing_result.arguments = args
141
- verify_required_options!(command.flags,parsing_result.command_options)
142
+ verify_required_options!(command.flags, parsing_result.command, parsing_result.command_options)
142
143
  end
143
144
 
144
145
  private
@@ -71,7 +71,7 @@ module GLI
71
71
  unless help_args.empty?
72
72
  help_args << "Get help for #{command.name}"
73
73
  option_parser.on(*help_args) do
74
- raise CommandException.new(nil,command,0)
74
+ raise RequestHelp.new(command)
75
75
  end
76
76
  end
77
77
  end
data/lib/gli/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module GLI
2
2
  unless const_defined? :VERSION
3
- VERSION = '2.10.0'
3
+ VERSION = '2.11.0'
4
4
  end
5
5
  end
@@ -35,7 +35,10 @@ version Todo::VERSION
35
35
  commands_from 'todo/commands'
36
36
  commands_from File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'todo_plugins', 'commands'))
37
37
 
38
+ arg_name :argument, :optional
38
39
  command :first do |c| c.action { |g,o,a| puts "first: #{a.join(',')}" } end
40
+
41
+ arg :argument, :optional
39
42
  command :second do |c| c.action { |g,o,a| puts "second: #{a.join(',')}" } end
40
43
 
41
44
  command :chained => [ :first, :second ]
@@ -2,6 +2,7 @@ desc "Create a new task or context"
2
2
  command [:create,:new] do |c|
3
3
  c.desc "Make a new task"
4
4
  c.arg_name 'task_name', :multiple
5
+ c.arg :should_ignore_this
5
6
  c.command :tasks do |tasks|
6
7
  tasks.action do |global,options,args|
7
8
  puts "#{args}"
@@ -9,6 +10,7 @@ command [:create,:new] do |c|
9
10
  end
10
11
 
11
12
  c.desc "Make a new context"
13
+ c.arg :should_ignore_this
12
14
  c.arg_name 'context_name', :optional
13
15
  c.command :contexts do |contexts|
14
16
  contexts.action do |global,options,args|
@@ -20,5 +22,32 @@ command [:create,:new] do |c|
20
22
  c.action do
21
23
  puts "default action"
22
24
  end
25
+
26
+ c.arg "first"
27
+ c.arg "second"
28
+ c.arg "name", :optional
29
+ c.command :"relation_1-1" do |remote|
30
+ remote.action do |global,options,args|
31
+ puts "relation: #{args}"
32
+ end
33
+ end
34
+
35
+ c.arg "first", :multiple
36
+ c.arg "second"
37
+ c.arg "name", :optional
38
+ c.command :"relation_n-1" do |remote|
39
+ remote.action do |global,options,args|
40
+ puts "relation: #{args}"
41
+ end
42
+ end
43
+
44
+ c.arg "first"
45
+ c.arg "second", :multiple
46
+ c.arg "name", :optional
47
+ c.command :"relation_1-n" do |remote|
48
+ remote.action do |global,options,args|
49
+ puts "relation: #{args}"
50
+ end
51
+ end
23
52
  end
24
53
 
@@ -15,6 +15,7 @@ command [:list] do |c|
15
15
  c.switch [:l,:long]
16
16
 
17
17
  c.flag :required_flag, :required => true
18
+ c.flag :required_flag2, :required => true
18
19
 
19
20
  c.desc "List tasks"
20
21
  c.long_desc %(
data/test/tc_gli.rb CHANGED
@@ -84,6 +84,7 @@ class TC_testGLI < Clean::Test::TestCase
84
84
  assert_equal 64, @app.run(['foo']), "Expected exit status to be 64"
85
85
  assert @fake_stderr.contained?(/flag is required/), @fake_stderr.strings.inspect
86
86
  assert @fake_stderr.contained?(/other_flag is required/), @fake_stderr.strings.inspect
87
+ assert @fake_stderr.contained?(/flag is required, other_flag is required/), @fake_stderr.strings.inspect
87
88
  assert !@called
88
89
 
89
90
  assert_equal 0, @app.run(['foo','--flag=bar','--other_flag=blah']), "Expected exit status to be 0 #{@fake_stderr.strings.join(',')}"
@@ -104,6 +105,7 @@ class TC_testGLI < Clean::Test::TestCase
104
105
  assert_equal 64, @app.run(['foo']), "Expected exit status to be 64"
105
106
  assert @fake_stderr.contained?(/flag is required/), @fake_stderr.strings.inspect
106
107
  assert @fake_stderr.contained?(/other_flag is required/), @fake_stderr.strings.inspect
108
+ assert @fake_stderr.contained?(/flag is required, other_flag is required/), @fake_stderr.strings.inspect
107
109
  assert !@called
108
110
 
109
111
  assert_equal 0, @app.run(['--flag=bar','--other_flag=blah','foo']), "Expected exit status to be 0 #{@fake_stderr.strings.join(',')}"
@@ -663,6 +665,19 @@ class TC_testGLI < Clean::Test::TestCase
663
665
  assert_raises(GLI::CustomExit) { @app.run(['foo']) }
664
666
  end
665
667
 
668
+ def test_gli_help_does_not_raise_on_debug
669
+ ENV['GLI_DEBUG'] = 'true'
670
+
671
+ @app.reset
672
+ @app.command(:multiply) do |c|
673
+ c.action do |g,o,a|
674
+ # Nothing
675
+ end
676
+ end
677
+
678
+ assert_nothing_raised(GLI::CustomExit) { @app.run(['multiply', '--help']) }
679
+ end
680
+
666
681
  class ConvertMe
667
682
  attr_reader :value
668
683
  def initialize(value)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.10.0
4
+ version: 2.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Copeland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-22 00:00:00.000000000 Z
11
+ date: 2014-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -174,6 +174,7 @@ files:
174
174
  - lib/gli.rb
175
175
  - lib/gli/app.rb
176
176
  - lib/gli/app_support.rb
177
+ - lib/gli/argument.rb
177
178
  - lib/gli/command.rb
178
179
  - lib/gli/command_finder.rb
179
180
  - lib/gli/command_line_option.rb