tty-prompt 0.21.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.
@@ -1,2 +1 @@
1
- require_relative 'tty/prompt'
2
- require_relative 'tty/test_prompt'
1
+ require_relative "tty/prompt"
@@ -1,51 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
4
- require 'pastel'
5
- require 'tty-cursor'
6
- require 'tty-reader'
7
- require 'tty-screen'
8
-
9
- require_relative 'prompt/answers_collector'
10
- require_relative 'prompt/confirm_question'
11
- require_relative 'prompt/expander'
12
- require_relative 'prompt/enum_list'
13
- require_relative 'prompt/keypress'
14
- require_relative 'prompt/list'
15
- require_relative 'prompt/multi_list'
16
- require_relative 'prompt/multiline'
17
- require_relative 'prompt/mask_question'
18
- require_relative 'prompt/question'
19
- require_relative 'prompt/slider'
20
- require_relative 'prompt/statement'
21
- require_relative 'prompt/suggestion'
22
- require_relative 'prompt/symbols'
23
- require_relative 'prompt/utils'
24
- require_relative 'prompt/version'
3
+ require "forwardable"
4
+ require "pastel"
5
+ require "tty-cursor"
6
+ require "tty-reader"
7
+ require "tty-screen"
8
+
9
+ require_relative "prompt/answers_collector"
10
+ require_relative "prompt/confirm_question"
11
+ require_relative "prompt/errors"
12
+ require_relative "prompt/expander"
13
+ require_relative "prompt/enum_list"
14
+ require_relative "prompt/keypress"
15
+ require_relative "prompt/list"
16
+ require_relative "prompt/multi_list"
17
+ require_relative "prompt/multiline"
18
+ require_relative "prompt/mask_question"
19
+ require_relative "prompt/question"
20
+ require_relative "prompt/slider"
21
+ require_relative "prompt/statement"
22
+ require_relative "prompt/suggestion"
23
+ require_relative "prompt/symbols"
24
+ require_relative "prompt/utils"
25
+ require_relative "prompt/version"
25
26
 
26
27
  module TTY
27
28
  # A main entry for asking prompt questions.
28
29
  class Prompt
29
30
  extend Forwardable
30
31
 
31
- # Raised when wrong parameter is used to configure prompt
32
- ConfigurationError = Class.new(StandardError)
33
-
34
- # Raised when type conversion cannot be performed
35
- ConversionError = Class.new(StandardError)
36
-
37
- # Raised when the passed in validation argument is of wrong type
38
- ValidationCoercion = Class.new(TypeError)
39
-
40
- # Raised when the required argument is not supplied
41
- ArgumentRequired = Class.new(ArgumentError)
42
-
43
- # Raised when the argument validation fails
44
- ArgumentValidation = Class.new(ArgumentError)
45
-
46
- # Raised when the argument is not expected
47
- InvalidArgument = Class.new(ArgumentError)
48
-
49
32
  # @api private
50
33
  attr_reader :input
51
34
 
@@ -71,22 +54,27 @@ module TTY
71
54
  # @api private
72
55
  attr_reader :active_color, :help_color, :error_color, :enabled_color
73
56
 
57
+ # Quiet mode
58
+ #
59
+ # @api private
60
+ attr_reader :quiet
61
+
74
62
  # The collection of display symbols
75
63
  #
76
64
  # @example
77
- # prompt = TTY::Prompt.new(symbols: {marker: '>'})
65
+ # prompt = TTY::Prompt.new(symbols: {marker: ">"})
78
66
  #
79
67
  # @return [Hash]
80
68
  #
81
69
  # @api private
82
70
  attr_reader :symbols
83
71
 
84
- def_delegators :@pastel, :decorate, :strip
72
+ def_delegators :@pastel, :strip
85
73
 
86
74
  def_delegators :@cursor, :clear_lines, :clear_line,
87
75
  :show, :hide
88
76
 
89
- def_delegators :@reader, :read_char, :read_keypress, # :read_line,
77
+ def_delegators :@reader, :read_char, :read_keypress, :read_line,
90
78
  :read_multiline, :on, :subscribe, :unsubscribe, :trigger,
91
79
  :count_screen_lines
92
80
 
@@ -94,69 +82,90 @@ module TTY
94
82
 
95
83
  def self.messages
96
84
  {
97
- range?: 'Value %{value} must be within the range %{in}',
98
- valid?: 'Your answer is invalid (must match %{valid})',
99
- required?: 'Value must be provided'
85
+ range?: "Value %{value} must be within the range %{in}",
86
+ valid?: "Your answer is invalid (must match %{valid})",
87
+ required?: "Value must be provided",
88
+ convert?: "Cannot convert `%{value}` to '%{type}' type"
100
89
  }
101
90
  end
102
91
 
103
- # This fixes Forwardable module keyword arguments warning
104
- def read_line(message, **options)
105
- @reader.read_line(message, **options)
106
- end
107
-
108
92
  # Initialize a Prompt
109
93
  #
110
- # @param [Hash] options
111
- # @option options [IO] :input
94
+ # @param [IO] :input
112
95
  # the input stream
113
- # @option options [IO] :output
96
+ # @param [IO] :output
114
97
  # the output stream
115
- # @option options [Hash] :env
98
+ # @param [Hash] :env
116
99
  # the environment variables
117
- # @option options [String] :prefix
100
+ # @param [Hash] :symbols
101
+ # the symbols displayed in prompts such as :marker, :cross
102
+ # @param options [Boolean] :quiet
103
+ # enable quiet mode, don't re-echo the question
104
+ # @param [String] :prefix
118
105
  # the prompt prefix, by default empty
119
- # @option options [Boolean] :enable_color
106
+ # @param [Symbol] :interrupt
107
+ # handling of Ctrl+C key out of :signal, :exit, :noop
108
+ # @param [Boolean] :track_history
109
+ # disable line history tracking, true by default
110
+ # @param [Boolean] :enable_color
120
111
  # enable color support, true by default
121
- # @option options [String] :active_color
112
+ # @param [String,Proc] :active_color
122
113
  # the color used for selected option
123
- # @option options [String] :help_color
114
+ # @param [String,Proc] :help_color
124
115
  # the color used for help text
125
- # @option options [String] :error_color
116
+ # @param [String] :error_color
126
117
  # the color used for displaying error messages
127
- # @option options [Symbol] :interrupt
128
- # handling of Ctrl+C key out of :signal, :exit, :noop
129
- # @option options [Boolean] :track_history
130
- # disable line history tracking, true by default
131
- # @option options [Hash] :symbols
132
- # the symbols displayed in prompts such as :marker, :cross
133
118
  #
134
119
  # @api public
135
- def initialize(*args)
136
- options = Utils.extract_options!(args)
137
- @input = options.fetch(:input) { $stdin }
138
- @output = options.fetch(:output) { $stdout }
139
- @env = options.fetch(:env) { ENV }
140
- @prefix = options.fetch(:prefix) { '' }
141
- @enabled_color = options[:enable_color]
142
- @active_color = options.fetch(:active_color) { :green }
143
- @help_color = options.fetch(:help_color) { :bright_black }
144
- @error_color = options.fetch(:error_color) { :red }
145
- @interrupt = options.fetch(:interrupt) { :error }
146
- @track_history = options.fetch(:track_history) { true }
147
- @symbols = Symbols.symbols.merge(options.fetch(:symbols, {}))
120
+ def initialize(input: $stdin, output: $stdout, env: ENV, symbols: {},
121
+ prefix: "", interrupt: :error, track_history: true,
122
+ quiet: false, enable_color: nil, active_color: :green,
123
+ help_color: :bright_black, error_color: :red)
124
+ @input = input
125
+ @output = output
126
+ @env = env
127
+ @prefix = prefix
128
+ @enabled_color = enable_color
129
+ @active_color = active_color
130
+ @help_color = help_color
131
+ @error_color = error_color
132
+ @interrupt = interrupt
133
+ @track_history = track_history
134
+ @symbols = Symbols.symbols.merge(symbols)
135
+ @quiet = quiet
148
136
 
149
137
  @cursor = TTY::Cursor
150
- @pastel = Pastel.new(@enabled_color.nil? ? {} : { enabled: @enabled_color })
138
+ @pastel = enabled_color.nil? ? Pastel.new : Pastel.new(enabled: enabled_color)
151
139
  @reader = TTY::Reader.new(
152
- input: @input,
153
- output: @output,
154
- interrupt: @interrupt,
155
- track_history: @track_history,
156
- env: @env
140
+ input: input,
141
+ output: output,
142
+ interrupt: interrupt,
143
+ track_history: track_history,
144
+ env: env
157
145
  )
158
146
  end
159
147
 
148
+ # Decorate a string with colors
149
+ #
150
+ # @param [String] :string
151
+ # the string to color
152
+ # @param [Array<Proc|Symbol>] :colors
153
+ # collection of color symbols or callable object
154
+ #
155
+ # @api public
156
+ def decorate(string, *colors)
157
+ if Utils.blank?(string) || @enabled_color == false || colors.empty?
158
+ return string
159
+ end
160
+
161
+ coloring = colors.first
162
+ if coloring.respond_to?(:call)
163
+ coloring.call(string)
164
+ else
165
+ @pastel.decorate(string, *colors)
166
+ end
167
+ end
168
+
160
169
  # Invoke a question type of prompt
161
170
  #
162
171
  # @example
@@ -189,7 +198,7 @@ module TTY
189
198
  # @return [TTY::Prompt::Question]
190
199
  #
191
200
  # @api public
192
- def ask(message = '', **options, &block)
201
+ def ask(message = "", **options, &block)
193
202
  invoke_question(Question, message, **options, &block)
194
203
  end
195
204
 
@@ -198,19 +207,19 @@ module TTY
198
207
  # @see #ask
199
208
  #
200
209
  # @api public
201
- def keypress(message = '', **options, &block)
210
+ def keypress(message = "", **options, &block)
202
211
  invoke_question(Keypress, message, **options, &block)
203
212
  end
204
213
 
205
214
  # Ask a question with a multiline answer
206
215
  #
207
216
  # @example
208
- # prompt.multiline('Description?')
217
+ # prompt.multiline("Description?")
209
218
  #
210
219
  # @return [Array[String]]
211
220
  #
212
221
  # @api public
213
- def multiline(message = '', **options, &block)
222
+ def multiline(message = "", **options, &block)
214
223
  invoke_question(Multiline, message, **options, &block)
215
224
  end
216
225
 
@@ -226,9 +235,7 @@ module TTY
226
235
  # @api public
227
236
  def invoke_select(object, question, *args, &block)
228
237
  options = Utils.extract_options!(args)
229
- choices = if block
230
- []
231
- elsif args.empty?
238
+ choices = if args.empty? && !block
232
239
  possible = options.dup
233
240
  options = {}
234
241
  possible
@@ -251,7 +258,7 @@ module TTY
251
258
  # @return [TTY::Prompt::MaskQuestion]
252
259
  #
253
260
  # @api public
254
- def mask(message = '', **options, &block)
261
+ def mask(message = "", **options, &block)
255
262
  invoke_question(MaskQuestion, message, **options, &block)
256
263
  end
257
264
 
@@ -320,42 +327,36 @@ module TTY
320
327
  end
321
328
 
322
329
  # A shortcut method to ask the user positive question and return
323
- # true for 'yes' reply, false for 'no'.
330
+ # true for "yes" reply, false for "no".
324
331
  #
325
332
  # @example
326
333
  # prompt = TTY::Prompt.new
327
- # prompt.yes?('Are you human?')
334
+ # prompt.yes?("Are you human?")
328
335
  # # => Are you human? (Y/n)
329
336
  #
330
337
  # @return [Boolean]
331
338
  #
332
339
  # @api public
333
- def yes?(message, *args, &block)
334
- defaults = { default: true }
335
- options = Utils.extract_options!(args)
336
- options.merge!(defaults.reject { |k, _| options.key?(k) })
337
-
338
- question = ConfirmQuestion.new(self, **options)
340
+ def yes?(message, **options, &block)
341
+ opts = { default: true }.merge(options)
342
+ question = ConfirmQuestion.new(self, **opts)
339
343
  question.call(message, &block)
340
344
  end
341
345
 
342
346
  # A shortcut method to ask the user negative question and return
343
- # true for 'no' reply.
347
+ # true for "no" reply.
344
348
  #
345
349
  # @example
346
350
  # prompt = TTY::Prompt.new
347
- # prompt.no?('Are you alien?') # => true
351
+ # prompt.no?("Are you alien?") # => true
348
352
  # # => Are you human? (y/N)
349
353
  #
350
354
  # @return [Boolean]
351
355
  #
352
356
  # @api public
353
- def no?(message, *args, &block)
354
- defaults = { default: false }
355
- options = Utils.extract_options!(args)
356
- options.merge!(defaults.reject { |k, _| options.key?(k) })
357
-
358
- question = ConfirmQuestion.new(self, **options)
357
+ def no?(message, **options, &block)
358
+ opts = { default: false }.merge(options)
359
+ question = ConfirmQuestion.new(self, **opts)
359
360
  !question.call(message, &block)
360
361
  end
361
362
 
@@ -364,15 +365,15 @@ module TTY
364
365
  # @example
365
366
  # prompt = TTY::Prompt.new
366
367
  # choices = [{
367
- # key: 'Y',
368
- # name: 'Overwrite',
368
+ # key: "Y",
369
+ # name: "Overwrite",
369
370
  # value: :yes
370
371
  # }, {
371
- # key: 'n',
372
- # name: 'Skip',
372
+ # key: "n",
373
+ # name: "Skip",
373
374
  # value: :no
374
375
  # }]
375
- # prompt.expand('Overwirte Gemfile?', choices)
376
+ # prompt.expand("Overwirte Gemfile?", choices)
376
377
  #
377
378
  # @return [Object]
378
379
  # the user specified value
@@ -386,7 +387,7 @@ module TTY
386
387
  #
387
388
  # @example
388
389
  # prompt = TTY::Prompt.new
389
- # prompt.slider('What size?', min: 32, max: 54, step: 2)
390
+ # prompt.slider("What size?", min: 32, max: 54, step: 2)
390
391
  #
391
392
  # @param [String] question
392
393
  # the question to ask
@@ -394,8 +395,7 @@ module TTY
394
395
  # @return [String]
395
396
  #
396
397
  # @api public
397
- def slider(question, *args, &block)
398
- options = Utils.extract_options!(args)
398
+ def slider(question, **options, &block)
399
399
  slider = Slider.new(self, **options)
400
400
  slider.call(question, &block)
401
401
  end
@@ -411,11 +411,11 @@ module TTY
411
411
  # @return [String]
412
412
  #
413
413
  # @api public
414
- def say(message = '', options = {})
414
+ def say(message = "", **options)
415
415
  message = message.to_s
416
416
  return if message.empty?
417
417
 
418
- statement = Statement.new(self, options)
418
+ statement = Statement.new(self, **options)
419
419
  statement.call(message)
420
420
  end
421
421
 
@@ -430,9 +430,9 @@ module TTY
430
430
  # @return [Array] messages
431
431
  #
432
432
  # @api public
433
- def ok(*args)
434
- options = Utils.extract_options!(args)
435
- args.each { |message| say message, options.merge(color: :green) }
433
+ def ok(*args, **options)
434
+ opts = { color: :green }.merge(options)
435
+ args.each { |message| say(message, **opts) }
436
436
  end
437
437
 
438
438
  # Print statement(s) out in yellow color.
@@ -446,9 +446,9 @@ module TTY
446
446
  # @return [Array] messages
447
447
  #
448
448
  # @api public
449
- def warn(*args)
450
- options = Utils.extract_options!(args)
451
- args.each { |message| say message, options.merge(color: :yellow) }
449
+ def warn(*args, **options)
450
+ opts = { color: :yellow }.merge(options)
451
+ args.each { |message| say(message, **opts) }
452
452
  end
453
453
 
454
454
  # Print statement(s) out in red color.
@@ -462,9 +462,9 @@ module TTY
462
462
  # @return [Array] messages
463
463
  #
464
464
  # @api public
465
- def error(*args)
466
- options = Utils.extract_options!(args)
467
- args.each { |message| say message, options.merge(color: :red) }
465
+ def error(*args, **options)
466
+ opts = { color: :red }.merge(options)
467
+ args.each { |message| say(message, **opts) }
468
468
  end
469
469
 
470
470
  # Print debug information in terminal top right corner
@@ -481,7 +481,7 @@ module TTY
481
481
  longest = messages.max_by(&:length).size
482
482
  width = TTY::Screen.width - longest
483
483
  print cursor.save
484
- messages.reverse_each.with_index do |msg, i|
484
+ messages.reverse_each do |msg|
485
485
  print cursor.column(width) + cursor.up + cursor.clear_line_after
486
486
  print msg
487
487
  end
@@ -493,7 +493,7 @@ module TTY
493
493
  # matches to suggest an unambigous string
494
494
  #
495
495
  # @example
496
- # prompt.suggest('sta', ['status', 'stage', 'commit', 'branch'])
496
+ # prompt.suggest("sta", ["status", "stage", "commit", "branch"])
497
497
  # # => "status, stage"
498
498
  #
499
499
  # @param [String] message
@@ -520,7 +520,7 @@ module TTY
520
520
  #
521
521
  # @example
522
522
  # prompt.collect do
523
- # key(:name).ask('Name?')
523
+ # key(:name).ask("Name?")
524
524
  # end
525
525
  #
526
526
  # @return [Hash]
@@ -562,21 +562,24 @@ module TTY
562
562
  $stderr
563
563
  end
564
564
 
565
- # Inspect class name and public attributes
565
+ # Inspect this instance public attributes
566
+ #
566
567
  # @return [String]
567
568
  #
568
569
  # @api public
569
570
  def inspect
570
- attributes = {
571
- input: input,
572
- output: output,
573
- prefix: prefix,
574
- active_color: active_color,
575
- error_color: error_color,
576
- enabled_color: enabled_color,
577
- help_color: help_color
578
- }
579
- "#<#{self.class}: #{attributes.each { |name, val| "@#{name}=#{val}" }}"
571
+ attributes = [
572
+ :prefix,
573
+ :quiet,
574
+ :enabled_color,
575
+ :active_color,
576
+ :error_color,
577
+ :help_color,
578
+ :input,
579
+ :output,
580
+ ]
581
+ name = self.class.name
582
+ "#<#{name}#{attributes.map { |attr| " #{attr}=#{send(attr).inspect}" }.join}>"
580
583
  end
581
584
  end # Prompt
582
585
  end # TTY