gli 2.11.0 → 2.12.0

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