lib/sql_tracker/report.rb
module SqlTracker
class Report
attr_accessor :raw_data
attr_accessor :terminal_width
def initialize(data)
self.raw_data = data
end
def valid?
return false unless raw_data.key?('format_version')
return false unless raw_data.key?('data')
return false if raw_data['data'].nil? || raw_data['data'].empty?
sample = raw_data['data'].values.first
%w(sql count source).each do |key|
return false unless sample.key?(key)
end
true
end
def version
raw_data['format_version']
end
def data
raw_data['data']
end
def print_text(options)
self.terminal_width = options.fetch(:terminal_width)
f = STDOUT
f.puts '=================================='
f.puts "Total Unique SQL Queries: #{data.keys.size}"
f.puts '=================================='
f.printf(
"%-#{count_width}s | %-#{duration_width}s | %-#{sql_width}s | Source\n",
'Count', 'Avg Time (ms)', 'SQL Query'
)
f.puts '-' * terminal_width
sorted_data = sort_data(data.values, options[:sort_by])
sorted_data.each do |row|
chopped_sql = wrap_text(row['sql'], sql_width)
source_list = wrap_list(row['source'].uniq, sql_width - 10)
avg_duration = row['duration'].to_f / row['count']
total_lines = if chopped_sql.length >= source_list.length
chopped_sql.length
else
source_list.length
end
(0...total_lines).each do |line|
count = line == 0 ? row['count'].to_s : ''
duration = line == 0 ? avg_duration.round(2) : ''
source = source_list.length > line ? source_list[line] : ''
query = row['sql'].length > line ? chopped_sql[line] : ''
f.printf(row_format, count, duration, query, source)
end
f.puts '-' * terminal_width
end
end
def row_format
"%-#{count_width}s | %-#{duration_width}s | %-#{sql_width}s | %-#{sql_width}s\n"
end
def sort_data(data, sort_by)
data.sort_by do |d|
if sort_by == 'duration'
-d['duration'].to_f / d['count']
else
-d['count']
end
end
end
def +(other)
unless self.class == other.class
raise ArgumentError, "cannot combine #{other.class}"
end
unless version == other.version
raise ArgumentError, "cannot combine v#{version} with v#{other.version}"
end
r1 = data
r2 = other.data
merged = (r1.keys + r2.keys).uniq.each_with_object({}) do |id, memo|
if !r1.key?(id)
memo[id] = r2[id]
elsif r2.key?(id)
memo[id] = r1[id]
memo[id]['count'] += r2[id]['count']
memo[id]['duration'] += r2[id]['duration']
memo[id]['source'] += r2[id]['source']
else
memo[id] = r1[id]
end
end
merged_data = { 'data' => merged, 'format_version' => version }
self.class.new(merged_data)
end
private
def wrap_text(text, width)
return [text] if text.length <= width
text.scan(/.{1,#{width}}/)
end
# an array of text
def wrap_list(list, width)
list.map do |text|
wrap_text(text, width)
end.flatten
end
def sql_width
@sql_width ||= (terminal_width - count_width - duration_width) / 2
end
def count_width
5
end
def duration_width
15
end
end
end