Skip to content

Instantly share code, notes, and snippets.

@tlowrimore
Created March 9, 2012 17:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tlowrimore/2007605 to your computer and use it in GitHub Desktop.
Save tlowrimore/2007605 to your computer and use it in GitHub Desktop.
Layout Plain-text with columns and shit. (Ruby 1.9)
# Quickly thrown together, but it works, and maybe it'll get YOU started on
# a better solution to the problem. Enjoy!
class TextLayout
attr_reader :page_width, :tab_width
def initialize(tab_width=4, page_width=80)
@tab_width, @page_width = tab_width, page_width
end
# Takes a title and an optional character which will be used
# to surround the title with a rule that spans the width
# of the page.
def header(title, chr="-")
[ rule(chr), title, rule(chr), "" ] * "\n"
end
# Takes either a Hash or an Array of Arrays and an optional
# character used to create a rule between each record in
# the dataset. This method makes its best effort to layout
# the data in columns.
def columns(data, chr=nil)
unless data.empty?
data_array = data.to_a
ws = column_widths data_array
data_array.each_with_object([]) { |row, a|
a << apply_widths(row, ws)
a << rule(chr) if chr
} * "\n"
end
end
private
# Returns the column widths of the supplied data with @tab_width padding
# applied to all but the last column.
def column_widths(data)
# ws (widths) is an array of the lengths of the longest word in each column.
data.transpose.map { |col| col.map(&:to_s).max_by(&:length).length }.tap { |ws|
# total column padding width
pw = (ws.length - 1) * @tab_width
# Determine the overflow; that is to say, the difference between the sum of
# column widths and the page width. Also, find the index of the max (i.e. widest)
# column.
overflow, max = [0, ws.reduce(:+) + pw - @page_width].max, ws.each_with_index.max.last
# Subtract the overflow from the widest column width.
ws[max] = ws[max] - overflow
}
end
def apply_widths(row, widths)
rw_pairs = row.map{ |val| val.to_s.dup }.zip(widths)
# First we need to know the maximum number of lines in a row, after
# the line has been split on it column width.
line_count = rw_pairs.map { |str, w|
str.gsub! /\n/, " "
str.scan(line_upto(w)).length
}.max
# Now we format the row.
rw_pairs.map { |str, w|
line_count.times.map { str.slice!(line_upto(w)).to_s.strip.ljust(w + @tab_width) }
}.transpose.map(&:join) * "\n"
end
def line_upto(length)
/.{1,#{length}}(?:\s|$)/
end
def rule(chars=nil)
if chars.to_s.length > 0
ch_len = chars.length
chars * (@page_width / ch_len) + chars[0, (@page_width % ch_len)]
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment