gli 2.11.0 → 2.12.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: 362815ed2ee02ae4318a321eca52acb7175f802f
4
- data.tar.gz: 27dadfccc2e537d4ae9824cf1a75075020391d6c
3
+ metadata.gz: e3051959ac78b44c2e6c7d8be0701aebcb375700
4
+ data.tar.gz: 5ef912011fd093bf0afc55b7642d7eaa85f84d4f
5
5
  SHA512:
6
- metadata.gz: 261bf4ac97f86f4b2b1d68aba7cb7396da8c6e2651504dfd8ce1a736215cca382b723db02820165b69c71e74721b4ac4588c45cfcf7014596c80e1225845a504
7
- data.tar.gz: 4a12460017ee29eb4dd7f97111ee5cbb8b5112cd44db7dd933c7edc069b3f8053e0245417e4fa3ffaaeb108b4f85384ab2a86bf0483b7811ad52dbdafb9d6110
6
+ metadata.gz: 464e43336b07485f3824dbacdeb8fbe5f5ebce4bd41af6f2dbc06406560b6c1b2d4b6ea1dc413de364ddba2d0d37576e121dcc0bebf962fef2f4d1c57221f4d8
7
+ data.tar.gz: 03296c0c993306c51903bc142cd8478348cd616aa735b4799c27dfb7b53bd048e115b32434644c3462f63742bd4b46730cb227936baafbef7ceed4d0bf9ad62b
data/bin/gli CHANGED
@@ -9,6 +9,10 @@ program_desc 'create scaffolding for a GLI-powered application'
9
9
 
10
10
  version GLI::VERSION
11
11
 
12
+ # Can't use these without changing the current behavior of gli
13
+ # arguments :strict
14
+ # subcommand_option_handling :normal
15
+
12
16
  switch :v, :desc => 'Be verbose'
13
17
 
14
18
  switch :n, :desc => 'Dry run; don''t change the disk'
@@ -29,7 +33,9 @@ for command line processing. Specifically, this will create
29
33
  an executable ready to go, as well as a lib and test directory, all
30
34
  inside the directory named for your project
31
35
  EOS
32
- arg_name 'project_name [command[ command]*]'
36
+ arg :project_name
37
+ arg :command_name, [:optional, :multiple]
38
+ arg_name "project_name [command_name][, [command_name]]*"
33
39
  command [:init,:scaffold] do |c|
34
40
 
35
41
  c.switch :e,:ext, :desc => 'Create an ext dir'
@@ -49,7 +49,7 @@ Feature: The GLI executable works as intended
49
49
  init - Create a new GLI-based project
50
50
 
51
51
  SYNOPSIS
52
- gli [global options] init [command options] project_name [command[ command]*]
52
+ gli [global options] init [command options] project_name [command_name][, [command_name]]*
53
53
 
54
54
  DESCRIPTION
55
55
  This will create a scaffold command line project that uses GLI for command
@@ -185,7 +185,7 @@ Feature: The todo app has a nice user interface
185
185
  list - List things, such as tasks or contexts
186
186
 
187
187
  SYNOPSIS
188
- todo [global options] list [command options] [tasks] [--flag arg] [-x arg]
188
+ todo [global options] list [command options] [tasks] [--flag arg] [-x arg] [task][, [task]]*
189
189
  todo [global options] list [command options] contexts [--otherflag arg] [-b] [-f|--foobar]
190
190
 
191
191
  DESCRIPTION
@@ -233,7 +233,7 @@ Feature: The todo app has a nice user interface
233
233
  list - List things, such as tasks or contexts
234
234
 
235
235
  SYNOPSIS
236
- todo [global options] list [command options] [tasks] [--flag arg] [-x arg]
236
+ todo [global options] list [command options] [tasks] [--flag arg] [-x arg] [task][, [task]]*
237
237
  todo [global options] list [command options] contexts [--otherflag arg] [-b] [-f|--foobar]
238
238
 
239
239
  DESCRIPTION
@@ -258,7 +258,7 @@ Feature: The todo app has a nice user interface
258
258
  list - List things, such as tasks or contexts
259
259
 
260
260
  SYNOPSIS
261
- todo [global options] list [command options] [tasks] [--flag arg] [-x arg]
261
+ todo [global options] list [command options] [tasks] [--flag arg] [-x arg] [task][, [task]]*
262
262
  todo [global options] list [command options] contexts [--otherflag arg] [-b] [-f|--foobar]
263
263
 
264
264
  DESCRIPTION
@@ -291,7 +291,7 @@ Feature: The todo app has a nice user interface
291
291
  list - List things, such as tasks or contexts
292
292
 
293
293
  SYNOPSIS
294
- todo [global options] list [command options] [tasks] [--flag arg] [-x arg]
294
+ todo [global options] list [command options] [tasks] [--flag arg] [-x arg] [task][, [task]]*
295
295
  todo [global options] list [command options] contexts [--otherflag arg] [-b] [-f|--foobar]
296
296
 
297
297
  DESCRIPTION
@@ -315,7 +315,7 @@ Feature: The todo app has a nice user interface
315
315
  tasks - List tasks
316
316
 
317
317
  SYNOPSIS
318
- todo [global options] list tasks [command options]
318
+ todo [global options] list tasks [command options] [task][, [task]]*
319
319
  todo [global options] list tasks [command options] open
320
320
 
321
321
  DESCRIPTION
@@ -502,7 +502,7 @@ Feature: The todo app has a nice user interface
502
502
  list - List things, such as tasks or contexts
503
503
 
504
504
  SYNOPSIS
505
- todo [global options] list [command options] [tasks] [subcommand options]
505
+ todo [global options] list [command options] [tasks] [subcommand options] [task][, [task]]*
506
506
  todo [global options] list [command options] contexts [subcommand options]
507
507
 
508
508
  DESCRIPTION
@@ -291,6 +291,13 @@ module GLI
291
291
  @subcommand_option_handling_strategy = handling_strategy
292
292
  end
293
293
 
294
+ # How to handle argument validation. Either +:loose+ (which does not validate argument at all)
295
+ # or +:strict+ (which will validate the number of arguments).
296
+ # If nothing is specified, +:loose+ is assumed
297
+ def arguments(handling_strategy)
298
+ @argument_handling_strategy = handling_strategy
299
+ end
300
+
294
301
  private
295
302
 
296
303
  def load_commands(path)
@@ -28,6 +28,7 @@ module GLI
28
28
  @default_command = :help
29
29
  @around_block = nil
30
30
  @subcommand_option_handling_strategy = :legacy
31
+ @argument_handling_strategy = :loose
31
32
  clear_nexts
32
33
  end
33
34
 
@@ -68,7 +69,8 @@ module GLI
68
69
  switches,
69
70
  accepts,
70
71
  @default_command,
71
- self.subcommand_option_handling_strategy)
72
+ self.subcommand_option_handling_strategy,
73
+ self.argument_handling_strategy)
72
74
 
73
75
  parsing_result = gli_option_parser.parse_options(args)
74
76
  parsing_result.convert_to_openstruct! if @use_openstruct
@@ -201,6 +203,10 @@ module GLI
201
203
  end
202
204
  end
203
205
 
206
+ def argument_handling_strategy
207
+ @argument_handling_strategy || :loose
208
+ end
209
+
204
210
  def subcommand_option_handling_strategy
205
211
  @subcommand_option_handling_strategy || :legacy
206
212
  end
@@ -59,7 +59,8 @@ module GLI
59
59
  super(:names => :help,
60
60
  :description => 'Shows a list of commands or help for one command',
61
61
  :arguments_name => 'command',
62
- :long_desc => 'Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function')
62
+ :long_desc => 'Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function',
63
+ :arguments => [Argument.new(:command_name, [:multiple, :optional])])
63
64
  @app = app
64
65
  @parent = app
65
66
  @sorter = SORTERS[@app.help_sort_type]
@@ -284,6 +284,9 @@ program_desc 'Describe your application here'
284
284
 
285
285
  version #{project_name_as_module_name(project_name)}::VERSION
286
286
 
287
+ # Use argument validation
288
+ arguments :strict
289
+
287
290
  desc 'Describe some switch here'
288
291
  switch [:s,:switch]
289
292
 
@@ -1,11 +1,12 @@
1
1
  module GLI
2
2
  # Parses the command-line options using an actual +OptionParser+
3
3
  class GLIOptionParser
4
- def initialize(commands,flags,switches,accepts,default_command = nil,subcommand_option_handling_strategy=:legacy)
4
+ def initialize(commands,flags,switches,accepts,default_command = nil,subcommand_option_handling_strategy=:legacy,argument_handling_strategy=:loose)
5
5
  command_finder = CommandFinder.new(commands,default_command || "help")
6
6
  @global_option_parser = GlobalOptionParser.new(OptionParserFactory.new(flags,switches,accepts),command_finder,flags)
7
7
  @accepts = accepts
8
8
  @subcommand_option_handling_strategy = subcommand_option_handling_strategy
9
+ @argument_handling_strategy = argument_handling_strategy
9
10
  end
10
11
 
11
12
  # Given the command-line argument array, returns an OptionParsingResult
@@ -14,7 +15,7 @@ module GLI
14
15
  OptionParsingResult.new.tap { |parsing_result|
15
16
  parsing_result.arguments = args
16
17
  parsing_result = @global_option_parser.parse!(parsing_result)
17
- option_parser_class.new(@accepts).parse!(parsing_result)
18
+ option_parser_class.new(@accepts).parse!(parsing_result, @argument_handling_strategy)
18
19
  }
19
20
  end
20
21
 
@@ -43,6 +44,38 @@ module GLI
43
44
  end
44
45
 
45
46
  protected
47
+ def verify_arguments!(arguments, command)
48
+ # Lets assume that if the user sets a 'arg_name' for the command it is for a complex scenario
49
+ # and we should not validate the arguments
50
+ return unless command.arguments_description.empty?
51
+
52
+ # Go through all declared arguments for the command, counting the min and max number
53
+ # of arguments
54
+ min_number_of_arguments = 0
55
+ max_number_of_arguments = 0
56
+ command.arguments.each do |arg|
57
+ if arg.optional?
58
+ max_number_of_arguments = max_number_of_arguments + 1
59
+ else
60
+ min_number_of_arguments = min_number_of_arguments + 1
61
+ max_number_of_arguments = max_number_of_arguments + 1
62
+ end
63
+
64
+ # Special case, as soon as we have a 'multiple' arguments, all bets are off for the
65
+ # maximum number of arguments !
66
+ if arg.multiple?
67
+ max_number_of_arguments = 99999
68
+ end
69
+ end
70
+
71
+ # Now validate the number of arguments
72
+ if arguments.size < min_number_of_arguments
73
+ raise MissingRequiredArgumentsException.new("Not enough arguments for command", command)
74
+ end
75
+ if arguments.size > max_number_of_arguments
76
+ raise MissingRequiredArgumentsException.new("Too many arguments for command", command)
77
+ end
78
+ end
46
79
 
47
80
  def verify_required_options!(flags, command, options)
48
81
  missing_required_options = flags.values.
@@ -70,7 +103,7 @@ module GLI
70
103
  }
71
104
  end
72
105
 
73
- def parse!(parsing_result)
106
+ def parse!(parsing_result,argument_handling_strategy)
74
107
  parsed_command_options = {}
75
108
  command = parsing_result.command
76
109
  arguments = nil
@@ -121,13 +154,17 @@ module GLI
121
154
  parsing_result.command_options = command_options
122
155
  parsing_result.command = command
123
156
  parsing_result.arguments = Array(arguments.compact)
157
+
158
+ # Lets validate the arguments now that we know for sure the command that is invoked
159
+ verify_arguments!(parsing_result.arguments, parsing_result.command) if argument_handling_strategy == :strict
160
+
124
161
  parsing_result
125
162
  end
126
163
 
127
164
  end
128
165
 
129
166
  class LegacyCommandOptionParser < NormalCommandOptionParser
130
- def parse!(parsing_result)
167
+ def parse!(parsing_result,argument_handling_strategy)
131
168
  command = parsing_result.command
132
169
  option_parser_factory = OptionParserFactory.for_command(command,@accepts)
133
170
  option_block_parser = LegacyCommandOptionBlockParser.new(option_parser_factory, self.error_handler)
@@ -1,5 +1,5 @@
1
1
  module GLI
2
2
  unless const_defined? :VERSION
3
- VERSION = '2.11.0'
3
+ VERSION = '2.12.0'
4
4
  end
5
5
  end
@@ -20,6 +20,7 @@ synopsis_format (ENV['SYNOPSES'] || 'full').to_sym
20
20
  hide_commands_without_desc ENV['HIDE_COMMANDS_WITHOUT_DESC'] === 'true'
21
21
 
22
22
  subcommand_option_handling :normal
23
+ arguments :strict
23
24
 
24
25
  program_desc 'Manages tasks'
25
26
  program_long_desc "A test program that has a sophisticated UI that can be used to exercise a lot of GLI's power"
@@ -41,7 +42,12 @@ command :first do |c| c.action { |g,o,a| puts "first: #{a.join(',')}" } end
41
42
  arg :argument, :optional
42
43
  command :second do |c| c.action { |g,o,a| puts "second: #{a.join(',')}" } end
43
44
 
45
+ arg :first
46
+ arg :second
44
47
  command :chained => [ :first, :second ]
48
+
49
+ arg :first
50
+ arg :second
45
51
  command [:chained2,:ch2] => [ :second, :first ]
46
52
 
47
53
  pre do |global,command,options,args|
@@ -8,6 +8,7 @@ long_desc %(
8
8
  stored in
9
9
  your todo databases.
10
10
  )
11
+
11
12
  command [:list] do |c|
12
13
  c.default_command :tasks
13
14
 
@@ -22,6 +23,8 @@ command [:list] do |c|
22
23
  Lists all of your tasks that you have, in varying orders, and
23
24
  all that stuff. Yes, this is long, but I need a long description.
24
25
  )
26
+
27
+ c.arg :task, [:optional, :multiple]
25
28
  c.command :tasks do |tasks|
26
29
  tasks.desc "blah blah crud x whatever"
27
30
  tasks.flag [:x], :must_match => Array
@@ -13,7 +13,8 @@ command [:make] do |c|
13
13
  puts o[:long]
14
14
  end
15
15
 
16
- desc 'make a bug'
16
+ task.desc 'make a bug'
17
+ task.arg :argument, [:multiple, :optional]
17
18
  task.command :bug do |bug|
18
19
  bug.desc 'make this bug in the legacy system'
19
20
  bug.flag [:l,:legacy]
@@ -4,6 +4,30 @@ require 'pp'
4
4
  class TC_testSubCommandParsing < Clean::Test::TestCase
5
5
  include TestHelper
6
6
 
7
+ def setup
8
+ @fake_stdout = FakeStdOut.new
9
+ @fake_stderr = FakeStdOut.new
10
+
11
+ @original_stdout = $stdout
12
+ $stdout = @fake_stdout
13
+ @original_stderr = $stderr
14
+ $stderr = @fake_stderr
15
+
16
+ @app = CLIApp.new
17
+ @app.reset
18
+ @app.subcommand_option_handling :legacy
19
+ @app.error_device=@fake_stderr
20
+ ENV.delete('GLI_DEBUG')
21
+
22
+ @results = {}
23
+ @exit_code = 0
24
+ end
25
+
26
+ def teardown
27
+ $stdout = @original_stdout
28
+ $stderr = @original_stderr
29
+ end
30
+
7
31
  test_that "commands options may clash with globals and it gets sorted out" do
8
32
  Given :app_with_subcommands_storing_results
9
33
  When {
@@ -24,7 +48,7 @@ class TC_testSubCommandParsing < Clean::Test::TestCase
24
48
  @app.run(%w(-f global command1 -f command -s subcommand10 -f sub))
25
49
  }
26
50
  Then {
27
- with_clue(@results) {
51
+ with_clue {
28
52
  assert_equal 'subcommand10',@results[:command_name]
29
53
  assert_equal 'global', @results[:global_options][:f],'global'
30
54
  assert !@results[:global_options][:s]
@@ -42,7 +66,7 @@ class TC_testSubCommandParsing < Clean::Test::TestCase
42
66
  @app.run(%w(-f global command1 -f command -s subcommand10 -f sub))
43
67
  }
44
68
  Then {
45
- with_clue(@results) {
69
+ with_clue {
46
70
  assert_equal 'subcommand10',@results[:command_name]
47
71
  assert_equal 'global', @results[:global_options][:f],'global'
48
72
  assert !@results[:global_options][:s]
@@ -54,18 +78,68 @@ class TC_testSubCommandParsing < Clean::Test::TestCase
54
78
  }
55
79
  end
56
80
 
81
+ test_that "in loose mode, argument validation is ignored" do
82
+ Given :app_with_arguments, 1, 1, false, :loose
83
+ When :run_app_with_X_arguments, 0
84
+ Then {
85
+ with_clue {
86
+ assert_equal 0, @results[:nbargs]
87
+ assert_equal 0, @exit_code
88
+ }
89
+ }
90
+ end
91
+
92
+ ix = -1
93
+ [
94
+ [1, 1, false, 0, :not_enough], [1, 1, false, 1, :success],
95
+ [1, 1, false, 2, :success], [1, 1, false, 3, :too_many],
96
+ [1, 1, true, 0, :not_enough], [1, 1, true, 1, :success],
97
+ [1, 1, true, 2, :success], [1, 1, true, 3, :success],
98
+ [1, 1, true, 30, :success], [0, 0, false, 0, :success],
99
+ [0, 0, false, 1, :too_many], [0, 1, false, 1, :success],
100
+ [0, 1, false, 0, :success], [1, 0, false, 1, :success],
101
+ [1, 0, false, 0, :not_enough], [0, 0, true, 0, :success],
102
+ [0, 0, true, 10, :success]
103
+ ].each do |nb_required, nb_optional, has_multiple, nb_generated, status|
104
+ ix = ix + 1
105
+ test_that "in strict mode, number of arguments is validated -- #{ix}" do
106
+ Given :app_with_arguments, nb_required, nb_optional, has_multiple, :strict
107
+ When :run_app_with_X_arguments, nb_generated
108
+ Then {
109
+ with_clue {
110
+ if status == :success then
111
+ assert_equal nb_generated, @results[:nbargs]
112
+ assert_equal 0, @exit_code
113
+ assert !@fake_stderr.contained?(/Not enough arguments for command/)
114
+ assert !@fake_stderr.contained?(/Too many arguments for command/)
115
+ elsif status == :not_enough then
116
+ assert_equal nil, @results[:nbargs]
117
+ assert_equal 64, @exit_code
118
+ assert @fake_stderr.contained?(/Not enough arguments for command/)
119
+ elsif status == :too_many then
120
+ assert_equal nil, @results[:nbargs]
121
+ assert_equal 64, @exit_code
122
+ assert @fake_stderr.contained?(/Too many arguments for command/)
123
+ else
124
+ assert false
125
+ end
126
+ }
127
+ }
128
+ end
129
+ end
57
130
  private
58
- def with_clue(message,&block)
131
+ def with_clue(&block)
59
132
  block.call
60
133
  rescue Exception
61
- PP.pp message,dump=""
62
- puts dump
134
+ dump = ""
135
+ PP.pp "\nRESULTS---#{@results}", dump unless @results.empty?
136
+ PP.pp "\nSTDERR---\n#{@fake_stderr.to_s}", dump
137
+ PP.pp "\nSTDOUT---\n#{@fake_stdout.to_s}", dump
138
+ @original_stdout.puts dump
63
139
  raise
64
140
  end
65
141
 
66
142
  def app_with_subcommands_storing_results(subcommand_option_handling_strategy = :legacy)
67
- @results = {}
68
- @app = CLIApp.new
69
143
  @app.subcommand_option_handling subcommand_option_handling_strategy
70
144
  @app.flag ['f','flag']
71
145
  @app.switch ['s','switch']
@@ -101,4 +175,25 @@ private
101
175
  end
102
176
  end
103
177
  end
178
+
179
+ def app_with_arguments(nb_required_arguments, nb_optional_arguments, has_argument_multiple, arguments_handling_strategy = :loose)
180
+ @app.arguments arguments_handling_strategy
181
+ @app.subcommand_option_handling :normal
182
+
183
+ nb_required_arguments.times { |i| @app.arg("needed#{i}") }
184
+ nb_optional_arguments.times { |i| @app.arg("optional#{i}", :optional) }
185
+ @app.arg :multiple, [:multiple, :optional] if has_argument_multiple
186
+
187
+ @app.command :cmd do |c|
188
+ c.action do |g,o,a|
189
+ @results = {
190
+ :nbargs => a.size
191
+ }
192
+ end
193
+ end
194
+ end
195
+
196
+ def run_app_with_X_arguments(nb_arguments)
197
+ @exit_code = @app.run [].tap{|args| args << "cmd"; nb_arguments.times {|i| args << "arg#{i}"}}
198
+ end
104
199
  end
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.11.0
4
+ version: 2.12.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-06-09 00:00:00.000000000 Z
11
+ date: 2014-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake