module GreenHat::Cli

CLI Methods rubocop:disable Metrics/ModuleLength

Public Class Methods

auto() click to toggle source

Auto Complete

# File lib/greenhat/cli.rb, line 67
def self.auto
  word = reader.line.word

  if word.blank?
    help
  else
    auto_match(current_methods + current_submodules + cmd_list, word)
  end
end
auto_files(matches, word) click to toggle source

Auto Complete File Names

# File lib/greenhat/cli.rb, line 100
def self.auto_files(matches, word)
  if matches.count == 1
    auto_update(matches.first, word)

  # Print List of Options
  elsif matches.count > 1
    auto_update(common_substr(matches), word)
    puts matches.join("\t").pastel(:bright_green)
  end
end
auto_match(matches, word) click to toggle source
# File lib/greenhat/cli.rb, line 77
def self.auto_match(matches, word)
  matches.select! { |x| x[/^#{Regexp.escape(word)}/] }

  if submodule?
    submodule!
    reader.breaker = true
  # Only one Match!
  elsif matches.count == 1
    auto_update(matches.first, word)

  # Print List of Options
  elsif matches.count > 1
    puts matches.join("\t").pastel(:bright_green)

  # No other Matches
  else
    file_matches = files.select { |x| x[/^#{Regexp.escape(word)}/] }
    auto_files(file_matches, word) unless file_matches.empty?

  end
end
auto_update(match, word) click to toggle source

Handle Updates to Reader

# File lib/greenhat/cli.rb, line 112
def self.auto_update(match, word)
  add = match.split(word, 2).last
  reader.line.insert add unless add.nil?
end
available(list) click to toggle source
# File lib/greenhat/cli.rb, line 160
def self.available(list)
  list.map(&:to_s).map(&:downcase)
end
back() click to toggle source
# File lib/greenhat/cli.rb, line 294
def self.back
  move location[-2] if location.count > 1
end
bad_file(archive) click to toggle source
# File lib/greenhat/cli.rb, line 513
def self.bad_file(archive)
  puts color("Cannot find archive: #{archive}", :red)
  exit 1
end
clear_screen() click to toggle source
# File lib/greenhat/cli.rb, line 325
def self.clear_screen
  print cursor.clear_screen + cursor.move_to(0, 0)
end
cli_help() click to toggle source
# File lib/greenhat/cli.rb, line 338
def self.cli_help
  Shell.version
  puts
  puts 'Usage'.pastel(:yellow)
  puts '  greenhat  <sos-archive.tgz> <sos-archive2.tgz> '
  puts

  puts 'Options'.pastel(:yellow)
  puts '  --report, -r'.pastel(:green)
  puts '    Run `report` against archives and exit'
  puts

  puts '  --quiet, -r'.pastel(:green)
  puts '    Surpress GreenHat logging output'
  puts

  puts '  --load, -l'.pastel(:green)
  puts '    Automatically attempt to read/parse/preload all included files'
  puts

  puts '  --command, -c'.pastel(:green)
  puts '    Run and then exit a GreenHat Shell command'
  puts

  puts '  --version, -v'.pastel(:green)
  puts '    Print version and exit'
  puts
end
cmd() click to toggle source

Reader helper

# File lib/greenhat/cli.rb, line 165
def self.cmd
  @list.first
end
cmd?() click to toggle source
# File lib/greenhat/cli.rb, line 178
def self.cmd?
  cmd_list.any? cmd
end
cmd_list() click to toggle source

Special Command Overrides

# File lib/greenhat/cli.rb, line 174
def self.cmd_list
  ['help', '~', '..', 'back', 'clear']
end
cmd_run() click to toggle source
# File lib/greenhat/cli.rb, line 186
def self.cmd_run
  case cmd
  when 'help' then help
  when '~' then home
  when '..', 'back' then back
  when 'clear' then clear_screen
  end

  @list.shift
end
common_substr(strings) click to toggle source

Complete as much as possible comparison stackoverflow.com/a/2158481/1678507

# File lib/greenhat/cli.rb, line 119
def self.common_substr(strings)
  shortest = strings.min_by(&:length)
  maxlen = shortest.length
  maxlen.downto(0) do |len|
    0.upto(maxlen - len) do |start|
      substr = shortest[start, len]
      return substr if strings.all? { |str| str.include? substr }
    end
  end
end
current_location() click to toggle source
# File lib/greenhat/cli.rb, line 313
def self.current_location
  location.last
end
current_methods() click to toggle source

Reader to Get Last Location's Available Methods / Keep Helper Methods

# File lib/greenhat/cli.rb, line 299
def self.current_methods
  current_location.methods(false).map(&:to_s)
  # .reject { |x| x.include? '_help' }
end
current_submodule?() click to toggle source

Full Commands at the Submodule `disk summary` from `disk`

# File lib/greenhat/cli.rb, line 309
def self.current_submodule?
  current_location.to_s.demodulize.downcase == @list.first
end
current_submodules() click to toggle source
# File lib/greenhat/cli.rb, line 304
def self.current_submodules
  current_location.constants.map(&:to_s).map(&:downcase)
end
cursor() click to toggle source
# File lib/greenhat/cli.rb, line 6
def self.cursor
  @cursor = TTY::Cursor
end
default?() click to toggle source

Check for `default` method and files

# File lib/greenhat/cli.rb, line 236
def self.default?
  @list.any? { |x| x.include?(cmd) } && current_methods.include?('default')
end
did_you_mean() click to toggle source
# File lib/greenhat/cli.rb, line 131
def self.did_you_mean
  dictionary = current_methods + current_submodules + cmd_list

  # all.select! { |x| x.include? cmd }

  all = DidYouMean::SpellChecker.new(dictionary: dictionary).correct(cmd)

  if all.empty?
    puts [
      'Command not found '.pastel(:red),
      cmd.pastel(:bright_yellow),
      ' ('.pastel(:bright_black),
      'help'.pastel(:blue),
      ' to show available commands'.pastel(:bright_black),
      ')'.pastel(:bright_black)
    ].join
  else
    puts "#{'Did you mean?'.pastel(:cyan)}  #{all.join("\t").pastel(:green)}"
  end
end
files() click to toggle source
# File lib/greenhat/cli.rb, line 15
def self.files
  @files ||= Thing.all.map(&:name).uniq
end
flags?(list = [], flags = {}) click to toggle source

Helper to Simplify checking flags

# File lib/greenhat/cli.rb, line 414
def self.flags?(list = [], flags = {})
  list.any? { |x| flags.key? x }
end
help(long = true) click to toggle source

General Helper

# File lib/greenhat/cli.rb, line 241
def self.help(long = true)
  if current_location.methods(false).count.zero?
    puts 'No Commands'.pastel(:red)
  else
    puts 'Commands: '
    current_location.methods(false).map(&:to_s).sort.each do |item|
      next if %w[default help].any? { |x| x == item }

      puts "=> #{item.to_s.pastel(:blue)}"
    end

  end

  puts ''

  if current_location.constants.count.zero?
    puts 'No Submodules'.pastel(:red)
  else
    puts 'Submodules'
    current_location.constants.each do |item|
      puts "-> #{item.to_s.demodulize.downcase.pastel(:yellow)}"
    end
  end

  # Execute local help last if exists
  if current_methods.include?('help') && long
    puts
    current_location.send(:help)
  end

  puts ''
end
home() click to toggle source
# File lib/greenhat/cli.rb, line 290
def self.home
  move Shell
end
load_files(files) click to toggle source
# File lib/greenhat/cli.rb, line 467
def self.load_files(files)
  # TODO: Web Helpers?
  # suppress_output { GreenHat::Web.start }

  # Don't double up on archives / Only Existing files
  puts 'Loading Archives'.pastel(:blue) unless Cli.quiet
  files.uniq.each do |file|
    next unless File.exist?(file)

    puts "- #{file}".pastel(:magenta) unless Cli.quiet
    ArchiveLoader.load file
  end
end
location() click to toggle source
# File lib/greenhat/cli.rb, line 274
def self.location
  @location ||= [Shell]
end
location_reader() click to toggle source
# File lib/greenhat/cli.rb, line 317
def self.location_reader
  location.map(&:to_s).map(&:downcase).map(&:demodulize).join('/').gsub('shell', '~').pastel(:blue)
end
menu() click to toggle source

TODO

move(spot) click to toggle source

Replace Location

# File lib/greenhat/cli.rb, line 279
def self.move(spot)
  @location = [spot]
end
output() click to toggle source
# File lib/greenhat/cli.rb, line 496
def self.output
  'greenhat.html'
end
post_args(flags) click to toggle source

Arguments to be handled after general processing

# File lib/greenhat/cli.rb, line 396
def self.post_args(flags)
  # Run report and exit / Don't Clear for reports
  if flags?(%i[report r], flags)
    @quiet = true
    # Don't Use Pagination
    GreenHat::Shell.report(['--raw'])
    exit 0
  else
    clear_screen
  end

  # CTL Tails need to be parsed for new 'things'
  Thing.where(kind: :gitlab_tail)&.map(&:process)

  Thing.all.each(&:process) if flags?(%i[load l], flags)
end
pre_args(flags, files) click to toggle source

Arguments before general processing

# File lib/greenhat/cli.rb, line 368
def self.pre_args(flags, files)
  # Help
  if flags?(%i[help h], flags)
    cli_help
    exit 0
  end

  # Version
  if flags?(%i[version v], flags)
    Shell.version
    exit 0
  end

  # Quiet Flag
  @quiet = true if flags?(%i[quiet q], flags)

  # rubocop:disable Style/GuardClause
  # MIA Files
  if files.empty? || files.count { |x| File.exist? x }.zero?
    puts "No arguments or files don't exist".pastel(:red)
    puts 'Usage: greenhat  <sos-archive.tgz> <sos-archive2.tgz>'
    cli_help
    Shell.version
  end
  # rubocop:enable Style/GuardClause
end
process() click to toggle source

Auto/Run Process - Populate and parse: @list

# File lib/greenhat/cli.rb, line 153
def self.process
  line = reader.line
  @list = Shellwords.split line.text
rescue StandardError => e
  puts "#{'Invalid Command'.pastel(:red)}: #{e.message.pastel(:green)}"
end
prompt() click to toggle source
# File lib/greenhat/cli.rb, line 500
def self.prompt
  TTY::Prompt.new(active_color: :cyan)
end
quiet() click to toggle source
# File lib/greenhat/cli.rb, line 329
def self.quiet
  @quiet
end
quiet!() click to toggle source

Toggle Quiet Settings

# File lib/greenhat/cli.rb, line 334
def self.quiet!
  @quiet = !@quiet
end
reader() click to toggle source

Input Loop Listener

# File lib/greenhat/cli.rb, line 11
def self.reader
  @reader ||= reader_setup
end
reader_setup() click to toggle source
# File lib/greenhat/cli.rb, line 19
def self.reader_setup
  reader = TTY::Reader.new(history_duplicates: false, interrupt: -> { back })

  Settings.cmd_history_clean.each do |line|
    reader.add_to_history(line)
  end

  # Blank?
  reader.add_to_history ''

  # Remove Line
  reader.on(:keyctrl_u) do |_event|
    reader.line.remove reader.line.text.size
  end

  # Navigate Word Left
  reader.on(:keyctrl_left) { reader.line.move_word_left }

  # Navigate Word Right
  reader.on(:keyctrl_right) { reader.line.move_word_right }

  # Navigate Beginning
  reader.on(:keyctrl_a) { reader.line.move_to_start }

  # Navigate End
  reader.on(:keyctrl_e) { reader.line.move_to_end }

  reader.on(:keyback_tab) { back }

  reader.on(:keytab) do
    process
    auto
  end

  reader.instance_variable_get(:@history)

  # DEBUG PRY
  reader.on(:keyctrl_p) do |event|
    # rubocop:disable Lint/Debugger
    binding.pry
    # rubocop:enable Lint/Debugger
  end
  reader
end
readline_notch() click to toggle source
# File lib/greenhat/cli.rb, line 321
def self.readline_notch
  "#{'greenhat'.pastel(:bright_black)} #{location_reader} ยป "
end
run() click to toggle source
# File lib/greenhat/cli.rb, line 207
def self.run
  return true if @list.blank?

  if cmd?
    cmd_run
  elsif run?
    run!

    return true # End Early

  elsif current_submodule?
    @list.shift
  elsif submodule?
    submodule!
  # Prepend Default if exists
  elsif default?
    @list.unshift 'default'
  else
    did_you_mean
    @list.shift
  end

  run # Loop Back
rescue StandardError => e
  LogBot.fatal('CLI Run', e.message)
  puts e.backtrace[0..4].join("\n").pastel(:red)
end
run!() click to toggle source

Final Command Execution

# File lib/greenhat/cli.rb, line 198
def self.run!
  # Shift to prevent duplicate Runs / Arity for identifying if Method has params
  if current_location.method(@list.first).arity.zero?
    current_location.send(@list.shift.clone)
  else
    current_location.send(@list.shift.clone, @list.clone)
  end
end
run?() click to toggle source
# File lib/greenhat/cli.rb, line 169
def self.run?
  current_methods.include? cmd
end
run_command(args) click to toggle source

Run and Exit Command Helper

# File lib/greenhat/cli.rb, line 455
def self.run_command(args)
  args.each do |arg|
    # Quick Validation
    next unless run_command?(arg) && !arg.value.empty?

    reader.line = ''
    @list = Shellwords.split arg.value
    run
  end
  exit 0
end
run_command?(arg) click to toggle source
# File lib/greenhat/cli.rb, line 448
def self.run_command?(arg)
  %i[command c].any? do |x|
    arg[:field] == x
  end
end
start(raw) click to toggle source

If no arguments Supplied Print and quit - rather than nasty exception

# File lib/greenhat/cli.rb, line 419
def self.start(raw)
  Settings.start
  files, flags, args = Args.parse(raw)
  pre_args(flags, files)
  load_files files
  run_command(args) if args.any? { |arg| run_command?(arg) }
  post_args(flags)

  value ||= '' # Empty Start

  loop do
    line = reader.read_line(readline_notch, value: value)
    value = '' # Remove Afterwards

    if reader.breaker
      value = line
      puts ''
      next
    end

    break if line =~ /^exit/i

    Settings.cmd_add(line) unless line.blank?

    process
    run
  end
end
submodule!() click to toggle source

Append to Location

# File lib/greenhat/cli.rb, line 284
def self.submodule!
  spot = @list.shift
  reader.line.replace @list.join(' ')
  @location << current_location.const_get(spot.capitalize.to_sym)
end
submodule?() click to toggle source
# File lib/greenhat/cli.rb, line 182
def self.submodule?
  current_submodules.include? @list.first
end
suppress_output() { || ... } click to toggle source
# File lib/greenhat/cli.rb, line 481
def self.suppress_output
  original_stderr = $stderr.clone
  original_stdout = $stdout.clone
  $stderr.reopen(File.new('/dev/null', 'w'))
  $stdout.reopen(File.new('/dev/null', 'w'))
  yield
ensure
  $stdout.reopen(original_stdout)
  $stderr.reopen(original_stderr)
end
template() click to toggle source
# File lib/greenhat/cli.rb, line 492
def self.template
  "#{__dir__}/views/index.slim"
end