trollop 1.8.2 → 1.9

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.
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