highline 1.7.10 → 2.0.3

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.
Files changed (65) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +5 -0
  3. data/.rubocop.yml +84 -0
  4. data/.simplecov +5 -0
  5. data/.travis.yml +35 -9
  6. data/Changelog.md +214 -15
  7. data/Gemfile +16 -5
  8. data/README.md +202 -0
  9. data/Rakefile +8 -20
  10. data/appveyor.yml +37 -0
  11. data/examples/ansi_colors.rb +6 -11
  12. data/examples/asking_for_arrays.rb +6 -2
  13. data/examples/basic_usage.rb +31 -21
  14. data/examples/color_scheme.rb +11 -10
  15. data/examples/get_character.rb +8 -4
  16. data/examples/limit.rb +4 -0
  17. data/examples/menus.rb +16 -10
  18. data/examples/overwrite.rb +9 -5
  19. data/examples/page_and_wrap.rb +5 -4
  20. data/examples/password.rb +4 -0
  21. data/examples/repeat_entry.rb +10 -5
  22. data/examples/trapping_eof.rb +2 -1
  23. data/examples/using_readline.rb +2 -1
  24. data/highline.gemspec +25 -27
  25. data/lib/highline/builtin_styles.rb +129 -0
  26. data/lib/highline/color_scheme.rb +49 -32
  27. data/lib/highline/compatibility.rb +11 -4
  28. data/lib/highline/custom_errors.rb +57 -0
  29. data/lib/highline/import.rb +19 -12
  30. data/lib/highline/io_console_compatible.rb +37 -0
  31. data/lib/highline/list.rb +177 -0
  32. data/lib/highline/list_renderer.rb +261 -0
  33. data/lib/highline/menu/item.rb +32 -0
  34. data/lib/highline/menu.rb +306 -111
  35. data/lib/highline/paginator.rb +52 -0
  36. data/lib/highline/question/answer_converter.rb +103 -0
  37. data/lib/highline/question.rb +281 -131
  38. data/lib/highline/question_asker.rb +150 -0
  39. data/lib/highline/simulate.rb +24 -13
  40. data/lib/highline/statement.rb +88 -0
  41. data/lib/highline/string.rb +36 -0
  42. data/lib/highline/string_extensions.rb +83 -64
  43. data/lib/highline/style.rb +196 -63
  44. data/lib/highline/template_renderer.rb +62 -0
  45. data/lib/highline/terminal/io_console.rb +36 -0
  46. data/lib/highline/terminal/ncurses.rb +38 -0
  47. data/lib/highline/terminal/unix_stty.rb +51 -0
  48. data/lib/highline/terminal.rb +190 -0
  49. data/lib/highline/version.rb +3 -1
  50. data/lib/highline/wrapper.rb +53 -0
  51. data/lib/highline.rb +390 -788
  52. metadata +56 -35
  53. data/INSTALL +0 -59
  54. data/README.rdoc +0 -74
  55. data/lib/highline/system_extensions.rb +0 -254
  56. data/setup.rb +0 -1360
  57. data/test/string_methods.rb +0 -32
  58. data/test/tc_color_scheme.rb +0 -96
  59. data/test/tc_highline.rb +0 -1402
  60. data/test/tc_import.rb +0 -52
  61. data/test/tc_menu.rb +0 -439
  62. data/test/tc_simulator.rb +0 -33
  63. data/test/tc_string_extension.rb +0 -33
  64. data/test/tc_string_highline.rb +0 -38
  65. data/test/tc_style.rb +0 -578
@@ -0,0 +1,103 @@
1
+ # coding: utf-8
2
+
3
+ require "forwardable"
4
+
5
+ class HighLine
6
+ class Question
7
+ # It provides all answer conversion flow.
8
+ class AnswerConverter
9
+ extend Forwardable
10
+
11
+ def_delegators :@question,
12
+ :answer, :answer=, :check_range,
13
+ :directory, :answer_type, :choices_complete
14
+
15
+ # It should be initialized with a Question object.
16
+ # The class will get the answer from {Question#answer}
17
+ # and then convert it to the proper {Question#answer_type}.
18
+ # It is mainly used by {Question#convert}
19
+ #
20
+ # @param question [Question]
21
+ def initialize(question)
22
+ @question = question
23
+ end
24
+
25
+ # Based on the given Question object's settings,
26
+ # it makes the conversion and returns the answer.
27
+ # @return [Object] the converted answer.
28
+ def convert
29
+ return unless answer_type
30
+
31
+ self.answer = convert_by_answer_type
32
+ check_range
33
+ answer
34
+ end
35
+
36
+ # @return [HighLine::String] answer converted to a HighLine::String
37
+ def to_string
38
+ HighLine::String(answer)
39
+ end
40
+
41
+ # That's a weird name for a method!
42
+ # But it's working ;-)
43
+ define_method "to_highline::string" do
44
+ HighLine::String(answer)
45
+ end
46
+
47
+ # @return [Integer] answer converted to an Integer
48
+ def to_integer
49
+ Kernel.send(:Integer, answer)
50
+ end
51
+
52
+ # @return [Float] answer converted to a Float
53
+ def to_float
54
+ Kernel.send(:Float, answer)
55
+ end
56
+
57
+ # @return [Symbol] answer converted to an Symbol
58
+ def to_symbol
59
+ answer.to_sym
60
+ end
61
+
62
+ # @return [Regexp] answer converted to a Regexp
63
+ def to_regexp
64
+ Regexp.new(answer)
65
+ end
66
+
67
+ # @return [File] answer converted to a File
68
+ def to_file
69
+ self.answer = choices_complete(answer)
70
+ File.open(File.join(directory.to_s, answer.last))
71
+ end
72
+
73
+ # @return [Pathname] answer converted to an Pathname
74
+ def to_pathname
75
+ self.answer = choices_complete(answer)
76
+ Pathname.new(File.join(directory.to_s, answer.last))
77
+ end
78
+
79
+ # @return [Array] answer converted to an Array
80
+ def to_array
81
+ self.answer = choices_complete(answer)
82
+ answer.last
83
+ end
84
+
85
+ # @return [Proc] answer converted to an Proc
86
+ def to_proc
87
+ answer_type.call(answer)
88
+ end
89
+
90
+ private
91
+
92
+ def convert_by_answer_type
93
+ if answer_type.respond_to? :parse
94
+ answer_type.parse(answer)
95
+ elsif answer_type.is_a? Class
96
+ send("to_#{answer_type.name.downcase}")
97
+ else
98
+ send("to_#{answer_type.class.name.downcase}")
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -1,3 +1,6 @@
1
+ # coding: utf-8
2
+
3
+ #--
1
4
  # question.rb
2
5
  #
3
6
  # Created by James Edward Gray II on 2005-04-26.
@@ -5,9 +8,11 @@
5
8
  #
6
9
  # This is Free Software. See LICENSE and COPYING for details.
7
10
 
11
+ require "English"
8
12
  require "optparse"
9
13
  require "date"
10
14
  require "pathname"
15
+ require "highline/question/answer_converter"
11
16
 
12
17
  class HighLine
13
18
  #
@@ -17,43 +22,49 @@ class HighLine
17
22
  # process is handled according to the users wishes.
18
23
  #
19
24
  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
25
+ include CustomErrors
26
+
27
+ #
28
+ # If _template_or_question_ is already a Question object just return it.
29
+ # If not, build it.
30
+ #
31
+ # @param template_or_question [String, Question] what to ask
32
+ # @param answer_type [Class] to what class to convert the answer
33
+ # @param details to be passed to Question.new
34
+ # @return [Question]
35
+ def self.build(template_or_question, answer_type = nil, &details)
36
+ if template_or_question.is_a? Question
37
+ template_or_question
38
+ else
39
+ Question.new(template_or_question, answer_type, &details)
40
+ end
23
41
  end
24
42
 
25
43
  #
26
- # Create an instance of HighLine::Question. Expects a _question_ to ask
44
+ # Create an instance of HighLine::Question. Expects a _template_ to ask
27
45
  # (can be <tt>""</tt>) and an _answer_type_ to convert the answer to.
28
46
  # The _answer_type_ parameter must be a type recognized by
29
47
  # Question.convert(). If given, a block is yielded the new Question
30
48
  # object to allow custom initialization.
31
49
  #
32
- def initialize( question, answer_type )
50
+ # @param template [String] any String
51
+ # @param answer_type [Class] the type the answer will be converted to it.
52
+ def initialize(template, answer_type)
33
53
  # initialize instance data
34
- @question = String(question).dup
54
+ @template = String(template).dup
35
55
  @answer_type = answer_type
36
- @completion = @answer_type
56
+ @completion = @answer_type
37
57
 
38
- @character = nil
39
- @limit = nil
40
58
  @echo = true
41
- @readline = false
42
59
  @whitespace = :strip
43
60
  @case = nil
44
- @default = nil
45
- @validate = nil
46
- @above = nil
47
- @below = nil
48
61
  @in = nil
49
- @confirm = nil
50
- @gather = false
51
- @verify_match = false
52
62
  @first_answer = nil
53
- @directory = Pathname.new(File.expand_path(File.dirname($0)))
54
63
  @glob = "*"
55
- @responses = Hash.new
56
64
  @overwrite = false
65
+ @user_responses = {}
66
+ @internal_responses = default_responses_hash
67
+ @directory = Pathname.new(File.expand_path(File.dirname($PROGRAM_NAME)))
57
68
 
58
69
  # allow block to override settings
59
70
  yield self if block_given?
@@ -63,7 +74,11 @@ class HighLine
63
74
  end
64
75
 
65
76
  # The ERb template of the question to be asked.
66
- attr_accessor :question
77
+ attr_accessor :template
78
+
79
+ # The answer, set by HighLine#ask
80
+ attr_accessor :answer
81
+
67
82
  # The type that will be used to convert this answer.
68
83
  attr_accessor :answer_type
69
84
  # For Auto-completion
@@ -133,11 +148,15 @@ class HighLine
133
148
  attr_accessor :in
134
149
  #
135
150
  # Asks a yes or no confirmation question, to ensure a user knows what
136
- # they have just agreed to. If set to +true+ the question will be,
137
- # "Are you sure? " Any other true value for this attribute is assumed
138
- # to be the question to ask. When +false+ or +nil+ (the default),
139
- # answers are not confirmed.
140
- #
151
+ # they have just agreed to. The confirm attribute can be set to :
152
+ # +true+ : In this case the question will be, "Are you sure?".
153
+ # Proc : The Proc is yielded the answer given. The Proc must
154
+ # output a string which is then used as the confirm
155
+ # question.
156
+ # String : The String must use ERB syntax. The String is
157
+ # evaluated with access to question and answer and
158
+ # is then used as the confirm question.
159
+ # When set to +false+ or +nil+ (the default), answers are not confirmed.
141
160
  attr_accessor :confirm
142
161
  #
143
162
  # When set, the user will be prompted for multiple answers which will
@@ -201,7 +220,9 @@ class HighLine
201
220
  # <tt>:not_valid</tt>:: The error message shown when
202
221
  # validation checks fail.
203
222
  #
204
- attr_reader :responses
223
+ def responses
224
+ @user_responses
225
+ end
205
226
  #
206
227
  # When set to +true+ the question is asked, but output does not progress to
207
228
  # the next line. The Cursor is moved back to the beginning of the question
@@ -214,12 +235,11 @@ class HighLine
214
235
  # Returns the provided _answer_string_ or the default answer for this
215
236
  # Question if a default was set and the answer is empty.
216
237
  #
217
- def answer_or_default( answer_string )
218
- if answer_string.length == 0 and not @default.nil?
219
- @default
220
- else
221
- answer_string
222
- end
238
+ # @param answer_string [String]
239
+ # @return [String] the answer itself or a default message.
240
+ def answer_or_default(answer_string)
241
+ return default if answer_string.empty? && default
242
+ answer_string
223
243
  end
224
244
 
225
245
  #
@@ -227,36 +247,55 @@ class HighLine
227
247
  # responses based on the details of this Question object.
228
248
  # Also used by Menu#update_responses.
229
249
  #
230
- def build_responses(message_source = answer_type, new_hash_wins = false)
250
+ # @return [Hash] responses Hash winner (new and old merge).
251
+ # @param message_source [Class] Array or String for example.
252
+ # Same as {#answer_type}.
253
+
254
+ def build_responses(message_source = answer_type)
231
255
  append_default if [::String, Symbol].include? default.class
232
256
 
233
- choice_error_str_func = lambda do
234
- message_source.is_a?(Array) \
235
- ? '[' + message_source.map { |s| "#{s}" }.join(', ') + ']' \
236
- : message_source.inspect
237
- end
257
+ new_hash = build_responses_new_hash(message_source)
258
+ # Update our internal responses with the new hash
259
+ # generated from the message source
260
+ @internal_responses = @internal_responses.merge(new_hash)
261
+ end
262
+
263
+ def default_responses_hash
264
+ {
265
+ ask_on_error: "? ",
266
+ mismatch: "Your entries didn't match."
267
+ }
268
+ end
269
+
270
+ # When updating the responses hash, it generates the new one.
271
+ # @param message_source (see #build_responses)
272
+ # @return [Hash] responses hash
273
+ def build_responses_new_hash(message_source)
274
+ { ambiguous_completion: "Ambiguous choice. Please choose one of " +
275
+ choice_error_str(message_source) + ".",
276
+ invalid_type: "You must enter a valid #{message_source}.",
277
+ no_completion: "You must choose one of " +
278
+ choice_error_str(message_source) + ".",
279
+ not_in_range: "Your answer isn't within the expected range " \
280
+ "(#{expected_range}).",
281
+ not_valid: "Your answer isn't valid (must match " \
282
+ "#{validate.inspect})." }
283
+ end
238
284
 
239
- old_hash = @responses
240
-
241
- new_hash = { :ambiguous_completion =>
242
- "Ambiguous choice. Please choose one of " +
243
- choice_error_str_func.call + '.',
244
- :ask_on_error =>
245
- "? ",
246
- :invalid_type =>
247
- "You must enter a valid #{message_source}.",
248
- :no_completion =>
249
- "You must choose one of " + choice_error_str_func.call + '.',
250
- :not_in_range =>
251
- "Your answer isn't within the expected range " +
252
- "(#{expected_range}).",
253
- :mismatch =>
254
- "Your entries didn't match.",
255
- :not_valid =>
256
- "Your answer isn't valid (must match " +
257
- "#{@validate.inspect})." }
258
-
259
- @responses = new_hash_wins ? old_hash.merge(new_hash) : new_hash.merge(old_hash)
285
+ # This is the actual responses hash that gets used in determining output
286
+ # Notice that we give @user_responses precedence over the responses
287
+ # generated internally via build_response
288
+ def final_responses
289
+ @internal_responses.merge(@user_responses)
290
+ end
291
+
292
+ def final_response(error)
293
+ response = final_responses[error]
294
+ if response.respond_to?(:call)
295
+ response.call(answer)
296
+ else
297
+ response
298
+ end
260
299
  end
261
300
 
262
301
  #
@@ -273,7 +312,10 @@ class HighLine
273
312
  #
274
313
  # An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
275
314
  #
276
- def change_case( answer_string )
315
+ # @param answer_string [String]
316
+ # @return [String] upcased, downcased, capitalized
317
+ # or unchanged answer String.
318
+ def change_case(answer_string)
277
319
  if [:up, :upcase].include?(@case)
278
320
  answer_string.upcase
279
321
  elsif [:down, :downcase].include?(@case)
@@ -314,46 +356,34 @@ class HighLine
314
356
  # This method throws ArgumentError, if the conversion cannot be
315
357
  # completed for any reason.
316
358
  #
317
- def convert( answer_string )
318
- if @answer_type.nil?
319
- answer_string
320
- elsif [::String, HighLine::String].include?(@answer_type)
321
- HighLine::String(answer_string)
322
- elsif [Float, Integer, String].include?(@answer_type)
323
- Kernel.send(@answer_type.to_s.to_sym, answer_string)
324
- elsif @answer_type == Symbol
325
- answer_string.to_sym
326
- elsif @answer_type == Regexp
327
- Regexp.new(answer_string)
328
- elsif @answer_type.is_a?(Array) or [File, Pathname].include?(@answer_type)
329
- # cheating, using OptionParser's Completion module
330
- choices = selection
331
- choices.extend(OptionParser::Completion)
332
- answer = choices.complete(answer_string)
333
- if answer.nil?
334
- raise NoAutoCompleteMatch
335
- end
336
- if @answer_type.is_a?(Array)
337
- answer.last
338
- elsif @answer_type == File
339
- File.open(File.join(@directory.to_s, answer.last))
340
- else
341
- Pathname.new(File.join(@directory.to_s, answer.last))
342
- end
343
- elsif [Date, DateTime].include?(@answer_type) or @answer_type.is_a?(Class)
344
- @answer_type.parse(answer_string)
345
- elsif @answer_type.is_a?(Proc)
346
- @answer_type[answer_string]
347
- end
359
+ def convert
360
+ AnswerConverter.new(self).convert
361
+ end
362
+
363
+ # Run {#in_range?} and raise an error if not succesful
364
+ def check_range
365
+ raise NotInRangeQuestionError unless in_range?
366
+ end
367
+
368
+ # Try to auto complete answer_string
369
+ # @param answer_string [String]
370
+ # @return [String]
371
+ def choices_complete(answer_string)
372
+ # cheating, using OptionParser's Completion module
373
+ choices = selection
374
+ choices.extend(OptionParser::Completion)
375
+ answer = choices.complete(answer_string)
376
+ raise NoAutoCompleteMatch unless answer
377
+ answer
348
378
  end
349
379
 
350
380
  # Returns an English explanation of the current range settings.
351
- def expected_range( )
352
- expected = [ ]
381
+ def expected_range
382
+ expected = []
353
383
 
354
- expected << "above #{@above}" unless @above.nil?
355
- expected << "below #{@below}" unless @below.nil?
356
- expected << "included in #{@in.inspect}" unless @in.nil?
384
+ expected << "above #{above}" if above
385
+ expected << "below #{below}" if below
386
+ expected << "included in #{@in.inspect}" if @in
357
387
 
358
388
  case expected.size
359
389
  when 0 then ""
@@ -364,15 +394,15 @@ class HighLine
364
394
  end
365
395
 
366
396
  # Returns _first_answer_, which will be unset following this call.
367
- def first_answer( )
397
+ def first_answer
368
398
  @first_answer
369
399
  ensure
370
400
  @first_answer = nil
371
401
  end
372
402
 
373
403
  # Returns true if _first_answer_ is set.
374
- def first_answer?( )
375
- not @first_answer.nil?
404
+ def first_answer?
405
+ true if @first_answer
376
406
  end
377
407
 
378
408
  #
@@ -381,10 +411,10 @@ class HighLine
381
411
  # _in_ attribute. Otherwise, +false+ is returned. Any +nil+ attributes
382
412
  # are not checked.
383
413
  #
384
- def in_range?( answer_object )
385
- (@above.nil? or answer_object > @above) and
386
- (@below.nil? or answer_object < @below) and
387
- (@in.nil? or @in.include?(answer_object))
414
+ def in_range?
415
+ (!above || answer > above) &&
416
+ (!below || answer < below) &&
417
+ (!@in || @in.include?(answer))
388
418
  end
389
419
 
390
420
  #
@@ -406,43 +436,55 @@ class HighLine
406
436
  #
407
437
  # This process is skipped for single character input.
408
438
  #
409
- def remove_whitespace( answer_string )
410
- if @whitespace.nil?
439
+ # @param answer_string [String]
440
+ # @return [String] answer string with whitespaces removed
441
+ def remove_whitespace(answer_string)
442
+ if !whitespace
411
443
  answer_string
412
- elsif [:strip, :chomp].include?(@whitespace)
413
- answer_string.send(@whitespace)
414
- elsif @whitespace == :collapse
444
+ elsif [:strip, :chomp].include?(whitespace)
445
+ answer_string.send(whitespace)
446
+ elsif whitespace == :collapse
415
447
  answer_string.gsub(/\s+/, " ")
416
- elsif [:strip_and_collapse, :chomp_and_collapse].include?(@whitespace)
417
- result = answer_string.send(@whitespace.to_s[/^[a-z]+/])
448
+ elsif [:strip_and_collapse, :chomp_and_collapse].include?(whitespace)
449
+ result = answer_string.send(whitespace.to_s[/^[a-z]+/])
418
450
  result.gsub(/\s+/, " ")
419
- elsif @whitespace == :remove
451
+ elsif whitespace == :remove
420
452
  answer_string.gsub(/\s+/, "")
421
453
  else
422
454
  answer_string
423
455
  end
424
456
  end
425
457
 
458
+ # Convert to String, remove whitespace and change case
459
+ # when necessary
460
+ # @param answer_string [String]
461
+ # @return [String] converted String
462
+ def format_answer(answer_string)
463
+ answer_string = String(answer_string)
464
+ answer_string = remove_whitespace(answer_string)
465
+ change_case(answer_string)
466
+ end
467
+
426
468
  #
427
469
  # Returns an Array of valid answers to this question. These answers are
428
470
  # only known when _answer_type_ is set to an Array of choices, File, or
429
471
  # Pathname. Any other time, this method will return an empty Array.
430
472
  #
431
- def selection( )
432
- if @completion.is_a?(Array)
433
- @completion
434
- elsif [File, Pathname].include?(@completion)
435
- Dir[File.join(@directory.to_s, @glob)].map do |file|
473
+ def selection
474
+ if completion.is_a?(Array)
475
+ completion
476
+ elsif [File, Pathname].include?(completion)
477
+ Dir[File.join(directory.to_s, glob)].map do |file|
436
478
  File.basename(file)
437
479
  end
438
480
  else
439
- [ ]
481
+ []
440
482
  end
441
483
  end
442
484
 
443
- # Stringifies the question to be asked.
485
+ # Stringifies the template to be asked.
444
486
  def to_s
445
- @question
487
+ template
446
488
  end
447
489
 
448
490
  #
@@ -452,10 +494,110 @@ class HighLine
452
494
  # It's important to realize that an answer is validated after whitespace
453
495
  # and case handling.
454
496
  #
455
- def valid_answer?( answer_string )
456
- @validate.nil? or
457
- (@validate.is_a?(Regexp) and answer_string =~ @validate) or
458
- (@validate.is_a?(Proc) and @validate[answer_string])
497
+ def valid_answer?
498
+ !validate ||
499
+ (validate.is_a?(Regexp) && answer =~ validate) ||
500
+ (validate.is_a?(Proc) && validate[answer])
501
+ end
502
+
503
+ #
504
+ # Return a line or character of input, as requested for this question.
505
+ # Character input will be returned as a single character String,
506
+ # not an Integer.
507
+ #
508
+ # This question's _first_answer_ will be returned instead of input, if set.
509
+ #
510
+ # Raises EOFError if input is exhausted.
511
+ #
512
+ # @param highline [HighLine] context
513
+ # @return [String] a character or line
514
+
515
+ def get_response(highline)
516
+ return first_answer if first_answer?
517
+
518
+ case character
519
+ when :getc
520
+ highline.get_response_getc_mode(self)
521
+ when true
522
+ highline.get_response_character_mode(self)
523
+ else
524
+ highline.get_response_line_mode(self)
525
+ end
526
+ end
527
+
528
+ # Uses {#get_response} but returns a default answer
529
+ # using {#answer_or_default} in case no answers was
530
+ # returned.
531
+ #
532
+ # @param highline [HighLine] context
533
+ # @return [String]
534
+
535
+ def get_response_or_default(highline)
536
+ self.answer = answer_or_default(get_response(highline))
537
+ end
538
+
539
+ # Returns the String to be shown when asking for an answer confirmation.
540
+ # @param highline [HighLine] context
541
+ # @return [String] default "Are you sure?" if {#confirm} is +true+
542
+ # @return [String] {#confirm} rendered as a template if it is a String
543
+ def confirm_question(highline)
544
+ if confirm == true
545
+ "Are you sure? "
546
+ elsif confirm.is_a?(Proc)
547
+ confirm.call(answer)
548
+ else
549
+ # evaluate ERb under initial scope, so it will have
550
+ # access to question and answer
551
+ template = if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
552
+ ERB.new(confirm, trim_mode: "%")
553
+ else
554
+ ERB.new(confirm, nil, "%")
555
+ end
556
+ template_renderer = TemplateRenderer.new(template, self, highline)
557
+ template_renderer.render
558
+ end
559
+ end
560
+
561
+ # Provides the String to be asked when at an error situation.
562
+ # It may be just the question itself (repeat on error).
563
+ # @return [self] if :ask_on_error on responses Hash is set to :question
564
+ # @return [String] if :ask_on_error on responses Hash is set to
565
+ # something else
566
+ def ask_on_error_msg
567
+ if final_responses[:ask_on_error] == :question
568
+ self
569
+ elsif final_responses[:ask_on_error]
570
+ final_responses[:ask_on_error]
571
+ end
572
+ end
573
+
574
+ # readline() needs to handle its own output, but readline only supports
575
+ # full line reading. Therefore if question.echo is anything but true,
576
+ # the prompt will not be issued. And we have to account for that now.
577
+ # Also, JRuby-1.7's ConsoleReader.readLine() needs to be passed the prompt
578
+ # to handle line editing properly.
579
+ # @param highline [HighLine] context
580
+ # @return [void]
581
+ def show_question(highline)
582
+ highline.say(self)
583
+ end
584
+
585
+ # Returns an echo string that is adequate for this Question settings.
586
+ # @param response [String]
587
+ # @return [String] the response itself if {#echo} is +true+.
588
+ # @return [String] echo character if {#echo} is truethy. Mainly a String.
589
+ # @return [String] empty string if {#echo} is falsy.
590
+ def get_echo_for_response(response)
591
+ # actually true, not only truethy value
592
+ if echo == true
593
+ response
594
+ # any truethy value, probably a String
595
+ elsif echo
596
+ echo
597
+ # any falsy value, false or nil
598
+ else
599
+ ""
600
+ end
459
601
  end
460
602
 
461
603
  private
@@ -465,15 +607,23 @@ class HighLine
465
607
  # Trailing whitespace is preserved so the function of HighLine.say() is
466
608
  # not affected.
467
609
  #
468
- def append_default( )
469
- if @question =~ /([\t ]+)\Z/
470
- @question << "|#{@default}|#{$1}"
471
- elsif @question == ""
472
- @question << "|#{@default}| "
473
- elsif @question[-1, 1] == "\n"
474
- @question[-2, 0] = " |#{@default}|"
610
+ def append_default
611
+ if template =~ /([\t ]+)\Z/
612
+ template << "|#{default}|#{Regexp.last_match(1)}"
613
+ elsif template == ""
614
+ template << "|#{default}| "
615
+ elsif template[-1, 1] == "\n"
616
+ template[-2, 0] = " |#{default}|"
617
+ else
618
+ template << " |#{default}|"
619
+ end
620
+ end
621
+
622
+ def choice_error_str(message_source)
623
+ if message_source.is_a? Array
624
+ "[" + message_source.join(", ") + "]"
475
625
  else
476
- @question << " |#{@default}|"
626
+ message_source.inspect
477
627
  end
478
628
  end
479
629
  end