rye 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/CHANGES.txt +6 -0
  2. data/README.rdoc +5 -7
  3. data/bin/rye +3 -3
  4. data/lib/rye.rb +1 -1
  5. data/lib/rye/box.rb +1 -1
  6. data/rye.gemspec +31 -1
  7. data/vendor/highline-1.5.1/CHANGELOG +222 -0
  8. data/vendor/highline-1.5.1/INSTALL +35 -0
  9. data/vendor/highline-1.5.1/LICENSE +7 -0
  10. data/vendor/highline-1.5.1/README +63 -0
  11. data/vendor/highline-1.5.1/Rakefile +82 -0
  12. data/vendor/highline-1.5.1/TODO +6 -0
  13. data/vendor/highline-1.5.1/examples/ansi_colors.rb +38 -0
  14. data/vendor/highline-1.5.1/examples/asking_for_arrays.rb +18 -0
  15. data/vendor/highline-1.5.1/examples/basic_usage.rb +75 -0
  16. data/vendor/highline-1.5.1/examples/color_scheme.rb +32 -0
  17. data/vendor/highline-1.5.1/examples/limit.rb +12 -0
  18. data/vendor/highline-1.5.1/examples/menus.rb +65 -0
  19. data/vendor/highline-1.5.1/examples/overwrite.rb +19 -0
  20. data/vendor/highline-1.5.1/examples/page_and_wrap.rb +322 -0
  21. data/vendor/highline-1.5.1/examples/password.rb +7 -0
  22. data/vendor/highline-1.5.1/examples/trapping_eof.rb +22 -0
  23. data/vendor/highline-1.5.1/examples/using_readline.rb +17 -0
  24. data/vendor/highline-1.5.1/lib/highline.rb +758 -0
  25. data/vendor/highline-1.5.1/lib/highline/color_scheme.rb +120 -0
  26. data/vendor/highline-1.5.1/lib/highline/compatibility.rb +17 -0
  27. data/vendor/highline-1.5.1/lib/highline/import.rb +43 -0
  28. data/vendor/highline-1.5.1/lib/highline/menu.rb +395 -0
  29. data/vendor/highline-1.5.1/lib/highline/question.rb +463 -0
  30. data/vendor/highline-1.5.1/lib/highline/system_extensions.rb +193 -0
  31. data/vendor/highline-1.5.1/setup.rb +1360 -0
  32. data/vendor/highline-1.5.1/test/tc_color_scheme.rb +56 -0
  33. data/vendor/highline-1.5.1/test/tc_highline.rb +823 -0
  34. data/vendor/highline-1.5.1/test/tc_import.rb +54 -0
  35. data/vendor/highline-1.5.1/test/tc_menu.rb +429 -0
  36. data/vendor/highline-1.5.1/test/ts_all.rb +15 -0
  37. metadata +31 -1
@@ -0,0 +1,7 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require "rubygems"
4
+ require "highline/import"
5
+
6
+ pass = ask("Enter your password: ") { |q| q.echo = false }
7
+ puts "Your password is #{pass}!"
@@ -0,0 +1,22 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # trapping_eof.rb
4
+ #
5
+ # Created by James Edward Gray II on 2006-02-20.
6
+ # Copyright 2006 Gray Productions. All rights reserved.
7
+
8
+ require "rubygems"
9
+ require "highline/import"
10
+
11
+ loop do
12
+ begin
13
+ name = ask("What's your name?")
14
+ break if name == "exit"
15
+ puts "Hello, #{name}!"
16
+ rescue EOFError # HighLine throws this if @input.eof?
17
+ break
18
+ end
19
+ end
20
+
21
+ puts "Goodbye, dear friend."
22
+ exit
@@ -0,0 +1,17 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # using_readline.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-07-06.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+
8
+ require "rubygems"
9
+ require "highline/import"
10
+
11
+ loop do
12
+ cmd = ask("Enter command: ", %w{save sample load reset quit}) do |q|
13
+ q.readline = true
14
+ end
15
+ say("Executing \"#{cmd}\"...")
16
+ break if cmd == "quit"
17
+ end
@@ -0,0 +1,758 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # highline.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-04-26.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+ #
8
+ # See HighLine for documentation.
9
+ #
10
+ # This is Free Software. See LICENSE and COPYING for details.
11
+
12
+ require "erb"
13
+ require "optparse"
14
+ require "stringio"
15
+ require "abbrev"
16
+ require "highline/compatibility"
17
+ require "highline/system_extensions"
18
+ require "highline/question"
19
+ require "highline/menu"
20
+ require "highline/color_scheme"
21
+
22
+
23
+ #
24
+ # A HighLine object is a "high-level line oriented" shell over an input and an
25
+ # output stream. HighLine simplifies common console interaction, effectively
26
+ # replacing puts() and gets(). User code can simply specify the question to ask
27
+ # and any details about user interaction, then leave the rest of the work to
28
+ # HighLine. When HighLine.ask() returns, you'll have the answer you requested,
29
+ # even if HighLine had to ask many times, validate results, perform range
30
+ # checking, convert types, etc.
31
+ #
32
+ class HighLine
33
+ # The version of the installed library.
34
+ VERSION = "1.5.1".freeze
35
+
36
+ # An internal HighLine error. User code does not need to trap this.
37
+ class QuestionError < StandardError
38
+ # do nothing, just creating a unique error type
39
+ end
40
+
41
+ # The setting used to disable color output.
42
+ @@use_color = true
43
+
44
+ # Pass +false+ to _setting_ to turn off HighLine's color escapes.
45
+ def self.use_color=( setting )
46
+ @@use_color = setting
47
+ end
48
+
49
+ # Returns true if HighLine is currently using color escapes.
50
+ def self.use_color?
51
+ @@use_color
52
+ end
53
+
54
+ # The setting used to disable EOF tracking.
55
+ @@track_eof = true
56
+
57
+ # Pass +false+ to _setting_ to turn off HighLine's EOF tracking.
58
+ def self.track_eof=( setting )
59
+ @@track_eof = setting
60
+ end
61
+
62
+ # Returns true if HighLine is currently tracking EOF for input.
63
+ def self.track_eof?
64
+ @@track_eof
65
+ end
66
+
67
+ # The setting used to control color schemes.
68
+ @@color_scheme = nil
69
+
70
+ # Pass ColorScheme to _setting_ to turn set a HighLine color scheme.
71
+ def self.color_scheme=( setting )
72
+ @@color_scheme = setting
73
+ end
74
+
75
+ # Returns the current color scheme.
76
+ def self.color_scheme
77
+ @@color_scheme
78
+ end
79
+
80
+ # Returns +true+ if HighLine is currently using a color scheme.
81
+ def self.using_color_scheme?
82
+ not @@color_scheme.nil?
83
+ end
84
+
85
+ #
86
+ # Embed in a String to clear all previous ANSI sequences. This *MUST* be
87
+ # done before the program exits!
88
+ #
89
+ CLEAR = "\e[0m"
90
+ # An alias for CLEAR.
91
+ RESET = CLEAR
92
+ # Erase the current line of terminal output.
93
+ ERASE_LINE = "\e[K"
94
+ # Erase the character under the cursor.
95
+ ERASE_CHAR = "\e[P"
96
+ # The start of an ANSI bold sequence.
97
+ BOLD = "\e[1m"
98
+ # The start of an ANSI dark sequence. (Terminal support uncommon.)
99
+ DARK = "\e[2m"
100
+ # The start of an ANSI underline sequence.
101
+ UNDERLINE = "\e[4m"
102
+ # An alias for UNDERLINE.
103
+ UNDERSCORE = UNDERLINE
104
+ # The start of an ANSI blink sequence. (Terminal support uncommon.)
105
+ BLINK = "\e[5m"
106
+ # The start of an ANSI reverse sequence.
107
+ REVERSE = "\e[7m"
108
+ # The start of an ANSI concealed sequence. (Terminal support uncommon.)
109
+ CONCEALED = "\e[8m"
110
+
111
+ # Set the terminal's foreground ANSI color to black.
112
+ BLACK = "\e[30m"
113
+ # Set the terminal's foreground ANSI color to red.
114
+ RED = "\e[31m"
115
+ # Set the terminal's foreground ANSI color to green.
116
+ GREEN = "\e[32m"
117
+ # Set the terminal's foreground ANSI color to yellow.
118
+ YELLOW = "\e[33m"
119
+ # Set the terminal's foreground ANSI color to blue.
120
+ BLUE = "\e[34m"
121
+ # Set the terminal's foreground ANSI color to magenta.
122
+ MAGENTA = "\e[35m"
123
+ # Set the terminal's foreground ANSI color to cyan.
124
+ CYAN = "\e[36m"
125
+ # Set the terminal's foreground ANSI color to white.
126
+ WHITE = "\e[37m"
127
+
128
+ # Set the terminal's background ANSI color to black.
129
+ ON_BLACK = "\e[40m"
130
+ # Set the terminal's background ANSI color to red.
131
+ ON_RED = "\e[41m"
132
+ # Set the terminal's background ANSI color to green.
133
+ ON_GREEN = "\e[42m"
134
+ # Set the terminal's background ANSI color to yellow.
135
+ ON_YELLOW = "\e[43m"
136
+ # Set the terminal's background ANSI color to blue.
137
+ ON_BLUE = "\e[44m"
138
+ # Set the terminal's background ANSI color to magenta.
139
+ ON_MAGENTA = "\e[45m"
140
+ # Set the terminal's background ANSI color to cyan.
141
+ ON_CYAN = "\e[46m"
142
+ # Set the terminal's background ANSI color to white.
143
+ ON_WHITE = "\e[47m"
144
+
145
+ #
146
+ # Create an instance of HighLine, connected to the streams _input_
147
+ # and _output_.
148
+ #
149
+ def initialize( input = $stdin, output = $stdout,
150
+ wrap_at = nil, page_at = nil )
151
+ @input = input
152
+ @output = output
153
+
154
+ self.wrap_at = wrap_at
155
+ self.page_at = page_at
156
+
157
+ @question = nil
158
+ @answer = nil
159
+ @menu = nil
160
+ @header = nil
161
+ @prompt = nil
162
+ @gather = nil
163
+ @answers = nil
164
+ @key = nil
165
+ end
166
+
167
+ include HighLine::SystemExtensions
168
+
169
+ # The current column setting for wrapping output.
170
+ attr_reader :wrap_at
171
+ # The current row setting for paging output.
172
+ attr_reader :page_at
173
+
174
+ #
175
+ # A shortcut to HighLine.ask() a question that only accepts "yes" or "no"
176
+ # answers ("y" and "n" are allowed) and returns +true+ or +false+
177
+ # (+true+ for "yes"). If provided a +true+ value, _character_ will cause
178
+ # HighLine to fetch a single character response. A block can be provided
179
+ # to further configure the question as in HighLine.ask()
180
+ #
181
+ # Raises EOFError if input is exhausted.
182
+ #
183
+ def agree( yes_or_no_question, character = nil )
184
+ ask(yes_or_no_question, lambda { |yn| yn.downcase[0] == ?y}) do |q|
185
+ q.validate = /\Ay(?:es)?|no?\Z/i
186
+ q.responses[:not_valid] = 'Please enter "yes" or "no".'
187
+ q.responses[:ask_on_error] = :question
188
+ q.character = character
189
+
190
+ yield q if block_given?
191
+ end
192
+ end
193
+
194
+ #
195
+ # This method is the primary interface for user input. Just provide a
196
+ # _question_ to ask the user, the _answer_type_ you want returned, and
197
+ # optionally a code block setting up details of how you want the question
198
+ # handled. See HighLine.say() for details on the format of _question_, and
199
+ # HighLine::Question for more information about _answer_type_ and what's
200
+ # valid in the code block.
201
+ #
202
+ # If <tt>@question</tt> is set before ask() is called, parameters are
203
+ # ignored and that object (must be a HighLine::Question) is used to drive
204
+ # the process instead.
205
+ #
206
+ # Raises EOFError if input is exhausted.
207
+ #
208
+ def ask( question, answer_type = String, &details ) # :yields: question
209
+ @question ||= Question.new(question, answer_type, &details)
210
+
211
+ return gather if @question.gather
212
+
213
+ # readline() needs to handle it's own output, but readline only supports
214
+ # full line reading. Therefore if @question.echo is anything but true,
215
+ # the prompt will not be issued. And we have to account for that now.
216
+ say(@question) unless (@question.readline and @question.echo == true)
217
+ begin
218
+ @answer = @question.answer_or_default(get_response)
219
+ unless @question.valid_answer?(@answer)
220
+ explain_error(:not_valid)
221
+ raise QuestionError
222
+ end
223
+
224
+ @answer = @question.convert(@answer)
225
+
226
+ if @question.in_range?(@answer)
227
+ if @question.confirm
228
+ # need to add a layer of scope to ask a question inside a
229
+ # question, without destroying instance data
230
+ context_change = self.class.new(@input, @output, @wrap_at, @page_at)
231
+ if @question.confirm == true
232
+ confirm_question = "Are you sure? "
233
+ else
234
+ # evaluate ERb under initial scope, so it will have
235
+ # access to @question and @answer
236
+ template = ERB.new(@question.confirm, nil, "%")
237
+ confirm_question = template.result(binding)
238
+ end
239
+ unless context_change.agree(confirm_question)
240
+ explain_error(nil)
241
+ raise QuestionError
242
+ end
243
+ end
244
+
245
+ @answer
246
+ else
247
+ explain_error(:not_in_range)
248
+ raise QuestionError
249
+ end
250
+ rescue QuestionError
251
+ retry
252
+ rescue ArgumentError, NameError => error
253
+ raise if error.is_a?(NoMethodError)
254
+ if error.message =~ /ambiguous/
255
+ # the assumption here is that OptionParser::Completion#complete
256
+ # (used for ambiguity resolution) throws exceptions containing
257
+ # the word 'ambiguous' whenever resolution fails
258
+ explain_error(:ambiguous_completion)
259
+ else
260
+ explain_error(:invalid_type)
261
+ end
262
+ retry
263
+ rescue Question::NoAutoCompleteMatch
264
+ explain_error(:no_completion)
265
+ retry
266
+ ensure
267
+ @question = nil # Reset Question object.
268
+ end
269
+ end
270
+
271
+ #
272
+ # This method is HighLine's menu handler. For simple usage, you can just
273
+ # pass all the menu items you wish to display. At that point, choose() will
274
+ # build and display a menu, walk the user through selection, and return
275
+ # their choice amoung the provided items. You might use this in a case
276
+ # statement for quick and dirty menus.
277
+ #
278
+ # However, choose() is capable of much more. If provided, a block will be
279
+ # passed a HighLine::Menu object to configure. Using this method, you can
280
+ # customize all the details of menu handling from index display, to building
281
+ # a complete shell-like menuing system. See HighLine::Menu for all the
282
+ # methods it responds to.
283
+ #
284
+ # Raises EOFError if input is exhausted.
285
+ #
286
+ def choose( *items, &details )
287
+ @menu = @question = Menu.new(&details)
288
+ @menu.choices(*items) unless items.empty?
289
+
290
+ # Set _answer_type_ so we can double as the Question for ask().
291
+ @menu.answer_type = if @menu.shell
292
+ lambda do |command| # shell-style selection
293
+ first_word = command.to_s.split.first || ""
294
+
295
+ options = @menu.options
296
+ options.extend(OptionParser::Completion)
297
+ answer = options.complete(first_word)
298
+
299
+ if answer.nil?
300
+ raise Question::NoAutoCompleteMatch
301
+ end
302
+
303
+ [answer.last, command.sub(/^\s*#{first_word}\s*/, "")]
304
+ end
305
+ else
306
+ @menu.options # normal menu selection, by index or name
307
+ end
308
+
309
+ # Provide hooks for ERb layouts.
310
+ @header = @menu.header
311
+ @prompt = @menu.prompt
312
+
313
+ if @menu.shell
314
+ selected = ask("Ignored", @menu.answer_type)
315
+ @menu.select(self, *selected)
316
+ else
317
+ selected = ask("Ignored", @menu.answer_type)
318
+ @menu.select(self, selected)
319
+ end
320
+ end
321
+
322
+ #
323
+ # This method provides easy access to ANSI color sequences, without the user
324
+ # needing to remember to CLEAR at the end of each sequence. Just pass the
325
+ # _string_ to color, followed by a list of _colors_ you would like it to be
326
+ # affected by. The _colors_ can be HighLine class constants, or symbols
327
+ # (:blue for BLUE, for example). A CLEAR will automatically be embedded to
328
+ # the end of the returned String.
329
+ #
330
+ # This method returns the original _string_ unchanged if HighLine::use_color?
331
+ # is +false+.
332
+ #
333
+ def color( string, *colors )
334
+ return string unless self.class.use_color?
335
+
336
+ colors.map! do |c|
337
+ if self.class.using_color_scheme? and self.class.color_scheme.include? c
338
+ self.class.color_scheme[c]
339
+ elsif c.is_a? Symbol
340
+ self.class.const_get(c.to_s.upcase)
341
+ else
342
+ c
343
+ end
344
+ end
345
+ "#{colors.flatten.join}#{string}#{CLEAR}"
346
+ end
347
+
348
+ #
349
+ # This method is a utility for quickly and easily laying out lists. It can
350
+ # be accessed within ERb replacements of any text that will be sent to the
351
+ # user.
352
+ #
353
+ # The only required parameter is _items_, which should be the Array of items
354
+ # to list. A specified _mode_ controls how that list is formed and _option_
355
+ # has different effects, depending on the _mode_. Recognized modes are:
356
+ #
357
+ # <tt>:columns_across</tt>:: _items_ will be placed in columns, flowing
358
+ # from left to right. If given, _option_ is the
359
+ # number of columns to be used. When absent,
360
+ # columns will be determined based on _wrap_at_
361
+ # or a default of 80 characters.
362
+ # <tt>:columns_down</tt>:: Identical to <tt>:columns_across</tt>, save
363
+ # flow goes down.
364
+ # <tt>:inline</tt>:: All _items_ are placed on a single line. The
365
+ # last two _items_ are separated by _option_ or
366
+ # a default of " or ". All other _items_ are
367
+ # separated by ", ".
368
+ # <tt>:rows</tt>:: The default mode. Each of the _items_ is
369
+ # placed on it's own line. The _option_
370
+ # parameter is ignored in this mode.
371
+ #
372
+ # Each member of the _items_ Array is passed through ERb and thus can contain
373
+ # their own expansions. Color escape expansions do not contribute to the
374
+ # final field width.
375
+ #
376
+ def list( items, mode = :rows, option = nil )
377
+ items = items.to_ary.map do |item|
378
+ ERB.new(item, nil, "%").result(binding)
379
+ end
380
+
381
+ case mode
382
+ when :inline
383
+ option = " or " if option.nil?
384
+
385
+ case items.size
386
+ when 0
387
+ ""
388
+ when 1
389
+ items.first
390
+ when 2
391
+ "#{items.first}#{option}#{items.last}"
392
+ else
393
+ items[0..-2].join(", ") + "#{option}#{items.last}"
394
+ end
395
+ when :columns_across, :columns_down
396
+ max_length = actual_length(
397
+ items.max { |a, b| actual_length(a) <=> actual_length(b) }
398
+ )
399
+
400
+ if option.nil?
401
+ limit = @wrap_at || 80
402
+ option = (limit + 2) / (max_length + 2)
403
+ end
404
+
405
+ items = items.map do |item|
406
+ pad = max_length + (item.length - actual_length(item))
407
+ "%-#{pad}s" % item
408
+ end
409
+ row_count = (items.size / option.to_f).ceil
410
+
411
+ if mode == :columns_across
412
+ rows = Array.new(row_count) { Array.new }
413
+ items.each_with_index do |item, index|
414
+ rows[index / option] << item
415
+ end
416
+
417
+ rows.map { |row| row.join(" ") + "\n" }.join
418
+ else
419
+ columns = Array.new(option) { Array.new }
420
+ items.each_with_index do |item, index|
421
+ columns[index / row_count] << item
422
+ end
423
+
424
+ list = ""
425
+ columns.first.size.times do |index|
426
+ list << columns.map { |column| column[index] }.
427
+ compact.join(" ") + "\n"
428
+ end
429
+ list
430
+ end
431
+ else
432
+ items.map { |i| "#{i}\n" }.join
433
+ end
434
+ end
435
+
436
+ #
437
+ # The basic output method for HighLine objects. If the provided _statement_
438
+ # ends with a space or tab character, a newline will not be appended (output
439
+ # will be flush()ed). All other cases are passed straight to Kernel.puts().
440
+ #
441
+ # The _statement_ parameter is processed as an ERb template, supporting
442
+ # embedded Ruby code. The template is evaluated with a binding inside
443
+ # the HighLine instance, providing easy access to the ANSI color constants
444
+ # and the HighLine.color() method.
445
+ #
446
+ def say( statement )
447
+ statement = statement.to_str
448
+ return unless statement.length > 0
449
+
450
+ template = ERB.new(statement, nil, "%")
451
+ statement = template.result(binding)
452
+
453
+ statement = wrap(statement) unless @wrap_at.nil?
454
+ statement = page_print(statement) unless @page_at.nil?
455
+
456
+ if statement[-1, 1] == " " or statement[-1, 1] == "\t"
457
+ @output.print(statement)
458
+ @output.flush
459
+ else
460
+ @output.puts(statement)
461
+ end
462
+ end
463
+
464
+ #
465
+ # Set to an integer value to cause HighLine to wrap output lines at the
466
+ # indicated character limit. When +nil+, the default, no wrapping occurs. If
467
+ # set to <tt>:auto</tt>, HighLine will attempt to determing the columns
468
+ # available for the <tt>@output</tt> or use a sensible default.
469
+ #
470
+ def wrap_at=( setting )
471
+ @wrap_at = setting == :auto ? output_cols : setting
472
+ end
473
+
474
+ #
475
+ # Set to an integer value to cause HighLine to page output lines over the
476
+ # indicated line limit. When +nil+, the default, no paging occurs. If
477
+ # set to <tt>:auto</tt>, HighLine will attempt to determing the rows available
478
+ # for the <tt>@output</tt> or use a sensible default.
479
+ #
480
+ def page_at=( setting )
481
+ @page_at = setting == :auto ? output_rows : setting
482
+ end
483
+
484
+ #
485
+ # Returns the number of columns for the console, or a default it they cannot
486
+ # be determined.
487
+ #
488
+ def output_cols
489
+ return 80 unless @output.tty?
490
+ terminal_size.first
491
+ rescue
492
+ return 80
493
+ end
494
+
495
+ #
496
+ # Returns the number of rows for the console, or a default if they cannot be
497
+ # determined.
498
+ #
499
+ def output_rows
500
+ return 24 unless @output.tty?
501
+ terminal_size.last
502
+ rescue
503
+ return 24
504
+ end
505
+
506
+ private
507
+
508
+ #
509
+ # A helper method for sending the output stream and error and repeat
510
+ # of the question.
511
+ #
512
+ def explain_error( error )
513
+ say(@question.responses[error]) unless error.nil?
514
+ if @question.responses[:ask_on_error] == :question
515
+ say(@question)
516
+ elsif @question.responses[:ask_on_error]
517
+ say(@question.responses[:ask_on_error])
518
+ end
519
+ end
520
+
521
+ #
522
+ # Collects an Array/Hash full of answers as described in
523
+ # HighLine::Question.gather().
524
+ #
525
+ # Raises EOFError if input is exhausted.
526
+ #
527
+ def gather( )
528
+ @gather = @question.gather
529
+ @answers = [ ]
530
+ original_question = @question
531
+
532
+ @question.gather = false
533
+
534
+ case @gather
535
+ when Integer
536
+ @answers << ask(@question)
537
+ @gather -= 1
538
+
539
+ original_question.question = ""
540
+ until @gather.zero?
541
+ @question = original_question
542
+ @answers << ask(@question)
543
+ @gather -= 1
544
+ end
545
+ when String, Regexp
546
+ @answers << ask(@question)
547
+
548
+ original_question.question = ""
549
+ until (@gather.is_a?(String) and @answers.last.to_s == @gather) or
550
+ (@gather.is_a?(Regexp) and @answers.last.to_s =~ @gather)
551
+ @question = original_question
552
+ @answers << ask(@question)
553
+ end
554
+
555
+ @answers.pop
556
+ when Hash
557
+ @answers = { }
558
+ @gather.keys.sort.each do |key|
559
+ @question = original_question
560
+ @key = key
561
+ @answers[key] = ask(@question)
562
+ end
563
+ end
564
+
565
+ @answers
566
+ end
567
+
568
+ #
569
+ # Read a line of input from the input stream and process whitespace as
570
+ # requested by the Question object.
571
+ #
572
+ # If Question's _readline_ property is set, that library will be used to
573
+ # fetch input. *WARNING*: This ignores the currently set input stream.
574
+ #
575
+ # Raises EOFError if input is exhausted.
576
+ #
577
+ def get_line( )
578
+ if @question.readline
579
+ require "readline" # load only if needed
580
+
581
+ # capture say()'s work in a String to feed to readline()
582
+ old_output = @output
583
+ @output = StringIO.new
584
+ say(@question)
585
+ question = @output.string
586
+ @output = old_output
587
+
588
+ # prep auto-completion
589
+ Readline.completion_proc = lambda do |string|
590
+ @question.selection.grep(/\A#{Regexp.escape(string)}/)
591
+ end
592
+
593
+ # work-around ugly readline() warnings
594
+ old_verbose = $VERBOSE
595
+ $VERBOSE = nil
596
+ answer = @question.change_case(
597
+ @question.remove_whitespace(
598
+ Readline.readline(question, true) ) )
599
+ $VERBOSE = old_verbose
600
+
601
+ answer
602
+ else
603
+ raise EOFError, "The input stream is exhausted." if @@track_eof and
604
+ @input.eof?
605
+
606
+ @question.change_case(@question.remove_whitespace(@input.gets))
607
+ end
608
+ end
609
+
610
+ #
611
+ # Return a line or character of input, as requested for this question.
612
+ # Character input will be returned as a single character String,
613
+ # not an Integer.
614
+ #
615
+ # This question's _first_answer_ will be returned instead of input, if set.
616
+ #
617
+ # Raises EOFError if input is exhausted.
618
+ #
619
+ def get_response( )
620
+ return @question.first_answer if @question.first_answer?
621
+
622
+ if @question.character.nil?
623
+ if @question.echo == true and @question.limit.nil?
624
+ get_line
625
+ else
626
+ raw_no_echo_mode if stty = CHARACTER_MODE == "stty"
627
+
628
+ line = ""
629
+ backspace_limit = 0
630
+ begin
631
+
632
+ while character = (stty ? @input.getbyte : get_character(@input))
633
+ # honor backspace and delete
634
+ if character == 127 or character == 8
635
+ line.slice!(-1, 1)
636
+ backspace_limit -= 1
637
+ else
638
+ line << character.chr
639
+ backspace_limit = line.size
640
+ end
641
+ # looking for carriage return (decimal 13) or
642
+ # newline (decimal 10) in raw input
643
+ break if character == 13 or character == 10 or
644
+ (@question.limit and line.size == @question.limit)
645
+ if @question.echo != false
646
+ if character == 127 or character == 8
647
+ # only backspace if we have characters on the line to
648
+ # eliminate, otherwise we'll tromp over the prompt
649
+ if backspace_limit >= 0 then
650
+ @output.print("\b#{ERASE_CHAR}")
651
+ else
652
+ # do nothing
653
+ end
654
+ else
655
+ if @question.echo == true
656
+ @output.print(character.chr)
657
+ else
658
+ @output.print(@question.echo)
659
+ end
660
+ end
661
+ @output.flush
662
+ end
663
+ end
664
+ ensure
665
+ restore_mode if stty
666
+ end
667
+ if @question.overwrite
668
+ @output.print("\r#{ERASE_LINE}")
669
+ @output.flush
670
+ else
671
+ say("\n")
672
+ end
673
+
674
+ @question.change_case(@question.remove_whitespace(line))
675
+ end
676
+ elsif @question.character == :getc
677
+ @question.change_case(@input.getbyte.chr)
678
+ else
679
+ response = get_character(@input).chr
680
+ if @question.overwrite
681
+ @output.print("\r#{ERASE_LINE}")
682
+ @output.flush
683
+ else
684
+ echo = if @question.echo == true
685
+ response
686
+ elsif @question.echo != false
687
+ @question.echo
688
+ else
689
+ ""
690
+ end
691
+ say("#{echo}\n")
692
+ end
693
+ @question.change_case(response)
694
+ end
695
+ end
696
+
697
+ #
698
+ # Page print a series of at most _page_at_ lines for _output_. After each
699
+ # page is printed, HighLine will pause until the user presses enter/return
700
+ # then display the next page of data.
701
+ #
702
+ # Note that the final page of _output_ is *not* printed, but returned
703
+ # instead. This is to support any special handling for the final sequence.
704
+ #
705
+ def page_print( output )
706
+ lines = output.scan(/[^\n]*\n?/)
707
+ while lines.size > @page_at
708
+ @output.puts lines.slice!(0...@page_at).join
709
+ @output.puts
710
+ # Return last line if user wants to abort paging
711
+ return (["...\n"] + lines.slice(-2,1)).join unless continue_paging?
712
+ end
713
+ return lines.join
714
+ end
715
+
716
+ #
717
+ # Ask user if they wish to continue paging output. Allows them to type "q" to
718
+ # cancel the paging process.
719
+ #
720
+ def continue_paging?
721
+ command = HighLine.new(@input, @output).ask(
722
+ "-- press enter/return to continue or q to stop -- "
723
+ ) { |q| q.character = true }
724
+ command !~ /\A[qQ]\Z/ # Only continue paging if Q was not hit.
725
+ end
726
+
727
+ #
728
+ # Wrap a sequence of _lines_ at _wrap_at_ characters per line. Existing
729
+ # newlines will not be affected by this process, but additional newlines
730
+ # may be added.
731
+ #
732
+ def wrap( text )
733
+ wrapped = [ ]
734
+ text.each_line do |line|
735
+ while line =~ /([^\n]{#{@wrap_at + 1},})/
736
+ search = $1.dup
737
+ replace = $1.dup
738
+ if index = replace.rindex(" ", @wrap_at)
739
+ replace[index, 1] = "\n"
740
+ replace.sub!(/\n[ \t]+/, "\n")
741
+ line.sub!(search, replace)
742
+ else
743
+ line[@wrap_at, 0] = "\n"
744
+ end
745
+ end
746
+ wrapped << line
747
+ end
748
+ return wrapped.join
749
+ end
750
+
751
+ #
752
+ # Returns the length of the passed +string_with_escapes+, minus and color
753
+ # sequence escapes.
754
+ #
755
+ def actual_length( string_with_escapes )
756
+ string_with_escapes.gsub(/\e\[\d{1,2}m/, "").length
757
+ end
758
+ end