tty-prompt 0.21.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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