Skip to content

Commit aab2c57

Browse files
committed
Add support for printing tables with borders
Adding borders to tables can improve their legibility. This would allow replacing the custom table for code statistics in Rails, with the generic implementation in Thor. By adding :separators to a table a horizontal separator will be added. This functionality was inspired by: https://github.com/piotrmurach/tty-table
1 parent 9c9ab52 commit aab2c57

File tree

3 files changed

+90
-14
lines changed

3 files changed

+90
-14
lines changed

lib/thor/shell/basic.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ def print_in_columns(array)
175175
# ==== Options
176176
# indent<Integer>:: Indent the first column by indent value.
177177
# colwidth<Integer>:: Force the first column to colwidth spaces wide.
178+
# borders<Boolean>:: Adds ascii borders.
178179
#
179180
def print_table(array, options = {}) # rubocop:disable Metrics/MethodLength
180181
printer = TablePrinter.new(stdout, options)

lib/thor/shell/table_printer.rb

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,55 +4,61 @@
44
class Thor
55
module Shell
66
class TablePrinter < ColumnPrinter
7+
BORDER_SEPARATOR = :separator
8+
79
def initialize(stdout, options = {})
810
super
911
@formats = []
1012
@maximas = []
1113
@colwidth = options[:colwidth]
1214
@truncate = options[:truncate] == true ? Terminal.terminal_width : options[:truncate]
15+
@padding = 1
1316
end
1417

1518
def print(array)
1619
return if array.empty?
1720

1821
prepare(array)
1922

23+
print_border_separator if options[:borders]
24+
2025
array.each do |row|
26+
if options[:borders] && row == BORDER_SEPARATOR
27+
print_border_separator
28+
next
29+
end
30+
2131
sentence = "".dup
2232

2333
row.each_with_index do |column, index|
24-
maxima = @maximas[index]
25-
26-
f = if column.is_a?(Numeric)
27-
if index == row.size - 1
28-
# Don't output 2 trailing spaces when printing the last column
29-
"%#{maxima}s"
30-
else
31-
"%#{maxima}s "
32-
end
33-
else
34-
@formats[index]
35-
end
36-
sentence << f % column.to_s
34+
sentence << format_cell(column, row.size, index)
3735
end
3836

3937
sentence = truncate(sentence)
38+
sentence << "|" if options[:borders]
4039
stdout.puts sentence
40+
4141
end
42+
print_border_separator if options[:borders]
4243
end
4344

4445
private
4546

4647
def prepare(array)
48+
array = array.reject{|row| row == BORDER_SEPARATOR }
49+
4750
@formats << "%-#{@colwidth + 2}s".dup if @colwidth
4851
start = @colwidth ? 1 : 0
4952

5053
colcount = array.max { |a, b| a.size <=> b.size }.size
5154

5255
start.upto(colcount - 1) do |index|
5356
maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max
57+
5458
@maximas << maxima
55-
@formats << if index == colcount - 1
59+
@formats << if options[:borders]
60+
"%-#{maxima}s".dup
61+
elsif index == colcount - 1
5662
# Don't output 2 trailing spaces when printing the last column
5763
"%-s".dup
5864
else
@@ -64,6 +70,37 @@ def prepare(array)
6470
@formats << "%s"
6571
end
6672

73+
def format_cell(column, row_size, index)
74+
maxima = @maximas[index]
75+
76+
f = if column.is_a?(Numeric)
77+
if options[:borders]
78+
# With borders we handle padding separately
79+
"%#{maxima}s"
80+
elsif index == row_size - 1
81+
# Don't output 2 trailing spaces when printing the last column
82+
"%#{maxima}s"
83+
else
84+
"%#{maxima}s "
85+
end
86+
else
87+
@formats[index]
88+
end
89+
90+
cell = "".dup
91+
cell << "|" + " " * @padding if options[:borders]
92+
cell << f % column.to_s
93+
cell << " " * @padding if options[:borders]
94+
cell
95+
end
96+
97+
def print_border_separator
98+
top = @maximas.map do |maxima|
99+
" " * @indent + "+" + "-" * (maxima + 2 * @padding)
100+
end
101+
stdout.puts top.join + "+"
102+
end
103+
67104
def truncate(string)
68105
return string unless @truncate
69106
as_unicode do

spec/shell/basic_spec.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,44 @@ def #456 Lanç...
432432
Erik 1234567890123 green
433433
TABLE
434434
end
435+
436+
it "prints a table with borders" do
437+
content = capture(:stdout) { shell.print_table(@table, borders: true) }
438+
expect(content).to eq(<<-TABLE)
439+
+-----+------+-------------+
440+
| abc | #123 | first three |
441+
| | #0 | empty |
442+
| xyz | #786 | last three |
443+
+-----+------+-------------+
444+
TABLE
445+
end
446+
447+
it "prints a table with borders and separators" do
448+
@table.insert(1, :separator)
449+
content = capture(:stdout) { shell.print_table(@table, borders: true) }
450+
expect(content).to eq(<<-TABLE)
451+
+-----+------+-------------+
452+
| abc | #123 | first three |
453+
+-----+------+-------------+
454+
| | #0 | empty |
455+
| xyz | #786 | last three |
456+
+-----+------+-------------+
457+
TABLE
458+
end
459+
460+
it "prints a table with borders and small numbers and right-aligns them" do
461+
table = [
462+
["Name", "Number", "Color"], # rubocop: disable Style/WordArray
463+
["Erik", 1, "green"]
464+
]
465+
content = capture(:stdout) { shell.print_table(table, borders: true) }
466+
expect(content).to eq(<<-TABLE)
467+
+------+--------+-------+
468+
| Name | Number | Color |
469+
| Erik | 1 | green |
470+
+------+--------+-------+
471+
TABLE
472+
end
435473
end
436474

437475
describe "#file_collision" do

0 commit comments

Comments
 (0)