Skip to content

Loading…

Add Style class for width, borders and padding of cells #14

Merged
merged 7 commits into from

3 participants

@rrrene

This request introduces a Style class to handle the representation of the table on screen.

You now no longer have to patch constants on runtime to change the appearance of borders, and you can define the cell padding for both left and right side:

      @table.headings = ['Char', 'Num']
      @table.style = {
             :border_x => "=", :border_y => ":", :border_i => "x", 
             :padding_left => 0, :padding_right => 2
          }
      @table << ['a', 1]
      @table << ['b', 2]
      @table << ['c', 3]

which outputs:

        x======x=====x
        :Char  :Num  :
        x======x=====x
        :a     :1    :
        :b     :2    :
        :c     :3    :
        x======x=====x

As you can see from the commits, this also helps us get rid of all the magic numbers like + 2 and * 3 in Table and Row.

To provide the effect of

      Terminal::Table::X = ""
      Terminal::Table::Y = ""
      Terminal::Table::I = ""

one can set defaults in the Style class:

      Terminal::Table::Style.defaults = {:border_x => "", :border_y => "", :border_i => ""}

Then all tables created afterwards get these style values by default.

This might be handy to set a standard width, to get a nice unified looking output:

      Terminal::Table::Style.defaults = {:width => 80}

There are a few minor changes as well, like the ability to print an empty Table without errors.

@scottjg scottjg merged commit 66208ef into tj:master
@indiesquidge

@rrrene @scottjg , I'm trying to create a board that only has specific borders different than the others (i.e. I want the 3rd, 6th, and 9th column lines to be changed). Do you know of a way I could go about doing such a thing?

@indiesquidge

Currently, the closest working method I have is to create three separate tables within loops, as such:

require 'terminal-table'

table = Terminal::Table.new do |t|
  t << [1, 2, 3, 4, 5, 6, 7, 8, 9]
  2.times do 
    t << :separator
    t << [1, 2, 3, 4, 5, 6, 7, 8, 9]
  end
  t << :separator
  3.times do 
    t << :separator
    t << [1, 2, 3, 4, 5, 6, 7, 8, 9]
  end
  t << :separator
  3.times do 
    t << :separator
    t << [1, 2, 3, 4, 5, 6, 7, 8, 9]
  end
end

puts table

Which spits out:

+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+---+---+---+---+---+---+---+---+---+

Kind of there. It's a very hacky approach, and it would be nicer to have those "row borders" as a single border of "===" rather than tow borders of "---". Not sure how to go about implementing the "column borders", though, nor do I think this is the right approach anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
View
3 examples/examples.rb
@@ -6,6 +6,7 @@
puts
t = table ['a', 'b']
+t.style = {:padding_left => 2, :width => 80}
t << [1, 2]
t << [3, 4]
t << :separator
@@ -24,7 +25,7 @@
puts
user_table = table do |v|
- v.width = 80
+ v.style.width = 80
v.headings = 'First Name', 'Last Name', 'Email'
v << %w( TJ Holowaychuk tj@vision-media.ca )
v << %w( Bob Someone bob@vision-media.ca )
View
2 lib/terminal-table.rb
@@ -22,6 +22,6 @@
#++
$:.unshift File.dirname(__FILE__)
-%w(version core_ext table cell row table_helper).each do |file|
+%w(version core_ext table cell row style table_helper).each do |file|
require "terminal-table/#{file}"
end
View
6 lib/terminal-table/cell.rb
@@ -56,7 +56,9 @@ def lines
# Render the cell.
def render(line = 0)
- " #{lines[line]} ".align(alignment, width + 2)
+ left = " " * @table.style.padding_left
+ right = " " * @table.style.padding_right
+ "#{left}#{lines[line]}#{right}".align(alignment, width + @table.cell_padding)
end
alias :to_s :render
@@ -75,7 +77,7 @@ def value_for_column_width_recalc
# Returns the width of this cell
def width
- padding = (colspan - 1) * (2 + @table.class::Y.length)
+ padding = (colspan - 1) * @table.cell_spacing
inner_width = (1..@colspan).to_a.inject(0) do |w, counter|
w + @table.column_width(@index + counter - 1)
end
View
2 lib/terminal-table/row.rb
@@ -53,7 +53,7 @@ def method_missing m, *args, &block
end
def render
- y = Terminal::Table::Y
+ y = @table.style.border_y
if separator?
@table.separator
else
View
60 lib/terminal-table/style.rb
@@ -0,0 +1,60 @@
+
+module Terminal
+ class Table
+ # A Style object holds all the formatting information for a Table object
+ #
+ # To create a table with a certain style, use either the constructor
+ # option <tt>:style</tt>, the Table#style object or the Table#style= method
+ #
+ # All these examples have the same effect:
+ #
+ # # by constructor
+ # @table = Table.new(:style => {:padding_left => 2, :width => 40})
+ #
+ # # by object
+ # @table.style.padding_left = 2
+ # @table.style.width = 40
+ #
+ # # by method
+ # @table.style = {:padding_left => 2, :width => 40}
+ #
+ # To set a default style for all tables created afterwards use Style.defaults=
+ #
+ # Terminal::Table::Style.defaults = {:width => 80}
+ #
+ class Style
+ @@defaults = {
+ :border_x => "-", :border_y => "|", :border_i => "+",
+ :padding_left => 1, :padding_right => 1
+ }
+
+ attr_accessor :border_x
+ attr_accessor :border_y
+ attr_accessor :border_i
+
+ attr_accessor :padding_left
+ attr_accessor :padding_right
+
+ attr_accessor :width
+
+
+ def initialize options = {}
+ apply self.class.defaults.merge(options)
+ end
+
+ def apply options
+ options.each { |m, v| __send__ "#{m}=", v }
+ end
+
+ class << self
+ def defaults
+ @@defaults
+ end
+
+ def defaults= options
+ @@defaults = defaults.merge(options)
+ end
+ end
+ end
+ end
+end
View
57 lib/terminal-table/table.rb
@@ -2,30 +2,18 @@
module Terminal
class Table
- #--
- # Exceptions
- #++
-
- Error = Class.new StandardError
-
- ##
- # Table characters, x axis, y axis, and intersection.
-
- X, Y, I = '-', '|', '+'
-
attr_reader :title
attr_reader :headings
- attr_accessor :width
##
# Generates a ASCII table with the given _options_.
def initialize options = {}, &block
@column_widths = []
+ self.style = options.fetch :style, {}
+ self.title = options.fetch :title, nil
self.headings = options.fetch :headings, []
- @rows = []
- @width = options[:width]
- options.fetch(:rows, []).each { |row| add_row row }
+ self.rows = options.fetch :rows, []
yield_or_eval(&block) if block
end
@@ -55,7 +43,15 @@ def add_row array
def add_separator
self << :separator
end
-
+
+ def cell_spacing
+ cell_padding + style.border_y.length
+ end
+
+ def cell_padding
+ style.padding_left + style.padding_right
+ end
+
##
# Return column _n_.
@@ -93,11 +89,7 @@ def column_width n
# Return total number of columns available.
def number_of_columns
- if rows.empty?
- raise Error, 'your table needs some rows'
- else
- rows.map { |r| r.size }.max
- end
+ headings_with_rows.map { |r| r.size }.max
end
##
@@ -139,6 +131,7 @@ def rows
end
def rows= array
+ @rows = []
array.each { |arr| self << arr }
end
@@ -147,12 +140,20 @@ def rows= array
def separator
@separator ||= begin
- I + (0...number_of_columns).to_a.map do |i|
- X * (column_width(i) + 2)
- end.join(I) + I
+ style.border_i + (0...number_of_columns).to_a.map do |i|
+ style.border_x * (column_width(i) + cell_padding)
+ end.join(style.border_i) + style.border_i
end
end
+ def style=(options)
+ style.apply options
+ end
+
+ def style
+ @style ||= Style.new
+ end
+
def title=(title)
@title = title
recalc_column_widths Row.new(self, [title])
@@ -171,12 +172,12 @@ def == other
private
def columns_width
- @column_widths.inject(0) { |s, i| s + i + 2 + Y.length } + Y.length
+ @column_widths.inject(0) { |s, i| s + i + cell_spacing } + style.border_y.length
end
def additional_column_widths
- return [] if @width.nil?
- spacing = @width - columns_width
+ return [] if style.width.nil?
+ spacing = style.width - columns_width
if spacing < 0
raise "Table width exceeds wanted width of #{wanted} characters."
else
@@ -197,7 +198,7 @@ def recalc_column_widths row
colspan.downto(1) do |j|
cell_length = cell_value.to_s.length
if colspan > 1
- spacing_length = (2 + Y.length) * (colspan - 1)
+ spacing_length = cell_spacing * (colspan - 1)
length_in_columns = (cell_length - spacing_length)
cell_length = (length_in_columns.to_f / colspan).ceil
end
View
16 spec/cell_spec.rb
@@ -20,6 +20,14 @@ class String; include Term::ANSIColor; end
cell.alignment.should == :center
end
+ it "should allow :left, :right and :center for alignment" do
+ @cell = Cell.new :value => 'foo', :table => Terminal::Table.new, :index => 0
+ @cell.alignment = :left
+ @cell.alignment = :right
+ @cell.alignment = :center
+ lambda { @cell.alignment = "foo" }.should raise_error
+ end
+
it "should allow multiline content" do
cell = Cell.new :value => "foo\nbarrissimo", :table => Terminal::Table.new, :index => 0
cell.value.should == "foo\nbarrissimo"
@@ -34,5 +42,13 @@ class String; include Term::ANSIColor; end
cell.value_for_column_width_recalc.should == 'foo'
cell.render.should == " \e[31mfoo\e[0m "
end
+
+ it "should render padding properly" do
+ @table = Terminal::Table.new(:rows => [['foo', '2'], ['3', '4']], :style => {:padding_right => 3})
+ cell = @table.rows.first.cells.first
+ cell.value.should == 'foo'
+ cell.alignment.should == :left
+ cell.render.should == " foo "
+ end
end
View
45 spec/table_spec.rb
@@ -95,8 +95,11 @@ module Terminal
@table.rows.size.should == 2
end
- it "should bitch and complain when you have no rows" do
- lambda { @table.render }.should raise_error(Terminal::Table::Error)
+ it "should render an empty table properly" do
+ @table.render.should == <<-EOF.deindent
+ ++
+ ++
+ EOF
end
it "should render properly" do
@@ -115,12 +118,30 @@ module Terminal
EOF
end
+ it "should render styles properly" do
+ @table.headings = ['Char', 'Num']
+ @table.style = {:border_x => "=", :border_y => ":", :border_i => "x", :padding_left => 0, :padding_right => 2}
+ @table << ['a', 1]
+ @table << ['b', 2]
+ @table << ['c', 3]
+ @table.style.padding_right.should == 2
+ @table.render.should == <<-EOF.deindent
+ x======x=====x
+ :Char :Num :
+ x======x=====x
+ :a :1 :
+ :b :2 :
+ :c :3 :
+ x======x=====x
+ EOF
+ end
+
it "should render width properly" do
@table.headings = ['Char', 'Num']
@table << ['a', 1]
@table << ['b', 2]
@table << ['c', 3]
- @table.width = 21
+ @table.style.width = 21
@table.render.should == <<-EOF.deindent
+---------+---------+
| Char | Num |
@@ -466,10 +487,8 @@ module Terminal
end
it "should render a table with only X cell borders" do
- Terminal::Table::X = "-"
- Terminal::Table::Y = ""
- Terminal::Table::I = ""
-
+ @table.style = {:border_x => "-", :border_y => "", :border_i => ""}
+
@table.headings = ['name', { :value => 'values', :alignment => :right, :colspan => 3}]
@table.headings = ['name', { :value => 'values', :colspan => 3}]
@table.rows = [['a', 1, 2, 3], ['b', 4, 5, 6], ['c', 7, 8, 9]]
@@ -483,16 +502,10 @@ module Terminal
c 7 8 9
---------------
EOF
-
- Terminal::Table::X = "-"
- Terminal::Table::Y = "|"
- Terminal::Table::I = "+"
end
it "should render a table without cell borders" do
- Terminal::Table::X = ""
- Terminal::Table::Y = ""
- Terminal::Table::I = ""
+ @table.style = {:border_x => "", :border_y => "", :border_i => ""}
@table.headings = ['name', { :value => 'values', :alignment => :right, :colspan => 3}]
@table.headings = ['name', { :value => 'values', :colspan => 3}]
@@ -506,10 +519,6 @@ module Terminal
b 4 5 6
c 7 8 9
EOF
-
- Terminal::Table::X = "-"
- Terminal::Table::Y = "|"
- Terminal::Table::I = "+"
end
end
end
Something went wrong with that request. Please try again.