tty-prompt 0.1.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 (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