Module: Philiprehberger::CliKit::Menu

Defined in:
lib/philiprehberger/cli_kit/menu.rb

Overview

Numbered menu selection for CLI applications.

Class Method Summary collapse

Class Method Details

.multi_select(message, choices, defaults: [], input: $stdin, output: $stdout) ⇒ Array<String>

Present a numbered menu and allow multiple selections.

The user enters a comma- or space-separated list of numbers (e.g. “1,3” or “1 3”). Unknown or out-of-range entries are ignored, and duplicates are collapsed. An empty answer returns defaults or an empty array if no defaults are given.

Parameters:

  • message (String)

    the prompt message

  • choices (Array<String>)

    the list of choices

  • defaults (Array<String>) (defaults to: [])

    choices pre-selected by default

  • input (IO) (defaults to: $stdin)

    input stream (default: $stdin)

  • output (IO) (defaults to: $stdout)

    output stream (default: $stdout)

Returns:

  • (Array<String>)

    the selected values in choice order

Raises:

  • (ArgumentError)

    if choices is empty



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/philiprehberger/cli_kit/menu.rb', line 61

def self.multi_select(message, choices, defaults: [], input: $stdin, output: $stdout)
  raise ArgumentError, 'choices must not be empty' if choices.empty?

  default_indexes = defaults.map { |d| choices.index(d) }.compact

  output.puts message
  choices.each_with_index do |choice, idx|
    marker = default_indexes.include?(idx) ? '*' : ' '
    output.puts "  #{marker} #{idx + 1}) #{choice}"
  end

  output.print 'Choose (comma-separated): '
  output.flush

  answer = input.gets&.strip || ''
  return defaults.dup if answer.empty?

  picks = answer.split(/[,\s]+/).filter_map do |token|
    idx = Integer(token, 10) - 1
    idx if idx >= 0 && idx < choices.length
  rescue ArgumentError
    nil
  end

  picks.uniq.sort.map { |i| choices[i] }
end

.select(message, choices, default: nil, input: $stdin, output: $stdout) ⇒ String

Present a numbered menu and return the selected value.

Parameters:

  • message (String)

    the prompt message

  • choices (Array<String>)

    the list of choices

  • default (String, nil) (defaults to: nil)

    pre-selected default choice

  • input (IO) (defaults to: $stdin)

    input stream (default: $stdin)

  • output (IO) (defaults to: $stdout)

    output stream (default: $stdout)

Returns:

  • (String)

    the selected value

Raises:

  • (ArgumentError)

    if choices is empty



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/philiprehberger/cli_kit/menu.rb', line 16

def self.select(message, choices, default: nil, input: $stdin, output: $stdout)
  raise ArgumentError, 'choices must not be empty' if choices.empty?

  default_index = default ? choices.index(default) : nil

  output.puts message
  choices.each_with_index do |choice, idx|
    marker = default_index == idx ? '*' : ' '
    output.puts "  #{marker} #{idx + 1}) #{choice}"
  end

  prompt_text = default ? "Choose [#{default_index + 1}]: " : 'Choose: '
  output.print prompt_text
  output.flush

  answer = input.gets&.strip || ''

  if answer.empty? && default
    return default
  end

  index = answer.to_i - 1
  if index >= 0 && index < choices.length
    choices[index]
  elsif default
    default
  else
    choices.first
  end
end