class Locd::CLI::Command::Base

Abstract base for CLI interface commands using the `thor` gem.

@abstract

@see whatisthor.com/

Protected Instance Methods

agent_file(agent) click to toggle source
# File lib/locd/cli/command/base.rb, line 81
def agent_file agent
  agent.path.to_s.sub( /\A#{ Regexp.escape( ENV['HOME'] ) }/, '~' )
end
capture_stdout(&block) click to toggle source

Swap `$stdout` for a {StringIO}, call `block`, swap original `$stdout` back in and return the string contents.

'Cause we got that damn threaded logging going on, we want to write output all as one string using `$stdout.write`, but some stuff like {Thor::Shell::Basic#print_table} just write to {#stdout} (which resolves to `$stdout`) and don't offer an option to returned a formatted string instead.

This seems like the simplest way to handle it, though it may still run into trouble with the threads, we shall see…

@param [Proc] &block

Block to run that writes to `$stdout`

@return [String]

# File lib/locd/cli/command/base.rb, line 71
def capture_stdout &block
  io = StringIO.new
  stdout = $stdout
  $stdout = io
  block.call
  $stdout = stdout
  io.string
end
find_multi!(pattern) click to toggle source
# File lib/locd/cli/command/base.rb, line 269
def find_multi! pattern
  # Behavior depend on the `:all` option...
  if options[:all]
    # `:all` is set, so we find all the agents for the pattern, raising
    # if we don't find any
    Locd::Agent.find_all!(
      pattern,
      **option_kwds( groups: :pattern )
    ).values
  else
    # `:all` is not set, so we need to find exactly one or error
    [find_only!( pattern )]
  end
end
find_only!(pattern) click to toggle source

Find exactly one {Locd::Agent} for a `pattern`, using the any `:pattern` shared options provided, and raising if there are no matches or more than one.

@param pattern (see Locd::Agent.find_only!)

@return [Locd::Agent]

Matched agent.

@raise If more or less than one agent is matched.

# File lib/locd/cli/command/base.rb, line 264
def find_only! pattern
  Locd::Agent.find_only! pattern, **option_kwds( groups: :pattern )
end
on_run_error(error, command, args) click to toggle source

Hook called from {Thor::Command#run} when an error occurs. Errors are not recoverable from my understanding, so this method should provide necessary feedback to the user and exit with an error code for errors it expects and re-raise those that it doesn't.

# File lib/locd/cli/command/base.rb, line 231
def on_run_error error, command, args
  case error
  when NRSER::CountError
    if error.count == 0
      logger.error "No results"
    else
      logger.error "Too many results:\n\n#{ render_text error.value }\n"
    end
  
    # If the command supports `--all`, let them know that they can use it
    if command.options[:all]
      logger.info "You can use `--all` to to operate on ALL matches."
      logger.info "See `locd help #{ command.name }`\n"
    end
  
    exit 1
  else
    raise error
  end
end
render_color(src, lexer_name) click to toggle source
# File lib/locd/cli/command/base.rb, line 170
def render_color src, lexer_name
  formatter = Rouge::Formatters::Terminal256.new
  lexer = Rouge::Lexers.const_get( lexer_name ).new
  formatter.format( lexer.lex( src ) )
end
render_color?() click to toggle source
# File lib/locd/cli/command/base.rb, line 161
def render_color?
  if options.key? :color
    options[:color]
  else
    $stdout.isatty
  end
end
render_table(table) click to toggle source
# File lib/locd/cli/command/base.rb, line 86
def render_table table
  string = capture_stdout do
    print_table table.to_a
  end
  
  width = string.lines.map( &:length ).max
  
  return (
    string.lines[0] +
    "-" * width + "\n" +
    string.lines[1..-1].join +
    "---\n" +
    table.footer +
    "\n\n"
  )
  
  string.lines.each_with_index.map { |line, index|
    if index == 0
      '# ' + line
    else
      '  ' + line
    end
  }.join
end
render_text(object) click to toggle source
# File lib/locd/cli/command/base.rb, line 112
def render_text object
  # Does it look like an array/list?
  if NRSER.array_like? object
    # It does! Convert it to an actual array to make life easier
    array = object.to_a
    
    return "(EMPTY)" if array.empty?
    
    # Is it a list of agents?
    if array.all? { |entry| entry.is_a? Locd::Agent }
      # Ok, we want to display them. What mode are we in accoring to the
      # options?
      if options[:long]
        # We're in long-mode, render a table
        render_table agent_table( array )
      else
        # We're in regular mode, render each agent on it's own line by
        # recurring
        array.map( &method( __method__ ) ).join( "\n" )
      end
    
    # Is it a list of arrays?
    elsif array.all? { |entry| entry.is_a? Array }
      # It is, let's render that as a table
      render_table message
    else
      # It's not, let's just render each entry on it's own line by
      # recurring
      message.map( &method( __method__ ) ).join( "\n" )
    end
  
  else
    # It's not array-ish. Special-case {Locd::Agent} instances and render
    # the rest as `#to_s`
    case object
    when Locd::Agent::Site
      # TODO  Want to add options for this, but for now just render agent
      #       URLs 'cause they have the label in them and are
      #       `cmd+click`-able in iTerm2 to open, which is most useful
      object.url
    when Locd::Agent
      object.label
    else
      object.to_s
    end
  end
end
respond(message, title: nil) click to toggle source
# File lib/locd/cli/command/base.rb, line 177
def respond message, title: nil
  formatted = if options[:json]
    json = begin
      JSON.pretty_generate message
    rescue JSON::GeneratorError => error
      logger.debug "Error generating 'pretty' JSON, falling back to dump",
        error: error
      
      JSON.dump message
    end

    if render_color?
      render_color json, :JSON
    else
      json
    end
    
  elsif options[:yaml] || NRSER.hash_like?( message )
    # TODO  This sucks, but it's the easiest way to get "nice" YAML :/
    yaml = YAML.dump JSON.load( JSON.dump message )

    if render_color?
      # formatter = Rouge::Formatters::Terminal256.new
      # lexer = Rouge::Lexers::YAML.new
      # formatter.format( lexer.lex( yaml ) )
      render_color yaml, :YAML
    else
      yaml
    end
    
  else
    render_text message
  end
  
  if title
    formatted = (
      "# #{ title }\n#{ '#' * 78}\n#\n" + formatted
    )
  end
  
  # Be puts-like
  unless formatted[-1] == "\n"
    formatted = formatted + "\n"
  end
  
  $stdout.write formatted
end