#!/usr/bin/env ruby # -*- encoding: utf-8 -*- # Copyright Steffie Dorn , 2017 # License: GNU APGLv3 (or later) module Enumerable # find shortest element def shortest self.min_by{|s| s.str_length} end # find longest element def longest self.max_by{|s| s.str_length} end def length_of_longest self.longest.str_length end def length_of_shortest self.shortest.str_length end end class Object # consistent string length def str_length s = self.to_s ss = StringScanner.new(s) len = s.length len -= ss.matched_size while ss.skip_until(String::ANSIColorRegexp) len end end class Array def align str="\t", alignment: :left, force: false just_function = case alignment when :left ; Proc.new{|e,l,_c| e.ljust(l) } when :right ; Proc.new{|e,l,_c| e.rjust(l) } when :center ; Proc.new{|e,l,_c| e.center(l) } when Proc ; alignment else raise "invalid alignment: #{alignment}" end # split all lines lines = [] columns = 0 rx = /[ ]#{str}/ if force self.each do |line| line .gsub! rx, str if force line = line.split(str, -1) lines << line columns = [columns, line.size].max end # justify all columns lengths = columns.times.map do |column| lines.reduce(0){|sum, line| [line[column].str_length, sum].max} end lines.map! do |line| # align columns line.map!.with_index do |elem, column| # how much the element is internally longer than it appears elem_diff = elem.to_s.length - elem.str_length just_function.call(elem, lengths[column] + elem_diff, column) end # join line back together line.join(str) end lines end def align! str=" ", alignment: :left self.replace(self.align(str, alignment: alignment)) end end class String def align str=" ", alignment: :left self.split("\n").align(str, alignment: alignment).join("\n") end def align! str=" ", alignment: :left self.replace(self.align(str, aligment: alignment)) end end