flowengine 0.4.0 → 0.4.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.
@@ -0,0 +1,1746 @@
1
+ <div align="center">
2
+ <a href="https://ttytoolkit.org"><img width="130" src="https://github.com/piotrmurach/tty/raw/master/images/tty.png" alt="TTY Toolkit logo" /></a>
3
+ </div>
4
+
5
+ # TTY::Prompt
6
+
7
+ [![Gem Version](https://badge.fury.io/rb/tty-prompt.svg)][gem]
8
+ [![Actions CI](https://github.com/piotrmurach/tty-prompt/actions/workflows/ci.yml/badge.svg)][gh_actions_ci]
9
+ [![Build status](https://ci.appveyor.com/api/projects/status/4cguoiah5dprbq7n?svg=true)][appveyor]
10
+ [![Code Climate](https://codeclimate.com/github/piotrmurach/tty-prompt/badges/gpa.svg)][codeclimate]
11
+ [![Coverage Status](https://coveralls.io/repos/github/piotrmurach/tty-prompt/badge.svg)][coverage]
12
+
13
+ [gem]: http://badge.fury.io/rb/tty-prompt
14
+ [gh_actions_ci]: https://github.com/piotrmurach/tty-prompt/actions/workflows/ci.yml
15
+ [travis]: http://travis-ci.org/piotrmurach/tty-prompt
16
+ [appveyor]: https://ci.appveyor.com/project/piotrmurach/tty-prompt
17
+ [codeclimate]: https://codeclimate.com/github/piotrmurach/tty-prompt
18
+ [coverage]: https://coveralls.io/github/piotrmurach/tty-prompt
19
+
20
+ > A beautiful and powerful interactive command line prompt.
21
+
22
+ **TTY::Prompt** provides independent prompt component for [TTY](https://github.com/piotrmurach/tty) toolkit.
23
+
24
+ ## Features
25
+
26
+ * Number of prompt types for gathering user input
27
+ * A robust API for validating complex inputs
28
+ * User friendly error feedback
29
+ * Intuitive DSL for creating complex menus
30
+ * Ability to page long menus
31
+ * Support for Linux, OS X, FreeBSD and Windows systems
32
+
33
+ ## Windows support
34
+
35
+ `tty-prompt` works across all Unix and Windows systems in the "best possible" way. On Windows, it uses Win32 API in place of terminal device to provide matching functionality.
36
+
37
+ Since Unix terminals provide richer set of features than Windows PowerShell consoles, expect to have a better experience on Unix-like platform.
38
+
39
+ Some features like `select` or `multi_select` menus may not work on Windows when run from Git Bash. See GitHub suggested [fixes](https://github.com/git-for-windows/git/wiki/FAQ#some-native-console-programs-dont-work-when-run-from-git-bash-how-to-fix-it).
40
+
41
+ For Windows, consider installing [ConEmu](https://conemu.github.io/), [cmder](http://cmder.net/) or [PowerCmd](http://www.powercmd.com/).
42
+
43
+ ## Installation
44
+
45
+ Add this line to your application's Gemfile:
46
+
47
+ ```ruby
48
+ gem "tty-prompt"
49
+ ```
50
+
51
+ And then execute:
52
+
53
+ $ bundle
54
+
55
+ Or install it yourself as:
56
+
57
+ $ gem install tty-prompt
58
+
59
+ ## Contents
60
+
61
+ * [1. Usage](#1-usage)
62
+ * [2. Interface](#2-interface)
63
+ * [2.1 ask](#21-ask)
64
+ * [2.1.1 :convert](#211-convert)
65
+ * [2.1.2 :default](#212-default)
66
+ * [2.1.3 :value](#213-value)
67
+ * [2.1.4 :echo](#214-echo)
68
+ * [2.1.5 error messages](#215-error-messages)
69
+ * [2.1.6 :in](#216-in)
70
+ * [2.1.7 :modify](#217-modify)
71
+ * [2.1.8 :required](#218-required)
72
+ * [2.1.9 :validate](#219-validate)
73
+ * [2.2 keypress](#22-keypress)
74
+ * [2.2.1 :timeout](#221-timeout)
75
+ * [2.3 multiline](#23-multiline)
76
+ * [2.4 mask](#24-mask)
77
+ * [2.5 yes?/no?](#25-yesno)
78
+ * [2.6 menu](#26-menu)
79
+ * [2.6.1 choices](#261-choices)
80
+ * [2.6.1.1 :disabled](#2611-disabled)
81
+ * [2.6.2 select](#262-select)
82
+ * [2.6.2.1 :cycle](#2621-cycle)
83
+ * [2.6.2.2 :enum](#2622-enum)
84
+ * [2.6.2.3 :help](#2623-help)
85
+ * [2.6.2.4 :marker](#2624-marker)
86
+ * [2.6.2.5 :per_page](#2625-per_page)
87
+ * [2.6.2.6 :disabled](#2626-disabled)
88
+ * [2.6.2.7 :filter](#2627-filter)
89
+ * [2.6.3 multi_select](#263-multi_select)
90
+ * [2.6.3.1 :cycle](#2631-cycle)
91
+ * [2.6.3.2 :enum](#2632-enum)
92
+ * [2.6.3.3 :help](#2633-help)
93
+ * [2.6.3.4 :per_page](#2634-per_page)
94
+ * [2.6.3.5 :disabled](#2635-disabled)
95
+ * [2.6.3.6 :echo](#2636-echo)
96
+ * [2.6.3.7 :filter](#2637-filter)
97
+ * [2.6.3.8 :min](#2638-min)
98
+ * [2.6.3.9 :max](#2639-max)
99
+ * [2.6.4 enum_select](#264-enum_select)
100
+ * [2.6.4.1 :per_page](#2641-per_page)
101
+ * [2.6.4.1 :disabled](#2641-disabled)
102
+ * [2.7 expand](#27-expand)
103
+ * [2.7.1 :auto_hint](#271-auto_hint)
104
+ * [2.8 collect](#28-collect)
105
+ * [2.9 suggest](#29-suggest)
106
+ * [2.10 slider](#210-slider)
107
+ * [2.11 say](#211-say)
108
+ * [2.11.1 ok](#2111-ok)
109
+ * [2.11.2 warn](#2112-warn)
110
+ * [2.11.3 error](#2113-error)
111
+ * [2.12 keyboard events](#212-keyboard-events)
112
+ * [3. settings](#3-settings)
113
+ * [3.1 :symbols](#31-symbols)
114
+ * [3.2 :active_color](#32-active_color)
115
+ * [3.3 :enable_color](#33-enable_color)
116
+ * [3.4 :help_color](#34-help_color)
117
+ * [3.5 :interrupt](#35-interrupt)
118
+ * [3.6 :prefix](#36-prefix)
119
+ * [3.7 :quiet](#37-quiet)
120
+ * [3.8 :track_history](#38-track_history)
121
+
122
+ ## 1. Usage
123
+
124
+ In order to start asking questions on the command line, create prompt:
125
+
126
+ ```ruby
127
+ require "tty-prompt"
128
+
129
+ prompt = TTY::Prompt.new
130
+ ```
131
+
132
+ And then call `ask` with the question for simple input:
133
+
134
+ ```ruby
135
+ prompt.ask("What is your name?", default: ENV["USER"])
136
+ # => What is your name? (piotr)
137
+ ```
138
+
139
+ To confirm input use `yes?`:
140
+
141
+ ```ruby
142
+ prompt.yes?("Do you like Ruby?")
143
+ # => Do you like Ruby? (Y/n)
144
+ ```
145
+
146
+ If you want to input password or secret information use `mask`:
147
+
148
+ ```ruby
149
+ prompt.mask("What is your secret?")
150
+ # => What is your secret? ••••
151
+ ```
152
+
153
+ Asking question with list of options couldn't be easier using `select` like so:
154
+
155
+ ```ruby
156
+ prompt.select("Choose your destiny?", %w(Scorpion Kano Jax))
157
+ # =>
158
+ # Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select)
159
+ # ‣ Scorpion
160
+ # Kano
161
+ # Jax
162
+ ```
163
+
164
+ Also, asking multiple choice questions is a breeze with `multi_select`:
165
+
166
+ ```ruby
167
+ choices = %w(vodka beer wine whisky bourbon)
168
+ prompt.multi_select("Select drinks?", choices)
169
+ # =>
170
+ #
171
+ # Select drinks? (Use ↑/↓ arrow keys, press Space to select and Enter to finish)"
172
+ # ‣ ⬡ vodka
173
+ # ⬡ beer
174
+ # ⬡ wine
175
+ # ⬡ whisky
176
+ # ⬡ bourbon
177
+ ```
178
+
179
+ To ask for a selection from enumerated list you can use `enum_select`:
180
+
181
+ ```ruby
182
+ choices = %w(emacs nano vim)
183
+ prompt.enum_select("Select an editor?", choices)
184
+ # =>
185
+ #
186
+ # Select an editor?
187
+ # 1) emacs
188
+ # 2) nano
189
+ # 3) vim
190
+ # Choose 1-3 [1]:
191
+ ```
192
+
193
+ However, if you have a lot of options to choose from you may want to use `expand`:
194
+
195
+ ```ruby
196
+ choices = [
197
+ { key: "y", name: "overwrite this fil", name: "do not overwrite this file", value: :no },
198
+ { key: "a", name: "overwrite this file and all later files", value: :all },
199
+ { key: "d", name: "show diff", value: :diff },
200
+ { key: "q", name: "quit; do not overwrite this file ", value: :quit }
201
+ ]
202
+ prompt.expand("Overwrite Gemfile?", choices)
203
+ # =>
204
+ # Overwrite Gemfile? (enter "h" for help) [y,n,a,d,q,h]
205
+ ```
206
+
207
+ If you wish to collect more than one answer use `collect`:
208
+
209
+ ```ruby
210
+ result = prompt.collect do
211
+ key(:name).ask("Name?")
212
+
213
+ key(:age).ask("Age?", convert: :int)
214
+
215
+ key(:address) do
216
+ key(:street).ask("Street?", required: true)
217
+ key(:city).ask("City?")
218
+ key(:zip).ask("Zip?", validate: /\A\d{3}\Z/)
219
+ end
220
+ end
221
+ # =>
222
+ # {:name => "Piotr", :age => 30, :address => {:street => "Street", :city => "City", :zip => "123"}}
223
+ ```
224
+
225
+ ## 2. Interface
226
+
227
+ ### 2.1 ask
228
+
229
+ In order to ask a basic question do:
230
+
231
+ ```ruby
232
+ prompt.ask("What is your name?")
233
+ ```
234
+
235
+ However, to prompt for more complex input you can use robust API by passing hash of properties or using a block like so:
236
+
237
+ ```ruby
238
+ prompt.ask("What is your name?") do |q|
239
+ q.required true
240
+ q.validate /\A\w+\Z/
241
+ q.modify :capitalize
242
+ end
243
+ ```
244
+
245
+ #### 2.1.1 `:convert`
246
+
247
+ The `convert` property is used to convert input to a required type.
248
+
249
+ By default no conversion of input is performed. To change this use one of the following conversions:
250
+
251
+ * `:boolean`|`:bool` - e.g. 'yes/1/y/t/' becomes `true`, 'no/0/n/f' becomes `false`
252
+ * `:date` - parses dates formats "28/03/2020", "March 28th 2020"
253
+ * `:time` - parses time formats "11:20:03"
254
+ * `:float` - e.g. `-1` becomes `-1.0`
255
+ * `:int`|`:integer` - e.g. `+1` becomes `1`
256
+ * `:sym`|`:symbol` - e.g. "foo" becomes `:foo`
257
+ * `:filepath` - converts to file path
258
+ * `:path`|`:pathname` - converts to `Pathname` object
259
+ * `:range` - e.g. '1-10' becomes `1..10` range object
260
+ * `:regexp` - e.g. "foo|bar" becomes `/foo|bar/`
261
+ * `:uri` - converts to `URI` object
262
+ * `:list`|`:array` - e.g. 'a,b,c' becomes `["a", "b", "c"]`
263
+ * `:map`|`:hash` - e.g. 'a:1 b:2 c:3' becomes `{a: "1", b: "2", c: "3"}`
264
+
265
+ In addition you can specify a plural or append `list` or `array` to any base type:
266
+
267
+ * `:ints` or `:int_list` - will convert to a list of integers
268
+ * `:floats` or `:float_list` - will convert to a list of floats
269
+ * `:bools` or `:bool_list` - will convert to a list of booleans, e.g. `t,f,t` becomes `[true, false, true]`
270
+
271
+ Similarly, you can append `map` or `hash` to any base type:
272
+
273
+ * `:int_map`|`:integer_map`|`:int_hash` - will convert to a hash of integers, e.g `a:1 b:2 c:3` becomes `{a: 1, b: 2, c: 3}`
274
+ * `:bool_map` | `:boolean_map`|`:bool_hash` - will convert to a hash of booleans, e.g `a:t b:f c:t` becomes `{a: true, b: false, c: true}`
275
+
276
+ By default, `map` converts keys to symbols, if you wish to use strings instead specify key type like so:
277
+
278
+ * `:str_int_map` - will convert to a hash of string keys and integer values
279
+ * `:string_integer_hash` - will convert to a hash of string keys and integer values
280
+
281
+ For example, if you are interested in range type as answer do the following:
282
+
283
+ ```ruby
284
+ prompt.ask("Provide range of numbers?", convert: :range)
285
+ # Provide range of numbers? 1-10
286
+ # => 1..10
287
+ ```
288
+
289
+ If, on the other hand, you wish to convert input to a hash of integer values do:
290
+
291
+ ```ruby
292
+ prompt.ask("Provide keys and values:", convert: :int_map)
293
+ # Provide keys and values: a=1 b=2 c=3
294
+ # => {a: 1, b: 2, c: 3}
295
+ ```
296
+
297
+ If a user provides a wrong type for conversion an error message will be printed in the console:
298
+
299
+ ```ruby
300
+ prompt.ask("Provide digit:", convert: :float)
301
+ # Provide digit: x
302
+ # >> Cannot convert `x` into 'float' type
303
+ ```
304
+
305
+ You can further customize error message:
306
+
307
+ ```ruby
308
+ prompt.ask("Provide digit:", convert: :float) do |q|
309
+ q.convert(:float, "Wrong value of %{value} for %{type} conversion")
310
+ # or
311
+ q.convert :float
312
+ q.messages[:convert?] = "Wrong value of %{value} for %{type} conversion"
313
+ end
314
+ ```
315
+
316
+ You can also provide a custom conversion like so:
317
+
318
+ ```ruby
319
+ prompt.ask("Ingredients? (comma sep list)") do |q|
320
+ q.convert -> (input) { input.split(/,\s*/) }
321
+ end
322
+ # Ingredients? (comma sep list) milk, eggs, flour
323
+ # => ["milk", "eggs", "flour"]
324
+ ```
325
+
326
+ #### 2.1.2 `:default`
327
+
328
+ The `:default` option is used if the user presses return key:
329
+
330
+ ```ruby
331
+ prompt.ask("What is your name?", default: "Anonymous")
332
+ # =>
333
+ # What is your name? (Anonymous)
334
+ ```
335
+
336
+ #### 2.1.3 `:value`
337
+
338
+ To pre-populate the input line for editing use `:value` option:
339
+
340
+ ```ruby
341
+ prompt.ask("What is your name?", value: "Piotr")
342
+ # =>
343
+ # What is your name? Piotr
344
+ ```
345
+
346
+ #### 2.1.4 `:echo`
347
+
348
+ To control whether the input is shown back in terminal or not use `:echo` option like so:
349
+
350
+ ```ruby
351
+ prompt.ask("password:", echo: false)
352
+ ```
353
+
354
+ #### 2.1.5 error messages
355
+
356
+ By default `tty-prompt` comes with predefined error messages for `convert`, `required`, `in`, `validate` options.
357
+
358
+ You can change these and configure to your liking either by passing message as second argument with the option:
359
+
360
+ ```ruby
361
+ prompt.ask("What is your email?") do |q|
362
+ q.validate(/\A\w+@\w+\.\w+\Z/, "Invalid email address")
363
+ end
364
+ ```
365
+
366
+ Or change the `messages` key entry out of `:convert?`, `:range?`, `:required?` and `:valid?`:
367
+
368
+ ```ruby
369
+ prompt.ask("What is your email?") do |q|
370
+ q.validate(/\A\w+@\w+\.\w+\Z/)
371
+ q.messages[:valid?] = "Invalid email address"
372
+ end
373
+ ```
374
+
375
+ To change default range validation error message do:
376
+
377
+ ```ruby
378
+ prompt.ask("How spicy on scale (1-5)? ") do |q|
379
+ q.in "1-5"
380
+ q.messages[:range?] = "%{value} out of expected range %{in}"
381
+ end
382
+ ```
383
+
384
+ #### 2.1.6 `:in`
385
+
386
+ In order to check that provided input falls inside a range of inputs use the `in` option. For example, if we wanted to ask a user for a single digit in given range we may do following:
387
+
388
+ ```ruby
389
+ prompt.ask("Provide number in range: 0-9?") { |q| q.in("0-9") }
390
+ ```
391
+
392
+ #### 2.1.7 `:modify`
393
+
394
+ Set the `:modify` option if you want to handle whitespace or letter capitalization.
395
+
396
+ ```ruby
397
+ prompt.ask("Enter text:") do |q|
398
+ q.modify :strip, :collapse
399
+ end
400
+ ```
401
+
402
+ Available letter casing settings are:
403
+ ```ruby
404
+ :up # change to upper case
405
+ :down # change to small case
406
+ :capitalize # capitalize each word
407
+ ```
408
+
409
+ Available whitespace settings are:
410
+
411
+ ```ruby
412
+ :trim # remove whitespace from both ends of the input
413
+ :strip # same as :trim
414
+ :chomp # remove whitespace at the end of input
415
+ :collapse # reduce all whitespace to single character
416
+ :remove # remove all whitespace
417
+ ```
418
+
419
+ #### 2.1.8 `:required`
420
+
421
+ To ensure that input is provided use `:required` option:
422
+
423
+ ```ruby
424
+ prompt.ask("What's your phone number?", required: true)
425
+ # What's your phone number?
426
+ # >> Value must be provided
427
+ ```
428
+
429
+ #### 2.1.9 `:validate`
430
+
431
+ In order to validate that input matches a given pattern you can pass the `validate` option/method.
432
+
433
+ Validate accepts `Regex`, `Proc` or `Symbol`.
434
+
435
+ ```ruby
436
+ prompt.ask("What is your username?") do |q|
437
+ q.validate(/\A[^.]+\.[^.]+\Z/)
438
+ end
439
+ ```
440
+
441
+ The above can also be expressed as a `Proc`:
442
+
443
+ ```ruby
444
+ prompt.ask("What is your username?") do |q|
445
+ q.validate ->(input) { input =~ /\A[^.]+\.[^.]+\Z/ }
446
+ end
447
+ ```
448
+
449
+ There is a built-in validation for `:email` and you can use it directly like so:
450
+
451
+ ```ruby
452
+ prompt.ask("What is your email?") { |q| q.validate :email }
453
+ ```
454
+
455
+ The default validation message is `"Your answer is invalid (must match %{valid})"` and you can customise it by passing in a second argument:
456
+
457
+ ```ruby
458
+ prompt.ask("What is your username?") do |q|
459
+ q.validate(/\A[^.]+\.[^.]+\Z/, "Invalid username: %{value}, must match %{valid}")
460
+ end
461
+ ```
462
+
463
+ The default message can also be set using `messages` and the `:valid?` key:
464
+
465
+ ```ruby
466
+ prompt.ask("What is your username?") do |q|
467
+ q.validate(/\A[^.]+\.[^.]+\Z/)
468
+ q.messages[:valid?] = "Invalid username: %{value}, must match %{valid}")
469
+ end
470
+ ```
471
+
472
+ ### 2.2. keypress
473
+
474
+ In order to ask question that awaits a single character answer use `keypress` prompt like so:
475
+
476
+ ```ruby
477
+ prompt.keypress("Press key ?")
478
+ # Press key?
479
+ # => a
480
+ ```
481
+
482
+ By default any key is accepted but you can limit keys by using `:keys` option. Any key event names such as `:space` or `:ctrl_k` are valid:
483
+
484
+ ```ruby
485
+ prompt.keypress("Press space or enter to continue", keys: [:space, :return])
486
+ ```
487
+
488
+ #### 2.2.1 timeout
489
+
490
+ Timeout can be set using `:timeout` option to expire prompt and allow the script to continue automatically:
491
+
492
+ ```ruby
493
+ prompt.keypress("Press any key to continue, resumes automatically in 3 seconds ...", timeout: 3)
494
+ ```
495
+
496
+ In addition the `keypress` recognises `:countdown` token when inserted inside the question. It will automatically countdown the time in seconds:
497
+
498
+ ```ruby
499
+ prompt.keypress("Press any key to continue, resumes automatically in :countdown ...", timeout: 3)
500
+ ```
501
+
502
+ ### 2.3 multiline
503
+
504
+ Asking for multiline input can be done with `multiline` method. The reading of input will terminate when `Ctrl+d` or `Ctrl+z` is pressed. Empty lines will not be included in the returned array.
505
+
506
+ ```ruby
507
+ prompt.multiline("Description?")
508
+ # Description? (Press CTRL-D or CTRL-Z to finish)
509
+ # I know not all that may be coming,
510
+ # but be it what it will,
511
+ # I'll go to it laughing.
512
+ # =>
513
+ # ["I know not all that may be coming,\n", "but be it what it will,\n", "I'll go to it laughing.\n"]
514
+ ```
515
+
516
+ The `multiline` uses similar options to those supported by `ask` prompt. For example, to provide default description:
517
+
518
+ ```ruby
519
+ prompt.multiline("Description?", default: "A super sweet prompt.")
520
+ ```
521
+
522
+ Or using DSL:
523
+
524
+ ```ruby
525
+ prompt.multiline("Description?") do |q|
526
+ q.default "A super sweet prompt."
527
+ q.help "Press thy ctrl+d to end"
528
+ end
529
+ ```
530
+
531
+ ### 2.4 mask
532
+
533
+ If you require input of confidential information use `mask` method. By default each character that is printed is replaced by `•` symbol. All configuration options applicable to `ask` method can be used with `mask` as well.
534
+
535
+ ```ruby
536
+ prompt.mask("What is your secret?")
537
+ # => What is your secret? ••••
538
+ ```
539
+
540
+ The masking character can be changed by passing the `:mask` key:
541
+
542
+ ```ruby
543
+ heart = prompt.decorate(prompt.symbols[:heart] + " ", :magenta)
544
+ prompt.mask("What is your secret?", mask: heart)
545
+ # => What is your secret? ❤ ❤ ❤ ❤ ❤
546
+ ```
547
+
548
+ If you don't wish to show any output use `:echo` option like so:
549
+
550
+ ```ruby
551
+ prompt.mask("What is your secret?", echo: false)
552
+ ```
553
+
554
+ You can also provide validation for your mask to enforce for instance strong passwords:
555
+
556
+ ```ruby
557
+ prompt.mask("What is your s) do |q|
558
+ q.validate(/[a-z\ ]{5,15}/)
559
+ end
560
+ ```
561
+
562
+ ### 2.5 yes?/no?
563
+
564
+ In order to display a query asking for boolean input from user use `yes?` like so:
565
+
566
+ ```ruby
567
+ prompt.yes?("Do you like Ruby?")
568
+ # =>
569
+ # Do you like Ruby? (Y/n)
570
+ ```
571
+
572
+ You can further customize question by passing `suffix`, `positive`, `negative` and `convert` options. The `suffix` changes text of available options, the `positive` specifies display string for successful answer and `negative` changes display string for negative answer. The final value is a boolean provided the `convert` option evaluates to boolean.
573
+
574
+ It's enough to provide the `suffix` option for the prompt to accept matching answers with correct labels:
575
+
576
+ ```ruby
577
+ prompt.yes?("Are you a human?") do |q|
578
+ q.suffix "Yup/nope"
579
+ end
580
+ # =>
581
+ # Are you a human? (Yup/nope)
582
+ ```
583
+
584
+ Alternatively, instead of `suffix` option provide the `positive` and `negative` labels:
585
+
586
+ ```ruby
587
+ prompt.yes?("Are you a human?") do |q|
588
+ q.default false
589
+ q.positive "Yup"
590
+ q.negative "Nope"
591
+ end
592
+ # =>
593
+ # Are you a human? (yup/Nope)
594
+ ```
595
+
596
+ Finally, providing all available options you can ask fully customized question:
597
+
598
+ ```ruby
599
+ prompt.yes?("Are you a human?") do |q|
600
+ q.suffix "Agree/Disagree"
601
+ q.positive "Agree"
602
+ q.negative "Disagree"
603
+ q.convert -> (input) { !input.match(/^agree$/i).nil? }
604
+ end
605
+ # =>
606
+ # Are you a human? (Agree/Disagree)
607
+ ```
608
+
609
+ There is also the opposite for asking confirmation of negative question:
610
+
611
+ ```ruby
612
+ prompt.no?("Do you hate Ruby?")
613
+ # =>
614
+ # Do you hate Ruby? (y/N)
615
+ ```
616
+
617
+ Similarly to `yes?` method, you can supply the same options to customize the question.
618
+
619
+ ### 2.6 menu
620
+
621
+ ### 2.6.1 choices
622
+
623
+ There are many ways in which you can add menu choices. The simplest way is to create an array of values:
624
+
625
+ ```ruby
626
+ choices = %w(small medium large)
627
+ ```
628
+
629
+ By default the choice name is also the value the prompt will return when selected. To provide custom values, you can provide a hash with keys as choice names and their respective values:
630
+
631
+ ```ruby
632
+ choices = {small: 1, medium: 2, large: 3}
633
+ prompt.select("What size?", choices)
634
+ # =>
635
+ # What size? (Press ↑/↓ arrow to move and Enter to select)
636
+ # ‣ small
637
+ # medium
638
+ # large
639
+ ```
640
+
641
+ Finally, you can define an array of choices where each choice is a hash value with `:name` & `:value` keys which can include other options for customising individual choices:
642
+
643
+ ```ruby
644
+ choices = [
645
+ {name: "small", value: 1},
646
+ {name: "medium", value: 2, disabled: "(out of stock)"},
647
+ {name: "large", value: 3}
648
+ ]
649
+ ```
650
+
651
+ You can specify `:key` as an additional option which will be used as short name for selecting the choice via keyboard key press.
652
+
653
+ Another way to create menu with choices is using the DSL and the `choice` method. For example, the previous array of choices with hash values can be translated as:
654
+
655
+ ```ruby
656
+ prompt.select("What size?") do |menu|
657
+ menu.choice name: "small", value: 1
658
+ menu.choice name: "medium", value: 2, disabled: "(out of stock)"
659
+ menu.choice name: "large", value: 3
660
+ end
661
+ # =>
662
+ # What size? (Press ↑/↓ arrow to move and Enter to select)
663
+ # ‣ small
664
+ # ✘ medium (out of stock)
665
+ #or in a more compact way:
666
+
667
+ ```ruby
668
+ prompt.select("What size?") do |menu|
669
+ menu.choice "small", 1
670
+ menu.choice "medium", 2, disabled: "(out of stock)"
671
+ menu.choice "large", 3
672
+ end
673
+ ```
674
+
675
+ #### 2.6.1.1 `:disabled`
676
+
677
+ The `:disabled` key indicates to display a choice as currently unavailable to select. Disabled choices are displayed with a cross `✘` character next to them. If the choice is disabled, it cannot be selected. The value for the `:disabled` is used next to the choice to provide reason for excluding it from the selection menu. For example:
678
+
679
+ ```ruby
680
+ choices = [
681
+ {name: "small", value: 1},
682
+ {name: "medium", value: 2, disabled: "(out of stock)"},
683
+ {name: "large", value: 3}
684
+ ]
685
+ ```
686
+
687
+ ### 2.6.2 select
688
+
689
+ For asking questions involving list of options use `select` method by passing the question and possible choices:
690
+
691
+ ```ruby
692
+ prompt.select("Choose your destiny?", %w(Scorpion Kano Jax))
693
+ # =>
694
+ # Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select)
695
+ # ‣ Scorpion
696
+ # Kano
697
+ # Jax
698
+ ```
699
+
700
+ You can also options through DSL using the `choice` method for single entry and/or `choices` for more than one choice:
701
+
702
+ ```ruby
703
+ prompt.select("Choose your destiny?") do |menu|
704
+ menu.choice "Scorpion"
705
+ menu.choice "Kano"
706
+ menu.choice "Jax"
707
+ end
708
+ # =>
709
+ # Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select)
710
+ # ‣ Scorpion
711
+ # Kano
712
+ # Jax
713
+ ```
714
+
715
+ By default the choice name is used as return value, but you can provide your custom values including a `Proc` object:
716
+
717
+ ```ruby
718
+ prompt.select("Choose your destiny?") do |menu|
719
+ menu.choice "Scorpion", 1
720
+ menu.choice "Kano", 2
721
+ menu.choice "Jax", -> { "Nice choice captain!" }
722
+ end
723
+ # =>
724
+ # Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select)
725
+ # ‣ Scorpion
726
+ # Kano
727
+ # Jax
728
+ ```
729
+
730
+ If you wish you can also provide a simple hash to denote choice name and its value like so:
731
+
732
+ ```ruby
733
+ choices = {"Scorpion" => 1, "Kano" => 2, "Jax" => 3}
734
+ prompt.select("Choose your destiny?", choices)
735
+ ```
736
+
737
+ To mark particular answer as selected use `default` with either an index ofstarting from `1` or a choice's name:
738
+
739
+ ```ruby
740
+ prompt.select("Choose your destiny?") do |menu|
741
+ menu.default 3
742
+ # or menu.default "Jax"
743
+
744
+ menu.choice "Scorpion", 1
745
+ menu.choice "Kano", 2
746
+ menu.choice "Jax", 3
747
+ end
748
+ # =>
749
+ # Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select)
750
+ # Scorpion
751
+ # Kano
752
+ # ‣ Jax
753
+ ```
754
+
755
+ #### 2.6.2.1 `:cycle`
756
+
757
+ You can navigate the choices using the arrow keys or define your own key mappings (see [keyboard events](#212-keyboard-events). When reaching the top/bottom of the list, the selection does not cycle around by default. If you wish to enable cycling, you can pass `cycle: true` to `select` and `multi_select`:
758
+
759
+ ```ruby
760
+ prompt.select("Choose your destiny?", %w(Scorpion Kano Jax), cycle: true)
761
+ # =>
762
+ # Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select)
763
+ # ‣ Scorpion
764
+ # Kano
765
+ # Jax
766
+ ```
767
+
768
+ #### 2.6.2.2 `:enum`
769
+
770
+ For ordered choices set `enum` to any delimiter String. In that way, you can use arrows keys and numbers (0-9) to select the item.
771
+
772
+ ```ruby
773
+ pr"Choose your destiny?") do |menu|
774
+ menu.enum "."
775
+
776
+ menu.choice "Scorpion", 1
777
+ menu.choice "Kano", 2
778
+ menu.choice "Jax", 3
779
+ end
780
+ # =>
781
+ # Choose your destiny? (Use ↑/↓ arrow or number (0-9) keys, press Enter to select)
782
+ # 1. Scorpion
783
+ # 2. Kano
784
+ # ‣ 3. Jax
785
+ ```
786
+
787
+ #### 2.6.2.3 `:help`
788
+
789
+ You can configure help message with `:help` and when to display it with `:show_help` options. The help can be displayed on `start`, `never` or `always`:
790
+
791
+ ```ruby
792
+ choices = %w(Scorpion Kano Jax)
793
+ prompt.select("Choose your destiny?", choices, help: "(Bash keyboard keys)", show_help: :always)
794
+ # =>
795
+ # Choose your destiny? (Bash keyboard keys)
796
+ # > Scorpion
797
+ # Kano
798
+ # Jax
799
+ ```
800
+
801
+ #### 2.6.2.4 `:marker`
802
+
803
+ You can configure active marker like so:
804
+
805
+ ```ruby
806
+ choices = %w(Scorpion Kano Jax)
807
+ prompt.select("Choose your destiny?", choices, symbols: { marker: ">" })
808
+ # =>
809
+ # Choose your destiny? (Use ↑/↓ and ←/→ arrow keys, press Enter to select)
810
+ # > Scorpion
811
+ # Kano
812
+ # Jax
813
+ ```
814
+
815
+ #### 2.6.2.5 `:per_page`
816
+
817
+ By default the menu is paginaon grows beyond `6` items. To change this setting use `:per_page` configuration.
818
+
819
+ ```ruby
820
+ letters = ("A".."Z").to_a
821
+ prompt.select("Choose your letter?", letters, per_page: 4)
822
+ # =>
823
+ # Which letter? (Use ↑/↓ and ←/→ arrow keys, press Enter to select)
824
+ # ‣ A
825
+ # B
826
+ # C
827
+ # D
828
+ ```
829
+
830
+ You can also customise page navigation text using `:help` option:
831
+ ```ruby
832
+ letters = ("A".."Z").to_a
833
+ prompt.select("Choose your letter?") do |menu|
834
+ menu.per_page 4
835
+ menu.help "(Wiggle thy finger up/down and left/right to see more)"
836
+ menu.choices letters
837
+ end
838
+ # =>
839
+ # Which letter? (Wiggle thy finger up/down and left/right to see more)
840
+ # ‣ A
841
+ # B
842
+ # C
843
+ # D
844
+ ```
845
+
846
+ #### 2.6.2.6 `:disabled`
847
+
848
+ To disable menu choice, use the `:disabled` key with a value that explains the reason for the choice being unavailable. For example, out of all warriors, the Goro is currently injured:
849
+
850
+ ```ruby
851
+ warriors = [
852
+ "Scorpion",
853
+ "Kano",
854
+ { name: "Goro", disabled: "(injury)" },
855
+ "Jax",
856
+ "Kitana",
857
+ "Raiden"
858
+ ]
859
+ ```
860
+
861
+ The disabled choice will be dh a cross `✘` character next to it and followed by an explanation:
862
+
863
+ ```ruby
864
+ prompt.select("Choose your destiny?", warriors)
865
+ # =>
866
+ # Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select)
867
+ # ‣ Scorpion
868
+ # Kano
869
+ # ✘ Goro (injury)
870
+ # Jax
871
+ # Kitana
872
+ # Raiden
873
+ ```
874
+
875
+ #### 2.6.2.7 `:filter`
876
+
877
+ To activate dynamic list searching on letter/number key presses use `:filter` option:
878
+
879
+ ```ruby
880
+ warriors = %w(Scorpion Kano Jax Kitana Raiden)
881
+ prompt.select("Choose your destiny?", warriors, filter: true)
882
+ # =>
883
+ # Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select, and letter keys to filter)
884
+ # ‣ Scorpion
885
+ # Kano
886
+ # Jax
887
+ # Kitana
888
+ # Raiden
889
+ ```
890
+
891
+ After the user presses "k":
892
+
893
+ ```ruby
894
+ # =>
895
+ # Choose your destiny? (Filter: "k")
896
+ # ‣ Kano
897
+ # Kitana
898
+ ```
899
+
900
+ After the user presses "ka":
901
+
902
+ ```ruby
903
+ # =>
904
+ # Choose your destiny? (Filter: "ka")
905
+ # ‣ Kano
906
+ ```
907
+
908
+ Filter characters can be deleted partially or entirely via, respectively, Backspace and Canc.
909
+
910
+ If the user changes or deletes a filter, the choices previously selected remain selected.
911
+
912
+ ### 2.6.3 multi_select
913
+
914
+ For asking questions involving multiple selection list use `multi_select` method by passing the question and possible choices:
915
+
916
+ ```ruby
917
+ choices = %w(vodka beer wine whisky bourbon)
918
+ prompt.multi_select("Select drinks?", choices)
919
+ # =>
920
+ #
921
+ # Select drinks? (Use ↑/↓ arrow keys, press Space to select and Enter to finish)"
922
+ # ‣ ⬡ vodka
923
+ # ⬡ beer
924
+ # ⬡ wine
925
+ # ⬡ whisky
926
+ # ⬡ bourbon
927
+ ```
928
+
929
+ As a return value, the `multi_select` will always return an array by default populated with the names of the choices. If you wish to return custom values for the available choices do:
930
+
931
+ ```ruby
932
+ choices = {vodka: 1, beer: 2, wine: 3, whisky: 4, bourbon: 5}
933
+ prompt.multi_select("Select drinks?", choices)
934
+
935
+ # Provided that vodka and beer have been selected, the function will return
936
+ # => [1, 2]
937
+ ```
938
+
939
+ Similar to `select` method, you can also provide options through DSL using the `choice` method for single entry and/or `choices` call for more than one choice:
940
+
941
+ ```rks?") do |menu|
942
+ menu.choice :vodka, {score: 1}
943
+ menu.choice :beer, 2
944
+ menu.choice :wine, 3
945
+ menu.choices whisky: 4, bourbon: 5
946
+ end
947
+ ```
948
+
949
+ To mark choice(s) as selected use the `default` option with either index(s) of the choice(s) starting from `1` or choice name(s):
950
+
951
+ ```ruby
952
+ prompt.multi_select("Select drinks?") do |menu|
953
+ menu.default 2, 5
954
+ # or menu.default :beer, :whisky
955
+
956
+ menu.choice :vodka, {score: 10}
957
+ menu.choice :beer, {score: 20}
958
+ menu.choice :wine, {score: 30}
959
+ menu.choice :whisky, {score: 40}
960
+ menu.choice :bourbon, {score: 50}
961
+ end
962
+ # =>
963
+ # Select drinks? beer, bourbon
964
+ # ⬡ vodka
965
+ # ⬢ beer
966
+ # ⬡ wine
967
+ # ⬡ whisky
968
+ # ‣ ⬢ bourbon
969
+ ```
970
+
971
+ #### 2.6.3.1 `:cycle`
972
+
973
+ Also like, `select`, the method takes an option `cycle` (which defaults to `false`), which lets you configure whether the selection should cycle around when reaching the top/bottom of the list when navigating:
974
+
975
+ ```ruby
976
+ prompt.multi_select("Select drinks?", %w(vodka beer wine), cycle: true)
977
+ ```
978
+
979
+ #### 2.6.3.2 `:enum`
980
+ t`, for ordered choices set `enum` to any delimiter String. In that way, you can use arrows keys and numbers (0-9) to select the item.
981
+
982
+ ```ruby
983
+ prompt.multi_select("Select drinks?") do |menu|
984
+ menu.enum ")"
985
+
986
+ menu.choice :vodka, {score: 10}
987
+ menu.choice :beer, {score: 20}
988
+ menu.choice :wine, {score: 30}
989
+ menu.choice :whisky, {score: 40}
990
+ menu.choice :bourbon, {score: 50}
991
+ end
992
+ # =>
993
+ # Select drinks? beer, bourbon
994
+ # ⬡ 1) vodka
995
+ # ⬢ 2) beer
996
+ # ⬡ 3) wine
997
+ # ⬡ 4) whisky
998
+ # ‣ ⬢ 5) bourbon
999
+ ```
1000
+
1001
+ And when you press enter you will see the following selected:
1002
+
1003
+ ```ruby
1004
+ # Select drinks? beer, bourbon
1005
+ # => [{score: 20}, {score: 50}]
1006
+ ```
1007
+
1008
+ #### 2.6.3.3 `:help`
1009
+
1010
+ You can configure help message with `:help` and when to display it with `:show_help` options. The help can be displayed on `start`, `never` or `always`:
1011
+
1012
+ ```ruby
1013
+ choices = {vodka: 1, beer: 2, wine: 3, whisky: 4, bourbon: 5}
1014
+ prompt.multi_select("Select drinks?", choices, help: "Press beer can against keyboard", show_help: :always)
1015
+ # =>
1016
+ # s? (Press beer can against keyboard)"
1017
+ # ‣ ⬡ vodka
1018
+ # ⬡ beer
1019
+ # ⬡ wine
1020
+ # ⬡ whisky
1021
+ # ⬡ bourbon
1022
+ ```
1023
+
1024
+ #### 2.6.3.4 `:per_page`
1025
+
1026
+ By default the menu is paginated if selection grows beyond `6` items. To change this setting use `:per_page` configuration.
1027
+
1028
+ ```ruby
1029
+ letters = ("A".."Z").to_a
1030
+ prompt.multi_select("Choose your letter?", letters, per_page: 4)
1031
+ # =>
1032
+ # Which letter? (Use ↑/↓ and ←/→ arrow keys, press Space to select and Enter to finish)
1033
+ # ‣ ⬡ A
1034
+ # ⬡ B
1035
+ # ⬡ C
1036
+ # ⬡ D
1037
+ ```
1038
+
1039
+ #### 2.6.3.5 `:disabled`
1040
+
1041
+ To disable menu choice, use the `:disabled` key with a value that explains the reason for the choice being unavailable. For example, out of all drinks, the sake and beer are currently out of stock:
1042
+
1043
+ ```ruby
1044
+ drinks = [
1045
+ "bourbon",
1046
+ {name: "sake", disabled: "(out of stock)"},
1047
+ "vodka",
1048
+ {name: "beer", disabled: "(out of stock)"},
1049
+ "wine",
1050
+ "whisky"
1051
+ ]
1052
+ ```
1053
+
1054
+ The disabled choice will be displayed with a cross `✘` character next to it and followed by an explanation:
1055
+
1056
+ ```ruby
1057
+ pfavourite drink?", drinks)
1058
+ # =>
1059
+ # Choose your favourite drink? (Use ↑/↓ arrow keys, press Space to select and Enter to finish)
1060
+ # ‣ ⬡ bourbon
1061
+ # ✘ sake (out of stock)
1062
+ # ⬡ vodka
1063
+ # ✘ beer (out of stock)
1064
+ # ⬡ wine
1065
+ # ⬡ whisky
1066
+ ```
1067
+
1068
+ #### 2.6.3.6 `:echo`
1069
+
1070
+ To control whether the selected items are shown on the question
1071
+ header use the :echo option:
1072
+
1073
+ ```ruby
1074
+ choices = %w(vodka beer wine whisky bourbon)
1075
+ prompt.multi_select("Select drinks?", choices, echo: false)
1076
+ # =>
1077
+ # Select drinks?
1078
+ # ⬡ vodka
1079
+ # ⬢ 2) beer
1080
+ # ⬡ 3) wine
1081
+ # ⬡ 4) whisky
1082
+ # ‣ ⬢ 5) bourbon
1083
+ ```
1084
+
1085
+ #### 2.6.3.7 `:filter`
1086
+
1087
+ To activate dynamic list filtering on letter/number typing, use the :filter option:
1088
+
1089
+ ```ruby
1090
+ choices = %w(vodka beer wine whisky bourbon)
1091
+ prompt.multi_select("Select drinks?", choices, filter: true)
1092
+ # =>
1093
+ # Select drinks? (Use ↑/↓ arrow keys, press Space to select and Enter to finish, and letter keys to filter)
1094
+ # ‣ ⬡ vodka
1095
+ # ⬡ beer
1096
+ # ⬡ wine
1097
+ # ⬡ whisky
1098
+ # ⬡ bourbon
1099
+ ```
1100
+
1101
+ After the ulter: "w")
1102
+ # ‣ ⬡ wine
1103
+ # ⬡ whisky
1104
+ ```
1105
+
1106
+ Filter characters can be deleted partially or entirely via, respectively, Backspace and Canc.
1107
+
1108
+ If the user changes or deletes a filter, the choices previously selected remain selected.
1109
+
1110
+ The `filter` option is not compatible with `enum`.
1111
+
1112
+ #### 2.6.3.8 `:min`
1113
+
1114
+ To force the minimum number of choices an user must select, use the `:min` option:
1115
+
1116
+ ```ruby
1117
+ choices = %w(vodka beer wine whisky bourbon)
1118
+ prompt.multi_select("Select drinks?", choices, min: 3)
1119
+ # =>
1120
+ # Select drinks? (min. 3) vodka, beer
1121
+ # ⬢ vodka
1122
+ # ⬢ beer
1123
+ # ⬡ wine
1124
+ # ⬡ wiskey
1125
+ # ‣ ⬡ bourbon
1126
+ ```
1127
+
1128
+ #### 2.6.3.9 `:max`
1129
+
1130
+ To limit the number of choices an user can select, use the `:max` option:
1131
+
1132
+ ```ruby
1133
+ choices = %w(vodka beer wine whisky bourbon)
1134
+ prompt.multi_select("Select drinks?", choices, max: 3)
1135
+ # =>
1136
+ # Select drinks? (max. 3) vodka, beer, whisky
1137
+ # ⬢ vodka
1138
+ # ⬢ beer
1139
+ # ⬡ wine
1140
+ # ⬢ whisky
1141
+ # ‣ ⬡ bourbon
1142
+ ```
1143
+
1144
+ ### 2.6.4 enum_select
1145
+
1146
+ In order to ask for standard selection from indeect` and pass question together with possible choices:
1147
+
1148
+ ```ruby
1149
+ choices = %w(emacs nano vim)
1150
+ prompt.enum_select("Select an editor?", choices)
1151
+ # =>
1152
+ #
1153
+ # Select an editor?
1154
+ # 1) nano
1155
+ # 2) vim
1156
+ # 3) emacs
1157
+ # Choose 1-3 [1]:
1158
+ ```
1159
+
1160
+ Similar to `select` and `multi_select`, you can provide question options through DSL using `choice` method and/or `choices` like so:
1161
+
1162
+ ```ruby
1163
+ choices = %w(nano vim emacs)
1164
+ prompt.enum_select("Select an editor?") do |menu|
1165
+ menu.choice :nano, "/bin/nano"
1166
+ menu.choice :vim, "/usr/bin/vim"
1167
+ menu.choice :emacs, "/usr/bin/emacs"
1168
+ end
1169
+ # =>
1170
+ #
1171
+ # Select an editor?
1172
+ # 1) nano
1173
+ # 2) vim
1174
+ # 3) emacs
1175
+ # Choose 1-3 [1]:
1176
+ #
1177
+ # Select an editor? /bin/nano
1178
+ ```
1179
+
1180
+ You can change the indexed numbers formatting by passing `enum` option. The `default` option lets you specify which choice to mark as selected by default. It accepts an index of the choice starting from `1` or a choice name:
1181
+
1182
+ ```ruby
1183
+ choices = %w(nano vim emacs)
1184
+ prompt.enum_select("Select an editor?") do |menu|
1185
+ menu.default 2
1186
+ # or menu.defualt "/usr/bin/vim"
1187
+ menu.enum "."
1188
+
1189
+ menu.choice :nano, "/bin/nano"
1190
+ menu.choice :vim, "/usr/bin/vim"
1191
+ menu.choice :emacs, "/usr/bin/emacs"
1192
+ end
1193
+ # =>
1194
+ #
1195
+ # Select an editor?
1196
+ # 1. nano
1197
+ # 2. vim
1198
+ # 3. emacs
1199
+ # Choose 1-3 [2]:
1200
+ #
1201
+ # Select an editor? /usr/bin/vim
1202
+ ```
1203
+
1204
+ #### 2.6.4.1 `:per_page`
1205
+
1206
+ By default the menu is paginated if selection grows beyond `6` items. To change this setting use `:per_page` configuration.
1207
+
1208
+ ```ruby
1209
+ letters = ("A".."Z").to_a
1210
+ prompt.enum_select("Choose your letter?", letters, per_page: 4)
1211
+ # =>
1212
+ # Which letter?
1213
+ # 1) A
1214
+ # 2) B
1215
+ # 3) C
1216
+ # 4) D
1217
+ # Choose 1-26 [1]:
1218
+ # (Press tab/right or left to reveal more choices)
1219
+ ```
1220
+
1221
+ #### 2.6.4.2 `:disabled`
1222
+
1223
+ To make a choice unavailable use the `:disabled` option and, if you wish, as value provide a reason:
1224
+
1225
+ ```ruby
1226
+ choices = [
1227
+ {name: "Emacs", disabled: "(not installed)"},
1228
+ "Atom",
1229
+ "GNU nano",
1230
+ {name: "Notepad++", disabled: "(not installed)"},
1231
+ "Sublime",
1232
+ "Vim"
1233
+ ]
1234
+ ```
1235
+
1236
+ The disabled choice will be displayed with a cross ✘ chacter next to it and followed by an explanation:
1237
+
1238
+
1239
+ ```ruby
1240
+ prompt.enum_select("Select an editor", choices)
1241
+ # =>
1242
+ # Select an editor
1243
+ # ✘ 1) Emacs (not installed)
1244
+ # 2) Atom
1245
+ # 3) GNU nano
1246
+ # ✘ 4) Notepad++ (not installed)
1247
+ # 5) Sublime
1248
+ # 6) Vim
1249
+ # Choose 1-6 [2]:
1250
+ ```
1251
+
1252
+ ### 2.7 expand
1253
+
1254
+ The `expand` provides a compact way to ask a question with many options.
1255
+
1256
+ As first argument `expand` takes the message to display and as a second an array of choices. Compared to the `select`, `multi_select` and `enum_select`, the choices need to be objects that include `:key`, `:name` and `:value` keys. The `:key` must be a single character. The help choice is added automatically as the last option under the key `h`.
1257
+
1258
+ ```ruby
1259
+ choices = [
1260
+ {
1261
+ key: "y",
1262
+ name: "overwrite this file",
1263
+ value: :yes
1264
+ }, {
1265
+ key: "n",
1266
+ name: "do not overwrite this file",
1267
+ value: :no
1268
+ }, {
1269
+ key: "q",
1270
+ name: "quit; do not overwrite this file ",
1271
+ value: :quit
1272
+ }
1273
+ ]
1274
+ ```
1275
+
1276
+ The choices can also be provided through DSL usihe `choice` method. The `:value` can be a primitive value or `Proc` instance that gets executed and whose value is used as returned type. For example:
1277
+
1278
+ ```ruby
1279
+ prompt.expand("Overwrite Gemfile?") do |q|
1280
+ q.choice key: "y", name: "Overwrite" do :ok end
1281
+ q.choice key: "n", name: "Skip", value: :no
1282
+ q.choice key: "a", name: "Overwrite all", value: :all
1283
+ q.choice key: "d", name: "Show diff", value: :diff
1284
+ q.choice key: "q", name: "Quit", value: :quit
1285
+ end
1286
+ ```
1287
+
1288
+ The first element in the array of choices or provided via `choice` DSL will be the default choice, you can change that by passing `default` option.
1289
+
1290
+ ```ruby
1291
+ prompt.expand("Overwrite Gemfile?", choices)
1292
+ # =>
1293
+ # Overwrite Gemfile? (enter "h" for help) [y,n,q,h]
1294
+ ```
1295
+
1296
+ Each time user types an option a hint will be displayed:
1297
+
1298
+ ```ruby
1299
+ # Overwrite Gemfile? (enter "h" for help) [y,n,a,d,q,h] y
1300
+ # >> overwrite this file
1301
+ ```
1302
+
1303
+ If user types `h` and presses enter, an expanded view will be shown which further allows to refine the choice:
1304
+
1305
+ ```ruby
1306
+ # Overwrite Gemfile?
1307
+ # y - overwrite this file
1308
+ # n - do not overwrite this file
1309
+ # q - quit; do not overwrite this file
1310
+ # h - print help
1311
+ # Choice [y]:
1312
+ ```
1313
+
1314
+ Run `examples/expand.rb` to see the prompt in action.
1315
+
1316
+ #### 2.7.1 `:auto_hint`
1317
+
1318
+ To show hint by default use `:auto_hint` option:
1319
+
1320
+ ```ruby
1321
+ prompt.expand("Overwrite Gemfile?", choices, auto_hint: true)
1322
+ # =>
1323
+ # Overwrite Gemfile? (enter "h" for help) [y,n,q,h]
1324
+ # >> overwrite this file
1325
+ ```
1326
+
1327
+ ### 2.8 collect
1328
+
1329
+ In order to collect more than one answer use `collect` method. Using the `key` you can describe the answers key name. All the methods for asking user input such as `ask`, `mask`, `select` can be directly invoked on the key. The key composition is very flexible by allowing nested keys. If you want the value to be automatically converted to required type use [convert](#221-convert).
1330
+
1331
+ For example to gather some contact information do:
1332
+
1333
+ ```ruby
1334
+ prompt.collect do
1335
+ key(:name).ask("Name?")
1336
+
1337
+ key(:age).ask("Age?", convert: :int)
1338
+
1339
+ key(:address) do
1340
+ key(:street).ask("Street?", required: true)
1341
+ key(:city).ask("City?")
1342
+ key(:zip).ask("Zip?", validate: /\A\d{3}\Z/)
1343
+ end
1344
+ end
1345
+ # =>
1346
+ # {:name => "Piotr", :age => 30, :address => {:street => "Street", :city => "City", :zip => "123"}}
1347
+ ```
1348
+
1349
+ In order to collect _mutliple values_ for a given key in a loop, chain `values` onto the `key` desired:
1350
+
1351
+ ```ruby
1352
+ result = prompt.collect do
1353
+ key(:name).ask("Name?")
1354
+
1355
+ key(:age).ask("Age?", convert: :int)
1356
+
1357
+ while prompt.yes?("continue?")
1358
+ key(:addresses).values do
1359
+ key(:street).ask("Street?", required: true)
1360
+ key(:city).ask("City?")
1361
+ key(:zip).ask("Zip?", validate: /\A\d{3}\Z/)
1362
+ end
1363
+ end
1364
+ end
1365
+ # =>
1366
+ # {
1367
+ # :name => "Piotr",
1368
+ # :age => 30,
1369
+ # :addresses => [
1370
+ # {:street => "Street", :city => "City", :zip => "123"},
1371
+ # {:street => "Street", :city => "City", :zip => "234"}
1372
+ # ]
1373
+ # }
1374
+ ```
1375
+
1376
+ ### 2.9 suggest
1377
+
1378
+ To suggest possible matches for the user input use `suggest` method like so:
1379
+
1380
+ ```ruby
1381
+ prompt.suggest("sta", ["stage", "stash", "commit", "branch"])
1382
+ # =>
1383
+ # Did you mean one of these?
1384
+ # stage
1385
+ # stash
1386
+ ```
1387
+
1388
+ To customize query text presented pass `:single_text` and `:plural_text` options to respectively change the message when one match is found or many.
1389
+
1390
+ ```ruby
1391
+ possible = %w(status stage stash commit branch blame)
1392
+ prompt.suggest("b", possible, indent: 4, single_text: "Perhaps you meant?")
1393
+ # =>
1394
+ # Perhaps you meant?
1395
+ # blame
1396
+ ```
1397
+
1398
+ ### 2.10 slider
1399
+
1400
+ If you'd rather not display all possible values in a vertical list, you may consider using `slider`. The slider provides easy visual way of picking a value marked by `●` symbol.
1401
+
1402
+ For integers, you can set `:min`(defaults to 0), `:max` and `:step`(defaults to 1) options to configure slider range:
1403
+
1404
+ ```ruby
1405
+ prompt.slider("Volume", min: 0, max: 100, step: 5)
1406
+ # =>
1407
+ # Volume ──────────●────────── 50
1408
+ # (Use ←/→ arrow keys, press Enter to select)
1409
+ ```
1410
+
1411
+ For everything else, you can provide an array of your desired choices:
1412
+
1413
+ ```ruby
1414
+ prompt.slider(─────●───────────── m
1415
+ # (Use ←/→ arrow keys, press Enter to select)
1416
+ ```
1417
+
1418
+ By default the slider is configured to pick middle of the range as a start value, you can change this by using the `:default` option:
1419
+
1420
+ ```ruby
1421
+ prompt.slider("Volume", max: 100, step: 5, default: 75)
1422
+ # =>
1423
+ # Volume ───────────────●────── 75
1424
+ # (Use ←/→ arrow keys, press Enter to select)
1425
+ ```
1426
+
1427
+ You can also select the default value by name:
1428
+
1429
+ ```ruby
1430
+ prompt.slider("Letter", ('a'..'z').to_a, default: 'q')
1431
+ # =>
1432
+ # Letter ──────────────────●─────── q
1433
+ # (Use ←/→ arrow keys, press Enter to select)
1434
+ ```
1435
+
1436
+ You can also change the default slider formatting using the `:format`. The value must contain the `:slider` token to show current value and any `sprintf` compatible flag for number display, in our case `%d`:
1437
+
1438
+ ```ruby
1439
+ prompt.slider("Volume", max: 100, step: 5, default: 75, format: "|:slider| %d%%")
1440
+ # =>
1441
+ # Voxample, to have a step of `0.5` and display each value with a single decimal place use `%f` as format:
1442
+
1443
+ ```ruby
1444
+ prompt.slider("Volume", max: 10, step: 0.5, default: 5, format: "|:slider| %.1f")
1445
+ # =>
1446
+ # Volume |───────────────●──────| 7.5
1447
+ # (Use ←/→ arrow keys, press Enter to select)
1448
+ ```
1449
+
1450
+ You can alternatively provide a proc/lambda to customize your formatting even further:
1451
+
1452
+ ```ruby
1453
+ slider_format = -> (slider, value) { "|#{slider}| #{value.zero? ? "muted" : "%.1f"}" % value }
1454
+ prompt.slider("Volume", max: 10, step: 0.5, default: 0, format: slider_format)
1455
+ # =>
1456
+ # Volume |●─────────────────────| muted
1457
+ # (Use ←/→ arrow keys, press Enter to select)
1458
+ ```
1459
+
1460
+ If you wish to change the slider handle and the slider range display use `:symbols` option:
1461
+
1462
+ ```ruby
1463
+ prompt.slider("Volume", max: 100, step: 5, default: 75, symbols: {bullet: "x", line: "_"})
1464
+ # =>
1465
+ # Volume _______________x______ 75%
1466
+ # (Use ←/→ arrow keys, press Enter tions. The help can be displayed on `start`, `never` or `always`:
1467
+
1468
+ ```ruby
1469
+ prompt.slider("Volume", max: 10, default: 7, help: "(Move arrows left and right to set value)", show_help: :always)
1470
+ # =>
1471
+ # Volume ───────────────●────── 7
1472
+ # (Move arrows left and right to set value)
1473
+ ```
1474
+
1475
+ Slider can be configured through DSL as well:
1476
+
1477
+ ```ruby
1478
+ prompt.slider("What size?") do |range|
1479
+ range.max 100
1480
+ range.step 5
1481
+ range.default 75
1482
+ range.format "|:slider| %d%%"
1483
+ end
1484
+ # =>
1485
+ # Volume |───────────────●──────| 75%
1486
+ # (Use ←/→ arrow keys, press Enter to select)
1487
+ ```
1488
+
1489
+ ```ruby
1490
+ prompt.slider("What letter?") do |range|
1491
+ range.choices ('a'..'z').to_a
1492
+ range.format "|:slider| %s"
1493
+ range.default 'q'
1494
+ end
1495
+ # =>
1496
+ # What letter? |──────────────────●───────| q
1497
+ # (Use ←/→ arrow keys, press Enter to select)
1498
+ ```
1499
+
1500
+
1501
+ ### 2.11 say
1502
+
1503
+ To simply print message out to standard output use `say` like so:
1504
+
1505
+ ``murach/pastel#3-supported-colors)
1506
+
1507
+ **TTY::Prompt** provides more specific versions of `say` method to better express intention behind the message such as `ok`, `warn` and `error`.
1508
+
1509
+ #### 2.11.1 ok
1510
+
1511
+ Print message(s) in green do:
1512
+
1513
+ ```ruby
1514
+ prompt.ok(...)
1515
+ ```
1516
+
1517
+ #### 2.12.2 warn
1518
+
1519
+ Print message(s) in yellow do:
1520
+
1521
+ ```ruby
1522
+ prompt.warn(...)
1523
+ ```
1524
+
1525
+ #### 2.11.3 error
1526
+
1527
+ Print message(s) in red do:
1528
+
1529
+ ```ruby
1530
+ prompt.error(...)
1531
+ ```
1532
+
1533
+ #### 2.12 keyboard events
1534
+
1535
+ All the prompt types, when a key is pressed, fire key press events. You can subscribe to listen to this events by calling `on` with type of event name.
1536
+
1537
+ ```ruby
1538
+ prompt.on(:keypress) { |event| ... }
1539
+ ```
1540
+
1541
+ The event object is yielded to a block whenever particular event fires. The event has `key` and `value` methods. Further, the `key` responds to following messages:
1542
+
1543
+ * `name` - the name of the event such as :up, :down, letter or digit
1544
+ * `meta` - true if event is non-standard key associated
1545
+ * `shift` - true if shift has been pressed with the key
1546
+ * `ctrl` - true if ctrl has been pressed with the key
1547
+
1548
+ For example, to add vim like key navigation to `select` prompt one would do the following:
1549
+
1550
+ ```ruby
1551
+ prompt.on(:keypress) do |event|
1552
+ if event.value == "j"
1553
+ prompt.trigger(:keydown)
1554
+ end
1555
+
1556
+ if event.value == "k"
1557
+ prompt.trigger(:keyup)
1558
+ end
1559
+ end
1560
+ ```
1561
+
1562
+ You can subscribe to more than one event:
1563
+
1564
+ ```ruby
1565
+ prompt.on(:keypress) { |key| ... }
1566
+ .on(:keydown) { |key| ... }
1567
+ ```
1568
+
1569
+ The available events are:
1570
+
1571
+ * `:keypress`
1572
+ * `:keydown`
1573
+ * `:keyup`
1574
+ * `:keyleft`
1575
+ * `:keyright`
1576
+ * `:keynum`
1577
+ * `:keytab`
1578
+ * `:keyenter`
1579
+ * `:keyreturn`
1580
+ * `:keyspace`
1581
+ * `:keyescape`
1582
+ * `:keydelete`
1583
+ * `:keybackspace`
1584
+
1585
+ ## 3 settings
1586
+
1587
+ ### 3.1. `:symbols`
1588
+
1589
+ Many prompts use symbols to display information. You can overwrite the default symbols for all the prompts using the `:symbols` key and hash of symbol names as value:
1590
+
1591
+ ```ruby
1592
+ prompt = TTY::Prompt.new(symbols: {marker: ">"})
1593
+ ```
1594
+
1595
+ The following symbols can be overwritten:
1596
+
1597
+ | Symbols | Unicode | ASCII |
1598
+ | ----------- |:-------:|:-----:|
1599
+ | tick | `✓` `√` |
1600
+ | cross | `✘` | `x` |
1601
+ | marker | `‣` | `>` |
1602
+ | dot | `•` | `.` |
1603
+ | bullet | `●` | `O` |
1604
+ | line | `─` | `-` |
1605
+ | radio_on | `⬢` | `(*)` |
1606
+ | radio_off | `⬡` | `( )` |
1607
+ | arrow_up | `↑` | `↑` |
1608
+ | arrow_down | `↓` | `↓` |
1609
+ | arrow_left | `←` | `←` |
1610
+ | arrow_right| `→` | `→` |
1611
+
1612
+ ### 3.2 `:active_color`
1613
+
1614
+ All prompt types support `:active_color` option. By default it's set to `:green` value.
1615
+
1616
+ The `select`, `multi_select`, `enum_select` and `expand` prompts use the active color to highlight the currently selected choice.
1617
+
1618
+ The answer provided by the user is also highlighted with the active color.
1619
+
1620
+ This `:active_color` as value accepts either a color symbol or callable object.
1621
+
1622
+ For example, to change all prompts active color to `:cyan` do:
1623
+
1624
+ ```ruby
1625
+ prompt = TTY::Prompt.new(active_color: :cyan)
1626
+ ```
1627
+
1628
+ You could also use `pastel`:
1629
+
1630
+ ```ruby
1631
+ notice = Pastel.new.cyan.on_blue.detach
1632
+ promr: notice)
1633
+ ````
1634
+
1635
+ Or use coloring of your own choice:
1636
+
1637
+ ```
1638
+ prompt = TTY::Prompt.new(active_color: ->(str) { my-color-gem(str) })
1639
+ ```
1640
+
1641
+ This option can be applied either globally for all prompts or individually:
1642
+
1643
+ ```ruby
1644
+ prompt.select("What size?", %w(Large Medium Small), active_color: :cyan)
1645
+ ```
1646
+
1647
+ Please [see pastel](https://github.com/piotrmurach/pastel#3-supported-colors) for all supported colors.
1648
+
1649
+ ### 3.3 `:enable_color`
1650
+
1651
+ If you wish to disable coloring for a prompt simply pass `:enable_color` option
1652
+
1653
+ ```ruby
1654
+ prompt = TTY::Prompt.new(enable_color: false)
1655
+ ```
1656
+
1657
+ ### 3.4 `:help_color`
1658
+
1659
+ The `:help_color` option is used to customize the display color for all the help text. By default it's set to `:bright_black` value.
1660
+
1661
+ Prompts such as `select`, `multi_select`, `expand` support `:help_color`. This option can be applied either globally for all prompts or individually.
1662
+
1663
+ The `:help_color` option as value accepts either a color symbol or callable object.
1664
+
1665
+ For example, to change all prompts help color to `:cyan` do:
1666
+
1667
+ ```ruby
1668
+ prompt = TTY::Prompt.new(help_color: :cyan)
1669
+ ```
1670
+
1671
+ You could also use `pastel`:
1672
+
1673
+ ```ruby
1674
+ notice = Pastel.new.cyan.on_blue.detach
1675
+ prompt = TTY::Prompt.new(help_color: notice)
1676
+ ````
1677
+
1678
+ Or use coloring of your own choice:
1679
+
1680
+ ```ruby
1681
+ prompt = TTY::Prompt.new(help_color: ->(str) { my-color-gem(str) })
1682
+ ```
1683
+
1684
+ Or configure `:help_color` for an individual prompt:
1685
+
1686
+ ```ruby
1687
+ prompt.select("What size?", %w(Large Medium Small), help_color: :cyan)
1688
+ ```
1689
+
1690
+ Please [see pastel](https://github.com/piotrmurach/pastel#3-supported-colors) for all supported colors.
1691
+
1692
+ ### 3.5 `:interrupt`
1693
+
1694
+ By default `InputInterrupt` error will be raised when the user hits the interrupt key(Control-C). However, you can customise this behaviour by passing the `:interrupt` option. The available options are:
1695
+
1696
+ * `:signal` - sends interrupt signal
1697
+ * `:exit` - exits with status code
1698
+ * `:noop` - skips handler
1699
+ * custom proc
1700
+
1701
+ For example, to send interrupt signal do:
1702
+
1703
+ ```ruby
1704
+ prompt = TTY::Prompt.new(interrupt: :signal)
1705
+ ```
1706
+
1707
+ ### 3.6 `:prefix`
1708
+
1709
+ You can prefix each question asked using the `:prefix` option. This option can be applied either globally for all prompts or individual for each one:
1710
+
1711
+ ```ruby
1712
+ prompt = TTY::Prompt.new(prefix: "[?] ")
1713
+ ```
1714
+
1715
+ ### 3.7 `:quiet`
1716
+
1717
+ Prompts such as `select`, `multi_select`, `expand`, `slider` support `:quiet` which is used to disable re-echoing of the question and answer after selection is done. This option can be applied either globally for all prompts or individually.
1718
+
1719
+ ```ruby
1720
+ # global
1721
+ prompt = TTY::Prompt.new(quiet: true)
1722
+ # single prompt
1723
+ prompt.select("What is your favorite color?", %w(blue yellow orange))
1724
+ ````
1725
+
1726
+ ### 3.8 `:track_history`
1727
+
1728
+ The prompts that accept line input such as `multiline` or `ask` provide history buffer that tracks all the lines entered during `TTY::Prompt.new` interactions. The history buffer provides previous or next lines when user presses up/down arrows respectively. However, if you wish to disable this behaviour use `:track_history` option like so:
1729
+
1730
+ ```ruby
1731
+ prompt = TTY::Prompt.new(track_history: false)
1732
+ ```
1733
+
1734
+ ## Contributing
1735
+
1736
+ 1. Fork it ( https://github.com/piotrmurach/tty-prompt/fork )
1737
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
1738
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
1739
+ 4. Push to the branch (`git push origin my-new-feature`)
1740
+ 5. Create a new Pull Request
1741
+
1742
+ This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
1743
+
1744
+ ## Copyright
1745
+
1746
+ Copyright (c) 2015 Piotr Murach. See LICENSE for further details.