tty-prompt 0.19.0 → 0.23.1

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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +81 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +485 -233
  5. data/lib/tty-prompt.rb +1 -2
  6. data/lib/tty/prompt.rb +159 -147
  7. data/lib/tty/prompt/answers_collector.rb +5 -5
  8. data/lib/tty/prompt/block_paginator.rb +1 -1
  9. data/lib/tty/prompt/choice.rb +31 -13
  10. data/lib/tty/prompt/choices.rb +30 -12
  11. data/lib/tty/prompt/confirm_question.rb +42 -16
  12. data/lib/tty/prompt/const.rb +17 -0
  13. data/lib/tty/prompt/converter_dsl.rb +6 -7
  14. data/lib/tty/prompt/converter_registry.rb +31 -26
  15. data/lib/tty/prompt/converters.rb +139 -32
  16. data/lib/tty/prompt/enum_list.rb +58 -25
  17. data/lib/tty/prompt/errors.rb +31 -0
  18. data/lib/tty/prompt/evaluator.rb +2 -2
  19. data/lib/tty/prompt/expander.rb +27 -15
  20. data/lib/tty/prompt/keypress.rb +5 -3
  21. data/lib/tty/prompt/list.rb +101 -38
  22. data/lib/tty/prompt/mask_question.rb +9 -5
  23. data/lib/tty/prompt/multi_list.rb +108 -29
  24. data/lib/tty/prompt/multiline.rb +9 -7
  25. data/lib/tty/prompt/paginator.rb +1 -1
  26. data/lib/tty/prompt/question.rb +67 -36
  27. data/lib/tty/prompt/question/checks.rb +20 -2
  28. data/lib/tty/prompt/question/modifier.rb +4 -2
  29. data/lib/tty/prompt/question/validation.rb +3 -3
  30. data/lib/tty/prompt/selected_choices.rb +77 -0
  31. data/lib/tty/prompt/slider.rb +110 -23
  32. data/lib/tty/prompt/statement.rb +3 -3
  33. data/lib/tty/prompt/suggestion.rb +7 -6
  34. data/lib/tty/prompt/symbols.rb +58 -58
  35. data/lib/tty/prompt/test.rb +36 -0
  36. data/lib/tty/prompt/utils.rb +1 -3
  37. data/lib/tty/prompt/version.rb +1 -1
  38. metadata +27 -196
  39. data/Rakefile +0 -8
  40. data/examples/ask.rb +0 -7
  41. data/examples/ask_blank.rb +0 -9
  42. data/examples/ask_valid.rb +0 -12
  43. data/examples/collect.rb +0 -21
  44. data/examples/echo.rb +0 -11
  45. data/examples/enum_select.rb +0 -7
  46. data/examples/enum_select_disabled.rb +0 -16
  47. data/examples/enum_select_paged.rb +0 -9
  48. data/examples/enum_select_wrapped.rb +0 -15
  49. data/examples/expand.rb +0 -29
  50. data/examples/expand_auto.rb +0 -29
  51. data/examples/in.rb +0 -9
  52. data/examples/inputs.rb +0 -10
  53. data/examples/key_events.rb +0 -15
  54. data/examples/keypress.rb +0 -9
  55. data/examples/mask.rb +0 -13
  56. data/examples/multi_select.rb +0 -8
  57. data/examples/multi_select_disabled.rb +0 -17
  58. data/examples/multi_select_disabled_paged.rb +0 -22
  59. data/examples/multi_select_paged.rb +0 -9
  60. data/examples/multi_select_wrapped.rb +0 -15
  61. data/examples/multiline.rb +0 -9
  62. data/examples/pause.rb +0 -9
  63. data/examples/select.rb +0 -24
  64. data/examples/select_disabled.rb +0 -18
  65. data/examples/select_disabled_paged.rb +0 -22
  66. data/examples/select_enum.rb +0 -8
  67. data/examples/select_filtered.rb +0 -11
  68. data/examples/select_paginated.rb +0 -11
  69. data/examples/select_wrapped.rb +0 -15
  70. data/examples/slider.rb +0 -6
  71. data/examples/validation.rb +0 -9
  72. data/examples/yes_no.rb +0 -7
  73. data/lib/tty/prompt/messages.rb +0 -49
  74. data/lib/tty/test_prompt.rb +0 -20
  75. data/spec/spec_helper.rb +0 -61
  76. data/spec/unit/ask_spec.rb +0 -173
  77. data/spec/unit/block_paginator_spec.rb +0 -84
  78. data/spec/unit/choice/eql_spec.rb +0 -22
  79. data/spec/unit/choice/from_spec.rb +0 -112
  80. data/spec/unit/choices/add_spec.rb +0 -12
  81. data/spec/unit/choices/each_spec.rb +0 -13
  82. data/spec/unit/choices/find_by_spec.rb +0 -10
  83. data/spec/unit/choices/new_spec.rb +0 -10
  84. data/spec/unit/choices/pluck_spec.rb +0 -9
  85. data/spec/unit/collect_spec.rb +0 -96
  86. data/spec/unit/converters/convert_bool_spec.rb +0 -58
  87. data/spec/unit/converters/convert_char_spec.rb +0 -11
  88. data/spec/unit/converters/convert_custom_spec.rb +0 -14
  89. data/spec/unit/converters/convert_date_spec.rb +0 -34
  90. data/spec/unit/converters/convert_file_spec.rb +0 -18
  91. data/spec/unit/converters/convert_number_spec.rb +0 -39
  92. data/spec/unit/converters/convert_path_spec.rb +0 -15
  93. data/spec/unit/converters/convert_range_spec.rb +0 -22
  94. data/spec/unit/converters/convert_regex_spec.rb +0 -12
  95. data/spec/unit/converters/convert_string_spec.rb +0 -21
  96. data/spec/unit/converters/on_error_spec.rb +0 -9
  97. data/spec/unit/distance/distance_spec.rb +0 -73
  98. data/spec/unit/enum_select_spec.rb +0 -518
  99. data/spec/unit/error_spec.rb +0 -20
  100. data/spec/unit/evaluator_spec.rb +0 -67
  101. data/spec/unit/expand_spec.rb +0 -290
  102. data/spec/unit/keypress_spec.rb +0 -66
  103. data/spec/unit/mask_spec.rb +0 -140
  104. data/spec/unit/multi_select_spec.rb +0 -741
  105. data/spec/unit/multiline_spec.rb +0 -77
  106. data/spec/unit/new_spec.rb +0 -20
  107. data/spec/unit/ok_spec.rb +0 -10
  108. data/spec/unit/paginator_spec.rb +0 -92
  109. data/spec/unit/question/checks_spec.rb +0 -97
  110. data/spec/unit/question/default_spec.rb +0 -31
  111. data/spec/unit/question/echo_spec.rb +0 -38
  112. data/spec/unit/question/in_spec.rb +0 -115
  113. data/spec/unit/question/initialize_spec.rb +0 -12
  114. data/spec/unit/question/modifier/apply_to_spec.rb +0 -24
  115. data/spec/unit/question/modifier/letter_case_spec.rb +0 -41
  116. data/spec/unit/question/modifier/whitespace_spec.rb +0 -51
  117. data/spec/unit/question/modify_spec.rb +0 -41
  118. data/spec/unit/question/required_spec.rb +0 -92
  119. data/spec/unit/question/validate_spec.rb +0 -115
  120. data/spec/unit/question/validation/call_spec.rb +0 -31
  121. data/spec/unit/question/validation/coerce_spec.rb +0 -30
  122. data/spec/unit/result_spec.rb +0 -40
  123. data/spec/unit/say_spec.rb +0 -67
  124. data/spec/unit/select_spec.rb +0 -942
  125. data/spec/unit/slider_spec.rb +0 -142
  126. data/spec/unit/statement/initialize_spec.rb +0 -15
  127. data/spec/unit/subscribe_spec.rb +0 -22
  128. data/spec/unit/suggest_spec.rb +0 -28
  129. data/spec/unit/timer_spec.rb +0 -29
  130. data/spec/unit/warn_spec.rb +0 -21
  131. data/spec/unit/yes_no_spec.rb +0 -251
  132. data/tasks/console.rake +0 -11
  133. data/tasks/coverage.rake +0 -11
  134. data/tasks/spec.rake +0 -29
  135. data/tty-prompt.gemspec +0 -31
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'question'
3
+ require_relative "question"
4
4
 
5
5
  module TTY
6
6
  class Prompt
7
7
  class MaskQuestion < Question
8
+ # Names for delete keys
9
+ DELETE_KEYS = %i[backspace delete].freeze
10
+
8
11
  # Create masked question
9
12
  #
10
13
  # @param [Hash] options
@@ -27,19 +30,20 @@ module TTY
27
30
  # @api public
28
31
  def mask(char = (not_set = true))
29
32
  return @mask if not_set
33
+
30
34
  @mask = char
31
35
  end
32
36
 
33
- def keyreturn(event)
37
+ def keyreturn(_event)
34
38
  @done_masked = true
35
39
  end
36
40
 
37
- def keyenter(event)
41
+ def keyenter(_event)
38
42
  @done_masked = true
39
43
  end
40
44
 
41
45
  def keypress(event)
42
- if [:backspace, :delete].include?(event.key.name)
46
+ if DELETE_KEYS.include?(event.key.name)
43
47
  @input.chop! unless @input.empty?
44
48
  elsif event.value =~ /^[^\e\n\r]/
45
49
  @input += event.value
@@ -75,7 +79,7 @@ module TTY
75
79
  def read_input(question)
76
80
  @done_masked = false
77
81
  @failure = false
78
- @input = ''
82
+ @input = ""
79
83
  @prompt.print(question)
80
84
  until @done_masked
81
85
  @prompt.read_keypress
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'list'
3
+ require_relative "list"
4
+ require_relative "selected_choices"
4
5
 
5
6
  module TTY
6
7
  class Prompt
@@ -9,8 +10,6 @@ module TTY
9
10
  #
10
11
  # @api private
11
12
  class MultiList < List
12
- HELP = '(Use %s arrow%s keys, press Space to select and Enter to finish%s)'
13
-
14
13
  # Create instance of TTY::Prompt::MultiList menu.
15
14
  #
16
15
  # @param [Prompt] :prompt
@@ -19,12 +18,20 @@ module TTY
19
18
  # @api public
20
19
  def initialize(prompt, **options)
21
20
  super
22
- @selected = []
21
+ @selected = SelectedChoices.new
23
22
  @help = options[:help]
24
23
  @echo = options.fetch(:echo, true)
24
+ @min = options[:min]
25
25
  @max = options[:max]
26
26
  end
27
27
 
28
+ # Set a minimum number of choices
29
+ #
30
+ # @api public
31
+ def min(value)
32
+ @min = value
33
+ end
34
+
28
35
  # Set a maximum number of choices
29
36
  #
30
37
  # @api public
@@ -32,19 +39,54 @@ module TTY
32
39
  @max = value
33
40
  end
34
41
 
42
+ # Callback fired when enter/return key is pressed
43
+ #
44
+ # @api private
45
+ def keyenter(*)
46
+ valid = true
47
+ valid = @min <= @selected.size if @min
48
+ valid = @selected.size <= @max if @max
49
+
50
+ super if valid
51
+ end
52
+ alias keyreturn keyenter
53
+
35
54
  # Callback fired when space key is pressed
36
55
  #
37
56
  # @api private
38
57
  def keyspace(*)
39
58
  active_choice = choices[@active - 1]
40
59
  if @selected.include?(active_choice)
41
- @selected.delete(active_choice)
60
+ @selected.delete_at(@active - 1)
42
61
  else
43
62
  return if @max && @selected.size >= @max
44
- @selected << active_choice
63
+
64
+ @selected.insert(@active - 1, active_choice)
45
65
  end
46
66
  end
47
67
 
68
+ # Selects all choices when Ctrl+A is pressed
69
+ #
70
+ # @api private
71
+ def keyctrl_a(*)
72
+ return if @max && @max < choices.size
73
+
74
+ @selected = SelectedChoices.new(choices.enabled, choices.enabled_indexes)
75
+ end
76
+
77
+ # Revert currently selected choices when Ctrl+I is pressed
78
+ #
79
+ # @api private
80
+ def keyctrl_r(*)
81
+ return if @max && @max < choices.size
82
+
83
+ indexes = choices.each_with_index.reduce([]) do |acc, (choice, idx)|
84
+ acc << idx if !choice.disabled? && !@selected.include?(choice)
85
+ acc
86
+ end
87
+ @selected = SelectedChoices.new(choices.enabled - @selected.to_a, indexes)
88
+ end
89
+
48
90
  private
49
91
 
50
92
  # Setup default options and active selection
@@ -53,12 +95,23 @@ module TTY
53
95
  def setup_defaults
54
96
  validate_defaults
55
97
  # At this stage, @choices matches all the visible choices.
56
- @selected = @choices.values_at(*@default.map { |d| d - 1 })
98
+ default_indexes = @default.map do |d|
99
+ if d.to_s =~ INTEGER_MATCHER
100
+ d - 1
101
+ else
102
+ choices.index(choices.find_by(:name, d.to_s))
103
+ end
104
+ end
105
+ @selected = SelectedChoices.new(@choices.values_at(*default_indexes),
106
+ default_indexes)
57
107
 
58
- if !@default.empty?
108
+ if @default.empty?
109
+ # no default, pick the first non-disabled choice
110
+ @active = choices.index { |choice| !choice.disabled? } + 1
111
+ elsif @default.last.to_s =~ INTEGER_MATCHER
59
112
  @active = @default.last
60
- else
61
- @active = @choices.index { |choice| !choice.disabled? } + 1
113
+ elsif default_choice = choices.find_by(:name, @default.last.to_s)
114
+ @active = choices.index(default_choice) + 1
62
115
  end
63
116
  end
64
117
 
@@ -68,37 +121,63 @@ module TTY
68
121
  #
69
122
  # @api private
70
123
  def selected_names
71
- @selected.map(&:name).join(', ')
124
+ @selected.map(&:name).join(", ")
72
125
  end
73
126
 
74
- # Header part showing the maximum number of choices
127
+ # Header part showing the minimum/maximum number of choices
75
128
  #
76
129
  # @return [String]
77
130
  #
78
131
  # @api private
79
- def max_help
80
- "(max. #{@max}) "
132
+ def minmax_help
133
+ help = []
134
+ help << "min. #{@min}" if @min
135
+ help << "max. #{@max}" if @max
136
+ "(%s) " % [help.join(", ")]
137
+ end
138
+
139
+ # Build a default help text
140
+ #
141
+ # @return [String]
142
+ #
143
+ # @api private
144
+ def default_help
145
+ str = []
146
+ str << "(Press "
147
+ str << "#{arrows_help} arrow"
148
+ str << " or 1-#{choices.size} number" if enumerate?
149
+ str << " to move, Space"
150
+ str << "/Ctrl+A|R" if @max.nil?
151
+ str << " to select"
152
+ str << " (all|rev)" if @max.nil?
153
+ str << (filterable? ? "," : " and")
154
+ str << " Enter to finish"
155
+ str << " and letters to filter" if filterable?
156
+ str << ")"
157
+ str.join
81
158
  end
82
159
 
83
160
  # Render initial help text and then currently selected choices
84
161
  #
85
162
  # @api private
86
163
  def render_header
87
- instructions = @prompt.decorate(help, :bright_black)
88
- max_suffix = @max ? max_help : ""
164
+ instructions = @prompt.decorate(help, @help_color)
165
+ minmax_suffix = @min || @max ? minmax_help : ""
166
+ print_selected = @selected.size.nonzero? && @echo
89
167
 
90
168
  if @done && @echo
91
169
  @prompt.decorate(selected_names, @active_color)
92
- elsif @selected.size.nonzero? && @echo
93
- help_suffix = filterable? && @filter.any? ? " #{filter_help}" : ""
94
- max_suffix + selected_names +
95
- (@first_render ? " #{instructions}" : help_suffix)
96
- elsif @first_render
97
- max_suffix + instructions
170
+ elsif (@first_render && (help_start? || help_always?)) ||
171
+ (help_always? && !@filter.any? && !@done)
172
+ minmax_suffix +
173
+ (print_selected ? "#{selected_names} " : "") +
174
+ instructions
98
175
  elsif filterable? && @filter.any?
99
- max_suffix + filter_help
100
- elsif @max
101
- max_help
176
+ minmax_suffix +
177
+ (print_selected ? "#{selected_names} " : "") +
178
+ @prompt.decorate(filter_help, @help_color)
179
+ else
180
+ minmax_suffix + (print_selected ? selected_names : "")
102
181
  end
103
182
  end
104
183
 
@@ -121,9 +200,9 @@ module TTY
121
200
 
122
201
  sync_paginators if @paging_changed
123
202
  paginator.paginate(choices, @active, @per_page) do |choice, index|
124
- num = enumerate? ? (index + 1).to_s + @enum + ' ' : ''
125
- indicator = (index + 1 == @active) ? @symbols[:marker] : ' '
126
- indicator += ' '
203
+ num = enumerate? ? (index + 1).to_s + @enum + " " : ""
204
+ indicator = (index + 1 == @active) ? @symbols[:marker] : " "
205
+ indicator += " "
127
206
  message = if @selected.include?(choice) && !choice.disabled?
128
207
  selected = @prompt.decorate(@symbols[:radio_on], @active_color)
129
208
  "#{selected} #{num}#{choice.name}"
@@ -134,7 +213,7 @@ module TTY
134
213
  "#{@symbols[:radio_off]} #{num}#{choice.name}"
135
214
  end
136
215
  end_index = paginated? ? paginator.end_index : choices.size - 1
137
- newline = (index == end_index) ? '' : "\n"
216
+ newline = (index == end_index) ? "" : "\n"
138
217
  output << indicator + message + newline
139
218
  end
140
219
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'question'
4
- require_relative 'symbols'
3
+ require_relative "question"
4
+ require_relative "symbols"
5
5
 
6
6
  module TTY
7
7
  class Prompt
@@ -9,9 +9,9 @@ module TTY
9
9
  #
10
10
  # @api private
11
11
  class Multiline < Question
12
- HELP = '(Press CTRL-D or CTRL-Z to finish)'.freeze
12
+ HELP = "(Press Ctrl+D or Ctrl+Z to finish)".freeze
13
13
 
14
- def initialize(prompt, options = {})
14
+ def initialize(prompt, **options)
15
15
  super
16
16
  @help = options[:help] || self.class::HELP
17
17
  @first_render = true
@@ -25,6 +25,7 @@ module TTY
25
25
  # @api public
26
26
  def help(value = (not_set = true))
27
27
  return @help if not_set
28
+
28
29
  @help = value
29
30
  end
30
31
 
@@ -42,7 +43,7 @@ module TTY
42
43
  if !echo?
43
44
  header
44
45
  elsif @done
45
- header << @prompt.decorate("#{@input}", @active_color)
46
+ header << @prompt.decorate(@input.to_s, @active_color)
46
47
  elsif @first_render
47
48
  header << @prompt.decorate(help, @help_color)
48
49
  @first_render = false
@@ -55,8 +56,9 @@ module TTY
55
56
  @prompt.print(question)
56
57
  @lines = read_input
57
58
  @input = "#{@lines.first.strip} ..." unless @lines.first.to_s.empty?
58
- if Utils.blank?(@input)
59
- @input = default? ? default : nil
59
+ if Utils.blank?(@input) && default?
60
+ @input = default
61
+ @lines = default
60
62
  end
61
63
  @evaluator.(@lines)
62
64
  end
@@ -40,7 +40,7 @@ module TTY
40
40
  #
41
41
  # @api private
42
42
  def check_page_size!
43
- raise InvalidArgument, 'per_page must be > 0' if @per_page < 1
43
+ raise InvalidArgument, "per_page must be > 0" if @per_page < 1
44
44
  end
45
45
 
46
46
  # Paginate collection given an active index
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'converters'
4
- require_relative 'evaluator'
5
- require_relative 'question/modifier'
6
- require_relative 'question/validation'
7
- require_relative 'question/checks'
8
- require_relative 'utils'
3
+ require_relative "converters"
4
+ require_relative "evaluator"
5
+ require_relative "question/modifier"
6
+ require_relative "question/validation"
7
+ require_relative "question/checks"
8
+ require_relative "utils"
9
9
 
10
10
  module TTY
11
11
  # A class responsible for shell prompt interactions.
@@ -35,23 +35,30 @@ module TTY
35
35
  #
36
36
  # @api public
37
37
  def initialize(prompt, **options)
38
- @prompt = prompt
39
- @prefix = options.fetch(:prefix) { @prompt.prefix }
40
- @default = options.fetch(:default) { UndefinedSetting }
41
- @required = options.fetch(:required) { false }
42
- @echo = options.fetch(:echo) { true }
43
- @in = options.fetch(:in) { UndefinedSetting }
44
- @modifier = options.fetch(:modifier) { [] }
45
- @validation = options.fetch(:validation) { UndefinedSetting }
46
- @convert = options.fetch(:convert) { UndefinedSetting }
38
+ # Option deprecation
39
+ if options[:validation]
40
+ warn "[DEPRECATION] The `:validation` option is deprecated. Use `:validate` instead."
41
+ options[:validate] = options[:validation]
42
+ end
43
+
44
+ @prompt = prompt
45
+ @prefix = options.fetch(:prefix) { @prompt.prefix }
46
+ @default = options.fetch(:default) { UndefinedSetting }
47
+ @required = options.fetch(:required) { false }
48
+ @echo = options.fetch(:echo) { true }
49
+ @in = options.fetch(:in) { UndefinedSetting }
50
+ @modifier = options.fetch(:modifier) { [] }
51
+ @validation = options.fetch(:validate) { UndefinedSetting }
52
+ @convert = options.fetch(:convert) { UndefinedSetting }
47
53
  @active_color = options.fetch(:active_color) { @prompt.active_color }
48
- @help_color = options.fetch(:help_color) { @prompt.help_color }
49
- @error_color = options.fetch(:error_color) { :red }
50
- @value = options.fetch(:value) { UndefinedSetting }
51
- @messages = Utils.deep_copy(options.fetch(:messages) { { } })
52
- @done = false
54
+ @help_color = options.fetch(:help_color) { @prompt.help_color }
55
+ @error_color = options.fetch(:error_color) { :red }
56
+ @value = options.fetch(:value) { UndefinedSetting }
57
+ @quiet = options.fetch(:quiet) { @prompt.quiet }
58
+ @messages = Utils.deep_copy(options.fetch(:messages) { {} })
59
+ @done = false
53
60
  @first_render = true
54
- @input = nil
61
+ @input = nil
55
62
 
56
63
  @evaluator = Evaluator.new(self)
57
64
 
@@ -60,6 +67,7 @@ module TTY
60
67
  @evaluator << CheckRange
61
68
  @evaluator << CheckValidation
62
69
  @evaluator << CheckModifier
70
+ @evaluator << CheckConversion
63
71
  end
64
72
 
65
73
  # Stores all the error messages displayed to user
@@ -85,7 +93,7 @@ module TTY
85
93
  if template && !template.match(/\%\{/).nil?
86
94
  [template % tokens]
87
95
  else
88
- [template || '']
96
+ [template || ""]
89
97
  end
90
98
  end
91
99
 
@@ -96,7 +104,7 @@ module TTY
96
104
  # @return [self]
97
105
  #
98
106
  # @api public
99
- def call(message = '', &block)
107
+ def call(message = "", &block)
100
108
  @message = message
101
109
  block.call(self) if block
102
110
  @prompt.subscribe(self) do
@@ -122,8 +130,8 @@ module TTY
122
130
  total_lines = @prompt.count_screen_lines(input_line)
123
131
  @prompt.print(refresh(question.lines.count, total_lines))
124
132
  end
125
- @prompt.print(render_question)
126
- convert_result(result.value)
133
+ @prompt.print(render_question) unless @quiet
134
+ result.value
127
135
  end
128
136
 
129
137
  # Render question
@@ -141,7 +149,7 @@ module TTY
141
149
  elsif @done
142
150
  header << @prompt.decorate(@input.to_s, @active_color)
143
151
  elsif default? && !Utils.blank?(@default)
144
- header << @prompt.decorate("(#{default})", @help_color) + ' '
152
+ header << @prompt.decorate("(#{default})", @help_color) + " "
145
153
  end
146
154
  header << "\n" if @done
147
155
  header.join
@@ -162,12 +170,12 @@ module TTY
162
170
  #
163
171
  # @api private
164
172
  def read_input(question)
165
- options = {echo: echo}
173
+ options = { echo: echo }
166
174
  if value? && @first_render
167
175
  options[:value] = @value
168
176
  @first_render = false
169
177
  end
170
- @prompt.read_line(question, options).chomp
178
+ @prompt.read_line(question, **options).chomp
171
179
  end
172
180
 
173
181
  # Handle error condition
@@ -177,7 +185,7 @@ module TTY
177
185
  # @api private
178
186
  def render_error(errors)
179
187
  errors.reduce([]) do |acc, err|
180
- acc << @prompt.decorate('>>', :red) + ' ' + err
188
+ acc << @prompt.decorate(">>", :red) + " " + err
181
189
  acc
182
190
  end.join("\n")
183
191
  end
@@ -211,8 +219,13 @@ module TTY
211
219
  #
212
220
  # @api private
213
221
  def convert_result(value)
214
- if convert? & !Utils.blank?(value)
215
- Converters.convert(@convert, value)
222
+ if convert? && !Utils.blank?(value)
223
+ case @convert
224
+ when Proc
225
+ @convert.call(value)
226
+ else
227
+ Converters.convert(@convert, value)
228
+ end
216
229
  else
217
230
  value
218
231
  end
@@ -221,8 +234,13 @@ module TTY
221
234
  # Specify answer conversion
222
235
  #
223
236
  # @api public
224
- def convert(value)
225
- @convert = value
237
+ def convert(value = (not_set = true), message = nil)
238
+ messages[:convert?] = message if message
239
+ if not_set
240
+ @convert
241
+ else
242
+ @convert = value
243
+ end
226
244
  end
227
245
 
228
246
  # Check if conversion is set
@@ -239,6 +257,7 @@ module TTY
239
257
  # @api public
240
258
  def default(value = (not_set = true))
241
259
  return @default if not_set
260
+
242
261
  @default = value
243
262
  end
244
263
 
@@ -259,9 +278,10 @@ module TTY
259
278
  def required(value = (not_set = true), message = nil)
260
279
  messages[:required?] = message if message
261
280
  return @required if not_set
281
+
262
282
  @required = value
263
283
  end
264
- alias_method :required?, :required
284
+ alias required? required
265
285
 
266
286
  # Set validation rule for an argument
267
287
  #
@@ -280,6 +300,7 @@ module TTY
280
300
  # @api public
281
301
  def value(val)
282
302
  return @value if val.nil?
303
+
283
304
  @value = val
284
305
  end
285
306
 
@@ -309,18 +330,20 @@ module TTY
309
330
  # @api public
310
331
  def echo(value = nil)
311
332
  return @echo if value.nil?
333
+
312
334
  @echo = value
313
335
  end
314
- alias_method :echo?, :echo
336
+ alias echo? echo
315
337
 
316
338
  # Turn raw mode on or off. This enables character-based input.
317
339
  #
318
340
  # @api public
319
341
  def raw(value = nil)
320
342
  return @raw if value.nil?
343
+
321
344
  @raw = value
322
345
  end
323
- alias_method :raw?, :raw
346
+ alias raw? raw
324
347
 
325
348
  # Set expected range of values
326
349
  #
@@ -333,6 +356,7 @@ module TTY
333
356
  @in = Converters.convert(:range, @in)
334
357
  end
335
358
  return @in if not_set
359
+
336
360
  @in = Converters.convert(:range, value)
337
361
  end
338
362
 
@@ -345,6 +369,13 @@ module TTY
345
369
  @in != UndefinedSetting
346
370
  end
347
371
 
372
+ # Set quiet mode.
373
+ #
374
+ # @api public
375
+ def quiet(value)
376
+ @quiet = value
377
+ end
378
+
348
379
  # @api public
349
380
  def to_s
350
381
  message.to_s