Class: Blufin::Terminal

Inherits:
Object
  • Object
show all
Extended by:
Columnist
Defined in:
lib/core/terminal.rb

Constant Summary collapse

MSG_INFO =
'info'
MSG_WARNING =
'warning'
MSG_ERROR =
'error'
MSG_TODO =
'todo'
MSG_AUTOMATIC =
'automatic'
MSG_GENERATED =
'generated'
MSG_PROCESSED =
'processed'
MSG_PROGRESS =
'progress'
MSG_CUSTOM =
'custom'
MSG_CUSTOM_AUTO_PAD =
'custom_auto_pad'
ERR_OUTPUT =
'/tmp/execute-output'

Class Method Summary collapse

Class Method Details

.abort(title = nil, message = nil, exit_script = true, preceding_blank_line = false) ⇒ Object

Displays error and exits script by default.

Returns:

  • void



168
169
170
171
172
# File 'lib/core/terminal.rb', line 168

def self.abort(title = nil, message = nil, exit_script = true, preceding_blank_line = false)
    title   = 'Abandon ship!' if title.nil?
    message = "You have chosen to \x1B[38;5;196mABORT\x1B[38;5;240m the script." if message.nil?
    Blufin::Terminal::error(title, message, exit_script, preceding_blank_line, 'ABORT')
end

.any_key_to_continue(text = nil, preceding_blank_line = true) ⇒ Object

Deprecated.

Gives a prompt where ANY KEY will continue executing the script.

Returns:

  • void



539
540
541
542
543
544
545
546
547
# File 'lib/core/terminal.rb', line 539

def self.any_key_to_continue(text = nil, preceding_blank_line = true)
    STDOUT.flush
    puts if preceding_blank_line
    text = 'Press any key to continue' if text.nil?
    print "     \x1B[38;5;240m#{text}\x1B[0m => "
    STDIN.gets
    puts
    nil
end

.arg_instructions(arg_descriptions, preceding_blank_line = true) ⇒ Object

Displays warning message.

Returns:

  • void



217
218
219
220
221
222
223
# File 'lib/core/terminal.rb', line 217

def self.arg_instructions(arg_descriptions, preceding_blank_line = true)
    puts if preceding_blank_line
    puts "  \x1B[38;5;231m\x1B[48;5;22m Instructions \x1B[0m \xe2\x86\x92 This command expects the following arguments:\n"
    arg_descriptions_converted = []
    arg_descriptions.each_with_index { |x, idx| arg_descriptions_converted << "#{idx + 1}) \x1B[38;5;208m#{x}\x1B[0m" }
    parse_messages(arg_descriptions_converted)
end

.automatic(title = nil, message = nil, preceding_blank_line = true) ⇒ Object

Displays automatic message.

Returns:

  • void



185
186
187
188
189
# File 'lib/core/terminal.rb', line 185

def self.automatic(title = nil, message = nil, preceding_blank_line = true)
    puts if preceding_blank_line
    puts "\x1B[38;5;231m\x1B[48;5;96m Automatic \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
    parse_messages(message)
end

.code_highlight(string, type, indent = 5) ⇒ Object

Outputs code to terminal that is syntax highlighted.

Returns:

  • void

Raises:

  • (RuntimeError)


612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
# File 'lib/core/terminal.rb', line 612

def self.code_highlight(string, type, indent = 5)
    raise RuntimeError, "Expected String, instead got:#{string.class}" unless string.is_a?(String)
    type  = type.downcase
    types = {
        'yml'  => Rouge::Lexers::YAML.new,
        'json' => Rouge::Lexers::JSON.new,
        'js'   => Rouge::Lexers::Javascript.new,
        'java' => Rouge::Lexers::Javascript.new,
        'sass' => Rouge::Lexers::Sass.new
    }
    raise RuntimeError, "Lexer not defined for type: #{type}" unless types.has_key?(type)
    repeat    = ' ' * indent
    formatter = Rouge::Formatters::Terminal256.new
    formatter.format(types[type].lex(string)).split("\n").each do |line|
        puts "#{repeat}#{line}"
    end
end

.code_highlight_file(path_and_file, type, indent = 5) ⇒ Object

Outputs code to terminal that is syntax highlighted.

Returns:

  • void

Raises:

  • (RuntimeError)


605
606
607
608
# File 'lib/core/terminal.rb', line 605

def self.code_highlight_file(path_and_file, type, indent = 5)
    raise RuntimeError, "File does not exist: #{path_and_file}" unless Blufin::Files::file_exists(path_and_file)
    code_highlight(File.read(path_and_file), type, indent)
end

.command(commands, location = nil, verbose = true, verbose_cd = true, capture_real_output = false, pbl: false, tbl: false) ⇒ Object

Runs a series of commands in the terminal.

Returns:

  • void



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/core/terminal.rb', line 29

def self.command(commands, location = nil, verbose = true, verbose_cd = true, capture_real_output = false, pbl: false, tbl: false)
    unless commands.is_a?(Array)
        commands = [commands]
    end
    unless location.nil?
        unless File.directory?(File.expand_path(location))
            error('Directory not found.', "Cannot find the following directory: \x1B[38;5;205m#{location}\x1B[0m", true)
        end
    end
    output = []
    if verbose_cd && verbose && commands.any? && !location.nil?
        puts "\x1B[38;5;231m\x1B[48;5;22m Executing \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m #{Blufin::Terminal::format_command("cd #{location}")}\n"
    end
    if commands.any?
        commands.each do |command|
            if verbose
                puts "\x1B[38;5;231m\x1B[48;5;22m Executing \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m #{Blufin::Terminal::format_command("#{command}")}\n"
                puts if pbl
            end

            if location.nil?
                if capture_real_output
                    output << `#{command}`
                else
                    output << system("#{command}")
                end
            else
                if capture_real_output
                    output << `cd #{location} && #{command}`
                else
                    output << system("cd #{location} && #{command}")
                end
            end
        end
    end
    if capture_real_output
        output.map! { |v| v.gsub('\n', "\n") }
    end
    puts if tbl
    output
end

.command_capture(commands, location = nil, verbose = true, verbose_cd = true) ⇒ Object

Runs a series of commands in the terminal (and captures the output).

Returns:

  • void



73
74
75
# File 'lib/core/terminal.rb', line 73

def self.command_capture(commands, location = nil, verbose = true, verbose_cd = true)
    command(commands, location, verbose, verbose_cd, true)
end

.custom(keyword = 'N/A', color = 1, title = nil, message = nil, preceding_blank_line = true) ⇒ Object

Displays custom message (ideally, keyword should be 7 characters long to match the rest of the output).

Returns:

  • void



227
228
229
230
231
232
# File 'lib/core/terminal.rb', line 227

def self.custom(keyword = 'N/A', color = 1, title = nil, message = nil, preceding_blank_line = true)
    color = 55 if color.nil?
    puts if preceding_blank_line
    puts "  \x1B[38;5;231m\x1B[48;5;#{color}m #{keyword} \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
    parse_messages(message)
end

.custom_progress(keyword = 'N/A', color = 1, title = nil, message = nil, preceding_blank_line = true) ⇒ Object

Displays custom message with a progress indicator. Deprecated

Returns:

  • void



237
238
239
240
241
# File 'lib/core/terminal.rb', line 237

def self.custom_progress(keyword = 'N/A', color = 1, title = nil, message = nil, preceding_blank_line = true)
    puts if preceding_blank_line
    puts "  \x1B[38;5;231m\x1B[48;5;#{color}m #{keyword} \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
    parse_messages(message)
end

.error(title = nil, message = nil, exit_script = true, preceding_blank_line = true, error_text = 'Error') ⇒ Object

Displays error and exits script by default.

Returns:

  • void



176
177
178
179
180
181
# File 'lib/core/terminal.rb', line 176

def self.error(title = nil, message = nil, exit_script = true, preceding_blank_line = true, error_text = 'Error')
    puts if preceding_blank_line
    puts "    \x1B[38;5;231m\x1B[48;5;124m #{error_text} \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
    parse_messages(message)
    exit 1 if exit_script
end

.execute(command, path = nil, capture: false, verbose: true, text: nil, error_output: ERR_OUTPUT, display_error: true, ignore_error: false) ⇒ Object

Executes a command and shows that something is happening (via a cli-spinner). If capture: is false, returns TRUE:FALSE whether command was successful or not. If capture: is true, returns raw output of command. See: github.com/piotrmurach/tty-spinner/blob/master/lib/tty/spinner/formats.rb (for spinner options).

Returns:

  • idx-1 => Command was successful (exit code 0) will output TRUE if capture: FALSE or otherwise STDOUT will be returned, IE: ‘On branch master’

  • idx-2 => STDERR (if any), IE: anything written to /tmp/execute-output



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/core/terminal.rb', line 83

def self.execute(command, path = nil, capture: false, verbose: true, text: nil, error_output: ERR_OUTPUT, display_error: true, ignore_error: false)
    text    = text.is_a?(String) ? text : command
    t1      = Time.now
    spinner = TTY::Spinner.new("[:spinner] \x1B[38;5;208m#{text}#{!path.nil? ? " \x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;240m#{path}" : nil}\x1B[0m", format: :dots) if verbose
    spinner.auto_spin if verbose
    path = File.expand_path('~/') if path.nil?
    path = File.expand_path(path) unless path.nil?
    `echo '' > #{ERR_OUTPUT}`
    if capture
        res = `cd #{path} && #{command} 2>#{error_output}`
    else
        res = system("cd #{path} && #{command} 1>/dev/null 2>#{error_output}")
    end
    t2    = Time.now
    delta = "#{'%.3f' % (t2 - t1).abs}s"
    error = `cat #{ERR_OUTPUT}`.to_s.strip
    if (res && error.length == 0) || ignore_error
        spinner.success("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;46mComplete \x1B[38;5;240m(#{delta})\x1B[0m\x1B[0m") if verbose
        res = true unless capture
    else
        spinner.error("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;196mFailed (#{delta})\x1B[0m") if verbose
        # If there is an error, output it (even if verbose == false).
        if error.length > 0 && display_error
            puts "\x1B[38;5;240m"
            system("cat #{ERR_OUTPUT}")
            puts "\x1B[0m"
        end
        res = false unless capture
    end
    error = nil if error.nil? || error.strip == ''
    return res, error
end

.execute_proc(title, proc, verbose: true) ⇒ Object

Same as above but with a proc/lambda instead of a terminal command.

Returns:

  • void

Raises:

  • (RuntimeError)


118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/core/terminal.rb', line 118

def self.execute_proc(title, proc, verbose: true)
    raise RuntimeError, "Expected String, instead got:#{title.class}" unless title.is_a?(String)
    raise RuntimeError, "Expected proc to be an instance of Proc, instead got: #{proc.class}" unless proc.is_a?(Proc)
    t1      = Time.now
    spinner = nil
    spinner = TTY::Spinner.new("[:spinner] \x1B[38;5;208m#{title}\x1B[0m", format: :dots) if verbose
    spinner.auto_spin if verbose
    res   = proc.call
    t2    = Time.now
    delta = "#{'%.3f' % (t2 - t1).abs}s"
    spinner.success("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;46mComplete \x1B[38;5;240m(#{delta})\x1B[0m\x1B[0m") if verbose && res
    spinner.error("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;196mFailed (#{delta})\x1B[0m") if verbose && !res
    res
end

.fill(length = 0, string = ' ') ⇒ Object

Generates ‘filler’ string.

Returns:

  • String



325
326
327
328
329
330
331
# File 'lib/core/terminal.rb', line 325

def self.fill(length = 0, string = ' ')
    fill_string = ''
    (0..length - 1).each {
        fill_string = "#{fill_string}#{string}"
    }
    fill_string
end

.format_action(action_text, capitalize = true) ⇒ Object

Returns action message in consistent, uniform manner.

Returns:

  • String



245
246
247
248
249
# File 'lib/core/terminal.rb', line 245

def self.format_action(action_text, capitalize = true)
    action_text = action_text.nil? ? 'N/A' : action_text
    action_text = action_text.to_s.upcase if capitalize
    "\x1B[38;5;170m#{action_text}\x1B[0m"
end

.format_branch(branch_name) ⇒ Object

Returns branch name in consistent, uniform manner.

Returns:

  • String



253
254
255
# File 'lib/core/terminal.rb', line 253

def self.format_branch(branch_name)
    "\x1B[38;5;40m[#{branch_name}]\x1B[0m"
end

.format_command(command_name) ⇒ Object

Returns command name in consistent, uniform manner.

Returns:

  • String



259
260
261
# File 'lib/core/terminal.rb', line 259

def self.format_command(command_name)
    "\x1B[38;5;70m$ \x1B[38;5;240m#{command_name}\x1B[0m"
end

.format_directory(directory_name, expand_path = true) ⇒ Object

Returns directory name in consistent, uniform manner.

Returns:

  • String



265
266
267
268
# File 'lib/core/terminal.rb', line 265

def self.format_directory(directory_name, expand_path = true)
    return "\x1B[38;5;48m#{File.expand_path(directory_name)}\x1B[0m" if expand_path
    return "\x1B[38;5;48m#{directory_name}\x1B[0m" unless expand_path
end

.format_flag(flag_letter, display_flag_text = true) ⇒ Object

Returns flag name in consistent, uniform manner.

Returns:

  • String



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/core/terminal.rb', line 272

def self.format_flag(flag_letter, display_flag_text = true)
    if flag_letter.is_a? String
        letter_array = [flag_letter]
    elsif flag_letter.is_a? Array
        letter_array = flag_letter
    else
        raise RuntimeError, 'Terminal::format_flag expects either String or Array.'
    end
    flag_txt = ''
    letter_array.each do |letter|
        flag_txt = "#{flag_txt}, -#{letter}"
    end
    xtra_txt = letter_array.length > 1 ? ' flags' : ' flag'
    flag_txt = flag_txt[2..-1]
    "\x1B[38;5;177m#{flag_txt}#{display_flag_text ? xtra_txt : ''}\x1B[0m"
end

.format_highlight(highlighted_text, capitalize = false, target: nil) ⇒ Object

Returns action message in consistent, uniform manner.

Returns:

  • String



291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/core/terminal.rb', line 291

def self.format_highlight(highlighted_text, capitalize = false, target: nil)
    case target
        when 'cf' # CloudFormation
            color     = 82
            color_end = "\x1B[38;5;246m"
        when 'cf-ctt' # CloudFormation (Creation Time Title)
            color     = 196
            color_end = "\x1B[38;5;246m"
        when 'cf-ct' # CloudFormation (Creation Time)
            color     = 82
            color_end = "\x1B[38;5;246m"
        else
            color     = 117
            color_end = "\x1B[0m"
    end
    if capitalize
        return "\x1B[38;5;#{color}m#{highlighted_text.upcase}#{color_end}"
    else
        return "\x1B[38;5;#{color}m#{highlighted_text}#{color_end}"
    end
end

.format_invalid(invalid_string, capitalize = false) ⇒ Object

Returns invalid data in consistent, uniform manner.

Returns:

  • String



315
316
317
318
319
320
321
# File 'lib/core/terminal.rb', line 315

def self.format_invalid(invalid_string, capitalize = false)
    if capitalize
        return "\x1B[38;5;196m#{invalid_string.upcase}\x1B[0m"
    else
        return "\x1B[38;5;196m#{invalid_string}\x1B[0m"
    end
end

.get_terminal_heightObject

Gets the Terminal height

Returns:

  • Integer



558
559
560
561
# File 'lib/core/terminal.rb', line 558

def self.get_terminal_height
    terminal_size = HighLine::SystemExtensions.terminal_size
    terminal_size[1]
end

.get_terminal_widthObject

Gets the Terminal width

Returns:

  • Integer



551
552
553
554
# File 'lib/core/terminal.rb', line 551

def self.get_terminal_width
    terminal_size = HighLine::SystemExtensions.terminal_size
    terminal_size[0]
end

.info(title = nil, message = nil, preceding_blank_line = true) ⇒ Object

Displays info message.

Returns:

  • void



193
194
195
196
197
# File 'lib/core/terminal.rb', line 193

def self.info(title = nil, message = nil, preceding_blank_line = true)
    puts if preceding_blank_line
    puts "  \x1B[38;5;231m\x1B[48;5;240m Message \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
    parse_messages(message)
end

.output(message = 'No message', type = MSG_INFO, title = nil, bg_color = nil) ⇒ Object

Outputs messages to the terminal. If CUSTOM, title must be 11 characters to match existing scheme.

Returns:

  • void

Raises:

  • (RuntimeError)


136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/core/terminal.rb', line 136

def self.output(message = 'No message', type = MSG_INFO, title = nil, bg_color = nil)
    raise RuntimeError, "'title' cannot be nil." if title.nil? && [MSG_CUSTOM, MSG_CUSTOM_AUTO_PAD].include?(type)
    raise RuntimeError, "'bg_color' cannot be nil." if bg_color.nil? && [MSG_CUSTOM, MSG_CUSTOM_AUTO_PAD].include?(type)
    raise RuntimeError, "'title' cannot be more that 9 characerts when MSG_CUSTOM_AUTO_PAD is the message type." if type == MSG_CUSTOM_AUTO_PAD && title.length > 9
    case type
        when MSG_INFO
            puts "\x1B[38;5;231m\x1B[48;5;22m Executing \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m\n"
        when MSG_WARNING
            puts "\x1B[38;5;231m\x1B[48;5;202m  Warning  \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m\n"
        when MSG_ERROR
            puts "\x1B[38;5;231m\x1B[48;5;196m   Error   \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m\n"
        when MSG_TODO
            puts "\x1B[38;5;231m\x1B[48;5;199m   @TODO   \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m\n"
        when MSG_AUTOMATIC
            puts "\x1B[38;5;231m\x1B[48;5;96m Automatic \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m\n"
        when MSG_GENERATED
            puts "\x1B[38;5;231m\x1B[48;5;96m Generated \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m\n"
        when MSG_PROCESSED
            puts "\x1B[38;5;231m\x1B[48;5;238m Processed \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m\n"
        when MSG_PROGRESS
            puts "    \x1B[38;5;240m#{message}\x1B[0m\n"
        when MSG_CUSTOM
            puts "\x1B[38;5;231m\x1B[48;5;#{bg_color}m#{title}\x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m\n"
        when MSG_CUSTOM_AUTO_PAD
            puts "\x1B[38;5;231m#{''.rjust((9 - title.length), ' ')}\x1B[48;5;#{bg_color}m #{title} \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m\n"
        else
            raise RuntimeError, "Invalid Terminal::output type: #{type}"
    end
end

Outputs Exceptions in a pretty, standardized format.

Returns:

  • void



565
566
567
568
569
570
571
572
573
574
# File 'lib/core/terminal.rb', line 565

def self.print_exception(e)
    if e.is_a?(Exception)
        custom('Exception', 235, "There was an error in your code: \x1B[48;5;124m #{e.message} \x1B[0m", e.backtrace)
    elsif e.is_a?(String)
        custom('Exception', 235, "There was an error in your code: \x1B[48;5;124m #{e} \x1B[0m")
    else
        raise RuntimeError, "Expected String|Exception, instead got:#{e.class}"
    end
    exit 1
end

Outputs Array of files/directories in a pretty, standardized format.

Returns:

  • void

Raises:

  • (RuntimeError)


578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
# File 'lib/core/terminal.rb', line 578

def self.print_files_written(array_of_paths_or_files, message = nil, limit = 10, preceding_blank_line = true)
    raise RuntimeError, "Expected Array of files, instead got: #{array_of_paths_or_files.class}" unless array_of_paths_or_files.is_a?(Array)
    message     = "Wrote the following #{Blufin::Terminal::format_highlight('files/directories')}" if message.nil?
    limit       = limit.nil? ? 99999999999 : limit.to_i
    file_output = []
    file_count  = 0
    file_more   = 0
    array_of_paths_or_files.compact!
    array_of_paths_or_files.sort!
    array_of_paths_or_files.each do |path_or_file|
        file_count += 1
        if file_count > limit
            file_more += 1
            next
        end
        if Blufin::Files::path_exists(path_or_file)
            file_output << "Path: \x1B[38;5;248m#{path_or_file.strip}\x1B[0m"
        else
            file_output << "File: \x1B[38;5;46m#{path_or_file.strip}\x1B[0m"
        end
    end
    file_output << "\x1B[38;5;168m#{file_more} more...\x1B[0m" if file_more > 0
    Blufin::Terminal::automatic(message, file_output, preceding_blank_line)
end

.prompt_ask(question, default: nil, help: nil, validation_regex: nil) ⇒ Object

See: github.com/piotrmurach/tty-prompt#1-usage See: www.rubydoc.info/gems/tty-prompt

> What is your name? (Albert Rannetsperger)

Returns:

  • string



391
392
393
394
395
396
397
398
# File 'lib/core/terminal.rb', line 391

def self.prompt_ask(question, default: nil, help: nil, validation_regex: nil)
    puts display_prompt_help(help) unless help.nil?
    prompt = TTY::Prompt.new
    prompt.ask(display_prompt_text(question), default: default) do |q|
        q.modify :strip
        q.validate validation_regex unless validation_regex.nil?
    end
end

.prompt_enum_select(question, options) ⇒ Object

Select an editor?

1) emacs
2) nano
3) vim
Choose 1-3 [1]:

Returns:

  • Array

Raises:

  • (RuntimeError)


495
496
497
498
499
# File 'lib/core/terminal.rb', line 495

def self.prompt_enum_select(question, options)
    raise RuntimeError, "Expected Array, instead got #{options.class}" unless options.is_a?(Array)
    prompt = TTY::Prompt.new
    prompt.enum_select(display_prompt_text(question), options)
end

.prompt_expand(question, options) ⇒ Object

Overwrite Gemfile? (enter “h” for help) [y,n,a,d,q,h] ]

Returns:

  • multiple

Raises:

  • (RuntimeError)


510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
# File 'lib/core/terminal.rb', line 510

def self.prompt_expand(question, options)
    raise RuntimeError, "Expected Array, instead got #{options.class}" unless options.is_a?(Array)
    options.each do |option|
        found_key   = false
        found_name  = false
        found_value = false
        option.each do |k, v|
            case k
                when :key
                    found_key = true
                when :name
                    found_name = true
                when :value
                    found_value = true
                else
                    raise RuntimeError, "Unrecognized Key: #{k}"
            end
        end
        raise RuntimeError, "Option is missing #{Blufin::Terminal::format_highlight('key')}: #{option.inspect}" unless found_key
        raise RuntimeError, "Option is missing #{Blufin::Terminal::format_highlight('name')}: #{option.inspect}" unless found_name
        raise RuntimeError, "Option is missing #{Blufin::Terminal::format_highlight('value')}: #{option.inspect}" unless found_value
    end
    prompt = TTY::Prompt.new
    prompt.expand(display_prompt_text(question), options)
end

.prompt_for_input(title = 'Input Required', message = nil, subtitle_array = nil, validation_proc = nil, validation_message = 'Invalid value', clear_screen = true, preceding_blank_line = true) ⇒ Object

Deprecated.

Shows a prompt waiting for user input. If validation_proc is passed, the Proc must return TRUE in order for validation to pass.

Returns:

  • string



365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/core/terminal.rb', line 365

def self.prompt_for_input(title = 'Input Required', message = nil, subtitle_array = nil, validation_proc = nil, validation_message = 'Invalid value', clear_screen = true, preceding_blank_line = true)
    system('clear') if clear_screen
    puts if preceding_blank_line
    puts "  \x1B[38;5;231m\x1B[48;5;125m #{title.upcase} \x1B[0m \xe2\x86\x92 #{message} \x1B[38;5;240m\xe2\x80\x94 \x1B[38;5;160m(or 'X' to cancel)\x1B[0m\n"
    parse_messages(subtitle_array) unless subtitle_array.nil?
    puts if subtitle_array.nil?
    response = nil
    while response.nil?
        response = Readline::readline("     \x1B[38;5;245m=>\x1B[0m ", true)
        unless validation_proc.nil?
            raise RuntimeError, "Expected validation_proc to be an instance of Proc, instead got: #{validation_proc.class}" unless validation_proc.is_a?(Proc)
            unless response.upcase == 'X' || validation_proc.call(response)
                puts "     \x1B[38;5;245m=>\x1B[0m \x1B[38;5;196mERROR\x1B[0m \xe2\x80\x94 #{validation_message}\n"
                response = nil
            end
        end
    end
    abort if response.upcase == 'X'
    puts
    response
end

.prompt_mask(question) ⇒ Object

> What is your secret? ••••

Returns:

  • string



413
414
415
416
# File 'lib/core/terminal.rb', line 413

def self.prompt_mask(question)
    prompt = TTY::Prompt.new
    prompt.mask(display_prompt_text(question))
end

.prompt_multi_select(question, options, help: nil, per_page: 20, cycle: true) ⇒ Object

Select drinks? (Use ↑/↓ arrow keys, press Space to select and Enter to finish)“ ‣ ⬡ vodka

 beer
 wine
 whisky
 bourbon

Returns:

  • Array

Raises:

  • (RuntimeError)


466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/core/terminal.rb', line 466

def self.prompt_multi_select(question, options, help: nil, per_page: 20, cycle: true)
    raise RuntimeError, "Expected Array, instead got #{options.class}" unless options.is_a?(Array)
    puts display_prompt_help(help) unless help.nil?
    prompt = TTY::Prompt.new
    if options[0].is_a?(String)
        prompt.multi_select(display_prompt_text(question), options, per_page: per_page, cycle: cycle)
    elsif options[0].is_a?(Hash)
        prompt.multi_select(display_prompt_text(question), options, per_page: per_page, cycle: cycle) do |menu|
            options.each do |option|
                raise RuntimeError, "Expected option to be Hash, instead got: (#{option.class}) #{option.inspect}" unless option.is_a?(Hash)
                raise RuntimeError, 'Option is missing key => :text' unless option.has_key?(:text)
                raise RuntimeError, 'Option is missing key => :value' unless option.has_key?(:value)
                raise RuntimeError, "Expected :disabled option to be String, instead got: #{option[:disabled].class}" if option.has_key?(:disabled) && !option[:disabled].is_a?(String) && !option[:disabled].nil?
                menu.choice option[:text], option[:value] unless option.has_key?(:disabled)
                menu.choice option[:text], option[:value], disabled: "\x1B[38;5;196m#{option[:disabled]}\x1B[0m" if option.has_key?(:disabled)
            end
        end
    else
        raise RuntimeError, "Expected options Array to consist of either Strings or Hashes, instead got: #{options.inspect}"
    end
end

.prompt_select(question, options, help: nil, per_page: 20) ⇒ Object

Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select) ‣ Scorpion

Kano
Jax

Returns:

  • string

Raises:

  • (RuntimeError)


425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/core/terminal.rb', line 425

def self.prompt_select(question, options, help: nil, per_page: 20)
    raise RuntimeError, "Expected Array, instead got #{options.class}" unless options.is_a?(Array)
    raise RuntimeError, 'Array cannot be empty.' unless options.any?
    puts display_prompt_help(help) unless help.nil?
    prompt = TTY::Prompt.new
    if options[0].is_a?(String)
        prompt.select(display_prompt_text(question), options, per_page: per_page)
    elsif options[0].is_a?(Hash)
        options_fixed = []
        options.each do |option|
            raise RuntimeError, "Expected option to be Hash, instead got: (#{option.class}) #{option.inspect}" unless option.is_a?(Hash)
            raise RuntimeError, 'Option is missing key => :text' unless option.has_key?(:text)
            raise RuntimeError, 'Option is missing key => :value' unless option.has_key?(:value)
            raise RuntimeError, "Expected :disabled option to be String, instead got: #{option[:disabled].class}" if option.has_key?(:disabled) && !option[:disabled].is_a?(String) && !option[:disabled].nil?
            v = option[:value]
            v = 'true' if option[:value].is_a?(TrueClass)
            v = 'false' if option[:value].is_a?(FalseClass)
            options_fixed << {:text => option[:text], :value => v}
        end
        result = prompt.select(display_prompt_text(question), per_page: per_page) do |menu|
            options_fixed.each do |option|
                menu.choice option[:text], option[:value] unless option.has_key?(:disabled)
                menu.choice option[:text], option[:value], disabled: "\x1B[38;5;196m#{option[:disabled]}\x1B[0m" if option.has_key?(:disabled)
            end
        end
        return true if result == 'true'
        return false if result == 'false'
        return result
    else
        raise RuntimeError, "Expected options Array to consist of either Strings or Hashes, instead got: #{options.inspect}"
    end
end

.prompt_yes?(question) ⇒ Boolean

> Do you like Ruby? (Y/n)

Returns:

  • (Boolean)

    boolean



402
403
404
405
406
407
408
409
# File 'lib/core/terminal.rb', line 402

def self.prompt_yes?(question)
    begin
        prompt = TTY::Prompt.new
        return prompt.yes?(display_prompt_text(question))
    rescue
        prompt_yes?(question)
    end
end

.prompt_yes_no(title = nil, message = nil, confirmation_message = nil, preceding_blank_line = true) ⇒ Object

Gives a prompt where ‘y/Y’ will return TRUE, ‘n/N’ will return false, and ANY other key will do nothing.

Returns:

  • void



335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/core/terminal.rb', line 335

def self.prompt_yes_no(title = nil, message = nil, confirmation_message = nil, preceding_blank_line = true)
    title                = 'Please confirm YES or NO.' if title.nil?
    confirmation_message = 'Would you like to continue?' if confirmation_message.nil?
    puts if preceding_blank_line
    puts "  \x1B[38;5;231m\x1B[48;5;55m Confirm \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
    parse_messages(message)
    response = ''
    until %w[y Y n N x X a A].include? response
        response = Readline::readline("     \x1B[38;5;161m#{confirmation_message} \x1B[0m[y/n]\x1B[90m => \x1B[0m", true)
    end
    case response.downcase
        when 'y'
            puts "\n"
            return true
        when 'n'
            puts "\n"
            return false
        when 'a'
            Blufin::Terminal::error('Abandon ship!', ["You have chosen to \x1B[38;5;196mABORT\x1B[38;5;240m the script.", nil, 'Please note that whenever you do this, any scripted tasks which were running', 'will have been interrupted mid-script. This may (or may not) cause problems.'], true)
        when 'x'
            Blufin::Terminal::error('Abandon ship!', ["You have chosen to \x1B[38;5;196mABORT\x1B[38;5;240m the script.", nil, 'Please note that whenever you do this, any scripted tasks which were running', 'will have been interrupted mid-script. This may (or may not) cause problems.'], true)
        else
            raise RuntimeError, "Un-handled response: #{response.downcase}"
    end
end

.success(title = nil, message = nil, preceding_blank_line = true) ⇒ Object

Displays success message.

Returns:

  • void



201
202
203
204
205
# File 'lib/core/terminal.rb', line 201

def self.success(title = nil, message = nil, preceding_blank_line = true)
    puts if preceding_blank_line
    puts "  \x1B[38;5;231m\x1B[48;5;22m Success \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
    parse_messages(message)
end

.warning(title = nil, message = nil, preceding_blank_line = true) ⇒ Object

Displays warning message.

Returns:

  • void



209
210
211
212
213
# File 'lib/core/terminal.rb', line 209

def self.warning(title = nil, message = nil, preceding_blank_line = true)
    puts if preceding_blank_line
    puts "  \x1B[38;5;231m\x1B[48;5;130m Warning \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
    parse_messages(message)
end