ivanvc-choice 0.1.3.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 +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
|