preload
List Dir Tree with Ignore list using Python Radiation Meter Readings
Apr 28

From my bag of tricks…

On occasion, I run into the need to present some kind of table information on to a console/terminal session so that the information is readable. After doing this several times, I decided to write a text table class so that i didn’t have to worry about formatting the table within my code. I just set the table up with column titles and column widths and just focus on the data.

Here’s the code:

#
#  texttable.rb  --  testing tool to output a nicely formatted table.
#
# John Allen, June 2005
#  June 2010 -- Added support for word wrap fields
#

class TextTable
  #
  # tableinfo = Hash.new {
  #    "name" =>  Array(:string),
  #    "width" => Array(:fixnum),
  #    ["linebetweenrows" => Boolean,]
  #    ["hdrlinechar" => "=",]
  #    ["rowlinechar" => "-",]
  #    ["wordwrap" => Boolean]
  # }

  attr_accessor  :names,  :widths, :hdr_print_flg, :lbr_flg, :hdrchar, :linechar, :wordwrap

  @hdr_print_flg = false
  @ldr_flg = false
  @wordwrap = false

  def initialize(tableinfo)
    @names = tableinfo["name"]
    @widths = tableinfo["width"]
    if tableinfo["linebetweenrows"]
      @lbr_flg = true
    end
    if tableinfo["wordwrap"]
      @wordwrap = true
    end
    @hdrchar = tableinfo["hdrlinechar"] || "="
    @linechar = tableinfo["rowlinechar"] || "-"
  end

  def printrow(a)
    # a = Array
    buf = ""
    if not @hdr_print_flg
      buf << printHdr()
    end
    buf << "|"
    extra = []
    a.each_with_index do |n,i|
      if n.length > @widths[i]        ## check to see if value is bigger than field; chop if so
        b = n.slice(0..(@widths[i] -1))
        extra[i] = n.slice(@widths[i]..-1)
      else
        b = n
      end
      buf << " #{b}#{" "*(@widths[i] - b.length)}|"
    end
    buf << "\n"
    if @wordwrap and not extra.empty?   ## Word Wrap
      eflg = true
      while eflg          ## While stuff to word wrap
        eflg = false
        buf << "|"
        extra.each_with_index do |n,i|
          if not n.nil?
            if n.length > @widths[i]        ## check to see if value is bigger than field; chop if so
              b = n.slice(0..(@widths[i] -1))
              extra[i] = n.slice(@widths[i]..-1)
              eflg = true           ## still more to word wrap!!
            else
              b = n
              extra[i] = nil
            end
            buf << " #{b}#{" "*(@widths[i] - b.length)}|"
          else
            buf << " #{" "*@widths[i]}|"  ## add blank space for non-wordwrap field
          end
        end
        buf << "\n"
      end
    end
    buf << _line(@linechar)  if @lbr_flg
    return buf
  end

  def printHdr
    buf = _line(@hdrchar)
    buf << "|"
    @names.each_with_index do |n,i|
      buf << " #{n}#{" "*(@widths[i] - n.length)}|"
    end
    buf << "\n"
    buf << _line(@hdrchar)
    @hdr_print_flg = true
    return buf
  end

  def printLine
    buf = _line(@linechar)
    return buf
  end

  #-------------------------------------------------------------------------------------------------------#
  private
  #-------------------------------------------------------------------------------------------------------#

  def _line(char)
    b = "+"
    @names.each_with_index do |n,i|
      b << char*@widths[i]
      b << "#{char}+"
    end
    b << "\n"
    return b
  end

end

if __FILE__ == $0
  tt = {
     "name" => ["First","Last","City","State"],
     "width" => [15,15,15,6],
     "linebetweenrows" => false
  }

  names = [
      ["John","Allen","Redmond","WA"],
      ["Herman","Gonzales","Mill Creek","WA"],
      ["Jimmy","Doogle","Bothell","WA"],
      ["Jane","Goodman","Seattle","WA"]
  ]

  table = TextTable.new(tt)

  buf = ""
  names.each do |name|
    buf << table.printrow(name)
  end
  buf << table.printLine
  puts buf

end

Running the example code at the bottom prints out the following result:

C:\Server4\Dev\Ruby\exo>ruby texttable.rb
+================+================+================+=======+
| First          | Last           | City           | State |
+================+================+================+=======+
| John           | Allen          | Redmond        | WA    |
| Herman         | Gonzales       | Mill Creek     | WA    |
| Jimmy          | Doogle         | Bothell        | WA    |
| Jane           | Goodman        | Seattle        | WA    |
+----------------+----------------+----------------+-------+

the printrow() method takes an array of String values to print out. You can have a line printed out between each row if you set the linebetweenrows hash value to ‘true’ (it defaults to ‘false’). There is not a lot of error checking (as in, you can crash the program if you feed the printrow() method an array that is shorter than the number of columns), but since I mainly use it for testing or utilities, I didn’t put a lot in. Its a very handy tool to have around.

Update: I added support for word wrap in all fields recently, so I have updated the code above with that version.

SociBook del.icio.us Digg Facebook Google Yahoo Buzz StumbleUpon

2 Responses to “Textual Table class for Ruby”

  1. fport program in Ruby | JohnAllen.us / KD7SEE.us Says:

    [...] program uses my TextTable class from a previous post. It runs the ‘netstat -aon’ command and then filters out the [...]

  2. aaron Says:

    Hi,
    This code works great for me, but I just wonder how the word-wrap funciton works. I tested some example with very long word, it seems that the long word is chopped rather than wrapped. So, how can I change to the word-wrap mode. Thanks a lot~~

Leave a Reply