ivanvc-choice 0.1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +22 -0
- data/LICENSE +18 -0
- data/README +447 -0
- data/examples/ftpd.rb +87 -0
- data/examples/gamble.rb +42 -0
- data/lib/choice.rb +166 -0
- data/lib/choice/lazyhash.rb +67 -0
- data/lib/choice/option.rb +90 -0
- data/lib/choice/parser.rb +233 -0
- data/lib/choice/version.rb +9 -0
- data/lib/choice/writer.rb +187 -0
- data/test/test_choice.rb +234 -0
- data/test/test_lazyhash.rb +76 -0
- data/test/test_option.rb +145 -0
- data/test/test_parser.rb +388 -0
- data/test/test_writer.rb +103 -0
- metadata +69 -0
@@ -0,0 +1,233 @@
|
|
1
|
+
module Choice
|
2
|
+
|
3
|
+
# The parser takes our option definitions and our arguments and produces
|
4
|
+
# a hash of values.
|
5
|
+
module Parser #:nodoc: all
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# What method to call on an object for each given 'cast' value.
|
9
|
+
CAST_METHODS = { Integer => :to_i, String => :to_s, Float => :to_f,
|
10
|
+
Symbol => :to_sym }
|
11
|
+
|
12
|
+
# Perhaps this method does too much. It is, however, a parser.
|
13
|
+
# You pass it an array of arrays, the first element of each element being
|
14
|
+
# the option's name and the second element being a hash of the option's
|
15
|
+
# info. You also pass in your current arguments, so it knows what to
|
16
|
+
# check against.
|
17
|
+
def parse(options, args)
|
18
|
+
# Return empty hash if the parsing adventure would be fruitless.
|
19
|
+
return {} if options.nil? || !options || args.nil? || !args.is_a?(Array)
|
20
|
+
|
21
|
+
# Operate on a copy of the inputs
|
22
|
+
args = args.dup
|
23
|
+
|
24
|
+
# If we are passed an array, make the best of it by converting it
|
25
|
+
# to a hash.
|
26
|
+
options = options.inject({}) do |hash, value|
|
27
|
+
value.is_a?(Array) ? hash.merge(value.first => value[1]) : hash
|
28
|
+
end if options.is_a? Array
|
29
|
+
|
30
|
+
# Define local hashes we're going to use. choices is where we store
|
31
|
+
# the actual values we've pulled from the argument list.
|
32
|
+
hashes, longs, required, validators, choices, arrayed = {}, {}, {}, {}, {}, {}
|
33
|
+
hard_required = {}
|
34
|
+
|
35
|
+
# We can define these on the fly because they are all so similar.
|
36
|
+
params = %w[short cast filter action default valid]
|
37
|
+
params.each { |param| hashes["#{param}s"] = {} }
|
38
|
+
|
39
|
+
# Inspect each option and move its info into our local hashes.
|
40
|
+
options.each do |name, obj|
|
41
|
+
name = name.to_s
|
42
|
+
|
43
|
+
# Only take hashes or hash-like duck objects.
|
44
|
+
raise HashExpectedForOption unless obj.respond_to? :to_h
|
45
|
+
obj = obj.to_h
|
46
|
+
|
47
|
+
# Is this option required?
|
48
|
+
hard_required[name] = true if obj['required']
|
49
|
+
|
50
|
+
# Set the local hashes if the value exists on this option object.
|
51
|
+
params.each { |param| hashes["#{param}s"][name] = obj[param] if obj[param] }
|
52
|
+
|
53
|
+
# If there is a validate statement, make it a regex or proc.
|
54
|
+
validators[name] = make_validation(obj['validate']) if obj['validate']
|
55
|
+
|
56
|
+
# Parse the long option. If it contains a =, figure out if the
|
57
|
+
# argument is required or optional. Optional arguments are formed
|
58
|
+
# like [=ARG], whereas required are just ARG (in --long=ARG style).
|
59
|
+
if obj['long'] && obj['long'] =~ /(=|\[| )/
|
60
|
+
# Save the separator we used, as we're gonna need it, then split
|
61
|
+
sep = $1
|
62
|
+
option, *argument = obj['long'].split(sep)
|
63
|
+
|
64
|
+
# The actual name of the long switch
|
65
|
+
longs[name] = option
|
66
|
+
|
67
|
+
# Preserve the original argument, as it may contain [ or =,
|
68
|
+
# by joining with the character we split on. Add a [ in front if
|
69
|
+
# we split on that.
|
70
|
+
argument = (sep == '[' ? '[' : '') << Array(argument).join(sep)
|
71
|
+
|
72
|
+
# Do we expect multiple arguments which get turned into an array?
|
73
|
+
arrayed[name] = true if argument =~ /^\[?=?\*(.+)\]?$/
|
74
|
+
|
75
|
+
# Is this long required or optional?
|
76
|
+
required[name] = true unless argument =~ /^\[=?\*?(.+)\]$/
|
77
|
+
elsif obj['long']
|
78
|
+
# We can't have a long as a switch when valid is set -- die.
|
79
|
+
raise ArgumentRequiredWithValid if obj['valid']
|
80
|
+
|
81
|
+
# Set without any checking if it's just --long
|
82
|
+
longs[name] = obj['long']
|
83
|
+
end
|
84
|
+
|
85
|
+
# If we were given a list of valid arguments with 'valid,' this option
|
86
|
+
# is definitely required.
|
87
|
+
required[name] = true if obj['valid']
|
88
|
+
end
|
89
|
+
|
90
|
+
rest = []
|
91
|
+
|
92
|
+
# Go through the arguments and try to figure out whom they belong to
|
93
|
+
# at this point.
|
94
|
+
while arg = args.shift
|
95
|
+
if hashes['shorts'].value?(arg)
|
96
|
+
# Set the value to the next element in the args array since
|
97
|
+
# this is a short.
|
98
|
+
|
99
|
+
# If the next argument isn't a value, set this value to true
|
100
|
+
if args.empty? || args.first.match(/^-/)
|
101
|
+
value = true
|
102
|
+
else
|
103
|
+
value = args.shift
|
104
|
+
end
|
105
|
+
|
106
|
+
# Add this value to the choices hash with the key of the option's
|
107
|
+
# name. If we expect an array, tack this argument on.
|
108
|
+
name = hashes['shorts'].index(arg)
|
109
|
+
if arrayed[name]
|
110
|
+
choices[name] ||= []
|
111
|
+
choices[name] << value unless value.nil?
|
112
|
+
choices[name] += arrayize_arguments(args)
|
113
|
+
else
|
114
|
+
choices[name] = value
|
115
|
+
end
|
116
|
+
|
117
|
+
elsif (m = arg.match(/^(--[^=]+)=?/)) && longs.value?(m[1])
|
118
|
+
# The joke here is we always accept both --long=VALUE and --long VALUE.
|
119
|
+
|
120
|
+
# Grab values from --long=VALUE format
|
121
|
+
name, value = arg.split('=', 2)
|
122
|
+
name = longs.index(name)
|
123
|
+
|
124
|
+
if value.nil? && args.first !~ /^-/
|
125
|
+
# Grab value otherwise if not in --long=VALUE format. Assume --long VALUE.
|
126
|
+
# Value is nil if we don't have a = and the next argument is no good
|
127
|
+
value = args.shift
|
128
|
+
end
|
129
|
+
|
130
|
+
# If we expect an array, tack this argument on.
|
131
|
+
if arrayed[name]
|
132
|
+
# If this is arrayed and the value isn't nil, set it.
|
133
|
+
choices[name] ||= []
|
134
|
+
choices[name] << value unless value.nil?
|
135
|
+
choices[name] += arrayize_arguments(args)
|
136
|
+
else
|
137
|
+
# If we set the value to nil, that means nothing was set and we
|
138
|
+
# need to set the value to true. We'll find out later if that's
|
139
|
+
# acceptable or not.
|
140
|
+
choices[name] = value.nil? ? true : value
|
141
|
+
end
|
142
|
+
|
143
|
+
else
|
144
|
+
# If we're here, we have no idea what the passed argument is. Die.
|
145
|
+
if arg =~ /^-/
|
146
|
+
raise UnknownOption
|
147
|
+
else
|
148
|
+
rest << arg
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Okay, we got all the choices. Now go through and run any filters or
|
154
|
+
# whatever on them.
|
155
|
+
choices.each do |name, value|
|
156
|
+
# Check to make sure we have all the required arguments.
|
157
|
+
raise ArgumentRequired if required[name] && value === true
|
158
|
+
|
159
|
+
# Validate the argument if we need to, against a regexp or a block.
|
160
|
+
if validators[name]
|
161
|
+
if validators[name].is_a?(Regexp) && validators[name] =~ value
|
162
|
+
elsif validators[name].is_a?(Proc) && validators[name].call(value)
|
163
|
+
else raise ArgumentValidationFails
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Make sure the argument is valid
|
168
|
+
raise InvalidArgument unless value.to_a.all? { |v| hashes['valids'][name].include?(v) } if hashes['valids'][name]
|
169
|
+
|
170
|
+
# Cast the argument using the method defined in the constant hash.
|
171
|
+
value = value.send(CAST_METHODS[hashes['casts'][name]]) if hashes['casts'].include?(name)
|
172
|
+
|
173
|
+
# Run the value through a filter and re-set it with the return.
|
174
|
+
value = hashes['filters'][name].call(value) if hashes['filters'].include?(name)
|
175
|
+
|
176
|
+
# Run an action block if there is one associated.
|
177
|
+
hashes['actions'][name].call(value) if hashes['actions'].include?(name)
|
178
|
+
|
179
|
+
# Now that we've done all that, re-set the element of the choice hash
|
180
|
+
# with the (potentially) new value.
|
181
|
+
choices[name] = value
|
182
|
+
end
|
183
|
+
|
184
|
+
# Die if we're missing any required arguments
|
185
|
+
hard_required.each do |name, value|
|
186
|
+
raise ArgumentRequired unless choices[name]
|
187
|
+
end
|
188
|
+
|
189
|
+
# Home stretch. Go through all the defaults defined and if a choice
|
190
|
+
# does not exist in our choices hash, set its value to the requested
|
191
|
+
# default.
|
192
|
+
hashes['defaults'].each do |name, value|
|
193
|
+
choices[name] = value unless choices[name]
|
194
|
+
end
|
195
|
+
|
196
|
+
# Return the choices hash and the rest of the args
|
197
|
+
[ choices, rest ]
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
# Turns trailing command line arguments into an array for an arrayed value
|
202
|
+
def arrayize_arguments(args)
|
203
|
+
# Go through trailing arguments and suck them in if they don't seem
|
204
|
+
# to have an owner.
|
205
|
+
array = []
|
206
|
+
until args.empty? || args.first.match(/^-/)
|
207
|
+
array << args.shift
|
208
|
+
end
|
209
|
+
array
|
210
|
+
end
|
211
|
+
|
212
|
+
def make_validation(validation)
|
213
|
+
case validation
|
214
|
+
when Proc then
|
215
|
+
validation
|
216
|
+
when Regexp, String then
|
217
|
+
Regexp.new(validation.to_s)
|
218
|
+
else
|
219
|
+
raise ValidateExpectsRegexpOrBlock
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# All the possible exceptions this module can raise.
|
224
|
+
class ParseError < Exception; end
|
225
|
+
class HashExpectedForOption < Exception; end
|
226
|
+
class UnknownOption < ParseError; end
|
227
|
+
class ArgumentRequired < ParseError; end
|
228
|
+
class ValidateExpectsRegexpOrBlock < ParseError; end
|
229
|
+
class ArgumentValidationFails < ParseError; end
|
230
|
+
class InvalidArgument < ParseError; end
|
231
|
+
class ArgumentRequiredWithValid < ParseError; end
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
module Choice
|
2
|
+
# This module writes to the screen. As of now, its only real use is writing
|
3
|
+
# the help screen.
|
4
|
+
module Writer #:nodoc: all
|
5
|
+
|
6
|
+
# Some constants used for printing and line widths
|
7
|
+
SHORT_LENGTH = 6
|
8
|
+
SHORT_BREAK_LENGTH = 2
|
9
|
+
LONG_LENGTH = 29
|
10
|
+
PRE_DESC_LENGTH = SHORT_LENGTH + SHORT_BREAK_LENGTH + LONG_LENGTH
|
11
|
+
|
12
|
+
|
13
|
+
# The main method. Takes a hash of arguments with the following possible
|
14
|
+
# keys, running them through the appropriate method:
|
15
|
+
# banner, header, options, footer
|
16
|
+
#
|
17
|
+
# Can also be told where to print (default STDOUT) and not to exit after
|
18
|
+
# printing the help screen, which it does by default.
|
19
|
+
def self.help(args, target = STDOUT, dont_exit = false)
|
20
|
+
# Set our printing target.
|
21
|
+
self.target = target
|
22
|
+
|
23
|
+
# The banner method needs to know about the passed options if it's going
|
24
|
+
# to do its magic. Only really needs :options if :banner is nil.
|
25
|
+
banner(args[:banner], args[:options])
|
26
|
+
|
27
|
+
# Run these three methods, passing in the appropriate hash element.
|
28
|
+
%w[header options footer].each do |meth|
|
29
|
+
send(meth, args[meth.to_sym])
|
30
|
+
end
|
31
|
+
|
32
|
+
# Exit. Unless you don't want to.
|
33
|
+
exit unless dont_exit
|
34
|
+
end
|
35
|
+
|
36
|
+
class <<self
|
37
|
+
private
|
38
|
+
|
39
|
+
# Print a passed banner or assemble the default banner, which is usage.
|
40
|
+
def banner(banner, options)
|
41
|
+
if banner
|
42
|
+
puts banner
|
43
|
+
else
|
44
|
+
# Usage needs to know about the defined options.
|
45
|
+
usage(options)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Print our header, which is just lines after the banner and before the
|
50
|
+
# options block. Needs an array, prints each element as a line.
|
51
|
+
def header(header)
|
52
|
+
if header.is_a?(Array) and header.size > 0
|
53
|
+
header.each { |line| puts line }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Print out the options block by going through each option and printing
|
58
|
+
# it as a line (or more). Expects an array.
|
59
|
+
def options(options)
|
60
|
+
# Do nothing if there's nothing to do.
|
61
|
+
return if options.nil? || !options.size
|
62
|
+
|
63
|
+
# If the option is a hash, run it through option_line. Otherwise
|
64
|
+
# just print it out as is.
|
65
|
+
options.each do |name, option|
|
66
|
+
if option.respond_to?(:to_h)
|
67
|
+
option_line(option.to_h)
|
68
|
+
else
|
69
|
+
puts name
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# The heavy lifting: print a line for an option. Has intimate knowledge
|
75
|
+
# of what keys are expected.
|
76
|
+
def option_line(option)
|
77
|
+
# Expect a hash
|
78
|
+
return unless option.is_a?(Hash)
|
79
|
+
|
80
|
+
# Make this easier on us
|
81
|
+
short = option['short']
|
82
|
+
long = option['long']
|
83
|
+
line = ''
|
84
|
+
|
85
|
+
# Get the short part.
|
86
|
+
line << sprintf("%#{SHORT_LENGTH}s", short)
|
87
|
+
line << sprintf("%-#{SHORT_BREAK_LENGTH}s", (',' if short && long))
|
88
|
+
|
89
|
+
# Get the long part.
|
90
|
+
line << sprintf("%-#{LONG_LENGTH}s", long)
|
91
|
+
|
92
|
+
# Print what we have so far
|
93
|
+
print line
|
94
|
+
|
95
|
+
# If there's a desc, print it.
|
96
|
+
if option['desc']
|
97
|
+
# If the line is too long, spill over to the next line
|
98
|
+
if line.length > PRE_DESC_LENGTH
|
99
|
+
puts
|
100
|
+
print " " * PRE_DESC_LENGTH
|
101
|
+
end
|
102
|
+
|
103
|
+
puts option['desc'].shift
|
104
|
+
|
105
|
+
# If there is more than one desc line, print each one in succession
|
106
|
+
# as separate lines.
|
107
|
+
option['desc'].each do |desc|
|
108
|
+
puts ' '*37 + desc
|
109
|
+
end
|
110
|
+
|
111
|
+
else
|
112
|
+
# No desc, just print a newline.
|
113
|
+
puts
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Expects an array, prints each element as a line.
|
119
|
+
def footer(footer)
|
120
|
+
footer.each { |line| puts line } unless footer.nil?
|
121
|
+
end
|
122
|
+
|
123
|
+
# Prints the usage statement, e.g. Usage prog.rb [-abc]
|
124
|
+
# Expects an array.
|
125
|
+
def usage(options)
|
126
|
+
# Really we just need an enumerable.
|
127
|
+
return unless options.respond_to?(:each)
|
128
|
+
|
129
|
+
# Start off the options with a dash.
|
130
|
+
opts = '-'
|
131
|
+
|
132
|
+
# Figure out the option shorts.
|
133
|
+
options.dup.each do |option|
|
134
|
+
# We really need an array here.
|
135
|
+
next unless option.is_a?(Array)
|
136
|
+
|
137
|
+
# Grab the hash of the last element, which should be the second
|
138
|
+
# element.
|
139
|
+
option = option.last.to_h
|
140
|
+
|
141
|
+
# Add the short to the options string.
|
142
|
+
opts << option['short'].sub('-','') if option['short']
|
143
|
+
end
|
144
|
+
|
145
|
+
# Figure out if we actually got any options.
|
146
|
+
opts = if opts =~ /^-(.+)/
|
147
|
+
" [#{opts}]"
|
148
|
+
end.to_s
|
149
|
+
|
150
|
+
# Print it out, with our newly aquired options string.
|
151
|
+
puts "Usage: #{program}" << opts
|
152
|
+
end
|
153
|
+
|
154
|
+
# Figure out the name of this program based on what was run.
|
155
|
+
def program
|
156
|
+
(/(\/|\\)/ =~ $0) ? File.basename($0) : $0
|
157
|
+
end
|
158
|
+
|
159
|
+
# Set where we print.
|
160
|
+
def target=(target)
|
161
|
+
@@target = target
|
162
|
+
end
|
163
|
+
|
164
|
+
# Where do we print?
|
165
|
+
def target
|
166
|
+
@@target
|
167
|
+
end
|
168
|
+
|
169
|
+
public
|
170
|
+
# Fake puts
|
171
|
+
def puts(str = nil)
|
172
|
+
str = '' if str.nil?
|
173
|
+
print(str + "\n")
|
174
|
+
end
|
175
|
+
|
176
|
+
# Fake printf
|
177
|
+
def printf(format, *args)
|
178
|
+
print(sprintf(format, *args))
|
179
|
+
end
|
180
|
+
|
181
|
+
# Fake print -- just add to target, which may not be STDOUT.
|
182
|
+
def print(str)
|
183
|
+
target << str
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
data/test/test_choice.rb
ADDED
@@ -0,0 +1,234 @@
|
|
1
|
+
$:.unshift "../lib:lib"
|
2
|
+
require 'test/unit'
|
3
|
+
require 'choice'
|
4
|
+
|
5
|
+
$VERBOSE = nil
|
6
|
+
|
7
|
+
class TestChoice < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
Choice.reset!
|
11
|
+
Choice.dont_exit_on_help = true
|
12
|
+
Choice.send(:class_variable_set, '@@choices', true)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_choices
|
16
|
+
Choice.options do
|
17
|
+
header "Tell me about yourself?"
|
18
|
+
header ""
|
19
|
+
option :band do
|
20
|
+
short "-b"
|
21
|
+
long "--band=BAND"
|
22
|
+
cast String
|
23
|
+
desc "Your favorite band."
|
24
|
+
validate /\w+/
|
25
|
+
end
|
26
|
+
option :animal do
|
27
|
+
short "-a"
|
28
|
+
long "--animal=ANIMAL"
|
29
|
+
cast String
|
30
|
+
desc "Your favorite animal."
|
31
|
+
end
|
32
|
+
footer ""
|
33
|
+
footer "--help This message"
|
34
|
+
end
|
35
|
+
|
36
|
+
band = 'LedZeppelin'
|
37
|
+
animal = 'Reindeer'
|
38
|
+
|
39
|
+
args = ['-b', band, "--animal=#{animal}"]
|
40
|
+
Choice.args = args
|
41
|
+
|
42
|
+
assert_equal band, Choice.choices['band']
|
43
|
+
assert_equal animal, Choice.choices[:animal]
|
44
|
+
assert_equal ["Tell me about yourself?", ""], Choice.header
|
45
|
+
assert_equal ["", "--help This message"], Choice.footer
|
46
|
+
|
47
|
+
assert_equal Choice.choices['band'], Choice['band']
|
48
|
+
assert_equal Choice.choices[:animal], Choice[:animal]
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_failed_parse
|
52
|
+
assert Hash.new, Choice.parse
|
53
|
+
end
|
54
|
+
|
55
|
+
HELP_STRING = ''
|
56
|
+
def test_help
|
57
|
+
Choice.output_to(HELP_STRING)
|
58
|
+
|
59
|
+
Choice.options do
|
60
|
+
banner "Usage: choice [-mu]"
|
61
|
+
header ""
|
62
|
+
option :meal do
|
63
|
+
short '-m'
|
64
|
+
desc 'Your favorite meal.'
|
65
|
+
end
|
66
|
+
|
67
|
+
separator ""
|
68
|
+
separator "And you eat it with..."
|
69
|
+
|
70
|
+
option :utencil do
|
71
|
+
short "-u"
|
72
|
+
long "--utencil[=UTENCIL]"
|
73
|
+
desc "Your favorite eating utencil."
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
Choice.args = ['-m', 'lunch', '--help']
|
78
|
+
|
79
|
+
help_string = <<-HELP
|
80
|
+
Usage: choice [-mu]
|
81
|
+
|
82
|
+
-m Your favorite meal.
|
83
|
+
|
84
|
+
And you eat it with...
|
85
|
+
-u, --utencil[=UTENCIL] Your favorite eating utencil.
|
86
|
+
HELP
|
87
|
+
|
88
|
+
assert_equal help_string, HELP_STRING
|
89
|
+
end
|
90
|
+
|
91
|
+
UNKNOWN_STRING = ''
|
92
|
+
def test_unknown_argument
|
93
|
+
Choice.output_to(UNKNOWN_STRING)
|
94
|
+
|
95
|
+
Choice.options do
|
96
|
+
banner "Usage: choice [-mu]"
|
97
|
+
header ""
|
98
|
+
option :meal do
|
99
|
+
short '-m'
|
100
|
+
desc 'Your favorite meal.'
|
101
|
+
end
|
102
|
+
|
103
|
+
separator ""
|
104
|
+
separator "And you eat it with..."
|
105
|
+
|
106
|
+
option :utencil do
|
107
|
+
short "-u"
|
108
|
+
long "--utencil[=UTENCIL]"
|
109
|
+
desc "Your favorite eating utencil."
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
Choice.args = ['-m', 'lunch', '--motorcycles']
|
114
|
+
|
115
|
+
help_string = <<-HELP
|
116
|
+
Usage: choice [-mu]
|
117
|
+
|
118
|
+
-m Your favorite meal.
|
119
|
+
|
120
|
+
And you eat it with...
|
121
|
+
-u, --utencil[=UTENCIL] Your favorite eating utencil.
|
122
|
+
HELP
|
123
|
+
|
124
|
+
assert_equal help_string, UNKNOWN_STRING
|
125
|
+
end
|
126
|
+
|
127
|
+
REQUIRED_STRING = ''
|
128
|
+
def test_required_argument
|
129
|
+
Choice.output_to(REQUIRED_STRING)
|
130
|
+
|
131
|
+
Choice.options do
|
132
|
+
banner "Usage: choice [-mu]"
|
133
|
+
header ""
|
134
|
+
option :meal, :required => true do
|
135
|
+
short '-m'
|
136
|
+
desc 'Your favorite meal.'
|
137
|
+
end
|
138
|
+
|
139
|
+
separator ""
|
140
|
+
separator "And you eat it with..."
|
141
|
+
|
142
|
+
option :utencil do
|
143
|
+
short "-u"
|
144
|
+
long "--utencil[=UTENCIL]"
|
145
|
+
desc "Your favorite eating utencil."
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
Choice.args = ['-u', 'spork']
|
150
|
+
|
151
|
+
help_string = <<-HELP
|
152
|
+
Usage: choice [-mu]
|
153
|
+
|
154
|
+
-m Your favorite meal.
|
155
|
+
|
156
|
+
And you eat it with...
|
157
|
+
-u, --utencil[=UTENCIL] Your favorite eating utencil.
|
158
|
+
HELP
|
159
|
+
|
160
|
+
assert_equal help_string, REQUIRED_STRING
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_shorthand_choices
|
164
|
+
Choice.options do
|
165
|
+
header "Tell me about yourself?"
|
166
|
+
header ""
|
167
|
+
options :band => { :short => "-b", :long => "--band=BAND", :cast => String, :desc => ["Your favorite band.", "Something cool."],
|
168
|
+
:validate => /\w+/ },
|
169
|
+
:animal => { :short => "-a", :long => "--animal=ANIMAL", :cast => String, :desc => "Your favorite animal." }
|
170
|
+
|
171
|
+
footer ""
|
172
|
+
footer "--help This message"
|
173
|
+
end
|
174
|
+
|
175
|
+
band = 'LedZeppelin'
|
176
|
+
animal = 'Reindeer'
|
177
|
+
|
178
|
+
args = ['-b', band, "--animal=#{animal}"]
|
179
|
+
Choice.args = args
|
180
|
+
|
181
|
+
assert_equal band, Choice.choices['band']
|
182
|
+
assert_equal animal, Choice.choices[:animal]
|
183
|
+
assert_equal ["Tell me about yourself?", ""], Choice.header
|
184
|
+
assert_equal ["", "--help This message"], Choice.footer
|
185
|
+
end
|
186
|
+
|
187
|
+
def test_args_of
|
188
|
+
suits = %w[clubs diamonds spades hearts]
|
189
|
+
stringed_numerics = (1..13).to_a.map { |a| a.to_s }
|
190
|
+
valid_cards = stringed_numerics + %w[jack queen king ace]
|
191
|
+
cards = {}
|
192
|
+
stringed_numerics.each { |n| cards[n] = n }
|
193
|
+
cards.merge!('1' => 'ace', '11' => 'jack', '12' => 'queen', '13' => 'king')
|
194
|
+
|
195
|
+
Choice.options do
|
196
|
+
header "Gambling is fun again! Pick a card and a suit (or two), then see if you win!"
|
197
|
+
header ""
|
198
|
+
header "Options:"
|
199
|
+
|
200
|
+
option :suit, :required => true do
|
201
|
+
short '-s'
|
202
|
+
long '--suit *SUITS'
|
203
|
+
desc "The suit you wish to choose. Required. You can pass in more than one, even."
|
204
|
+
desc " Valid suits: #{suits * ' '}"
|
205
|
+
valid suits
|
206
|
+
end
|
207
|
+
|
208
|
+
separator ''
|
209
|
+
|
210
|
+
option :card, :required => true do
|
211
|
+
short '-c'
|
212
|
+
long '--card CARD'
|
213
|
+
desc "The card you wish to gamble on. Required. Only one, please."
|
214
|
+
desc " Valid cards: 1 - 13, jack, queen, king, ace"
|
215
|
+
valid valid_cards
|
216
|
+
cast String
|
217
|
+
end
|
218
|
+
|
219
|
+
#cheat! to test --option=
|
220
|
+
option :autowin do
|
221
|
+
short '-a'
|
222
|
+
long '--autowin=PLAYER'
|
223
|
+
desc 'The person who should automatically win every time'
|
224
|
+
desc 'Beware: raises the suspitions of other players'
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
args = ["-c", "king", "--suit", "clubs", "diamonds", "spades", "hearts", "--autowin", "Grant"]
|
229
|
+
Choice.args = args
|
230
|
+
assert_equal ["king"], Choice.args_of("-c")
|
231
|
+
assert_equal ["clubs", "diamonds", "spades", "hearts"], Choice.args_of("--suit")
|
232
|
+
assert_equal ["Grant"], Choice.args_of("--autowin")
|
233
|
+
end
|
234
|
+
end
|