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 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. No spaces.
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=1
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=warn
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: 21000
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
@@ -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
 
@@ -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 :(')
@@ -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 ||= false
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 prtined.
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
@@ -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(option = nil, &block)
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
- # This might be going away in the future. If you pass nothing but a
23
- # name, Option will try and guess what you want.
24
- defaultize(option) unless option.nil?
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 = []
@@ -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
- option, argument = obj['long'].split('=')
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
- required[name] = true unless argument =~ /^\[(.+)\]$/
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
- choices[hashes['shorts'].index(arg)] = value
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 arg =~ /=/ && longs.value?(arg.split('=')[0])
88
- # If we get a long with a = in it, grab it and the argument
89
- # passed to it.
90
- choices[longs.index(arg.split('=')[0])] = arg.split('=')[1]
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
- elsif longs.value?(arg)
93
- # If we get a long with no =, just set it to true.
94
- choices[longs.index(arg)] = true
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 UnknownArgument if arg =~ /^-/
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 UnknownArgument < ParseError; end
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
@@ -1,8 +1,8 @@
1
1
  module Choice
2
2
  module Version #:nodoc:
3
- STRING = "0.1.0"
4
3
  MAJOR = 0
5
4
  MINOR = 1
6
- TINY = 0
5
+ TINY = 1
6
+ STRING = [MAJOR, MINOR, TINY] * '.'
7
7
  end
8
- end
8
+ end
@@ -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}" << opts
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
- if (/(\/|\\)/ =~ $0) then File.basename($0) else $0 end
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?
@@ -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=[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=[UTENCIL] Your favorite eating 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=[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=[UTENCIL] Your favorite eating 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
- end
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
@@ -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
@@ -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=[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::UnknownArgument) do
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.8.11
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.0
7
- date: 2006-04-30 00:00:00 -07:00
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: []