Columnized text datasets in Rails
A few days ago, Courtenay of Caboose posted an article called 'pretty tables for ruby objects' that give a MySQL-command-line-client style textual view of data stored in your Rails database. The syntax worked like this:
puts Invoice.pretty_table(nil,{:max_width => 50, :line_spacer => '-', :find => { :order => 'created_at desc' }})
For an output like so:
| created_at | updated_at | name | due_at | p | --------------------------------------------------------------------------------------------------------------- | Fri Jun 09 14:17:00 PDT 2006 | Fri Jun 09 18:34:55 PDT 2006 | Website | Sun Jul 09 14:17:00 PDT 2006 | 1 | | Fri Jun 09 14:01:00 PDT 2006 | Fri Jun 09 18:34:55 PDT 2006 | May | Fri Jun 23 14:01:00 PDT 2006 | 1 | | Sat Jun 03 12:25:00 PDT 2006 | Fri Jun 09 18:34:55 PDT 2006 | Invoice 3 | Mon Jul 03 12:25:00 PDT 2006 | 3 |
It didn't make sense to me and I suggested developing a version that worked in this way instead:
Something.find(:all, :conditions => 'whatever').pretty_print
This way you can use your regular model accessors and simply use the 'pretty' output on any result set whatsoever. I didn't hear anything back so I wrote it myself and am releasing the code here (Note: it seems courtenay was listening, and he also developed the same thing - see the article previous - but mine is different enough for it to be worth posting it still):
class Array protected def columnized_row(fields, sized) r = [] fields.each_with_index do |f, i| r << sprintf("%0-#{sized[i]}s", f.to_s.gsub(/\n|\r/, '').slice(0, sized[i])) end r.join(' | ') end public def columnized(options = {}) sized = {} self.each do |row| row.attributes.values.each_with_index do |value, i| sized[i] = [sized[i].to_i, row.attributes.keys[i].length, value.to_s.length].max sized[i] = [options[:max_width], sized[i].to_i].min if options[:max_width] end end table = [] table << header = columnized_row(self.first.attributes.keys, sized) table << header.gsub(/./, '-') self.each { |row| table << columnized_row(row.attributes.values, sized) } table.join("\n") end end class ActiveRecord::Base def columnized(options = {}) [*self].columnized(options) end end
To use:
>> puts Post.find(:all).columnized(:max_width => 10) updated_at | title | private | url | thumb | metadata | movie | id | views | content | user_id | created_at ------------------------------------------------------------------------------------------------------------------------------ Wed May 31 | tetwer | 0 | | | | | 909 | 0 | video:xyzz | 1 | Wed May 31 Wed May 31 | bbbb | 0 | | | | | 1 | 15 | // descrip | 1 | Tue May 23 Wed May 31 | cxzcxzx | 0 | | | | | 906 | 19 | // descrip | 1 | Tue May 23 Wed May 31 | jklklkl; | 0 | | | | | 907 | 35 | // descrip | 1 | Tue May 23
If you want to use it with your project, put the code into lib/columnized.rb, use require 'columnized', and you're ready to roll. Unlike courtenay's version, mine only supports max_width, but I didn't consider changing the column separator too important :)