class KLog::LogStructure

Log Structure is flexible logger for working through a complex object graph

Attributes

convert_data_to[R]
formatter[R]
graph[R]
graph_node[R]
graph_path[R]
heading[R]
heading_type[R]
indent[R]
key_format[R]
key_width[R]
line_width[R]
lines[R]
output_as[R]
output_file[R]
recursion_depth[R]
show_array_count[R]
title[R]
title_type[R]

Public Class Methods

new(opts) click to toggle source

Log a structure

Can handle Hash, Array, OpenStruct, Struct, DryStruct, Hash convertible custom classes

@option opts [String] :indent Indent with string, defaults to ' ' @option opts [String] :heading Log heading using logger.dynamic_heading @option opts [String] :heading_type :heading, :subheading, :section_heading @option opts [String] :line_width line width defaults to 80, but can be overridden here @option opts [String] :key_width key width defaults to 30, but can be overridden here @option opts [String] :formatter is a complex configuration for formatting different data within the structure

# File lib/k_log/log_structure.rb, line 39
def initialize(opts)
  @indent           = opts[:indent] || '  '
  @title            = opts[:title]
  @title_type       = opts[:title_type] || :heading

  @heading          = opts[:heading]
  @heading_type     = opts[:heading_type] || :heading
  puts ':heading should be :title'              if opts[:heading]
  puts ':heading_type should be :title_type'    if opts[:heading_type]

  @formatter        = opts[:formatter]          || {}
  @graph            = parse_graph(opts[:graph]  || {})
  @convert_data_to  = opts[:convert_data_to]    || :raw # by default leave data as is

  @line_width       = opts[:line_width]         || 80
  @key_width        = opts[:key_width]          || 30
  @show_array_count = opts[:show_array_count]   || false
  @output_as        = opts[:output_as]          || [:console]
  @output_as        = [@output_as]              unless @output_as.is_a?(Array)
  @output_file      = opts[:output_file]

  @recursion_depth  = 0
  @key_format       = nil
  @graph_path       = []
  @lines            = []

  update_indent_label
end

Public Instance Methods

add_line(line) click to toggle source
# File lib/k_log/log_structure.rb, line 102
def add_line(line)
  @lines << line
end
add_lines(lines) click to toggle source
# File lib/k_log/log_structure.rb, line 98
def add_lines(lines)
  @lines += lines
end
clean_content() click to toggle source
# File lib/k_log/log_structure.rb, line 88
def clean_content
  # remove color escape codes
  @clean_content ||= content.gsub(/\x1B\[\d*m/, '')
end
clean_lines() click to toggle source
# File lib/k_log/log_structure.rb, line 93
def clean_lines
  # remove color escape codes
  lines.flat_map { |line| line.gsub(/\x1B\[\d*m/, '').split("\n") }
end
content() click to toggle source
# File lib/k_log/log_structure.rb, line 84
def content
  @content ||= lines.join("\n")
end
l() click to toggle source
# File lib/k_log/log_structure.rb, line 68
def l
  @l ||= KLog::LogUtil.new(KLog.logger)
end
log(data) click to toggle source
# File lib/k_log/log_structure.rb, line 72
def log(data)
  log_heading(title, title_type) if title

  data = convert_data(data)

  log_data(data)

  add_line(KLog::LogHelper.line(line_width))

  render_output
end

Private Instance Methods

convert_data(data) click to toggle source

convert_data_to: :open_struct

# File lib/k_log/log_structure.rb, line 256
def convert_data(data)
  return KUtil.data.to_open_struct(data)  if convert_data_to == :open_struct

  data
end
data_enumerator(data) click to toggle source

format_config = @formatter if format_config.nil? && @recursion_depth.zero?

# File lib/k_log/log_structure.rb, line 110
def data_enumerator(data)
  return data.attributes if data.respond_to?(:attributes)

  data
end
depth_down() click to toggle source
# File lib/k_log/log_structure.rb, line 239
def depth_down
  @recursion_depth = recursion_depth + 1
  update_indent_label
end
depth_up() click to toggle source
# File lib/k_log/log_structure.rb, line 244
def depth_up
  @recursion_depth = recursion_depth - 1
  update_indent_label
end
indent_in() click to toggle source
# File lib/k_log/log_structure.rb, line 231
def indent_in
  @indent = "#{@indent}  "
end
indent_out() click to toggle source
# File lib/k_log/log_structure.rb, line 235
def indent_out
  @indent = indent.chomp('  ')
end
log_array(key, array) click to toggle source
# File lib/k_log/log_structure.rb, line 173
def log_array(key, array)
  'puts xmen' if graph_node.pry_at?(:before_array)

  items = array.clone
  items.select! { |item| graph_node.filter(item) }  if graph_node.filter?
  items = items.take(graph_node.take)               if graph_node.limited?
  items.sort!(&graph_node.sort)                     if graph_node.sort?

  'puts xmen' if graph_node.pry_at?(:before_array_print)

  return if items.length.zero? && graph_node.skip_empty?

  log_heading(graph_node.heading, graph_node.heading_type) if graph_node.heading

  if primitive?(items)
    add_line KLog::LogHelper.kv "#{@indent_label}#{key}", items.map(&:to_s).join(', ')
  else
    table_print items, tp_columns(items)

    # NEED SUPPORT FOR A configured ARRAY COUNT with width and label
    add_line KLog::LogHelper.kv key.to_s, items.count if show_array_count
  end
rescue StandardError => e
  KLog.logger.exception(e)
end
log_child_data(value) click to toggle source
# File lib/k_log/log_structure.rb, line 167
def log_child_data(value)
  depth_down
  log_data(value)
  depth_up
end
log_data(data) click to toggle source
# File lib/k_log/log_structure.rb, line 116
def log_data(data)
  data_enumerator(data).each_pair do |k, v|
    key = k.is_a?(String) ? k.to_sym : k

    graph_path.push(key)
    @graph_node = GraphNode.for(self, graph, graph_path)
    # l.kv 'key', "#{key.to_s.ljust(15)}#{graph_node.skip?.to_s.ljust(6)}#{@recursion_depth}"

    if graph_node.skip?
      # l.kv 'key', 'skipping...'
      @graph_path.pop
      next
    end

    'puts xmen' if graph_node.pry_at?(:before_value)

    value = graph_node.transform? ? graph_node.transform(v) : v

    'puts xmen' if graph_node.pry_at?(:after_value)
    if value.is_a?(OpenStruct) || value.respond_to?(:attributes)

      # l.kv 'go', 'open struct ->'
      'puts xmen' if graph_node.pry_at?(:before_structure)
      log_structure(key, value)
      # l.kv 'go', 'open struct <-'
    elsif value.is_a?(Array)
      # l.kv 'go', 'array ->'
      log_array(key, value)
      # l.kv 'go', 'array <-'
    else
      # l.kv 'go', 'value ->'
      'puts xmen' if graph_node.pry_at?(:before_kv)
      log_heading(graph_node.heading, graph_node.heading_type) if graph_node.heading
      add_line KLog::LogHelper.kv("#{@indent_label}#{key}", value, key_width)
      # l.kv 'go', 'value <-'
    end

    # l.line
    # @graph_node = graph.for_path(graph_path)
    # l.line
    @graph_path.pop
  end
  nil
end
log_heading(heading, heading_type) click to toggle source
# File lib/k_log/log_structure.rb, line 212
def log_heading(heading, heading_type)
  add_lines(KLog::LogHelper.dynamic_heading(heading, size: line_width, type: heading_type))
end
log_structure(key, value) click to toggle source
# File lib/k_log/log_structure.rb, line 161
def log_structure(key, value)
  log_heading(graph_node.heading, graph_node.heading_type) if graph_node.heading
  add_line(KLog::LogHelper.green("#{@indent_label}#{key}"))
  log_child_data(value)
end
parse_graph(data) click to toggle source
# File lib/k_log/log_structure.rb, line 262
def parse_graph(data)
  if data.is_a?(Hash)
    transform_hash = data.each_with_object({}) do |(key, value), new_hash|
      new_hash[key] = if key == :columns && value.is_a?(Array)
                        # Don't transform the table_print GEM columns definition as it must stay as a hash
                        value
                      else
                        parse_graph(value)
                      end
    end

    return OpenStruct.new(transform_hash.to_h)
  end

  return data.map { |o| parse_graph(o) }                               if data.is_a?(Array)
  return parse_graph(data.to_h)                                        if data.respond_to?(:to_h) # hash_convertible?(data)

  # Some primitave type: String, True/False or an ObjectStruct
  data
end
primitive?(items) click to toggle source
# File lib/k_log/log_structure.rb, line 207
def primitive?(items)
  item = items.first
  KUtil.data.basic_type?(item)
end
render_output() click to toggle source
# File lib/k_log/log_structure.rb, line 249
def render_output
  puts content                            if output_as.include?(:console)
  File.write(output_file, clean_content)  if output_as.include?(:file) && output_file
  # content
end
table_print(items, columns) click to toggle source
# File lib/k_log/log_structure.rb, line 199
def table_print(items, columns)
  io = TablePrintIo.new(self)

  tp.set :io, io
  tp items, columns
  tp.clear :io
end
tp_columns(items) click to toggle source
# File lib/k_log/log_structure.rb, line 216
def tp_columns(items)
  # Use configured array columns
  return graph_node.columns if graph_node.columns

  # Slow but complete list of keys
  # items.flat_map { |v| v.to_h.keys }.uniq

  items.first.to_h.keys
end
update_indent_label() click to toggle source
# File lib/k_log/log_structure.rb, line 226
def update_indent_label
  # puts "indent_label: #{indent} - #{@recursion_depth} - #{(indent * @recursion_depth)}"
  @indent_label = (indent * @recursion_depth)
end