austb-tty-prompt 0.13.0

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 (80) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +25 -0
  5. data/CHANGELOG.md +218 -0
  6. data/CODE_OF_CONDUCT.md +49 -0
  7. data/Gemfile +19 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +1132 -0
  10. data/Rakefile +8 -0
  11. data/appveyor.yml +23 -0
  12. data/benchmarks/speed.rb +27 -0
  13. data/examples/ask.rb +15 -0
  14. data/examples/collect.rb +19 -0
  15. data/examples/echo.rb +11 -0
  16. data/examples/enum.rb +8 -0
  17. data/examples/enum_paged.rb +9 -0
  18. data/examples/enum_select.rb +7 -0
  19. data/examples/expand.rb +29 -0
  20. data/examples/in.rb +9 -0
  21. data/examples/inputs.rb +10 -0
  22. data/examples/key_events.rb +11 -0
  23. data/examples/keypress.rb +9 -0
  24. data/examples/mask.rb +13 -0
  25. data/examples/multi_select.rb +8 -0
  26. data/examples/multi_select_paged.rb +9 -0
  27. data/examples/multiline.rb +9 -0
  28. data/examples/pause.rb +7 -0
  29. data/examples/select.rb +18 -0
  30. data/examples/select_paginated.rb +9 -0
  31. data/examples/slider.rb +6 -0
  32. data/examples/validation.rb +9 -0
  33. data/examples/yes_no.rb +7 -0
  34. data/lib/tty-prompt.rb +4 -0
  35. data/lib/tty/prompt.rb +535 -0
  36. data/lib/tty/prompt/answers_collector.rb +59 -0
  37. data/lib/tty/prompt/choice.rb +90 -0
  38. data/lib/tty/prompt/choices.rb +110 -0
  39. data/lib/tty/prompt/confirm_question.rb +129 -0
  40. data/lib/tty/prompt/converter_dsl.rb +22 -0
  41. data/lib/tty/prompt/converter_registry.rb +64 -0
  42. data/lib/tty/prompt/converters.rb +77 -0
  43. data/lib/tty/prompt/distance.rb +49 -0
  44. data/lib/tty/prompt/enum_list.rb +337 -0
  45. data/lib/tty/prompt/enum_paginator.rb +56 -0
  46. data/lib/tty/prompt/evaluator.rb +29 -0
  47. data/lib/tty/prompt/expander.rb +292 -0
  48. data/lib/tty/prompt/keypress.rb +94 -0
  49. data/lib/tty/prompt/list.rb +317 -0
  50. data/lib/tty/prompt/mask_question.rb +91 -0
  51. data/lib/tty/prompt/multi_list.rb +108 -0
  52. data/lib/tty/prompt/multiline.rb +71 -0
  53. data/lib/tty/prompt/paginator.rb +88 -0
  54. data/lib/tty/prompt/question.rb +333 -0
  55. data/lib/tty/prompt/question/checks.rb +87 -0
  56. data/lib/tty/prompt/question/modifier.rb +94 -0
  57. data/lib/tty/prompt/question/validation.rb +72 -0
  58. data/lib/tty/prompt/reader.rb +352 -0
  59. data/lib/tty/prompt/reader/codes.rb +121 -0
  60. data/lib/tty/prompt/reader/console.rb +57 -0
  61. data/lib/tty/prompt/reader/history.rb +145 -0
  62. data/lib/tty/prompt/reader/key_event.rb +91 -0
  63. data/lib/tty/prompt/reader/line.rb +162 -0
  64. data/lib/tty/prompt/reader/mode.rb +44 -0
  65. data/lib/tty/prompt/reader/win_api.rb +29 -0
  66. data/lib/tty/prompt/reader/win_console.rb +53 -0
  67. data/lib/tty/prompt/result.rb +42 -0
  68. data/lib/tty/prompt/slider.rb +182 -0
  69. data/lib/tty/prompt/statement.rb +55 -0
  70. data/lib/tty/prompt/suggestion.rb +115 -0
  71. data/lib/tty/prompt/symbols.rb +61 -0
  72. data/lib/tty/prompt/timeout.rb +69 -0
  73. data/lib/tty/prompt/utils.rb +44 -0
  74. data/lib/tty/prompt/version.rb +7 -0
  75. data/lib/tty/test_prompt.rb +20 -0
  76. data/tasks/console.rake +11 -0
  77. data/tasks/coverage.rake +11 -0
  78. data/tasks/spec.rake +29 -0
  79. data/tty-prompt.gemspec +32 -0
  80. metadata +243 -0
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ FileList['tasks/**/*.rake'].each(&method(:import))
6
+
7
+ desc 'Run all specs'
8
+ task ci: %w[ spec ]
@@ -0,0 +1,23 @@
1
+ ---
2
+ install:
3
+ - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
4
+ - ruby --version
5
+ - gem --version
6
+ - bundle install
7
+ build: off
8
+ test_script:
9
+ - bundle exec rake ci
10
+ environment:
11
+ matrix:
12
+ - ruby_version: "193"
13
+ - ruby_version: "200"
14
+ - ruby_version: "200-x64"
15
+ - ruby_version: "21"
16
+ - ruby_version: "21-x64"
17
+ - ruby_version: "22"
18
+ - ruby_version: "22-x64"
19
+ - ruby_version: "23"
20
+ - ruby_version: "23-x64"
21
+ matrix:
22
+ allow_failures:
23
+ - ruby_version: "193"
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+
3
+ require 'tty-prompt'
4
+ require 'benchmark/ips'
5
+ require 'stringio'
6
+
7
+ input = ::StringIO.new
8
+ output = ::StringIO.new
9
+ prompt = TTY::Prompt.new(input: input, output: output)
10
+
11
+ Benchmark.ips do |r|
12
+
13
+ r.report("Ruby #puts") do
14
+ output.puts "What is your name?"
15
+ end
16
+
17
+ r.report("TTY::Prompt #ask") do
18
+ prompt.ask("What is your name?")
19
+ end
20
+ end
21
+
22
+ # Calculating -------------------------------------
23
+ # Ruby #puts 34601 i/100ms
24
+ # TTY::Prompt #ask 12 i/100ms
25
+ # -------------------------------------------------
26
+ # Ruby #puts 758640.5 (±14.9%) i/s - 3736908 in 5.028562s
27
+ # TTY::Prompt #ask 63.1 (±7.9%) i/s - 324 in 5.176857s
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+
7
+ prompt.ask('What is your name?', default: ENV['USER'])
8
+
9
+ prompt.ask('Folder name?') do |q|
10
+ q.required(true)
11
+ q.validate ->(v) { return !Dir.exist?(v) }
12
+ q.messages[:valid?] = 'Folder already exists?'
13
+ q.messages[:required?] = 'Folder name must not be empty'
14
+ end
15
+
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new(prefix: '[?] ')
6
+
7
+ result = prompt.collect do
8
+ key(:name).ask('Name?')
9
+
10
+ key(:age).ask('Age?', convert: :int)
11
+
12
+ key(:address) do
13
+ key(:street).ask('Street?', required: true)
14
+ key(:city).ask('City?')
15
+ key(:zip).ask('Zip?', validate: /\A\d{3}\Z/)
16
+ end
17
+ end
18
+
19
+ puts result
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+
7
+ answer = prompt.ask('Password?', echo: false) do |q|
8
+ q.validate(/^[^\.]+\.[^\.]+/)
9
+ end
10
+
11
+ #puts answer
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+
7
+ warriors = %w(Scorpion Kano Jax)
8
+ prompt.select('Choose your destiny?', warriors, enum: ')')
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+
7
+ alfabet = ('A'..'Z').to_a
8
+
9
+ prompt.enum_select('Which letter?', alfabet, per_page: 4)
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+ choices = %w(/bin/nano /usr/bin/vim.basic /usr/bin/vim.tiny)
7
+ prompt.enum_select('Select an editor', choices, default: 2)
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ choices = [{
6
+ key: 'y',
7
+ name: 'overwrite this file',
8
+ value: :yes
9
+ }, {
10
+ key: 'n',
11
+ name: 'do not overwrite this file',
12
+ value: :no
13
+ }, {
14
+ key: 'a',
15
+ name: 'overwrite this file and all later files',
16
+ value: :all
17
+ }, {
18
+ key: 'd',
19
+ name: 'show diff',
20
+ value: :diff
21
+ }, {
22
+ key: 'q',
23
+ name: 'quit; do not overwrite this file ',
24
+ value: :quit
25
+ }]
26
+
27
+ prompt = TTY::Prompt.new
28
+
29
+ prompt.expand('Overwrite Gemfile?', choices, default: 3)
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+
7
+ prompt.ask('How do you like it on scale 1 - 10?', in: '1-10') do |q|
8
+ q.messages[:range?] = "Sorry wrong one!"
9
+ end
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+
7
+ prompt.ask('What is your name?', default: ENV['USER'])
8
+ prompt.yes?('Do you like Ruby?')
9
+ prompt.mask("What is your secret?")
10
+ prompt.select("Choose your destiny?", %w(Scorpion Kano Jax))
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt::new(interrupt: :exit)
6
+
7
+ prompt.on(:keypress) do |event|
8
+ puts "name: #{event.key.name}, value: #{event.value.dump}"
9
+ end
10
+
11
+ prompt.read_keypress
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt::new
6
+
7
+ answer = prompt.keypress("Press any key to continue")
8
+
9
+ puts "Answer: #{answer}"
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+ require 'pastel'
5
+
6
+ prompt = TTY::Prompt.new
7
+ heart = prompt.decorate('❤ ', :magenta)
8
+
9
+ res = prompt.mask('What is your secret?', mask: heart) do |q|
10
+ q.validate(/[a-z\ ]{5,15}/)
11
+ end
12
+
13
+ puts "Secret: \"#{res}\""
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+
7
+ drinks = %w(vodka beer wine whisky bourbon)
8
+ prompt.multi_select('Choose your favourite drink?', drinks, help: '(Use arrow keys and Enter to finish)')
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+
7
+ alfabet = ('A'..'Z').to_a
8
+
9
+ prompt.multi_select('Which letter?', alfabet, per_page: 5)
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt::new
6
+
7
+ answer = prompt.multiline("Description:")
8
+
9
+ puts "Answer: #{answer.inspect}"
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt::new
6
+
7
+ prompt.keypress("Press space or enter to continue, continuing automatically in :countdown ...", keys: [:space, :return], timeout: 3)
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+
7
+ warriors = %w(Scorpion Kano Jax Kitana Raiden)
8
+
9
+ prompt.on(:keypress) do |event|
10
+ if event.value == 'j'
11
+ prompt.trigger(:keydown)
12
+ end
13
+ if event.value == 'k'
14
+ prompt.trigger(:keyup)
15
+ end
16
+ end
17
+
18
+ prompt.select('Choose your destiny?', warriors)
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+
7
+ alfabet = ('A'..'Z').to_a
8
+
9
+ prompt.select('Which letter?', alfabet, per_page: 8)
@@ -0,0 +1,6 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+ prompt.slider("What size?", min: 0, max: 40, step: 1)
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+
7
+ prompt.ask('What is your username?') do |q|
8
+ q.validate(/^[^\.]+\.[^\.]+/)
9
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tty-prompt'
4
+
5
+ prompt = TTY::Prompt.new
6
+
7
+ prompt.yes?('Do you like Ruby?')
@@ -0,0 +1,4 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative 'tty/prompt'
4
+ require_relative 'tty/test_prompt'
@@ -0,0 +1,535 @@
1
+ # encoding: utf-8
2
+
3
+ require 'forwardable'
4
+ require 'pastel'
5
+ require 'tty-cursor'
6
+
7
+ require_relative 'prompt/answers_collector'
8
+ require_relative 'prompt/confirm_question'
9
+ require_relative 'prompt/expander'
10
+ require_relative 'prompt/enum_list'
11
+ require_relative 'prompt/keypress'
12
+ require_relative 'prompt/list'
13
+ require_relative 'prompt/multi_list'
14
+ require_relative 'prompt/multiline'
15
+ require_relative 'prompt/mask_question'
16
+ require_relative 'prompt/question'
17
+ require_relative 'prompt/reader'
18
+ require_relative 'prompt/slider'
19
+ require_relative 'prompt/statement'
20
+ require_relative 'prompt/suggestion'
21
+ require_relative 'prompt/utils'
22
+ require_relative 'prompt/version'
23
+
24
+ module TTY
25
+ # A main entry for asking prompt questions.
26
+ class Prompt
27
+ extend Forwardable
28
+
29
+ # Raised when wrong parameter is used to configure prompt
30
+ ConfigurationError = Class.new(StandardError)
31
+
32
+ # Raised when type conversion cannot be performed
33
+ ConversionError = Class.new(StandardError)
34
+
35
+ # Raised when the passed in validation argument is of wrong type
36
+ ValidationCoercion = Class.new(TypeError)
37
+
38
+ # Raised when the required argument is not supplied
39
+ ArgumentRequired = Class.new(ArgumentError)
40
+
41
+ # Raised when the argument validation fails
42
+ ArgumentValidation = Class.new(ArgumentError)
43
+
44
+ # Raised when the argument is not expected
45
+ InvalidArgument = Class.new(ArgumentError)
46
+
47
+ # @api private
48
+ attr_reader :input
49
+
50
+ # @api private
51
+ attr_reader :output
52
+
53
+ attr_reader :reader
54
+
55
+ attr_reader :cursor
56
+
57
+ # Prompt prefix
58
+ #
59
+ # @example
60
+ # prompt = TTY::Prompt.new(prefix: [?])
61
+ #
62
+ # @return [String]
63
+ #
64
+ # @api private
65
+ attr_reader :prefix
66
+
67
+ # Theme colors
68
+ #
69
+ # @api private
70
+ attr_reader :active_color, :help_color, :error_color, :enabled_color
71
+
72
+ def_delegators :@pastel, :decorate, :strip
73
+
74
+ def_delegators :@cursor, :clear_lines, :clear_line,
75
+ :show, :hide
76
+
77
+ def_delegators :@reader, :read_char, :read_line, :read_keypress,
78
+ :read_multiline, :on, :subscribe, :trigger
79
+
80
+ def_delegators :@output, :print, :puts, :flush
81
+
82
+ def self.messages
83
+ {
84
+ range?: 'Value %{value} must be within the range %{in}',
85
+ valid?: 'Your answer is invalid (must match %{valid})',
86
+ required?: 'Value must be provided'
87
+ }
88
+ end
89
+
90
+ # Initialize a Prompt
91
+ #
92
+ # @param [Hash] options
93
+ # @option options [IO] :input
94
+ # the input stream
95
+ # @option options [IO] :output
96
+ # the output stream
97
+ # @option options [Hash] :env
98
+ # the environment variables
99
+ # @option options [String] :prefix
100
+ # the prompt prefix, by default empty
101
+ # @option options [Boolean] :enable_color
102
+ # enable color support, true by default
103
+ # @option options [String] :active_color
104
+ # the color used for selected option
105
+ # @option options [String] :help_color
106
+ # the color used for help text
107
+ # @option options [String] :error_color
108
+ # the color used for displaying error messages
109
+ # @option options [Symbol] :interrupt
110
+ # handling of Ctrl+C key out of :signal, :exit, :noop
111
+ # @option options [Boolean] :track_history
112
+ # disable line history tracking, true by default
113
+ #
114
+ # @api public
115
+ def initialize(*args)
116
+ options = Utils.extract_options!(args)
117
+ @input = options.fetch(:input) { $stdin }
118
+ @output = options.fetch(:output) { $stdout }
119
+ @env = options.fetch(:env) { ENV }
120
+ @prefix = options.fetch(:prefix) { '' }
121
+ @enabled_color = options[:enable_color]
122
+ @active_color = options.fetch(:active_color) { :green }
123
+ @help_color = options.fetch(:help_color) { :bright_black }
124
+ @error_color = options.fetch(:error_color) { :red }
125
+ @interrupt = options.fetch(:interrupt) { :error }
126
+ @track_history = options.fetch(:track_history) { true }
127
+
128
+ @cursor = TTY::Cursor
129
+ @pastel = Pastel.new(@enabled_color.nil? ? {} : { enabled: @enabled_color })
130
+ @reader = Reader.new(@input, @output, interrupt: @interrupt,
131
+ track_history: @track_history, env: @env)
132
+ end
133
+
134
+ # Invoke a question type of prompt
135
+ #
136
+ # @example
137
+ # prompt = TTY::Prompt.new
138
+ # prompt.invoke_question(Question, "Your name? ")
139
+ #
140
+ # @return [String]
141
+ #
142
+ # @api public
143
+ def invoke_question(object, message, *args, &block)
144
+ options = Utils.extract_options!(args)
145
+ options[:messages] = self.class.messages
146
+ question = object.new(self, options)
147
+ question.(message, &block)
148
+ end
149
+
150
+ # Ask a question.
151
+ #
152
+ # @example
153
+ # propmt = TTY::Prompt.new
154
+ # prompt.ask("What is your name?")
155
+ #
156
+ # @param [String] message
157
+ # the question to be asked
158
+ #
159
+ # @yieldparam [TTY::Prompt::Question] question
160
+ # further configure the question
161
+ #
162
+ # @yield [question]
163
+ #
164
+ # @return [TTY::Prompt::Question]
165
+ #
166
+ # @api public
167
+ def ask(message, *args, &block)
168
+ invoke_question(Question, message, *args, &block)
169
+ end
170
+
171
+ # Ask a question with a keypress answer
172
+ #
173
+ # @see #ask
174
+ #
175
+ # @api public
176
+ def keypress(message, *args, &block)
177
+ invoke_question(Keypress, message, *args, &block)
178
+ end
179
+
180
+ # Ask a question with a multiline answer
181
+ #
182
+ # @example
183
+ # prompt.multiline('Description?')
184
+ #
185
+ # @return [Array[String]]
186
+ #
187
+ # @api public
188
+ def multiline(message, *args, &block)
189
+ invoke_question(Multiline, message, *args, &block)
190
+ end
191
+
192
+ # Invoke a list type of prompt
193
+ #
194
+ # @example
195
+ # prompt = TTY::Prompt.new
196
+ # editors = %w(emacs nano vim)
197
+ # prompt.invoke_select(EnumList, "Select editor: ", editors)
198
+ #
199
+ # @return [String]
200
+ #
201
+ # @api public
202
+ def invoke_select(object, question, *args, &block)
203
+ options = Utils.extract_options!(args)
204
+ choices = if block
205
+ []
206
+ elsif args.empty?
207
+ possible = options.dup
208
+ options = {}
209
+ possible
210
+ elsif args.size == 1 && args[0].is_a?(Hash)
211
+ Utils.extract_options!(args)
212
+ else
213
+ args.flatten
214
+ end
215
+
216
+ list = object.new(self, options)
217
+ list.(question, choices, &block)
218
+ end
219
+
220
+ # Ask masked question
221
+ #
222
+ # @example
223
+ # propmt = TTY::Prompt.new
224
+ # prompt.mask("What is your secret?")
225
+ #
226
+ # @return [TTY::Prompt::MaskQuestion]
227
+ #
228
+ # @api public
229
+ def mask(message, *args, &block)
230
+ invoke_question(MaskQuestion, message, *args, &block)
231
+ end
232
+
233
+ # Ask a question with a list of options
234
+ #
235
+ # @example
236
+ # prompt = TTY::Prompt.new
237
+ # prompt.select("What size?", %w(large medium small))
238
+ #
239
+ # @example
240
+ # prompt = TTY::Prompt.new
241
+ # prompt.select("What size?") do |menu|
242
+ # menu.choice :large
243
+ # menu.choices %w(:medium :small)
244
+ # end
245
+ #
246
+ # @param [String] question
247
+ # the question to ask
248
+ #
249
+ # @param [Array[Object]] choices
250
+ # the choices to select from
251
+ #
252
+ # @api public
253
+ def select(question, *args, &block)
254
+ invoke_select(List, question, *args, &block)
255
+ end
256
+
257
+ # Ask a question with multiple attributes activated
258
+ #
259
+ # @example
260
+ # prompt = TTY::Prompt.new
261
+ # choices = %w(Scorpion Jax Kitana Baraka Jade)
262
+ # prompt.multi_select("Choose your destiny?", choices)
263
+ #
264
+ # @param [String] question
265
+ # the question to ask
266
+ #
267
+ # @param [Array[Object]] choices
268
+ # the choices to select from
269
+ #
270
+ # @return [String]
271
+ #
272
+ # @api public
273
+ def multi_select(question, *args, &block)
274
+ invoke_select(MultiList, question, *args, &block)
275
+ end
276
+
277
+ # Ask a question with indexed list
278
+ #
279
+ # @example
280
+ # prompt = TTY::Prompt.new
281
+ # editors = %w(emacs nano vim)
282
+ # prompt.enum_select(EnumList, "Select editor: ", editors)
283
+ #
284
+ # @param [String] question
285
+ # the question to ask
286
+ #
287
+ # @param [Array[Object]] choices
288
+ # the choices to select from
289
+ #
290
+ # @return [String]
291
+ #
292
+ # @api public
293
+ def enum_select(question, *args, &block)
294
+ invoke_select(EnumList, question, *args, &block)
295
+ end
296
+
297
+ # A shortcut method to ask the user positive question and return
298
+ # true for 'yes' reply, false for 'no'.
299
+ #
300
+ # @example
301
+ # prompt = TTY::Prompt.new
302
+ # prompt.yes?('Are you human?')
303
+ # # => Are you human? (Y/n)
304
+ #
305
+ # @return [Boolean]
306
+ #
307
+ # @api public
308
+ def yes?(message, *args, &block)
309
+ defaults = { default: true }
310
+ options = Utils.extract_options!(args)
311
+ options.merge!(defaults.reject { |k, _| options.key?(k) })
312
+
313
+ question = ConfirmQuestion.new(self, options)
314
+ question.call(message, &block)
315
+ end
316
+
317
+ # A shortcut method to ask the user negative question and return
318
+ # true for 'no' reply.
319
+ #
320
+ # @example
321
+ # prompt = TTY::Prompt.new
322
+ # prompt.no?('Are you alien?') # => true
323
+ # # => Are you human? (y/N)
324
+ #
325
+ # @return [Boolean]
326
+ #
327
+ # @api public
328
+ def no?(message, *args, &block)
329
+ defaults = { default: false }
330
+ options = Utils.extract_options!(args)
331
+ options.merge!(defaults.reject { |k, _| options.key?(k) })
332
+
333
+ question = ConfirmQuestion.new(self, options)
334
+ !question.call(message, &block)
335
+ end
336
+
337
+ # Expand available options
338
+ #
339
+ # @example
340
+ # prompt = TTY::Prompt.new
341
+ # choices = [{
342
+ # key: 'Y',
343
+ # name: 'Overwrite',
344
+ # value: :yes
345
+ # }, {
346
+ # key: 'n',
347
+ # name: 'Skip',
348
+ # value: :no
349
+ # }]
350
+ # prompt.expand('Overwirte Gemfile?', choices)
351
+ #
352
+ # @return [Object]
353
+ # the user specified value
354
+ #
355
+ # @api public
356
+ def expand(message, *args, &block)
357
+ invoke_select(Expander, message, *args, &block)
358
+ end
359
+
360
+ # Ask a question with a range slider
361
+ #
362
+ # @example
363
+ # prompt = TTY::Prompt.new
364
+ # prompt.slider('What size?', min: 32, max: 54, step: 2)
365
+ #
366
+ # @param [String] question
367
+ # the question to ask
368
+ #
369
+ # @return [String]
370
+ #
371
+ # @api public
372
+ def slider(question, *args, &block)
373
+ options = Utils.extract_options!(args)
374
+ slider = Slider.new(self, options)
375
+ slider.call(question, &block)
376
+ end
377
+
378
+ # Print statement out. If the supplied message ends with a space or
379
+ # tab character, a new line will not be appended.
380
+ #
381
+ # @example
382
+ # say("Simple things.", color: :red)
383
+ #
384
+ # @param [String] message
385
+ #
386
+ # @return [String]
387
+ #
388
+ # @api public
389
+ def say(message = '', options = {})
390
+ message = message.to_s
391
+ return if message.empty?
392
+
393
+ statement = Statement.new(self, options)
394
+ statement.call(message)
395
+ end
396
+
397
+ # Print statement(s) out in red green.
398
+ #
399
+ # @example
400
+ # prompt.ok "Are you sure?"
401
+ # prompt.ok "All is fine!", "This is fine too."
402
+ #
403
+ # @param [Array] messages
404
+ #
405
+ # @return [Array] messages
406
+ #
407
+ # @api public
408
+ def ok(*args)
409
+ options = Utils.extract_options!(args)
410
+ args.each { |message| say message, options.merge(color: :green) }
411
+ end
412
+
413
+ # Print statement(s) out in yellow color.
414
+ #
415
+ # @example
416
+ # prompt.warn "This action can have dire consequences"
417
+ # prompt.warn "Carefull young apprentice", "This is potentially dangerous"
418
+ #
419
+ # @param [Array] messages
420
+ #
421
+ # @return [Array] messages
422
+ #
423
+ # @api public
424
+ def warn(*args)
425
+ options = Utils.extract_options!(args)
426
+ args.each { |message| say message, options.merge(color: :yellow) }
427
+ end
428
+
429
+ # Print statement(s) out in red color.
430
+ #
431
+ # @example
432
+ # prompt.error "Shutting down all systems!"
433
+ # prompt.error "Nothing is fine!", "All is broken!"
434
+ #
435
+ # @param [Array] messages
436
+ #
437
+ # @return [Array] messages
438
+ #
439
+ # @api public
440
+ def error(*args)
441
+ options = Utils.extract_options!(args)
442
+ args.each { |message| say message, options.merge(color: :red) }
443
+ end
444
+
445
+ # Takes the string provided by the user and compare it with other possible
446
+ # matches to suggest an unambigous string
447
+ #
448
+ # @example
449
+ # prompt.suggest('sta', ['status', 'stage', 'commit', 'branch'])
450
+ # # => "status, stage"
451
+ #
452
+ # @param [String] message
453
+ #
454
+ # @param [Array] possibilities
455
+ #
456
+ # @param [Hash] options
457
+ # @option options [String] :indent
458
+ # The number of spaces for indentation
459
+ # @option options [String] :single_text
460
+ # The text for a single suggestion
461
+ # @option options [String] :plural_text
462
+ # The text for multiple suggestions
463
+ #
464
+ # @return [String]
465
+ #
466
+ # @api public
467
+ def suggest(message, possibilities, options = {})
468
+ suggestion = Suggestion.new(options)
469
+ say(suggestion.suggest(message, possibilities))
470
+ end
471
+
472
+ # Gathers more than one aswer
473
+ #
474
+ # @example
475
+ # prompt.collect do
476
+ # key(:name).ask('Name?')
477
+ # end
478
+ #
479
+ # @return [Hash]
480
+ # the collection of answers
481
+ #
482
+ # @api public
483
+ def collect(options = {}, &block)
484
+ collector = AnswersCollector.new(self, options)
485
+ collector.call(&block)
486
+ end
487
+
488
+ # Check if outputing to terminal
489
+ #
490
+ # @return [Boolean]
491
+ #
492
+ # @api public
493
+ def tty?
494
+ stdout.tty?
495
+ end
496
+
497
+ # Return standard in
498
+ #
499
+ # @api private
500
+ def stdin
501
+ $stdin
502
+ end
503
+
504
+ # Return standard out
505
+ #
506
+ # @api private
507
+ def stdout
508
+ $stdout
509
+ end
510
+
511
+ # Return standard error
512
+ #
513
+ # @api private
514
+ def stderr
515
+ $stderr
516
+ end
517
+
518
+ # Inspect class name and public attributes
519
+ # @return [String]
520
+ #
521
+ # @api public
522
+ def inspect
523
+ attributes = {
524
+ input: input,
525
+ output: output,
526
+ prefix: prefix,
527
+ active_color: active_color,
528
+ error_color: error_color,
529
+ enabled_color: enabled_color,
530
+ help_color: help_color
531
+ }
532
+ "#<#{self.class}: #{attributes.each { |name, val| "@#{name}=#{val}" }}"
533
+ end
534
+ end # Prompt
535
+ end # TTY