trollop 1.0

Sign up to get free protection for your applications and to get access to all the features.
data/FAQ.txt ADDED
@@ -0,0 +1,31 @@
1
+ Trollop FAQ
2
+ -----------
3
+
4
+ Q: Whither Trollop?
5
+ A: There are more than a few pre-existing commandline argument
6
+ processing libraries for Ruby. I was never too happy with any of
7
+ them. They all seemed to want me to type way too much for what I
8
+ wanted to get done. I just wanted to parse some options and be
9
+ done with it.
10
+
11
+ Q: Whither "Trollop"?
12
+ A: No reason. Just thought it sounded funny.
13
+
14
+ Q: Why does Trollop disallow numeric short argument names, like '-1'
15
+ and '-9'?
16
+ A: Because it's ambiguous whether these are arguments or negative
17
+ integer or floating-point parameters to arguments. E.g., what
18
+ about "-f -3". Is that a negative three parameter to -f, or two
19
+ separate parameters?
20
+
21
+ I could be very clever and detect when there are no arguments
22
+ that require floating-point parameters, and allow such short option
23
+ names in those cases, but I'd rather be simple and consistent.
24
+
25
+ Q: Why does Trollop disallow options appearing multiple times, despite
26
+ the POSIX standard allowing it?
27
+ A: Because basically I think it's confusing, and more often than
28
+ not, a symptom of you making a mistake (e.g. getting lost in a long
29
+ command line and accidentally setting the same thing twice.) Given
30
+ that, I really don't see that much advantage to "-vvvvv" over "-v
31
+ 5", so Trollop will produce an error if it happens.
@@ -0,0 +1,2 @@
1
+ == 1.0 / 2007-01-29
2
+ * Initial release.
@@ -0,0 +1,7 @@
1
+ FAQ.txt
2
+ History.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ lib/trollop.rb
7
+ test/test_trollop.rb
@@ -0,0 +1,73 @@
1
+ == trollop
2
+
3
+ by William Morgan <wmorgan-trollop@masanjin.net>
4
+
5
+ http://trollop.rubyforge.org
6
+
7
+ == DESCRIPTION:
8
+
9
+ Trollop is YAFCLAP --- yet another fine commandline argument
10
+ processing library for Ruby. Trollop is designed to provide the
11
+ maximal amount of GNU-style argument processing in the minimum number
12
+ of lines of code (for you, the programmer).
13
+
14
+ Trollop provides a nice automatically-generated help page, robust
15
+ option parsing, and sensible defaults for everything you don't
16
+ specify.
17
+
18
+ Trollop: getting you 90% of the way there with only 10% of the effort.
19
+
20
+ == FEATURES/PROBLEMS:
21
+
22
+ - Simple usage.
23
+ - Sensible defaults. No tweaking necessary, much tweaking possible.
24
+ - Support for long options, short options, short option bundling,
25
+ and automatic type validation and conversion.
26
+ - Automatic help message generation, wrapped to current screen width.
27
+ - Lots of unit tests.
28
+
29
+ == SYNOPSIS:
30
+
31
+ ###### simple ######
32
+
33
+ opts = Trollop::options do
34
+ opt :monkey, "Use monkey mode."
35
+ opt :goat, "Use goat model", :default => true
36
+ opt :num_limbs, "Set number of limbs", :default => 4
37
+ end
38
+
39
+ p opts
40
+
41
+ ###### complex ######
42
+
43
+ opts = Trollop::options do
44
+ version "test 1.2.3 (c) 2007 William Morgan"
45
+ banner <<-EOS
46
+ Test is an awesome program that does something very, very important.
47
+
48
+ Usage:
49
+ test [options] <filenames>+
50
+ where [options] are:
51
+ EOS
52
+
53
+ opt :ignore, "Ignore incorrect values"
54
+ opt :file, "Extra data filename to read in, with a very long option description like this one", :type => String
55
+ opt :volume, "Volume level", :default => 3.0
56
+ opt :iters, "Number of iterations", :default => 5
57
+ end
58
+ Trollop::die :volume, "must be non-negative" if opts[:volume] < 0
59
+ Trollop::die :file, "must exist" unless File.exists?(opts[:file]) if opts[:file]
60
+
61
+ == REQUIREMENTS:
62
+
63
+ * none
64
+
65
+ == INSTALL:
66
+
67
+ * gem install trollop
68
+
69
+ == LICENSE:
70
+
71
+ Copyright (c) 2007 William Morgan.
72
+
73
+ Trollop is distributed under the same terms as Ruby.
@@ -0,0 +1,25 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require 'trollop'
6
+
7
+ Hoe.new('trollop', Trollop::VERSION) do |p|
8
+ p.rubyforge_name = 'trollop'
9
+ p.author = "William Morgan"
10
+ 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)."
11
+
12
+ p.description = p.paragraphs_of('README.txt', 4..5, 9..18).join("\n\n").gsub(/== SYNOPSIS/, "Synopsis")
13
+ p.url = "http://trollop.rubyforge.org"
14
+ p.changes = p.paragraphs_of('History.txt', 0..0).join("\n\n")
15
+ p.email = "wmorgan-trollop@masanjin.net"
16
+ end
17
+
18
+ ## is there really no way to make a rule for this?
19
+ WWW_FILES = %w(www/index.html README.txt FAQ.txt)
20
+
21
+ task :upload_webpage => WWW_FILES do |t|
22
+ sh "scp -C #{t.prerequisites * ' '} wmorgan@rubyforge.org:/var/www/gforge-projects/trollop/"
23
+ end
24
+
25
+ # vim: syntax=Ruby
@@ -0,0 +1,392 @@
1
+ ## lib/trollop.rb -- trollop command-line processing library
2
+ ## Author:: William Morgan (mailto: wmorgan-trollop@masanjin.net)
3
+ ## Copyright:: Copyright 2007 William Morgan
4
+ ## License:: GNU GPL version 2
5
+
6
+ module Trollop
7
+
8
+ VERSION = "1.0"
9
+
10
+ ## Thrown by Parser in the event of a commandline error. Not needed if
11
+ ## you're using the Trollop::options entry.
12
+ class CommandlineError < StandardError; end
13
+
14
+ ## Thrown by Parser if the user passes in '-h' or '--help'. Handled
15
+ ## automatically by Trollop#options.
16
+ class HelpNeeded < StandardError; end
17
+
18
+ ## Thrown by Parser if the user passes in '-h' or '--version'. Handled
19
+ ## automatically by Trollop#options.
20
+ class VersionNeeded < StandardError; end
21
+
22
+ ## Regex for floating point numbers
23
+ FLOAT_RE = /^-?((\d+(\.\d+)?)|(\.\d+))$/
24
+
25
+ ## Regex for parameters
26
+ PARAM_RE = /^-(-|\.$|[^\d\.])/
27
+
28
+ ## The commandline parser. In typical usage, the methods in this class
29
+ ## will be handled internally by Trollop#options, in which case only
30
+ ## the methods #opt, #banner and #version will be called.
31
+ class Parser
32
+ ## The set of values specifiable as the :type parameter to #opt.
33
+ TYPES = [:flag, :boolean, :bool, :int, :integer, :string, :double, :float]
34
+
35
+ ## The values from the commandline that were not interpreted by #parse.
36
+ attr_reader :leftovers
37
+
38
+ ## The complete configuration hashes for each option. (Mainly useful
39
+ ## for testing.)
40
+ attr_reader :specs
41
+
42
+ ## Initializes the parser, and instance-evaluates any block given.
43
+ def initialize &b
44
+ @version = nil
45
+ @banner = nil
46
+ @leftovers = []
47
+ @specs = {}
48
+ @long = {}
49
+ @short = {}
50
+
51
+ opt :help, "Show this message"
52
+ instance_eval(&b) if b
53
+ end
54
+
55
+ ## Add an option. 'name' is the argument name, a unique identifier
56
+ ## for the option that you will use internally. 'desc' a string
57
+ ## description which will be displayed in help messages. Takes the
58
+ ## following optional arguments:
59
+ ##
60
+ ## * :long: Specify the long form of the argument, i.e. the form
61
+ ## with two dashes. If unspecified, will be automatically derived
62
+ ## based on the argument name.
63
+ ## * :short: Specify the short form of the argument, i.e. the form
64
+ ## with one dash. If unspecified, will be automatically derived
65
+ ## based on the argument name.
66
+ ## * :type: Require that the argument take a parameter of type
67
+ ## 'type'. Can by any member of the TYPES constant or a
68
+ ## corresponding class (e.g. Integer for :int). If unset, the
69
+ ## default argument type is :flag, meaning the argument does not
70
+ ## take a parameter. Not necessary if :default: is specified.
71
+ ## * :default: Set the default value for an argument. Without a
72
+ ## default value, the hash returned by #parse (and thus
73
+ ## Trollop#options) will not contain the argument unless it is
74
+ ## given on the commandline. The argument type is derived
75
+ ## automatically from the class of the default value given, if
76
+ ## any. Specifying a :flag argument on the commandline whose
77
+ ## default value is true will change its value to false.
78
+ ## * :required: if set to true, the argument must be provided on the
79
+ ## commandline.
80
+ def opt name, desc="", opts={}
81
+ raise ArgumentError, "you already have an argument named #{name.inspect}" if @specs.member? name
82
+
83
+ ## fill in :type
84
+ opts[:type] =
85
+ case opts[:type]
86
+ when :flag, :boolean, :bool: :flag
87
+ when :int, :integer: :int
88
+ when :string: :string
89
+ when :double, :float: :float
90
+ when Class
91
+ case opts[:type].to_s # sigh... there must be a better way to do this
92
+ when 'TrueClass', 'FalseClass': :flag
93
+ when 'String': :string
94
+ when 'Integer': :int
95
+ when 'Float': :float
96
+ else
97
+ raise ArgumentError, "unsupported argument type '#{opts[:type].class.name}'"
98
+ end
99
+ when nil: nil
100
+ else
101
+ raise ArgumentError, "unsupported argument type '#{opts[:type]}'" unless TYPES.include?(opts[:type])
102
+ end
103
+
104
+ type_from_default =
105
+ case opts[:default]
106
+ when Integer: :int
107
+ when Numeric: :float
108
+ when TrueClass, FalseClass: :flag
109
+ when String: :string
110
+ when nil: nil
111
+ else
112
+ raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
113
+ end
114
+
115
+ raise ArgumentError, ":type specification and default type don't match" if opts[:type] && type_from_default && opts[:type] != type_from_default
116
+
117
+ opts[:type] = (opts[:type] || type_from_default || :flag)
118
+
119
+ ## fill in :long
120
+ opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.gsub("_", "-")
121
+ opts[:long] =
122
+ case opts[:long]
123
+ when /^--([^-].*)$/
124
+ $1
125
+ when /^[^-]/
126
+ opts[:long]
127
+ else
128
+ raise ArgumentError, "invalid long option name #{opts[:long].inspect}"
129
+ end
130
+ raise ArgumentError, "long option name #{opts[:long].inspect} is already taken; please specify a (different) :long" if @long[opts[:long]]
131
+
132
+ ## fill in :short
133
+ opts[:short] = opts[:short].to_s if opts[:short]
134
+ opts[:short] =
135
+ case opts[:short]
136
+ when nil
137
+ opts[:long].split(//).find { |c| c !~ /[\d]/ && !@short.member?(c) }
138
+ when /^-(.)$/
139
+ $1
140
+ when /^.$/
141
+ opts[:short]
142
+ else
143
+ raise ArgumentError, "invalid short option name #{opts[:long].inspect}"
144
+ end
145
+ raise ArgumentError, "can't generate a short option name (out of characters)" unless opts[:short]
146
+ raise ArgumentError, "short option name #{opts[:short].inspect} is already taken; please specify a (different) :short" if @short[opts[:short]]
147
+ raise ArgumentError, "a short option name can't be a number or a dash" if opts[:short] =~ /[\d-]/
148
+
149
+ ## fill in :default for flags
150
+ opts[:default] = false if opts[:type] == :flag && opts[:default].nil?
151
+
152
+ opts[:desc] ||= desc
153
+ @short[opts[:short]] = @long[opts[:long]] = name
154
+ @specs[name] = opts
155
+ end
156
+
157
+ ## Sets the version string. If set, the user can request the version
158
+ ## on the commandline. Should be of the form "<program name>
159
+ ## <version number>".
160
+ def version s=nil;
161
+ if s
162
+ @version = s
163
+ opt :version, "Print version and exit"
164
+ end
165
+ @version
166
+ end
167
+
168
+ ## Sets the banner. If set, this will be printed at the top of the
169
+ ## help display.
170
+ def banner s=nil; @banner = s if s; @banner end
171
+
172
+ ## yield successive arg, parameter pairs
173
+ def each_arg args # :nodoc:
174
+ remains = []
175
+ i = 0
176
+
177
+ until i >= args.length
178
+ case args[i]
179
+ when /^--$/ # arg terminator
180
+ remains += args[(i + 1) .. -1]
181
+ break
182
+ when /^--(\S+?)=(\S+)$/ # long argument with equals
183
+ yield "--#{$1}", $2
184
+ i += 1
185
+ when /^--(\S+)$/ # long argument
186
+ if args[i + 1] && args[i + 1] !~ PARAM_RE
187
+ remains << args[i + 1] unless yield args[i], args[i + 1]
188
+ i += 2
189
+ else # long argument no parameter
190
+ yield args[i], nil
191
+ i += 1
192
+ end
193
+ when /^-(\S+)$/ # one or more short arguments
194
+ shortargs = $1.split(//)
195
+ shortargs.each_with_index do |a, j|
196
+ if j == (shortargs.length - 1) && args[i + 1] && args[i + 1] !~ PARAM_RE
197
+ remains << args[i + 1] unless yield "-#{a}", args[i + 1]
198
+ i += 1 # once more below
199
+ else
200
+ yield "-#{a}", nil
201
+ end
202
+ end
203
+ i += 1
204
+ else
205
+ remains << args[i]
206
+ i += 1
207
+ end
208
+ end
209
+ remains
210
+ end
211
+
212
+ def parse args #:nodoc:
213
+ vals = {}
214
+ required = {}
215
+ found = {}
216
+
217
+ @specs.each do |name, opts|
218
+ required[name] = true if opts[:required]
219
+ vals[name] = opts[:default]
220
+ end
221
+
222
+ @leftovers = each_arg args do |arg, param|
223
+ raise VersionNeeded if @version && %w(-v --version).include?(arg)
224
+ raise HelpNeeded if %w(-h --help).include?(arg)
225
+
226
+ name =
227
+ case arg
228
+ when /^-([^-])$/
229
+ @short[$1]
230
+ when /^--([^-]\S*)$/
231
+ @long[$1]
232
+ else
233
+ raise CommandlineError, "invalid argument syntax: '#{arg}'"
234
+ end
235
+ raise CommandlineError, "unknown argument '#{arg}'" unless name
236
+ raise CommandlineError, "option '#{arg}' specified multiple times" if found[name]
237
+
238
+ found[name] = true
239
+ opts = @specs[name]
240
+
241
+ case opts[:type]
242
+ when :flag
243
+ vals[name] = !opts[:default]
244
+ false
245
+ when :int
246
+ raise CommandlineError, "option '#{arg}' needs a parameter" unless param
247
+ raise CommandlineError, "option '#{arg}' needs an integer" unless param =~ /^\d+$/
248
+ vals[name] = param.to_i
249
+ true
250
+ when :float
251
+ raise CommandlineError, "option '#{arg}' needs a parameter" unless param
252
+ raise CommandlineError, "option '#{arg}' needs a floating-point number" unless param =~ FLOAT_RE
253
+ vals[name] = param.to_f
254
+ true
255
+ when :string
256
+ raise CommandlineError, "option '#{arg}' needs a parameter" unless param
257
+ vals[name] = param
258
+ true
259
+ end
260
+ end
261
+
262
+ raise CommandlineError, "option '#{required.keys.first}' must be specified" if required.any? { |name, x| !found[name] }
263
+ vals
264
+ end
265
+
266
+ def width #:nodoc:
267
+ @width ||=
268
+ begin
269
+ require 'curses'
270
+ Curses::init_screen
271
+ x = Curses::cols
272
+ Curses::close_screen
273
+ x
274
+ rescue Exception
275
+ 80
276
+ end
277
+ end
278
+
279
+ ## Print the help message to 'stream'.
280
+ def educate stream=$stdout
281
+ width # just calculate it now; otherwise we have to be careful not to
282
+ # call this unless the cursor's at the beginning of a line.
283
+ if @banner
284
+ stream.puts wrap(@banner)
285
+ elsif @version
286
+ stream.puts
287
+ stream.puts @version
288
+ end
289
+
290
+ unless @banner
291
+ stream.puts "Options: "
292
+ end
293
+
294
+ specs = @long.keys.sort.map { |longname| @specs[@long[longname]] }
295
+ leftcols = specs.map { |spec| "--#{spec[:long]}, -#{spec[:short]}" }
296
+ leftcol_width = leftcols.map { |s| s.length }.max
297
+ rightcol_start = leftcol_width + 6 # spaces
298
+ specs.each_with_index do |spec, i|
299
+ stream.printf(" %#{leftcol_width}s: ", leftcols[i]);
300
+ desc = spec[:desc] +
301
+ if spec[:default]
302
+ if spec[:desc] =~ /\.$/
303
+ " (Default: #{spec[:default]})"
304
+ else
305
+ " (default: #{spec[:default]})"
306
+ end
307
+ else
308
+ ""
309
+ end
310
+ stream.puts wrap(desc, :width => width - rightcol_start, :prefix => rightcol_start)
311
+ end
312
+ end
313
+
314
+ def wrap_line str, opts={} # :nodoc:
315
+ prefix = opts[:prefix] || 0
316
+ width = opts[:width] || self.width
317
+ start = 0
318
+ ret = []
319
+ until start > str.length
320
+ nextt =
321
+ if start + width >= str.length
322
+ str.length
323
+ else
324
+ x = str.rindex(/\s/, start + width)
325
+ x = str.index(/\s/, start) if x && x < start
326
+ x || str.length
327
+ end
328
+ ret << (ret.empty? ? "" : " " * prefix) + str[start ... nextt]
329
+ start = nextt + 1
330
+ end
331
+ ret
332
+ end
333
+
334
+ def wrap str, opts={} # :nodoc:
335
+ if str == ""
336
+ [""]
337
+ else
338
+ str.split("\n").map { |s| wrap_line s, opts }.flatten
339
+ end
340
+ end
341
+ end
342
+
343
+ ## The top-level entry method into Trollop. Creates a Parser object,
344
+ ## passes the block to it, then parses ARGV with it, handling any
345
+ ## errors or requests for help or version information appropriately
346
+ ## (and then exiting). Modifies ARGV in place. Returns a hash of
347
+ ## option values.
348
+ ##
349
+ ## The block passed in should contain one or more calls to #opt
350
+ ## (Parser#opt), and optionally a call to banner (Parser#banner)
351
+ ## and a call to version (Parser#version).
352
+ ##
353
+ ## See the synopsis in README.txt for examples.
354
+ def options &b
355
+ @p = Parser.new(&b)
356
+ begin
357
+ vals = @p.parse ARGV
358
+ ARGV.clear
359
+ @p.leftovers.each { |l| ARGV << l }
360
+ vals
361
+ rescue CommandlineError => e
362
+ $stderr.puts "Error: #{e.message}."
363
+ $stderr.puts "Try --help for help."
364
+ exit(-1)
365
+ rescue HelpNeeded
366
+ @p.educate
367
+ exit
368
+ rescue VersionNeeded
369
+ puts @p.version
370
+ exit
371
+ end
372
+ end
373
+
374
+ ## Informs the user that their usage of 'arg' was wrong, as detailed by
375
+ ## 'msg', and dies. Example:
376
+ ##
377
+ ## options do
378
+ ## opt :volume, :default => 0.0
379
+ ## end
380
+ ##
381
+ ## die :volume, "too loud" if opts[:volume] > 10.0
382
+ ## die :volume, "too soft" if opts[:volume] < 0.1
383
+
384
+ def die arg, msg
385
+ $stderr.puts "Error: parameter for option '--#{@p.specs[arg][:long]}' or '-#{@p.specs[arg][:short]}' #{msg}."
386
+ $stderr.puts "Try --help for help."
387
+ exit(-1)
388
+ end
389
+
390
+ module_function :options, :die
391
+
392
+ end # module
@@ -0,0 +1,300 @@
1
+ ## test/test_trollop.rb -- unit tests for trollop
2
+ ## Author:: William Morgan (mailto: wmorgan-trollop@masanjin.net)
3
+ ## Copyright:: Copyright 2007 William Morgan
4
+ ## License:: GNU GPL version 2
5
+
6
+ require 'test/unit'
7
+ require 'yaml'
8
+ require 'trollop'
9
+
10
+ module Trollop
11
+ module Test
12
+
13
+ class Trollop < ::Test::Unit::TestCase
14
+ def setup
15
+ @p = Parser.new
16
+ end
17
+
18
+ def test_unknown_arguments
19
+ assert_raise(CommandlineError) { @p.parse(%w(--arg)) }
20
+ @p.opt "arg", "desc"
21
+ assert_nothing_raised { @p.parse(%w(--arg)) }
22
+ assert_raise(CommandlineError) { @p.parse(%w(--arg2)) }
23
+ end
24
+
25
+ def test_syntax_check
26
+ @p.opt "arg", "desc"
27
+
28
+ assert_nothing_raised { @p.parse(%w(--arg)) }
29
+ assert_nothing_raised { @p.parse(%w(arg)) }
30
+ assert_raise(CommandlineError) { @p.parse(%w(---arg)) }
31
+ assert_raise(CommandlineError) { @p.parse(%w(-arg)) }
32
+ end
33
+
34
+ def test_required_flags_are_required
35
+ @p.opt "arg", "desc", :required => true
36
+ @p.opt "arg2", "desc", :required => false
37
+ @p.opt "arg3", "desc", :required => false
38
+
39
+ assert_nothing_raised { @p.parse(%w(--arg)) }
40
+ assert_nothing_raised { @p.parse(%w(--arg --arg2)) }
41
+ assert_raise(CommandlineError) { @p.parse(%w(--arg2)) }
42
+ assert_raise(CommandlineError) { @p.parse(%w(--arg2 --arg3)) }
43
+ end
44
+
45
+ ## flags that take an argument error unless given one
46
+ def test_argflags_demand_args
47
+ @p.opt "goodarg", "desc", :type => String
48
+ @p.opt "goodarg2", "desc", :type => String
49
+
50
+ assert_nothing_raised { @p.parse(%w(--goodarg goat)) }
51
+ assert_raise(CommandlineError) { @p.parse(%w(--goodarg --goodarg2 goat)) }
52
+ assert_raise(CommandlineError) { @p.parse(%w(--goodarg)) }
53
+ end
54
+
55
+ ## flags that don't take arguments ignore them
56
+ def test_arglessflags_refuse_args
57
+ @p.opt "goodarg", "desc"
58
+ @p.opt "goodarg2", "desc"
59
+ assert_nothing_raised { @p.parse(%w(--goodarg)) }
60
+ assert_nothing_raised { @p.parse(%w(--goodarg --goodarg2)) }
61
+ opts = @p.parse %w(--goodarg a)
62
+ assert_equal true, opts["goodarg"]
63
+ assert_equal ["a"], @p.leftovers
64
+ end
65
+
66
+ ## flags that require args of a specific type refuse args of other
67
+ ## types
68
+ def test_typed_args_refuse_args_of_other_types
69
+ assert_nothing_raised { @p.opt "goodarg", "desc", :type => :int }
70
+ assert_raise(ArgumentError) { @p.opt "badarg", "desc", :type => :asdf }
71
+
72
+ assert_nothing_raised { @p.parse(%w(--goodarg 3)) }
73
+ assert_raise(CommandlineError) { @p.parse(%w(--goodarg 4.2)) }
74
+ assert_raise(CommandlineError) { @p.parse(%w(--goodarg hello)) }
75
+ end
76
+
77
+ ## type is correctly derived from :default
78
+ def test_type_correctly_derived_from_default
79
+ assert_nothing_raised { @p.opt "goodarg", "desc", :default => 0 }
80
+ assert_raise(ArgumentError) { @p.opt "badarg", "desc", :default => [] }
81
+
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)) }
85
+ end
86
+
87
+ ## :type and :default must match if both are specified
88
+ 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
+ assert_raise(ArgumentError) { @p.opt "badarg", "desc", :type => :int, :default => "hello" }
92
+ assert_raise(ArgumentError) { @p.opt "badarg2", "desc", :type => :String, :default => 4 }
93
+ end
94
+
95
+ def test_long_detects_bad_names
96
+ assert_nothing_raised { @p.opt "goodarg", "desc", :long => "none" }
97
+ assert_nothing_raised { @p.opt "goodarg2", "desc", :long => "--two" }
98
+ assert_raise(ArgumentError) { @p.opt "badarg", "desc", :long => "" }
99
+ assert_raise(ArgumentError) { @p.opt "badarg2", "desc", :long => "--" }
100
+ assert_raise(ArgumentError) { @p.opt "badarg3", "desc", :long => "-one" }
101
+ assert_raise(ArgumentError) { @p.opt "badarg4", "desc", :long => "---toomany" }
102
+ end
103
+
104
+ def test_short_detects_bad_names
105
+ assert_nothing_raised { @p.opt "goodarg", "desc", :short => "a" }
106
+ assert_nothing_raised { @p.opt "goodarg2", "desc", :short => "-b" }
107
+ assert_raise(ArgumentError) { @p.opt "badarg", "desc", :short => "" }
108
+ assert_raise(ArgumentError) { @p.opt "badarg2", "desc", :short => "-ab" }
109
+ assert_raise(ArgumentError) { @p.opt "badarg3", "desc", :short => "--t" }
110
+ end
111
+
112
+ def test_short_names_determined_automatically
113
+ @p.opt "arg"
114
+ @p.opt "arg2"
115
+ @p.opt "arg3"
116
+ assert_raise(ArgumentError) { @p.opt "gra" }
117
+ opts = @p.parse %w(-a -g)
118
+ assert_equal true, opts["arg"]
119
+ assert_equal false, opts["arg2"]
120
+ assert_equal true, opts["arg3"]
121
+ end
122
+
123
+ ## two args can't have the same name
124
+ def test_conflicting_names_are_detected
125
+ assert_nothing_raised { @p.opt "goodarg", "desc" }
126
+ assert_raise(ArgumentError) { @p.opt "goodarg", "desc" }
127
+ end
128
+
129
+ ## two args can't have the same :long
130
+ def test_conflicting_longs_detected
131
+ assert_nothing_raised { @p.opt "goodarg", "desc", :long => "--goodarg" }
132
+ assert_raise(ArgumentError) { @p.opt "badarg", "desc", :long => "--goodarg" }
133
+ end
134
+
135
+ ## two args can't have the same :short
136
+ def test_conflicting_shorts_detected
137
+ assert_nothing_raised { @p.opt "goodarg", "desc", :short => "-g" }
138
+ assert_raise(ArgumentError) { @p.opt "badarg", "desc", :short => "-g" }
139
+ end
140
+
141
+ def test_flag_defaults
142
+ @p.opt "defaultfalse", "desc"
143
+ @p.opt "defaulttrue", "desc", :default => true
144
+ opts = @p.parse []
145
+ assert_equal false, opts["defaultfalse"]
146
+ assert_equal true, opts["defaulttrue"]
147
+
148
+ opts = @p.parse %w(--defaultfalse --defaulttrue)
149
+ assert_equal true, opts["defaultfalse"]
150
+ assert_equal false, opts["defaulttrue"]
151
+ end
152
+
153
+ def test_special_flags_work
154
+ @p.version "asdf fdas"
155
+ assert_raise(VersionNeeded) { @p.parse(%w(-v)) }
156
+ assert_raise(HelpNeeded) { @p.parse(%w(-h)) }
157
+ end
158
+
159
+ def test_short_options_combine
160
+ @p.opt :arg1, "desc", :short => "a"
161
+ @p.opt :arg2, "desc", :short => "b"
162
+ @p.opt :arg3, "desc", :short => "c", :type => :int
163
+
164
+ opts = nil
165
+ assert_nothing_raised { opts = @p.parse %w(-a -b) }
166
+ assert_equal true, opts[:arg1]
167
+ assert_equal true, opts[:arg2]
168
+ assert_equal nil, opts[:arg3]
169
+
170
+ assert_nothing_raised { opts = @p.parse %w(-ab) }
171
+ assert_equal true, opts[:arg1]
172
+ assert_equal true, opts[:arg2]
173
+ assert_equal nil, opts[:arg3]
174
+
175
+ assert_nothing_raised { opts = @p.parse %w(-ac 4 -b) }
176
+ assert_equal true, opts[:arg1]
177
+ assert_equal true, opts[:arg2]
178
+ assert_equal 4, opts[:arg3]
179
+
180
+ assert_raises(CommandlineError) { @p.parse %w(-cab 4) }
181
+ assert_raises(CommandlineError) { @p.parse %w(-cba 4) }
182
+ end
183
+
184
+ def test_version_only_appears_if_set
185
+ @p.opt "arg", "desc"
186
+ assert_raise(CommandlineError) { @p.parse %w(-v) }
187
+ @p.version "trollop 1.2.3.4"
188
+ assert_raise(VersionNeeded) { @p.parse %w(-v) }
189
+ end
190
+
191
+ def test_doubledash_ends_option_processing
192
+ @p.opt :arg1, "desc", :short => "a", :default => 0
193
+ @p.opt :arg2, "desc", :short => "b", :default => 0
194
+ opts = nil
195
+ assert_nothing_raised { opts = @p.parse %w(-- -a 3 -b 2) }
196
+ assert_equal opts[:arg1], 0
197
+ assert_equal opts[:arg2], 0
198
+ assert_equal %w(-a 3 -b 2), @p.leftovers
199
+ assert_nothing_raised { opts = @p.parse %w(-a 3 -- -b 2) }
200
+ assert_equal opts[:arg1], 3
201
+ assert_equal opts[:arg2], 0
202
+ assert_equal %w(-b 2), @p.leftovers
203
+ assert_nothing_raised { opts = @p.parse %w(-a 3 -b 2 --) }
204
+ assert_equal opts[:arg1], 3
205
+ assert_equal opts[:arg2], 2
206
+ assert_equal %w(), @p.leftovers
207
+ end
208
+
209
+ def test_wrap
210
+ assert_equal [""], @p.wrap("")
211
+ assert_equal ["a"], @p.wrap("a")
212
+ assert_equal ["one two", "three"], @p.wrap("one two three", :width => 8)
213
+ assert_equal ["one two three"], @p.wrap("one two three", :width => 80)
214
+ assert_equal ["one", "two", "three"], @p.wrap("one two three", :width => 3)
215
+ assert_equal ["onetwothree"], @p.wrap("onetwothree", :width => 3)
216
+ assert_equal [
217
+ "Test is an awesome program that does something very, very important.",
218
+ "",
219
+ "Usage:",
220
+ " test [options] <filenames>+",
221
+ "where [options] are:"], @p.wrap(<<EOM, :width => 100)
222
+ Test is an awesome program that does something very, very important.
223
+
224
+ Usage:
225
+ test [options] <filenames>+
226
+ where [options] are:
227
+ EOM
228
+ end
229
+
230
+ def test_floating_point_formatting
231
+ @p.opt :arg, "desc", :type => :float, :short => "f"
232
+ opts = nil
233
+ assert_nothing_raised { opts = @p.parse %w(-f 1) }
234
+ assert_equal 1.0, opts[:arg]
235
+ assert_nothing_raised { opts = @p.parse %w(-f 1.0) }
236
+ assert_equal 1.0, opts[:arg]
237
+ assert_nothing_raised { opts = @p.parse %w(-f 0.1) }
238
+ assert_equal 0.1, opts[:arg]
239
+ assert_nothing_raised { opts = @p.parse %w(-f .1) }
240
+ assert_equal 0.1, opts[:arg]
241
+ assert_nothing_raised { opts = @p.parse %w(-f .99999999999999999999) }
242
+ assert_equal 1.0, opts[:arg]
243
+ assert_nothing_raised { opts = @p.parse %w(-f -1) }
244
+ assert_equal(-1.0, opts[:arg])
245
+ assert_nothing_raised { opts = @p.parse %w(-f -1.0) }
246
+ assert_equal(-1.0, opts[:arg])
247
+ assert_nothing_raised { opts = @p.parse %w(-f -0.1) }
248
+ assert_equal(-0.1, opts[:arg])
249
+ assert_nothing_raised { opts = @p.parse %w(-f -.1) }
250
+ assert_equal(-0.1, opts[:arg])
251
+ assert_raises(CommandlineError) { @p.parse %w(-f a) }
252
+ assert_raises(CommandlineError) { @p.parse %w(-f 1a) }
253
+ assert_raises(CommandlineError) { @p.parse %w(-f 1.a) }
254
+ assert_raises(CommandlineError) { @p.parse %w(-f a.1) }
255
+ assert_raises(CommandlineError) { @p.parse %w(-f 1.0.0) }
256
+ assert_raises(CommandlineError) { @p.parse %w(-f .) }
257
+ assert_raises(CommandlineError) { @p.parse %w(-f -.) }
258
+ end
259
+
260
+ def test_short_options_cant_be_numeric
261
+ assert_raises(ArgumentError) { @p.opt :arg, "desc", :short => "-1" }
262
+ @p.opt :a1b, "desc"
263
+ @p.opt :a2b, "desc"
264
+ assert_not_equal "2", @p.specs[:a2b][:short]
265
+ end
266
+
267
+ def test_short_options_can_be_weird
268
+ assert_nothing_raised { @p.opt :arg1, "desc", :short => "#" }
269
+ assert_nothing_raised { @p.opt :arg2, "desc", :short => "." }
270
+ assert_raises(ArgumentError) { @p.opt :arg3, "desc", :short => "-" }
271
+ end
272
+
273
+ def test_options_cant_be_set_multiple_times
274
+ @p.opt :arg, "desc", :short => "-x"
275
+ assert_nothing_raised { @p.parse %w(-x) }
276
+ assert_raises(CommandlineError) { @p.parse %w(-x -x) }
277
+ assert_raises(CommandlineError) { @p.parse %w(-xx) }
278
+ end
279
+
280
+ def test_long_options_also_take_equals
281
+ @p.opt :arg, "desc", :long => "arg", :type => String, :default => "hello"
282
+ opts = nil
283
+ assert_nothing_raised { opts = @p.parse %w() }
284
+ assert_equal "hello", opts[:arg]
285
+ assert_nothing_raised { opts = @p.parse %w(--arg goat) }
286
+ assert_equal "goat", opts[:arg]
287
+ assert_nothing_raised { opts = @p.parse %w(--arg=goat) }
288
+ assert_equal "goat", opts[:arg]
289
+ assert_raises(CommandlineError) { opts = @p.parse %w(--arg= goat) }
290
+ end
291
+
292
+ def test_auto_generated_long_names_convert_underscores_to_hyphens
293
+ @p.opt :hello_there
294
+ assert_equal "hello-there", @p.specs[:hello_there][:long]
295
+ end
296
+
297
+ end
298
+
299
+ end
300
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: trollop
5
+ version: !ruby/object:Gem::Version
6
+ version: "1.0"
7
+ date: 2007-01-29 00:00:00 -08:00
8
+ 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).
9
+ require_paths:
10
+ - lib
11
+ email: wmorgan-trollop@masanjin.net
12
+ homepage: http://trollop.rubyforge.org
13
+ rubyforge_project: trollop
14
+ description: "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). Trollop provides a nice automatically-generated help page, robust option parsing, and sensible defaults for everything you don't specify. Synopsis: ###### simple ###### opts = Trollop::options do opt :monkey, \"Use monkey mode.\" opt :goat, \"Use goat model\", :default => true opt :num_limbs, \"Set number of limbs\", :default => 4 end p opts ###### complex ###### 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.exists?(opts[:file]) if opts[:file] == REQUIREMENTS: * none"
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ authors:
29
+ - William Morgan
30
+ files:
31
+ - FAQ.txt
32
+ - History.txt
33
+ - Manifest.txt
34
+ - README.txt
35
+ - Rakefile
36
+ - lib/trollop.rb
37
+ - test/test_trollop.rb
38
+ test_files:
39
+ - test/test_trollop.rb
40
+ rdoc_options: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ executables: []
45
+
46
+ extensions: []
47
+
48
+ requirements: []
49
+
50
+ dependencies:
51
+ - !ruby/object:Gem::Dependency
52
+ name: hoe
53
+ version_requirement:
54
+ version_requirements: !ruby/object:Gem::Version::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 1.1.7
59
+ version: