trollop 1.0

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