tty-prompt 0.2.0 → 0.3.0

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.
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