Command Line Reporter

This gem provides an simple way to add RSpec like formatting of the output of your ruby scripts. It eliminates the need to litter your code with puts statements instead providing a cleaner, more ruby like way of reporting progress to the user throught the command line interface.

With the release of Version 2.0, it is now possible to produce tables with and without borders. See the section on Tables for more examples.

Installation

It is up on rubygems.org so add it to your bundle or do it the old fashioned way:

gem install command_line_reporter

Usage

The gem provides a mixin that can be included in your scripts.

include CommandLineReporter
Standard Methods

There are several methods the mixin provides that do not depend on the formatter used:

  • header(hash) and footer(hash)
    • :title - The title text for the section. Default: 'Report'
    • :width - The width in characters for the section. Default: 100
    • :align - 'left'|'right'|'center' align the title text. Default: 'left'
    • :spacing - Number of vertical lines to leave as spacing after|before the header|footer. Default: 1
    • :timestamp - Include a line indicating the timestamp below|above the header|footer text. Either true|false. Default: false
    • :rule - true|false indicates whether to include a horizontal rule below|above the header|footer. Default: false
  • report(hash) block
    • The first argument is a hash that defines the options for the method. See the details in the formatter section for allowed values.
    • The second argument is a block of ruby code that you want executed within the context of the reporter. Any ruby code is allowed. See the examples that follow in the formatter sections for details.
  • formatter=(string)
    • Factory method indicating the formatter you want your application to use. At present the 2 formatters are (Default: 'nested'):
    • 'progress' - Use the progress formatter
    • 'nested' - Use the nested (or documentation) formatter
  • horizontal_rule(hash)
    • :char - The character used to build the rule. Default: '-'
    • :width - The width in characters of the rule. Default: 100
  • vertical_spacing(int)
    • Number of blank lines to output. Default: 1
  • datetime(hash)
    • :align - 'left'|'center'|'right' alignment of the timestamp. Default: 'left'
    • :width - The width of the string in characters. Default: 100
    • :format - Any allowed format from #strftime#. Default: %Y-%m-%d %H:%I:%S%p
  • aligned(string, hash)
    • text - String to display
    • :align - 'left'|'right'|'center' align the string text. Default: 'left'
    • :width - The width in characters of the string text. Default: 100
  • table(hash) block
    • The first argument is a hash that defines properties of the table.
    • :border - true|false indicates whether to include borders around the table cells
    • The second argument is a block which includes calls the to the row method
  • row block
    • Only argument is a block with calls to column allowed
  • column(string, hash)
    • text - String to display in the table cell
    • options - The options to define the column
    • :width - defines the width of the column
    • :padding - The number of spaces to put on both the left and right of the text.
    • :align - Allowed values are left|right|center

Progress Formatter

Example

require 'command_line_reporter'

class Example
  include CommandLineReporter

  def initialize
    self.formatter = 'progress'
  end

  def run
    x = 0
    report do
      10.times do
        x += 1
        formatter.progress
      end
    end
  end
end

Example.new.run

This simply produces 10 dots (.) in succession:

[~/scratch]$ ruby example.rb
..........

Indicator

The indicator is the string that is displayed in the command line interface that indicates progress. The default is the dot (.) but any string is allowed. In fact one can use the erase character to get crafty with displaying percent complete in place.

Instance Methods

  • indicator(string) - This overrides the default value of the dot (.) being used for all calls to the report method.
formatter.indicator('*')
  • progress(string) - Call this method to invoke the output to the command line interface. The string overrides the indicator for this call only.
formatter.progress
formatter.progress("^H^H^H10%")

Nested Formatter

The nested formatter concept is inspired by the documentation formatter in RSpec. The idea is to be able to create nested grouping levels of output that are very readable as the code is being executed.

Example

require 'command_line_reporter'

class Example
  include CommandLineReporter

  def initialize
    self.formatter = 'nested'
  end

  def run
    x,y,z = 0,0,0

    report(:message => 'calculating first expression') do
      x = 2 + 2
      2.times do
        report(:message => 'calculating second expression') do
          y = 10 - x
          10.times do |i|
            report(:message => 'pixelizing', :type => 'inline', :complete => "#{i*10+10}%") do
              z = x + y
            end
          end
        end
      end
    end
  end
end

Example.new.run

This produces the more complex output:

[~/scratch]$ ruby example.rb
calculating first expression
  calculating second expression
    pixelizing...10%
    pixelizing...20%
    pixelizing...30%
    pixelizing...40%
    pixelizing...50%
    pixelizing...60%
    pixelizing...70%
    pixelizing...80%
    pixelizing...90%
    pixelizing...100%
  complete
  calculating second expression
    pixelizing...10%
    pixelizing...20%
    pixelizing...30%
    pixelizing...40%
    pixelizing...50%
    pixelizing...60%
    pixelizing...70%
    pixelizing...80%
    pixelizing...90%
    pixelizing...100%
  complete
complete

Instance Methods

  • message_string(string) - This defines the string that is displayed as the first part of the message to the user. The default value is "working".
formatter.message_string('working')
  • complete_string(string) - This defines the string that completes the message to the user for the report. The default value is "complete".
formatter.complete_string('done')
  • indent_size(int) - The number of spaces to indent for a single indentation level. The default value is 2 spaces.
formatter.indent_size(4)

Report Options

The following are the allowed values of the options hash argument to the report method:

  • :message - The string that is displayed as the first part of the message to the user
  • :complete - The string that completes the message to the user
  • :type - Define as 'inline' if you want the message and complete strings on the same line
  • :indent_size - The number of spaces to indent the current level
report(:message => 'running', :complete => 'finished', :type => 'inline', :indent_size => 8) do
  # code here
end

Tables

Examples are always helpful so let's look at the following:

require 'command_line_reporter'

class Example
  include CommandLineReporter

  def run
    table(:border => true) do
     row do
       column('NAME', :width => 20)
       column('ADDRESS', :width => 30, :align => 'right', :padding => 5)
       column('CITY', :width => 15)
     end
     row do
       column('Ceaser')
       column('1 Appian Way')
       column('Rome')
     end
     row do
       column('Richard Feynman')
       column('1 Golden Gate')
       column('Quantum Field')
     end
   end
  end
end

Example.new.run

This produces the very simple output with 2 rows and 3 columns of data:

+----------------------+--------------------------------+-----------------+
| NAME                 |                   ADDRESS      | CITY            | 
+----------------------+--------------------------------+-----------------+
| Ceaser               |              1 Appian Way      | Rome            | 
+----------------------+--------------------------------+-----------------+
| Richard Feynman      |             1 Golden Gate      | Quantum Field   | 
+----------------------+--------------------------------+-----------------+

Notice how the properties of the columns for the second and third rows have been inherited from the first like in HTML. This makes it a lot easier to write in freeform. What if you have data to iterate over and have text that is wider than the column width you have selected? Not a problem as the following example demonstrates all of the combinations of the various options:

require 'command_line_reporter'

class Example
  include CommandLineReporter

  def run
    table(:border => true) do
      3.times do
        row do
          i = 0
          3.times do
            i += 10
            column('x' * (0 + rand(50)), :align => %w[left right center][rand(3)], :width => i, :padding => rand(5))
          end
        end
      end
    end
  end
end

This randomly produces a table with a border that has 3 rows and 3 columns. Each column gets wider by 10 characters. The alignment of the column is demonstrated and you can see where some data elements have padding around them.

+------------+----------------------+--------------------------------+
| xxxxxxxxxx |    xxxxxxxxxxxxxx    |   xxxxxxxxxxxxxxxxxxxxxxxxxx   | 
|     xxxxxx |       xxxxxxxxx      |   xxxxxx                       | 
+------------+----------------------+--------------------------------+
| xxxxxxxxxx |    xxxxxxxxxxxxxx    |   xxxxxxxxxxxxxxxxxxxxxxxxxx   | 
| xxxxxxxxxx |    xxxxxxxxxxxxxx    |        xxxxxxxxxxxxxxxxx       | 
| xxxxxxxxxx |    xxxxxxxxxxxxxx    |                                | 
| xxxxxxx    |    xxxx              |                                | 
+------------+----------------------+--------------------------------+
| xxxxxxxxxx |         xxxx         |            xxxxxxxxxxxxxxxxxxx | 
| xxxxxx     |                      |                                | 
+------------+----------------------+--------------------------------+

This is all generated randomly to illustrate the features but you get the idea of how to use alignment, width and padding.

The best feature is wrapping. If the text you are display in a cell is larger than the width it was given, it will automatically wrap it for you. Padding and alignment are preserved. It palso properly handles the case where the data in one cell causes the wrapping but other cells my not have the same number of lines to wrap.

To Do

  • Add the ability for a column to span across others
  • Add the progress method to the top level mixin so that there is no need to invoke through the formatter.