trollop 1.8.2 → 1.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/History.txt +9 -0
  2. data/Rakefile +1 -2
  3. data/lib/trollop.rb +176 -52
  4. data/test/test_trollop.rb +279 -8
  5. metadata +15 -6
@@ -1,3 +1,12 @@
1
+ == 1.9 / 2008-08-20
2
+ * Added 'stop_on_unknown' command to stop parsing on any unknown argument.
3
+ This is useful for handling sub-commands when you don't know the entire
4
+ set of commands up front. (E.g. if the initial arguments can change it.)
5
+ * Added a :multi option for parameters, signifying that they can be specified
6
+ multiple times.
7
+ * Added :ints, :strings, :doubles, and :floats option types, which can take
8
+ multiple arguments.
9
+
1
10
  == 1.8.2 / 2008-06-25
2
11
  * Bugfix for #conflicts and #depends error messages
3
12
 
data/Rakefile CHANGED
@@ -13,8 +13,7 @@ end # thanks to "Mike H"
13
13
  Hoe.new('trollop', Trollop::VERSION) do |p|
14
14
  p.rubyforge_name = 'trollop'
15
15
  p.author = "William Morgan"
16
- p.summary = "Trollop is YAFCLAP --- yet another fine commandline argument processing library for Ruby. Trollop is designed to provide the maximal amount of GNU-style argument processing in the minimum number of lines of code (for you, the programmer)."
17
-
16
+ p.summary = "Trollop is a commandline option parser for Ruby that just gets out of your way. One line of code per option is all you need to write. For that, you get a nice automatically-generated help page, robust option parsing, command subcompletion, and sensible defaults for everything you don't specify."
18
17
  p.description = p.paragraphs_of('README.txt', 4..5, 9..18).join("\n\n").gsub(/== SYNOPSIS/, "Synopsis")
19
18
  p.url = "http://trollop.rubyforge.org"
20
19
  p.changes = p.paragraphs_of('History.txt', 0..0).join("\n\n")
@@ -5,7 +5,7 @@
5
5
 
6
6
  module Trollop
7
7
 
8
- VERSION = "1.8.2"
8
+ VERSION = "1.9"
9
9
 
10
10
  ## Thrown by Parser in the event of a commandline error. Not needed if
11
11
  ## you're using the Trollop::options entry.
@@ -30,11 +30,25 @@ PARAM_RE = /^-(-|\.$|[^\d\.])/
30
30
  ## methods #opt, #banner and #version, #depends, and #conflicts will
31
31
  ## typically be called.
32
32
  class Parser
33
+
34
+ ## The set of values that indicate a flag type of option when one of
35
+ ## the values is given to the :type parameter to #opt.
36
+ FLAG_TYPES = [:flag, :bool, :boolean]
37
+
38
+ ## The set of values that indicate an option that takes a single
39
+ ## parameter when one of the values is given to the :type parameter to
40
+ ## #opt.
41
+ SINGLE_ARG_TYPES = [:int, :integer, :string, :double, :float]
42
+
43
+ ## The set of values that indicate an option that takes multiple
44
+ ## parameters when one of the values is given to the :type parameter to
45
+ ## #opt.
46
+ MULTI_ARG_TYPES = [:ints, :integers, :strings, :doubles, :floats]
47
+
33
48
  ## The set of values specifiable as the :type parameter to #opt.
34
- TYPES = [:flag, :boolean, :bool, :int, :integer, :string, :double, :float]
49
+ TYPES = FLAG_TYPES + SINGLE_ARG_TYPES + MULTI_ARG_TYPES
35
50
 
36
- ## :nodoc:
37
- INVALID_SHORT_ARG_REGEX = /[\d-]/
51
+ INVALID_SHORT_ARG_REGEX = /[\d-]/ #:nodoc:
38
52
 
39
53
  ## The values from the commandline that were not interpreted by #parse.
40
54
  attr_reader :leftovers
@@ -53,6 +67,7 @@ class Parser
53
67
  @order = []
54
68
  @constraints = []
55
69
  @stop_words = []
70
+ @stop_on_unknown = false
56
71
 
57
72
  #instance_eval(&b) if b # can't take arguments
58
73
  cloaker(&b).bind(self).call(*a) if b
@@ -69,11 +84,14 @@ class Parser
69
84
  ## * :short: Specify the short form of the argument, i.e. the form
70
85
  ## with one dash. If unspecified, will be automatically derived
71
86
  ## based on the argument name.
72
- ## * :type: Require that the argument take a parameter of type
73
- ## 'type'. Can by any member of the TYPES constant or a
74
- ## corresponding class (e.g. Integer for :int). If unset, the
75
- ## default argument type is :flag, meaning the argument does not
76
- ## take a parameter. Not necessary if :default: is specified.
87
+ ## * :type: Require that the argument take a parameter or parameters
88
+ ## of type 'type'. For a single parameter, the value can be a
89
+ ## member of the SINGLE_ARG_TYPES constant or a corresponding class
90
+ ## (e.g. Integer for :int). For multiple parameters, the value can
91
+ ## be a member of the MULTI_ARG_TYPES constant. If unset, the
92
+ ## default argument type is :flag, meaning that the argument does
93
+ ## not take a parameter. The specification of :type is not
94
+ ## necessary if :default is given.
77
95
  ## * :default: Set the default value for an argument. Without a
78
96
  ## default value, the hash returned by #parse (and thus
79
97
  ## Trollop#options) will not contain the argument unless it is
@@ -81,8 +99,11 @@ class Parser
81
99
  ## automatically from the class of the default value given, if
82
100
  ## any. Specifying a :flag argument on the commandline whose
83
101
  ## default value is true will change its value to false.
84
- ## * :required: if set to true, the argument must be provided on the
102
+ ## * :required: If set to true, the argument must be provided on the
85
103
  ## commandline.
104
+ ## * :multi: If set to true, allows multiple instances of the
105
+ ## option. Otherwise, only a single instance of the option is
106
+ ## allowed.
86
107
  def opt name, desc="", opts={}
87
108
  raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? name
88
109
 
@@ -91,8 +112,11 @@ class Parser
91
112
  case opts[:type]
92
113
  when :flag, :boolean, :bool; :flag
93
114
  when :int, :integer; :int
115
+ when :ints, :integers; :ints
94
116
  when :string; :string
117
+ when :strings; :strings
95
118
  when :double, :float; :float
119
+ when :doubles, :floats; :floats
96
120
  when Class
97
121
  case opts[:type].to_s # sigh... there must be a better way to do this
98
122
  when 'TrueClass', 'FalseClass'; :flag
@@ -113,6 +137,17 @@ class Parser
113
137
  when Numeric; :float
114
138
  when TrueClass, FalseClass; :flag
115
139
  when String; :string
140
+ when Array
141
+ if opts[:default].empty?
142
+ raise ArgumentError, "multiple argument type cannot be deduced from an empty array for '#{opts[:default][0].class.name}'"
143
+ end
144
+ case opts[:default][0] # the first element determines the types
145
+ when Integer; :ints
146
+ when Numeric; :floats
147
+ when String; :strings
148
+ else
149
+ raise ArgumentError, "unsupported multiple argument type '#{opts[:default][0].class.name}'"
150
+ end
116
151
  when nil; nil
117
152
  else
118
153
  raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
@@ -160,6 +195,9 @@ class Parser
160
195
  ## fill in :default for flags
161
196
  opts[:default] = false if opts[:type] == :flag && opts[:default].nil?
162
197
 
198
+ ## fill in :multi
199
+ opts[:multi] ||= false
200
+
163
201
  opts[:desc] ||= desc
164
202
  @long[opts[:long]] = name
165
203
  @short[opts[:short]] = name if opts[:short]
@@ -201,6 +239,12 @@ class Parser
201
239
  @stop_words = [*words].flatten
202
240
  end
203
241
 
242
+ ## Similar to stop_on, but stops on any unknown word when encountered (unless
243
+ ## it is a parameter for an argument).
244
+ def stop_on_unknown
245
+ @stop_on_unknown = true
246
+ end
247
+
204
248
  ## yield successive arg, parameter pairs
205
249
  def each_arg args # :nodoc:
206
250
  remains = []
@@ -209,19 +253,28 @@ class Parser
209
253
  until i >= args.length
210
254
  if @stop_words.member? args[i]
211
255
  remains += args[i .. -1]
212
- break
256
+ return remains
213
257
  end
214
258
  case args[i]
215
259
  when /^--$/ # arg terminator
216
260
  remains += args[(i + 1) .. -1]
217
- break
261
+ return remains
218
262
  when /^--(\S+?)=(\S+)$/ # long argument with equals
219
- yield "--#{$1}", $2
263
+ yield "--#{$1}", [$2]
220
264
  i += 1
221
265
  when /^--(\S+)$/ # long argument
222
- if args[i + 1] && args[i + 1] !~ PARAM_RE && !@stop_words.member?(args[i + 1])
223
- remains << args[i + 1] unless yield args[i], args[i + 1]
224
- i += 2
266
+ params = collect_argument_parameters(args, i + 1)
267
+ unless params.empty?
268
+ num_params_taken = yield args[i], params
269
+ unless num_params_taken
270
+ if @stop_on_unknown
271
+ remains += args[i + 1 .. -1]
272
+ return remains
273
+ else
274
+ remains += params
275
+ end
276
+ end
277
+ i += 1 + num_params_taken
225
278
  else # long argument no parameter
226
279
  yield args[i], nil
227
280
  i += 1
@@ -229,26 +282,44 @@ class Parser
229
282
  when /^-(\S+)$/ # one or more short arguments
230
283
  shortargs = $1.split(//)
231
284
  shortargs.each_with_index do |a, j|
232
- if j == (shortargs.length - 1) && args[i + 1] && args[i + 1] !~ PARAM_RE && !@stop_words.member?(args[i + 1])
233
- remains << args[i + 1] unless yield "-#{a}", args[i + 1]
234
- i += 1 # once more below
285
+ if j == (shortargs.length - 1)
286
+ params = collect_argument_parameters(args, i + 1)
287
+ unless params.empty?
288
+ num_params_taken = yield "-#{a}", params
289
+ unless num_params_taken
290
+ if @stop_on_unknown
291
+ remains += args[i + 1 .. -1]
292
+ return remains
293
+ else
294
+ remains += params
295
+ end
296
+ end
297
+ i += 1 + num_params_taken
298
+ else # argument no parameter
299
+ yield "-#{a}", nil
300
+ i += 1
301
+ end
235
302
  else
236
303
  yield "-#{a}", nil
237
304
  end
238
305
  end
239
- i += 1
240
306
  else
241
- remains << args[i]
242
- i += 1
307
+ if @stop_on_unknown
308
+ remains += args[i .. -1]
309
+ return remains
310
+ else
311
+ remains << args[i]
312
+ i += 1
313
+ end
243
314
  end
244
315
  end
316
+
245
317
  remains
246
318
  end
247
319
 
248
320
  def parse cmdline #:nodoc:
249
321
  vals = {}
250
322
  required = {}
251
- found = {}
252
323
 
253
324
  opt :version, "Print version and exit" if @version unless @specs[:version] || @long["version"]
254
325
  opt :help, "Show this message" unless @specs[:help] || @long["help"]
@@ -259,8 +330,8 @@ class Parser
259
330
  end
260
331
 
261
332
  ## resolve symbols
262
- args = []
263
- @leftovers = each_arg cmdline do |arg, param|
333
+ given_args = {}
334
+ @leftovers = each_arg cmdline do |arg, params|
264
335
  sym =
265
336
  case arg
266
337
  when /^-([^-])$/
@@ -271,57 +342,107 @@ class Parser
271
342
  raise CommandlineError, "invalid argument syntax: '#{arg}'"
272
343
  end
273
344
  raise CommandlineError, "unknown argument '#{arg}'" unless sym
274
- raise CommandlineError, "option '#{arg}' specified multiple times" if found[sym]
275
- args << [sym, arg, param]
276
- found[sym] = true
277
345
 
278
- @specs[sym][:type] != :flag # take params on all except flags
346
+ if given_args.include?(sym) && !@specs[sym][:multi]
347
+ raise CommandlineError, "option '#{arg}' specified multiple times"
348
+ end
349
+
350
+ given_args[sym] ||= {}
351
+
352
+ given_args[sym][:arg] = arg
353
+ given_args[sym][:params] ||= []
354
+
355
+ # The block returns the number of parameters taken.
356
+ num_params_taken = 0
357
+
358
+ unless params.nil?
359
+ if SINGLE_ARG_TYPES.include?(@specs[sym][:type])
360
+ given_args[sym][:params] << params[0, 1] # take the first parameter
361
+ num_params_taken = 1
362
+ elsif MULTI_ARG_TYPES.include?(@specs[sym][:type])
363
+ given_args[sym][:params] << params # take all the parameters
364
+ num_params_taken = params.size
365
+ end
366
+ end
367
+
368
+ num_params_taken
279
369
  end
280
370
 
281
371
  ## check for version and help args
282
- raise VersionNeeded if args.any? { |sym, *a| sym == :version }
283
- raise HelpNeeded if args.any? { |sym, *a| sym == :help }
372
+ raise VersionNeeded if given_args.include? :version
373
+ raise HelpNeeded if given_args.include? :help
284
374
 
285
375
  ## check constraint satisfaction
286
376
  @constraints.each do |type, syms|
287
- constraint_sym = syms.find { |sym| found[sym] }
377
+ constraint_sym = syms.find { |sym| given_args[sym] }
288
378
  next unless constraint_sym
289
379
 
290
380
  case type
291
381
  when :depends
292
- syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} requires --#{@specs[sym][:long]}" unless found[sym] }
382
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} requires --#{@specs[sym][:long]}" unless given_args.include? sym }
293
383
  when :conflicts
294
- syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} conflicts with --#{@specs[sym][:long]}" if found[sym] && sym != constraint_sym }
384
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} conflicts with --#{@specs[sym][:long]}" if given_args.include?(sym) && (sym != constraint_sym) }
295
385
  end
296
386
  end
297
387
 
298
388
  required.each do |sym, val|
299
- raise CommandlineError, "option '#{sym}' must be specified" unless found[sym]
389
+ raise CommandlineError, "option '#{sym}' must be specified" unless given_args.include? sym
300
390
  end
301
391
 
302
392
  ## parse parameters
303
- args.each do |sym, arg, param|
304
- opts = @specs[sym]
393
+ given_args.each do |sym, given_data|
394
+ arg = given_data[:arg]
395
+ params = given_data[:params]
305
396
 
306
- raise CommandlineError, "option '#{arg}' needs a parameter" unless param || opts[:type] == :flag
397
+ opts = @specs[sym]
398
+ raise CommandlineError, "option '#{arg}' needs a parameter" if params.empty? && opts[:type] != :flag
307
399
 
308
400
  case opts[:type]
309
401
  when :flag
310
402
  vals[sym] = !opts[:default]
311
- when :int
312
- raise CommandlineError, "option '#{arg}' needs an integer" unless param =~ /^\d+$/
313
- vals[sym] = param.to_i
314
- when :float
315
- raise CommandlineError, "option '#{arg}' needs a floating-point number" unless param =~ FLOAT_RE
316
- vals[sym] = param.to_f
317
- when :string
318
- vals[sym] = param.to_s
403
+ when :int, :ints
404
+ vals[sym] = params.map { |pg| pg.map { |p| parse_integer_parameter p, arg } }
405
+ when :float, :floats
406
+ vals[sym] = params.map { |pg| pg.map { |p| parse_float_parameter p, arg } }
407
+ when :string, :strings
408
+ vals[sym] = params.map { |pg| pg.map { |p| p.to_s } }
409
+ end
410
+
411
+ if SINGLE_ARG_TYPES.include?(opts[:type])
412
+ unless opts[:multi] # single parameter
413
+ vals[sym] = vals[sym][0][0]
414
+ else # multiple options, each with a single parameter
415
+ vals[sym] = vals[sym].map { |p| p[0] }
416
+ end
417
+ elsif MULTI_ARG_TYPES.include?(opts[:type]) && !opts[:multi]
418
+ vals[sym] = vals[sym][0] # single option, with multiple parameters
319
419
  end
420
+ # else: multiple options, with multiple parameters
320
421
  end
321
422
 
322
423
  vals
323
424
  end
324
425
 
426
+ def parse_integer_parameter param, arg #:nodoc:
427
+ raise CommandlineError, "option '#{arg}' needs an integer" unless param =~ /^\d+$/
428
+ param.to_i
429
+ end
430
+
431
+ def parse_float_parameter param, arg #:nodoc:
432
+ raise CommandlineError, "option '#{arg}' needs a floating-point number" unless param =~ FLOAT_RE
433
+ param.to_f
434
+ end
435
+
436
+ def collect_argument_parameters args, start_at #:nodoc:
437
+ params = []
438
+ pos = start_at
439
+ while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos]) do
440
+ params << args[pos]
441
+ pos += 1
442
+ end
443
+ params
444
+ end
445
+
325
446
  def width #:nodoc:
326
447
  @width ||=
327
448
  if $stdout.tty?
@@ -351,8 +472,11 @@ class Parser
351
472
  case spec[:type]
352
473
  when :flag; ""
353
474
  when :int; " <i>"
475
+ when :ints; " <i+>"
354
476
  when :string; " <s>"
477
+ when :strings; " <s+>"
355
478
  when :float; " <f>"
479
+ when :floats; " <f+>"
356
480
  end
357
481
  end
358
482
 
@@ -427,9 +551,9 @@ class Parser
427
551
  end
428
552
 
429
553
  ## The top-level entry method into Trollop. Creates a Parser object,
430
- ## passes the block to it, then parses ARGV with it, handling any
554
+ ## passes the block to it, then parses +args+ with it, handling any
431
555
  ## errors or requests for help or version information appropriately
432
- ## (and then exiting). Modifies ARGV in place. Returns a hash of
556
+ ## (and then exiting). Modifies +args+ in place. Returns a hash of
433
557
  ## option values.
434
558
  ##
435
559
  ## The block passed in should contain one or more calls to #opt
@@ -437,12 +561,12 @@ end
437
561
  ## probably a call to version (Parser#version).
438
562
  ##
439
563
  ## See the synopsis in README.txt for examples.
440
- def options *a, &b
564
+ def options args = ARGV, *a, &b
441
565
  @p = Parser.new(*a, &b)
442
566
  begin
443
- vals = @p.parse ARGV
444
- ARGV.clear
445
- @p.leftovers.each { |l| ARGV << l }
567
+ vals = @p.parse args
568
+ args.clear
569
+ @p.leftovers.each { |l| args << l }
446
570
  vals
447
571
  rescue CommandlineError => e
448
572
  $stderr.puts "Error: #{e.message}."
@@ -76,20 +76,80 @@ class Trollop < ::Test::Unit::TestCase
76
76
 
77
77
  ## type is correctly derived from :default
78
78
  def test_type_correctly_derived_from_default
79
- assert_nothing_raised { @p.opt "goodarg", "desc", :default => 0 }
80
79
  assert_raise(ArgumentError) { @p.opt "badarg", "desc", :default => [] }
81
80
 
82
- assert_nothing_raised { @p.parse(%w(--goodarg 3)) }
83
- assert_raise(CommandlineError) { @p.parse(%w(--goodarg 4.2)) }
84
- assert_raise(CommandlineError) { @p.parse(%w(--goodarg hello)) }
81
+ opts = nil
82
+
83
+ # single arg: int
84
+ assert_nothing_raised { @p.opt "argsi", "desc", :default => 0 }
85
+ assert_nothing_raised { opts = @p.parse("--") }
86
+ assert_equal 0, opts["argsi"]
87
+ assert_nothing_raised { opts = @p.parse(%w(--argsi 4)) }
88
+ assert_equal 4, opts["argsi"]
89
+ assert_raise(CommandlineError) { @p.parse(%w(--argsi 4.2)) }
90
+ assert_raise(CommandlineError) { @p.parse(%w(--argsi hello)) }
91
+
92
+ # single arg: float
93
+ assert_nothing_raised { @p.opt "argsf", "desc", :default => 3.14 }
94
+ assert_nothing_raised { opts = @p.parse("--") }
95
+ assert_equal 3.14, opts["argsf"]
96
+ assert_nothing_raised { opts = @p.parse(%w(--argsf 2.41)) }
97
+ assert_equal 2.41, opts["argsf"]
98
+ assert_nothing_raised { opts = @p.parse(%w(--argsf 2)) }
99
+ assert_equal 2, opts["argsf"]
100
+ assert_raise(CommandlineError) { @p.parse(%w(--argsf hello)) }
101
+
102
+ # single arg: string
103
+ assert_nothing_raised { @p.opt "argss", "desc", :default => "foobar" }
104
+ assert_nothing_raised { opts = @p.parse("--") }
105
+ assert_equal "foobar", opts["argss"]
106
+ assert_nothing_raised { opts = @p.parse(%w(--argss 2.41)) }
107
+ assert_equal "2.41", opts["argss"]
108
+ assert_nothing_raised { opts = @p.parse(%w(--argss hello)) }
109
+ assert_equal "hello", opts["argss"]
110
+
111
+ # multi args: ints
112
+ assert_nothing_raised { @p.opt "argmi", "desc", :default => [3, 5] }
113
+ assert_nothing_raised { opts = @p.parse("--") }
114
+ assert_equal [3, 5], opts["argmi"]
115
+ assert_nothing_raised { opts = @p.parse(%w(--argmi 4)) }
116
+ assert_equal [4], opts["argmi"]
117
+ assert_raise(CommandlineError) { @p.parse(%w(--argmi 4.2)) }
118
+ assert_raise(CommandlineError) { @p.parse(%w(--argmi hello)) }
119
+
120
+ # multi args: floats
121
+ assert_nothing_raised { @p.opt "argmf", "desc", :default => [3.34, 5.21] }
122
+ assert_nothing_raised { opts = @p.parse("--") }
123
+ assert_equal [3.34, 5.21], opts["argmf"]
124
+ assert_nothing_raised { opts = @p.parse(%w(--argmf 2)) }
125
+ assert_equal [2], opts["argmf"]
126
+ assert_nothing_raised { opts = @p.parse(%w(--argmf 4.0)) }
127
+ assert_equal [4.0], opts["argmf"]
128
+ assert_raise(CommandlineError) { @p.parse(%w(--argmf hello)) }
129
+
130
+ # multi args: strings
131
+ assert_nothing_raised { @p.opt "argms", "desc", :default => %w(hello world) }
132
+ assert_nothing_raised { opts = @p.parse("--") }
133
+ assert_equal %w(hello world), opts["argms"]
134
+ assert_nothing_raised { opts = @p.parse(%w(--argms 3.4)) }
135
+ assert_equal ["3.4"], opts["argms"]
136
+ assert_nothing_raised { opts = @p.parse(%w(--argms goodbye)) }
137
+ assert_equal ["goodbye"], opts["argms"]
85
138
  end
86
139
 
87
140
  ## :type and :default must match if both are specified
88
141
  def test_type_and_default_must_match
89
- assert_nothing_raised { @p.opt "goodarg", "desc", :type => :int, :default => 4 }
90
- assert_nothing_raised { @p.opt "goodarg2", "desc", :type => :string, :default => "yo" }
91
142
  assert_raise(ArgumentError) { @p.opt "badarg", "desc", :type => :int, :default => "hello" }
92
143
  assert_raise(ArgumentError) { @p.opt "badarg2", "desc", :type => :String, :default => 4 }
144
+ assert_raise(ArgumentError) { @p.opt "badarg2", "desc", :type => :String, :default => ["hi"] }
145
+ assert_raise(ArgumentError) { @p.opt "badarg2", "desc", :type => :ints, :default => [3.14] }
146
+
147
+ assert_nothing_raised { @p.opt "argsi", "desc", :type => :int, :default => 4 }
148
+ assert_nothing_raised { @p.opt "argsf", "desc", :type => :float, :default => 3.14 }
149
+ assert_nothing_raised { @p.opt "argss", "desc", :type => :string, :default => "yo" }
150
+ assert_nothing_raised { @p.opt "argmi", "desc", :type => :ints, :default => [4] }
151
+ assert_nothing_raised { @p.opt "argmf", "desc", :type => :floats, :default => [3.14] }
152
+ assert_nothing_raised { @p.opt "argms", "desc", :type => :strings, :default => ["yo"] }
93
153
  end
94
154
 
95
155
  def test_long_detects_bad_names
@@ -306,13 +366,180 @@ EOM
306
366
  assert_raises(ArgumentError) { @p.opt :arg3, "desc", :short => "-" }
307
367
  end
308
368
 
309
- def test_options_cant_be_set_multiple_times
369
+ def test_options_cant_be_set_multiple_times_if_not_specified
310
370
  @p.opt :arg, "desc", :short => "-x"
311
371
  assert_nothing_raised { @p.parse %w(-x) }
312
372
  assert_raises(CommandlineError) { @p.parse %w(-x -x) }
313
373
  assert_raises(CommandlineError) { @p.parse %w(-xx) }
314
374
  end
315
375
 
376
+ def test_options_can_be_set_multiple_times_if_specified
377
+ assert_nothing_raised do
378
+ @p.opt :arg, "desc", :short => "-x", :multi => true
379
+ end
380
+ assert_nothing_raised { @p.parse %w(-x) }
381
+ assert_nothing_raised { @p.parse %w(-x -x) }
382
+ assert_nothing_raised { @p.parse %w(-xx) }
383
+ end
384
+
385
+ def test_short_options_with_multiple_options
386
+ opts = nil
387
+
388
+ assert_nothing_raised do
389
+ @p.opt :xarg, "desc", :short => "-x", :type => String, :multi => true
390
+ end
391
+ assert_nothing_raised { opts = @p.parse %w(-x a -x b) }
392
+ assert_equal %w(a b), opts[:xarg]
393
+ assert_equal [], @p.leftovers
394
+ end
395
+
396
+ def short_options_with_multiple_options_does_not_affect_flags_type
397
+ opts = nil
398
+
399
+ assert_nothing_raised do
400
+ @p.opt :xarg, "desc", :short => "-x", :type => :flag, :multi => true
401
+ end
402
+
403
+ assert_nothing_raised { opts = @p.parse %w(-x a) }
404
+ assert_equal true, opts[:xarg]
405
+ assert_equal %w(a), @p.leftovers
406
+
407
+ assert_nothing_raised { opts = @p.parse %w(-x a -x b) }
408
+ assert_equal true, opts[:xarg]
409
+ assert_equal %w(a b), @p.leftovers
410
+
411
+ assert_nothing_raised { opts = @p.parse %w(-xx a -x b) }
412
+ assert_equal true, opts[:xarg]
413
+ assert_equal %w(a b), @p.leftovers
414
+ end
415
+
416
+ def test_short_options_with_multiple_arguments
417
+ opts = nil
418
+
419
+ @p.opt :xarg, "desc", :type => :ints
420
+ assert_nothing_raised { opts = @p.parse %w(-x 3 4 0) }
421
+ assert_equal [3, 4, 0], opts[:xarg]
422
+ assert_equal [], @p.leftovers
423
+
424
+ @p.opt :yarg, "desc", :type => :floats
425
+ assert_nothing_raised { opts = @p.parse %w(-y 3.14 4.21 0.66) }
426
+ assert_equal [3.14, 4.21, 0.66], opts[:yarg]
427
+ assert_equal [], @p.leftovers
428
+
429
+ @p.opt :zarg, "desc", :type => :strings
430
+ assert_nothing_raised { opts = @p.parse %w(-z a b c) }
431
+ assert_equal %w(a b c), opts[:zarg]
432
+ assert_equal [], @p.leftovers
433
+ end
434
+
435
+ def test_short_options_with_multiple_options_and_arguments
436
+ opts = nil
437
+
438
+ @p.opt :xarg, "desc", :type => :ints, :multi => true
439
+ assert_nothing_raised { opts = @p.parse %w(-x 3 4 5 -x 6 7) }
440
+ assert_equal [[3, 4, 5], [6, 7]], opts[:xarg]
441
+ assert_equal [], @p.leftovers
442
+
443
+ @p.opt :yarg, "desc", :type => :floats, :multi => true
444
+ assert_nothing_raised { opts = @p.parse %w(-y 3.14 4.21 5.66 -y 6.99 7.01) }
445
+ assert_equal [[3.14, 4.21, 5.66], [6.99, 7.01]], opts[:yarg]
446
+ assert_equal [], @p.leftovers
447
+
448
+ @p.opt :zarg, "desc", :type => :strings, :multi => true
449
+ assert_nothing_raised { opts = @p.parse %w(-z a b c -z d e) }
450
+ assert_equal [%w(a b c), %w(d e)], opts[:zarg]
451
+ assert_equal [], @p.leftovers
452
+ end
453
+
454
+ def test_combined_short_options_with_multiple_arguments
455
+ @p.opt :arg1, "desc", :short => "a"
456
+ @p.opt :arg2, "desc", :short => "b"
457
+ @p.opt :arg3, "desc", :short => "c", :type => :ints
458
+ @p.opt :arg4, "desc", :short => "d", :type => :floats
459
+
460
+ opts = nil
461
+
462
+ assert_nothing_raised { opts = @p.parse %w(-abc 4 6 9) }
463
+ assert_equal true, opts[:arg1]
464
+ assert_equal true, opts[:arg2]
465
+ assert_equal [4, 6, 9], opts[:arg3]
466
+
467
+ assert_nothing_raised { opts = @p.parse %w(-ac 4 6 9 -bd 3.14 2.41) }
468
+ assert_equal true, opts[:arg1]
469
+ assert_equal true, opts[:arg2]
470
+ assert_equal [4, 6, 9], opts[:arg3]
471
+ assert_equal [3.14, 2.41], opts[:arg4]
472
+
473
+ assert_raises(CommandlineError) { opts = @p.parse %w(-abcd 3.14 2.41) }
474
+ end
475
+
476
+ def test_long_options_with_multiple_options
477
+ @p.opt :xarg, "desc", :type => String, :multi => true
478
+ opts = nil
479
+ assert_nothing_raised { opts = @p.parse %w(--xarg=a --xarg=b) }
480
+ assert_equal %w(a b), opts[:xarg]
481
+ assert_equal [], @p.leftovers
482
+ assert_nothing_raised { opts = @p.parse %w(--xarg a --xarg b) }
483
+ assert_equal %w(a b), opts[:xarg]
484
+ assert_equal [], @p.leftovers
485
+ end
486
+
487
+ def test_long_options_with_multiple_arguments
488
+ opts = nil
489
+
490
+ @p.opt :xarg, "desc", :type => :ints
491
+ assert_nothing_raised { opts = @p.parse %w(--xarg 3 2 5) }
492
+ assert_equal [3, 2, 5], opts[:xarg]
493
+ assert_equal [], @p.leftovers
494
+ assert_nothing_raised { opts = @p.parse %w(--xarg=3) }
495
+ assert_equal [3], opts[:xarg]
496
+ assert_equal [], @p.leftovers
497
+
498
+ @p.opt :yarg, "desc", :type => :floats
499
+ assert_nothing_raised { opts = @p.parse %w(--yarg 3.14 2.41 5.66) }
500
+ assert_equal [3.14, 2.41, 5.66], opts[:yarg]
501
+ assert_equal [], @p.leftovers
502
+ assert_nothing_raised { opts = @p.parse %w(--yarg=3.14) }
503
+ assert_equal [3.14], opts[:yarg]
504
+ assert_equal [], @p.leftovers
505
+
506
+ @p.opt :zarg, "desc", :type => :strings
507
+ assert_nothing_raised { opts = @p.parse %w(--zarg a b c) }
508
+ assert_equal %w(a b c), opts[:zarg]
509
+ assert_equal [], @p.leftovers
510
+ assert_nothing_raised { opts = @p.parse %w(--zarg=a) }
511
+ assert_equal %w(a), opts[:zarg]
512
+ assert_equal [], @p.leftovers
513
+ end
514
+
515
+ def test_long_options_with_multiple_options_and_arguments
516
+ opts = nil
517
+
518
+ @p.opt :xarg, "desc", :type => :ints, :multi => true
519
+ assert_nothing_raised { opts = @p.parse %w(--xarg 3 2 5 --xarg 2 1) }
520
+ assert_equal [[3, 2, 5], [2, 1]], opts[:xarg]
521
+ assert_equal [], @p.leftovers
522
+ assert_nothing_raised { opts = @p.parse %w(--xarg=3 --xarg=2) }
523
+ assert_equal [[3], [2]], opts[:xarg]
524
+ assert_equal [], @p.leftovers
525
+
526
+ @p.opt :yarg, "desc", :type => :floats, :multi => true
527
+ assert_nothing_raised { opts = @p.parse %w(--yarg 3.14 2.72 5 --yarg 2.41 1.41) }
528
+ assert_equal [[3.14, 2.72, 5], [2.41, 1.41]], opts[:yarg]
529
+ assert_equal [], @p.leftovers
530
+ assert_nothing_raised { opts = @p.parse %w(--yarg=3.14 --yarg=2.41) }
531
+ assert_equal [[3.14], [2.41]], opts[:yarg]
532
+ assert_equal [], @p.leftovers
533
+
534
+ @p.opt :zarg, "desc", :type => :strings, :multi => true
535
+ assert_nothing_raised { opts = @p.parse %w(--zarg a b c --zarg d e) }
536
+ assert_equal [%w(a b c), %w(d e)], opts[:zarg]
537
+ assert_equal [], @p.leftovers
538
+ assert_nothing_raised { opts = @p.parse %w(--zarg=a --zarg=d) }
539
+ assert_equal [%w(a), %w(d)], opts[:zarg]
540
+ assert_equal [], @p.leftovers
541
+ end
542
+
316
543
  def test_long_options_also_take_equals
317
544
  @p.opt :arg, "desc", :long => "arg", :type => String, :default => "hello"
318
545
  opts = nil
@@ -389,7 +616,7 @@ EOM
389
616
  assert_raises(HelpNeeded) { @p.parse(%w(--help)) }
390
617
  end
391
618
 
392
- def test_version_and_help_long_args_cann_be_overridden
619
+ def test_version_and_help_long_args_can_be_overridden
393
620
  @p.opt :asdf, "desc", :long => "help"
394
621
  @p.opt :asdf2, "desc2", :long => "version"
395
622
  assert_nothing_raised { @p.parse %w() }
@@ -599,6 +826,50 @@ EOM
599
826
  assert_equal cmd, "sub-command-1"
600
827
  assert_equal @q.leftovers, []
601
828
  end
829
+
830
+ def assert_parses_correctly(parser, commandline, expected_opts,
831
+ expected_leftovers)
832
+ opts = parser.parse commandline
833
+ assert_equal expected_opts, opts
834
+ assert_equal expected_leftovers, parser.leftovers
835
+ end
836
+
837
+ def test_unknown_subcommand
838
+ @p.opt :global_flag, "Global flag", :short => "-g", :type => :flag
839
+ @p.opt :global_param, "Global parameter", :short => "-p", :default => 5
840
+ @p.stop_on_unknown
841
+
842
+ expected_opts = { :global_flag =>true, :help =>false, :global_param =>5 }
843
+ expected_leftovers = [ "my_subcommand", "-c" ]
844
+
845
+ assert_parses_correctly @p, %w(--global-flag my_subcommand -c), \
846
+ expected_opts, expected_leftovers
847
+ assert_parses_correctly @p, %w(-g my_subcommand -c), \
848
+ expected_opts, expected_leftovers
849
+
850
+ expected_opts = { :global_flag =>false, :help =>false, :global_param =>5 }
851
+ expected_leftovers = [ "my_subcommand", "-c" ]
852
+
853
+ assert_parses_correctly @p, %w(-p 5 my_subcommand -c), \
854
+ expected_opts, expected_leftovers
855
+ assert_parses_correctly @p, %w(--global-param 5 my_subcommand -c), \
856
+ expected_opts, expected_leftovers
857
+ end
858
+
859
+ def test_alternate_args
860
+ args = %w(-a -b -c)
861
+
862
+ opts = ::Trollop.options(args) do
863
+ opt :alpher, "Ralph Alpher", :short => "-a"
864
+ opt :bethe, "Hans Bethe", :short => "-b"
865
+ opt :gamow, "George Gamow", :short => "-c"
866
+ end
867
+
868
+ physicists_with_humor = [:alpher, :bethe, :gamow]
869
+ physicists_with_humor.each do |physicist|
870
+ assert_equal true, opts[physicist]
871
+ end
872
+ end
602
873
  end
603
874
 
604
875
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trollop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.2
4
+ version: "1.9"
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Morgan
@@ -9,10 +9,19 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-06-25 00:00:00 -07:00
12
+ date: 2008-08-20 00:00:00 -07:00
13
13
  default_executable:
14
- dependencies: []
15
-
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.7.0
24
+ version:
16
25
  description: "== DESCRIPTION Trollop is a commandline option parser for Ruby that just gets out of your way. One line of code per option is all you need to write. For that, you get a nice automatically-generated help page, robust option parsing, command subcompletion, and sensible defaults for everything you don't specify. #################### ###### simple ###### #################### require 'trollop' opts = Trollop::options do opt :monkey, \"Use monkey mode\" opt :goat, \"Use goat mode\", :default => true opt :num_limbs, \"Set number of limbs\", :default => 4 end p opts # { :monkey => false, :goat => true, :num_limbs => 4 } #################### ###### medium ###### #################### require 'trollop' opts = Trollop::options do version \"test 1.2.3 (c) 2007 William Morgan\" banner <<-EOS Test is an awesome program that does something very, very important. Usage: test [options] <filenames>+ where [options] are: EOS opt :ignore, \"Ignore incorrect values\" opt :file, \"Extra data filename to read in, with a very long option description like this one\", :type => String opt :volume, \"Volume level\", :default => 3.0 opt :iters, \"Number of iterations\", :default => 5 end Trollop::die :volume, \"must be non-negative\" if opts[:volume] < 0 Trollop::die :file, \"must exist\" unless File.exist?(opts[:file]) if opts[:file] ################################ ##### sub-command support ###### ################################ require 'trollop' global_opts = Trollop::options do opt :global_option, \"This is a global option\" stop_on %w(sub-command-1 sub-command-2) end cmd = ARGV.shift cmd_opts = Trollop::options do opt :cmd_option, \"This is an option only for the subcommand\" end p global_opts p cmd p cmd_opts"
17
26
  email: wmorgan-trollop@masanjin.net
18
27
  executables: []
@@ -55,9 +64,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
55
64
  requirements: []
56
65
 
57
66
  rubyforge_project: trollop
58
- rubygems_version: 1.1.1
67
+ rubygems_version: 1.2.0
59
68
  signing_key:
60
69
  specification_version: 2
61
- summary: Trollop is YAFCLAP --- yet another fine commandline argument processing library for Ruby. Trollop is designed to provide the maximal amount of GNU-style argument processing in the minimum number of lines of code (for you, the programmer).
70
+ summary: Trollop is a commandline option parser for Ruby that just gets out of your way. One line of code per option is all you need to write. For that, you get a nice automatically-generated help page, robust option parsing, command subcompletion, and sensible defaults for everything you don't specify.
62
71
  test_files:
63
72
  - test/test_trollop.rb