choice 0.1.0 → 0.1.1
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/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: []
|