class WavefrontDisplay::Base

Print human-friendly output. If a command requires a dedicated handler to format its output, define a method with the same name as that which fetches the data, in a WavefrontDisplay class, extending this one.

We provide long_output() and multicolumn() methods to solve standard formatting problems. To use them, define a do_() method but rather than printing the output, have it call the method.

Attributes

data[R]
options[R]
raw[R]

Public Class Methods

new(raw_response, options = {}) click to toggle source

@param raw_response [Map, Hash, Array] the data returned by the SDK

response.

@param options [Hash] options from docopt

# File lib/wavefront-cli/display/base.rb, line 25
def initialize(raw_response, options = {})
  @raw = raw_response

  data = if raw_response.respond_to?(:items)
           raw_response.items
         else
           raw_response
         end

  @data = prioritize_keys(data, priority_keys)
  @options = options
  @printer_opts = {}
end

Public Instance Methods

_prioritize_keys(data, keys) click to toggle source

Move the given fields to the start of a Hash or Map @param data [Hash, Map] @param keys [Array] keys to float @return [Hash, Map]

# File lib/wavefront-cli/display/base.rb, line 130
def _prioritize_keys(data, keys)
  keys.each.with_object(data.is_a?(Map) ? Map.new : {}) do |k, a|
    next unless data.key?(k)

    a[k] = data[k]
    data.delete(k)
  end.merge(data)
rescue NoMethodError
  data
end
display_brief_freetext_results() click to toggle source

For freetext searches, we just display the matching fields in “brief” mode.

# File lib/wavefront-cli/display/base.rb, line 263
def display_brief_freetext_results
  search_keys = freetext_keys

  data.map! do |d|
    mf = d.select do |_k, v|
      search_keys.any? { |s| v.to_s.include?(s) }
    end

    { id: d[:id], matching_fields: mf.to_h.keys }
  end

  multicolumn(:id, :matching_fields)
end
do_delete() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 236
def do_delete
  puts "Deleted #{friendly_name} '#{options[:'<id>']}'."
end
do_import() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 231
def do_import
  puts "Imported #{friendly_name}."
  long_output
end
do_list() click to toggle source

The following do_ methods are default handlers called following their namesake operation in the corresponding WavefrontCli class. They can be overriden in the inheriting class.

# File lib/wavefront-cli/display/base.rb, line 219
def do_list
  long_output
end
do_list_brief() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 227
def do_list_brief
  multicolumn(:id, :name)
end
do_list_fields() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 223
def do_list_fields
  multicolumn(*filter_fields_as_arr.map(&:to_sym))
end
do_queries() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 332
def do_queries
  if options[:brief]
    multicolumn(:condition)
  else
    multicolumn(:id, :condition)
  end
end
do_search_brief() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 244
def do_search_brief
  search_keys = search_display_keys

  if search_keys.include?(:freetext)
    display_brief_freetext_results
  else
    multicolumn(*search_keys)
  end
rescue KeyError
  raise WavefrontCli::Exception::ImpossibleSearch
end
do_search_fields() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 256
def do_search_fields
  do_list_fields
end
do_tag_add() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 298
def do_tag_add
  puts "Tagged #{friendly_name} '#{options[:'<id>']}'."
end
do_tag_clear() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 306
def do_tag_clear
  puts "Cleared tags on #{friendly_name} '#{options[:'<id>']}'."
end
do_tag_delete() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 302
def do_tag_delete
  puts "Deleted tag from #{friendly_name} '#{options[:'<id>']}'."
end
do_tag_pathsearch() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 322
def do_tag_pathsearch
  if data.empty?
    puts 'No matches.'
  elsif options[:long]
    long_output
  else
    multicolumn(:id, :name)
  end
end
do_tag_set() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 310
def do_tag_set
  puts "Set tags on #{friendly_name} '#{options[:'<id>']}'."
end
do_tags() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 314
def do_tags
  if data.empty?
    puts "No tags set on #{friendly_name} '#{options[:'<id>']}'."
  else
    data.sort.each { |t| puts t }
  end
end
do_undelete() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 240
def do_undelete
  puts "Undeleted #{friendly_name} '#{options[:'<id>']}'."
end
drop_fields(*keys) click to toggle source

Modify, in-place, the data structure to remove fields which we deem not of interest to the user.

@param keys [Symbol] keys you do not wish to be shown. @return [Nil]

# File lib/wavefront-cli/display/base.rb, line 346
def drop_fields(*keys)
  if data.is_a?(Array)
    data.each { |i| i.delete_if { |k, _v| keys.include?(k.to_sym) } }
  else
    data.delete_if { |k, _v| keys.include?(k.to_sym) }
  end
end
filter_data(data, fields) click to toggle source

@return [Array] modified version of data. Each hash will

contain only the fields given in `fields`, in the given
order

@param data [Array] @param fields [Array]

# File lib/wavefront-cli/display/base.rb, line 78
def filter_data(data, fields)
  data.map! do |d|
    fields.each_with_object({}) { |f, a| a[f] = d[f] if d.key?(f) }
  end
end
filter_fields_as_arr() click to toggle source

@return [Array] filter fields from -O option

# File lib/wavefront-cli/display/base.rb, line 210
def filter_fields_as_arr
  options[:fields].split(',')
end
freetext_keys() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 277
def freetext_keys
  options[:'<condition>'].map { |c| c.split(SEARCH_SPLIT, 2).last }
end
friendly_name() click to toggle source

return [String] the name of the thing we're operating on, like

'alert' or 'dashboard'.
# File lib/wavefront-cli/display/base.rb, line 203
def friendly_name
  self.class.name.split('::').last.gsub(/([a-z])([A-Z])/, '\\1 \\2')
      .downcase
end
human_time(time, force_utc = false) click to toggle source

Make a time human-readable. Automatically deals with epoch seconds and epoch milliseconds

param t [Integer, String] a timestamp. If it's a string, it is

converted to an int.

param force_utc [Boolean] force output in UTC. Currently only

used for unit tests.

return [String] a human-readable timestamp

# File lib/wavefront-cli/display/base.rb, line 384
def human_time(time, force_utc = false)
  raise ArgumentError unless time.is_a?(Numeric) || time.is_a?(String)

  return 'FOREVER' if time == -1

  str = time.to_s
  fmt, out_fmt = time_formats(str)
  ret = DateTime.strptime(str, fmt).to_time

  ret = force_utc ? ret.utc : ret.localtime
  ret.strftime(out_fmt)
end
index_of_final_item() click to toggle source

Return the offset of the final item in view.

# File lib/wavefront-cli/display/base.rb, line 183
def index_of_final_item
  raw.limit.positive? ? raw.offset + raw.limit - 1 : 0
end
key_width(hash = {}, pad = 2) click to toggle source

Give it a key-value hash, and it will return the size of the first column to use when formatting that data.

@param hash [Hash] the data for which you need a column width @param pad [Integer] the number of spaces you want between columns @return [Integer] length of longest key + pad

# File lib/wavefront-cli/display/base.rb, line 194
def key_width(hash = {}, pad = 2)
  return 0 if hash.keys.empty?

  hash.keys.map(&:size).max + pad
end
long_output(fields = nil, modified_data = nil) click to toggle source

Default display method for 'describe' and long-list methods. Wraps around #_two_columns() giving you the chance to modify @data on the fly

@param fields [Array] a list of fields you wish to

display. If this is nil, all fields are displayed.

@param modified_data [Hash, Array] lets you modify @data

in-line. If this is truthy, it is used. Passing
modified_data means that any fields parameter is ignored.
# File lib/wavefront-cli/display/base.rb, line 151
def long_output(fields = nil, modified_data = nil)
  if data.empty? || modified_data&.empty?
    puts 'No data.'
  else
    require_relative 'printer/long'
    puts WavefrontDisplayPrinter::Long.new(data, fields, modified_data,
                                           @printer_opts)
    pagination_line
  end
end
multicolumn(*columns) click to toggle source
# File lib/wavefront-cli/display/base.rb, line 162
def multicolumn(*columns)
  require_relative 'printer/terse'
  puts WavefrontDisplayPrinter::Terse.new(data, columns)
  pagination_line
end
pagination_line() click to toggle source

if this is a section of a larger dataset, say so

# File lib/wavefront-cli/display/base.rb, line 170
def pagination_line
  return unless raw.respond_to?(:moreItems) && raw.moreItems == true

  puts format('List shows items %<first>d to %<last>d. ' \
              'Use -o and -L for more.',
              first: raw.offset,
              last: index_of_final_item)
rescue StandardError
  puts 'List shows paginated output. Use -o and -L for more.'
end
prioritize_keys(data, keys) click to toggle source
# File lib/wavefront-cli/display/base.rb, line 119
def prioritize_keys(data, keys)
  return _prioritize_keys(data, keys) unless data.is_a?(Array)

  data.map { |e| _prioritize_keys(e, keys) }
end
priority_keys() click to toggle source

Keys which we wish to float to the top of descriptions and long listing objects. Subclasses may define their own.

# File lib/wavefront-cli/display/base.rb, line 115
def priority_keys
  %i[id name identifier]
end
quoted(things) click to toggle source

@param things [Array] @return [String] all “things”, strong-quoted and comma-separated

# File lib/wavefront-cli/display/base.rb, line 415
def quoted(things)
  things.map { |item| "'#{item}'" }.join(', ')
end
readable_time(*keys) click to toggle source

Modify, in-place, the @data structure to make times human-readable. Automatically handles second and millisecond epoch times. Currently only operates on top-level keys.

param keys [Symbol, Array] the keys you wish to be

turned into readable times.

return [Nil]

# File lib/wavefront-cli/display/base.rb, line 362
def readable_time(*keys)
  keys.each { |k| data[k] = human_time(data[k]) if data.key?(k) }
end
readable_time_arr(*keys) click to toggle source

As for readable_time, but when @data is an array. For instance in “firing” alerts

# File lib/wavefront-cli/display/base.rb, line 369
def readable_time_arr(*keys)
  data.map do |row|
    keys.each { |k| row[k] = human_time(row[k]) if row.key?(k) }
  end
end
run(method) click to toggle source

find the correct method to deal with the output of the user's command.

rubocop:disable Metrics/MethodLength

# File lib/wavefront-cli/display/base.rb, line 43
def run(method)
  if method == 'do_list'
    run_list
  elsif method == 'do_search'
    run_search
  elsif respond_to?("#{method}_brief") && !options[:long]
    send("#{method}_brief")
  elsif respond_to?(method)
    send(method)
  else
    long_output
  end
end
run_error(method) click to toggle source

Display classes can provide a do_method_code() method, which handles <code> errors when running do_method(). (Code is 404 etc.)

@param method [Symbol] the error method we wish to call

# File lib/wavefront-cli/display/base.rb, line 105
def run_error(method)
  return unless respond_to?(method)

  send(method)
  exit 1
end
run_list() click to toggle source

Choose the correct list handler. The user can specifiy a long listing with the –long options.

# File lib/wavefront-cli/display/base.rb, line 61
def run_list
  if options[:long]
    @data = filter_data(data, filter_fields_as_arr) if options[:fields]
    do_list
  elsif options[:fields]
    do_list_fields
  else
    do_list_brief
  end
end
search_display_keys() click to toggle source
# File lib/wavefront-cli/display/base.rb, line 281
def search_display_keys
  ([search_identifier_key] + options[:'<condition>'].map do |c|
    c.split(SEARCH_SPLIT, 2).first.to_sym
  end).uniq
end
search_identifier_key() click to toggle source

Most objects refer to themselves by 'id'. Some, like accounts, don't. Override here.

# File lib/wavefront-cli/display/base.rb, line 290
def search_identifier_key
  :id
end
time_formats(str) click to toggle source

How do we format a timestamp? @param str [String] an epoch timestamp, as a string @return [String, String] DateTime formatter, strptime formatter

# File lib/wavefront-cli/display/base.rb, line 401
def time_formats(str)
  case str
  when /^\d{13}$/
    ['%Q', HUMAN_TIME_FORMAT_MS]
  when /^\d{10}$/
    ['%s', HUMAN_TIME_FORMAT]
  else
    raise ArgumentError
  end
end