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.
- checksums.yaml +4 -4
- data/.travis.yml +9 -6
- data/CHANGELOG.md +40 -3
- data/Gemfile +0 -1
- data/README.md +246 -65
- data/examples/ask.rb +7 -0
- data/examples/echo.rb +7 -0
- data/examples/in.rb +7 -0
- data/examples/mask.rb +9 -0
- data/examples/multi_select.rb +8 -0
- data/examples/select.rb +8 -0
- data/examples/validation.rb +9 -0
- data/examples/yes_no.rb +7 -0
- data/lib/tty-prompt.rb +6 -4
- data/lib/tty/prompt.rb +100 -25
- data/lib/tty/prompt/choice.rb +1 -1
- data/lib/tty/prompt/converter_dsl.rb +19 -0
- data/lib/tty/prompt/converter_registry.rb +56 -0
- data/lib/tty/prompt/converters.rb +77 -0
- data/lib/tty/prompt/evaluator.rb +29 -0
- data/lib/tty/prompt/list.rb +38 -36
- data/lib/tty/prompt/mask_question.rb +85 -0
- data/lib/tty/prompt/multi_list.rb +21 -32
- data/lib/tty/prompt/question.rb +184 -162
- data/lib/tty/prompt/question/checks.rb +85 -0
- data/lib/tty/prompt/question/modifier.rb +4 -5
- data/lib/tty/prompt/question/validation.rb +29 -35
- data/lib/tty/prompt/reader.rb +98 -52
- data/lib/tty/prompt/reader/codes.rb +63 -0
- data/lib/tty/prompt/reader/key_event.rb +67 -0
- data/lib/tty/prompt/reader/mode.rb +66 -0
- data/lib/tty/prompt/reader/mode/echo.rb +43 -0
- data/lib/tty/prompt/reader/mode/raw.rb +43 -0
- data/lib/tty/prompt/result.rb +42 -0
- data/lib/tty/prompt/statement.rb +9 -14
- data/lib/tty/prompt/suggestion.rb +4 -2
- data/lib/tty/prompt/symbols.rb +13 -0
- data/lib/tty/prompt/test.rb +3 -2
- data/lib/tty/prompt/utils.rb +1 -1
- data/lib/tty/prompt/version.rb +1 -1
- data/spec/unit/ask_spec.rb +31 -48
- data/spec/unit/choice/eql_spec.rb +0 -2
- data/spec/unit/choice/from_spec.rb +0 -2
- data/spec/unit/choices/add_spec.rb +0 -2
- data/spec/unit/choices/each_spec.rb +0 -2
- data/spec/unit/choices/new_spec.rb +0 -2
- data/spec/unit/choices/pluck_spec.rb +0 -2
- data/spec/unit/converters/convert_bool_spec.rb +58 -0
- data/spec/unit/{response/read_char_spec.rb → converters/convert_char_spec.rb} +2 -4
- data/spec/unit/converters/convert_custom_spec.rb +14 -0
- data/spec/unit/converters/convert_date_spec.rb +25 -0
- data/spec/unit/converters/convert_file_spec.rb +14 -0
- data/spec/unit/{response/read_number_spec.rb → converters/convert_number_spec.rb} +5 -7
- data/spec/unit/converters/convert_path_spec.rb +15 -0
- data/spec/unit/{response/read_range_spec.rb → converters/convert_range_spec.rb} +3 -5
- data/spec/unit/converters/convert_regex_spec.rb +12 -0
- data/spec/unit/converters/convert_string_spec.rb +21 -0
- data/spec/unit/distance/distance_spec.rb +0 -2
- data/spec/unit/error_spec.rb +0 -6
- data/spec/unit/evaluator_spec.rb +67 -0
- data/spec/unit/keypress_spec.rb +19 -0
- data/spec/unit/mask_spec.rb +95 -0
- data/spec/unit/multi_select_spec.rb +36 -24
- data/spec/unit/multiline_spec.rb +19 -0
- data/spec/unit/new_spec.rb +18 -0
- data/spec/unit/ok_spec.rb +10 -0
- data/spec/unit/question/default_spec.rb +17 -4
- data/spec/unit/question/echo_spec.rb +31 -0
- data/spec/unit/question/in_spec.rb +48 -16
- data/spec/unit/question/initialize_spec.rb +2 -9
- data/spec/unit/question/modifier/apply_to_spec.rb +9 -16
- data/spec/unit/question/modifier/letter_case_spec.rb +0 -2
- data/spec/unit/question/modifier/whitespace_spec.rb +12 -20
- data/spec/unit/question/modify_spec.rb +3 -7
- data/spec/unit/question/required_spec.rb +20 -14
- data/spec/unit/question/validate_spec.rb +20 -19
- data/spec/unit/question/validation/call_spec.rb +15 -6
- data/spec/unit/question/validation/coerce_spec.rb +17 -11
- data/spec/unit/reader/publish_keypress_event_spec.rb +81 -0
- data/spec/unit/reader/read_keypress_spec.rb +22 -0
- data/spec/unit/reader/read_line_spec.rb +31 -0
- data/spec/unit/reader/read_multiline_spec.rb +37 -0
- data/spec/unit/result_spec.rb +40 -0
- data/spec/unit/say_spec.rb +18 -23
- data/spec/unit/select_spec.rb +37 -32
- data/spec/unit/statement/initialize_spec.rb +4 -4
- data/spec/unit/suggest_spec.rb +0 -2
- data/spec/unit/warn_spec.rb +0 -5
- data/spec/unit/yes_no_spec.rb +70 -0
- data/tty-prompt.gemspec +7 -4
- metadata +123 -40
- data/lib/tty/prompt/codes.rb +0 -32
- data/lib/tty/prompt/cursor.rb +0 -131
- data/lib/tty/prompt/error.rb +0 -26
- data/lib/tty/prompt/mode.rb +0 -64
- data/lib/tty/prompt/mode/echo.rb +0 -41
- data/lib/tty/prompt/mode/raw.rb +0 -41
- data/lib/tty/prompt/response.rb +0 -247
- data/lib/tty/prompt/response_delegation.rb +0 -42
- data/spec/unit/cursor/new_spec.rb +0 -74
- data/spec/unit/question/character_spec.rb +0 -13
- data/spec/unit/reader/getc_spec.rb +0 -42
- data/spec/unit/response/read_bool_spec.rb +0 -58
- data/spec/unit/response/read_date_spec.rb +0 -16
- data/spec/unit/response/read_email_spec.rb +0 -45
- data/spec/unit/response/read_multiple_spec.rb +0 -21
- data/spec/unit/response/read_spec.rb +0 -69
- data/spec/unit/response/read_string_spec.rb +0 -14
@@ -0,0 +1,67 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'tty/prompt/reader/codes'
|
4
|
+
|
5
|
+
module TTY
|
6
|
+
class Prompt
|
7
|
+
class Reader
|
8
|
+
# Responsible for meta-data information about key pressed
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
class Key < Struct.new(:name, :ctrl, :meta, :shift)
|
12
|
+
def initialize(*)
|
13
|
+
super
|
14
|
+
@ctrl = false
|
15
|
+
@meta = false
|
16
|
+
@shift = false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Represents key event emitted during keyboard press
|
21
|
+
#
|
22
|
+
# @api public
|
23
|
+
class KeyEvent < Struct.new(:value, :key)
|
24
|
+
META_KEY_CODE_RE = /^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/
|
25
|
+
|
26
|
+
def self.from(char)
|
27
|
+
key = Key.new
|
28
|
+
case char
|
29
|
+
when Codes::RETURN
|
30
|
+
key.name = :return
|
31
|
+
when Codes::LINEFEED
|
32
|
+
key.name = :enter
|
33
|
+
when Codes::TAB
|
34
|
+
key.name = :tab
|
35
|
+
when Codes::BACKSPACE
|
36
|
+
key.name = :backspace
|
37
|
+
when Codes::DELETE
|
38
|
+
key.name = :delete
|
39
|
+
when Codes::SPACE
|
40
|
+
key.name = :space
|
41
|
+
when Codes::CTRL_C, Codes::ESCAPE
|
42
|
+
key.name = :escape
|
43
|
+
when proc { |c| c <= "\x1a" }
|
44
|
+
codes = char.each_codepoint.to_a
|
45
|
+
key.name = "#{codes}"
|
46
|
+
key.ctrl = true
|
47
|
+
when /\d/
|
48
|
+
key.name = :num
|
49
|
+
when META_KEY_CODE_RE
|
50
|
+
key.meta = true
|
51
|
+
case char
|
52
|
+
when Codes::KEY_UP, Codes::CTRL_K, Codes::CTRL_P
|
53
|
+
key.name = :up
|
54
|
+
when Codes::KEY_DOWN, Codes::CTRL_J, Codes::CTRL_N
|
55
|
+
key.name = :down
|
56
|
+
when Codes::KEY_RIGHT, Codes::CTRL_L
|
57
|
+
key.name = :right
|
58
|
+
when Codes::KEY_LEFT, Codes::CTRL_H
|
59
|
+
key.name = :left
|
60
|
+
end
|
61
|
+
end
|
62
|
+
new(char, key)
|
63
|
+
end
|
64
|
+
end # KeyEvent
|
65
|
+
end # Reader
|
66
|
+
end # Prompt
|
67
|
+
end # TTY
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'tty/prompt/reader/mode/echo'
|
4
|
+
require 'tty/prompt/reader/mode/raw'
|
5
|
+
|
6
|
+
module TTY
|
7
|
+
class Prompt
|
8
|
+
class Reader
|
9
|
+
class Mode
|
10
|
+
# Initialize a Terminal
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
def initialize(options = {})
|
14
|
+
@echo = Echo.new
|
15
|
+
@raw = Raw.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# Switch echo on
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
def echo_on
|
22
|
+
@echo.on
|
23
|
+
end
|
24
|
+
|
25
|
+
# Switch echo off
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
def echo_off
|
29
|
+
@echo.off
|
30
|
+
end
|
31
|
+
|
32
|
+
# Echo given block
|
33
|
+
#
|
34
|
+
# @param [Boolean] is_on
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
def echo(is_on = true, &block)
|
38
|
+
@echo.echo(is_on, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Switch raw mode on
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
def raw_on
|
45
|
+
@raw.on
|
46
|
+
end
|
47
|
+
|
48
|
+
# Switch raw mode off
|
49
|
+
#
|
50
|
+
# @api public
|
51
|
+
def raw_off
|
52
|
+
@raw.off
|
53
|
+
end
|
54
|
+
|
55
|
+
# Use raw mode in the given block
|
56
|
+
#
|
57
|
+
# @param [Boolean] is_on
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
def raw(is_on = true, &block)
|
61
|
+
@raw.raw(is_on, &block)
|
62
|
+
end
|
63
|
+
end # Mode
|
64
|
+
end # Reader
|
65
|
+
end # Prompt
|
66
|
+
end # TTY
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Prompt
|
5
|
+
class Reader
|
6
|
+
class Mode
|
7
|
+
# A class responsible for toggling echo.
|
8
|
+
class Echo
|
9
|
+
# Turn echo on
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
def on
|
13
|
+
%x{stty echo} if TTY::Platform.unix?
|
14
|
+
end
|
15
|
+
|
16
|
+
# Turn echo off
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
def off
|
20
|
+
%x{stty -echo} if TTY::Platform.unix?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Wrap code block inside echo
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
def echo(is_on=true, &block)
|
27
|
+
value = nil
|
28
|
+
off unless is_on
|
29
|
+
value = block.call if block_given?
|
30
|
+
rescue NoMethodError, Interrupt => error
|
31
|
+
puts "#{error.class} #{error.message}"
|
32
|
+
puts error.backtrace
|
33
|
+
on
|
34
|
+
exit
|
35
|
+
ensure
|
36
|
+
on
|
37
|
+
value
|
38
|
+
end
|
39
|
+
end # Echo
|
40
|
+
end # Mode
|
41
|
+
end # Reader
|
42
|
+
end # Prompt
|
43
|
+
end # TTY
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Prompt
|
5
|
+
class Reader
|
6
|
+
class Mode
|
7
|
+
# A class responsible for toggling raw mode.
|
8
|
+
class Raw
|
9
|
+
# Turn raw mode on
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
def on
|
13
|
+
%x{stty raw} if TTY::Platform.unix?
|
14
|
+
end
|
15
|
+
|
16
|
+
# Turn raw mode off
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
def off
|
20
|
+
%x{stty -raw} if TTY::Platform.unix?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Wrap code block inside raw mode
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
def raw(is_on=true, &block)
|
27
|
+
value = nil
|
28
|
+
on if is_on
|
29
|
+
value = block.call if block_given?
|
30
|
+
rescue NoMethodError, Interrupt => error
|
31
|
+
puts "#{error.class} #{error.message}"
|
32
|
+
puts error.backtrace
|
33
|
+
off
|
34
|
+
exit
|
35
|
+
ensure
|
36
|
+
off
|
37
|
+
value
|
38
|
+
end
|
39
|
+
end # Raw
|
40
|
+
end # Mode
|
41
|
+
end # Reader
|
42
|
+
end # Prompt
|
43
|
+
end # TTY
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Prompt
|
5
|
+
# Accumulates errors
|
6
|
+
class Result
|
7
|
+
attr_reader :question, :value, :errors
|
8
|
+
|
9
|
+
def initialize(question, value, errors = [])
|
10
|
+
@question = question
|
11
|
+
@value = value
|
12
|
+
@errors = errors
|
13
|
+
end
|
14
|
+
|
15
|
+
def with(condition = nil, &block)
|
16
|
+
validator = (condition || block)
|
17
|
+
(new_value, validation_error) = validator.call(question, value)
|
18
|
+
accumulated_errors = errors + Array(validation_error)
|
19
|
+
|
20
|
+
if accumulated_errors.empty?
|
21
|
+
Success.new(question, new_value)
|
22
|
+
else
|
23
|
+
Failure.new(question, new_value, accumulated_errors)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def success?
|
28
|
+
is_a?(Success)
|
29
|
+
end
|
30
|
+
|
31
|
+
def failure?
|
32
|
+
is_a?(Failure)
|
33
|
+
end
|
34
|
+
|
35
|
+
class Success < Result
|
36
|
+
end
|
37
|
+
|
38
|
+
class Failure < Result
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end # Prompt
|
42
|
+
end # TTY
|
data/lib/tty/prompt/statement.rb
CHANGED
@@ -5,10 +5,6 @@ module TTY
|
|
5
5
|
class Prompt
|
6
6
|
# A class representing a statement output to prompt.
|
7
7
|
class Statement
|
8
|
-
# @api private
|
9
|
-
attr_reader :prompt
|
10
|
-
private :prompt
|
11
|
-
|
12
8
|
# Flag to display newline
|
13
9
|
#
|
14
10
|
# @api public
|
@@ -32,11 +28,10 @@ module TTY
|
|
32
28
|
# change the message display to color
|
33
29
|
#
|
34
30
|
# @api public
|
35
|
-
def initialize(prompt
|
36
|
-
@prompt
|
37
|
-
@
|
38
|
-
@
|
39
|
-
@color = options.fetch(:color, false)
|
31
|
+
def initialize(prompt, options = {})
|
32
|
+
@prompt = prompt
|
33
|
+
@newline = options.fetch(:newline) { true }
|
34
|
+
@color = options.fetch(:color) { false }
|
40
35
|
end
|
41
36
|
|
42
37
|
# Output the message to the prompt
|
@@ -45,14 +40,14 @@ module TTY
|
|
45
40
|
# the message to be printed to stdout
|
46
41
|
#
|
47
42
|
# @api public
|
48
|
-
def
|
49
|
-
message = @
|
43
|
+
def call(message)
|
44
|
+
message = @prompt.decorate(message, *color) if color
|
50
45
|
|
51
46
|
if newline && /( |\t)(\e\[\d+(;\d+)*m)?\Z/ !~ message
|
52
|
-
prompt.
|
47
|
+
@prompt.puts message
|
53
48
|
else
|
54
|
-
prompt.
|
55
|
-
prompt.
|
49
|
+
@prompt.print message
|
50
|
+
@prompt.flush
|
56
51
|
end
|
57
52
|
end
|
58
53
|
end # Statement
|
@@ -3,9 +3,11 @@
|
|
3
3
|
require 'tty/prompt/distance'
|
4
4
|
|
5
5
|
module TTY
|
6
|
-
# A class responsible for
|
6
|
+
# A class responsible for terminal prompt interactions.
|
7
7
|
class Prompt
|
8
|
-
# A class representing a suggestion
|
8
|
+
# A class representing a suggestion out of possible choices
|
9
|
+
#
|
10
|
+
# @api public
|
9
11
|
class Suggestion
|
10
12
|
DEFAULT_INDENT = 8
|
11
13
|
|
data/lib/tty/prompt/test.rb
CHANGED
@@ -8,8 +8,9 @@ module TTY
|
|
8
8
|
def initialize(options = {})
|
9
9
|
@input = StringIO.new
|
10
10
|
@output = StringIO.new
|
11
|
-
|
12
|
-
super(
|
11
|
+
options.merge!({input: @input, output: @output})
|
12
|
+
super(options)
|
13
|
+
@pastel = Pastel.new(enabled: true)
|
13
14
|
end
|
14
15
|
end # TestPrompt
|
15
16
|
end # TTY
|
data/lib/tty/prompt/utils.rb
CHANGED
data/lib/tty/prompt/version.rb
CHANGED
data/spec/unit/ask_spec.rb
CHANGED
@@ -1,74 +1,57 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
3
|
RSpec.describe TTY::Prompt, '#ask' do
|
6
|
-
|
7
4
|
subject(:prompt) { TTY::TestPrompt.new }
|
8
5
|
|
9
|
-
it '
|
10
|
-
prompt.ask(
|
11
|
-
expect(prompt.output.string).to
|
6
|
+
it 'asks question' do
|
7
|
+
prompt.ask('What is your name?')
|
8
|
+
expect(prompt.output.string).to eq([
|
9
|
+
"What is your name? ",
|
10
|
+
"\e[1000D\e[K\e[1A",
|
11
|
+
"\e[1000D\e[K",
|
12
|
+
"What is your name? \n"
|
13
|
+
].join)
|
12
14
|
end
|
13
15
|
|
14
|
-
it '
|
16
|
+
it 'asks an empty question ' do
|
15
17
|
prompt.ask('')
|
16
18
|
expect(prompt.output.string).to eql('')
|
17
19
|
end
|
18
20
|
|
19
|
-
it '
|
21
|
+
it 'asks an empty question and returns nil if EOF is sent to stdin' do
|
20
22
|
prompt.input << nil
|
21
23
|
prompt.input.rewind
|
22
|
-
|
23
|
-
expect(
|
24
|
+
answer = prompt.ask('')
|
25
|
+
expect(answer).to eql(nil)
|
24
26
|
expect(prompt.output.string).to eq('')
|
25
27
|
end
|
26
28
|
|
27
|
-
it "asks a question with a prefix
|
28
|
-
prompt = TTY::TestPrompt.new(prefix:
|
29
|
-
prompt.input <<
|
29
|
+
it "asks a question with a prefix [?]" do
|
30
|
+
prompt = TTY::TestPrompt.new(prefix: "[?] ")
|
31
|
+
prompt.input << "\r"
|
30
32
|
prompt.input.rewind
|
31
|
-
|
32
|
-
expect(
|
33
|
-
expect(prompt.output.string).to
|
33
|
+
answer = prompt.ask 'Are you Polish?'
|
34
|
+
expect(answer).to eq(nil)
|
35
|
+
expect(prompt.output.string).to eq([
|
36
|
+
"[?] Are you Polish? ",
|
37
|
+
"\e[1000D\e[K\e[1A",
|
38
|
+
"\e[1000D\e[K",
|
39
|
+
"[?] Are you Polish? \n"
|
40
|
+
].join)
|
34
41
|
end
|
35
42
|
|
36
43
|
it 'asks a question with block' do
|
37
44
|
prompt.input << ''
|
38
45
|
prompt.input.rewind
|
39
|
-
|
46
|
+
answer = prompt.ask "What is your name?" do |q|
|
40
47
|
q.default 'Piotr'
|
41
48
|
end
|
42
|
-
expect(
|
43
|
-
expect(prompt.output.string).to eq(
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
prompt.input.rewind
|
50
|
-
expect(prompt.yes?("Are you a human?")).to eq(true)
|
51
|
-
expect(prompt.output.string).to eq('Are you a human?')
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'disagrees' do
|
55
|
-
prompt.input << 'no'
|
56
|
-
prompt.input.rewind
|
57
|
-
expect(prompt.yes?("Are you a human?")).to eq(false)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
context 'no?' do
|
62
|
-
it 'agrees' do
|
63
|
-
prompt.input << 'no'
|
64
|
-
prompt.input.rewind
|
65
|
-
expect(prompt.no?("Are you a human?")).to eq(true)
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'disagrees' do
|
69
|
-
prompt.input << 'yes'
|
70
|
-
prompt.input.rewind
|
71
|
-
expect(prompt.no?("Are you a human?")).to eq(false)
|
72
|
-
end
|
49
|
+
expect(answer).to eq('Piotr')
|
50
|
+
expect(prompt.output.string).to eq([
|
51
|
+
"What is your name? \e[90m(Piotr)\e[0m ",
|
52
|
+
"\e[1000D\e[K\e[1A",
|
53
|
+
"\e[1000D\e[K",
|
54
|
+
"What is your name? \e[32mPiotr\e[0m\n"
|
55
|
+
].join)
|
73
56
|
end
|
74
57
|
end
|