tty-prompt 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +9 -6
  3. data/CHANGELOG.md +40 -3
  4. data/Gemfile +0 -1
  5. data/README.md +246 -65
  6. data/examples/ask.rb +7 -0
  7. data/examples/echo.rb +7 -0
  8. data/examples/in.rb +7 -0
  9. data/examples/mask.rb +9 -0
  10. data/examples/multi_select.rb +8 -0
  11. data/examples/select.rb +8 -0
  12. data/examples/validation.rb +9 -0
  13. data/examples/yes_no.rb +7 -0
  14. data/lib/tty-prompt.rb +6 -4
  15. data/lib/tty/prompt.rb +100 -25
  16. data/lib/tty/prompt/choice.rb +1 -1
  17. data/lib/tty/prompt/converter_dsl.rb +19 -0
  18. data/lib/tty/prompt/converter_registry.rb +56 -0
  19. data/lib/tty/prompt/converters.rb +77 -0
  20. data/lib/tty/prompt/evaluator.rb +29 -0
  21. data/lib/tty/prompt/list.rb +38 -36
  22. data/lib/tty/prompt/mask_question.rb +85 -0
  23. data/lib/tty/prompt/multi_list.rb +21 -32
  24. data/lib/tty/prompt/question.rb +184 -162
  25. data/lib/tty/prompt/question/checks.rb +85 -0
  26. data/lib/tty/prompt/question/modifier.rb +4 -5
  27. data/lib/tty/prompt/question/validation.rb +29 -35
  28. data/lib/tty/prompt/reader.rb +98 -52
  29. data/lib/tty/prompt/reader/codes.rb +63 -0
  30. data/lib/tty/prompt/reader/key_event.rb +67 -0
  31. data/lib/tty/prompt/reader/mode.rb +66 -0
  32. data/lib/tty/prompt/reader/mode/echo.rb +43 -0
  33. data/lib/tty/prompt/reader/mode/raw.rb +43 -0
  34. data/lib/tty/prompt/result.rb +42 -0
  35. data/lib/tty/prompt/statement.rb +9 -14
  36. data/lib/tty/prompt/suggestion.rb +4 -2
  37. data/lib/tty/prompt/symbols.rb +13 -0
  38. data/lib/tty/prompt/test.rb +3 -2
  39. data/lib/tty/prompt/utils.rb +1 -1
  40. data/lib/tty/prompt/version.rb +1 -1
  41. data/spec/unit/ask_spec.rb +31 -48
  42. data/spec/unit/choice/eql_spec.rb +0 -2
  43. data/spec/unit/choice/from_spec.rb +0 -2
  44. data/spec/unit/choices/add_spec.rb +0 -2
  45. data/spec/unit/choices/each_spec.rb +0 -2
  46. data/spec/unit/choices/new_spec.rb +0 -2
  47. data/spec/unit/choices/pluck_spec.rb +0 -2
  48. data/spec/unit/converters/convert_bool_spec.rb +58 -0
  49. data/spec/unit/{response/read_char_spec.rb → converters/convert_char_spec.rb} +2 -4
  50. data/spec/unit/converters/convert_custom_spec.rb +14 -0
  51. data/spec/unit/converters/convert_date_spec.rb +25 -0
  52. data/spec/unit/converters/convert_file_spec.rb +14 -0
  53. data/spec/unit/{response/read_number_spec.rb → converters/convert_number_spec.rb} +5 -7
  54. data/spec/unit/converters/convert_path_spec.rb +15 -0
  55. data/spec/unit/{response/read_range_spec.rb → converters/convert_range_spec.rb} +3 -5
  56. data/spec/unit/converters/convert_regex_spec.rb +12 -0
  57. data/spec/unit/converters/convert_string_spec.rb +21 -0
  58. data/spec/unit/distance/distance_spec.rb +0 -2
  59. data/spec/unit/error_spec.rb +0 -6
  60. data/spec/unit/evaluator_spec.rb +67 -0
  61. data/spec/unit/keypress_spec.rb +19 -0
  62. data/spec/unit/mask_spec.rb +95 -0
  63. data/spec/unit/multi_select_spec.rb +36 -24
  64. data/spec/unit/multiline_spec.rb +19 -0
  65. data/spec/unit/new_spec.rb +18 -0
  66. data/spec/unit/ok_spec.rb +10 -0
  67. data/spec/unit/question/default_spec.rb +17 -4
  68. data/spec/unit/question/echo_spec.rb +31 -0
  69. data/spec/unit/question/in_spec.rb +48 -16
  70. data/spec/unit/question/initialize_spec.rb +2 -9
  71. data/spec/unit/question/modifier/apply_to_spec.rb +9 -16
  72. data/spec/unit/question/modifier/letter_case_spec.rb +0 -2
  73. data/spec/unit/question/modifier/whitespace_spec.rb +12 -20
  74. data/spec/unit/question/modify_spec.rb +3 -7
  75. data/spec/unit/question/required_spec.rb +20 -14
  76. data/spec/unit/question/validate_spec.rb +20 -19
  77. data/spec/unit/question/validation/call_spec.rb +15 -6
  78. data/spec/unit/question/validation/coerce_spec.rb +17 -11
  79. data/spec/unit/reader/publish_keypress_event_spec.rb +81 -0
  80. data/spec/unit/reader/read_keypress_spec.rb +22 -0
  81. data/spec/unit/reader/read_line_spec.rb +31 -0
  82. data/spec/unit/reader/read_multiline_spec.rb +37 -0
  83. data/spec/unit/result_spec.rb +40 -0
  84. data/spec/unit/say_spec.rb +18 -23
  85. data/spec/unit/select_spec.rb +37 -32
  86. data/spec/unit/statement/initialize_spec.rb +4 -4
  87. data/spec/unit/suggest_spec.rb +0 -2
  88. data/spec/unit/warn_spec.rb +0 -5
  89. data/spec/unit/yes_no_spec.rb +70 -0
  90. data/tty-prompt.gemspec +7 -4
  91. metadata +123 -40
  92. data/lib/tty/prompt/codes.rb +0 -32
  93. data/lib/tty/prompt/cursor.rb +0 -131
  94. data/lib/tty/prompt/error.rb +0 -26
  95. data/lib/tty/prompt/mode.rb +0 -64
  96. data/lib/tty/prompt/mode/echo.rb +0 -41
  97. data/lib/tty/prompt/mode/raw.rb +0 -41
  98. data/lib/tty/prompt/response.rb +0 -247
  99. data/lib/tty/prompt/response_delegation.rb +0 -42
  100. data/spec/unit/cursor/new_spec.rb +0 -74
  101. data/spec/unit/question/character_spec.rb +0 -13
  102. data/spec/unit/reader/getc_spec.rb +0 -42
  103. data/spec/unit/response/read_bool_spec.rb +0 -58
  104. data/spec/unit/response/read_date_spec.rb +0 -16
  105. data/spec/unit/response/read_email_spec.rb +0 -45
  106. data/spec/unit/response/read_multiple_spec.rb +0 -21
  107. data/spec/unit/response/read_spec.rb +0 -69
  108. data/spec/unit/response/read_string_spec.rb +0 -14
@@ -1,32 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module TTY
4
- class Prompt
5
- module Codes
6
- BACKSPACE = "\177"
7
- DELETE = "\004"
8
- ESCAPE = "\e"
9
- LINEFEED = "\n"
10
- RETURN = "\r"
11
- SPACE = " "
12
- TAB = "\t"
13
-
14
- KEY_UP = "\e[A"
15
- KEY_DOWN = "\e[B"
16
- KEY_RIGHT = "\e[C"
17
- KEY_LEFT = "\e[D"
18
- KEY_DELETE = "\e[3"
19
-
20
- CTRL_J = "\x0A"
21
- CTRL_N = "\x0E"
22
- CTRL_K = "\x0B"
23
- CTRL_P = "\x10"
24
- SIGINT = "\x03"
25
-
26
- ITEM_SECURE = "•"
27
- ITEM_SELECTED = "‣"
28
- RADIO_CHECKED = "⬢"
29
- RADIO_UNCHECKED = "⬡"
30
- end # Codes
31
- end # Prompt
32
- end # TTY
@@ -1,131 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module TTY
4
- class Prompt
5
- class Cursor
6
- ECMA_CSI = "\e[".freeze
7
- DEC_RST = 'l'.freeze
8
- DEC_SET = 'h'.freeze
9
- DEC_TCEM = '?25'.freeze
10
- ECMA_CLR = 'K'.freeze
11
-
12
- attr_reader :shell
13
-
14
- def initialize(stream = nil, options = {})
15
- @stream = stream || $stdout
16
- @shell = options.fetch(:shell, false)
17
- @hidden = options.fetch(:hidden, false)
18
- end
19
-
20
- def print
21
- self.class.new(@stream, shell: true)
22
- end
23
-
24
- def show
25
- @hidden = false
26
- ECMA_CSI + DEC_TCEM + DEC_SET
27
- end
28
-
29
- def hide
30
- show if @hidden
31
- @hidden = true
32
- ECMA_CSI + DEC_TCEM + DEC_RST
33
- end
34
-
35
- # Switch off cursor for the block
36
- # @api public
37
- def invisible
38
- hide
39
- yield
40
- ensure
41
- show
42
- end
43
-
44
- # Save current position
45
- # @api public
46
- def save
47
- ECMA_CSI + "s"
48
- end
49
-
50
- # Restore cursor position
51
- # @api public
52
- def restore
53
- ECMA_CSI + "u"
54
- end
55
-
56
- def current
57
- ECMA_CSI + "6n"
58
- end
59
-
60
- # Move cursor relative to its current position
61
- # @api public
62
- def move(x, y)
63
- end
64
-
65
- # Move cursor up by number of lines
66
- #
67
- # @param [Integer] count
68
- #
69
- # @api public
70
- def move_up(count = nil)
71
- ECMA_CSI + "#{(count || 1)}A"
72
- end
73
-
74
- def move_down(count = nil)
75
- ECMA_CSI + "#{(count || 1)}B"
76
- end
77
-
78
- # Move to start of the line
79
- #
80
- # @api public
81
- def move_start
82
- ECMA_CSI + '1000D'
83
- end
84
-
85
- # @param [Integer] count
86
- # how far to go left
87
- # @api public
88
- def move_left(count = nil)
89
- ECMA_CSI + "#{count || 1}D"
90
- end
91
-
92
- # @api public
93
- def move_right(count = nil)
94
- ECMA_CSI + "#{count || 1}C"
95
- end
96
-
97
- def next_line
98
- ECMA_CSI + 'E'
99
- end
100
-
101
- def prev_line
102
- ECMA_CSI + 'F'
103
- end
104
-
105
- # @api public
106
- def clear_line
107
- move_start + ECMA_CSI + ECMA_CLR
108
- end
109
-
110
- # @api public
111
- def clear_lines(amount, direction = :up)
112
- amount.times.reduce("") do |acc|
113
- dir = direction == :up ? move_up : move_down
114
- acc << dir + clear_line
115
- end
116
- end
117
-
118
- # Clear screen down from current position
119
- # @api public
120
- def clear_down
121
- ECMA_CSI + "J"
122
- end
123
-
124
- # Clear screen up from current position
125
- # @api public
126
- def clear_up
127
- ECMA_CSI + "1J"
128
- end
129
- end # Cursor
130
- end # Prompt
131
- end # TTY
@@ -1,26 +0,0 @@
1
- # encoding: utf-8
2
-
3
- class Error
4
-
5
- attr_reader :errors
6
-
7
- def initialize(question, errors=[])
8
- @question = question
9
- @errors = errors
10
- end
11
-
12
- def <<(type, message)
13
- @errors << [type, message]
14
- end
15
-
16
-
17
- # Handle exception
18
- #
19
- # @api private
20
- def error_wrapping(&block)
21
- yield
22
- rescue
23
- question.error? ? block.call : raise
24
- end
25
-
26
- end # Error
@@ -1,64 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'tty/prompt/mode/echo'
4
- require 'tty/prompt/mode/raw'
5
-
6
- module TTY
7
- class Prompt
8
- class Mode
9
- # Initialize a Terminal
10
- #
11
- # @api public
12
- def initialize(options = {})
13
- @echo = TTY::Prompt::Mode::Echo.new
14
- @raw = TTY::Prompt::Mode::Raw.new
15
- end
16
-
17
- # Switch echo on
18
- #
19
- # @api public
20
- def echo_on
21
- @echo.on
22
- end
23
-
24
- # Switch echo off
25
- #
26
- # @api public
27
- def echo_off
28
- @echo.off
29
- end
30
-
31
- # Echo given block
32
- #
33
- # @param [Boolean] is_on
34
- #
35
- # @api public
36
- def echo(is_on = true, &block)
37
- @echo.echo(is_on, &block)
38
- end
39
-
40
- # Switch raw mode on
41
- #
42
- # @api public
43
- def raw_on
44
- @raw.on
45
- end
46
-
47
- # Switch raw mode off
48
- #
49
- # @api public
50
- def raw_off
51
- @raw.off
52
- end
53
-
54
- # Use raw mode in the given block
55
- #
56
- # @param [Boolean] is_on
57
- #
58
- # @api public
59
- def raw(is_on = true, &block)
60
- @raw.raw(is_on, &block)
61
- end
62
- end # Mode
63
- end # Prompt
64
- end # TTY
@@ -1,41 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module TTY
4
- class Prompt
5
- class Mode
6
- # A class responsible for toggling echo.
7
- class Echo
8
- # Turn echo on
9
- #
10
- # @api public
11
- def on
12
- %x{stty echo} if TTY::Platform.unix?
13
- end
14
-
15
- # Turn echo off
16
- #
17
- # @api public
18
- def off
19
- %x{stty -echo} if TTY::Platform.unix?
20
- end
21
-
22
- # Wrap code block inside echo
23
- #
24
- # @api public
25
- def echo(is_on=true, &block)
26
- value = nil
27
- off unless is_on
28
- value = block.call if block_given?
29
- rescue NoMethodError, Interrupt => error
30
- puts "#{error.class} #{error.message}"
31
- puts error.backtrace
32
- on
33
- exit
34
- ensure
35
- on
36
- return value
37
- end
38
- end # Echo
39
- end # Mode
40
- end # Prompt
41
- end # TTY
@@ -1,41 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module TTY
4
- class Prompt
5
- class Mode
6
- # A class responsible for toggling raw mode.
7
- class Raw
8
- # Turn raw mode on
9
- #
10
- # @api public
11
- def on
12
- %x{stty raw} if TTY::Platform.unix?
13
- end
14
-
15
- # Turn raw mode off
16
- #
17
- # @api public
18
- def off
19
- %x{stty -raw} if TTY::Platform.unix?
20
- end
21
-
22
- # Wrap code block inside raw mode
23
- #
24
- # @api public
25
- def raw(is_on=true, &block)
26
- value = nil
27
- on if is_on
28
- value = block.call if block_given?
29
- rescue NoMethodError, Interrupt => error
30
- puts "#{error.class} #{error.message}"
31
- puts error.backtrace
32
- off
33
- exit
34
- ensure
35
- off
36
- return value
37
- end
38
- end # Raw
39
- end # Mode
40
- end # Prompt
41
- end # TTY
@@ -1,247 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module TTY
4
- # A class responsible for shell prompt interactions
5
- class Prompt
6
- # A class representing a shell response
7
- class Response
8
- attr_reader :reader
9
- private :reader
10
-
11
- attr_reader :question
12
- private :question
13
-
14
- # Initialize a Response
15
- #
16
- # @api public
17
- def initialize(question, prompt)
18
- @question = question
19
- @prompt = prompt
20
- @converter = Necromancer.new
21
- @reader = Reader.new(@prompt)
22
- end
23
-
24
- # Read input from STDIN either character or line
25
- #
26
- # @param [Symbol] type
27
- #
28
- # @return [undefined]
29
- #
30
- # @api public
31
- def read(type = nil)
32
- question.evaluate_response read_input
33
- end
34
-
35
- # @api private
36
- def read_input
37
- if question.mask? && question.echo?
38
- reader.getc(question.mask)
39
- else
40
- reader.mode.echo(question.echo) do
41
- reader.mode.raw(question.raw) do
42
- if question.raw?
43
- reader.readpartial(10)
44
- elsif question.character?
45
- reader.getc(question.mask)
46
- else
47
- reader.gets
48
- end
49
- end
50
- end
51
- end
52
- end
53
-
54
- def no_input?(input)
55
- !input || input == "\n" || input.empty?
56
- end
57
-
58
- # @api private
59
- def evaluate_response
60
- input = read_input
61
- input = if no_input?(input)
62
- nil
63
- elsif block_given?
64
- yield(input)
65
- else input
66
- end
67
- question.evaluate_response(input)
68
- end
69
-
70
- # Read answer and cast to String type
71
- #
72
- # @param [String] error
73
- # error to display on failed conversion to string type
74
- #
75
- # @api public
76
- def read_string(error = nil)
77
- evaluate_response { |input| String(input).strip }
78
- end
79
-
80
- # Read answer's first character
81
- #
82
- # @api public
83
- def read_char
84
- question.char(true)
85
- evaluate_response { |input| String(input).chars.to_a[0] }
86
- end
87
-
88
- # Read multiple line answer and cast to String type
89
- #
90
- # @api public
91
- def read_text
92
- evaluate_response { |input| String(input) }
93
- end
94
-
95
- # Read ansewr and cast to Symbol type
96
- #
97
- # @api public
98
- def read_symbol(error = nil)
99
- evaluate_response { |input| input.to_sym }
100
- end
101
-
102
- # Read integer value
103
- #
104
- # @api public
105
- def read_int(error = nil)
106
- evaluate_response { |input| @converter.convert(input).to(:integer) }
107
- end
108
-
109
- # Read float value
110
- #
111
- # @api public
112
- def read_float(error = nil)
113
- evaluate_response { |input| @converter.convert(input).to(:float) }
114
- end
115
-
116
- # Read regular expression
117
- #
118
- # @api public
119
- def read_regex(error = nil)
120
- evaluate_response { |input| Kernel.send(:Regex, input) }
121
- end
122
-
123
- # Read range expression
124
- #
125
- # @api public
126
- def read_range
127
- evaluate_response { |input| @converter.convert(input).to(:range, strict: true) }
128
- end
129
-
130
- # Read date
131
- #
132
- # @api public
133
- def read_date
134
- evaluate_response { |input| @converter.convert(input).to(:date) }
135
- end
136
-
137
- # Read datetime
138
- #
139
- # @api public
140
- def read_datetime
141
- evaluate_response { |input| @converter.convert(input).to(:datetime) }
142
- end
143
-
144
- # Read boolean
145
- #
146
- # @api public
147
- def read_bool(error = nil)
148
- evaluate_response { |input| @converter.convert(input).to(:boolean, strict: true) }
149
- end
150
-
151
- # Read file contents
152
- #
153
- # @api public
154
- def read_file(error = nil)
155
- evaluate_response { |input| File.open(File.join(directory, input)) }
156
- end
157
-
158
- # Read string answer and validate against email regex
159
- #
160
- # @return [String]
161
- #
162
- # @api public
163
- def read_email
164
- question.validate(/^[a-z0-9._%+-]+@([a-z0-9-]+\.)+[a-z]{2,6}$/i)
165
- question.call("\n" + question.statement) if question.error?
166
- with_exception { read_string }
167
- end
168
-
169
- # Read answer provided on multiple lines
170
- #
171
- # @api public
172
- def read_multiline
173
- response = ''
174
- loop do
175
- value = evaluate_response
176
- break if !value || value == ''
177
- next if value !~ /\S/
178
- response << value
179
- end
180
- response
181
- end
182
-
183
- # Read password
184
- #
185
- # @api public
186
- def read_password
187
- question.echo false
188
- evaluate_response
189
- end
190
-
191
- # Read a single keypress
192
- #
193
- # @api public
194
- def read_keypress
195
- question.echo false
196
- question.raw true
197
- question.evaluate_response(read_input).tap do |key|
198
- raise Interrupt if key == "\x03" # Ctrl-C
199
- end
200
- end
201
-
202
- # Ignore exception
203
- #
204
- # @api private
205
- def with_exception(&block)
206
- yield
207
- rescue
208
- question.error? ? block.call : raise
209
- end
210
-
211
- # @param [Symbol] type
212
- # :boolean, :string, :numeric, :array
213
- #
214
- # @api private
215
- def read_type(class_or_name = nil)
216
- case class_or_name
217
- when :bool
218
- read_bool
219
- when :email
220
- read_email
221
- when :char
222
- read_char
223
- when :date
224
- read_date
225
- when :int
226
- read_int
227
- when :range
228
- read_range
229
- when :multiline
230
- read_multiline
231
- when :float
232
- read_float
233
- when :file
234
- read_file
235
- when :string
236
- read_string
237
- when :symbol
238
- read_symbol
239
- when :keypress
240
- read_keypress
241
- else
242
- read
243
- end
244
- end
245
- end # Response
246
- end # Prompt
247
- end # TTY