rudy 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. data/CHANGES.txt +54 -30
  2. data/README.rdoc +100 -12
  3. data/Rakefile +103 -8
  4. data/Rudyfile +119 -0
  5. data/bin/ird +175 -0
  6. data/bin/rudy +259 -156
  7. data/bin/rudy-ec2 +228 -95
  8. data/bin/rudy-s3 +76 -0
  9. data/bin/rudy-sdb +67 -0
  10. data/lib/annoy.rb +270 -0
  11. data/lib/console.rb +30 -9
  12. data/lib/escape.rb +305 -0
  13. data/lib/rudy.rb +151 -182
  14. data/lib/rudy/aws.rb +56 -49
  15. data/lib/rudy/aws/ec2.rb +47 -292
  16. data/lib/rudy/aws/ec2/address.rb +157 -0
  17. data/lib/rudy/aws/ec2/group.rb +301 -0
  18. data/lib/rudy/aws/ec2/image.rb +168 -0
  19. data/lib/rudy/aws/ec2/instance.rb +434 -0
  20. data/lib/rudy/aws/ec2/keypair.rb +104 -0
  21. data/lib/rudy/aws/ec2/snapshot.rb +98 -0
  22. data/lib/rudy/aws/ec2/volume.rb +230 -0
  23. data/lib/rudy/aws/ec2/zone.rb +77 -0
  24. data/lib/rudy/aws/s3.rb +54 -0
  25. data/lib/rudy/aws/sdb.rb +298 -0
  26. data/lib/rudy/aws/sdb/error.rb +46 -0
  27. data/lib/rudy/{metadata/backup.rb → backup.rb} +26 -51
  28. data/lib/rudy/cli.rb +157 -0
  29. data/lib/rudy/cli/aws/ec2/addresses.rb +105 -0
  30. data/lib/rudy/cli/aws/ec2/candy.rb +208 -0
  31. data/lib/rudy/cli/aws/ec2/groups.rb +121 -0
  32. data/lib/rudy/cli/aws/ec2/images.rb +196 -0
  33. data/lib/rudy/cli/aws/ec2/instances.rb +194 -0
  34. data/lib/rudy/cli/aws/ec2/keypairs.rb +53 -0
  35. data/lib/rudy/cli/aws/ec2/snapshots.rb +49 -0
  36. data/lib/rudy/cli/aws/ec2/volumes.rb +104 -0
  37. data/lib/rudy/cli/aws/ec2/zones.rb +22 -0
  38. data/lib/rudy/cli/aws/s3/buckets.rb +50 -0
  39. data/lib/rudy/cli/aws/s3/store.rb +22 -0
  40. data/lib/rudy/cli/aws/sdb/domains.rb +41 -0
  41. data/lib/rudy/cli/candy.rb +8 -0
  42. data/lib/rudy/{command → cli}/config.rb +34 -24
  43. data/lib/rudy/cli/disks.rb +35 -0
  44. data/lib/rudy/cli/machines.rb +94 -0
  45. data/lib/rudy/cli/routines.rb +57 -0
  46. data/lib/rudy/config.rb +77 -72
  47. data/lib/rudy/config/objects.rb +29 -0
  48. data/lib/rudy/disks.rb +248 -0
  49. data/lib/rudy/global.rb +121 -0
  50. data/lib/rudy/huxtable.rb +340 -0
  51. data/lib/rudy/machines.rb +245 -0
  52. data/lib/rudy/metadata.rb +123 -13
  53. data/lib/rudy/routines.rb +47 -0
  54. data/lib/rudy/routines/helpers/diskhelper.rb +101 -0
  55. data/lib/rudy/routines/helpers/scripthelper.rb +91 -0
  56. data/lib/rudy/routines/release.rb +34 -0
  57. data/lib/rudy/routines/shutdown.rb +57 -0
  58. data/lib/rudy/routines/startup.rb +58 -0
  59. data/lib/rudy/scm/svn.rb +1 -1
  60. data/lib/rudy/utils.rb +322 -4
  61. data/lib/storable.rb +26 -17
  62. data/lib/sysinfo.rb +274 -0
  63. data/lib/tryouts.rb +6 -13
  64. data/rudy.gemspec +128 -42
  65. data/support/randomize-root-password +45 -0
  66. data/support/rudy-ec2-startup +9 -9
  67. data/support/update-ec2-ami-tools +20 -0
  68. data/test/05_config/00_setup_test.rb +20 -0
  69. data/test/05_config/30_machines_test.rb +69 -0
  70. data/test/20_sdb/00_setup_test.rb +16 -0
  71. data/test/20_sdb/10_domains_test.rb +115 -0
  72. data/test/25_ec2/00_setup_test.rb +29 -0
  73. data/test/25_ec2/10_keypairs_test.rb +41 -0
  74. data/test/25_ec2/20_groups_test.rb +131 -0
  75. data/test/25_ec2/30_addresses_test.rb +38 -0
  76. data/test/25_ec2/40_volumes_test.rb +49 -0
  77. data/test/25_ec2/50_snapshots_test.rb +74 -0
  78. data/test/26_ec2_instances/00_setup_test.rb +28 -0
  79. data/test/26_ec2_instances/10_instances_test.rb +83 -0
  80. data/test/26_ec2_instances/50_images_test.rb +13 -0
  81. data/test/30_sdb_metadata/00_setup_test.rb +21 -0
  82. data/test/30_sdb_metadata/10_disks_test.rb +109 -0
  83. data/test/30_sdb_metadata/20_backups_test.rb +102 -0
  84. data/test/coverage.txt +51 -0
  85. data/test/helper.rb +36 -0
  86. data/vendor/highline-1.5.1/CHANGELOG +222 -0
  87. data/vendor/highline-1.5.1/INSTALL +35 -0
  88. data/vendor/highline-1.5.1/LICENSE +7 -0
  89. data/vendor/highline-1.5.1/README +63 -0
  90. data/vendor/highline-1.5.1/Rakefile +82 -0
  91. data/vendor/highline-1.5.1/TODO +6 -0
  92. data/vendor/highline-1.5.1/examples/ansi_colors.rb +38 -0
  93. data/vendor/highline-1.5.1/examples/asking_for_arrays.rb +18 -0
  94. data/vendor/highline-1.5.1/examples/basic_usage.rb +75 -0
  95. data/vendor/highline-1.5.1/examples/color_scheme.rb +32 -0
  96. data/vendor/highline-1.5.1/examples/limit.rb +12 -0
  97. data/vendor/highline-1.5.1/examples/menus.rb +65 -0
  98. data/vendor/highline-1.5.1/examples/overwrite.rb +19 -0
  99. data/vendor/highline-1.5.1/examples/page_and_wrap.rb +322 -0
  100. data/vendor/highline-1.5.1/examples/password.rb +7 -0
  101. data/vendor/highline-1.5.1/examples/trapping_eof.rb +22 -0
  102. data/vendor/highline-1.5.1/examples/using_readline.rb +17 -0
  103. data/vendor/highline-1.5.1/lib/highline.rb +758 -0
  104. data/vendor/highline-1.5.1/lib/highline/color_scheme.rb +120 -0
  105. data/vendor/highline-1.5.1/lib/highline/compatibility.rb +17 -0
  106. data/vendor/highline-1.5.1/lib/highline/import.rb +43 -0
  107. data/vendor/highline-1.5.1/lib/highline/menu.rb +395 -0
  108. data/vendor/highline-1.5.1/lib/highline/question.rb +463 -0
  109. data/vendor/highline-1.5.1/lib/highline/system_extensions.rb +193 -0
  110. data/vendor/highline-1.5.1/setup.rb +1360 -0
  111. data/vendor/highline-1.5.1/test/tc_color_scheme.rb +56 -0
  112. data/vendor/highline-1.5.1/test/tc_highline.rb +823 -0
  113. data/vendor/highline-1.5.1/test/tc_import.rb +54 -0
  114. data/vendor/highline-1.5.1/test/tc_menu.rb +429 -0
  115. data/vendor/highline-1.5.1/test/ts_all.rb +15 -0
  116. metadata +141 -38
  117. data/lib/aws_sdb.rb +0 -3
  118. data/lib/aws_sdb/error.rb +0 -42
  119. data/lib/aws_sdb/service.rb +0 -215
  120. data/lib/rudy/aws/simpledb.rb +0 -53
  121. data/lib/rudy/command/addresses.rb +0 -46
  122. data/lib/rudy/command/backups.rb +0 -175
  123. data/lib/rudy/command/base.rb +0 -841
  124. data/lib/rudy/command/deploy.rb +0 -12
  125. data/lib/rudy/command/disks.rb +0 -213
  126. data/lib/rudy/command/environment.rb +0 -73
  127. data/lib/rudy/command/groups.rb +0 -61
  128. data/lib/rudy/command/images.rb +0 -91
  129. data/lib/rudy/command/instances.rb +0 -85
  130. data/lib/rudy/command/machines.rb +0 -161
  131. data/lib/rudy/command/metadata.rb +0 -41
  132. data/lib/rudy/command/release.rb +0 -174
  133. data/lib/rudy/command/volumes.rb +0 -66
  134. data/lib/rudy/metadata/disk.rb +0 -138
  135. data/tryouts/console_tryout.rb +0 -91
@@ -0,0 +1,463 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # question.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-04-26.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+ #
8
+ # This is Free Software. See LICENSE and COPYING for details.
9
+
10
+ require "optparse"
11
+ require "date"
12
+ require "pathname"
13
+
14
+ class HighLine
15
+ #
16
+ # Question objects contain all the details of a single invocation of
17
+ # HighLine.ask(). The object is initialized by the parameters passed to
18
+ # HighLine.ask() and then queried to make sure each step of the input
19
+ # process is handled according to the users wishes.
20
+ #
21
+ class Question
22
+ # An internal HighLine error. User code does not need to trap this.
23
+ class NoAutoCompleteMatch < StandardError
24
+ # do nothing, just creating a unique error type
25
+ end
26
+
27
+ #
28
+ # Create an instance of HighLine::Question. Expects a _question_ to ask
29
+ # (can be <tt>""</tt>) and an _answer_type_ to convert the answer to.
30
+ # The _answer_type_ parameter must be a type recongnized by
31
+ # Question.convert(). If given, a block is yeilded the new Question
32
+ # object to allow custom initializaion.
33
+ #
34
+ def initialize( question, answer_type )
35
+ # initialize instance data
36
+ @question = question
37
+ @answer_type = answer_type
38
+
39
+ @character = nil
40
+ @limit = nil
41
+ @echo = true
42
+ @readline = false
43
+ @whitespace = :strip
44
+ @case = nil
45
+ @default = nil
46
+ @validate = nil
47
+ @above = nil
48
+ @below = nil
49
+ @in = nil
50
+ @confirm = nil
51
+ @gather = false
52
+ @first_answer = nil
53
+ @directory = Pathname.new(File.expand_path(File.dirname($0)))
54
+ @glob = "*"
55
+ @responses = Hash.new
56
+ @overwrite = false
57
+
58
+ # allow block to override settings
59
+ yield self if block_given?
60
+
61
+ # finalize responses based on settings
62
+ build_responses
63
+ end
64
+
65
+ # The ERb template of the question to be asked.
66
+ attr_accessor :question
67
+ # The type that will be used to convert this answer.
68
+ attr_accessor :answer_type
69
+ #
70
+ # Can be set to +true+ to use HighLine's cross-platform character reader
71
+ # instead of fetching an entire line of input. (Note: HighLine's
72
+ # character reader *ONLY* supports STDIN on Windows and Unix and may not
73
+ # work correctly if STDIN is redirected.) Can also be set to <tt>:getc</tt>
74
+ # to use that method on the input stream.
75
+ #
76
+ # *WARNING*: The _echo_ and _overwrite_ attributes for a question are
77
+ # ignored when using the <tt>:getc</tt> method.
78
+ #
79
+ attr_accessor :character
80
+ #
81
+ # Allows you to set a character limit for input.
82
+ #
83
+ # *WARNING*: This option forces a character by character read.
84
+ #
85
+ attr_accessor :limit
86
+ #
87
+ # Can be set to +true+ or +false+ to control whether or not input will
88
+ # be echoed back to the user. A setting of +true+ will cause echo to
89
+ # match input, but any other true value will be treated as to String to
90
+ # echo for each character typed.
91
+ #
92
+ # This requires HighLine's character reader. See the _character_
93
+ # attribute for details.
94
+ #
95
+ # *Note*: When using HighLine to manage echo on Unix based systems, we
96
+ # recommend installing the termios gem. Without it, it's possible to type
97
+ # fast enough to have letters still show up (when reading character by
98
+ # character only).
99
+ #
100
+ attr_accessor :echo
101
+ #
102
+ # Use the Readline library to fetch input. This allows input editing as
103
+ # well as keeping a history. In addition, tab will auto-complete
104
+ # within an Array of choices or a file listing.
105
+ #
106
+ # *WARNING*: This option is incompatible with all of HighLine's
107
+ # character reading modes and it causes HighLine to ignore the
108
+ # specified _input_ stream.
109
+ #
110
+ attr_accessor :readline
111
+ #
112
+ # Used to control whitespace processing for the answer to this question.
113
+ # See HighLine::Question.remove_whitespace() for acceptable settings.
114
+ #
115
+ attr_accessor :whitespace
116
+ #
117
+ # Used to control character case processing for the answer to this question.
118
+ # See HighLine::Question.change_case() for acceptable settings.
119
+ #
120
+ attr_accessor :case
121
+ # Used to provide a default answer to this question.
122
+ attr_accessor :default
123
+ #
124
+ # If set to a Regexp, the answer must match (before type conversion).
125
+ # Can also be set to a Proc which will be called with the provided
126
+ # answer to validate with a +true+ or +false+ return.
127
+ #
128
+ attr_accessor :validate
129
+ # Used to control range checks for answer.
130
+ attr_accessor :above, :below
131
+ # If set, answer must pass an include?() check on this object.
132
+ attr_accessor :in
133
+ #
134
+ # Asks a yes or no confirmation question, to ensure a user knows what
135
+ # they have just agreed to. If set to +true+ the question will be,
136
+ # "Are you sure? " Any other true value for this attribute is assumed
137
+ # to be the question to ask. When +false+ or +nil+ (the default),
138
+ # answers are not confirmed.
139
+ #
140
+ attr_accessor :confirm
141
+ #
142
+ # When set, the user will be prompted for multiple answers which will
143
+ # be collected into an Array or Hash and returned as the final answer.
144
+ #
145
+ # You can set _gather_ to an Integer to have an Array of exactly that
146
+ # many answers collected, or a String/Regexp to match an end input which
147
+ # will not be returned in the Array.
148
+ #
149
+ # Optionally _gather_ can be set to a Hash. In this case, the question
150
+ # will be asked once for each key and the answers will be returned in a
151
+ # Hash, mapped by key. The <tt>@key</tt> variable is set before each
152
+ # question is evaluated, so you can use it in your question.
153
+ #
154
+ attr_accessor :gather
155
+ #
156
+ # When set to a non *nil* value, this will be tried as an answer to the
157
+ # question. If this answer passes validations, it will become the result
158
+ # without the user ever being prompted. Otherwise this value is discarded,
159
+ # and this Question is resolved as a normal call to HighLine.ask().
160
+ #
161
+ attr_writer :first_answer
162
+ #
163
+ # The directory from which a user will be allowed to select files, when
164
+ # File or Pathname is specified as an _answer_type_. Initially set to
165
+ # <tt>Pathname.new(File.expand_path(File.dirname($0)))</tt>.
166
+ #
167
+ attr_accessor :directory
168
+ #
169
+ # The glob pattern used to limit file selection when File or Pathname is
170
+ # specified as an _answer_type_. Initially set to <tt>"*"</tt>.
171
+ #
172
+ attr_accessor :glob
173
+ #
174
+ # A Hash that stores the various responses used by HighLine to notify
175
+ # the user. The currently used responses and their purpose are as
176
+ # follows:
177
+ #
178
+ # <tt>:ambiguous_completion</tt>:: Used to notify the user of an
179
+ # ambiguous answer the auto-completion
180
+ # system cannot resolve.
181
+ # <tt>:ask_on_error</tt>:: This is the question that will be
182
+ # redisplayed to the user in the event
183
+ # of an error. Can be set to
184
+ # <tt>:question</tt> to repeat the
185
+ # original question.
186
+ # <tt>:invalid_type</tt>:: The error message shown when a type
187
+ # conversion fails.
188
+ # <tt>:no_completion</tt>:: Used to notify the user that their
189
+ # selection does not have a valid
190
+ # auto-completion match.
191
+ # <tt>:not_in_range</tt>:: Used to notify the user that a
192
+ # provided answer did not satisfy
193
+ # the range requirement tests.
194
+ # <tt>:not_valid</tt>:: The error message shown when
195
+ # validation checks fail.
196
+ #
197
+ attr_reader :responses
198
+ #
199
+ # When set to +true+ the question is asked, but output does not progress to
200
+ # the next line. The Cursor is moved back to the beginning of the question
201
+ # line and it is cleared so that all the contents of the line disappear from
202
+ # the screen.
203
+ #
204
+ attr_accessor :overwrite
205
+
206
+ #
207
+ # Returns the provided _answer_string_ or the default answer for this
208
+ # Question if a default was set and the answer is empty.
209
+ #
210
+ def answer_or_default( answer_string )
211
+ if answer_string.length == 0 and not @default.nil?
212
+ @default
213
+ else
214
+ answer_string
215
+ end
216
+ end
217
+
218
+ #
219
+ # Called late in the initialization process to build intelligent
220
+ # responses based on the details of this Question object.
221
+ #
222
+ def build_responses( )
223
+ ### WARNING: This code is quasi-duplicated in ###
224
+ ### Menu.update_responses(). Check there too when ###
225
+ ### making changes! ###
226
+ append_default unless default.nil?
227
+ @responses = { :ambiguous_completion =>
228
+ "Ambiguous choice. " +
229
+ "Please choose one of #{@answer_type.inspect}.",
230
+ :ask_on_error =>
231
+ "? ",
232
+ :invalid_type =>
233
+ "You must enter a valid #{@answer_type}.",
234
+ :no_completion =>
235
+ "You must choose one of " +
236
+ "#{@answer_type.inspect}.",
237
+ :not_in_range =>
238
+ "Your answer isn't within the expected range " +
239
+ "(#{expected_range}).",
240
+ :not_valid =>
241
+ "Your answer isn't valid (must match " +
242
+ "#{@validate.inspect})." }.merge(@responses)
243
+ ### WARNING: This code is quasi-duplicated in ###
244
+ ### Menu.update_responses(). Check there too when ###
245
+ ### making changes! ###
246
+ end
247
+
248
+ #
249
+ # Returns the provided _answer_string_ after changing character case by
250
+ # the rules of this Question. Valid settings for whitespace are:
251
+ #
252
+ # +nil+:: Do not alter character case.
253
+ # (Default.)
254
+ # <tt>:up</tt>:: Calls upcase().
255
+ # <tt>:upcase</tt>:: Calls upcase().
256
+ # <tt>:down</tt>:: Calls downcase().
257
+ # <tt>:downcase</tt>:: Calls downcase().
258
+ # <tt>:capitalize</tt>:: Calls capitalize().
259
+ #
260
+ # An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
261
+ #
262
+ def change_case( answer_string )
263
+ if [:up, :upcase].include?(@case)
264
+ answer_string.upcase
265
+ elsif [:down, :downcase].include?(@case)
266
+ answer_string.downcase
267
+ elsif @case == :capitalize
268
+ answer_string.capitalize
269
+ else
270
+ answer_string
271
+ end
272
+ end
273
+
274
+ #
275
+ # Transforms the given _answer_string_ into the expected type for this
276
+ # Question. Currently supported conversions are:
277
+ #
278
+ # <tt>[...]</tt>:: Answer must be a member of the passed Array.
279
+ # Auto-completion is used to expand partial
280
+ # answers.
281
+ # <tt>lambda {...}</tt>:: Answer is passed to lambda for conversion.
282
+ # Date:: Date.parse() is called with answer.
283
+ # DateTime:: DateTime.parse() is called with answer.
284
+ # File:: The entered file name is auto-completed in
285
+ # terms of _directory_ + _glob_, opened, and
286
+ # returned.
287
+ # Float:: Answer is converted with Kernel.Float().
288
+ # Integer:: Answer is converted with Kernel.Integer().
289
+ # +nil+:: Answer is left in String format. (Default.)
290
+ # Pathname:: Same as File, save that a Pathname object is
291
+ # returned.
292
+ # String:: Answer is converted with Kernel.String().
293
+ # Regexp:: Answer is fed to Regexp.new().
294
+ # Symbol:: The method to_sym() is called on answer and
295
+ # the result returned.
296
+ # <i>any other Class</i>:: The answer is passed on to
297
+ # <tt>Class.parse()</tt>.
298
+ #
299
+ # This method throws ArgumentError, if the conversion cannot be
300
+ # completed for any reason.
301
+ #
302
+ def convert( answer_string )
303
+ if @answer_type.nil?
304
+ answer_string
305
+ elsif [Float, Integer, String].include?(@answer_type)
306
+ Kernel.send(@answer_type.to_s.to_sym, answer_string)
307
+ elsif @answer_type == Symbol
308
+ answer_string.to_sym
309
+ elsif @answer_type == Regexp
310
+ Regexp.new(answer_string)
311
+ elsif @answer_type.is_a?(Array) or [File, Pathname].include?(@answer_type)
312
+ # cheating, using OptionParser's Completion module
313
+ choices = selection
314
+ choices.extend(OptionParser::Completion)
315
+ answer = choices.complete(answer_string)
316
+ if answer.nil?
317
+ raise NoAutoCompleteMatch
318
+ end
319
+ if @answer_type.is_a?(Array)
320
+ answer.last
321
+ elsif @answer_type == File
322
+ File.open(File.join(@directory.to_s, answer.last))
323
+ else
324
+ Pathname.new(File.join(@directory.to_s, answer.last))
325
+ end
326
+ elsif [Date, DateTime].include?(@answer_type) or @answer_type.is_a?(Class)
327
+ @answer_type.parse(answer_string)
328
+ elsif @answer_type.is_a?(Proc)
329
+ @answer_type[answer_string]
330
+ end
331
+ end
332
+
333
+ # Returns a english explination of the current range settings.
334
+ def expected_range( )
335
+ expected = [ ]
336
+
337
+ expected << "above #{@above}" unless @above.nil?
338
+ expected << "below #{@below}" unless @below.nil?
339
+ expected << "included in #{@in.inspect}" unless @in.nil?
340
+
341
+ case expected.size
342
+ when 0 then ""
343
+ when 1 then expected.first
344
+ when 2 then expected.join(" and ")
345
+ else expected[0..-2].join(", ") + ", and #{expected.last}"
346
+ end
347
+ end
348
+
349
+ # Returns _first_answer_, which will be unset following this call.
350
+ def first_answer( )
351
+ @first_answer
352
+ ensure
353
+ @first_answer = nil
354
+ end
355
+
356
+ # Returns true if _first_answer_ is set.
357
+ def first_answer?( )
358
+ not @first_answer.nil?
359
+ end
360
+
361
+ #
362
+ # Returns +true+ if the _answer_object_ is greater than the _above_
363
+ # attribute, less than the _below_ attribute and included?()ed in the
364
+ # _in_ attribute. Otherwise, +false+ is returned. Any +nil+ attributes
365
+ # are not checked.
366
+ #
367
+ def in_range?( answer_object )
368
+ (@above.nil? or answer_object > @above) and
369
+ (@below.nil? or answer_object < @below) and
370
+ (@in.nil? or @in.include?(answer_object))
371
+ end
372
+
373
+ #
374
+ # Returns the provided _answer_string_ after processing whitespace by
375
+ # the rules of this Question. Valid settings for whitespace are:
376
+ #
377
+ # +nil+:: Do not alter whitespace.
378
+ # <tt>:strip</tt>:: Calls strip(). (Default.)
379
+ # <tt>:chomp</tt>:: Calls chomp().
380
+ # <tt>:collapse</tt>:: Collapses all whitspace runs to a
381
+ # single space.
382
+ # <tt>:strip_and_collapse</tt>:: Calls strip(), then collapses all
383
+ # whitspace runs to a single space.
384
+ # <tt>:chomp_and_collapse</tt>:: Calls chomp(), then collapses all
385
+ # whitspace runs to a single space.
386
+ # <tt>:remove</tt>:: Removes all whitespace.
387
+ #
388
+ # An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
389
+ #
390
+ # This process is skipped, for single character input.
391
+ #
392
+ def remove_whitespace( answer_string )
393
+ if @whitespace.nil?
394
+ answer_string
395
+ elsif [:strip, :chomp].include?(@whitespace)
396
+ answer_string.send(@whitespace)
397
+ elsif @whitespace == :collapse
398
+ answer_string.gsub(/\s+/, " ")
399
+ elsif [:strip_and_collapse, :chomp_and_collapse].include?(@whitespace)
400
+ result = answer_string.send(@whitespace.to_s[/^[a-z]+/])
401
+ result.gsub(/\s+/, " ")
402
+ elsif @whitespace == :remove
403
+ answer_string.gsub(/\s+/, "")
404
+ else
405
+ answer_string
406
+ end
407
+ end
408
+
409
+ #
410
+ # Returns an Array of valid answers to this question. These answers are
411
+ # only known when _answer_type_ is set to an Array of choices, File, or
412
+ # Pathname. Any other time, this method will return an empty Array.
413
+ #
414
+ def selection( )
415
+ if @answer_type.is_a?(Array)
416
+ @answer_type
417
+ elsif [File, Pathname].include?(@answer_type)
418
+ Dir[File.join(@directory.to_s, @glob)].map do |file|
419
+ File.basename(file)
420
+ end
421
+ else
422
+ [ ]
423
+ end
424
+ end
425
+
426
+ # Stringifies the question to be asked.
427
+ def to_str( )
428
+ @question
429
+ end
430
+
431
+ #
432
+ # Returns +true+ if the provided _answer_string_ is accepted by the
433
+ # _validate_ attribute or +false+ if it's not.
434
+ #
435
+ # It's important to realize that an answer is validated after whitespace
436
+ # and case handling.
437
+ #
438
+ def valid_answer?( answer_string )
439
+ @validate.nil? or
440
+ (@validate.is_a?(Regexp) and answer_string =~ @validate) or
441
+ (@validate.is_a?(Proc) and @validate[answer_string])
442
+ end
443
+
444
+ private
445
+
446
+ #
447
+ # Adds the default choice to the end of question between <tt>|...|</tt>.
448
+ # Trailing whitespace is preserved so the function of HighLine.say() is
449
+ # not affected.
450
+ #
451
+ def append_default( )
452
+ if @question =~ /([\t ]+)\Z/
453
+ @question << "|#{@default}|#{$1}"
454
+ elsif @question == ""
455
+ @question << "|#{@default}| "
456
+ elsif @question[-1, 1] == "\n"
457
+ @question[-2, 0] = " |#{@default}|"
458
+ else
459
+ @question << " |#{@default}|"
460
+ end
461
+ end
462
+ end
463
+ end