# coding: utf-8 module RequestLogAnalyzer::Output # Fixed Width output class. # Outputs a fixed width ASCII or UF8 report. class FixedWidth < Base # Mixin module. Will disable any colorizing. module Monochrome def colorize(text, *options) text end end # Colorize module module Color STYLES = { :normal => 0, :bold => 1, :underscore => 4, :blink => 5, :inverse => 7, :concealed => 8 } COLORS = { :black => 0, :blue => 4, :green => 2, :cyan => 6, :red => 1, :purple => 5, :brown => 3, :white => 7 } # Colorize text # text The text to colorize # Options # * :background The background color to paint. Defined in Color::COLORS # * :color The foreground color to paint. Defined in Color::COLORS # * :on Alias for :background # * :style Font style, defined in Color::STYLES # # Returns ASCII colored string def colorize(text, *options) font_style = '' foreground_color = '0' background_color = '' options.each do |option| if option.kind_of?(Symbol) foreground_color = "3#{COLORS[option]}" if COLORS.include?(option) font_style = "#{STYLES[option]};" if STYLES.include?(option) elsif option.kind_of?(Hash) options.each do |key, value| case key when :color; foreground_color = "3#{COLORS[value]}" if COLORS.include?(value) when :background; background_color = "4#{COLORS[value]};" if COLORS.include?(value) when :on; background_color = "4#{COLORS[value]};" if COLORS.include?(value) when :style; font_style = "#{STYLES[value]};" if STYLES.include?(value) end end end end return "\e[#{background_color}#{font_style}#{foreground_color}m#{text}\e[0m" end end attr_reader :characters CHARACTERS = { :ascii => { :horizontal_line => '-', :vertical_line => '|', :block => '=' }, :utf => { :horizontal_line => '━', :vertical_line => '┃', :block => '░' } } # Initialize a report # io iO Object (file, STDOUT, etc.) # options # * :characters :utf for UTF8 or :ascii for ANSI compatible output. Defaults to :utf. # * :color If true, ASCII colorization is used, else Monochrome. Defaults to Monochrome. # * :width Output width in characters. Defaults to 80. def initialize(io, options = {}) super(io, options) @options[:width] ||= 80 @options[:characters] ||= :utf @characters = CHARACTERS[@options[:characters]] color_module = @options[:color] ? Color : Monochrome (class << self; self; end).send(:include, color_module) end # Write a string to the output object. # str The string to write. def print(str) @io << str end alias :<< :print # Write a string to the output object with a newline at the end. # str The string to write. def puts(str = '') @io << str << "\n" end # Write the title of a report # title The title to write def title(title) puts puts colorize(title, :bold, :white) line(:green) end # Write a line def line(*font) puts colorize(characters[:horizontal_line] * @options[:width], *font) end # Write a link # text The text in the link, or the URL itself if no text is given # url The url to link to. def link(text, url = nil) if url.nil? colorize(text, :blue, :bold) else "#{text} (#{colorize(url, :blue, :bold)})" end end # Generate a header for a report def header if io.kind_of?(File) puts colorize("Request-log-analyzer summary report", :white, :bold) line(:green) puts "Version #{RequestLogAnalyzer::VERSION} - written by Willem van Bergen and Bart ten Brinke" puts "Website: #{link('http://github.com/wvanbergen/request-log-analyzer')}" end end # Generate a footer for a report def footer puts puts "Need an expert to analyze your application?" puts "Mail to #{link('contact@railsdoctors.com')} or visit us at #{link('http://railsdoctors.com')}." line(:green) puts "Thanks for using #{colorize('request-log-analyzer', :white, :bold)}!" end # Generate a report table and push it into the output object. # *colums Columns hash # &block: A block yeilding the rows. def table(*columns, &block) rows = Array.new yield(rows) # determine maximum cell widths max_cell_widths = rows.inject(Array.new(columns.length, 0)) do |result, row| lengths = row.map { |column| column.to_s.length } result.each_with_index { |length, index| result[index] = ([length, lengths[index]].max rescue length) } end columns.each_with_index { |col, index| col[:actual_width] ||= max_cell_widths[index] } # determine actual column width column_widths = columns.map do |column| if column[:width] == :rest nil elsif column[:width] column[:width] elsif column[:min_width] [column[:min_width], column[:actual_width]].max elsif column[:max_width] [column[:max_width], column[:actual_width]].min else column[:actual_width] end end if column_widths.include?(nil) fill_column = columns[column_widths.index(nil)] width_left = options[:width] - ((columns.length - 1) * (style[:cell_separator] ? 3 : 1)) - column_widths.compact.inject(0) { |sum, col| sum + col} column_widths[column_widths.index(nil)] = case fill_column[:type] when :ratio; width_left # max out else; [width_left, fill_column[:actual_width]].min end end # Print table header if table_has_header?(columns) column_titles = [] columns.each_with_index do |column, index| width = column_widths[index] alignment = (column[:align] == :right ? '' : '-') column_titles.push(colorize("%#{alignment}#{width}s" % column[:title].to_s[0...width], :bold)) end puts column_titles.join(style[:cell_separator] ? " #{characters[:vertical_line]} " : ' ') line(:green) end # Print the rows rows.each do |row| row_values = [] columns.each_with_index do |column, index| width = column_widths[index] case column[:type] when :ratio if width > 4 if column[:treshold] && column[:treshold] < row[index].to_f bar = '' bar << characters[:block] * (width.to_f * column[:treshold]).round bar << colorize(characters[:block] * (width.to_f * (row[index].to_f - column[:treshold])).round, :red) row_values.push(bar) else # Create a bar by combining block characters row_values.push(characters[:block] * (width.to_f * row[index].to_f).round) end else # Too few characters for a ratio bar. Display nothing row_values.push('') end else alignment = (columns[index][:align] == :right ? '' : '-') cell_value = "%#{alignment}#{width}s" % row[index].to_s[0...width] cell_value = colorize(cell_value, :bold, :brown) if columns[index][:highlight] row_values.push(cell_value) end end puts row_values.join(style[:cell_separator] ? " #{characters[:vertical_line]} " : ' ') end end end end