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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.travis.yml +25 -0
- data/CHANGELOG.md +218 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +19 -0
- data/LICENSE.txt +22 -0
- data/README.md +1132 -0
- data/Rakefile +8 -0
- data/appveyor.yml +23 -0
- data/benchmarks/speed.rb +27 -0
- data/examples/ask.rb +15 -0
- data/examples/collect.rb +19 -0
- data/examples/echo.rb +11 -0
- data/examples/enum.rb +8 -0
- data/examples/enum_paged.rb +9 -0
- data/examples/enum_select.rb +7 -0
- data/examples/expand.rb +29 -0
- data/examples/in.rb +9 -0
- data/examples/inputs.rb +10 -0
- data/examples/key_events.rb +11 -0
- data/examples/keypress.rb +9 -0
- data/examples/mask.rb +13 -0
- data/examples/multi_select.rb +8 -0
- data/examples/multi_select_paged.rb +9 -0
- data/examples/multiline.rb +9 -0
- data/examples/pause.rb +7 -0
- data/examples/select.rb +18 -0
- data/examples/select_paginated.rb +9 -0
- data/examples/slider.rb +6 -0
- data/examples/validation.rb +9 -0
- data/examples/yes_no.rb +7 -0
- data/lib/tty-prompt.rb +4 -0
- data/lib/tty/prompt.rb +535 -0
- data/lib/tty/prompt/answers_collector.rb +59 -0
- data/lib/tty/prompt/choice.rb +90 -0
- data/lib/tty/prompt/choices.rb +110 -0
- data/lib/tty/prompt/confirm_question.rb +129 -0
- data/lib/tty/prompt/converter_dsl.rb +22 -0
- data/lib/tty/prompt/converter_registry.rb +64 -0
- data/lib/tty/prompt/converters.rb +77 -0
- data/lib/tty/prompt/distance.rb +49 -0
- data/lib/tty/prompt/enum_list.rb +337 -0
- data/lib/tty/prompt/enum_paginator.rb +56 -0
- data/lib/tty/prompt/evaluator.rb +29 -0
- data/lib/tty/prompt/expander.rb +292 -0
- data/lib/tty/prompt/keypress.rb +94 -0
- data/lib/tty/prompt/list.rb +317 -0
- data/lib/tty/prompt/mask_question.rb +91 -0
- data/lib/tty/prompt/multi_list.rb +108 -0
- data/lib/tty/prompt/multiline.rb +71 -0
- data/lib/tty/prompt/paginator.rb +88 -0
- data/lib/tty/prompt/question.rb +333 -0
- data/lib/tty/prompt/question/checks.rb +87 -0
- data/lib/tty/prompt/question/modifier.rb +94 -0
- data/lib/tty/prompt/question/validation.rb +72 -0
- data/lib/tty/prompt/reader.rb +352 -0
- data/lib/tty/prompt/reader/codes.rb +121 -0
- data/lib/tty/prompt/reader/console.rb +57 -0
- data/lib/tty/prompt/reader/history.rb +145 -0
- data/lib/tty/prompt/reader/key_event.rb +91 -0
- data/lib/tty/prompt/reader/line.rb +162 -0
- data/lib/tty/prompt/reader/mode.rb +44 -0
- data/lib/tty/prompt/reader/win_api.rb +29 -0
- data/lib/tty/prompt/reader/win_console.rb +53 -0
- data/lib/tty/prompt/result.rb +42 -0
- data/lib/tty/prompt/slider.rb +182 -0
- data/lib/tty/prompt/statement.rb +55 -0
- data/lib/tty/prompt/suggestion.rb +115 -0
- data/lib/tty/prompt/symbols.rb +61 -0
- data/lib/tty/prompt/timeout.rb +69 -0
- data/lib/tty/prompt/utils.rb +44 -0
- data/lib/tty/prompt/version.rb +7 -0
- data/lib/tty/test_prompt.rb +20 -0
- data/tasks/console.rake +11 -0
- data/tasks/coverage.rake +11 -0
- data/tasks/spec.rake +29 -0
- data/tty-prompt.gemspec +32 -0
- metadata +243 -0
data/Rakefile
ADDED
data/appveyor.yml
ADDED
@@ -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"
|
data/benchmarks/speed.rb
ADDED
@@ -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
|
data/examples/ask.rb
ADDED
@@ -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
|
+
|
data/examples/collect.rb
ADDED
@@ -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
|
data/examples/echo.rb
ADDED
data/examples/enum.rb
ADDED
data/examples/expand.rb
ADDED
@@ -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)
|
data/examples/in.rb
ADDED
data/examples/inputs.rb
ADDED
@@ -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))
|
data/examples/mask.rb
ADDED
@@ -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}\""
|
data/examples/pause.rb
ADDED
data/examples/select.rb
ADDED
@@ -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)
|
data/examples/slider.rb
ADDED
data/examples/yes_no.rb
ADDED
data/lib/tty-prompt.rb
ADDED
data/lib/tty/prompt.rb
ADDED
@@ -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
|