trollop 1.13 → 1.14

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