class Conjur::CLI::Complete

Class for generating ‘conjur` bash completions

Attributes

arg_words[R]
command_words[R]
commands[R]
current_word_index[R]
flag_words[R]
line[R]
switch_words[R]
words[R]

Public Class Methods

new(line, point=nil) click to toggle source
# File lib/conjur/complete.rb, line 31
def initialize line, point=nil
  @line = line
  @words = tokenize_cmd @line
  point ||= @line.length
  @current_word_index=(tokenize_cmd @line.slice(0,point)).length-1
  # fix arrays for empty "current word"
  # ie "conjur group list "
  if @line.match(/[ =]$/)
    @words << ''
    @current_word_index += 1
  end
  @commands,
  @switch_words,
  @flag_words,
  @arg_words = parse_command @words, @current_word_index
  @command_words = @commands
                   .drop(1)
                   .map(&:name)
                   .map(&:to_s)
                   .unshift('conjur')
end

Public Instance Methods

classify_word(word, command) click to toggle source
# File lib/conjur/complete.rb, line 116
def classify_word word, command
  if word.start_with? '-'
    sym = flag_to_sym word
    if switches(command).member? sym
      :switch
    else
      :flag
    end
  else
    if subcommands(command).has_key? word.to_sym
      :subcommand
    else
      :argument
    end
  end
end
complete(kind) click to toggle source
# File lib/conjur/complete.rb, line 133
def complete kind
  kind = kind.to_s.downcase.gsub(/[^a-z]/, '')
  case kind
  when 'resource'
    complete_resource
  when 'role'
    complete_role
  when 'file'
    complete_file current_word
  when 'hostname'
    complete_hostname
  else
    complete_resource kind if [
      'group',
      'user',
      'variable',
      'host',
      'layer',
    ].member? kind
  end or []
end
complete_args(cmd, prev, num_args) click to toggle source
# File lib/conjur/complete.rb, line 176
def complete_args cmd, prev, num_args
  kind=nil
  if prev.start_with? '-'
    flag_name=flag_to_sym prev
    flag = cmd.flags[flag_name]
    desc = flag.argument_name if defined? flag.argument_name
    kind = desc.to_s.downcase
  else
    desc = cmd.arguments_description if defined? cmd.arguments_description
    kind = desc.to_s.downcase.split[num_args-1]
  end
  complete kind
end
complete_file(word) click to toggle source
# File lib/conjur/complete.rb, line 209
def complete_file word
  # use Bash's file completion for compatibility
  `bash -c "compgen -f #{word}"`.shellsplit
end
complete_flags(cmd) click to toggle source

generate completions for the switches and flags of a Conjur::CLI::Command

@param cmd [Conjur::CLI::Command] command for which to search for flags

and switches

@return [Array] completion words

# File lib/conjur/complete.rb, line 160
def complete_flags cmd
  cmd.flags.values.map do |flag|
    candidates = [flag.name]
    candidates += flag.aliases if flag.aliases
    candidates.map do |c|
      "-#{'-' if c.length > 1}#{c}#{'=' if c.length > 1}"
    end
  end + cmd.switches.values.map do |switch|
    candidates = [switch.name]
    candidates += switch.aliases if switch.aliases
    candidates.map do |c|
      "-#{'-' if c.length > 1}#{c}"
    end
  end
end
complete_hostname() click to toggle source
# File lib/conjur/complete.rb, line 214
def complete_hostname
  `bash -c "compgen -A hostname"`.shellsplit
end
complete_resource(resource_kind=nil) click to toggle source
# File lib/conjur/complete.rb, line 190
def complete_resource resource_kind=nil
  Conjur::Command.api.resources({kind: resource_kind})
    .map do |r|
    res = Resource.new r.attributes['id']
    if resource_kind
      res.name
    else
      res.to_s
    end
  end
end
complete_role() click to toggle source
# File lib/conjur/complete.rb, line 202
def complete_role
  Conjur::Command.api.current_role(Conjur.configuration.account).memberships
    .map { |r| Resource.new(r.id) }
    .reject { |r| r.kind.start_with? '@' }
    .map(&:to_s)
end
completions() click to toggle source
# File lib/conjur/complete.rb, line 218
def completions
  prev = current_word(-1)
  if current_word.start_with? '-'
    complete_flags @commands.last
  else
    (subcommands @commands.last).keys.map(&:to_s) +
      (complete_args @commands.last, prev, @arg_words.length)
  end.flatten
    .select do |candidate|
    candidate.start_with? current_word.sub('\:',':') end
    .map do |candidate|
    # if the current word is colon separated, strip its complete tokens
    # eg. for --as-role=user:ryanprior, we're actually only completing 'ryanprior'
    # because bash treats 'user' as a separate word
    non_escaped_colon_regex = /(?<!\\):/
    num_tokens = current_word.split(non_escaped_colon_regex).length
    if num_tokens > 1
      candidate = candidate
                  .split(non_escaped_colon_regex)
                  .drop(num_tokens-1)
                  .join(':')
    end
    "#{candidate}#{' ' if not candidate.end_with? '='}" end
end
current_word(offset=0) click to toggle source
# File lib/conjur/complete.rb, line 53
def current_word offset=0
  @words[@current_word_index + offset]
end
flag_to_sym(flag) click to toggle source
# File lib/conjur/complete.rb, line 86
def flag_to_sym flag
  flag.match(/--?([^=]+)=?/)[1].to_sym
end
parse_command(words, current_word_index) click to toggle source
# File lib/conjur/complete.rb, line 90
def parse_command words, current_word_index
  command = Conjur::CLI
  commands = [command]
  switches = []
  flags = []
  arguments = []
  index = 1
  until index >= current_word_index do
    word = words[index]
    case classify_word word, command
    when :switch
      switches.push word
    when :flag
      flags.push [word, words[index+1]]
      index += 1
    when :subcommand
      command = command.commands[word.to_sym]
      commands.push command
    when :argument
      arguments.push word
    end
    index += 1
  end
  return commands, switches, flags, arguments
end
subcommands(cmd) click to toggle source

Generate array of subcommands for which documentation is not hidden

@param cmd [Conjur::CLI::Command] the command to search @return [Array] the subcommands

# File lib/conjur/complete.rb, line 61
def subcommands cmd
  cmd.commands.select do |_, c|
    c.nodoc.nil?
  end
end
switches(cmd) click to toggle source

Generate array of symbols representing switches for cmd and their aliases

@param cmd [Conjur::CLI::Command] the command to search @return [Array] the symbols representing switches and their aliases

# File lib/conjur/complete.rb, line 72
def switches cmd
  cmd.switches.map { |_,switch|
    [switch.name] + (switch.aliases or [])
  }.flatten
end
tokenize_cmd(line) click to toggle source

Split line according on spaces and after ‘=’

@param line [String] to split @return [Array] the substrings

# File lib/conjur/complete.rb, line 82
def tokenize_cmd line
  line.split(/ |(?<==)/)
end