trollop 1.13 → 1.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/FAQ.txt +66 -17
  2. data/History.txt +4 -0
  3. data/lib/trollop.rb +35 -9
  4. data/test/test_trollop.rb +47 -7
  5. metadata +3 -3
data/FAQ.txt CHANGED
@@ -1,11 +1,14 @@
1
1
  Trollop FAQ
2
2
  -----------
3
3
 
4
+ Q: Why is it called "Trollop"?
5
+ A: No reason.
6
+
4
7
  Q: Why should I use Trollop?
5
- A: Because it will take you FEWER LINES OF CODE to do reasonable option
6
- parsing than any other option parser in existence.
8
+ A: Because it will take you FEWER LINES OF CODE to do reasonable option parsing
9
+ than any other option parser out there.
7
10
 
8
- Look:
11
+ Look at this:
9
12
 
10
13
  opts = Trollop::options do
11
14
  opt :monkey, "Use monkey mode"
@@ -15,21 +18,67 @@ A: Because it will take you FEWER LINES OF CODE to do reasonable option
15
18
 
16
19
  That's it. And opts is a hash and you do whatever you want with it.
17
20
  Trivial. You don't have to mix option processing code blocks with the
18
- declarations. You don't have to make a class for every option (what is
19
- this, Java?). You don't have to write more than 1 line of code per
20
- option.
21
+ declarations. You don't have to make a class for every option (what is this,
22
+ Java?). You don't have to write more than 1 line of code per option.
21
23
 
22
- Q: Why is it called "Trollop"?
23
- A: No reason.
24
+ Plus, you get a beautiful help screen that detects your terminal width and
25
+ wraps appropriately. C'mon, that's hot.
26
+
27
+ Q: What is the philosophy behind Trollop?
28
+ A: Must a commandline option processor have a philosophy?
29
+
30
+ Q: Seriously now. What is it?
31
+ A: Ok, it's this: Trollop *just* does the parsing and gives you a hash table
32
+ of the result. So whatever fancy logic or constraints you need, you can
33
+ implement by operating on that hash table. Options that disable other
34
+ options, fancy constraints involving multiple sets of values across multiple
35
+ sets of options, etc. are all left for you to do manually.
36
+
37
+ (Trollop does support limited constraint setting (see #conflicts and
38
+ #depends), but any non-trivial program will need to get fancier.)
39
+
40
+ The result is that using Trollop is pretty simple, and whatever bizarre
41
+ logic you want, you can write yourself. And don't forget, you can call
42
+ Trollop::die to abort the program and give a fancy options-related error
43
+ message.
44
+
45
+ Q: What happens to the other stuff on the commandline?
46
+ A: Anything Trollop doesn't recognize as an option or as an option parameter is
47
+ left in ARGV for you to process.
48
+
49
+ Q: Does Trollop support multiple-value arguments?
50
+ A: Yes. If you set the :type of an option to something plural, like ":ints",
51
+ ":strings", ":doubles", ":floats", ":ios", it will accept multiple arguments
52
+ on the commandline and the value will be an array of these.
53
+
54
+ Q: Does Trollop support arguments that can be given multiple times?
55
+ A: Yes. If you set :multi to true, then the argument can appear multiple times
56
+ on the commandline, and the value will be an array of the parameters.
57
+
58
+ Q: Does Trollop support subcommands?
59
+ A: Yes. You get subcommand support by adding a call #stop_on within the options
60
+ block, and passing the names of the subcommands to it. (See the third
61
+ example on the webpage.) When Trollop encounters one of these subcommands on
62
+ the commandline, it stops processing and returns.
63
+
64
+ ARGV at that point will contain the subcommand followed by any subcommand
65
+ options, since Trollop has contained the rest. So you can consume the
66
+ subcommand and call Trollop.options again with the particular options set
67
+ for that subcommand.
68
+
69
+ If you don't know the subcommands ahead of time, you can call
70
+ #stop_on_unknown, which will cause Trollop to stop when it encounters any
71
+ unknown token. This might be more trouble than its worth if you're also
72
+ passing filenames on the commandline.
24
73
 
25
- Q: Why does Trollop disallow numeric short argument names, like '-1'
26
- and '-9'?
27
- A: Because it's ambiguous whether these are arguments or negative
28
- integer or floating-point parameters to arguments. E.g., what
29
- about "-f -3". Is that a negative three parameter to -f, or two
30
- separate parameters?
74
+ It's probably easier to see the example on the webpage than to read all
75
+ that.
31
76
 
32
- I could be very clever and detect when there are no arguments
33
- that require floating-point parameters, and allow such short option
34
- names in those cases, but opted for simplicity and consistency.
77
+ Q: Why does Trollop disallow numeric short argument names, like '-1' and '-9'?
78
+ A: Because it's ambiguous whether these are arguments or negative integer or
79
+ floating-point parameters to arguments. E.g., what about "-f -3". Is that a
80
+ negative three parameter to -f, or two separate parameters?
35
81
 
82
+ I could be very clever and detect when there are no arguments that require
83
+ floating-point parameters, and allow such short option names in those cases,
84
+ but opted for simplicity and consistency.
@@ -1,3 +1,7 @@
1
+ == 1.14 / 2009-06-19
2
+ * Make :multi arguments default to [], not nil, when not set on the commandline.
3
+ * Minor commenting and error message improvements
4
+
1
5
  == 1.13 / 2009-03-16
2
6
  * Fix parsing of "--longarg=<value with spaces>".
3
7
 
@@ -3,9 +3,11 @@
3
3
  ## Copyright:: Copyright 2007 William Morgan
4
4
  ## License:: GNU GPL version 2
5
5
 
6
+ require 'date'
7
+
6
8
  module Trollop
7
9
 
8
- VERSION = "1.13"
10
+ VERSION = "1.14"
9
11
 
10
12
  ## Thrown by Parser in the event of a commandline error. Not needed if
11
13
  ## you're using the Trollop::options entry.
@@ -39,16 +41,17 @@ class Parser
39
41
  ## +:type+ parameter of #opt.
40
42
  FLAG_TYPES = [:flag, :bool, :boolean]
41
43
 
42
- ## The set of values that indicate a single-parameter option when
44
+ ## The set of values that indicate a single-parameter (normal) option when
43
45
  ## passed as the +:type+ parameter of #opt.
44
46
  ##
45
47
  ## A value of +io+ corresponds to a readable IO resource, including
46
48
  ## a filename, URI, or the strings 'stdin' or '-'.
47
- SINGLE_ARG_TYPES = [:int, :integer, :string, :double, :float, :io]
49
+ SINGLE_ARG_TYPES = [:int, :integer, :string, :double, :float, :io, :date]
48
50
 
49
- ## The set of values that indicate a multiple-parameter option when
50
- ## passed as the +:type+ parameter of #opt.
51
- MULTI_ARG_TYPES = [:ints, :integers, :strings, :doubles, :floats, :ios]
51
+ ## The set of values that indicate a multiple-parameter option (i.e., that
52
+ ## takes multiple space-separated values on the commandline) when passed as
53
+ ## the +:type+ parameter of #opt.
54
+ MULTI_ARG_TYPES = [:ints, :integers, :strings, :doubles, :floats, :ios, :dates]
52
55
 
53
56
  ## The complete set of legal values for the +:type+ parameter of #opt.
54
57
  TYPES = FLAG_TYPES + SINGLE_ARG_TYPES + MULTI_ARG_TYPES
@@ -104,6 +107,8 @@ class Parser
104
107
  ##
105
108
  ## Arguments that can occur multiple times should be marked with
106
109
  ## +:multi+ => +true+. The value of this argument will also be an array.
110
+ ## In contrast with regular non-multi options, if not specified on
111
+ ## the commandline, the default value will be [], not nil.
107
112
  ##
108
113
  ## These two attributes can be combined (e.g. +:type+ => +:strings+,
109
114
  ## +:multi+ => +true+), in which case the value of the argument will be
@@ -128,12 +133,13 @@ class Parser
128
133
  when :double; :float
129
134
  when :doubles; :floats
130
135
  when Class
131
- case opts[:type].to_s # sigh... there must be a better way to do this
136
+ case opts[:type].name
132
137
  when 'TrueClass', 'FalseClass'; :flag
133
138
  when 'String'; :string
134
139
  when 'Integer'; :int
135
140
  when 'Float'; :float
136
141
  when 'IO'; :io
142
+ when 'Date'; :date
137
143
  else
138
144
  raise ArgumentError, "unsupported argument type '#{opts[:type].class.name}'"
139
145
  end
@@ -161,6 +167,7 @@ class Parser
161
167
  when TrueClass, FalseClass; :flag
162
168
  when String; :string
163
169
  when IO; :io
170
+ when Date; :date
164
171
  when Array
165
172
  if opts[:default].empty?
166
173
  raise ArgumentError, "multiple argument type cannot be deduced from an empty array for '#{opts[:default][0].class.name}'"
@@ -170,6 +177,7 @@ class Parser
170
177
  when Numeric; :floats
171
178
  when String; :strings
172
179
  when IO; :ios
180
+ when Date; :dates
173
181
  else
174
182
  raise ArgumentError, "unsupported multiple argument type '#{opts[:default][0].class.name}'"
175
183
  end
@@ -178,7 +186,7 @@ class Parser
178
186
  raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
179
187
  end
180
188
 
181
- raise ArgumentError, ":type specification and default type don't match" if opts[:type] && type_from_default && opts[:type] != type_from_default
189
+ raise ArgumentError, ":type specification and default type don't match (default type is #{type_from_default})" if opts[:type] && type_from_default && opts[:type] != type_from_default
182
190
 
183
191
  opts[:type] = opts[:type] || type_from_default || :flag
184
192
 
@@ -280,6 +288,7 @@ class Parser
280
288
  @specs.each do |sym, opts|
281
289
  required[sym] = true if opts[:required]
282
290
  vals[sym] = opts[:default]
291
+ vals[sym] = [] if opts[:multi] && !opts[:default] # multi arguments default to [], not nil
283
292
  end
284
293
 
285
294
  resolve_default_short_options
@@ -364,6 +373,8 @@ class Parser
364
373
  vals[sym] = params.map { |pg| pg.map { |p| p.to_s } }
365
374
  when :io, :ios
366
375
  vals[sym] = params.map { |pg| pg.map { |p| parse_io_parameter p, arg } }
376
+ when :date, :dates
377
+ vals[sym] = params.map { |pg| pg.map { |p| parse_date_parameter p, arg } }
367
378
  end
368
379
 
369
380
  if SINGLE_ARG_TYPES.include?(opts[:type])
@@ -387,6 +398,19 @@ class Parser
387
398
  vals
388
399
  end
389
400
 
401
+ def parse_date_parameter param, arg #:nodoc:
402
+ begin
403
+ begin
404
+ time = Chronic.parse(param)
405
+ rescue NameError
406
+ # chronic is not available
407
+ end
408
+ time ? Date.new(time.year, time.month, time.day) : Date.parse(param)
409
+ rescue ArgumentError => e
410
+ raise CommandlineError, "option '#{arg}' needs a date"
411
+ end
412
+ end
413
+
390
414
  ## Print the help message to +stream+.
391
415
  def educate stream=$stdout
392
416
  width # just calculate it now; otherwise we have to be careful not to
@@ -406,6 +430,8 @@ class Parser
406
430
  when :floats; " <f+>"
407
431
  when :io; " <filename/uri>"
408
432
  when :ios; " <filename/uri+>"
433
+ when :date; " <date>"
434
+ when :dates; " <date+>"
409
435
  end
410
436
  end
411
437
 
@@ -587,7 +613,7 @@ private
587
613
  opts = @specs[name]
588
614
  next if opts[:short]
589
615
 
590
- c = opts[:long].split(//).find { |c| c !~ INVALID_SHORT_ARG_REGEX && !@short.member?(c) }
616
+ c = opts[:long].split(//).find { |d| d !~ INVALID_SHORT_ARG_REGEX && !@short.member?(d) }
591
617
  raise ArgumentError, "can't generate a default short option name for #{opts[:long].inspect}: out of unique characters" unless c
592
618
 
593
619
  opts[:short] = c
@@ -99,6 +99,15 @@ class Trollop < ::Test::Unit::TestCase
99
99
  assert_equal 2, opts["argsf"]
100
100
  assert_raise(CommandlineError) { @p.parse(%w(--argsf hello)) }
101
101
 
102
+ # single arg: date
103
+ date = Date.today
104
+ assert_nothing_raised { @p.opt "argsd", "desc", :default => date }
105
+ assert_nothing_raised { opts = @p.parse("--") }
106
+ assert_equal Date.today, opts["argsd"]
107
+ assert_nothing_raised { opts = @p.parse(['--argsd', 'Jan 4, 2007']) }
108
+ assert_equal Date.civil(2007, 1, 4), opts["argsd"]
109
+ assert_raise(CommandlineError) { @p.parse(%w(--argsd hello)) }
110
+
102
111
  # single arg: string
103
112
  assert_nothing_raised { @p.opt "argss", "desc", :default => "foobar" }
104
113
  assert_nothing_raised { opts = @p.parse("--") }
@@ -127,14 +136,23 @@ class Trollop < ::Test::Unit::TestCase
127
136
  assert_equal [4.0], opts["argmf"]
128
137
  assert_raise(CommandlineError) { @p.parse(%w(--argmf hello)) }
129
138
 
139
+ # multi args: dates
140
+ dates = [Date.today, Date.civil(2007, 1, 4)]
141
+ assert_nothing_raised { @p.opt "argmd", "desc", :default => dates }
142
+ assert_nothing_raised { opts = @p.parse("--") }
143
+ assert_equal dates, opts["argmd"]
144
+ assert_nothing_raised { opts = @p.parse(['--argmd', 'Jan 4, 2007']) }
145
+ assert_equal [Date.civil(2007, 1, 4)], opts["argmd"]
146
+ assert_raise(CommandlineError) { @p.parse(%w(--argmd hello)) }
147
+
130
148
  # multi args: strings
131
- assert_nothing_raised { @p.opt "argms", "desc", :default => %w(hello world) }
149
+ assert_nothing_raised { @p.opt "argmst", "desc", :default => %w(hello world) }
132
150
  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"]
151
+ assert_equal %w(hello world), opts["argmst"]
152
+ assert_nothing_raised { opts = @p.parse(%w(--argmst 3.4)) }
153
+ assert_equal ["3.4"], opts["argmst"]
154
+ assert_nothing_raised { opts = @p.parse(%w(--argmst goodbye)) }
155
+ assert_equal ["goodbye"], opts["argmst"]
138
156
  end
139
157
 
140
158
  ## :type and :default must match if both are specified
@@ -146,10 +164,12 @@ class Trollop < ::Test::Unit::TestCase
146
164
 
147
165
  assert_nothing_raised { @p.opt "argsi", "desc", :type => :int, :default => 4 }
148
166
  assert_nothing_raised { @p.opt "argsf", "desc", :type => :float, :default => 3.14 }
167
+ assert_nothing_raised { @p.opt "argsd", "desc", :type => :date, :default => Date.today }
149
168
  assert_nothing_raised { @p.opt "argss", "desc", :type => :string, :default => "yo" }
150
169
  assert_nothing_raised { @p.opt "argmi", "desc", :type => :ints, :default => [4] }
151
170
  assert_nothing_raised { @p.opt "argmf", "desc", :type => :floats, :default => [3.14] }
152
- assert_nothing_raised { @p.opt "argms", "desc", :type => :strings, :default => ["yo"] }
171
+ assert_nothing_raised { @p.opt "argmd", "desc", :type => :dates, :default => [Date.today] }
172
+ assert_nothing_raised { @p.opt "argmst", "desc", :type => :strings, :default => ["yo"] }
153
173
  end
154
174
 
155
175
  def test_long_detects_bad_names
@@ -352,6 +372,20 @@ EOM
352
372
  assert_raises(CommandlineError) { @p.parse %w(-f -.) }
353
373
  end
354
374
 
375
+ def test_date_formatting
376
+ @p.opt :arg, "desc", :type => :date, :short => 'd'
377
+ opts = nil
378
+ assert_nothing_raised { opts = @p.parse(['-d', 'Jan 4, 2007']) }
379
+ assert_equal Date.civil(2007, 1, 4), opts[:arg]
380
+ begin
381
+ require 'chronic'
382
+ assert_nothing_raised { opts = @p.parse(['-d', 'today']) }
383
+ assert_equal Date.today, opts[:arg]
384
+ rescue LoadError
385
+ # chronic is not available
386
+ end
387
+ end
388
+
355
389
  def test_short_options_cant_be_numeric
356
390
  assert_raises(ArgumentError) { @p.opt :arg, "desc", :short => "-1" }
357
391
  @p.opt :a1b, "desc"
@@ -1002,6 +1036,12 @@ EOM
1002
1036
  assert_equal "hello there", opts[:arg2]
1003
1037
  assert_equal 0, @p.leftovers.size
1004
1038
  end
1039
+
1040
+ def test_multi_args_default_to_empty_array
1041
+ @p.opt :arg1, "arg", :multi => true
1042
+ opts = @p.parse ""
1043
+ assert_equal [], opts[:arg1]
1044
+ end
1005
1045
  end
1006
1046
 
1007
1047
  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.13"
4
+ version: "1.14"
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Morgan
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-16 00:00:00 -07:00
12
+ date: 2009-06-19 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -55,7 +55,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
55
55
  requirements: []
56
56
 
57
57
  rubyforge_project: trollop
58
- rubygems_version: 1.2.0
58
+ rubygems_version: 1.3.1
59
59
  signing_key:
60
60
  specification_version: 2
61
61
  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.