lib/collimator.rb in collimator-0.0.2 vs lib/collimator.rb in collimator-0.0.3

- old
+ new

@@ -1,582 +1,11 @@ -require File.expand_path("../collimator/version", __FILE__) - require 'rubygems' require 'colored' -module Collimator +require File.expand_path('../collimator/version', __FILE__) +require File.expand_path('../collimator/table', __FILE__) +require File.expand_path('../collimator/progress_bar', __FILE__) +require File.expand_path('../collimator/spinner', __FILE__) +require File.expand_path('../collimator/bar_graph', __FILE__) +require File.expand_path('../collimator/exceptions', __FILE__) - class TooManyDataPoints < Exception - end - module Table - - @horiz = '-' - @border = '|' - @corner = '+' - @columns = [] - @rows = [] - @headers = [] - @footers = [] - @column_names = [] - @use_column_headings = false - @auto_clear = true - @separators = [] - @live_update = false - @table_string = [] - @use_capture_string = false - @use_capture_html = false - @last_header_color = '#EEEEEE' - - def self.live_update=(live_upate) - @live_update = live_upate - end - - def self.live_update - @live_update - end - - def self.set_auto_clear(auto_clear = true) - @auto_clear = auto_clear - end - - def self.set_corner(corner_character) - @corner = corner_character - end - - def self.set_border(border_character) - @border = border_character - end - - def self.set_horizontal(horizontal_character) - @horiz = horizontal_character - end - - def self.header(text, opts = {}) - width, padding, justification, color = parse_options(opts) - - @headers << { :text => text, :padding => padding, :justification => justification , :color => color} - end - - def self.footer(text, opts) - width, padding, justification = parse_options(opts) - - @footers << { :text => text, :padding => padding, :justification => justification } - end - - def self.column(heading, opts = {}) - width, padding, justification, color = parse_options(opts) - - @columns << { :width => width, :padding => padding, :justification => justification, :color => color } - @use_column_headings = true if heading.length > 0 - @column_names << heading - end - - def self.row(row) - colored_data_array = [] - if row.class.to_s == "Hash" - colored_data_array = row[:data].map { |v| {:data => v, :color => row[:color] } } - else - colored_data_array = row.clone - end - if @live_update - put_line_of_data(colored_data_array) - else - @rows << colored_data_array - end - end - - def self.separator - if @live_update - put_horizontal_line_with_dividers - else - @separators << @rows.length - end - end - - def self.tabulate - put_header - put_column_heading_text - prep_data - put_table - put_horizontal_line - put_footer - - clear_all if @auto_clear - end - - def self.tabulate_to_string - @use_capture_html = false - @use_capture_string = true - @auto_clear = false - tabulate - @table_string.join("\n") - end - - def self.tabulate_to_html - @use_capture_string = false - @use_capture_html = true - @auto_clear = false - - prep_html_table - tabulate - complete_html_table - - @table_string.join("\n") - end - - def self.prep_html_table - @table_string = [] - @table_string << "<table STYLE=\"font-family: helvetica, verdana, tahoma; border-collapse: collapse;\">" - end - - def self.complete_html_table - @table_string << "</table>" - end - - def self.start_live_update - put_header - put_column_heading_text - end - - def self.complete_live_update - put_horizontal_line - put_footer - - clear_all if @auto_clear - end - - def self.csv - lines = [] - - lines.concat(@headers.map { |h| h[:text] }) if @headers.count > 0 - lines << @column_names.join(',') if @use_column_headings - @rows.each { |row| lines << row.join(',') } - lines.concat(@footers.map { |h| h[:text] }) if @headers.count > 0 - - out = lines.join("\n") - - clear_all if @auto_clear - - out - end - - def self.clear_all - @columns = [] - @rows = [] - @headers = [] - @footers = [] - @column_names = [] - @use_column_headings = false - @horiz = '-' - @border = '|' - @corner = '+' - @auto_clear = true - @separators = [] - @live_update = false - @table_string = [] - @use_capture_string = false - @use_capture_html = false - end - - def self.clear_data - @rows = [] - @separators = [] - end - - private - - def self.send_line(line) - if @use_capture_string || @use_capture_html - @table_string << line - else - puts line - end - end - - def self.parse_options(opts) - width = opts.has_key?(:width) ? opts[:width] : 10 - padding = opts.has_key?(:padding) ? opts[:padding] : 0 - justification = opts.has_key?(:justification) ? opts[:justification] : :center - col_color = opts.has_key?(:color) ? opts[:color] : nil - - padding = 0 if justification == :decimal - - [width, padding, justification, col_color] - end - - def self.put_table - put_horizontal_line if @headers.length == 0 and !@use_column_headings - row_number = 0 - @table_string << "<tbody>" if @use_capture_html - @rows.each do |row| - put_horizontal_line_with_dividers if @separators.include?(row_number) - put_line_of_data(row) - row_number += 1 - end - @table_string << "</tbody>" if @use_capture_html - end - - def self.prep_data - column = 0 - @columns.each do |c| - if c[:justification] == :decimal - column_width = c[:width] - column_center = column_width / 2 - values = [] - @rows.each do |r| - value = r[column].class.to_s == "Hash" ? r[column][:data] : r[column].to_s - color = r[column].class.to_s == "Hash" ? r[column][:color] : nil - - v2 = value - decimal_place = v2.index('.') ? v2.index('.') : v2.length - v2 = ' '*(column_center -decimal_place) + v2 - v2 = v2.ljust(column_width) - - v2 = {:data => v2, :color => color} if color - values << v2 - end - - row = 0 - @rows.each do |r| - r[column] = values[row] - row += 1 - end - end - - if c[:color] - @rows.each do |r| - value = '' - color = nil - if r[column].class.to_s == "Hash" # don't change the data point color if already set. single data point color overrides column color. - color = r[column][:color] - value = r[column][:data] - else - color = c[:color] - value = r[column] - end - r[column] = {:data => value, :color => color} - end - end - column += 1 - end - end - - def self.prep_data_orig - column = 0 - @columns.each do |c| - if c[:justification] == :decimal - column_width = c[:width] - column_center = column_width / 2 - values = [] - length = 0 - decimal_place = 0 - @rows.each do |r| - value = r[column].class.to_s == "Hash" ? r[column][:data] : r[column].to_s - values << value - length = value.length if value.length > length - end - values = values.map { |v| v.ljust(length) } - values.each do |value| - decimal_place = value.index('.') if (value.index('.') and (value.index('.') > decimal_place)) - end - values = values.map do |v| - place = v.index('.') ? v.index('.') : v.length - ' '*(decimal_place - place) + v - end - - row = 0 - @rows.each do |r| - r[column] = values[row] - row += 1 - end - end - - if c[:color] - @rows.each do |r| - value = '' - color = nil - if r[column].class.to_s == "Hash" # don't change the data point color if already set. single data point color overrides column color. - color = r[column][:color] - value = r[column][:data] - else - color = c[:color] - value = r[column] - end - r[column] = {:data => value, :color => color} - end - end - column += 1 - end - end - - def self.put_column_heading_text - @use_capture_html ? put_column_heading_text_html : put_column_heading_text_string if @use_column_headings - end - - def self.put_column_heading_text_string - put_line_of_data(@column_names) - put_horizontal_line_with_dividers - end - - def self.style_color(rgb) - luminance = get_luminance(rgb) - color = luminance < 50 ? '#EEEEEE' : '#222222' - color_style = "color: #{color};" - end - - def self.style_header_border(rgb) - luminance = get_luminance(rgb) - color = luminance < 50 ? '#EEEEEE' : '#222222' - color_style = "border-bottom: 1px solid #{color};" - end - - def self.get_luminance(rgb) - rgb_temp = rgb.gsub("#", '') - luminance = 0 - if rgb_temp.length == 6 - r = rgb_temp[0..1].hex - g = rgb_temp[2..3].hex - b = rgb_temp[4..5].hex - luminance = (0.299*r + 0.587*g + 0.114*b) - end - luminance - end - - def self.put_column_heading_text_html - c = @last_header_color - text_color = style_color(c) - border_color = style_header_border(c) - out = "<tr STYLE=\"background-color: #{@last_header_color}; #{text_color} #{border_color}\">\n" - column = 0 - @column_names.each do |cname| - padding_style = @columns[column][:padding] ? "STYLE=\"padding-left: #{@columns[column][:padding]}em; padding-right: #{@columns[column][:padding]}em;\"" : "" - out += "<th #{padding_style}>#{cname}</th>\n" - column += 1 - end - - out += "</tr>\n" - out += "</thead>" - - send_line out - end - - def self.put_header_or_footer(header_or_footer = :header) - data = header_or_footer == :footer ? @footers.clone : @headers.clone - - unless @use_capture_html - if header_or_footer == :header and data.length > 0 - put_horizontal_line - end - end - - return if data.length == 0 - - @table_string << "<thead>" if @use_capture_html if header_or_footer == :header - - data.each do | header | - send_line make_header_line(header) - end - - put_horizontal_line - end - - def self.make_header_line(data) - out = @use_capture_html ? make_header_line_html(data) : make_header_line_string(data) - out - end - - def self.make_header_line_string(header) - header_width = line_width - header_line = @border - header_line += ' '*header[:padding] if header[:justification] == :left - header_line += header[:text].center(header_width - 2) if header[:justification] == :center - header_line += header[:text].ljust(header_width - header[:padding] - 2) if header[:justification] == :left - header_line += header[:text].rjust(header_width - header[:padding] - 2) if header[:justification] == :right - header_line += ' '*header[:padding] if header[:justification] == :right - - header_line += @border - end - - def self.make_header_line_html(data) - @last_header_color = data[:color] || @last_header_color - - text_color = style_color(@last_header_color) - header_line = "<tr>" - header_line += "<th STYLE=\"background-color: #{@last_header_color}; #{text_color}\" colspan='#{@column_names.count + 1}'>#{data[:text]}</th>" - header_line += "</tr>" - header_line - end - - def self.put_footer - put_header_or_footer(:footer) unless @use_capture_html - end - - def self.put_header - put_header_or_footer(:header) - end - - def self.line_width - @columns.length + 1 + @columns.inject(0) { |sum, c| sum + c[:width] + c[:padding] } - end - - def self.format_data(value, width, padding, justification) - data_value = value.class.to_s == "Hash" ? value[:data] : value - - s = '' - s = data_value.to_s - s = ' '*padding + s.ljust(width) if justification == :left - s = s.rjust(width) + ' '*padding if justification == :right - s = s.center(width) if justification == :center || justification == :decimal - #s = s.send(value[:color].to_s) if value.class.to_s == "Hash" - s = s.gsub(data_value.to_s, data_value.to_s.send(value[:color])) if value.class.to_s == "Hash" - s - end - - def self.put_row_of_html_data(row_data) - column = 0 - row_string = "<tr>\n" - - row_data.each do | val | - style_info = @columns[column][:padding] ? " STYLE=\"padding-left: #{@columns[column][:padding]}em; padding-right: #{@columns[column][:padding]}em;\"" : '' - row_string += "<td#{style_info}>#{val}</td>\n" - column += 1 - end - - row_string += "</tr>" - - send_line row_string - end - - def self.put_row_of_string_data(row_data) - column = 0 - row_string = @border - - row_data.each do | val | - s = format_data(val, @columns[column][:width], @columns[column][:padding], @columns[column][:justification]) - row_string = row_string + s + @border - column += 1 - end - - send_line row_string - end - - def self.put_line_of_data(row_data) - raise TooManyDataPoints if row_data.count > @columns.length - - row_data << '' while row_data.length < @columns.length - - if @use_capture_html - put_row_of_html_data row_data - else - put_row_of_string_data row_data - end - end - - def self.put_horizontal_line - unless @use_capture_html - width = line_width - send_line @corner + @horiz * (width - 2) + @corner - end - end - - def self.put_horizontal_line_with_dividers - unless @use_capture_html - a = [] - @columns.each { | c | a << @horiz*(c[:width] + c[:padding]) } - s = @border + a.join(@corner) + @border - send_line s - end - end - end - - module ProgressBar - @display_width = 100 - @max = 100 - @min = 0 - @method = :percent - @step_size = 5 - @current = 0 - - def self.set_parameters(params) - @display_width = params[:display_width] if params.has_key?(:display_width) - @max = params[:max] if params.has_key?(:max) - @min = params[:min] if params.has_key?(:min) - @method = params[:method] if params.has_key?(:method) - @step_size = params[:step_size] if params.has_key?(:step_size) - end - - def self.start(params = {}) - set_parameters params - @current = @min - put_current_progress - end - - def self.increment - @current += @step_size - put_current_progress unless @current > @max - end - - def self.complete - clear - puts " "*(@display_width + 2) - end - - def self.clear - print "\b"*(@display_width + 2) - end - - private - - def self.put_current_progress - clear - STDOUT.flush - print build_bar_string - #puts build_bar_string - end - - def self.build_bar_string - units = @max - @min - unit_width = @display_width/units - percent_complete = @current / (units * 1.0) - - s = '-'*(percent_complete * @display_width) + ' '*((100-percent_complete) * @display_width) - s1 = s[0..(@display_width/2 - 4)] - s2 = s[(@display_width/2 + 3)..(@display_width - 1)] - - formatted_percent = "%0.1f" % (percent_complete * 100) - - s = '|' + s1 + "#{formatted_percent}%".center(6) + s2 + '|' - #s = "#{units} - #{unit_width} - #{percent_complete} - #{@current}" - end - end - - module Spinner - @spinning = nil - @icons = ['-', '\\', '|', '/'] - - def self.spin - @spinning = Thread.new(@icons) do |myIcons| - i = 0 - while true do - print myIcons[i] - STDOUT.flush - if i == myIcons.length - 1 - i = 0 - else - i += 1 - end - sleep 0.1 - - print "\b" - STDOUT.flush - end - end - end - - def self.stop - @spinning.exit - print "\b" - STDOUT.flush - end - end -end