austb-tty-prompt 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
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