inquirer.rb 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/inquirer.rb +3 -1
- data/lib/inquirer/prompts/checkbox.rb +1 -5
- data/lib/inquirer/prompts/checkbox_filterable.rb +255 -0
- data/lib/inquirer/prompts/confirm.rb +11 -9
- data/lib/inquirer/prompts/list_filterable.rb +209 -0
- data/lib/inquirer/style.rb +8 -10
- data/lib/inquirer/style/checkbox_filterable.rb +14 -0
- data/lib/inquirer/style/list.rb +1 -1
- data/lib/inquirer/style/list_filterable.rb +14 -0
- data/lib/inquirer/utils/iochar.rb +5 -1
- data/lib/inquirer/utils/iohelper.rb +22 -12
- data/lib/inquirer/version.rb +1 -1
- metadata +30 -30
- data/.gitignore +0 -9
- data/CODE_OF_CONDUCT.md +0 -49
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -21
- data/README.md +0 -203
- data/Rakefile +0 -2
- data/examples/checkbox.rb +0 -211
- data/examples/confirm.rb +0 -63
- data/examples/input.rb +0 -67
- data/examples/list.rb +0 -193
- data/examples/password.rb +0 -63
- data/inquirer.gemspec +0 -34
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2660e6433f7e913796819045ad9a22920a626bde
|
4
|
+
data.tar.gz: 409454f61b1bbc915e5f8684f0cd0136c918a9c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4c143a8915d1ff0ccc20d310fd5311ec448ee421e1f4f70e1f418273daef0eb887b4d0600a24ba4f06d67f6579d88653e2a56d31edaf149fe9deb06fa8a3663
|
7
|
+
data.tar.gz: 0267f36718cacf9eb9e2272a40fcab8190377c27c4b5ec6f6df4c792d2829c2cfcf41f9ffde4be46af9c40b46537f99cab01e7e8b7cb4e341b3a6e84f0ffd49e
|
data/lib/inquirer.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'inquirer/version'
|
2
2
|
require 'inquirer/utils/iohelper'
|
3
3
|
require 'inquirer/prompts/checkbox'
|
4
|
+
require 'inquirer/prompts/checkbox_filterable'
|
4
5
|
require 'inquirer/prompts/confirm'
|
5
6
|
require 'inquirer/prompts/input'
|
6
7
|
require 'inquirer/prompts/list'
|
8
|
+
require 'inquirer/prompts/list_filterable'
|
7
9
|
require 'inquirer/prompts/password'
|
8
10
|
|
9
11
|
module Inquirer
|
@@ -52,7 +54,7 @@ module Inquirer
|
|
52
54
|
repeat_counter: repeat_counter,
|
53
55
|
)
|
54
56
|
|
55
|
-
object = Kernel.const_get(prompt[:type].to_s.capitalize)
|
57
|
+
object = Kernel.const_get(prompt[:type].to_s.split('_').collect(&:capitalize).join)
|
56
58
|
answer = object.prompt(type_parameter)
|
57
59
|
|
58
60
|
if prompt[:filter] && prompt[:filter].is_a?(Proc)
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'inquirer/utils/paginator'
|
2
|
+
require 'inquirer/utils/iochar'
|
3
|
+
require 'inquirer/utils/iohelper'
|
4
|
+
require 'inquirer/style/checkbox_filterable'
|
5
|
+
|
6
|
+
module CheckboxFilterable
|
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
|
+
if choice[:when].is_a?(Proc)
|
22
|
+
|
23
|
+
when_parameter = opts.merge(
|
24
|
+
choice: choice,
|
25
|
+
)
|
26
|
+
|
27
|
+
ask_choice = choice[:when].call( when_parameter )
|
28
|
+
|
29
|
+
next if !ask_choice
|
30
|
+
elsif [true, false].include? choice[:when]
|
31
|
+
next if !choice[:when]
|
32
|
+
end
|
33
|
+
|
34
|
+
choice[:value] ||= choice[:name]
|
35
|
+
|
36
|
+
if !choice[:checked] && opts[:default].is_a?(Array)
|
37
|
+
choice[:checked] = opts[:default].include?( choice[:value] )
|
38
|
+
end
|
39
|
+
|
40
|
+
@choices.push(choice)
|
41
|
+
}
|
42
|
+
|
43
|
+
question_backup = @question
|
44
|
+
@question += ' '
|
45
|
+
@question += Inquirer::Style::CheckboxFilterable.selection_help
|
46
|
+
|
47
|
+
@filter = ''
|
48
|
+
cursor_position = 0
|
49
|
+
@choices_filtered = @choices
|
50
|
+
@question = question_backup
|
51
|
+
|
52
|
+
render_prompt
|
53
|
+
|
54
|
+
# loop through user input
|
55
|
+
IOHelper.read_char do |char|
|
56
|
+
|
57
|
+
key = IOChar.char_to_key(char)
|
58
|
+
|
59
|
+
if @error_message
|
60
|
+
@error_message = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
case key
|
64
|
+
when 'up'
|
65
|
+
if !@choices_filtered.empty?
|
66
|
+
@position = (@position - 1) % @choices_filtered.length
|
67
|
+
else
|
68
|
+
check_no_match
|
69
|
+
end
|
70
|
+
when 'down'
|
71
|
+
if !@choices_filtered.empty?
|
72
|
+
@position = (@position + 1) % @choices_filtered.length
|
73
|
+
else
|
74
|
+
check_no_match
|
75
|
+
end
|
76
|
+
when 'space'
|
77
|
+
if !@choices_filtered.empty?
|
78
|
+
@choices_filtered[@position][:checked] = !@choices_filtered[@position][:checked]
|
79
|
+
|
80
|
+
choice = @choices_filtered[@position]
|
81
|
+
|
82
|
+
@choices.map! {|possible_choice|
|
83
|
+
|
84
|
+
if choice[:name] == possible_choice[:name]
|
85
|
+
possible_choice = choice
|
86
|
+
end
|
87
|
+
|
88
|
+
possible_choice
|
89
|
+
}
|
90
|
+
end
|
91
|
+
when 'return'
|
92
|
+
if opts[:validate] and opts[:validate].is_a?(Proc)
|
93
|
+
|
94
|
+
value = @choices.reject { |choice|
|
95
|
+
!choice[:checked]
|
96
|
+
}.collect { |choice|
|
97
|
+
choice[:value]
|
98
|
+
}
|
99
|
+
|
100
|
+
validation_result = opts[:validate].call( value )
|
101
|
+
|
102
|
+
if !validation_result
|
103
|
+
@error_message = Inquirer::Style::CheckboxFilterable.error_message_invalid_value
|
104
|
+
elsif validation_result && validation_result.is_a?(String)
|
105
|
+
@error_message = validation_result
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
if !@error_message
|
110
|
+
check_no_match
|
111
|
+
end
|
112
|
+
when 'backspace'
|
113
|
+
|
114
|
+
index = @filter.size - cursor_position - 1
|
115
|
+
|
116
|
+
if index >= 0
|
117
|
+
# remove char at current index
|
118
|
+
@filter[index] = ''
|
119
|
+
end
|
120
|
+
|
121
|
+
filter_choices
|
122
|
+
when 'left'
|
123
|
+
if cursor_position < @filter.length
|
124
|
+
cursor_position += 1
|
125
|
+
end
|
126
|
+
|
127
|
+
check_no_match
|
128
|
+
when 'right'
|
129
|
+
if cursor_position > 0
|
130
|
+
cursor_position -= 1
|
131
|
+
end
|
132
|
+
|
133
|
+
check_no_match
|
134
|
+
else
|
135
|
+
@filter = @filter.insert(@filter.length - cursor_position, char)
|
136
|
+
|
137
|
+
filter_choices
|
138
|
+
end
|
139
|
+
|
140
|
+
IOHelper.clear
|
141
|
+
|
142
|
+
render_prompt
|
143
|
+
|
144
|
+
update_cursor_position(cursor_position)
|
145
|
+
|
146
|
+
# we are done if the user hits return
|
147
|
+
@error_message || key != 'return'
|
148
|
+
end
|
149
|
+
|
150
|
+
render_result
|
151
|
+
|
152
|
+
@choices.reject { |choice|
|
153
|
+
!choice[:checked]
|
154
|
+
}.collect { |choice|
|
155
|
+
choice[:value]
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
def render_prompt
|
160
|
+
# start with the question prefix
|
161
|
+
prompt = Inquirer::Style.question_prefix
|
162
|
+
|
163
|
+
# render the question
|
164
|
+
prompt += Inquirer::Style::CheckboxFilterable.question % @question
|
165
|
+
|
166
|
+
prompt += IOChar.newline
|
167
|
+
|
168
|
+
# render the list
|
169
|
+
prompt += @choices_filtered.map.with_index(0) do |choice, position|
|
170
|
+
|
171
|
+
choice_prompt = ''
|
172
|
+
|
173
|
+
if position == @position
|
174
|
+
choice_prompt += Inquirer::Style::CheckboxFilterable.selector
|
175
|
+
else
|
176
|
+
choice_prompt += ' '
|
177
|
+
end
|
178
|
+
|
179
|
+
if choice[:checked]
|
180
|
+
choice_prompt += Inquirer::Style::CheckboxFilterable.checkbox_on
|
181
|
+
choice_prompt += ' '
|
182
|
+
choice_prompt += Inquirer::Style::CheckboxFilterable.checked_item % choice[:name]
|
183
|
+
else
|
184
|
+
choice_prompt += Inquirer::Style::CheckboxFilterable.checkbox_off
|
185
|
+
choice_prompt += ' '
|
186
|
+
choice_prompt += Inquirer::Style::CheckboxFilterable.item % choice[:name]
|
187
|
+
end
|
188
|
+
|
189
|
+
choice_prompt
|
190
|
+
end.join('')
|
191
|
+
|
192
|
+
paginated_prompt = @paginator.paginate(prompt, @position)
|
193
|
+
|
194
|
+
# render error message
|
195
|
+
if @error_message
|
196
|
+
paginated_prompt += Inquirer::Style::CheckboxFilterable.error_message % @error_message
|
197
|
+
end
|
198
|
+
|
199
|
+
paginated_prompt += IOChar.newline
|
200
|
+
|
201
|
+
paginated_prompt += Inquirer::Style::CheckboxFilterable.filter_prefix
|
202
|
+
|
203
|
+
paginated_prompt += Inquirer::Style::CheckboxFilterable.filter % @filter
|
204
|
+
|
205
|
+
IOHelper.render( paginated_prompt )
|
206
|
+
end
|
207
|
+
|
208
|
+
def render_result
|
209
|
+
# start with the question prefix
|
210
|
+
result = Inquirer::Style.question_prefix
|
211
|
+
|
212
|
+
# render the question
|
213
|
+
result += Inquirer::Style::CheckboxFilterable.question % @question
|
214
|
+
|
215
|
+
selected_choices = @choices.reject { |choice|
|
216
|
+
!choice[:checked]
|
217
|
+
}.collect { |choice|
|
218
|
+
choice[:short] || choice[:name]
|
219
|
+
}.join(', ')
|
220
|
+
|
221
|
+
if !selected_choices.empty?
|
222
|
+
result += Inquirer::Style::CheckboxFilterable.response % selected_choices
|
223
|
+
end
|
224
|
+
|
225
|
+
result += IOChar.newline
|
226
|
+
|
227
|
+
IOHelper.clear
|
228
|
+
|
229
|
+
IOHelper.render( result )
|
230
|
+
end
|
231
|
+
|
232
|
+
def update_cursor_position(cursor_position)
|
233
|
+
print IOChar.cursor_left * cursor_position
|
234
|
+
end
|
235
|
+
|
236
|
+
def filter_choices
|
237
|
+
@position = 0
|
238
|
+
@choices_filtered = []
|
239
|
+
|
240
|
+
@choices.each { |choice|
|
241
|
+
|
242
|
+
next if !choice[:name].gsub(/\s/, '').downcase.index(@filter.gsub(/\s/, '').downcase)
|
243
|
+
|
244
|
+
@choices_filtered.push(choice)
|
245
|
+
}
|
246
|
+
|
247
|
+
check_no_match
|
248
|
+
end
|
249
|
+
|
250
|
+
def check_no_match
|
251
|
+
return if !@choices_filtered.empty?
|
252
|
+
|
253
|
+
@error_message = 'No matching choice found'
|
254
|
+
end
|
255
|
+
end
|
@@ -23,7 +23,7 @@ module Confirm
|
|
23
23
|
elsif key.casecmp( Inquirer::Style::Confirm.option_false[0] ) == 0
|
24
24
|
@value = false
|
25
25
|
false
|
26
|
-
elsif key == 'return'
|
26
|
+
elsif key == 'return' && !@default.nil?
|
27
27
|
@value = @default
|
28
28
|
false
|
29
29
|
else
|
@@ -64,19 +64,21 @@ module Confirm
|
|
64
64
|
|
65
65
|
def render_result
|
66
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
67
|
# start with the question prefix
|
75
68
|
result = Inquirer::Style.question_prefix
|
76
69
|
|
77
70
|
result += Inquirer::Style::Confirm.question % @question
|
78
71
|
|
79
|
-
|
72
|
+
response = nil
|
73
|
+
if !@value.nil?
|
74
|
+
if @value
|
75
|
+
response = Inquirer::Style::Confirm.option_true
|
76
|
+
else
|
77
|
+
response = Inquirer::Style::Confirm.option_false
|
78
|
+
end
|
79
|
+
|
80
|
+
result += Inquirer::Style::Confirm.response % response
|
81
|
+
end
|
80
82
|
|
81
83
|
result += IOChar.newline
|
82
84
|
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'inquirer/utils/iochar'
|
2
|
+
require 'inquirer/utils/iohelper'
|
3
|
+
require 'inquirer/utils/paginator'
|
4
|
+
require 'inquirer/style/list_filterable'
|
5
|
+
|
6
|
+
module ListFilterable
|
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
|
+
if choice[:when].is_a?(Proc)
|
21
|
+
|
22
|
+
when_parameter = opts.merge(
|
23
|
+
choice: choice,
|
24
|
+
)
|
25
|
+
|
26
|
+
ask_choice = choice[:when].call( when_parameter )
|
27
|
+
|
28
|
+
next if !ask_choice
|
29
|
+
elsif [true, false].include? choice[:when]
|
30
|
+
next if !choice[:when]
|
31
|
+
end
|
32
|
+
|
33
|
+
choice[:value] ||= choice[:name]
|
34
|
+
|
35
|
+
@choices.push(choice)
|
36
|
+
}
|
37
|
+
|
38
|
+
return nil if Array(@choices).empty?
|
39
|
+
|
40
|
+
return @choices[0][:value] if @choices.size == 1
|
41
|
+
|
42
|
+
if default.is_a?(String) || default.is_a?(Symbol)
|
43
|
+
@position = @choices.find_index { |choice| choice[:value] == default }
|
44
|
+
@position ||= 0
|
45
|
+
elsif default.is_a?(Integer) && default < @choices.size
|
46
|
+
@position = default
|
47
|
+
else
|
48
|
+
@position = 0
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
question_backup = @question
|
53
|
+
@question += ' '
|
54
|
+
@question += Inquirer::Style::ListFilterable.selection_help
|
55
|
+
|
56
|
+
@filter = ''
|
57
|
+
cursor_position = 0
|
58
|
+
@choices_filtered = @choices
|
59
|
+
@question = question_backup
|
60
|
+
|
61
|
+
render_prompt
|
62
|
+
|
63
|
+
IOHelper.read_char do |char|
|
64
|
+
|
65
|
+
key = IOChar.char_to_key(char)
|
66
|
+
|
67
|
+
if @error_message
|
68
|
+
@error_message = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
case key
|
72
|
+
when 'up'
|
73
|
+
if !@choices_filtered.empty?
|
74
|
+
@position = (@position - 1) % @choices_filtered.length
|
75
|
+
else
|
76
|
+
check_no_match
|
77
|
+
end
|
78
|
+
when 'down'
|
79
|
+
if !@choices_filtered.empty?
|
80
|
+
@position = (@position + 1) % @choices_filtered.length
|
81
|
+
else
|
82
|
+
check_no_match
|
83
|
+
end
|
84
|
+
when 'backspace'
|
85
|
+
|
86
|
+
index = @filter.size - cursor_position - 1
|
87
|
+
|
88
|
+
if index >= 0
|
89
|
+
# remove char at current index
|
90
|
+
@filter[index] = ''
|
91
|
+
end
|
92
|
+
|
93
|
+
filter_choices
|
94
|
+
when 'left'
|
95
|
+
if cursor_position < @filter.length
|
96
|
+
cursor_position += 1
|
97
|
+
end
|
98
|
+
|
99
|
+
check_no_match
|
100
|
+
when 'right'
|
101
|
+
if cursor_position > 0
|
102
|
+
cursor_position -= 1
|
103
|
+
end
|
104
|
+
|
105
|
+
check_no_match
|
106
|
+
else
|
107
|
+
|
108
|
+
if !['return'].include?(key)
|
109
|
+
@filter = @filter.insert(@filter.length - cursor_position, char)
|
110
|
+
filter_choices
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
IOHelper.clear
|
115
|
+
|
116
|
+
render_prompt
|
117
|
+
|
118
|
+
update_cursor_position(cursor_position)
|
119
|
+
|
120
|
+
key != 'return' || @choices_filtered.empty?
|
121
|
+
end
|
122
|
+
|
123
|
+
IOHelper.clear
|
124
|
+
|
125
|
+
render_result
|
126
|
+
|
127
|
+
@choices_filtered[@position][:value]
|
128
|
+
end
|
129
|
+
|
130
|
+
def render_prompt
|
131
|
+
# start with the question prefix
|
132
|
+
prompt = Inquirer::Style.question_prefix
|
133
|
+
|
134
|
+
prompt += Inquirer::Style::ListFilterable.question % @question
|
135
|
+
|
136
|
+
prompt += IOChar.newline
|
137
|
+
|
138
|
+
prompt += @choices_filtered.map.with_index(0) do |choice, position|
|
139
|
+
|
140
|
+
choice_prompt = ''
|
141
|
+
|
142
|
+
if position == @position
|
143
|
+
choice_prompt += Inquirer::Style::ListFilterable.selector
|
144
|
+
choice_prompt += ' '
|
145
|
+
choice_prompt += Inquirer::Style::ListFilterable.selected_item % choice[:name]
|
146
|
+
else
|
147
|
+
choice_prompt += ' '
|
148
|
+
choice_prompt += Inquirer::Style::ListFilterable.item % choice[:name]
|
149
|
+
end
|
150
|
+
|
151
|
+
choice_prompt
|
152
|
+
end.join('')
|
153
|
+
|
154
|
+
paginated_prompt = @paginator.paginate(prompt, @position)
|
155
|
+
|
156
|
+
# render error message
|
157
|
+
if @error_message
|
158
|
+
paginated_prompt += Inquirer::Style::ListFilterable.error_message % @error_message
|
159
|
+
end
|
160
|
+
|
161
|
+
paginated_prompt += IOChar.newline
|
162
|
+
|
163
|
+
paginated_prompt += Inquirer::Style::ListFilterable.filter_prefix
|
164
|
+
|
165
|
+
paginated_prompt += Inquirer::Style::ListFilterable.filter % @filter
|
166
|
+
|
167
|
+
IOHelper.render( paginated_prompt )
|
168
|
+
end
|
169
|
+
|
170
|
+
def render_result
|
171
|
+
|
172
|
+
# start with the question prefix
|
173
|
+
result = Inquirer::Style.question_prefix
|
174
|
+
|
175
|
+
result += Inquirer::Style::ListFilterable.question % @question
|
176
|
+
|
177
|
+
display_value = @choices_filtered[@position][:short] || @choices_filtered[@position][:name]
|
178
|
+
|
179
|
+
result += Inquirer::Style::ListFilterable.response % display_value
|
180
|
+
|
181
|
+
result += IOChar.newline
|
182
|
+
|
183
|
+
IOHelper.render( result )
|
184
|
+
end
|
185
|
+
|
186
|
+
def update_cursor_position(cursor_position)
|
187
|
+
print IOChar.cursor_left * cursor_position
|
188
|
+
end
|
189
|
+
|
190
|
+
def filter_choices
|
191
|
+
@position = 0
|
192
|
+
@choices_filtered = []
|
193
|
+
|
194
|
+
@choices.each { |choice|
|
195
|
+
|
196
|
+
next if !choice[:name].gsub(/\s/, '').downcase.index(@filter.gsub(/\s/, '').downcase)
|
197
|
+
|
198
|
+
@choices_filtered.push(choice)
|
199
|
+
}
|
200
|
+
|
201
|
+
check_no_match
|
202
|
+
end
|
203
|
+
|
204
|
+
def check_no_match
|
205
|
+
return if !@choices_filtered.empty?
|
206
|
+
|
207
|
+
@error_message = 'No matching choice found'
|
208
|
+
end
|
209
|
+
end
|