tty-prompt 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +24 -0
  6. data/Gemfile +16 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +199 -0
  9. data/Rakefile +8 -0
  10. data/lib/tty-prompt.rb +15 -0
  11. data/lib/tty/prompt.rb +206 -0
  12. data/lib/tty/prompt/distance.rb +49 -0
  13. data/lib/tty/prompt/error.rb +26 -0
  14. data/lib/tty/prompt/history.rb +16 -0
  15. data/lib/tty/prompt/mode.rb +64 -0
  16. data/lib/tty/prompt/mode/echo.rb +40 -0
  17. data/lib/tty/prompt/mode/raw.rb +40 -0
  18. data/lib/tty/prompt/question.rb +338 -0
  19. data/lib/tty/prompt/question/modifier.rb +93 -0
  20. data/lib/tty/prompt/question/validation.rb +92 -0
  21. data/lib/tty/prompt/reader.rb +113 -0
  22. data/lib/tty/prompt/response.rb +252 -0
  23. data/lib/tty/prompt/response_delegation.rb +41 -0
  24. data/lib/tty/prompt/statement.rb +60 -0
  25. data/lib/tty/prompt/suggestion.rb +113 -0
  26. data/lib/tty/prompt/utils.rb +16 -0
  27. data/lib/tty/prompt/version.rb +7 -0
  28. data/spec/spec_helper.rb +45 -0
  29. data/spec/unit/ask_spec.rb +77 -0
  30. data/spec/unit/distance/distance_spec.rb +75 -0
  31. data/spec/unit/error_spec.rb +30 -0
  32. data/spec/unit/question/argument_spec.rb +30 -0
  33. data/spec/unit/question/character_spec.rb +24 -0
  34. data/spec/unit/question/default_spec.rb +25 -0
  35. data/spec/unit/question/in_spec.rb +23 -0
  36. data/spec/unit/question/initialize_spec.rb +24 -0
  37. data/spec/unit/question/modifier/apply_to_spec.rb +31 -0
  38. data/spec/unit/question/modifier/letter_case_spec.rb +22 -0
  39. data/spec/unit/question/modifier/whitespace_spec.rb +33 -0
  40. data/spec/unit/question/modify_spec.rb +44 -0
  41. data/spec/unit/question/valid_spec.rb +46 -0
  42. data/spec/unit/question/validate_spec.rb +30 -0
  43. data/spec/unit/question/validation/coerce_spec.rb +24 -0
  44. data/spec/unit/question/validation/valid_value_spec.rb +22 -0
  45. data/spec/unit/reader/getc_spec.rb +42 -0
  46. data/spec/unit/response/read_bool_spec.rb +47 -0
  47. data/spec/unit/response/read_char_spec.rb +16 -0
  48. data/spec/unit/response/read_date_spec.rb +20 -0
  49. data/spec/unit/response/read_email_spec.rb +42 -0
  50. data/spec/unit/response/read_multiple_spec.rb +23 -0
  51. data/spec/unit/response/read_number_spec.rb +28 -0
  52. data/spec/unit/response/read_range_spec.rb +26 -0
  53. data/spec/unit/response/read_spec.rb +68 -0
  54. data/spec/unit/response/read_string_spec.rb +19 -0
  55. data/spec/unit/say_spec.rb +66 -0
  56. data/spec/unit/statement/initialize_spec.rb +19 -0
  57. data/spec/unit/suggest_spec.rb +33 -0
  58. data/spec/unit/warn_spec.rb +30 -0
  59. data/tasks/console.rake +10 -0
  60. data/tasks/coverage.rake +11 -0
  61. data/tasks/spec.rake +29 -0
  62. data/tty-prompt.gemspec +26 -0
  63. metadata +194 -0
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::Prompt::Question, '#read_char' do
6
+ let(:input) { StringIO.new }
7
+ let(:output) { StringIO.new }
8
+ let(:prompt) { TTY::Prompt.new(input, output) }
9
+
10
+ it 'reads single character' do
11
+ input << "abcde"
12
+ input.rewind
13
+ q = prompt.ask("What is your favourite letter?")
14
+ expect(q.read_char).to eql "a"
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::Prompt::Question, '#read_date' do
6
+ let(:input) { StringIO.new }
7
+ let(:output) { StringIO.new }
8
+ let(:prompt) { TTY::Prompt.new(input, output) }
9
+
10
+ it 'reads date' do
11
+ input << "20th April 1887"
12
+ input.rewind
13
+ q = prompt.ask("When were your born?")
14
+ answer = q.read_date
15
+ expect(answer).to be_kind_of Date
16
+ expect(answer.day).to eql 20
17
+ expect(answer.month).to eql 4
18
+ expect(answer.year).to eql 1887
19
+ end
20
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::Prompt::Question, '#read_email' do
6
+ let(:input) { StringIO.new }
7
+ let(:output) { StringIO.new }
8
+ let(:prompt) { TTY::Prompt.new(input, output) }
9
+
10
+ context 'with valid email' do
11
+ it 'reads empty' do
12
+ input << ""
13
+ input.rewind
14
+ q = prompt.ask("What is your email?")
15
+ expect(q.read_email).to eql(nil)
16
+ end
17
+
18
+ it 'reads valid email' do
19
+ input << "piotr@example.com"
20
+ input.rewind
21
+ q = prompt.ask("What is your email?")
22
+ expect(q.read_email).to eql "piotr@example.com"
23
+ end
24
+ end
25
+
26
+ context 'with invalid email' do
27
+ it 'fails to read invalid email' do
28
+ input << "this will@neverwork"
29
+ input.rewind
30
+ q = prompt.ask("What is your email?")
31
+ expect { q.read_email }.to raise_error(TTY::Prompt::InvalidArgument)
32
+ end
33
+
34
+ it 'reads invalid and asks again' do
35
+ input << "this will@neverwork\nthis.will@example.com"
36
+ input.rewind
37
+ q = prompt.ask("What is your email?").on_error(:retry)
38
+ expect(q.read_email).to eql "this.will@example.com"
39
+ expect(output.string).to eql "What is your email?\nWhat is your email?\n"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::Prompt::Question, '#read_multiple' do
6
+ let(:input) { StringIO.new }
7
+ let(:output) { StringIO.new }
8
+ let(:prompt) { TTY::Prompt.new(input, output) }
9
+
10
+ it 'reads multiple lines' do
11
+ input << "First line\nSecond line\nThird line"
12
+ input.rewind
13
+ q = prompt.ask("Provide description?")
14
+ expect(q.read_multiple).to eq("First line\nSecond line\nThird line")
15
+ end
16
+
17
+ it 'terminates on empty lines' do
18
+ input << "First line\n\nSecond line"
19
+ input.rewind
20
+ q = prompt.ask("Provide description?")
21
+ expect(q.read_multiple).to eq("First line\n")
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::Prompt::Question, '#read_numbers' do
6
+ let(:input) { StringIO.new }
7
+ let(:output) { StringIO.new }
8
+ let(:prompt) { TTY::Prompt.new(input, output) }
9
+
10
+ it 'reads integer' do
11
+ input << 35
12
+ input.rewind
13
+ q = prompt.ask("What temperature?")
14
+ answer = q.read_int
15
+ expect(answer).to be_kind_of Integer
16
+ expect(answer).to eql 35
17
+ end
18
+
19
+ it 'reads float' do
20
+ number = 6.666
21
+ input << number
22
+ input.rewind
23
+ q = prompt.ask("How tall are you?")
24
+ answer = q.read_float
25
+ expect(answer).to be_kind_of Float
26
+ expect(answer).to eql number
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::Prompt::Question, '#read_range' do
6
+ let(:input) { StringIO.new }
7
+ let(:output) { StringIO.new }
8
+ let(:prompt) { TTY::Prompt.new(input, output) }
9
+
10
+ it 'converts with valid range' do
11
+ input << "20-30"
12
+ input.rewind
13
+ response = prompt.ask("Which age group?").read_range
14
+
15
+ expect(response).to be_kind_of Range
16
+ expect(response).to eql (20..30)
17
+ end
18
+
19
+ it "fails to convert to range" do
20
+ input << "abcd"
21
+ input.rewind
22
+ expect {
23
+ prompt.ask("Which age group?").read_range
24
+ }.to raise_error(Necromancer::ConversionTypeError)
25
+ end
26
+ end
@@ -0,0 +1,68 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::Prompt::Question, '#read' do
6
+ let(:input) { StringIO.new }
7
+ let(:output) { StringIO.new }
8
+ let(:prompt) { TTY::Prompt.new(input, output) }
9
+
10
+ context 'with no mask' do
11
+ it 'asks with echo on' do
12
+ input << "password"
13
+ input.rewind
14
+ q = prompt.ask("What is your password: ").echo(true)
15
+ expect(q.read).to eql("password")
16
+ expect(output.string).to eql('What is your password: ')
17
+ expect(q.mask?).to eq(false)
18
+ end
19
+
20
+ it 'asks with echo off' do
21
+ input << "password"
22
+ input.rewind
23
+ q = prompt.ask("What is your password: ").echo(false)
24
+ expect(q.read).to eql("password")
25
+ expect(output.string).to eql('What is your password: ')
26
+ expect(q.mask?).to eq(false)
27
+ end
28
+ end
29
+
30
+ context 'with mask' do
31
+ it 'masks output with character' do
32
+ input << "password\n"
33
+ input.rewind
34
+ q = prompt.ask("What is your password: ").mask('*')
35
+ expect(q.read).to eql("password")
36
+ expect(output.string).to eql('What is your password: ********')
37
+ expect(q.mask?).to eq(true)
38
+ end
39
+
40
+ it 'ignores mask if echo is off' do
41
+ input << "password"
42
+ input.rewind
43
+ q = prompt.ask("What is your password: ").echo(false).mask('*')
44
+ expect(q.read).to eql("password")
45
+ expect(output.string).to eql('What is your password: ')
46
+ expect(q.mask?).to eq(true)
47
+ end
48
+ end
49
+
50
+ context 'with mask and echo as options' do
51
+ it 'asks with options' do
52
+ input << "password"
53
+ input.rewind
54
+ q = prompt.ask("What is your password: ", echo: false, mask: '*')
55
+ expect(q.read).to eq("password")
56
+ end
57
+
58
+ it 'asks with block' do
59
+ input << "password"
60
+ input.rewind
61
+ q = prompt.ask "What is your password: " do
62
+ echo false
63
+ mask '*'
64
+ end
65
+ expect(q.read).to eq("password")
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::Prompt::Question, '#initialize' do
6
+ let(:input) { StringIO.new }
7
+ let(:output) { StringIO.new }
8
+ let(:prompt) { TTY::Prompt.new(input, output) }
9
+
10
+ it 'reads string' do
11
+ name = "Piotr"
12
+ input << name
13
+ input.rewind
14
+ q = prompt.ask("What is your name?")
15
+ answer = q.read_string
16
+ expect(answer).to be_kind_of String
17
+ expect(answer).to eql name
18
+ end
19
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::Prompt, '#say' do
6
+ let(:input) { StringIO.new }
7
+ let(:output) { StringIO.new }
8
+ let(:color) { Pastel.new(enabled: true) }
9
+
10
+ subject(:prompt) { described_class.new(input, output) }
11
+
12
+ before { allow(Pastel).to receive(:new).and_return(color) }
13
+
14
+ after { output.rewind }
15
+
16
+ it 'prints an empty message' do
17
+ prompt.say ""
18
+ expect(output.string).to eql ""
19
+ end
20
+
21
+ context 'with new line' do
22
+ it 'prints a message with newline' do
23
+ prompt.say "Hell yeah!\n"
24
+ expect(output.string).to eql "Hell yeah!\n"
25
+ end
26
+
27
+ it 'prints a message with implicit newline' do
28
+ prompt.say "Hell yeah!\n"
29
+ expect(output.string).to eql "Hell yeah!\n"
30
+ end
31
+
32
+ it 'prints a message with newline within text' do
33
+ prompt.say "Hell\n yeah!"
34
+ expect(output.string).to eql "Hell\n yeah!\n"
35
+ end
36
+
37
+ it 'prints a message with newline within text and blank space' do
38
+ prompt.say "Hell\n yeah! "
39
+ expect(output.string).to eql "Hell\n yeah! "
40
+ end
41
+
42
+ it 'prints a message without newline' do
43
+ prompt.say "Hell yeah!", newline: false
44
+ expect(output.string).to eql "Hell yeah!"
45
+ end
46
+ end
47
+
48
+ context 'with tab or space' do
49
+ it 'prints ' do
50
+ prompt.say "Hell yeah!\t"
51
+ expect(output.string).to eql "Hell yeah!\t"
52
+ end
53
+ end
54
+
55
+ context 'with color' do
56
+ it 'prints message with ansi color' do
57
+ prompt.say "Hell yeah!", color: :green
58
+ expect(output.string).to eql "\e[32mHell yeah!\e[0m\n"
59
+ end
60
+
61
+ it 'prints message with ansi color without newline' do
62
+ prompt.say "Hell yeah! ", color: :green
63
+ expect(output.string).to eql "\e[32mHell yeah! \e[0m"
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::Prompt::Statement, '#new' do
6
+ let(:input) { StringIO.new }
7
+ let(:output) { StringIO.new }
8
+ let(:shell) { TTY::Prompt.new(input, output) }
9
+
10
+ it "forces newline after the prompt message" do
11
+ statement = described_class.new
12
+ expect(statement.newline).to eq(true)
13
+ end
14
+
15
+ it "displays prompt message in color" do
16
+ statement = described_class.new
17
+ expect(statement.color).to eq(false)
18
+ end
19
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::Prompt, '#suggest' do
6
+ let(:input) { StringIO.new }
7
+ let(:output) { StringIO.new }
8
+ let(:possible) { %w(status stage stash commit branch blame) }
9
+
10
+ subject(:prompt) { described_class.new(input, output) }
11
+
12
+ after { output.rewind }
13
+
14
+ it 'suggests few matches' do
15
+ prompt.suggest('sta', possible)
16
+ expect(output.string).to eql("Did you mean one of these?\n stage\n stash\n")
17
+ end
18
+
19
+ it 'suggests a single match for one character' do
20
+ prompt.suggest('b', possible)
21
+ expect(output.string).to eql("Did you mean this?\n blame\n")
22
+ end
23
+
24
+ it 'suggests a single match for two characters' do
25
+ prompt.suggest('co', possible)
26
+ expect(output.string).to eql("Did you mean this?\n commit\n")
27
+ end
28
+
29
+ it 'suggests with different text and indentation' do
30
+ prompt.suggest('b', possible, indent: 4, single_text: 'Perhaps you meant?')
31
+ expect(output.string).to eql("Perhaps you meant?\n blame\n")
32
+ end
33
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::Prompt, '#warn' do
6
+ let(:input) { StringIO.new }
7
+ let(:output) { StringIO.new }
8
+ let(:color) { Pastel.new(enabled: true) }
9
+
10
+ subject(:prompt) { TTY::Prompt.new(input, output) }
11
+
12
+ before { allow(Pastel).to receive(:new).and_return(color) }
13
+
14
+ after { output.rewind }
15
+
16
+ it 'displays one message' do
17
+ prompt.warn "Careful young apprentice!"
18
+ expect(output.string).to eql "\e[33mCareful young apprentice!\e[0m\n"
19
+ end
20
+
21
+ it 'displays many messages' do
22
+ prompt.warn "Careful there!", "It's dangerous!"
23
+ expect(output.string).to eql "\e[33mCareful there!\e[0m\n\e[33mIt's dangerous!\e[0m\n"
24
+ end
25
+
26
+ it 'displays message with option' do
27
+ prompt.warn "Careful young apprentice!", newline: false
28
+ expect(output.string).to eql "\e[33mCareful young apprentice!\e[0m"
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ desc 'Load gem inside irb console'
4
+ task :console do
5
+ require 'irb'
6
+ require 'irb/completion'
7
+ require File.join(__FILE__, '../../lib/tty-prompt')
8
+ ARGV.clear
9
+ IRB.start
10
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ desc 'Measure code coverage'
4
+ task :coverage do
5
+ begin
6
+ original, ENV['COVERAGE'] = ENV['COVERAGE'], 'true'
7
+ Rake::Task['spec'].invoke
8
+ ensure
9
+ ENV['COVERAGE'] = original
10
+ end
11
+ end
data/tasks/spec.rake ADDED
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+
6
+ desc 'Run all specs'
7
+ RSpec::Core::RakeTask.new(:spec) do |task|
8
+ task.pattern = 'spec/{unit,integration}{,/*/**}/*_spec.rb'
9
+ end
10
+
11
+ namespace :spec do
12
+ desc 'Run unit specs'
13
+ RSpec::Core::RakeTask.new(:unit) do |task|
14
+ task.pattern = 'spec/unit{,/*/**}/*_spec.rb'
15
+ end
16
+
17
+ desc 'Run integration specs'
18
+ RSpec::Core::RakeTask.new(:integration) do |task|
19
+ task.pattern = 'spec/integration{,/*/**}/*_spec.rb'
20
+ end
21
+ end
22
+
23
+ rescue LoadError
24
+ %w[spec spec:unit spec:integration].each do |name|
25
+ task name do
26
+ $stderr.puts "In order to run #{name}, do `gem install rspec`"
27
+ end
28
+ end
29
+ end