choice 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +14 -1
- data/README +80 -4
- data/examples/ftpd.rb +10 -1
- data/examples/gamble.rb +42 -0
- data/lib/choice.rb +5 -8
- data/lib/choice/option.rb +6 -14
- data/lib/choice/parser.rb +93 -17
- data/lib/choice/version.rb +3 -3
- data/lib/choice/writer.rb +3 -2
- data/test/test_choice.rb +47 -7
- data/test/test_option.rb +2 -8
- data/test/test_parser.rb +169 -2
- metadata +5 -3
data/CHANGELOG
CHANGED
@@ -1,2 +1,15 @@
|
|
1
|
+
0.1.1:
|
2
|
+
- Fixed test_option.rb to be self sufficient.
|
3
|
+
- Fix so that long argument with equals sign in it will parse correctly [Justin Bailey]
|
4
|
+
- Added 'defaultize' deprecation warning. Too much magic can be harmful.
|
5
|
+
- Made Choice::Writer::puts, print, and printf public, can now possibly be used by other Choice classes.
|
6
|
+
- Changed UnknownArgument to UnknownOption (more descriptive)
|
7
|
+
- Added a 'valid' option as per bugtracker request for 'enum.' [Alexis Li]
|
8
|
+
- Change --long=[ARG] optional format to --long[=ARG] but keep around old format just in case.
|
9
|
+
- Added list format to options as per bug tracker suggestion in the format of --long=*LONG [Alexis Li]
|
10
|
+
- Added --long ARG format. Works with --long [ARG] and --long *ARG and --long [*ARG]
|
11
|
+
- Added :required option which insists an option is present.
|
12
|
+
- Added gamble.rb card game example.
|
13
|
+
|
1
14
|
0.1.0:
|
2
|
-
- First release
|
15
|
+
- First release
|
data/README
CHANGED
@@ -159,6 +159,7 @@ For the quick learners, here's the list:
|
|
159
159
|
* default
|
160
160
|
* desc
|
161
161
|
* cast
|
162
|
+
* valid (takes array)
|
162
163
|
* validate (takes regex)
|
163
164
|
* filter (takes a block)
|
164
165
|
* action (ditto)
|
@@ -170,10 +171,12 @@ You can define these within your option in any order which pleases you.
|
|
170
171
|
Defines the short switch for an option. Expected to be a dash and a single
|
171
172
|
character.
|
172
173
|
|
174
|
+
short '-s'
|
175
|
+
|
173
176
|
=== long
|
174
177
|
|
175
178
|
Defines the long switch for an option. Expected to be a double dash followed by
|
176
|
-
a string, an equal sign, and another string.
|
179
|
+
a string, an equal sign (or a space), and another string.
|
177
180
|
|
178
181
|
There are two variants: longs where a parameter is required and longs where a
|
179
182
|
parameter is optional, in which case the value will be +true+ if the option is
|
@@ -193,17 +196,35 @@ we can do this:
|
|
193
196
|
$ ruby ftpd.rb --debug=1
|
194
197
|
debug: 1
|
195
198
|
|
199
|
+
$ ruby ftpd.rb --debug 1
|
200
|
+
debug: 1
|
201
|
+
|
196
202
|
*Required*:
|
197
203
|
long '--debug=LEVEL'
|
198
204
|
|
199
205
|
Assuming the same as above:
|
200
206
|
|
201
|
-
$ ruby ftpd.rb --debug
|
207
|
+
$ ruby ftpd.rb --debug 1
|
202
208
|
debug: 1
|
203
209
|
|
204
210
|
$ ruby ftpd.rb --debug
|
205
211
|
<help screen printed>
|
212
|
+
|
213
|
+
=== long as array
|
214
|
+
|
215
|
+
Often you may wish to allow users the ability to pass in multiple arguments and have
|
216
|
+
them all combined into an array. You can accomplish this by defining a +long+ and
|
217
|
+
setting the caps-argument to *ARG. Like this:
|
206
218
|
|
219
|
+
long '--suit *SUITS'
|
220
|
+
|
221
|
+
<tt>Choice.choices.suits</tt> will now return an array. Here's an example of usage:
|
222
|
+
|
223
|
+
$ ruby --suit hearts clubs
|
224
|
+
suit: ['hearts', 'clubs']
|
225
|
+
|
226
|
+
Check out <tt>examples/gamble.rb</tt> for more information on this cool feature.
|
227
|
+
|
207
228
|
=== default
|
208
229
|
|
209
230
|
You can define a default value for your option, if you'd like. If the option
|
@@ -220,7 +241,7 @@ will be 'info.'
|
|
220
241
|
$ ftpd.rb
|
221
242
|
debug: info
|
222
243
|
|
223
|
-
$ ftpd.rb --debug
|
244
|
+
$ ftpd.rb --debug warn
|
224
245
|
debug: warn
|
225
246
|
|
226
247
|
=== desc
|
@@ -244,6 +265,8 @@ By default, all members of the <tt>Choice.choices</tt> hash are strings. If
|
|
244
265
|
you want something different, like an Integer for a port number, you can use
|
245
266
|
the +cast+ statement.
|
246
267
|
|
268
|
+
cast Integer
|
269
|
+
|
247
270
|
Currently support +cast+ options:
|
248
271
|
|
249
272
|
* Integer
|
@@ -253,6 +276,22 @@ Currently support +cast+ options:
|
|
253
276
|
|
254
277
|
We'll probably add Date, Time, and DateTime in the future, if people want them.
|
255
278
|
|
279
|
+
=== valid
|
280
|
+
|
281
|
+
Giving +valid+ an array creates a whitelist of acceptable arguments.
|
282
|
+
|
283
|
+
valid %w[clubs hearts spades diamonds]
|
284
|
+
|
285
|
+
If our option is passed anything other than one of the four card suits, the help
|
286
|
+
screen will be printed. It might be a good idea to include acceptable arguments in
|
287
|
+
your option's "desc" value.
|
288
|
+
|
289
|
+
$ ruby gamble.rb -s clubs
|
290
|
+
suit: clubs
|
291
|
+
|
292
|
+
$ ruby gamble.rb -s joker
|
293
|
+
<help screen printed>
|
294
|
+
|
256
295
|
=== validate
|
257
296
|
|
258
297
|
The +validate+ statement accepts a regular expression which it will test
|
@@ -264,7 +303,7 @@ be printed. I love ports, so let's stick with that example:
|
|
264
303
|
Of course, 2100 matches this:
|
265
304
|
|
266
305
|
$ ruby ftpd.rb -p 2100
|
267
|
-
port:
|
306
|
+
port: 2100
|
268
307
|
|
269
308
|
I like dogs. I wish dogs could be ports. Alas, Choice knows better (once
|
270
309
|
I've told it so):
|
@@ -297,6 +336,25 @@ You can probably think of better uses.
|
|
297
336
|
A block passed to the +action+ statement will be run if that particular option
|
298
337
|
is passed. See the <tt>--version</tt> example earlier.
|
299
338
|
|
339
|
+
=== required options
|
340
|
+
|
341
|
+
You can specify an option as being required by passing :required => true to the
|
342
|
+
option definition. Choice will then print the help screen if this option is
|
343
|
+
not present. Please let your dear users know which options are required.
|
344
|
+
|
345
|
+
For example:
|
346
|
+
|
347
|
+
option :card, :required => true do
|
348
|
+
short '-c'
|
349
|
+
long '--card CARD'
|
350
|
+
desc "The card you wish to gamble on. Required. Only one, please."
|
351
|
+
end
|
352
|
+
|
353
|
+
Then:
|
354
|
+
|
355
|
+
$ ruby gamble.rb
|
356
|
+
<help screen, -c or --card wasn't passed>
|
357
|
+
|
300
358
|
== Other options
|
301
359
|
|
302
360
|
These statements are purely aesthetic, used to help make your <tt>--help</tt>
|
@@ -315,21 +373,33 @@ options defined:
|
|
315
373
|
You can pass any string to the +banner+ statement to override what prints. This
|
316
374
|
might be useful if you're into ascii art.
|
317
375
|
|
376
|
+
banner "Usage: ftpd.rb"
|
377
|
+
|
318
378
|
=== header
|
319
379
|
|
320
380
|
The header is what shows up after the banner but before your option definitions
|
321
381
|
are printed. Each header call is a newline. Check out the example above.
|
322
382
|
|
383
|
+
header "ftp is a harsh and unforgiving protocol."
|
384
|
+
|
323
385
|
=== separator
|
324
386
|
|
325
387
|
As in the example above, you can put separators between options to help display
|
326
388
|
the logical groupings of your options. Or whatever.
|
327
389
|
|
390
|
+
separator "----"
|
391
|
+
|
392
|
+
To get a blank line, rock an empty string:
|
393
|
+
|
394
|
+
separator ''
|
395
|
+
|
328
396
|
=== footer
|
329
397
|
|
330
398
|
The footer is displayed after all your options are displayed. Nothing new
|
331
399
|
here, works like the other options above.
|
332
400
|
|
401
|
+
footer "That's all there is to it!"
|
402
|
+
|
333
403
|
== It looks like poetry
|
334
404
|
|
335
405
|
That's it. Not much, I know. Maybe this will make handling your command
|
@@ -345,3 +415,9 @@ This'll run the unit tests. Also, if you would, send me a bit of information
|
|
345
415
|
on your platform. Choice was tested on OS X and RHEL with a 2.4 kernel but who
|
346
416
|
knows. Thanks a lot.
|
347
417
|
|
418
|
+
== Thanks to
|
419
|
+
|
420
|
+
For bug reports, patches, and ideas I'd be honored to thank the following:
|
421
|
+
|
422
|
+
- Justin Bailey
|
423
|
+
- Alexis Li
|
data/examples/ftpd.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
$:.unshift "../lib"
|
2
|
+
$:.unshift "lib"
|
2
3
|
require 'choice'
|
3
4
|
|
4
5
|
port = 21
|
@@ -33,6 +34,14 @@ Choice.options do
|
|
33
34
|
desc "The number of connections to allow at once (default 5)"
|
34
35
|
default 5
|
35
36
|
end
|
37
|
+
|
38
|
+
option :protocol do
|
39
|
+
short '-l'
|
40
|
+
long '--protocol=PROTOCOL'
|
41
|
+
desc "The protocol to use (default ftp)"
|
42
|
+
valid %w[ftp sftp]
|
43
|
+
default 'ftp'
|
44
|
+
end
|
36
45
|
|
37
46
|
option :yaml_cfg do
|
38
47
|
long '--config=FILE'
|
@@ -50,7 +59,7 @@ Choice.options do
|
|
50
59
|
|
51
60
|
option :debug do
|
52
61
|
short '-d'
|
53
|
-
long '--debug'
|
62
|
+
long '--debug[=LEVEL]'
|
54
63
|
desc 'Turn on debugging mode'
|
55
64
|
end
|
56
65
|
|
data/examples/gamble.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
$:.unshift "../lib"
|
2
|
+
$:.unshift "lib"
|
3
|
+
require 'choice'
|
4
|
+
|
5
|
+
suits = %w[clubs diamonds spades hearts]
|
6
|
+
stringed_numerics = (1..13).to_a.map { |a| a.to_s }
|
7
|
+
valid_cards = stringed_numerics + %w[jack queen king ace]
|
8
|
+
cards = {}
|
9
|
+
stringed_numerics.each { |n| cards[n] = n }
|
10
|
+
cards.merge!('1' => 'ace', '11' => 'jack', '12' => 'queen', '13' => 'king')
|
11
|
+
|
12
|
+
Choice.options do
|
13
|
+
header "Gambling is fun again! Pick a card and a suit (or two), then see if you win!"
|
14
|
+
header ""
|
15
|
+
header "Options:"
|
16
|
+
|
17
|
+
option :suit, :required => true do
|
18
|
+
short '-s'
|
19
|
+
long '--suit *SUITS'
|
20
|
+
desc "The suit you wish to choose. Required. You can pass in more than one, even."
|
21
|
+
desc " Valid suits: #{suits * ' '}"
|
22
|
+
valid suits
|
23
|
+
end
|
24
|
+
|
25
|
+
separator ''
|
26
|
+
|
27
|
+
option :card, :required => true do
|
28
|
+
short '-c'
|
29
|
+
long '--card CARD'
|
30
|
+
desc "The card you wish to gamble on. Required. Only one, please."
|
31
|
+
desc " Valid cards: 1 - 13, jack, queen, king, ace"
|
32
|
+
valid valid_cards
|
33
|
+
cast String
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
suit = suits[rand(suits.size)]
|
38
|
+
card = cards[(rand(13)+1).to_s]
|
39
|
+
|
40
|
+
puts "I drew the #{card} of #{suit}."
|
41
|
+
puts "You picked the #{Choice.choices.card} of #{Choice.choices.suit * ' or '}."
|
42
|
+
puts "You " << (Choice.choices.suit.include?(suit) && card == cards[Choice.choices.card] ? 'win!' : 'lose :(')
|
data/lib/choice.rb
CHANGED
@@ -12,20 +12,17 @@ module Choice
|
|
12
12
|
# The main method, which defines the options
|
13
13
|
def options(&block)
|
14
14
|
# Setup all instance variables
|
15
|
-
@@args ||=
|
15
|
+
@@args ||= ARGV
|
16
16
|
@@banner ||= false
|
17
17
|
@@header ||= Array.new
|
18
18
|
@@options ||= Array.new
|
19
19
|
@@footer ||= Array.new
|
20
20
|
|
21
|
-
# Args can be overriden, but shouldn't be
|
22
|
-
self.args = @@args || ARGV
|
23
|
-
|
24
21
|
# Eval the passed block to define the options.
|
25
22
|
instance_eval(&block)
|
26
23
|
|
27
24
|
# Parse what we've got.
|
28
|
-
parse
|
25
|
+
parse unless parsed?
|
29
26
|
end
|
30
27
|
|
31
28
|
# Returns a hash representing options passed in via the command line.
|
@@ -34,10 +31,10 @@ module Choice
|
|
34
31
|
end
|
35
32
|
|
36
33
|
# Defines an option.
|
37
|
-
def option(opt, &block)
|
34
|
+
def option(opt, options = {}, &block)
|
38
35
|
# Notice: options is maintained as an array of arrays, the first element
|
39
36
|
# the option name and the second the option object.
|
40
|
-
@@options << [opt.to_s, Option.new(&block)]
|
37
|
+
@@options << [opt.to_s, Option.new(options, &block)]
|
41
38
|
end
|
42
39
|
|
43
40
|
# Separators are text displayed by --help within the options block.
|
@@ -102,7 +99,7 @@ module Choice
|
|
102
99
|
@@args
|
103
100
|
end
|
104
101
|
|
105
|
-
# You can choose to not kill the script after the help screen is
|
102
|
+
# You can choose to not kill the script after the help screen is printed.
|
106
103
|
def dont_exit_on_help=(val) #:nodoc:
|
107
104
|
@@exit = true
|
108
105
|
end
|
data/lib/choice/option.rb
CHANGED
@@ -6,12 +6,12 @@ module Choice
|
|
6
6
|
|
7
7
|
# Since we define getters/setters on the fly, we need a white list of
|
8
8
|
# which to accept. Here's the list.
|
9
|
-
CHOICES = %w[short long desc default filter action cast validate]
|
9
|
+
CHOICES = %w[short long desc default filter action cast validate valid]
|
10
10
|
|
11
11
|
# You can instantiate an option on its own or by passing it a name and
|
12
12
|
# a block. If you give it a block, it will eval() the block and set itself
|
13
13
|
# up nicely.
|
14
|
-
def initialize(
|
14
|
+
def initialize(options = {}, &block)
|
15
15
|
# Here we store the definitions this option contains, to make to_a and
|
16
16
|
# to_h easier.
|
17
17
|
@choices = []
|
@@ -19,9 +19,9 @@ module Choice
|
|
19
19
|
# If we got a block, eval it and set everything up.
|
20
20
|
self.instance_eval(&block) if block_given?
|
21
21
|
|
22
|
-
#
|
23
|
-
|
24
|
-
|
22
|
+
# Is this option required?
|
23
|
+
@required = options[:required] || false
|
24
|
+
@choices << 'required'
|
25
25
|
end
|
26
26
|
|
27
27
|
# This is the catch all for the getter/setter choices defined in CHOICES.
|
@@ -52,14 +52,6 @@ module Choice
|
|
52
52
|
@choices << method if args[0] || block_given? unless @choices.index(method)
|
53
53
|
end
|
54
54
|
|
55
|
-
# Might be going away soon. Tries to make some guesses about what you
|
56
|
-
# want if you instantiated Option with a name and no block.
|
57
|
-
def defaultize(option)
|
58
|
-
option = option.to_s
|
59
|
-
short "-#{option[0..0].downcase}"
|
60
|
-
long "--#{option.downcase}=#{option.upcase}"
|
61
|
-
end
|
62
|
-
|
63
55
|
# The desc method is slightly special: it stores itself as an array and
|
64
56
|
# each subsequent call adds to that array, rather than overwriting it.
|
65
57
|
# This is so we can do multi-line descriptions easily.
|
@@ -78,7 +70,7 @@ module Choice
|
|
78
70
|
return false if @desc.nil?
|
79
71
|
true
|
80
72
|
end
|
81
|
-
|
73
|
+
|
82
74
|
# Returns Option converted to an array.
|
83
75
|
def to_a
|
84
76
|
array = []
|
data/lib/choice/parser.rb
CHANGED
@@ -27,10 +27,11 @@ module Choice
|
|
27
27
|
|
28
28
|
# Define local hashes we're going to use. choices is where we store
|
29
29
|
# the actual values we've pulled from the argument list.
|
30
|
-
hashes, longs, required, validators, choices = {}, {}, {}, {}, {}
|
30
|
+
hashes, longs, required, validators, choices, arrayed = {}, {}, {}, {}, {}, {}
|
31
|
+
hard_required = {}
|
31
32
|
|
32
33
|
# We can define these on the fly because they are all so similar.
|
33
|
-
params = %w[short cast filter action default]
|
34
|
+
params = %w[short cast filter action default valid]
|
34
35
|
params.each { |param| hashes["#{param}s"] = {} }
|
35
36
|
|
36
37
|
# Inspect each option and move its info into our local hashes.
|
@@ -44,6 +45,9 @@ module Choice
|
|
44
45
|
raise HashExpectedForOption
|
45
46
|
end
|
46
47
|
|
48
|
+
# Is this option required?
|
49
|
+
hard_required[name] = true if obj['required']
|
50
|
+
|
47
51
|
# Set the local hashes if the value exists on this option object.
|
48
52
|
params.each { |param| hashes["#{param}s"][name] = obj[param] if obj[param] }
|
49
53
|
|
@@ -57,15 +61,36 @@ module Choice
|
|
57
61
|
|
58
62
|
# Parse the long option. If it contains a =, figure out if the
|
59
63
|
# argument is required or optional. Optional arguments are formed
|
60
|
-
# like [ARG], whereas required are just ARG (in --long=ARG style).
|
61
|
-
if obj['long'] && obj['long'] =~
|
62
|
-
|
64
|
+
# like [=ARG], whereas required are just ARG (in --long=ARG style).
|
65
|
+
if obj['long'] && obj['long'] =~ /(=|\[| )/
|
66
|
+
# Save the separator we used, as we're gonna need it, then split
|
67
|
+
sep = $1
|
68
|
+
option, *argument = obj['long'].split(sep)
|
69
|
+
|
70
|
+
# The actual name of the long switch
|
63
71
|
longs[name] = option
|
64
|
-
|
72
|
+
|
73
|
+
# Preserve the original argument, as it may contain [ or =,
|
74
|
+
# by joining with the character we split on. Add a [ in front if
|
75
|
+
# we split on that.
|
76
|
+
argument = (sep == '[' ? '[' : '') << Array(argument).join(sep)
|
77
|
+
|
78
|
+
# Do we expect multiple arguments which get turned into an array?
|
79
|
+
arrayed[name] = true if argument =~ /^\[?=?\*(.+)\]?$/
|
80
|
+
|
81
|
+
# Is this long required or optional?
|
82
|
+
required[name] = true unless argument =~ /^\[=?\*?(.+)\]$/
|
65
83
|
elsif obj['long']
|
84
|
+
# We can't have a long as a switch when valid is set -- die.
|
85
|
+
raise ArgumentRequiredWithValid if obj['valid']
|
86
|
+
|
66
87
|
# Set without any checking if it's just --long
|
67
88
|
longs[name] = obj['long']
|
68
89
|
end
|
90
|
+
|
91
|
+
# If we were given a list of valid arguments with 'valid,' this option
|
92
|
+
# is definitely required.
|
93
|
+
required[name] = true if obj['valid']
|
69
94
|
end
|
70
95
|
|
71
96
|
# Go through the arguments and try to figure out whom they belong to
|
@@ -81,21 +106,49 @@ module Choice
|
|
81
106
|
value = true if !value || value =~ /^-/
|
82
107
|
|
83
108
|
# Add this value to the choices hash with the key of the option's
|
84
|
-
# name.
|
85
|
-
|
109
|
+
# name. If we expect an array, tack this argument on.
|
110
|
+
name = hashes['shorts'].index(arg)
|
111
|
+
if arrayed[name]
|
112
|
+
choices[name] ||= []
|
113
|
+
choices[name] += arrayize_arguments(name, args[i+1..-1])
|
114
|
+
else
|
115
|
+
choices[name] = value
|
116
|
+
end
|
86
117
|
|
87
|
-
elsif
|
88
|
-
#
|
89
|
-
|
90
|
-
|
118
|
+
elsif /^(--[^=]+)=?/ =~ arg && longs.value?($1)
|
119
|
+
# The joke here is we always accept both --long=VALUE and --long VALUE.
|
120
|
+
|
121
|
+
# Grab values from --long=VALUE format
|
122
|
+
if arg =~ /=/ && longs.value?((longed = arg.split('=')).first)
|
123
|
+
name = longs.index(longed.shift)
|
124
|
+
value = longed * '='
|
125
|
+
# For the arrayed options.
|
126
|
+
potential_args = args[i+1..-1]
|
127
|
+
else
|
128
|
+
# Grab value otherwise if not in --long=VALUE format. Assume --long VALUE.
|
129
|
+
name = longs.index(arg)
|
130
|
+
# Value is nil if we don't have a = and the next argument is no good
|
131
|
+
value = args[i+1] =~ /^-/ ? nil : args[i+1]
|
132
|
+
# For the arrayed options.
|
133
|
+
potential_args = args[i+2..-1]
|
134
|
+
end
|
91
135
|
|
92
|
-
|
93
|
-
|
94
|
-
|
136
|
+
# If we expect an array, tack this argument on.
|
137
|
+
if arrayed[name] && !value.nil?
|
138
|
+
# If this is arrayed and the value isn't nil, set it.
|
139
|
+
choices[name] ||= []
|
140
|
+
choices[name] << value
|
141
|
+
choices[name] += arrayize_arguments(name, potential_args)
|
142
|
+
else
|
143
|
+
# If we set the value to nil, that means nothing was set and we
|
144
|
+
# need to set the value to true. We'll find out later if that's
|
145
|
+
# acceptable or not.
|
146
|
+
choices[name] = value.nil? ? true : value
|
147
|
+
end
|
95
148
|
|
96
149
|
else
|
97
150
|
# If we're here, we have no idea what the passed argument is. Die.
|
98
|
-
raise
|
151
|
+
raise UnknownOption if arg =~ /^-/
|
99
152
|
|
100
153
|
end
|
101
154
|
end
|
@@ -108,6 +161,9 @@ module Choice
|
|
108
161
|
|
109
162
|
# Validate the argument if we need to.
|
110
163
|
raise ArgumentValidationFails if validators[name] && validators[name] !~ value
|
164
|
+
|
165
|
+
# Make sure the argument is valid
|
166
|
+
raise InvalidArgument unless value.to_a.all? { |v| hashes['valids'][name].include?(v) } if hashes['valids'][name]
|
111
167
|
|
112
168
|
# Cast the argument using the method defined in the constant hash.
|
113
169
|
value = value.send(CAST_METHODS[hashes['casts'][name]]) if hashes['casts'].include?(name)
|
@@ -123,6 +179,11 @@ module Choice
|
|
123
179
|
choices[name] = value
|
124
180
|
end
|
125
181
|
|
182
|
+
# Die if we're missing any required arguments
|
183
|
+
hard_required.each do |name, value|
|
184
|
+
raise ArgumentRequired unless choices[name]
|
185
|
+
end
|
186
|
+
|
126
187
|
# Home stretch. Go through all the defaults defined and if a choice
|
127
188
|
# does not exist in our choices hash, set its value to the requested
|
128
189
|
# default.
|
@@ -133,13 +194,28 @@ module Choice
|
|
133
194
|
# Return the choices hash.
|
134
195
|
choices
|
135
196
|
end
|
197
|
+
|
198
|
+
private
|
199
|
+
# Turns trailing command line arguments into an array for an arrayed value
|
200
|
+
def self.arrayize_arguments(name, args)
|
201
|
+
# Go through trailing arguments and suck them in if they don't seem
|
202
|
+
# to have an owner.
|
203
|
+
array = []
|
204
|
+
potential_args = args.dup
|
205
|
+
until (arg = potential_args.shift) =~ /^-/ || arg.nil?
|
206
|
+
array << arg
|
207
|
+
end
|
208
|
+
array
|
209
|
+
end
|
136
210
|
|
137
211
|
# All the possible exceptions this module can raise.
|
138
212
|
class ParseError < Exception; end
|
139
213
|
class HashExpectedForOption < Exception; end
|
140
|
-
class
|
214
|
+
class UnknownOption < ParseError; end
|
141
215
|
class ArgumentRequired < ParseError; end
|
142
216
|
class ValidateExpectsRegexp < ParseError; end
|
143
217
|
class ArgumentValidationFails < ParseError; end
|
218
|
+
class InvalidArgument < ParseError; end
|
219
|
+
class ArgumentRequiredWithValid < ParseError; end
|
144
220
|
end
|
145
221
|
end
|
data/lib/choice/version.rb
CHANGED
data/lib/choice/writer.rb
CHANGED
@@ -148,12 +148,12 @@ module Choice
|
|
148
148
|
end.to_s
|
149
149
|
|
150
150
|
# Print it out, with our newly aquired options string.
|
151
|
-
puts "Usage: #{program}" <<
|
151
|
+
puts "Usage: #{program}" << opts
|
152
152
|
end
|
153
153
|
|
154
154
|
# Figure out the name of this program based on what was run.
|
155
155
|
def program
|
156
|
-
|
156
|
+
(/(\/|\\)/ =~ $0) ? File.basename($0) : $0
|
157
157
|
end
|
158
158
|
|
159
159
|
# Set where we print.
|
@@ -166,6 +166,7 @@ module Choice
|
|
166
166
|
@@target
|
167
167
|
end
|
168
168
|
|
169
|
+
public
|
169
170
|
# Fake puts
|
170
171
|
def puts(str = nil)
|
171
172
|
str = '' if str.nil?
|
data/test/test_choice.rb
CHANGED
@@ -2,11 +2,14 @@ $:.unshift "../lib:lib"
|
|
2
2
|
require 'test/unit'
|
3
3
|
require 'choice'
|
4
4
|
|
5
|
+
$VERBOSE = nil
|
6
|
+
|
5
7
|
class TestChoice < Test::Unit::TestCase
|
6
8
|
|
7
9
|
def setup
|
8
10
|
Choice.reset
|
9
11
|
Choice.dont_exit_on_help = true
|
12
|
+
Choice.send(:class_variable_set, '@@choices', true)
|
10
13
|
end
|
11
14
|
|
12
15
|
def test_choices
|
@@ -49,7 +52,6 @@ class TestChoice < Test::Unit::TestCase
|
|
49
52
|
HELP_STRING = ''
|
50
53
|
def test_help
|
51
54
|
Choice.output_to(HELP_STRING)
|
52
|
-
Choice.args = ['-m', 'lunch', '--help']
|
53
55
|
|
54
56
|
Choice.options do
|
55
57
|
banner "Usage: choice [-mu]"
|
@@ -64,10 +66,12 @@ class TestChoice < Test::Unit::TestCase
|
|
64
66
|
|
65
67
|
option :utencil do
|
66
68
|
short "-u"
|
67
|
-
long "--utencil=
|
69
|
+
long "--utencil[=UTENCIL]"
|
68
70
|
desc "Your favorite eating utencil."
|
69
71
|
end
|
70
72
|
end
|
73
|
+
|
74
|
+
Choice.args = ['-m', 'lunch', '--help']
|
71
75
|
|
72
76
|
help_string = <<-HELP
|
73
77
|
Usage: choice [-mu]
|
@@ -75,7 +79,7 @@ Usage: choice [-mu]
|
|
75
79
|
-m Your favorite meal.
|
76
80
|
|
77
81
|
And you eat it with...
|
78
|
-
-u, --utencil=
|
82
|
+
-u, --utencil[=UTENCIL] Your favorite eating utencil.
|
79
83
|
HELP
|
80
84
|
|
81
85
|
assert_equal help_string, HELP_STRING
|
@@ -84,7 +88,6 @@ HELP
|
|
84
88
|
UNKNOWN_STRING = ''
|
85
89
|
def test_unknown_argument
|
86
90
|
Choice.output_to(UNKNOWN_STRING)
|
87
|
-
Choice.args = ['-m', 'lunch', '--motorcycles']
|
88
91
|
|
89
92
|
Choice.options do
|
90
93
|
banner "Usage: choice [-mu]"
|
@@ -99,10 +102,12 @@ HELP
|
|
99
102
|
|
100
103
|
option :utencil do
|
101
104
|
short "-u"
|
102
|
-
long "--utencil=
|
105
|
+
long "--utencil[=UTENCIL]"
|
103
106
|
desc "Your favorite eating utencil."
|
104
107
|
end
|
105
108
|
end
|
109
|
+
|
110
|
+
Choice.args = ['-m', 'lunch', '--motorcycles']
|
106
111
|
|
107
112
|
help_string = <<-HELP
|
108
113
|
Usage: choice [-mu]
|
@@ -110,10 +115,45 @@ Usage: choice [-mu]
|
|
110
115
|
-m Your favorite meal.
|
111
116
|
|
112
117
|
And you eat it with...
|
113
|
-
-u, --utencil=
|
118
|
+
-u, --utencil[=UTENCIL] Your favorite eating utencil.
|
114
119
|
HELP
|
115
120
|
|
116
121
|
assert_equal help_string, UNKNOWN_STRING
|
117
122
|
end
|
118
123
|
|
119
|
-
|
124
|
+
REQUIRED_STRING = ''
|
125
|
+
def test_required_argument
|
126
|
+
Choice.output_to(REQUIRED_STRING)
|
127
|
+
|
128
|
+
Choice.options do
|
129
|
+
banner "Usage: choice [-mu]"
|
130
|
+
header ""
|
131
|
+
option :meal, :required => true do
|
132
|
+
short '-m'
|
133
|
+
desc 'Your favorite meal.'
|
134
|
+
end
|
135
|
+
|
136
|
+
separator ""
|
137
|
+
separator "And you eat it with..."
|
138
|
+
|
139
|
+
option :utencil do
|
140
|
+
short "-u"
|
141
|
+
long "--utencil[=UTENCIL]"
|
142
|
+
desc "Your favorite eating utencil."
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
Choice.args = ['-u', 'spork']
|
147
|
+
|
148
|
+
help_string = <<-HELP
|
149
|
+
Usage: choice [-mu]
|
150
|
+
|
151
|
+
-m Your favorite meal.
|
152
|
+
|
153
|
+
And you eat it with...
|
154
|
+
-u, --utencil[=UTENCIL] Your favorite eating utencil.
|
155
|
+
HELP
|
156
|
+
|
157
|
+
assert_equal help_string, REQUIRED_STRING
|
158
|
+
end
|
159
|
+
end
|
data/test/test_option.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
$:.unshift "../lib:lib"
|
2
2
|
require 'test/unit'
|
3
|
+
require 'choice'
|
3
4
|
require 'choice/option'
|
4
5
|
|
5
6
|
class TestOption < Test::Unit::TestCase
|
@@ -65,13 +66,6 @@ class TestOption < Test::Unit::TestCase
|
|
65
66
|
assert_block(&@option.action)
|
66
67
|
end
|
67
68
|
|
68
|
-
def test_default_option
|
69
|
-
option = Choice::Option.new('port')
|
70
|
-
|
71
|
-
assert_equal '-p', option.short
|
72
|
-
assert_equal '--port=PORT', option.long
|
73
|
-
end
|
74
|
-
|
75
69
|
def test_format
|
76
70
|
@option = Choice::Option.new do
|
77
71
|
validate /^\w+$/
|
@@ -141,4 +135,4 @@ class TestOption < Test::Unit::TestCase
|
|
141
135
|
assert_equal cast, hash['cast']
|
142
136
|
assert_equal proc { 2 + 2 }.call, hash['filter'].call
|
143
137
|
end
|
144
|
-
end
|
138
|
+
end
|
data/test/test_parser.rb
CHANGED
@@ -85,7 +85,7 @@ class TestParser < Test::Unit::TestCase
|
|
85
85
|
end
|
86
86
|
@options['age'] = Choice::Option.new do
|
87
87
|
short '-a'
|
88
|
-
long 'age=
|
88
|
+
long 'age[=AGE]'
|
89
89
|
cast Integer
|
90
90
|
end
|
91
91
|
|
@@ -97,6 +97,24 @@ class TestParser < Test::Unit::TestCase
|
|
97
97
|
end
|
98
98
|
|
99
99
|
def test_text_optional
|
100
|
+
@options['color'] = Choice::Option.new do
|
101
|
+
short '-c'
|
102
|
+
long '--color[=COLOR]'
|
103
|
+
end
|
104
|
+
|
105
|
+
args = ['-c']
|
106
|
+
choices = Choice::Parser.parse(@options, args)
|
107
|
+
|
108
|
+
assert choices['color']
|
109
|
+
|
110
|
+
color = 'ladyblue'
|
111
|
+
args = ['-c', color]
|
112
|
+
choices = Choice::Parser.parse(@options, args)
|
113
|
+
|
114
|
+
assert_equal color, choices['color']
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_text_optional_deprecated
|
100
118
|
@options['color'] = Choice::Option.new do
|
101
119
|
short '-c'
|
102
120
|
long '--color=[COLOR]'
|
@@ -175,8 +193,157 @@ class TestParser < Test::Unit::TestCase
|
|
175
193
|
end
|
176
194
|
|
177
195
|
args = ['-c', 'BestOfYanni', '--grace']
|
178
|
-
assert_raise(Choice::Parser::
|
196
|
+
assert_raise(Choice::Parser::UnknownOption) do
|
197
|
+
choices = Choice::Parser.parse(@options, args)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_valid
|
202
|
+
@options['suit'] = Choice::Option.new do
|
203
|
+
short '-s'
|
204
|
+
long '--suit=SUIT'
|
205
|
+
valid %w[club diamond spade heart]
|
206
|
+
desc "The suit of your card, sir."
|
207
|
+
end
|
208
|
+
|
209
|
+
suit_good = 'club'
|
210
|
+
suit_bad = 'joker'
|
211
|
+
|
212
|
+
args = ['-s', suit_bad]
|
213
|
+
assert_raise(Choice::Parser::InvalidArgument) do
|
214
|
+
choices = Choice::Parser.parse(@options, args)
|
215
|
+
end
|
216
|
+
|
217
|
+
args = ['-s', suit_good]
|
218
|
+
choices = Choice::Parser.parse(@options, args)
|
219
|
+
|
220
|
+
assert_equal suit_good, choices['suit']
|
221
|
+
end
|
222
|
+
|
223
|
+
def test_valid_needs_argument
|
224
|
+
@options['pants'] = Choice::Option.new do
|
225
|
+
short '-p'
|
226
|
+
long '--pants'
|
227
|
+
valid %w[jeans slacks trunks boxers]
|
228
|
+
desc "Your preferred type of pants."
|
229
|
+
end
|
230
|
+
|
231
|
+
args = ['-p']
|
232
|
+
assert_raise(Choice::Parser::ArgumentRequiredWithValid) do
|
179
233
|
choices = Choice::Parser.parse(@options, args)
|
180
234
|
end
|
181
235
|
end
|
236
|
+
|
237
|
+
def test_long_as_array
|
238
|
+
@options['medium'] = Choice::Option.new do
|
239
|
+
short '-m'
|
240
|
+
long '--medium=*MEDIUM'
|
241
|
+
desc "The medium(s) you like best."
|
242
|
+
end
|
243
|
+
|
244
|
+
mediums = %w[canvas stone steel]
|
245
|
+
|
246
|
+
args = ['-m', mediums.first, '-m', mediums[1], '-m', mediums.last]
|
247
|
+
choices = Choice::Parser.parse(@options, args)
|
248
|
+
assert_equal mediums, choices['medium']
|
249
|
+
|
250
|
+
args = ['-m', mediums.first, mediums[1], mediums.last]
|
251
|
+
choices = Choice::Parser.parse(@options, args)
|
252
|
+
assert_equal mediums, choices['medium']
|
253
|
+
|
254
|
+
args = ["--medium=#{mediums.first}", "--medium=#{mediums[1]}", "--medium=#{mediums.last}"]
|
255
|
+
choices = Choice::Parser.parse(@options, args)
|
256
|
+
assert_equal mediums, choices['medium']
|
257
|
+
|
258
|
+
args = ["--medium=#{mediums.first}", mediums[1], mediums.last]
|
259
|
+
choices = Choice::Parser.parse(@options, args)
|
260
|
+
assert_equal mediums, choices['medium']
|
261
|
+
end
|
262
|
+
|
263
|
+
def test_long_as_array_optional
|
264
|
+
@options['instruments'] = Choice::Option.new do
|
265
|
+
short '-i'
|
266
|
+
long '--instruments[=*INSTRUMENTS]'
|
267
|
+
desc "Do you like instruments? Which ones do you like best?"
|
268
|
+
end
|
269
|
+
|
270
|
+
instruments = %w[xylophone guitar piano]
|
271
|
+
|
272
|
+
args = ["--instruments=#{instruments.first}", "--instruments=#{instruments[1]}",
|
273
|
+
"--instruments=#{instruments.last}"]
|
274
|
+
choices = Choice::Parser.parse(@options, args)
|
275
|
+
assert_equal instruments, choices['instruments']
|
276
|
+
|
277
|
+
args = %w[--instruments]
|
278
|
+
choices = Choice::Parser.parse(@options, args)
|
279
|
+
assert_equal true, choices['instruments']
|
280
|
+
end
|
281
|
+
|
282
|
+
def test_long_as_array_with_valid
|
283
|
+
@options['suits'] = Choice::Option.new do
|
284
|
+
short '-s'
|
285
|
+
long '--suits=*SUITS'
|
286
|
+
valid %w[club diamond spade heart]
|
287
|
+
desc "The suits of your deck, sir."
|
288
|
+
end
|
289
|
+
|
290
|
+
suits = %w[spade heart]
|
291
|
+
|
292
|
+
args = ['-s', suits.first, suits.last]
|
293
|
+
choices = Choice::Parser.parse(@options, args)
|
294
|
+
|
295
|
+
assert_equal suits, choices['suits']
|
296
|
+
|
297
|
+
args = ['-s', suits.first, 'notasuit']
|
298
|
+
assert_raise(Choice::Parser::InvalidArgument) do
|
299
|
+
choices = Choice::Parser.parse(@options, args)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def test_long_with_spaces
|
304
|
+
@options['donut'] = Choice::Option.new do
|
305
|
+
short '-d'
|
306
|
+
long '--donut DONUT'
|
307
|
+
desc "Your favorite donut style."
|
308
|
+
end
|
309
|
+
|
310
|
+
donut = 'long-john'
|
311
|
+
|
312
|
+
args = ['--donut', donut]
|
313
|
+
choices = Choice::Parser.parse(@options, args)
|
314
|
+
|
315
|
+
assert_equal donut, choices['donut']
|
316
|
+
end
|
317
|
+
|
318
|
+
def test_optional_long_with_spaces
|
319
|
+
@options['donut'] = Choice::Option.new do
|
320
|
+
short '-d'
|
321
|
+
long '--donut [DONUT]'
|
322
|
+
desc "Your favorite donut style."
|
323
|
+
end
|
324
|
+
|
325
|
+
donut = 'chocolate'
|
326
|
+
|
327
|
+
args = ['--donut', donut]
|
328
|
+
choices = Choice::Parser.parse(@options, args)
|
329
|
+
assert_equal donut, choices['donut']
|
330
|
+
|
331
|
+
args = ['--donut']
|
332
|
+
choices = Choice::Parser.parse(@options, args)
|
333
|
+
assert_equal true, choices['donut']
|
334
|
+
end
|
335
|
+
|
336
|
+
def test_long_with_spaces_arrayed
|
337
|
+
@options['donuts'] = Choice::Option.new do
|
338
|
+
short '-d'
|
339
|
+
long '--donuts *DONUTS'
|
340
|
+
desc "Your favorite donut styles."
|
341
|
+
end
|
342
|
+
|
343
|
+
donuts = %w[glazed cream-filled]
|
344
|
+
|
345
|
+
args = ['--donuts', donuts.first, donuts.last]
|
346
|
+
choices = Choice::Parser.parse(@options, args)
|
347
|
+
assert_equal donuts, choices['donuts']
|
348
|
+
end
|
182
349
|
end
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.
|
2
|
+
rubygems_version: 0.9.0
|
3
3
|
specification_version: 1
|
4
4
|
name: choice
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.
|
7
|
-
date: 2006-
|
6
|
+
version: 0.1.1
|
7
|
+
date: 2006-07-22 00:00:00 -07:00
|
8
8
|
summary: Choice is a command line option parser.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -25,6 +25,7 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
|
|
25
25
|
platform: ruby
|
26
26
|
signing_key:
|
27
27
|
cert_chain:
|
28
|
+
post_install_message:
|
28
29
|
authors:
|
29
30
|
- Chris Wanstrath
|
30
31
|
files:
|
@@ -44,6 +45,7 @@ files:
|
|
44
45
|
- test/test_parser.rb
|
45
46
|
- test/test_writer.rb
|
46
47
|
- examples/ftpd.rb
|
48
|
+
- examples/gamble.rb
|
47
49
|
test_files: []
|
48
50
|
|
49
51
|
rdoc_options: []
|