highline 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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