cli-ui 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +13 -0
- data/lib/cli/ui/prompt.rb +78 -36
- data/lib/cli/ui/prompt/options_handler.rb +24 -0
- data/lib/cli/ui/version.rb +1 -1
- metadata +3 -3
- data/Gemfile.lock +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 367667f90cce34fe2ac10fffbd13a24a8b7a90e3
|
4
|
+
data.tar.gz: fdc151c1343bc037c8f03c3308ec3646795e9dbd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51e249672f6214028aebf3ab31b2d90cde511da996118b50607522318528c57ae0bfe40edc4b447593fb5faf2712b964fdfafdc26e1b092ce96e8ee92366e759
|
7
|
+
data.tar.gz: 58e27c5790e1bb0f35581953467a8c5a38d7ca647f3cca7042e42d347c9708dc44e9007cb326fa563b241daaaaa480662de7dfdaa2ad5a3084ecf68dc4158cf6
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -49,6 +49,19 @@ Prompt user with options and ask them to choose. Can answer using arrow keys, nu
|
|
49
49
|
CLI::UI.ask('What language/framework do you use?', options: %w(rails go ruby python))
|
50
50
|
```
|
51
51
|
|
52
|
+
Can also assign callbacks to each option
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
CLI::UI.ask('What language/framework do you use?') do |handler|
|
56
|
+
handler.option('rails') { |selection| selection }
|
57
|
+
handler.option('go') { |selection| selection }
|
58
|
+
handler.option('ruby') { |selection| selection }
|
59
|
+
handler.option('python') { |selection| selection }
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
* Note that the two examples provided above are identical in functionality
|
64
|
+
|
52
65
|
![Interactive Prompt](https://user-images.githubusercontent.com/3074765/33797984-0ebb5e64-dcdf-11e7-9e7e-7204f279cece.gif)
|
53
66
|
|
54
67
|
---
|
data/lib/cli/ui/prompt.rb
CHANGED
@@ -5,12 +5,13 @@ module CLI
|
|
5
5
|
module UI
|
6
6
|
module Prompt
|
7
7
|
autoload :InteractiveOptions, 'cli/ui/prompt/interactive_options'
|
8
|
-
|
8
|
+
autoload :OptionsHandler, 'cli/ui/prompt/options_handler'
|
9
|
+
private_constant :InteractiveOptions, :OptionsHandler
|
9
10
|
|
10
11
|
class << self
|
11
|
-
# Ask a user a question with either free form answer or a set of answers
|
12
|
+
# Ask a user a question with either free form answer or a set of answers (multiple choice)
|
13
|
+
# Can use arrows, y/n, numbers (1/2), and vim bindings to control multiple choice selection
|
12
14
|
# Do not use this method for yes/no questions. Use +confirm+
|
13
|
-
# Can use arrows, y/n, numbers, and vim bindings to control
|
14
15
|
#
|
15
16
|
# * Handles free form answers (options are nil)
|
16
17
|
# * Handles default answers for free form text
|
@@ -25,14 +26,25 @@ module CLI
|
|
25
26
|
#
|
26
27
|
# ==== Options
|
27
28
|
#
|
28
|
-
# * +:options+ - Options
|
29
|
+
# * +:options+ - Options that the user may select from. Will use +InteractiveOptions+ to do so.
|
29
30
|
# * +:default+ - The default answer to the question (e.g. they just press enter and don't input anything)
|
30
31
|
# * +:is_file+ - Tells the input to use file auto-completion (tab completion)
|
31
32
|
# * +:allow_empty+ - Allows the answer to be empty
|
32
33
|
#
|
33
34
|
# Note:
|
34
|
-
# * +:options+ conflicts with +:default+ and +:is_file+, you cannot set options with either of these keywords
|
35
|
+
# * +:options+ or providing a +Block+ conflicts with +:default+ and +:is_file+, you cannot set options with either of these keywords
|
35
36
|
# * +:default+ conflicts with +:allow_empty:, you cannot set these together
|
37
|
+
# * +:options+ conflicts with providing a +Block+ , you may only set one
|
38
|
+
#
|
39
|
+
# ==== Block (optional)
|
40
|
+
#
|
41
|
+
# * A Proc that provides a +OptionsHandler+ and uses the public +:option+ method to add options and their
|
42
|
+
# respective handlers
|
43
|
+
#
|
44
|
+
# ==== Return Value
|
45
|
+
#
|
46
|
+
# * If a +Block+ was not provided, the selected option or response to the free form question will be returned
|
47
|
+
# * If a +Block+ was provided, the evaluted value of the +Block+ will be returned
|
36
48
|
#
|
37
49
|
# ==== Example Usage:
|
38
50
|
#
|
@@ -48,35 +60,50 @@ module CLI
|
|
48
60
|
# Free form question when the answer can be empty
|
49
61
|
# CLI::UI::Prompt.ask('What is your opinion on this question?', allow_empty: true)
|
50
62
|
#
|
51
|
-
#
|
63
|
+
# Interactive (multiple choice) question
|
52
64
|
# CLI::UI::Prompt.ask('What kind of project is this?', options: %w(rails go ruby python))
|
53
65
|
#
|
54
|
-
#
|
55
|
-
|
56
|
-
|
57
|
-
|
66
|
+
# Interactive (multiple choice) question with defined handlers
|
67
|
+
# CLI::UI::Prompt.ask('What kind of project is this?') do |handler|
|
68
|
+
# handler.option('rails') { |selection| selection }
|
69
|
+
# handler.option('go') { |selection| selection }
|
70
|
+
# handler.option('ruby') { |selection| selection }
|
71
|
+
# handler.option('python') { |selection| selection }
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
def ask(question, options: nil, default: nil, is_file: nil, allow_empty: true, &options_proc)
|
75
|
+
if ((options || block_given?) && (default || is_file))
|
76
|
+
raise(ArgumentError, 'conflicting arguments: options provided with default or is_file')
|
58
77
|
end
|
59
78
|
|
60
|
-
if
|
61
|
-
|
62
|
-
elsif options
|
63
|
-
puts_question("#{question} {{yellow:(choose with ↑ ↓ ⏎)}}")
|
79
|
+
if options || block_given?
|
80
|
+
ask_interactive(question, options, &options_proc)
|
64
81
|
else
|
65
|
-
|
82
|
+
ask_free_form(question, default, is_file, allow_empty)
|
66
83
|
end
|
84
|
+
end
|
67
85
|
|
68
|
-
|
69
|
-
|
70
|
-
|
86
|
+
# Asks the user a yes/no question.
|
87
|
+
# Can use arrows, y/n, numbers (1/2), and vim bindings to control
|
88
|
+
#
|
89
|
+
# ==== Example Usage:
|
90
|
+
#
|
91
|
+
# Confirmation question
|
92
|
+
# CLI::UI::Prompt.confirm('Is the sky blue?')
|
93
|
+
#
|
94
|
+
def confirm(question)
|
95
|
+
ask_interactive(question, %w(yes no)) == 'yes'
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
71
99
|
|
72
|
-
|
73
|
-
|
74
|
-
print(ANSI.cursor_save)
|
75
|
-
print(' ' * CLI::UI::Terminal.width)
|
76
|
-
print(ANSI.cursor_restore)
|
77
|
-
puts_question("#{question} (You chose: {{italic:#{resp}}})")
|
100
|
+
def ask_free_form(question, default, is_file, allow_empty)
|
101
|
+
raise(ArgumentError, 'conflicting arguments: default enabled but allow_empty is false') if (default && !allow_empty)
|
78
102
|
|
79
|
-
|
103
|
+
if default
|
104
|
+
puts_question("#{question} (empty = #{default})")
|
105
|
+
else
|
106
|
+
puts_question(question)
|
80
107
|
end
|
81
108
|
|
82
109
|
# Ask a free form question
|
@@ -94,19 +121,34 @@ module CLI
|
|
94
121
|
end
|
95
122
|
end
|
96
123
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
124
|
+
def ask_interactive(question, options = nil)
|
125
|
+
raise(ArgumentError, 'conflicting arguments: options and block given') if options && block_given?
|
126
|
+
|
127
|
+
options ||= if block_given?
|
128
|
+
handler = OptionsHandler.new
|
129
|
+
yield handler
|
130
|
+
handler.options
|
131
|
+
end
|
132
|
+
|
133
|
+
raise(ArgumentError, 'insufficient options') if options.nil? || options.size < 2
|
134
|
+
puts_question("#{question} {{yellow:(choose with ↑ ↓ ⏎)}}")
|
135
|
+
resp = interactive_prompt(options)
|
136
|
+
|
137
|
+
# Clear the line, and reset the question to include the answer
|
138
|
+
print(ANSI.previous_line + ANSI.end_of_line + ' ')
|
139
|
+
print(ANSI.cursor_save)
|
140
|
+
print(' ' * CLI::UI::Terminal.width)
|
141
|
+
print(ANSI.cursor_restore)
|
142
|
+
puts_question("#{question} (You chose: {{italic:#{resp}}})")
|
143
|
+
|
144
|
+
return handler.call(resp) if block_given?
|
145
|
+
resp
|
107
146
|
end
|
108
147
|
|
109
|
-
|
148
|
+
# Useful for stubbing in tests
|
149
|
+
def interactive_prompt(options)
|
150
|
+
InteractiveOptions.call(options)
|
151
|
+
end
|
110
152
|
|
111
153
|
def write_default_over_empty_input(default)
|
112
154
|
CLI::UI.raw do
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module CLI
|
2
|
+
module UI
|
3
|
+
module Prompt
|
4
|
+
# A class that handles the various options of an InteractivePrompt and their callbacks
|
5
|
+
class OptionsHandler
|
6
|
+
def initialize
|
7
|
+
@options = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def options
|
11
|
+
@options.keys
|
12
|
+
end
|
13
|
+
|
14
|
+
def option(option, &handler)
|
15
|
+
@options[option] = handler
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(option)
|
19
|
+
@options[option].call(option)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/cli/ui/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cli-ui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2018-02-23 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -67,7 +67,6 @@ files:
|
|
67
67
|
- ".rubocop.yml"
|
68
68
|
- ".travis.yml"
|
69
69
|
- Gemfile
|
70
|
-
- Gemfile.lock
|
71
70
|
- LICENSE.txt
|
72
71
|
- README.md
|
73
72
|
- Rakefile
|
@@ -84,6 +83,7 @@ files:
|
|
84
83
|
- lib/cli/ui/progress.rb
|
85
84
|
- lib/cli/ui/prompt.rb
|
86
85
|
- lib/cli/ui/prompt/interactive_options.rb
|
86
|
+
- lib/cli/ui/prompt/options_handler.rb
|
87
87
|
- lib/cli/ui/spinner.rb
|
88
88
|
- lib/cli/ui/spinner/async.rb
|
89
89
|
- lib/cli/ui/spinner/spin_group.rb
|
data/Gemfile.lock
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
cli-ui (0.1.2)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: https://rubygems.org/
|
8
|
-
specs:
|
9
|
-
ansi (1.5.0)
|
10
|
-
ast (2.3.0)
|
11
|
-
builder (3.2.3)
|
12
|
-
byebug (9.0.6)
|
13
|
-
metaclass (0.0.4)
|
14
|
-
method_source (0.8.2)
|
15
|
-
minitest (5.10.2)
|
16
|
-
minitest-reporters (1.1.14)
|
17
|
-
ansi
|
18
|
-
builder
|
19
|
-
minitest (>= 5.0)
|
20
|
-
ruby-progressbar
|
21
|
-
mocha (1.2.1)
|
22
|
-
metaclass (~> 0.0.1)
|
23
|
-
parallel (1.12.0)
|
24
|
-
parser (2.4.0.2)
|
25
|
-
ast (~> 2.3)
|
26
|
-
powerpack (0.1.1)
|
27
|
-
rainbow (3.0.0)
|
28
|
-
rake (10.5.0)
|
29
|
-
rubocop (0.52.0)
|
30
|
-
parallel (~> 1.10)
|
31
|
-
parser (>= 2.4.0.2, < 3.0)
|
32
|
-
powerpack (~> 0.1)
|
33
|
-
rainbow (>= 2.2.2, < 4.0)
|
34
|
-
ruby-progressbar (~> 1.7)
|
35
|
-
unicode-display_width (~> 1.0, >= 1.0.1)
|
36
|
-
ruby-progressbar (1.9.0)
|
37
|
-
unicode-display_width (1.3.0)
|
38
|
-
|
39
|
-
PLATFORMS
|
40
|
-
ruby
|
41
|
-
|
42
|
-
DEPENDENCIES
|
43
|
-
bundler (~> 1.15)
|
44
|
-
byebug
|
45
|
-
cli-ui!
|
46
|
-
method_source
|
47
|
-
minitest (>= 5.0.0)
|
48
|
-
minitest-reporters
|
49
|
-
mocha
|
50
|
-
rake (~> 10.0)
|
51
|
-
rubocop
|
52
|
-
|
53
|
-
BUNDLED WITH
|
54
|
-
1.16.0
|