prompts 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 00154e514a6a247088b47d92dd842df0628e33a7b9abf56f31d8a402f6174a16
4
- data.tar.gz: 65a2a890904ef991dd55e042081efad7762106d63f0039f3db566e9df262c800
3
+ metadata.gz: 40fa96bdb16258c1e9612971eeea11a1757e82cdbc68e6084000c26c96f2d86c
4
+ data.tar.gz: b7a7daee831ca3473aa043bfe0b1f94aff8f12a9aa8e2fa4acdb6a4a75fb0634
5
5
  SHA512:
6
- metadata.gz: 609f6e7c90beb7732f2b7853f25dbc44bce3e79ca5acdadd186a1455babea98e1364d2fff40dd380372974702f392774c8fd69b17e14241df8312ebc422e0e44
7
- data.tar.gz: aec86323aa427982ad95a58e9d4ab2fc73dfa0df358a4b8d88919fbf1c851321a25491a80b49b6618ed44af61ac24f819fc9f9fc55a9691858a41bc349587357
6
+ metadata.gz: fb8c9184e577f2e7b9f6c003407e028ee1613db35ee288c0ac914d5a8464f3c2135e22d4e280aab7233aa52418f60fed3ca6cf05c37d4c6907ef5937099f1445
7
+ data.tar.gz: cd5e4cb5512224d68c825b28f67398f5366d0d8a5a1026523c3cc431b44efea1ddef5fdb969832af49b2158dc1bb3e94b6a98cb8781c9422c324f02ebbce16c3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.1] - 2024-08-22
4
+
5
+ - Add `Form.submit` method
6
+ - All passing keyword arguments to `Form` prompt methods
7
+ - Return the key of the options hash when using `SelectPrompt`
8
+
3
9
  ## [0.2.0] - 2024-08-20
4
10
 
5
11
  - Add `TextPrompt`
data/README.md CHANGED
@@ -256,6 +256,32 @@ which generates a terminal screen like this (this representation doesn't show co
256
256
  <b>Press Enter ⏎ to continue...</b> |
257
257
  </pre>
258
258
 
259
+ ## Forms
260
+
261
+ Often, you will have multiple prompts that will be displayed in sequence to collect information before performing additional actions. You may use the `Prompts::Form` class to create a grouped set of prompts for the user to complete:
262
+
263
+ ```ruby
264
+ responses = Prompts::Form.submit do |form|
265
+ form.text(
266
+ label: "What is your name?",
267
+ required: true
268
+ )
269
+ form.select(
270
+ label: "What role should the user have?",
271
+ options: {
272
+ member: "Member",
273
+ contributor: "Contributor",
274
+ owner: "Owner",
275
+ }
276
+ )
277
+ form.confirm(
278
+ label: 'Do you accept the terms?'
279
+ )
280
+ end
281
+ ```
282
+
283
+ The `submit` method will return an array containing all of the responses from the form's prompts.
284
+
259
285
  ## Development
260
286
 
261
287
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/lib/prompts/box.rb CHANGED
@@ -4,10 +4,10 @@ module Prompts
4
4
  class Box
5
5
  include TextUtils
6
6
 
7
- SOLID_BORDER = { top_left: "┌", top_right: "┐", bottom_left: "└", bottom_right: "┘", horizontal: "─", vertical: "│" }.freeze
8
- DOUBLE_BORDER = { top_left: "╔", top_right: "╗", bottom_left: "╚", bottom_right: "╝", horizontal: "═", vertical: "║" }.freeze
9
- HEAVY_BORDER = { top_left: "┏", top_right: "┓", bottom_left: "┗", bottom_right: "┛", horizontal: "━", vertical: "┃" }.freeze
10
- ROUNDED_BORDER = { top_left: "╭", top_right: "╮", bottom_left: "╰", bottom_right: "╯", horizontal: "─", vertical: "│" }.freeze
7
+ SOLID_BORDER = {top_left: "┌", top_right: "┐", bottom_left: "└", bottom_right: "┘", horizontal: "─", vertical: "│"}.freeze
8
+ DOUBLE_BORDER = {top_left: "╔", top_right: "╗", bottom_left: "╚", bottom_right: "╝", horizontal: "═", vertical: "║"}.freeze
9
+ HEAVY_BORDER = {top_left: "┏", top_right: "┓", bottom_left: "┗", bottom_right: "┛", horizontal: "━", vertical: "┃"}.freeze
10
+ ROUNDED_BORDER = {top_left: "╭", top_right: "╮", bottom_left: "╰", bottom_right: "╯", horizontal: "─", vertical: "│"}.freeze
11
11
 
12
12
  def initialize(width: MAX_WIDTH, padded: false, border_color: nil, border_style: :rounded)
13
13
  @width = width
@@ -15,11 +15,11 @@ module Prompts
15
15
  @border_color = border_color
16
16
  @line_padding = SPACE * 1
17
17
  @border_parts = case border_style
18
- when :solid then SOLID_BORDER
19
- when :double then DOUBLE_BORDER
20
- when :heavy then HEAVY_BORDER
21
- else ROUNDED_BORDER
22
- end
18
+ when :solid then SOLID_BORDER
19
+ when :double then DOUBLE_BORDER
20
+ when :heavy then HEAVY_BORDER
21
+ else ROUNDED_BORDER
22
+ end
23
23
  @content = []
24
24
  end
25
25
 
@@ -53,19 +53,19 @@ module Prompts
53
53
 
54
54
  private
55
55
 
56
- def top_border
57
- border = @border_parts[:top_left] + @border_parts[:horizontal] * (@width - 2) + @border_parts[:top_right]
58
- Fmt("#{@line_padding}%{border}#{@border_color}", border: border)
59
- end
56
+ def top_border
57
+ border = @border_parts[:top_left] + @border_parts[:horizontal] * (@width - 2) + @border_parts[:top_right]
58
+ Fmt("#{@line_padding}%{border}#{@border_color}", border: border)
59
+ end
60
60
 
61
- def bottom_border
62
- border = @border_parts[:bottom_left] + @border_parts[:horizontal] * (@width - 2) + @border_parts[:bottom_right]
63
- Fmt("#{@line_padding}%{border}#{@border_color}", border: border)
64
- end
61
+ def bottom_border
62
+ border = @border_parts[:bottom_left] + @border_parts[:horizontal] * (@width - 2) + @border_parts[:bottom_right]
63
+ Fmt("#{@line_padding}%{border}#{@border_color}", border: border)
64
+ end
65
65
 
66
- def align(text, alignment, between: @border_parts[:vertical])
67
- formatted_boundary = Fmt("%{boundary}#{@border_color}", boundary: between)
68
- wrap_text(text, width: @width, line_prefix: formatted_boundary + SPACE, line_suffix: SPACE + formatted_boundary, alignment: alignment)
69
- end
66
+ def align(text, alignment, between: @border_parts[:vertical])
67
+ formatted_boundary = Fmt("%{boundary}#{@border_color}", boundary: between)
68
+ wrap_text(text, width: @width, line_prefix: formatted_boundary + SPACE, line_suffix: SPACE + formatted_boundary, alignment: alignment)
69
+ end
70
70
  end
71
- end
71
+ end
@@ -3,7 +3,7 @@
3
3
  module Prompts
4
4
  class ConfirmPrompt < Prompt
5
5
  def initialize(...)
6
- super(...)
6
+ super
7
7
 
8
8
  @prompt = if @default == false
9
9
  "Choose [y/N]:"
@@ -20,12 +20,12 @@ module Prompts
20
20
 
21
21
  private
22
22
 
23
- def resolve_choice_from(response)
24
- case response
25
- when "y", "Y" then true
26
- when "n", "N" then false
27
- when "" then @default_boolean
28
- end
23
+ def resolve_choice_from(response)
24
+ case response
25
+ when "y", "Y" then true
26
+ when "n", "N" then false
27
+ when "" then @default_boolean
29
28
  end
29
+ end
30
30
  end
31
31
  end
@@ -43,28 +43,28 @@ module Prompts
43
43
 
44
44
  private
45
45
 
46
- def clear_screen
47
- jump_cursor_to_top
48
- erase_down
49
- end
46
+ def clear_screen
47
+ jump_cursor_to_top
48
+ erase_down
49
+ end
50
50
 
51
- def render_frame
52
- @frame_stack << @slots.dup
53
- OUTPUT.puts SPACE
51
+ def render_frame
52
+ @frame_stack << @slots.dup
53
+ OUTPUT.puts SPACE
54
54
 
55
- return if @slots.empty?
55
+ return if @slots.empty?
56
56
 
57
- OUTPUT.puts @slots.join("\n")
58
- OUTPUT.puts SPACE
59
- @slots.clear
60
- end
57
+ OUTPUT.puts @slots.join("\n")
58
+ OUTPUT.puts SPACE
59
+ @slots.clear
60
+ end
61
61
 
62
- def jump_cursor_to_top
63
- OUTPUT.print "\033[H"
64
- end
62
+ def jump_cursor_to_top
63
+ OUTPUT.print "\033[H"
64
+ end
65
65
 
66
- def erase_down
67
- OUTPUT.print "\e[J"
68
- end
66
+ def erase_down
67
+ OUTPUT.print "\e[J"
68
+ end
69
69
  end
70
- end
70
+ end
data/lib/prompts/form.rb CHANGED
@@ -2,47 +2,52 @@
2
2
 
3
3
  module Prompts
4
4
  class Form
5
- def initialize()
6
- @content = nil
5
+ def self.submit(&block)
6
+ instance = new()
7
+ yield instance if block_given?
8
+ instance.submit
9
+ end
10
+
11
+ def initialize
12
+ @content = Prompts::Content.new
7
13
  @prompts = []
8
14
  @results = []
9
15
  end
10
16
 
11
17
  def content(&block)
12
- @content = Prompts::Content.new
13
18
  yield @content
14
19
  @content
15
20
  end
16
21
 
17
- def text(&block)
18
- prompt = TextPrompt.new
19
- yield(prompt)
22
+ def text(label: nil, prompt: "> ", hint: nil, default: nil, required: false, validate: nil, &block)
23
+ prompt = TextPrompt.new(label: label, prompt: prompt, hint: hint, default: default, required: required, validate: validate)
24
+ yield(prompt) if block_given?
20
25
  prepend_form_content_to_prompt(prompt)
21
26
  @prompts << prompt
22
27
  end
23
28
 
24
- def select(&block)
25
- prompt = SelectPrompt.new
26
- yield(prompt)
29
+ def select(label: nil, options: nil, prompt: "> ", hint: nil, default: nil, validate: nil, &block)
30
+ prompt = SelectPrompt.new(label: label, options: options, prompt: prompt, hint: hint, default: default, validate: validate)
31
+ yield(prompt) if block_given?
27
32
  prepend_form_content_to_prompt(prompt)
28
33
  @prompts << prompt
29
34
  end
30
35
 
31
- def pause(&block)
32
- prompt = PausePrompt.new
33
- yield(prompt)
36
+ def pause(label: nil, prompt: "> ", hint: nil, default: nil, required: false, validate: nil, &block)
37
+ prompt = PausePrompt.new(label: label, prompt: prompt, hint: hint, default: default, required: required, validate: validate)
38
+ yield(prompt) if block_given?
34
39
  prepend_form_content_to_prompt(prompt)
35
40
  @prompts << prompt
36
41
  end
37
42
 
38
- def confirm(&block)
39
- prompt = ConfirmPrompt.new
40
- yield(prompt)
43
+ def confirm(label: nil, prompt: "> ", hint: nil, default: nil, required: false, validate: nil, &block)
44
+ prompt = ConfirmPrompt.new(label: label, prompt: prompt, hint: hint, default: default, required: required, validate: validate)
45
+ yield(prompt) if block_given?
41
46
  prepend_form_content_to_prompt(prompt)
42
47
  @prompts << prompt
43
48
  end
44
49
 
45
- def start
50
+ def submit
46
51
  @prompts.each do |prompt|
47
52
  @results << prompt.ask
48
53
  end
@@ -51,10 +56,10 @@ module Prompts
51
56
 
52
57
  private
53
58
 
54
- def prepend_form_content_to_prompt(prompt)
55
- prompt.prepare_content
56
- @content.gap
57
- prompt.prepend_content(*@content.slots)
58
- end
59
+ def prepend_form_content_to_prompt(prompt)
60
+ prompt.prepare_content
61
+ @content.gap
62
+ prompt.prepend_content(*@content.slots)
63
+ end
59
64
  end
60
- end
65
+ end
@@ -16,4 +16,4 @@ module Prompts
16
16
  wrap_text(@text, width: @width, line_prefix: @line_padding, alignment: :none)
17
17
  end
18
18
  end
19
- end
19
+ end
@@ -3,7 +3,7 @@
3
3
  module Prompts
4
4
  class PausePrompt < Prompt
5
5
  def initialize(...)
6
- super(...)
6
+ super
7
7
 
8
8
  @prompt = "Press Enter ⏎ to continue..."
9
9
  end
@@ -33,6 +33,7 @@ module Prompts
33
33
  @content
34
34
  end
35
35
 
36
+ # standard:disable Style/TrivialAccessors
36
37
  def label(label)
37
38
  @label = label
38
39
  end
@@ -44,6 +45,7 @@ module Prompts
44
45
  def default(default)
45
46
  @default = default
46
47
  end
48
+ # standard:enable Style/TrivialAccessors
47
49
 
48
50
  def ask
49
51
  prepare_content if !@content_prepared
@@ -54,7 +56,7 @@ module Prompts
54
56
  @content.render
55
57
  *initial_prompt_lines, last_prompt_line = formatted_prompt
56
58
  puts initial_prompt_lines.join("\n") if initial_prompt_lines.any?
57
- response = Reline.readline(last_prompt_line, history = false).chomp
59
+ response = Reline.readline(last_prompt_line, _history = false).chomp
58
60
  @choice = resolve_choice_from(response)
59
61
 
60
62
  if (@error = ensure_validity(response))
@@ -87,53 +89,53 @@ module Prompts
87
89
 
88
90
  private
89
91
 
90
- def prepare_default
91
- Reline.pre_input_hook = -> do
92
- Reline.insert_text @default.to_s
93
- # Remove the hook right away.
94
- Reline.pre_input_hook = nil
95
- end
92
+ def prepare_default
93
+ Reline.pre_input_hook = -> do
94
+ Reline.insert_text @default.to_s
95
+ # Remove the hook right away.
96
+ Reline.pre_input_hook = nil
96
97
  end
98
+ end
97
99
 
98
- def prepare_validations
99
- if @required
100
- error_message = @required.is_a?(String) ? @required : "Value cannot be empty."
101
- @validations << ->(input) { error_message if input.empty? }
102
- end
103
-
104
- if @validate
105
- @validations << @validate
106
- end
100
+ def prepare_validations
101
+ if @required
102
+ error_message = @required.is_a?(String) ? @required : "Value cannot be empty."
103
+ @validations << ->(input) { error_message if input.empty? }
107
104
  end
108
105
 
109
- def resolve_choice_from(response)
110
- response
106
+ if @validate
107
+ @validations << @validate
111
108
  end
109
+ end
112
110
 
113
- def formatted_prompt
114
- prompt_with_space = @prompt.end_with?(SPACE) ? @prompt : @prompt + SPACE
115
- ansi_prompt = Fmt("%{prompt}faint|bold", prompt: prompt_with_space)
116
- @formatted_prompt ||= Paragraph.new(ansi_prompt, width: MAX_WIDTH).lines
117
- end
111
+ def resolve_choice_from(response)
112
+ response
113
+ end
118
114
 
119
- def formatted_label
120
- Fmt("%{label}cyan|bold %{instructions}faint|italic", label: @label, instructions: @instructions ? "(#{@instructions})" : "")
121
- end
115
+ def formatted_prompt
116
+ prompt_with_space = @prompt.end_with?(SPACE) ? @prompt : @prompt + SPACE
117
+ ansi_prompt = Fmt("%{prompt}faint|bold", prompt: prompt_with_space)
118
+ @formatted_prompt ||= Paragraph.new(ansi_prompt, width: MAX_WIDTH).lines
119
+ end
122
120
 
123
- def formatted_hint
124
- Fmt("%{hint}faint|bold", hint: @hint)
125
- end
121
+ def formatted_label
122
+ Fmt("%{label}cyan|bold %{instructions}faint|italic", label: @label, instructions: @instructions ? "(#{@instructions})" : "")
123
+ end
126
124
 
127
- def formatted_error
128
- Fmt("%{error}red|bold", error: @error + " Try again (×#{@attempts})...")
129
- end
125
+ def formatted_hint
126
+ Fmt("%{hint}faint|bold", hint: @hint)
127
+ end
130
128
 
131
- def ensure_validity(response)
132
- @validations.each do |validation|
133
- result = validation.call(response)
134
- return result if result
135
- end
136
- nil
129
+ def formatted_error
130
+ Fmt("%{error}red|bold", error: @error + " Try again (×#{@attempts})...")
131
+ end
132
+
133
+ def ensure_validity(response)
134
+ @validations.each do |validation|
135
+ result = validation.call(response)
136
+ return result if result
137
137
  end
138
+ nil
139
+ end
138
140
  end
139
141
  end
@@ -12,19 +12,19 @@ module Prompts
12
12
  super(**kwargs)
13
13
 
14
14
  @options = options.is_a?(Array) ? options.to_h { |item| [item, item] } : options
15
- if (index = @options.keys.index(@default))
16
- @default = index + 1
17
- else
18
- @default = nil
15
+ @default = if (index = @options.keys.index(@default))
16
+ index + 1
19
17
  end
20
18
  @instructions = "Enter the number of your choice"
21
19
  @hint ||= "Type your response and press Enter ⏎"
22
20
  @validations << ->(choice) { "Invalid choice." if !choice.to_i.between?(1, @options.size) }
23
21
  end
24
22
 
23
+ # standard:disable Style/TrivialAccessors
25
24
  def options(options)
26
25
  @options = options
27
26
  end
27
+ # standard:enable Style/TrivialAccessors
28
28
 
29
29
  def prepare_content
30
30
  super
@@ -36,10 +36,10 @@ module Prompts
36
36
 
37
37
  private
38
38
 
39
- def resolve_choice_from(response)
40
- choice = response.to_i
41
- key, value = @options.to_a[choice - 1]
42
- value
43
- end
39
+ def resolve_choice_from(response)
40
+ choice = response.to_i
41
+ key, _value = @options.to_a[choice - 1]
42
+ key
43
+ end
44
44
  end
45
45
  end
@@ -3,7 +3,7 @@
3
3
  module Prompts
4
4
  class TextPrompt < Prompt
5
5
  def initialize(...)
6
- super(...)
6
+ super
7
7
 
8
8
  @instructions = "Press Enter to submit"
9
9
  @hint ||= "Type your response and press Enter ⏎"
@@ -5,7 +5,7 @@ require "unicode/emoji"
5
5
 
6
6
  module Prompts
7
7
  module TextUtils
8
- ANSI_REGEX = /\e\[[0-9;]*[a-zA-Z]/.freeze
8
+ ANSI_REGEX = /\e\[[0-9;]*[a-zA-Z]/
9
9
 
10
10
  def wrap_text(text, width:, line_prefix: EMPTY, line_suffix: EMPTY, alignment: :left)
11
11
  words = text.scan(Regexp.union(/\S+/, ANSI_REGEX))
@@ -55,4 +55,4 @@ module Prompts
55
55
  text.gsub(ANSI_REGEX, EMPTY)
56
56
  end
57
57
  end
58
- end
58
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Prompts
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
data/lib/prompts.rb CHANGED
@@ -20,8 +20,8 @@ require_relative "prompts/select_prompt"
20
20
  require_relative "prompts/form"
21
21
 
22
22
  module Prompts
23
- EMPTY = "".freeze
24
- SPACE = " ".freeze
23
+ EMPTY = ""
24
+ SPACE = " "
25
25
  MAX_WIDTH = 80
26
26
  OUTPUT = $stdout
27
27
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prompts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Margheim
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-08-20 00:00:00.000000000 Z
11
+ date: 2024-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: unicode-display_width