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.
- data/FAQ.txt +66 -17
- data/History.txt +4 -0
- data/lib/trollop.rb +35 -9
- data/test/test_trollop.rb +47 -7
- 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
|
-
|
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
|
-
|
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
|
-
|
23
|
-
|
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
|
-
|
26
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
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.
|
data/History.txt
CHANGED
data/lib/trollop.rb
CHANGED
@@ -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.
|
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
|
50
|
-
##
|
51
|
-
|
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].
|
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 { |
|
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
|
data/test/test_trollop.rb
CHANGED
@@ -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 "
|
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["
|
134
|
-
assert_nothing_raised { opts = @p.parse(%w(--
|
135
|
-
assert_equal ["3.4"], opts["
|
136
|
-
assert_nothing_raised { opts = @p.parse(%w(--
|
137
|
-
assert_equal ["goodbye"], opts["
|
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 "
|
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.
|
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-
|
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.
|
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.
|