highline 1.0.1 → 1.0.2

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.
@@ -10,421 +10,418 @@ require "date"
10
10
  require "pathname"
11
11
 
12
12
  class HighLine
13
- #
14
- # Question objects contain all the details of a single invocation of
15
- # HighLine.ask(). The object is initialized by the parameters passed to
16
- # HighLine.ask() and then queried to make sure each step of the input
17
- # process is handled according to the users wishes.
18
- #
19
- class Question
20
- # An internal HighLine error. User code does not need to trap this.
21
- class NoAutoCompleteMatch < StandardError
22
- # do nothing, just creating a unique error type
23
- end
13
+ #
14
+ # Question objects contain all the details of a single invocation of
15
+ # HighLine.ask(). The object is initialized by the parameters passed to
16
+ # HighLine.ask() and then queried to make sure each step of the input
17
+ # process is handled according to the users wishes.
18
+ #
19
+ class Question
20
+ # An internal HighLine error. User code does not need to trap this.
21
+ class NoAutoCompleteMatch < StandardError
22
+ # do nothing, just creating a unique error type
23
+ end
24
24
 
25
- #
26
- # Create an instance of HighLine::Question. Expects a _question_ to ask
27
- # (can be <tt>""</tt>) and an _answer_type_ to convert the answer to.
28
- # The _answer_type_ parameter must be a type recongnized by
29
- # Question.convert(). If given, a block is yeilded the new Question
30
- # object to allow custom initializaion.
31
- #
32
- def initialize( question, answer_type )
33
- # initialize instance data
34
- @question = question
35
- @answer_type = answer_type
36
-
37
- @character = nil
38
- @limit = nil
39
- @echo = true
40
- @readline = false
41
- @whitespace = :strip
42
- @case = nil
43
- @default = nil
44
- @validate = nil
45
- @above = nil
46
- @below = nil
47
- @in = nil
48
- @confirm = nil
49
- @gather = false
50
- @directory = Pathname.new(File.expand_path(File.dirname($0)))
51
- @glob = "*"
52
- @responses = Hash.new
53
-
54
- # allow block to override settings
55
- yield self if block_given?
25
+ #
26
+ # Create an instance of HighLine::Question. Expects a _question_ to ask
27
+ # (can be <tt>""</tt>) and an _answer_type_ to convert the answer to.
28
+ # The _answer_type_ parameter must be a type recongnized by
29
+ # Question.convert(). If given, a block is yeilded the new Question
30
+ # object to allow custom initializaion.
31
+ #
32
+ def initialize( question, answer_type )
33
+ # initialize instance data
34
+ @question = question
35
+ @answer_type = answer_type
36
+
37
+ @character = nil
38
+ @limit = nil
39
+ @echo = true
40
+ @readline = false
41
+ @whitespace = :strip
42
+ @case = nil
43
+ @default = nil
44
+ @validate = nil
45
+ @above = nil
46
+ @below = nil
47
+ @in = nil
48
+ @confirm = nil
49
+ @gather = false
50
+ @directory = Pathname.new(File.expand_path(File.dirname($0)))
51
+ @glob = "*"
52
+ @responses = Hash.new
53
+
54
+ # allow block to override settings
55
+ yield self if block_given?
56
56
 
57
- # finalize responses based on settings
58
- build_responses
59
- end
60
-
61
- # The ERb template of the question to be asked.
62
- attr_accessor :question
63
- # The type that will be used to convert this answer.
64
- attr_accessor :answer_type
65
- #
66
- # Can be set to +true+ to use HighLine's cross-platform character reader
67
- # instead of fetching an entire line of input. (Note: HighLine's
68
- # character reader *ONLY* supports STDIN on Windows and Unix.) Can also
69
- # be set to <tt>:getc</tt> to use that method on the input stream.
70
- #
71
- # *WARNING*: The _echo_ attribute for a question is ignored when using
72
- # thw <tt>:getc</tt> method.
73
- #
74
- attr_accessor :character
75
- #
76
- # Allows you to set a character limit for input.
77
- #
78
- # *WARNING*: This option forces a character by character read.
79
- #
80
- attr_accessor :limit
81
- #
82
- # Can be set to +true+ or +false+ to control whether or not input will
83
- # be echoed back to the user. A setting of +true+ will cause echo to
84
- # match input, but any other true value will be treated as to String to
85
- # echo for each character typed.
86
- #
87
- # This requires HighLine's character reader. See the _character_
88
- # attribute for details.
89
- #
90
- attr_accessor :echo
91
- #
92
- # Use the Readline library to fetch input. This allows input editing as
93
- # well as keeping a history. In addition, tab will auto-complete
94
- # within an Array of choices or a file listing.
95
- #
96
- # *WARNING*: This option is incompatible with all of HighLine's
97
- # character reading modes and it causes HighLine to ignore the
98
- # specified _input_ stream.
99
- #
100
- attr_accessor :readline
101
- #
102
- # Used to control whitespace processing for the answer to this question.
103
- # See HighLine::Question.remove_whitespace() for acceptable settings.
104
- #
105
- attr_accessor :whitespace
106
- #
107
- # Used to control whitespace processing for the answer to this question.
108
- # See HighLine::Question.change_case() for acceptable settings.
109
- #
110
- attr_accessor :case
111
- # Used to provide a default answer to this question.
112
- attr_accessor :default
113
- #
114
- # If set to a Regexp, the answer must match (before type conversion).
115
- # Can also be set to a Proc which will be called with the provided
116
- # answer to validate with a +true+ or +false+ return.
117
- #
118
- attr_accessor :validate
119
- # Used to control range checks for answer.
120
- attr_accessor :above, :below
121
- # If set, answer must pass an include?() check on this object.
122
- attr_accessor :in
123
- #
124
- # Asks a yes or no confirmation question, to ensure a user knows what
125
- # they have just agreed to. If set to +true+ the question will be,
126
- # "Are you sure? " Any other true value for this attribute is assumed
127
- # to be the question to ask. When +false+ or +nil+ (the default),
128
- # answers are not confirmed.
129
- #
130
- attr_accessor :confirm
131
- #
132
- # When set, the user will be prompted for multiple answers which will
133
- # be collected into an Array or Hash and returned as the final answer.
134
- #
135
- # You can set _gather_ to an Integer to have an Array of exactly that
136
- # many answers collected, or a String/Regexp to match an end input which
137
- # will not be returned in the Array.
138
- #
139
- # Optionally _gather_ can be set to a Hash. In this case, the question
140
- # will be asked once for each key and the answers will be returned in a
141
- # Hash, mapped by key. The <tt>@key</tt> variable is set before each
142
- # question is evaluated, so you can use it in your question.
143
- #
144
- attr_accessor :gather
145
- #
146
- # The directory from which a user will be allowed to select files, when
147
- # File or Pathname is specified as an _answer_type_. Initially set to
148
- # <tt>Pathname.new(File.expand_path(File.dirname($0)))</tt>.
149
- #
150
- attr_accessor :directory
151
- #
152
- # The glob pattern used to limit file selection when File or Pathname is
153
- # specified as an _answer_type_. Initially set to <tt>"*"</tt>.
154
- #
155
- attr_accessor :glob
156
- #
157
- # A Hash that stores the various responses used by HighLine to notify
158
- # the user. The currently used responses and their purpose are as
159
- # follows:
160
- #
161
- # <tt>:ambiguous_completion</tt>:: Used to notify the user of an
162
- # ambiguous answer the auto-completion
163
- # system cannot resolve.
164
- # <tt>:ask_on_error</tt>:: This is the question that will be
165
- # redisplayed to the user in the event
166
- # of an error. Can be set to
167
- # <tt>:question</tt> to repeat the
168
- # original question.
169
- # <tt>:invalid_type</tt>:: The error message shown when a type
170
- # conversion fails.
171
- # <tt>:no_completion</tt>:: Used to notify the user that their
172
- # selection does not have a valid
173
- # auto-completion match.
174
- # <tt>:not_in_range</tt>:: Used to notify the user that a
175
- # provided answer did not satisfy
176
- # the range requirement tests.
177
- # <tt>:not_valid</tt>:: The error message shown when
178
- # validation checks fail.
179
- #
180
- attr_reader :responses
181
-
182
- #
183
- # Returns the provided _answer_string_ or the default answer for this
184
- # Question if a default was set and the answer is empty.
185
- #
186
- def answer_or_default( answer_string )
187
- if answer_string.length == 0 and not @default.nil?
188
- @default
189
- else
190
- answer_string
191
- end
192
- end
193
-
194
- #
195
- # Called late in the initialization process to build intelligent
196
- # responses based on the details of this Question object.
197
- #
198
- def build_responses( )
199
- ### WARNING: This code is quasi-duplicated in ###
200
- ### Menu.update_responses(). Check there too when ###
201
- ### making changes! ###
202
- append_default unless default.nil?
203
- @responses = { :ambiguous_completion =>
204
- "Ambiguous choice. " +
205
- "Please choose one of #{@answer_type.inspect}.",
206
- :ask_on_error =>
207
- "? ",
208
- :invalid_type =>
209
- "You must enter a valid #{@answer_type}.",
210
- :no_completion =>
211
- "You must choose one of " +
212
- "#{@answer_type.inspect}.",
213
- :not_in_range =>
214
- "Your answer isn't within the expected range " +
215
- "(#{expected_range}).",
216
- :not_valid =>
217
- "Your answer isn't valid (must match " +
218
- "#{@validate.inspect})." }.merge(@responses)
219
- ### WARNING: This code is quasi-duplicated in ###
220
- ### Menu.update_responses(). Check there too when ###
221
- ### making changes! ###
222
- end
223
-
224
- #
225
- # Returns the provided _answer_string_ after changing character case by
226
- # the rules of this Question. Valid settings for whitespace are:
227
- #
228
- # +nil+:: Do not alter character case.
229
- # (Default.)
230
- # <tt>:up</tt>:: Calls upcase().
231
- # <tt>:upcase</tt>:: Calls upcase().
232
- # <tt>:down</tt>:: Calls downcase().
233
- # <tt>:downcase</tt>:: Calls downcase().
234
- # <tt>:capitalize</tt>:: Calls capitalize().
235
- #
236
- # An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
237
- #
238
- def change_case( answer_string )
239
- if [:up, :upcase].include?(@case)
240
- answer_string.upcase
241
- elsif [:down, :downcase].include?(@case)
242
- answer_string.downcase
243
- elsif @case == :capitalize
244
- answer_string.capitalize
245
- else
246
- answer_string
247
- end
248
- end
57
+ # finalize responses based on settings
58
+ build_responses
59
+ end
60
+
61
+ # The ERb template of the question to be asked.
62
+ attr_accessor :question
63
+ # The type that will be used to convert this answer.
64
+ attr_accessor :answer_type
65
+ #
66
+ # Can be set to +true+ to use HighLine's cross-platform character reader
67
+ # instead of fetching an entire line of input. (Note: HighLine's
68
+ # character reader *ONLY* supports STDIN on Windows and Unix.) Can also
69
+ # be set to <tt>:getc</tt> to use that method on the input stream.
70
+ #
71
+ # *WARNING*: The _echo_ attribute for a question is ignored when using
72
+ # thw <tt>:getc</tt> method.
73
+ #
74
+ attr_accessor :character
75
+ #
76
+ # Allows you to set a character limit for input.
77
+ #
78
+ # *WARNING*: This option forces a character by character read.
79
+ #
80
+ attr_accessor :limit
81
+ #
82
+ # Can be set to +true+ or +false+ to control whether or not input will
83
+ # be echoed back to the user. A setting of +true+ will cause echo to
84
+ # match input, but any other true value will be treated as to String to
85
+ # echo for each character typed.
86
+ #
87
+ # This requires HighLine's character reader. See the _character_
88
+ # attribute for details.
89
+ #
90
+ attr_accessor :echo
91
+ #
92
+ # Use the Readline library to fetch input. This allows input editing as
93
+ # well as keeping a history. In addition, tab will auto-complete
94
+ # within an Array of choices or a file listing.
95
+ #
96
+ # *WARNING*: This option is incompatible with all of HighLine's
97
+ # character reading modes and it causes HighLine to ignore the
98
+ # specified _input_ stream.
99
+ #
100
+ attr_accessor :readline
101
+ #
102
+ # Used to control whitespace processing for the answer to this question.
103
+ # See HighLine::Question.remove_whitespace() for acceptable settings.
104
+ #
105
+ attr_accessor :whitespace
106
+ #
107
+ # Used to control whitespace processing for the answer to this question.
108
+ # See HighLine::Question.change_case() for acceptable settings.
109
+ #
110
+ attr_accessor :case
111
+ # Used to provide a default answer to this question.
112
+ attr_accessor :default
113
+ #
114
+ # If set to a Regexp, the answer must match (before type conversion).
115
+ # Can also be set to a Proc which will be called with the provided
116
+ # answer to validate with a +true+ or +false+ return.
117
+ #
118
+ attr_accessor :validate
119
+ # Used to control range checks for answer.
120
+ attr_accessor :above, :below
121
+ # If set, answer must pass an include?() check on this object.
122
+ attr_accessor :in
123
+ #
124
+ # Asks a yes or no confirmation question, to ensure a user knows what
125
+ # they have just agreed to. If set to +true+ the question will be,
126
+ # "Are you sure? " Any other true value for this attribute is assumed
127
+ # to be the question to ask. When +false+ or +nil+ (the default),
128
+ # answers are not confirmed.
129
+ #
130
+ attr_accessor :confirm
131
+ #
132
+ # When set, the user will be prompted for multiple answers which will
133
+ # be collected into an Array or Hash and returned as the final answer.
134
+ #
135
+ # You can set _gather_ to an Integer to have an Array of exactly that
136
+ # many answers collected, or a String/Regexp to match an end input which
137
+ # will not be returned in the Array.
138
+ #
139
+ # Optionally _gather_ can be set to a Hash. In this case, the question
140
+ # will be asked once for each key and the answers will be returned in a
141
+ # Hash, mapped by key. The <tt>@key</tt> variable is set before each
142
+ # question is evaluated, so you can use it in your question.
143
+ #
144
+ attr_accessor :gather
145
+ #
146
+ # The directory from which a user will be allowed to select files, when
147
+ # File or Pathname is specified as an _answer_type_. Initially set to
148
+ # <tt>Pathname.new(File.expand_path(File.dirname($0)))</tt>.
149
+ #
150
+ attr_accessor :directory
151
+ #
152
+ # The glob pattern used to limit file selection when File or Pathname is
153
+ # specified as an _answer_type_. Initially set to <tt>"*"</tt>.
154
+ #
155
+ attr_accessor :glob
156
+ #
157
+ # A Hash that stores the various responses used by HighLine to notify
158
+ # the user. The currently used responses and their purpose are as
159
+ # follows:
160
+ #
161
+ # <tt>:ambiguous_completion</tt>:: Used to notify the user of an
162
+ # ambiguous answer the auto-completion
163
+ # system cannot resolve.
164
+ # <tt>:ask_on_error</tt>:: This is the question that will be
165
+ # redisplayed to the user in the event
166
+ # of an error. Can be set to
167
+ # <tt>:question</tt> to repeat the
168
+ # original question.
169
+ # <tt>:invalid_type</tt>:: The error message shown when a type
170
+ # conversion fails.
171
+ # <tt>:no_completion</tt>:: Used to notify the user that their
172
+ # selection does not have a valid
173
+ # auto-completion match.
174
+ # <tt>:not_in_range</tt>:: Used to notify the user that a
175
+ # provided answer did not satisfy
176
+ # the range requirement tests.
177
+ # <tt>:not_valid</tt>:: The error message shown when
178
+ # validation checks fail.
179
+ #
180
+ attr_reader :responses
181
+
182
+ #
183
+ # Returns the provided _answer_string_ or the default answer for this
184
+ # Question if a default was set and the answer is empty.
185
+ #
186
+ def answer_or_default( answer_string )
187
+ if answer_string.length == 0 and not @default.nil?
188
+ @default
189
+ else
190
+ answer_string
191
+ end
192
+ end
193
+
194
+ #
195
+ # Called late in the initialization process to build intelligent
196
+ # responses based on the details of this Question object.
197
+ #
198
+ def build_responses( )
199
+ ### WARNING: This code is quasi-duplicated in ###
200
+ ### Menu.update_responses(). Check there too when ###
201
+ ### making changes! ###
202
+ append_default unless default.nil?
203
+ @responses = { :ambiguous_completion =>
204
+ "Ambiguous choice. " +
205
+ "Please choose one of #{@answer_type.inspect}.",
206
+ :ask_on_error =>
207
+ "? ",
208
+ :invalid_type =>
209
+ "You must enter a valid #{@answer_type}.",
210
+ :no_completion =>
211
+ "You must choose one of " +
212
+ "#{@answer_type.inspect}.",
213
+ :not_in_range =>
214
+ "Your answer isn't within the expected range " +
215
+ "(#{expected_range}).",
216
+ :not_valid =>
217
+ "Your answer isn't valid (must match " +
218
+ "#{@validate.inspect})." }.merge(@responses)
219
+ ### WARNING: This code is quasi-duplicated in ###
220
+ ### Menu.update_responses(). Check there too when ###
221
+ ### making changes! ###
222
+ end
223
+
224
+ #
225
+ # Returns the provided _answer_string_ after changing character case by
226
+ # the rules of this Question. Valid settings for whitespace are:
227
+ #
228
+ # +nil+:: Do not alter character case.
229
+ # (Default.)
230
+ # <tt>:up</tt>:: Calls upcase().
231
+ # <tt>:upcase</tt>:: Calls upcase().
232
+ # <tt>:down</tt>:: Calls downcase().
233
+ # <tt>:downcase</tt>:: Calls downcase().
234
+ # <tt>:capitalize</tt>:: Calls capitalize().
235
+ #
236
+ # An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
237
+ #
238
+ def change_case( answer_string )
239
+ if [:up, :upcase].include?(@case)
240
+ answer_string.upcase
241
+ elsif [:down, :downcase].include?(@case)
242
+ answer_string.downcase
243
+ elsif @case == :capitalize
244
+ answer_string.capitalize
245
+ else
246
+ answer_string
247
+ end
248
+ end
249
249
 
250
- #
251
- # Transforms the given _answer_string_ into the expected type for this
252
- # Question. Currently supported conversions are:
253
- #
254
- # <tt>[...]</tt>:: Answer must be a member of the passed Array.
255
- # Auto-completion is used to expand partial
256
- # answers.
257
- # <tt>lambda {...}</tt>:: Answer is passed to lambda for conversion.
258
- # Date:: Date.parse() is called with answer.
259
- # DateTime:: DateTime.parse() is called with answer.
260
- # File:: The entered file name is auto-completed in
261
- # terms of _directory_ + _glob_, opened, and
262
- # returned.
263
- # Float:: Answer is converted with Kernel.Float().
264
- # Integer:: Answer is converted with Kernel.Integer().
265
- # +nil+:: Answer is left in String format. (Default.)
266
- # Pathname:: Same as File, save that a Pathname object is
267
- # returned.
268
- # String:: Answer is converted with Kernel.String().
269
- # Regexp:: Answer is fed to Regexp.new().
270
- # Symbol:: The method to_sym() is called on answer and
271
- # the result returned.
272
- # <i>any other Class</i>:: The answer is passed on to
273
- # <tt>Class.parse()</tt>.
274
- #
275
- # This method throws ArgumentError, if the conversion cannot be
276
- # completed for any reason.
277
- #
278
- def convert( answer_string )
279
- if @answer_type.nil?
280
- answer_string
281
- elsif [Float, Integer, String].include?(@answer_type)
282
- Kernel.send(@answer_type.to_s.to_sym, answer_string)
283
- elsif @answer_type == Symbol
284
- answer_string.to_sym
285
- elsif @answer_type == Regexp
286
- Regexp.new(answer_string)
287
- elsif @answer_type.is_a?(Array) or
288
- [File, Pathname].include?(@answer_type)
289
- # cheating, using OptionParser's Completion module
290
- choices = selection
291
- choices.extend(OptionParser::Completion)
292
- answer = choices.complete(answer_string)
293
- if answer.nil?
294
- raise NoAutoCompleteMatch
295
- end
296
- if @answer_type.is_a?(Array)
297
- answer.last
298
- elsif @answer_type == File
299
- File.open(File.join(@directory.to_s, answer.last))
300
- else
301
- Pathname.new(File.join(@directory.to_s, answer.last))
302
- end
303
- elsif [Date, DateTime].include?(@answer_type) or
304
- @answer_type.is_a?(Class)
305
- @answer_type.parse(answer_string)
306
- elsif @answer_type.is_a?(Proc)
307
- @answer_type[answer_string]
308
- end
309
- end
250
+ #
251
+ # Transforms the given _answer_string_ into the expected type for this
252
+ # Question. Currently supported conversions are:
253
+ #
254
+ # <tt>[...]</tt>:: Answer must be a member of the passed Array.
255
+ # Auto-completion is used to expand partial
256
+ # answers.
257
+ # <tt>lambda {...}</tt>:: Answer is passed to lambda for conversion.
258
+ # Date:: Date.parse() is called with answer.
259
+ # DateTime:: DateTime.parse() is called with answer.
260
+ # File:: The entered file name is auto-completed in
261
+ # terms of _directory_ + _glob_, opened, and
262
+ # returned.
263
+ # Float:: Answer is converted with Kernel.Float().
264
+ # Integer:: Answer is converted with Kernel.Integer().
265
+ # +nil+:: Answer is left in String format. (Default.)
266
+ # Pathname:: Same as File, save that a Pathname object is
267
+ # returned.
268
+ # String:: Answer is converted with Kernel.String().
269
+ # Regexp:: Answer is fed to Regexp.new().
270
+ # Symbol:: The method to_sym() is called on answer and
271
+ # the result returned.
272
+ # <i>any other Class</i>:: The answer is passed on to
273
+ # <tt>Class.parse()</tt>.
274
+ #
275
+ # This method throws ArgumentError, if the conversion cannot be
276
+ # completed for any reason.
277
+ #
278
+ def convert( answer_string )
279
+ if @answer_type.nil?
280
+ answer_string
281
+ elsif [Float, Integer, String].include?(@answer_type)
282
+ Kernel.send(@answer_type.to_s.to_sym, answer_string)
283
+ elsif @answer_type == Symbol
284
+ answer_string.to_sym
285
+ elsif @answer_type == Regexp
286
+ Regexp.new(answer_string)
287
+ elsif @answer_type.is_a?(Array) or [File, Pathname].include?(@answer_type)
288
+ # cheating, using OptionParser's Completion module
289
+ choices = selection
290
+ choices.extend(OptionParser::Completion)
291
+ answer = choices.complete(answer_string)
292
+ if answer.nil?
293
+ raise NoAutoCompleteMatch
294
+ end
295
+ if @answer_type.is_a?(Array)
296
+ answer.last
297
+ elsif @answer_type == File
298
+ File.open(File.join(@directory.to_s, answer.last))
299
+ else
300
+ Pathname.new(File.join(@directory.to_s, answer.last))
301
+ end
302
+ elsif [Date, DateTime].include?(@answer_type) or @answer_type.is_a?(Class)
303
+ @answer_type.parse(answer_string)
304
+ elsif @answer_type.is_a?(Proc)
305
+ @answer_type[answer_string]
306
+ end
307
+ end
310
308
 
311
- # Returns a english explination of the current range settings.
312
- def expected_range( )
313
- expected = [ ]
309
+ # Returns a english explination of the current range settings.
310
+ def expected_range( )
311
+ expected = [ ]
314
312
 
315
- expected << "above #{@above}" unless @above.nil?
316
- expected << "below #{@below}" unless @below.nil?
317
- expected << "included in #{@in.inspect}" unless @in.nil?
313
+ expected << "above #{@above}" unless @above.nil?
314
+ expected << "below #{@below}" unless @below.nil?
315
+ expected << "included in #{@in.inspect}" unless @in.nil?
318
316
 
319
- case expected.size
320
- when 0 then ""
321
- when 1 then expected.first
322
- when 2 then expected.join(" and ")
323
- else expected[0..-2].join(", ") + ", and #{expected.last}"
324
- end
325
- end
326
-
327
- #
328
- # Returns +true+ if the _answer_object_ is greater than the _above_
329
- # attribute, less than the _below_ attribute and included?()ed in the
330
- # _in_ attribute. Otherwise, +false+ is returned. Any +nil+ attributes
331
- # are not checked.
332
- #
333
- def in_range?( answer_object )
334
- (@above.nil? or answer_object > @above) and
335
- (@below.nil? or answer_object < @below) and
336
- (@in.nil? or @in.include?(answer_object))
337
- end
338
-
339
- #
340
- # Returns the provided _answer_string_ after processing whitespace by
341
- # the rules of this Question. Valid settings for whitespace are:
342
- #
343
- # +nil+:: Do not alter whitespace.
344
- # <tt>:strip</tt>:: Calls strip(). (Default.)
345
- # <tt>:chomp</tt>:: Calls chomp().
346
- # <tt>:collapse</tt>:: Collapses all whitspace runs to a
347
- # single space.
348
- # <tt>:strip_and_collapse</tt>:: Calls strip(), then collapses all
349
- # whitspace runs to a single space.
350
- # <tt>:chomp_and_collapse</tt>:: Calls chomp(), then collapses all
351
- # whitspace runs to a single space.
352
- # <tt>:remove</tt>:: Removes all whitespace.
353
- #
354
- # An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
355
- #
356
- # This process is skipped, for single character input.
357
- #
358
- def remove_whitespace( answer_string )
359
- if @whitespace.nil?
360
- answer_string
361
- elsif [:strip, :chomp].include?(@whitespace)
362
- answer_string.send(@whitespace)
363
- elsif @whitespace == :collapse
364
- answer_string.gsub(/\s+/, " ")
365
- elsif [ :strip_and_collapse,
366
- :chomp_and_collapse ].include?(@whitespace)
367
- result = answer_string.send(@whitespace.to_s[/^[a-z]+/])
368
- result.gsub(/\s+/, " ")
369
- elsif @whitespace == :remove
370
- answer_string.gsub(/\s+/, "")
371
- else
372
- answer_string
373
- end
374
- end
317
+ case expected.size
318
+ when 0 then ""
319
+ when 1 then expected.first
320
+ when 2 then expected.join(" and ")
321
+ else expected[0..-2].join(", ") + ", and #{expected.last}"
322
+ end
323
+ end
324
+
325
+ #
326
+ # Returns +true+ if the _answer_object_ is greater than the _above_
327
+ # attribute, less than the _below_ attribute and included?()ed in the
328
+ # _in_ attribute. Otherwise, +false+ is returned. Any +nil+ attributes
329
+ # are not checked.
330
+ #
331
+ def in_range?( answer_object )
332
+ (@above.nil? or answer_object > @above) and
333
+ (@below.nil? or answer_object < @below) and
334
+ (@in.nil? or @in.include?(answer_object))
335
+ end
336
+
337
+ #
338
+ # Returns the provided _answer_string_ after processing whitespace by
339
+ # the rules of this Question. Valid settings for whitespace are:
340
+ #
341
+ # +nil+:: Do not alter whitespace.
342
+ # <tt>:strip</tt>:: Calls strip(). (Default.)
343
+ # <tt>:chomp</tt>:: Calls chomp().
344
+ # <tt>:collapse</tt>:: Collapses all whitspace runs to a
345
+ # single space.
346
+ # <tt>:strip_and_collapse</tt>:: Calls strip(), then collapses all
347
+ # whitspace runs to a single space.
348
+ # <tt>:chomp_and_collapse</tt>:: Calls chomp(), then collapses all
349
+ # whitspace runs to a single space.
350
+ # <tt>:remove</tt>:: Removes all whitespace.
351
+ #
352
+ # An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
353
+ #
354
+ # This process is skipped, for single character input.
355
+ #
356
+ def remove_whitespace( answer_string )
357
+ if @whitespace.nil?
358
+ answer_string
359
+ elsif [:strip, :chomp].include?(@whitespace)
360
+ answer_string.send(@whitespace)
361
+ elsif @whitespace == :collapse
362
+ answer_string.gsub(/\s+/, " ")
363
+ elsif [:strip_and_collapse, :chomp_and_collapse].include?(@whitespace)
364
+ result = answer_string.send(@whitespace.to_s[/^[a-z]+/])
365
+ result.gsub(/\s+/, " ")
366
+ elsif @whitespace == :remove
367
+ answer_string.gsub(/\s+/, "")
368
+ else
369
+ answer_string
370
+ end
371
+ end
375
372
 
376
- #
377
- # Returns an Array of valid answers to this question. These answers are
378
- # only known when _answer_type_ is set to an Array of choices, File, or
379
- # Pathname. Any other time, this method will return an empty Array.
380
- #
381
- def selection( )
382
- if @answer_type.is_a?(Array)
383
- @answer_type
384
- elsif [File, Pathname].include?(@answer_type)
385
- Dir[File.join(@directory.to_s, @glob)].map do |file|
386
- File.basename(file)
387
- end
388
- else
389
- [ ]
390
- end
391
- end
392
-
393
- # Stringifies the question to be asked.
394
- def to_str( )
395
- @question
396
- end
373
+ #
374
+ # Returns an Array of valid answers to this question. These answers are
375
+ # only known when _answer_type_ is set to an Array of choices, File, or
376
+ # Pathname. Any other time, this method will return an empty Array.
377
+ #
378
+ def selection( )
379
+ if @answer_type.is_a?(Array)
380
+ @answer_type
381
+ elsif [File, Pathname].include?(@answer_type)
382
+ Dir[File.join(@directory.to_s, @glob)].map do |file|
383
+ File.basename(file)
384
+ end
385
+ else
386
+ [ ]
387
+ end
388
+ end
389
+
390
+ # Stringifies the question to be asked.
391
+ def to_str( )
392
+ @question
393
+ end
397
394
 
398
- #
399
- # Returns +true+ if the provided _answer_string_ is accepted by the
400
- # _validate_ attribute or +false+ if it's not.
401
- #
402
- # It's important to realize that an answer is validated after whitespace
403
- # and case handling.
404
- #
405
- def valid_answer?( answer_string )
406
- @validate.nil? or
407
- (@validate.is_a?(Regexp) and answer_string =~ @validate) or
408
- (@validate.is_a?(Proc) and @validate[answer_string])
409
- end
410
-
411
- private
412
-
413
- #
414
- # Adds the default choice to the end of question between <tt>|...|</tt>.
415
- # Trailing whitespace is preserved so the function of HighLine.say() is
416
- # not affected.
417
- #
418
- def append_default( )
419
- if @question =~ /([\t ]+)\Z/
420
- @question << "|#{@default}|#{$1}"
421
- elsif @question == ""
422
- @question << "|#{@default}| "
423
- elsif @question[-1, 1] == "\n"
424
- @question[-2, 0] = " |#{@default}|"
425
- else
426
- @question << " |#{@default}|"
427
- end
428
- end
429
- end
395
+ #
396
+ # Returns +true+ if the provided _answer_string_ is accepted by the
397
+ # _validate_ attribute or +false+ if it's not.
398
+ #
399
+ # It's important to realize that an answer is validated after whitespace
400
+ # and case handling.
401
+ #
402
+ def valid_answer?( answer_string )
403
+ @validate.nil? or
404
+ (@validate.is_a?(Regexp) and answer_string =~ @validate) or
405
+ (@validate.is_a?(Proc) and @validate[answer_string])
406
+ end
407
+
408
+ private
409
+
410
+ #
411
+ # Adds the default choice to the end of question between <tt>|...|</tt>.
412
+ # Trailing whitespace is preserved so the function of HighLine.say() is
413
+ # not affected.
414
+ #
415
+ def append_default( )
416
+ if @question =~ /([\t ]+)\Z/
417
+ @question << "|#{@default}|#{$1}"
418
+ elsif @question == ""
419
+ @question << "|#{@default}| "
420
+ elsif @question[-1, 1] == "\n"
421
+ @question[-2, 0] = " |#{@default}|"
422
+ else
423
+ @question << " |#{@default}|"
424
+ end
425
+ end
426
+ end
430
427
  end