inquirer.rb 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +197 -0
- data/Rakefile +2 -0
- data/examples/checkbox.rb +203 -0
- data/examples/confirm.rb +63 -0
- data/examples/input.rb +67 -0
- data/examples/list.rb +185 -0
- data/examples/password.rb +63 -0
- data/inquirer.gemspec +34 -0
- data/lib/inquirer.rb +86 -0
- data/lib/inquirer/prompts/checkbox.rb +165 -0
- data/lib/inquirer/prompts/confirm.rb +89 -0
- data/lib/inquirer/prompts/input.rb +138 -0
- data/lib/inquirer/prompts/list.rb +116 -0
- data/lib/inquirer/prompts/password.rb +15 -0
- data/lib/inquirer/style.rb +51 -0
- data/lib/inquirer/style/checkbox.rb +16 -0
- data/lib/inquirer/style/confirm.rb +15 -0
- data/lib/inquirer/style/input.rb +13 -0
- data/lib/inquirer/style/list.rb +16 -0
- data/lib/inquirer/style/password.rb +12 -0
- data/lib/inquirer/utils/iochar.rb +32 -0
- data/lib/inquirer/utils/iohelper.rb +123 -0
- data/lib/inquirer/utils/paginator.rb +37 -0
- data/lib/inquirer/version.rb +3 -0
- data/screenshots/checkbox.png +0 -0
- data/screenshots/confirm.png +0 -0
- data/screenshots/input.png +0 -0
- data/screenshots/list.png +0 -0
- data/screenshots/password.png +0 -0
- metadata +121 -0
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'inquirer/utils/paginator'
|
2
|
+
require 'inquirer/utils/iochar'
|
3
|
+
require 'inquirer/utils/iohelper'
|
4
|
+
require 'inquirer/style/checkbox'
|
5
|
+
|
6
|
+
module Checkbox
|
7
|
+
|
8
|
+
extend self
|
9
|
+
|
10
|
+
def prompt opts = {}
|
11
|
+
|
12
|
+
# finish if there's nothing to do
|
13
|
+
return opts[:default] if Array(opts[:choices]).empty?
|
14
|
+
|
15
|
+
@question = opts[:message]
|
16
|
+
@position = 0
|
17
|
+
@paginator = Paginator.new
|
18
|
+
@choices = []
|
19
|
+
opts[:choices].each { |choice|
|
20
|
+
|
21
|
+
choice[:value] ||= choice[:name]
|
22
|
+
|
23
|
+
if !choice[:checked] && opts[:default].is_a?(Array)
|
24
|
+
choice[:checked] = opts[:default].include?( choice[:value] )
|
25
|
+
end
|
26
|
+
|
27
|
+
@choices.push(choice)
|
28
|
+
}
|
29
|
+
|
30
|
+
IOHelper.without_cursor do
|
31
|
+
|
32
|
+
question_backup = @question
|
33
|
+
@question += ' '
|
34
|
+
@question += Inquirer::Style::Checkbox.selection_help
|
35
|
+
|
36
|
+
render_prompt
|
37
|
+
|
38
|
+
@question = question_backup
|
39
|
+
|
40
|
+
# loop through user input
|
41
|
+
IOHelper.read_char do |char|
|
42
|
+
|
43
|
+
key = IOChar.char_to_key(char)
|
44
|
+
|
45
|
+
if @error_message
|
46
|
+
@error_message = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
case key
|
50
|
+
when 'up'
|
51
|
+
@position = (@position - 1) % @choices.length
|
52
|
+
when 'down'
|
53
|
+
@position = (@position + 1) % @choices.length
|
54
|
+
when 'space'
|
55
|
+
@choices[@position][:checked] = !@choices[@position][:checked]
|
56
|
+
when 'return'
|
57
|
+
if opts[:validate] and opts[:validate].is_a?(Proc)
|
58
|
+
|
59
|
+
value = @choices.reject { |choice|
|
60
|
+
!choice[:checked]
|
61
|
+
}.collect { |choice|
|
62
|
+
choice[:value]
|
63
|
+
}
|
64
|
+
|
65
|
+
validation_result = opts[:validate].call( value )
|
66
|
+
|
67
|
+
if !validation_result
|
68
|
+
@error_message = Inquirer::Style::Checkbox.error_message_invalid_value
|
69
|
+
elsif validation_result && validation_result.is_a?(String)
|
70
|
+
@error_message = validation_result
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
IOHelper.clear
|
76
|
+
|
77
|
+
render_prompt
|
78
|
+
|
79
|
+
# we are done if the user hits return
|
80
|
+
if @error_message or key != 'return'
|
81
|
+
true
|
82
|
+
else
|
83
|
+
false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
render_result
|
89
|
+
|
90
|
+
@choices.reject { |choice|
|
91
|
+
!choice[:checked]
|
92
|
+
}.collect { |choice|
|
93
|
+
choice[:value]
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
def render_prompt
|
98
|
+
# start with the question prefix
|
99
|
+
prompt = Inquirer::Style.question_prefix
|
100
|
+
|
101
|
+
# render the question
|
102
|
+
prompt += Inquirer::Style::Checkbox.question % @question
|
103
|
+
|
104
|
+
prompt += IOChar.newline
|
105
|
+
|
106
|
+
# render the list
|
107
|
+
prompt += @choices.map.with_index(0) do |choice, position|
|
108
|
+
|
109
|
+
choice_prompt = ''
|
110
|
+
|
111
|
+
if position == @position
|
112
|
+
choice_prompt += Inquirer::Style::Checkbox.selector
|
113
|
+
else
|
114
|
+
choice_prompt += ' '
|
115
|
+
end
|
116
|
+
|
117
|
+
if choice[:checked]
|
118
|
+
choice_prompt += Inquirer::Style::Checkbox.checkbox_on
|
119
|
+
choice_prompt += ' '
|
120
|
+
choice_prompt += Inquirer::Style::Checkbox.checked_item % choice[:name]
|
121
|
+
else
|
122
|
+
choice_prompt += Inquirer::Style::Checkbox.checkbox_off
|
123
|
+
choice_prompt += ' '
|
124
|
+
choice_prompt += Inquirer::Style::Checkbox.item % choice[:name]
|
125
|
+
end
|
126
|
+
|
127
|
+
choice_prompt
|
128
|
+
end.join('')
|
129
|
+
|
130
|
+
paginated_prompt = @paginator.paginate(prompt, @position)
|
131
|
+
|
132
|
+
# render error message
|
133
|
+
if @error_message
|
134
|
+
paginated_prompt += Inquirer::Style::Checkbox.error_message % @error_message
|
135
|
+
end
|
136
|
+
|
137
|
+
paginated_prompt += IOChar.carriage_return
|
138
|
+
|
139
|
+
IOHelper.render( paginated_prompt )
|
140
|
+
end
|
141
|
+
|
142
|
+
def render_result
|
143
|
+
# start with the question prefix
|
144
|
+
result = Inquirer::Style.question_prefix
|
145
|
+
|
146
|
+
# render the question
|
147
|
+
result += Inquirer::Style::Checkbox.question % @question
|
148
|
+
|
149
|
+
selected_choices = @choices.reject { |choice|
|
150
|
+
!choice[:checked]
|
151
|
+
}.collect { |choice|
|
152
|
+
choice[:short] || choice[:name]
|
153
|
+
}.join(', ')
|
154
|
+
|
155
|
+
if !selected_choices.empty?
|
156
|
+
result += Inquirer::Style::Checkbox.response % selected_choices
|
157
|
+
end
|
158
|
+
|
159
|
+
result += IOChar.newline
|
160
|
+
|
161
|
+
IOHelper.clear
|
162
|
+
|
163
|
+
IOHelper.render( result )
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'inquirer/utils/iochar'
|
2
|
+
require 'inquirer/utils/iohelper'
|
3
|
+
require 'inquirer/style/confirm'
|
4
|
+
|
5
|
+
module Confirm
|
6
|
+
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def prompt opts = {}
|
10
|
+
@question = opts[:message]
|
11
|
+
@default = opts[:default]
|
12
|
+
@value = nil
|
13
|
+
|
14
|
+
render_prompt
|
15
|
+
|
16
|
+
IOHelper.read_char do |char|
|
17
|
+
|
18
|
+
key = IOChar.char_to_key(char)
|
19
|
+
|
20
|
+
if key.casecmp( Inquirer::Style::Confirm.option_true[0] ) == 0
|
21
|
+
@value = true
|
22
|
+
false
|
23
|
+
elsif key.casecmp( Inquirer::Style::Confirm.option_false[0] ) == 0
|
24
|
+
@value = false
|
25
|
+
false
|
26
|
+
elsif key == 'return' and !@default.nil?
|
27
|
+
@value = @default
|
28
|
+
false
|
29
|
+
else
|
30
|
+
true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
render_result
|
35
|
+
|
36
|
+
@value
|
37
|
+
end
|
38
|
+
|
39
|
+
def render_prompt
|
40
|
+
|
41
|
+
options = [
|
42
|
+
Inquirer::Style::Confirm.option_true[0].downcase,
|
43
|
+
Inquirer::Style::Confirm.option_false[0].downcase
|
44
|
+
]
|
45
|
+
if !@default.nil?
|
46
|
+
if @default
|
47
|
+
options[0].capitalize!
|
48
|
+
else
|
49
|
+
options[1].capitalize!
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# start with the question prefix
|
54
|
+
prompt = Inquirer::Style.question_prefix
|
55
|
+
|
56
|
+
prompt += Inquirer::Style::Confirm.question % @question
|
57
|
+
|
58
|
+
prompt += Inquirer::Style::Confirm.options % options
|
59
|
+
|
60
|
+
prompt += ' '
|
61
|
+
|
62
|
+
IOHelper.render( prompt )
|
63
|
+
end
|
64
|
+
|
65
|
+
def render_result
|
66
|
+
|
67
|
+
response = nil
|
68
|
+
if @value
|
69
|
+
response = Inquirer::Style::Confirm.option_true
|
70
|
+
else
|
71
|
+
response = Inquirer::Style::Confirm.option_false
|
72
|
+
end
|
73
|
+
|
74
|
+
# start with the question prefix
|
75
|
+
result = Inquirer::Style.question_prefix
|
76
|
+
|
77
|
+
result += Inquirer::Style::Confirm.question % @question
|
78
|
+
|
79
|
+
result += Inquirer::Style::Confirm.response % response
|
80
|
+
|
81
|
+
result += IOChar.newline
|
82
|
+
|
83
|
+
# flush previous data
|
84
|
+
IOHelper.clear
|
85
|
+
|
86
|
+
# rerender result
|
87
|
+
IOHelper.render( result )
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'inquirer/utils/iochar'
|
2
|
+
require 'inquirer/utils/iohelper'
|
3
|
+
require 'inquirer/style/input'
|
4
|
+
|
5
|
+
module Input
|
6
|
+
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def prompt opts = {}
|
10
|
+
@question = opts[:message]
|
11
|
+
@default = opts[:default]
|
12
|
+
@value = ''
|
13
|
+
|
14
|
+
render_prompt
|
15
|
+
|
16
|
+
cursor_position = 0
|
17
|
+
|
18
|
+
IOHelper.read_char do |char|
|
19
|
+
|
20
|
+
key = IOChar.char_to_key(char)
|
21
|
+
|
22
|
+
@error_message = nil
|
23
|
+
|
24
|
+
case key
|
25
|
+
when 'backspace'
|
26
|
+
|
27
|
+
index = @value.size - cursor_position - 1
|
28
|
+
|
29
|
+
if index >= 0
|
30
|
+
# remove char at current index
|
31
|
+
@value[index] = ''
|
32
|
+
end
|
33
|
+
when 'left'
|
34
|
+
if cursor_position < @value.length
|
35
|
+
cursor_position += 1
|
36
|
+
end
|
37
|
+
when 'right'
|
38
|
+
if cursor_position > 0
|
39
|
+
cursor_position -= 1
|
40
|
+
end
|
41
|
+
when 'return'
|
42
|
+
if not @default.nil? and @value == ''
|
43
|
+
@value = @default.dup
|
44
|
+
end
|
45
|
+
|
46
|
+
if opts[:validate] and opts[:validate].is_a?(Proc)
|
47
|
+
|
48
|
+
validation_result = opts[:validate].call( @value )
|
49
|
+
|
50
|
+
if !validation_result
|
51
|
+
@error_message = Inquirer::Style::Input.error_message_invalid_value
|
52
|
+
elsif validation_result && validation_result.is_a?(String)
|
53
|
+
@error_message = validation_result
|
54
|
+
end
|
55
|
+
end
|
56
|
+
else
|
57
|
+
if !['up', 'down'].include?(key)
|
58
|
+
|
59
|
+
@value = @value.insert(@value.length - cursor_position, char)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# flush previous data
|
64
|
+
IOHelper.clear
|
65
|
+
|
66
|
+
render_prompt
|
67
|
+
|
68
|
+
update_cursor_position(cursor_position)
|
69
|
+
|
70
|
+
if @error_message or key != 'return'
|
71
|
+
true
|
72
|
+
else
|
73
|
+
false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
render_result
|
78
|
+
|
79
|
+
# return the value
|
80
|
+
@value
|
81
|
+
end
|
82
|
+
|
83
|
+
def render_prompt
|
84
|
+
|
85
|
+
prompt = Inquirer::Style.question_prefix
|
86
|
+
|
87
|
+
prompt += Inquirer::Style::Input.question % @question
|
88
|
+
|
89
|
+
# render the default value
|
90
|
+
if @default
|
91
|
+
prompt += Inquirer::Style::Input.default % @default
|
92
|
+
end
|
93
|
+
|
94
|
+
# render the input
|
95
|
+
prompt += Inquirer::Style::Input.value % display_value
|
96
|
+
|
97
|
+
# render error message
|
98
|
+
bottom_line = nil
|
99
|
+
if @error_message
|
100
|
+
bottom_line = Inquirer::Style::Input.error_message % @error_message
|
101
|
+
end
|
102
|
+
|
103
|
+
# rerender new prompt
|
104
|
+
IOHelper.render( prompt, bottom_line )
|
105
|
+
end
|
106
|
+
|
107
|
+
def render_result
|
108
|
+
|
109
|
+
# start with the question prefix
|
110
|
+
result = Inquirer::Style.question_prefix
|
111
|
+
|
112
|
+
# render the question
|
113
|
+
result += Inquirer::Style::Input.question % @question
|
114
|
+
|
115
|
+
# render the input
|
116
|
+
result += Inquirer::Style::Input.response % display_value
|
117
|
+
|
118
|
+
# finish up
|
119
|
+
result += IOChar.clear_line + IOChar.newline
|
120
|
+
|
121
|
+
# flush previous data
|
122
|
+
IOHelper.clear
|
123
|
+
|
124
|
+
# rerender result
|
125
|
+
IOHelper.render( result )
|
126
|
+
end
|
127
|
+
|
128
|
+
def update_cursor_position(cursor_position)
|
129
|
+
print IOChar.cursor_left * cursor_position
|
130
|
+
end
|
131
|
+
|
132
|
+
# this function is necessary to reduce code
|
133
|
+
# duplication of the password promt engine
|
134
|
+
# to a minimum
|
135
|
+
def display_value
|
136
|
+
@value
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'inquirer/utils/iochar'
|
2
|
+
require 'inquirer/utils/iohelper'
|
3
|
+
require 'inquirer/utils/paginator'
|
4
|
+
require 'inquirer/style/list'
|
5
|
+
|
6
|
+
module List
|
7
|
+
|
8
|
+
extend self
|
9
|
+
|
10
|
+
def prompt opts = {}
|
11
|
+
@question = opts[:message]
|
12
|
+
default = opts[:default] || 0
|
13
|
+
|
14
|
+
@position = 0
|
15
|
+
@paginator = Paginator.new
|
16
|
+
|
17
|
+
@choices = []
|
18
|
+
opts[:choices].each { |choice|
|
19
|
+
|
20
|
+
choice[:value] ||= choice[:name]
|
21
|
+
|
22
|
+
@choices.push(choice)
|
23
|
+
}
|
24
|
+
|
25
|
+
return nil if Array(@choices).empty?
|
26
|
+
|
27
|
+
if default.is_a?(String) || default.is_a?(Symbol)
|
28
|
+
@position = @choices.find_index { |choice| choice[:value] == default }
|
29
|
+
@position ||= 0
|
30
|
+
elsif default.is_a?(Integer) && default < @choices.size
|
31
|
+
@position = default
|
32
|
+
else
|
33
|
+
@position = 0
|
34
|
+
end
|
35
|
+
|
36
|
+
IOHelper.without_cursor do
|
37
|
+
|
38
|
+
question_backup = @question
|
39
|
+
@question += ' '
|
40
|
+
@question += Inquirer::Style::List.selection_help
|
41
|
+
|
42
|
+
render_prompt
|
43
|
+
|
44
|
+
@question = question_backup
|
45
|
+
|
46
|
+
IOHelper.read_char do |char|
|
47
|
+
|
48
|
+
key = IOChar.char_to_key(char)
|
49
|
+
|
50
|
+
case key
|
51
|
+
when 'up'
|
52
|
+
@position = (@position - 1) % @choices.length
|
53
|
+
when 'down'
|
54
|
+
@position = (@position + 1) % @choices.length
|
55
|
+
end
|
56
|
+
|
57
|
+
IOHelper.clear
|
58
|
+
|
59
|
+
render_prompt
|
60
|
+
|
61
|
+
key != 'return'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
IOHelper.clear
|
66
|
+
|
67
|
+
render_result
|
68
|
+
|
69
|
+
@choices[@position][:value]
|
70
|
+
end
|
71
|
+
|
72
|
+
def render_prompt
|
73
|
+
# start with the question prefix
|
74
|
+
prompt = Inquirer::Style.question_prefix
|
75
|
+
|
76
|
+
prompt += Inquirer::Style::List.question % @question
|
77
|
+
|
78
|
+
prompt += IOChar.newline
|
79
|
+
|
80
|
+
prompt += @choices.map.with_index(0) do |choice, position|
|
81
|
+
|
82
|
+
choice_prompt = ''
|
83
|
+
|
84
|
+
if position == @position
|
85
|
+
choice_prompt += Inquirer::Style::List.selector
|
86
|
+
choice_prompt += ' '
|
87
|
+
choice_prompt += Inquirer::Style::List.selected_item % choice[:name]
|
88
|
+
else
|
89
|
+
choice_prompt += ' '
|
90
|
+
choice_prompt += Inquirer::Style::List.item % choice[:name]
|
91
|
+
end
|
92
|
+
|
93
|
+
choice_prompt
|
94
|
+
end.join('')
|
95
|
+
|
96
|
+
paginated_promt = @paginator.paginate(prompt, @position)
|
97
|
+
|
98
|
+
IOHelper.render( paginated_promt )
|
99
|
+
end
|
100
|
+
|
101
|
+
def render_result
|
102
|
+
|
103
|
+
# start with the question prefix
|
104
|
+
result = Inquirer::Style.question_prefix
|
105
|
+
|
106
|
+
result += Inquirer::Style::List.question % @question
|
107
|
+
|
108
|
+
display_value = @choices[@position][:short] || @choices[@position][:name]
|
109
|
+
|
110
|
+
result += Inquirer::Style::List.response % display_value
|
111
|
+
|
112
|
+
result += IOChar.newline
|
113
|
+
|
114
|
+
IOHelper.render( result )
|
115
|
+
end
|
116
|
+
end
|