tty-prompt 0.15.0 → 0.16.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 (50) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +17 -0
  4. data/Gemfile +2 -3
  5. data/README.md +140 -12
  6. data/appveyor.yml +1 -0
  7. data/examples/enum_select_disabled.rb +16 -0
  8. data/examples/{enum_paged.rb → enum_select_paged.rb} +0 -0
  9. data/examples/enum_select_wrapped.rb +15 -0
  10. data/examples/multi_select_disabled.rb +17 -0
  11. data/examples/multi_select_wrapped.rb +15 -0
  12. data/examples/select.rb +3 -1
  13. data/examples/select_disabled.rb +18 -0
  14. data/examples/{enum.rb → select_enum.rb} +0 -0
  15. data/examples/select_filtered.rb +3 -1
  16. data/examples/select_paginated.rb +3 -1
  17. data/examples/select_wrapped.rb +15 -0
  18. data/lib/tty/prompt.rb +1 -0
  19. data/lib/tty/prompt/answers_collector.rb +1 -0
  20. data/lib/tty/prompt/choice.rb +67 -25
  21. data/lib/tty/prompt/choices.rb +1 -0
  22. data/lib/tty/prompt/confirm_question.rb +3 -4
  23. data/lib/tty/prompt/converter_dsl.rb +1 -0
  24. data/lib/tty/prompt/converter_registry.rb +1 -0
  25. data/lib/tty/prompt/converters.rb +1 -0
  26. data/lib/tty/prompt/distance.rb +1 -0
  27. data/lib/tty/prompt/enum_list.rb +58 -17
  28. data/lib/tty/prompt/enum_paginator.rb +1 -0
  29. data/lib/tty/prompt/evaluator.rb +1 -0
  30. data/lib/tty/prompt/expander.rb +14 -14
  31. data/lib/tty/prompt/keypress.rb +1 -1
  32. data/lib/tty/prompt/list.rb +78 -39
  33. data/lib/tty/prompt/mask_question.rb +5 -4
  34. data/lib/tty/prompt/multi_list.rb +13 -3
  35. data/lib/tty/prompt/multiline.rb +6 -5
  36. data/lib/tty/prompt/paginator.rb +1 -0
  37. data/lib/tty/prompt/question.rb +10 -9
  38. data/lib/tty/prompt/question/checks.rb +1 -0
  39. data/lib/tty/prompt/question/modifier.rb +1 -0
  40. data/lib/tty/prompt/question/validation.rb +1 -0
  41. data/lib/tty/prompt/result.rb +1 -0
  42. data/lib/tty/prompt/slider.rb +3 -2
  43. data/lib/tty/prompt/statement.rb +1 -0
  44. data/lib/tty/prompt/suggestion.rb +4 -6
  45. data/lib/tty/prompt/symbols.rb +2 -1
  46. data/lib/tty/prompt/timeout.rb +16 -11
  47. data/lib/tty/prompt/utils.rb +1 -0
  48. data/lib/tty/prompt/version.rb +1 -1
  49. data/lib/tty/test_prompt.rb +1 -0
  50. metadata +11 -5
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'question'
4
5
  require_relative 'symbols'
@@ -52,18 +53,18 @@ module TTY
52
53
  #
53
54
  # @api private
54
55
  def render_question
55
- header = "#{@prefix}#{message} "
56
+ header = ["#{@prefix}#{message} "]
56
57
  if echo?
57
- masked = "#{@mask * "#{@input}".length}"
58
+ masked = @mask.to_s * @input.to_s.length
58
59
  if @done_masked && !@failure
59
60
  masked = @prompt.decorate(masked, @active_color)
60
61
  elsif @done_masked && @failure
61
62
  masked = @prompt.decorate(masked, @error_color)
62
63
  end
63
- header += masked
64
+ header << masked
64
65
  end
65
66
  header << "\n" if @done
66
- header
67
+ header.join
67
68
  end
68
69
 
69
70
  def render_error(errors)
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'list'
4
5
 
@@ -47,6 +48,10 @@ module TTY
47
48
  # At this stage, @choices matches all the visible choices.
48
49
  @selected = @choices.values_at(*@default.map { |d| d - 1 })
49
50
  @active = @default.last unless @selected.empty?
51
+ if choices[@active - 1] && choices[@active - 1].disabled?
52
+ raise ConfigurationError,
53
+ "active choice '#{choices[@active - 1]}' matches disabled item"
54
+ end
50
55
  end
51
56
 
52
57
  # Generate selected items names
@@ -90,14 +95,18 @@ module TTY
90
95
  #
91
96
  # @api private
92
97
  def render_menu
93
- output = ''
98
+ output = []
99
+
94
100
  @paginator.paginate(choices, @active, @per_page) do |choice, index|
95
101
  num = enumerate? ? (index + 1).to_s + @enum + ' ' : ''
96
102
  indicator = (index + 1 == @active) ? @marker : ' '
97
103
  indicator += ' '
98
- message = if @selected.include?(choice)
104
+ message = if @selected.include?(choice) && !choice.disabled?
99
105
  selected = @prompt.decorate(symbols[:radio_on], @active_color)
100
106
  selected + ' ' + num + choice.name
107
+ elsif choice.disabled?
108
+ @prompt.decorate(symbols[:cross], :red) +
109
+ ' ' + num + choice.name + ' ' + choice.disabled.to_s
101
110
  else
102
111
  symbols[:radio_off] + ' ' + num + choice.name
103
112
  end
@@ -105,7 +114,8 @@ module TTY
105
114
  newline = (index == max_index) ? '' : "\n"
106
115
  output << indicator + message + newline
107
116
  end
108
- output
117
+
118
+ output.join
109
119
  end
110
120
  end # MultiList
111
121
  end # Prompt
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'question'
4
5
  require_relative 'symbols'
@@ -40,17 +41,17 @@ module TTY
40
41
  alias keyenter keyreturn
41
42
 
42
43
  def render_question
43
- header = "#{@prefix}#{message} "
44
+ header = ["#{@prefix}#{message} "]
44
45
  if !echo?
45
46
  header
46
47
  elsif @done
47
- header += @prompt.decorate("#{@input}", @active_color)
48
+ header << @prompt.decorate("#{@input}", @active_color)
48
49
  elsif @first_render
49
- header += @prompt.decorate(help, @help_color)
50
+ header << @prompt.decorate(help, @help_color)
50
51
  @first_render = false
51
52
  end
52
- header += "\n"
53
- header
53
+ header << "\n"
54
+ header.join
54
55
  end
55
56
 
56
57
  def process_input(question)
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class Prompt
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'converters'
4
5
  require_relative 'evaluator'
@@ -20,7 +21,7 @@ module TTY
20
21
  def to_s
21
22
  "undefined"
22
23
  end
23
- alias inspect to_s
24
+ alias_method :inspect, :to_s
24
25
  end
25
26
 
26
27
  # Store question message
@@ -46,7 +47,7 @@ module TTY
46
47
  @convert = options.fetch(:convert) { UndefinedSetting }
47
48
  @active_color = options.fetch(:active_color) { @prompt.active_color }
48
49
  @help_color = options.fetch(:help_color) { @prompt.help_color }
49
- @error_color = options.fetch(:error_color) { :red }
50
+ @error_color = options.fetch(:error_color) { :red }
50
51
  @messages = Utils.deep_copy(options.fetch(:messages) { { } })
51
52
  @done = false
52
53
  @input = nil
@@ -129,16 +130,16 @@ module TTY
129
130
  #
130
131
  # @api private
131
132
  def render_question
132
- header = "#{@prefix}#{message} "
133
+ header = ["#{@prefix}#{message} "]
133
134
  if !echo?
134
135
  header
135
136
  elsif @done
136
- header += @prompt.decorate("#{@input}", @active_color)
137
+ header << @prompt.decorate(@input.to_s, @active_color)
137
138
  elsif default? && !Utils.blank?(@default)
138
- header += @prompt.decorate("(#{default})", @help_color) + ' '
139
+ header << @prompt.decorate("(#{default})", @help_color) + ' '
139
140
  end
140
141
  header << "\n" if @done
141
- header
142
+ header.join
142
143
  end
143
144
 
144
145
  # Decide how to handle input from user
@@ -180,7 +181,7 @@ module TTY
180
181
  #
181
182
  # @api private
182
183
  def refresh(lines, lines_to_clear)
183
- output = ''
184
+ output = []
184
185
  if @done
185
186
  if @errors.count.zero?
186
187
  output << @prompt.cursor.up(lines)
@@ -191,7 +192,7 @@ module TTY
191
192
  else
192
193
  output << @prompt.cursor.up(lines)
193
194
  end
194
- output + @prompt.clear_lines(lines_to_clear)
195
+ output.join + @prompt.clear_lines(lines_to_clear)
195
196
  end
196
197
 
197
198
  # Convert value to expected type
@@ -321,7 +322,7 @@ module TTY
321
322
 
322
323
  # @api public
323
324
  def to_s
324
- "#{message}"
325
+ message.to_s
325
326
  end
326
327
 
327
328
  # String representation of this question
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class Prompt
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class Prompt
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class Prompt
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class Prompt
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'symbols'
4
5
 
@@ -160,7 +161,7 @@ module TTY
160
161
  #
161
162
  # @api private
162
163
  def render_question
163
- header = "#{@prefix}#{@question} "
164
+ header = ["#{@prefix}#{@question} "]
164
165
  if @done
165
166
  header << @prompt.decorate(answer.to_s, @active_color)
166
167
  header << "\n"
@@ -171,7 +172,7 @@ module TTY
171
172
  header << "\n" + @prompt.decorate(HELP, @help_color)
172
173
  @first_render = false
173
174
  end
174
- header
175
+ header.join
175
176
  end
176
177
 
177
178
  # Render slider representation
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  # A class responsible for shell prompt interactions.
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'distance'
4
5
 
@@ -97,16 +98,13 @@ module TTY
97
98
 
98
99
  # @api private
99
100
  def build_single_suggestion
100
- suggestion = ''
101
- suggestion << single_text + "\n"
102
- suggestion << (' ' * indent + @suggestions.first)
101
+ single_text + "\n" + (' ' * indent) + @suggestions.first
103
102
  end
104
103
 
105
104
  # @api private
106
105
  def build_multiple_suggestions
107
- suggestion = ''
108
- suggestion << plural_text + "\n"
109
- suggestion << @suggestions.map do |sugest|
106
+ plural_text + "\n" +
107
+ @suggestions.map do |sugest|
110
108
  ' ' * indent + sugest
111
109
  end.join("\n")
112
110
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class Prompt
@@ -28,7 +29,7 @@ module TTY
28
29
 
29
30
  WIN_KEYS = {
30
31
  tick: '√',
31
- cross: '×',
32
+ cross: 'x',
32
33
  star: '*',
33
34
  square: '[█]',
34
35
  square_empty: '[ ]',
@@ -1,16 +1,15 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'timers'
4
5
 
5
6
  module TTY
6
7
  class Prompt
7
8
  class Timeout
8
- Error = Class.new(RuntimeError)
9
-
10
- TIMEOUT_HANDLER = proc { |t| t.raise Error, 'timeout expired' }
11
-
9
+ # A class responsible for measuring interval
10
+ #
11
+ # @api private
12
12
  def initialize(options = {})
13
- @timeout_handler = options.fetch(:timeout_handler) { TIMEOUT_HANDLER }
14
13
  @interval_handler = options.fetch(:interval_handler) { proc {} }
15
14
  @lock = Mutex.new
16
15
  @running = true
@@ -30,17 +29,23 @@ module TTY
30
29
  #
31
30
  # @api public
32
31
  def timeout(time, interval, &job)
33
- @runner = async_run(time, interval)
34
- job.()
35
- @runner.join
32
+ input_thread = Thread.new { job.() }
33
+ timing_thread = measure_intervals(time, interval, input_thread)
34
+ [input_thread, timing_thread].each(&:join)
36
35
  end
37
36
 
37
+ # Cancel this timeout measurement
38
+ #
39
+ # @api public
38
40
  def cancel
39
41
  return unless @running
40
42
  @running = false
41
43
  end
42
44
 
43
- def async_run(time, interval)
45
+ # Measure intervals and terminate input
46
+ #
47
+ # @api private
48
+ def measure_intervals(time, interval, input_thread)
44
49
  Thread.new do
45
50
  Thread.current.abort_on_exception = true
46
51
  start = Time.now
@@ -60,12 +65,12 @@ module TTY
60
65
  delta = time - runtime
61
66
 
62
67
  if delta <= 0.0
63
- @timeout_handler.(Thread.current)
64
- break
68
+ @running = false
65
69
  end
66
70
  }
67
71
  end
68
72
 
73
+ input_thread.terminate
69
74
  interval_timer.cancel
70
75
  end
71
76
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  module Utils
@@ -2,6 +2,6 @@
2
2
 
3
3
  module TTY
4
4
  class Prompt
5
- VERSION = '0.15.0'.freeze
5
+ VERSION = '0.16.0'.freeze
6
6
  end # Prompt
7
7
  end # TTY
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'prompt'
4
5
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tty-prompt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-08 00:00:00.000000000 Z
11
+ date: 2018-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: necromancer
@@ -151,9 +151,10 @@ files:
151
151
  - examples/ask_valid.rb
152
152
  - examples/collect.rb
153
153
  - examples/echo.rb
154
- - examples/enum.rb
155
- - examples/enum_paged.rb
156
154
  - examples/enum_select.rb
155
+ - examples/enum_select_disabled.rb
156
+ - examples/enum_select_paged.rb
157
+ - examples/enum_select_wrapped.rb
157
158
  - examples/expand.rb
158
159
  - examples/in.rb
159
160
  - examples/inputs.rb
@@ -161,12 +162,17 @@ files:
161
162
  - examples/keypress.rb
162
163
  - examples/mask.rb
163
164
  - examples/multi_select.rb
165
+ - examples/multi_select_disabled.rb
164
166
  - examples/multi_select_paged.rb
167
+ - examples/multi_select_wrapped.rb
165
168
  - examples/multiline.rb
166
169
  - examples/pause.rb
167
170
  - examples/select.rb
171
+ - examples/select_disabled.rb
172
+ - examples/select_enum.rb
168
173
  - examples/select_filtered.rb
169
174
  - examples/select_paginated.rb
175
+ - examples/select_wrapped.rb
170
176
  - examples/slider.rb
171
177
  - examples/validation.rb
172
178
  - examples/yes_no.rb
@@ -227,7 +233,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
227
233
  version: '0'
228
234
  requirements: []
229
235
  rubyforge_project:
230
- rubygems_version: 2.5.1
236
+ rubygems_version: 2.7.3
231
237
  signing_key:
232
238
  specification_version: 4
233
239
  summary: A beautiful and powerful interactive command line prompt.