tty-prompt 0.18.0 → 0.22.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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +92 -0
  3. data/README.md +549 -248
  4. data/lib/tty-prompt.rb +1 -2
  5. data/lib/tty/prompt.rb +187 -143
  6. data/lib/tty/prompt/answers_collector.rb +5 -5
  7. data/lib/tty/prompt/{enum_paginator.rb → block_paginator.rb} +20 -19
  8. data/lib/tty/prompt/choice.rb +5 -7
  9. data/lib/tty/prompt/choices.rb +29 -11
  10. data/lib/tty/prompt/confirm_question.rb +38 -16
  11. data/lib/tty/prompt/const.rb +17 -0
  12. data/lib/tty/prompt/converter_dsl.rb +6 -7
  13. data/lib/tty/prompt/converter_registry.rb +31 -26
  14. data/lib/tty/prompt/converters.rb +139 -32
  15. data/lib/tty/prompt/enum_list.rb +57 -27
  16. data/lib/tty/prompt/errors.rb +31 -0
  17. data/lib/tty/prompt/evaluator.rb +1 -1
  18. data/lib/tty/prompt/expander.rb +39 -13
  19. data/lib/tty/prompt/keypress.rb +31 -36
  20. data/lib/tty/prompt/list.rb +175 -65
  21. data/lib/tty/prompt/mask_question.rb +4 -5
  22. data/lib/tty/prompt/multi_list.rb +124 -33
  23. data/lib/tty/prompt/multiline.rb +7 -6
  24. data/lib/tty/prompt/paginator.rb +38 -26
  25. data/lib/tty/prompt/question.rb +83 -34
  26. data/lib/tty/prompt/question/checks.rb +18 -0
  27. data/lib/tty/prompt/question/validation.rb +3 -3
  28. data/lib/tty/prompt/selected_choices.rb +76 -0
  29. data/lib/tty/prompt/slider.rb +83 -16
  30. data/lib/tty/prompt/statement.rb +3 -3
  31. data/lib/tty/prompt/suggestion.rb +6 -6
  32. data/lib/tty/prompt/symbols.rb +58 -34
  33. data/lib/tty/prompt/test.rb +36 -0
  34. data/lib/tty/prompt/timer.rb +75 -0
  35. data/lib/tty/prompt/utils.rb +1 -3
  36. data/lib/tty/prompt/version.rb +1 -1
  37. metadata +29 -227
  38. data/Rakefile +0 -8
  39. data/examples/ask.rb +0 -7
  40. data/examples/ask_valid.rb +0 -12
  41. data/examples/collect.rb +0 -21
  42. data/examples/echo.rb +0 -11
  43. data/examples/enum_select.rb +0 -7
  44. data/examples/enum_select_disabled.rb +0 -16
  45. data/examples/enum_select_paged.rb +0 -9
  46. data/examples/enum_select_wrapped.rb +0 -15
  47. data/examples/expand.rb +0 -29
  48. data/examples/in.rb +0 -9
  49. data/examples/inputs.rb +0 -10
  50. data/examples/key_events.rb +0 -15
  51. data/examples/keypress.rb +0 -9
  52. data/examples/mask.rb +0 -13
  53. data/examples/multi_select.rb +0 -8
  54. data/examples/multi_select_disabled.rb +0 -17
  55. data/examples/multi_select_paged.rb +0 -9
  56. data/examples/multi_select_wrapped.rb +0 -15
  57. data/examples/multiline.rb +0 -9
  58. data/examples/pause.rb +0 -9
  59. data/examples/select.rb +0 -20
  60. data/examples/select_disabled.rb +0 -18
  61. data/examples/select_enum.rb +0 -8
  62. data/examples/select_filtered.rb +0 -11
  63. data/examples/select_paginated.rb +0 -11
  64. data/examples/select_wrapped.rb +0 -15
  65. data/examples/slider.rb +0 -6
  66. data/examples/validation.rb +0 -9
  67. data/examples/yes_no.rb +0 -7
  68. data/lib/tty/prompt/messages.rb +0 -49
  69. data/lib/tty/prompt/timeout.rb +0 -78
  70. data/lib/tty/test_prompt.rb +0 -20
  71. data/spec/spec_helper.rb +0 -45
  72. data/spec/unit/ask_spec.rb +0 -132
  73. data/spec/unit/choice/eql_spec.rb +0 -22
  74. data/spec/unit/choice/from_spec.rb +0 -96
  75. data/spec/unit/choices/add_spec.rb +0 -12
  76. data/spec/unit/choices/each_spec.rb +0 -13
  77. data/spec/unit/choices/find_by_spec.rb +0 -10
  78. data/spec/unit/choices/new_spec.rb +0 -10
  79. data/spec/unit/choices/pluck_spec.rb +0 -9
  80. data/spec/unit/collect_spec.rb +0 -96
  81. data/spec/unit/converters/convert_bool_spec.rb +0 -58
  82. data/spec/unit/converters/convert_char_spec.rb +0 -11
  83. data/spec/unit/converters/convert_custom_spec.rb +0 -14
  84. data/spec/unit/converters/convert_date_spec.rb +0 -34
  85. data/spec/unit/converters/convert_file_spec.rb +0 -18
  86. data/spec/unit/converters/convert_number_spec.rb +0 -39
  87. data/spec/unit/converters/convert_path_spec.rb +0 -15
  88. data/spec/unit/converters/convert_range_spec.rb +0 -22
  89. data/spec/unit/converters/convert_regex_spec.rb +0 -12
  90. data/spec/unit/converters/convert_string_spec.rb +0 -21
  91. data/spec/unit/converters/on_error_spec.rb +0 -9
  92. data/spec/unit/distance/distance_spec.rb +0 -73
  93. data/spec/unit/enum_paginator_spec.rb +0 -75
  94. data/spec/unit/enum_select_spec.rb +0 -446
  95. data/spec/unit/error_spec.rb +0 -20
  96. data/spec/unit/evaluator_spec.rb +0 -67
  97. data/spec/unit/expand_spec.rb +0 -198
  98. data/spec/unit/keypress_spec.rb +0 -72
  99. data/spec/unit/mask_spec.rb +0 -132
  100. data/spec/unit/multi_select_spec.rb +0 -495
  101. data/spec/unit/multiline_spec.rb +0 -77
  102. data/spec/unit/new_spec.rb +0 -20
  103. data/spec/unit/ok_spec.rb +0 -10
  104. data/spec/unit/paginator_spec.rb +0 -73
  105. data/spec/unit/question/checks_spec.rb +0 -97
  106. data/spec/unit/question/default_spec.rb +0 -31
  107. data/spec/unit/question/echo_spec.rb +0 -38
  108. data/spec/unit/question/in_spec.rb +0 -115
  109. data/spec/unit/question/initialize_spec.rb +0 -12
  110. data/spec/unit/question/modifier/apply_to_spec.rb +0 -24
  111. data/spec/unit/question/modifier/letter_case_spec.rb +0 -41
  112. data/spec/unit/question/modifier/whitespace_spec.rb +0 -51
  113. data/spec/unit/question/modify_spec.rb +0 -41
  114. data/spec/unit/question/required_spec.rb +0 -92
  115. data/spec/unit/question/validate_spec.rb +0 -115
  116. data/spec/unit/question/validation/call_spec.rb +0 -31
  117. data/spec/unit/question/validation/coerce_spec.rb +0 -30
  118. data/spec/unit/result_spec.rb +0 -40
  119. data/spec/unit/say_spec.rb +0 -67
  120. data/spec/unit/select_spec.rb +0 -643
  121. data/spec/unit/slider_spec.rb +0 -100
  122. data/spec/unit/statement/initialize_spec.rb +0 -15
  123. data/spec/unit/subscribe_spec.rb +0 -22
  124. data/spec/unit/suggest_spec.rb +0 -28
  125. data/spec/unit/warn_spec.rb +0 -21
  126. data/spec/unit/yes_no_spec.rb +0 -251
  127. data/tasks/console.rake +0 -11
  128. data/tasks/coverage.rake +0 -11
  129. data/tasks/spec.rake +0 -29
  130. data/tty-prompt.gemspec +0 -33
@@ -1,78 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'timers'
4
-
5
- module TTY
6
- class Prompt
7
- class Timeout
8
- # A class responsible for measuring interval
9
- #
10
- # @api private
11
- def initialize(options = {})
12
- @interval_handler = options.fetch(:interval_handler) { proc {} }
13
- @lock = Mutex.new
14
- @running = true
15
- @timers = Timers::Group.new
16
- end
17
-
18
- def self.timeout(time, interval, &block)
19
- (@scheduler ||= new).timeout(time, interval, &block)
20
- end
21
-
22
- # Evalute block and time it
23
- #
24
- # @param [Float] time
25
- # the time by which to stop
26
- # @param [Float] interval
27
- # the interval time for each tick
28
- #
29
- # @api public
30
- def timeout(time, interval, &job)
31
- input_thread = Thread.new { job.() }
32
- timing_thread = measure_intervals(time, interval, input_thread)
33
- [input_thread, timing_thread].each(&:join)
34
- end
35
-
36
- # Cancel this timeout measurement
37
- #
38
- # @api public
39
- def cancel
40
- return unless @running
41
- @running = false
42
- end
43
-
44
- # Measure intervals and terminate input
45
- #
46
- # @api private
47
- def measure_intervals(time, interval, input_thread)
48
- Thread.new do
49
- Thread.current.abort_on_exception = true
50
- start = Time.now
51
-
52
- interval_timer = @timers.every(interval) do
53
- runtime = Time.now - start
54
- delta = time - runtime
55
- if delta.round >= 0
56
- @interval_handler.(delta.round)
57
- end
58
- end
59
-
60
- while @running
61
- @lock.synchronize {
62
- @timers.wait
63
- runtime = Time.now - start
64
- delta = time - runtime
65
-
66
- if delta <= 0.0
67
- @running = false
68
- end
69
- }
70
- end
71
-
72
- input_thread.terminate
73
- interval_timer.cancel
74
- end
75
- end
76
- end # Scheduler
77
- end # Prompt
78
- end # TTY
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'prompt'
4
-
5
- module TTY
6
- # Used for initializing test cases
7
- class TestPrompt < Prompt
8
- def initialize(options = {})
9
- @input = StringIO.new
10
- @output = StringIO.new
11
- options.merge!({
12
- input: @input,
13
- output: @output,
14
- env: { "TTY_TEST" => true },
15
- enable_color: options.fetch(:enable_color) { true }
16
- })
17
- super(options)
18
- end
19
- end # TestPrompt
20
- end # TTY
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if ENV['COVERAGE'] || ENV['TRAVIS']
4
- require 'simplecov'
5
- require 'coveralls'
6
-
7
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
8
- SimpleCov::Formatter::HTMLFormatter,
9
- Coveralls::SimpleCov::Formatter
10
- ]
11
-
12
- SimpleCov.start do
13
- command_name 'spec'
14
- add_filter 'spec'
15
- end
16
- end
17
-
18
- require 'tty-prompt'
19
-
20
- RSpec.configure do |config|
21
- config.expect_with :rspec do |expectations|
22
- expectations.include_chain_clauses_in_custom_matcher_descriptions = true
23
- end
24
-
25
- config.mock_with :rspec do |mocks|
26
- mocks.verify_partial_doubles = true
27
- end
28
-
29
- # Limits the available syntax to the non-monkey patched syntax that is recommended.
30
- config.disable_monkey_patching!
31
-
32
- # This setting enables warnings. It's recommended, but in some cases may
33
- # be too noisy due to issues in dependencies.
34
- config.warnings = true
35
-
36
- if config.files_to_run.one?
37
- config.default_formatter = 'doc'
38
- end
39
-
40
- config.profile_examples = 2
41
-
42
- config.order = :random
43
-
44
- Kernel.srand config.seed
45
- end
@@ -1,132 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Prompt, '#ask' do
4
-
5
- subject(:prompt) { TTY::TestPrompt.new }
6
-
7
- it 'asks question' do
8
- prompt.ask('What is your name?')
9
- expect(prompt.output.string).to eq([
10
- "What is your name? ",
11
- "\e[1A\e[2K\e[1G",
12
- "What is your name? \n"
13
- ].join)
14
- end
15
-
16
- it 'asks an empty question ' do
17
- prompt.ask('')
18
- expect(prompt.output.string).to eql('')
19
- end
20
-
21
- it 'asks an empty question and returns nil if EOF is sent to stdin' do
22
- prompt.input << nil
23
- prompt.input.rewind
24
- answer = prompt.ask('')
25
- expect(answer).to eql(nil)
26
- expect(prompt.output.string).to eq('')
27
- end
28
-
29
- it "asks a question with a prefix [?]" do
30
- prompt = TTY::TestPrompt.new(prefix: "[?] ")
31
- prompt.input << "\r"
32
- prompt.input.rewind
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[2K\e[1G[?] Are you Polish? \n",
38
- "\e[1A\e[2K\e[1G",
39
- "[?] Are you Polish? \n"
40
- ].join)
41
- end
42
-
43
- it 'asks a question with block' do
44
- prompt.input << ''
45
- prompt.input.rewind
46
- answer = prompt.ask "What is your name?" do |q|
47
- q.default 'Piotr'
48
- 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[1A\e[2K\e[1G",
53
- "What is your name? \e[32mPiotr\e[0m\n"
54
- ].join)
55
- end
56
-
57
- it "changes question color" do
58
- prompt.input << ''
59
- prompt.input.rewind
60
- options = {default: 'Piotr', help_color: :red, active_color: :cyan}
61
- answer = prompt.ask("What is your name?", options)
62
- expect(answer).to eq('Piotr')
63
- expect(prompt.output.string).to eq([
64
- "What is your name? \e[31m(Piotr)\e[0m ",
65
- "\e[1A\e[2K\e[1G",
66
- "What is your name? \e[36mPiotr\e[0m\n"
67
- ].join)
68
- end
69
-
70
- it "permits empty default parameter" do
71
- prompt.input << "\r"
72
- prompt.input.rewind
73
-
74
- answer = prompt.ask("What is your name?", default: '')
75
- expect(answer).to eq('')
76
- expect(prompt.output.string).to eq([
77
- "What is your name? ",
78
- "\e[2K\e[1GWhat is your name? \n",
79
- "\e[1A\e[2K\e[1G",
80
- "What is your name? \n"
81
- ].join)
82
- end
83
-
84
- it "permits nil default parameter" do
85
- prompt.input << "\r"
86
- prompt.input.rewind
87
-
88
- answer = prompt.ask("What is your name?", default: nil)
89
- expect(answer).to eq(nil)
90
- expect(prompt.output.string).to eq([
91
- "What is your name? ",
92
- "\e[2K\e[1GWhat is your name? \n",
93
- "\e[1A\e[2K\e[1G",
94
- "What is your name? \n"
95
- ].join)
96
- end
97
-
98
- it "overwrites global settings" do
99
- global_settings = {prefix: "[?] ", active_color: :cyan, help_color: :red}
100
- prompt = TTY::TestPrompt.new(global_settings)
101
-
102
- prompt.input << "Piotr\r"
103
- prompt.input.rewind
104
- prompt.ask('What is your name?')
105
-
106
- prompt.input << "Piotr\r"
107
- prompt.input.rewind
108
- local_settings = {prefix: ':-) ', active_color: :blue, help_color: :magenta}
109
- prompt.ask('What is your name?', local_settings)
110
-
111
- expect(prompt.output.string).to eq([
112
- "[?] What is your name? ",
113
- "\e[2K\e[1G[?] What is your name? P",
114
- "\e[2K\e[1G[?] What is your name? Pi",
115
- "\e[2K\e[1G[?] What is your name? Pio",
116
- "\e[2K\e[1G[?] What is your name? Piot",
117
- "\e[2K\e[1G[?] What is your name? Piotr",
118
- "\e[2K\e[1G[?] What is your name? Piotr\n",
119
- "\e[1A\e[2K\e[1G",
120
- "[?] What is your name? \e[36mPiotr\e[0m\n",
121
- ":-) What is your name? ",
122
- "\e[2K\e[1G:-) What is your name? P",
123
- "\e[2K\e[1G:-) What is your name? Pi",
124
- "\e[2K\e[1G:-) What is your name? Pio",
125
- "\e[2K\e[1G:-) What is your name? Piot",
126
- "\e[2K\e[1G:-) What is your name? Piotr",
127
- "\e[2K\e[1G:-) What is your name? Piotr\n",
128
- "\e[1A\e[2K\e[1G",
129
- ":-) What is your name? \e[34mPiotr\e[0m\n"
130
- ].join)
131
- end
132
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Prompt::Choice, '#==' do
4
- it "is true with the same name and value attributes" do
5
- expect(described_class.new(:large, 1)).
6
- to eq(described_class.new(:large, 1))
7
- end
8
-
9
- it "is false with different name attribute" do
10
- expect(described_class.new(:large, 1)).
11
- not_to eq(described_class.new(:medium, 1))
12
- end
13
-
14
- it "is false with different value attribute" do
15
- expect(described_class.new(:large, 1)).
16
- not_to eq(described_class.new(:large, 2))
17
- end
18
-
19
- it "is false with non-choice object" do
20
- expect(described_class.new(:large, 1)).not_to eq(:other)
21
- end
22
- end
@@ -1,96 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Prompt::Choice, '#from' do
4
- it "skips Choice instance" do
5
- choice = described_class.new(:large, 1)
6
-
7
- expect(described_class.from(choice)).to eq(choice)
8
- end
9
-
10
- it "creates choice from string" do
11
- expected_choice = described_class.new('large', 'large')
12
- choice = described_class.from('large')
13
-
14
- expect(choice).to eq(expected_choice)
15
- expect(choice.name).to eq('large')
16
- expect(choice.value).to eq('large')
17
- end
18
-
19
- it "creates choice from array with one element" do
20
- expected_choice = described_class.new('large', 'large')
21
- choice = described_class.from([:large])
22
-
23
- expect(choice).to eq(expected_choice)
24
- expect(choice.name).to eq('large')
25
- expect(choice.value).to eq('large')
26
- end
27
-
28
- it "creates choice from array with more than one element" do
29
- expected_choice = described_class.new('large', 1)
30
- choice = described_class.from([:large, 1])
31
-
32
- expect(choice).to eq(expected_choice)
33
- expect(choice.name).to eq('large')
34
- expect(choice.value).to eq(1)
35
- end
36
-
37
- it "creates choice from hash value" do
38
- expected_choice = described_class.new('large', 1)
39
- choice = described_class.from({large: 1})
40
-
41
- expect(choice).to eq(expected_choice)
42
- expect(choice.name).to eq('large')
43
- expect(choice.value).to eq(1)
44
- end
45
-
46
- it "creats choice from array with key value pair" do
47
- expected_choice = described_class.new('large', 1)
48
- choice = described_class.from([{'large' => 1}])
49
-
50
- expect(choice).to eq(expected_choice)
51
- expect(choice.name).to eq('large')
52
- expect(choice.value).to eq(1)
53
- end
54
-
55
- it "creats choice from array with hash elements" do
56
- expected_choice = described_class.new('large', 1)
57
- choice = described_class.from([{name: 'large', value: 1}])
58
-
59
- expect(choice).to eq(expected_choice)
60
- expect(choice.name).to eq('large')
61
- expect(choice.value).to eq(1)
62
- end
63
-
64
- it "creats choice from array with hash elements without value" do
65
- expected_choice = described_class.new('large', 'large')
66
- choice = described_class.from([{name: 'large'}])
67
-
68
- expect(choice).to eq(expected_choice)
69
- expect(choice.name).to eq('large')
70
- expect(choice.value).to eq('large')
71
- end
72
-
73
- it "creates choice from hash with key property" do
74
- default = {key: 'h', name: 'Help', value: :help}
75
- expected_choice = described_class.new('Help', :help, key: 'h')
76
- choice = described_class.from(default)
77
-
78
- expect(choice).to eq(expected_choice)
79
- expect(choice.name).to eq('Help')
80
- expect(choice.value).to eq(:help)
81
- expect(choice.disabled?).to eq(false)
82
- end
83
-
84
- it "creates disabled choice" do
85
- expected_choice = described_class.new('Disabled', :none, disabled: true)
86
- choice = described_class.from({
87
- name: 'Disabled',
88
- value: :none,
89
- disabled: 'unavailable'})
90
-
91
- expect(choice).to eq(expected_choice)
92
- expect(choice.name).to eq('Disabled')
93
- expect(choice.value).to eq(:none)
94
- expect(choice.disabled?).to eq(true)
95
- end
96
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Prompt::Choices, '#<<' do
4
- it "adds choice to collection" do
5
- choices = described_class.new
6
- expect(choices).to be_empty
7
- choice = TTY::Prompt::Choice.from([:label, 1])
8
- choices << [:label, 1]
9
- expect(choices.size).to eq(1)
10
- expect(choices.to_ary).to eq([choice])
11
- end
12
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Prompt::Choices, '.each' do
4
- it "iterates over collection" do
5
- choices = described_class[:large, :medium, :small]
6
- actual = []
7
- choices.each do |choice|
8
- actual << choice.name
9
- end
10
- expect(actual).to eq([:large, :medium, :small])
11
- expect(choices.each).to be_kind_of(Enumerator)
12
- end
13
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Prompt::Choices, '#find_by' do
4
- it "finds a matching choice by key name" do
5
- collection = [{name: 'large'},{name: 'medium'},{name: 'small'}]
6
- choice = TTY::Prompt::Choice.from(name: 'small')
7
- choices = described_class[*collection]
8
- expect(choices.find_by(:name, 'small')).to eq(choice)
9
- end
10
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Prompt::Choices, '.new' do
4
- it "creates choices collection" do
5
- choice_1 = TTY::Prompt::Choice.from(:label1)
6
- choice_2 = TTY::Prompt::Choice.from(:label2)
7
- collection = described_class[:label1, :label2]
8
- expect(collection.choices).to eq([choice_1, choice_2])
9
- end
10
- end