terminal-table 1.7.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yml +28 -0
- data/.gitignore +5 -1
- data/Gemfile +2 -0
- data/Gemfile.lock +49 -0
- data/History.rdoc +64 -0
- data/LICENSE.txt +21 -0
- data/README.md +391 -0
- data/Rakefile +10 -4
- data/examples/examples.rb +0 -0
- data/examples/examples_unicode.rb +89 -0
- data/examples/issue100.rb +34 -0
- data/examples/issue111.rb +4 -0
- data/examples/issue95.rb +42 -0
- data/examples/strong_separator.rb +23 -0
- data/lib/terminal-table.rb +2 -2
- data/lib/terminal-table/cell.rb +3 -11
- data/lib/terminal-table/row.rb +21 -3
- data/lib/terminal-table/separator.rb +56 -4
- data/lib/terminal-table/style.rb +227 -10
- data/lib/terminal-table/table.rb +183 -48
- data/lib/terminal-table/util.rb +13 -0
- data/lib/terminal-table/version.rb +1 -1
- data/terminal-table.gemspec +3 -3
- metadata +27 -13
- data/README.rdoc +0 -238
data/Rakefile
CHANGED
@@ -1,8 +1,14 @@
|
|
1
|
-
require
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.setup
|
3
|
+
Bundler::GemHelper.install_tasks
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
|
5
|
+
require 'rake'
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
|
8
|
+
desc "Run all examples"
|
9
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
10
|
+
t.ruby_opts = %w[-w]
|
11
|
+
t.rspec_opts = %w[--color]
|
6
12
|
end
|
7
13
|
|
8
14
|
desc "Default: Run specs"
|
data/examples/examples.rb
CHANGED
File without changes
|
@@ -0,0 +1,89 @@
|
|
1
|
+
#!/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
4
|
+
require 'terminal-table/import'
|
5
|
+
|
6
|
+
Terminal::Table::Style.defaults = { :border => :unicode_round }
|
7
|
+
# Terminal::Table::UnicodeThickEdgeBorder.new()
|
8
|
+
|
9
|
+
puts
|
10
|
+
puts table(['a', 'b'], [1, 2], [3, 4])
|
11
|
+
|
12
|
+
puts
|
13
|
+
puts table(['name', 'content'], ['ftp.example.com', '1.1.1.1'], ['www.example.com', '|lalalala|lalala|'])
|
14
|
+
|
15
|
+
puts
|
16
|
+
t = table ['a', 'b']
|
17
|
+
t.style = {:padding_left => 2, :width => 80}
|
18
|
+
t << [1, 2]
|
19
|
+
t << [3, 4]
|
20
|
+
t << :separator
|
21
|
+
t << [4, 6]
|
22
|
+
puts t
|
23
|
+
|
24
|
+
puts
|
25
|
+
user_table = table do |v|
|
26
|
+
v.title = "Contact Information"
|
27
|
+
v.headings = 'First Name', 'Last Name', 'Email'
|
28
|
+
v << %w( TJ Holowaychuk tj@vision-media.ca )
|
29
|
+
v << %w( Bob Someone bob@vision-media.ca )
|
30
|
+
v << %w( Joe Whatever bob@vision-media.ca )
|
31
|
+
end
|
32
|
+
puts user_table
|
33
|
+
|
34
|
+
puts
|
35
|
+
user_table = table do |v|
|
36
|
+
v.style.width = 80
|
37
|
+
v.headings = 'First Name', 'Last Name', 'Email'
|
38
|
+
v << %w( TJ Holowaychuk tj@vision-media.ca )
|
39
|
+
v << %w( Bob Someone bob@vision-media.ca )
|
40
|
+
v << %w( Joe Whatever bob@vision-media.ca )
|
41
|
+
end
|
42
|
+
puts user_table
|
43
|
+
|
44
|
+
puts
|
45
|
+
user_table = table do
|
46
|
+
self.headings = 'First Name', 'Last Name', 'Email'
|
47
|
+
add_row ['TJ', 'Holowaychuk', 'tj@vision-media.ca']
|
48
|
+
add_row ['Bob', 'Someone', 'bob@vision-media.ca']
|
49
|
+
add_row ['Joe', 'Whatever', 'joe@vision-media.ca']
|
50
|
+
add_separator
|
51
|
+
add_row ['Total', { :value => '3', :colspan => 2, :alignment => :right }]
|
52
|
+
align_column 1, :center
|
53
|
+
end
|
54
|
+
puts user_table
|
55
|
+
|
56
|
+
puts
|
57
|
+
user_table = table do
|
58
|
+
self.headings = ['First Name', 'Last Name', {:value => 'Phones', :colspan => 2, :alignment => :center}]
|
59
|
+
#add_row ['Bob', 'Someone', '123', '456']
|
60
|
+
add_row [{:value => "Bob Someone", :colspan => 3, :alignment => :center}, '123456']
|
61
|
+
add_row :separator
|
62
|
+
add_row ['TJ', 'Holowaychuk', {:value => "No phones\navaiable", :colspan => 2, :alignment => :center}]
|
63
|
+
add_row :separator
|
64
|
+
add_row ['Joe', 'Whatever', '4324', '343242']
|
65
|
+
end
|
66
|
+
puts user_table
|
67
|
+
|
68
|
+
rows = []
|
69
|
+
rows << ['Lines', 100]
|
70
|
+
rows << ['Comments', 20]
|
71
|
+
rows << ['Ruby', 70]
|
72
|
+
rows << ['JavaScript', 30]
|
73
|
+
puts table([nil, 'Lines'], *rows)
|
74
|
+
|
75
|
+
rows = []
|
76
|
+
rows << ['Lines', 100]
|
77
|
+
rows << ['Comments', 20]
|
78
|
+
rows << ['Ruby', 70]
|
79
|
+
rows << ['JavaScript', 30]
|
80
|
+
puts table(nil, *rows)
|
81
|
+
|
82
|
+
rows = []
|
83
|
+
rows << ['Lines', 100]
|
84
|
+
rows << ['Comments', 20]
|
85
|
+
rows << ['Ruby', 70]
|
86
|
+
rows << ['JavaScript', 30]
|
87
|
+
table = table([{ :value => 'Stats', :colspan => 2, :alignment => :center }], *rows)
|
88
|
+
table.align_column 1, :right
|
89
|
+
puts table
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Methods to suppress left/right borders using border_left & border_right
|
4
|
+
|
5
|
+
require_relative "../lib/terminal-table"
|
6
|
+
table = Terminal::Table.new do |t|
|
7
|
+
t.headings = ['id', 'name']
|
8
|
+
t.rows = [[1, 'One'], [2, 'Two'], [3, 'Three']]
|
9
|
+
t.style = { :border_left => false, :border_top => false, :border_bottom => false }
|
10
|
+
end
|
11
|
+
|
12
|
+
puts table
|
13
|
+
puts
|
14
|
+
|
15
|
+
# no right
|
16
|
+
table.style = {:border_right => false }
|
17
|
+
puts table
|
18
|
+
puts
|
19
|
+
|
20
|
+
# no right
|
21
|
+
table.style = {:border_left => true }
|
22
|
+
puts table
|
23
|
+
puts
|
24
|
+
|
25
|
+
table.style.border = Terminal::Table::UnicodeBorder.new
|
26
|
+
puts table
|
27
|
+
|
28
|
+
|
29
|
+
table.style = {:border_right => false, :border_left => true }
|
30
|
+
puts table
|
31
|
+
|
32
|
+
table.style = {:border_right => true, :border_left => false }
|
33
|
+
puts table
|
34
|
+
|
data/examples/issue95.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'colorize'
|
3
|
+
require_relative '../lib/terminal-table.rb'
|
4
|
+
|
5
|
+
original_sample_data = [
|
6
|
+
["Sep 2016", 33, [-38, -53.52], 46, [-25, -35.21]],
|
7
|
+
["Oct 2016", 35, [2, 6.06], 50, [4, 8.69]]
|
8
|
+
]
|
9
|
+
|
10
|
+
table = Terminal::Table.new headings: ["Month".cyan,"Monthly IT".cyan,"IT Difference OPM".cyan,
|
11
|
+
"Monthly OOT".cyan,"OOT Difference OPM".cyan], rows: original_sample_data
|
12
|
+
|
13
|
+
table.style = { padding_left: 2, padding_right: 2, border_x: "-".blue, border_y: "|".blue, border_i: "+".blue }
|
14
|
+
|
15
|
+
puts table
|
16
|
+
|
17
|
+
puts ""
|
18
|
+
puts "^ good table"
|
19
|
+
puts "v wonky table"
|
20
|
+
puts ""
|
21
|
+
|
22
|
+
split_column_sample_data = [
|
23
|
+
["Sep 2016", 33, -38, -53.52, 46, -25, -35.21],
|
24
|
+
["Oct 2016", 35, 2, 6.06, 50, 4, 8.69]
|
25
|
+
]
|
26
|
+
|
27
|
+
table = Terminal::Table.new headings: ["Month".cyan,"Monthly IT".cyan,
|
28
|
+
{value: "IT Difference OPM".cyan, colspan: 2}, "Monthly OOT".cyan,
|
29
|
+
{value: "OOT Difference OPM".cyan, colspan: 2}], rows: split_column_sample_data
|
30
|
+
|
31
|
+
table.style = { padding_left: 2, padding_right: 2, border_x: "-".blue, border_y: "|".blue, border_i: "+".blue }
|
32
|
+
|
33
|
+
puts table
|
34
|
+
|
35
|
+
|
36
|
+
table = Terminal::Table.new headings: ["Month","Monthly IT",
|
37
|
+
{value: "IT Difference OPM", colspan: 2}, "Monthly OOT",
|
38
|
+
{value: "OOT Difference OPM", colspan: 2}], rows: split_column_sample_data
|
39
|
+
|
40
|
+
table.style = { padding_left: 2, padding_right: 2, border_x: "-".blue, border_y: "|".cyan, border_i: "+" }
|
41
|
+
|
42
|
+
puts table
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require_relative "../lib/terminal-table"
|
3
|
+
|
4
|
+
#
|
5
|
+
# An example of how to manually add separators with non-default
|
6
|
+
# border_type to enable a footer row.
|
7
|
+
#
|
8
|
+
table = Terminal::Table.new do |t|
|
9
|
+
# set the style
|
10
|
+
t.style = { border: :unicode_thick_edge }
|
11
|
+
|
12
|
+
# header row
|
13
|
+
t.headings = ['fruit', 'count']
|
14
|
+
|
15
|
+
# some row data
|
16
|
+
t.add_row ['apples', 7]
|
17
|
+
t.add_row ['bananas', 19]
|
18
|
+
t.add_separator border_type: :strong
|
19
|
+
# footer row
|
20
|
+
t.add_row ['total', 26]
|
21
|
+
end
|
22
|
+
|
23
|
+
puts table.render
|
data/lib/terminal-table.rb
CHANGED
@@ -21,6 +21,6 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
#++
|
23
23
|
|
24
|
-
%w(cell row separator style table table_helper version).each do |file|
|
25
|
-
|
24
|
+
%w(cell row separator style table table_helper util version).each do |file|
|
25
|
+
require_relative "./terminal-table/#{file}"
|
26
26
|
end
|
data/lib/terminal-table/cell.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'unicode/display_width'
|
1
|
+
require 'unicode/display_width/no_string_ext'
|
2
2
|
|
3
3
|
module Terminal
|
4
4
|
class Table
|
@@ -57,7 +57,7 @@ module Terminal
|
|
57
57
|
def render(line = 0)
|
58
58
|
left = " " * @table.style.padding_left
|
59
59
|
right = " " * @table.style.padding_right
|
60
|
-
display_width = Unicode::DisplayWidth.of(
|
60
|
+
display_width = Unicode::DisplayWidth.of(Util::ansi_escape(lines[line]))
|
61
61
|
render_width = lines[line].to_s.size - display_width + width
|
62
62
|
align("#{left}#{lines[line]}#{right}", alignment, render_width + @table.cell_padding)
|
63
63
|
end
|
@@ -68,7 +68,7 @@ module Terminal
|
|
68
68
|
# removes all ANSI escape sequences (e.g. color)
|
69
69
|
|
70
70
|
def value_for_column_width_recalc
|
71
|
-
lines.map{ |s|
|
71
|
+
lines.map{ |s| Util::ansi_escape(s) }.max_by{ |s| Unicode::DisplayWidth.of(s) }
|
72
72
|
end
|
73
73
|
|
74
74
|
##
|
@@ -81,14 +81,6 @@ module Terminal
|
|
81
81
|
end
|
82
82
|
inner_width + padding
|
83
83
|
end
|
84
|
-
|
85
|
-
##
|
86
|
-
# removes all ANSI escape sequences (e.g. color)
|
87
|
-
def escape(line)
|
88
|
-
line.to_s.gsub(/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/, '').
|
89
|
-
gsub(/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/, '').
|
90
|
-
gsub(/(\x03|\x1a)/, '')
|
91
|
-
end
|
92
84
|
end
|
93
85
|
end
|
94
86
|
end
|
data/lib/terminal-table/row.rb
CHANGED
@@ -36,13 +36,31 @@ module Terminal
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def render
|
39
|
-
|
39
|
+
vleft, vcenter, vright = @table.style.vertical
|
40
40
|
(0...height).to_a.map do |line|
|
41
|
-
|
41
|
+
vleft + cells.map do |cell|
|
42
42
|
cell.render(line)
|
43
|
-
end.join(
|
43
|
+
end.join(vcenter) + vright
|
44
44
|
end.join("\n")
|
45
45
|
end
|
46
|
+
|
47
|
+
def number_of_columns
|
48
|
+
@cells.collect(&:colspan).inject(0, &:+)
|
49
|
+
end
|
50
|
+
|
51
|
+
# used to find indices where we have table '+' crossings.
|
52
|
+
# in cases where the colspan > 1, then we will skip over some numbers
|
53
|
+
# if colspan is always 1, then the list should be incrementing by 1.
|
54
|
+
#
|
55
|
+
# skip 0 entry, because it's the left side.
|
56
|
+
# skip last entry, because it's the right side.
|
57
|
+
# we only care about "+/T" style crossings.
|
58
|
+
def crossings
|
59
|
+
idx = 0
|
60
|
+
@cells[0...-1].map { |c| idx += c.colspan }
|
61
|
+
end
|
62
|
+
|
46
63
|
end
|
64
|
+
|
47
65
|
end
|
48
66
|
end
|
@@ -2,13 +2,65 @@ module Terminal
|
|
2
2
|
class Table
|
3
3
|
class Separator < Row
|
4
4
|
|
5
|
+
##
|
6
|
+
# `prevrow`, `nextrow` contain references to adjacent rows.
|
7
|
+
#
|
8
|
+
# `border_type` is a symbol used to control which type of border is used
|
9
|
+
# on the separator (:top for top-edge, :bot for bottom-edge,
|
10
|
+
# :div for interior, and :strong for emphasized-interior)
|
11
|
+
#
|
12
|
+
# `implicit` is false for user-added separators, and true for
|
13
|
+
# implicit/auto-generated separators.
|
14
|
+
|
15
|
+
def initialize(*args, border_type: :div, implicit: false)
|
16
|
+
super
|
17
|
+
@prevrow, @nextrow = nil, nil
|
18
|
+
@border_type = border_type
|
19
|
+
@implicit = implicit
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_accessor :border_type
|
23
|
+
attr_reader :implicit
|
24
|
+
|
5
25
|
def render
|
6
|
-
|
7
|
-
|
26
|
+
left_edge, ctrflat, ctrud, right_edge, ctrdn, ctrup = @table.style.horizontal(border_type)
|
27
|
+
|
28
|
+
prev_crossings = @prevrow.respond_to?(:crossings) ? @prevrow.crossings : []
|
29
|
+
next_crossings = @nextrow.respond_to?(:crossings) ? @nextrow.crossings : []
|
30
|
+
rval = [left_edge]
|
31
|
+
numcols = @table.number_of_columns
|
32
|
+
(0...numcols).each do |idx|
|
33
|
+
rval << ctrflat * (@table.column_width(idx) + @table.cell_padding)
|
34
|
+
pcinc = prev_crossings.include?(idx+1)
|
35
|
+
ncinc = next_crossings.include?(idx+1)
|
36
|
+
border_center = if pcinc && ncinc
|
37
|
+
ctrud
|
38
|
+
elsif pcinc
|
39
|
+
ctrup
|
40
|
+
elsif ncinc
|
41
|
+
ctrdn
|
42
|
+
elsif !ctrud.empty?
|
43
|
+
# special case if the center-up-down intersection is empty
|
44
|
+
# which happens when verticals/intersections are removed. in that case
|
45
|
+
# we do not want to replace with a flat element so return empty-string in else block
|
46
|
+
ctrflat
|
47
|
+
else
|
48
|
+
''
|
49
|
+
end
|
50
|
+
rval << border_center if idx < numcols-1
|
8
51
|
end
|
9
|
-
|
10
|
-
|
52
|
+
|
53
|
+
rval << right_edge
|
54
|
+
rval.join
|
55
|
+
end
|
56
|
+
|
57
|
+
# Save off neighboring rows, so that we can use them later in determining
|
58
|
+
# which types of table edges to use.
|
59
|
+
def save_adjacent_rows(prevrow, nextrow)
|
60
|
+
@prevrow = prevrow
|
61
|
+
@nextrow = nextrow
|
11
62
|
end
|
63
|
+
|
12
64
|
end
|
13
65
|
end
|
14
66
|
end
|
data/lib/terminal-table/style.rb
CHANGED
@@ -1,5 +1,171 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'forwardable'
|
3
|
+
|
1
4
|
module Terminal
|
2
5
|
class Table
|
6
|
+
|
7
|
+
class Border
|
8
|
+
|
9
|
+
attr_accessor :data, :top, :bottom, :left, :right
|
10
|
+
def initialize
|
11
|
+
@top, @bottom, @left, @right = true, true, true, true
|
12
|
+
end
|
13
|
+
def []=(key, val)
|
14
|
+
@data[key] = val
|
15
|
+
end
|
16
|
+
def [](key)
|
17
|
+
@data[key]
|
18
|
+
end
|
19
|
+
def initialize_dup(other)
|
20
|
+
super
|
21
|
+
@data = other.data.dup
|
22
|
+
end
|
23
|
+
def remove_verticals
|
24
|
+
self.class.const_get("VERTICALS").each { |key| @data[key] = "" }
|
25
|
+
self.class.const_get("INTERSECTIONS").each { |key| @data[key] = "" }
|
26
|
+
end
|
27
|
+
def remove_horizontals
|
28
|
+
self.class.const_get("HORIZONTALS").each { |key| @data[key] = "" }
|
29
|
+
end
|
30
|
+
|
31
|
+
# If @left, return the edge else empty-string.
|
32
|
+
def maybeleft(key) ; @left ? @data[key] : '' ; end
|
33
|
+
|
34
|
+
# If @right, return the edge else empty-string.
|
35
|
+
def mayberight(key) ; @right ? @data[key] : '' ; end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class AsciiBorder < Border
|
40
|
+
HORIZONTALS = %i[x]
|
41
|
+
VERTICALS = %i[y]
|
42
|
+
INTERSECTIONS = %i[i]
|
43
|
+
|
44
|
+
def initialize
|
45
|
+
super
|
46
|
+
@data = { x: "-", y: "|", i: "+" }
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get vertical border elements
|
50
|
+
# @return [Array] 3-element list of [left, center, right]
|
51
|
+
def vertical
|
52
|
+
[maybeleft(:y), @data[:y], mayberight(:y)] # left, center, right
|
53
|
+
end
|
54
|
+
|
55
|
+
# Get horizontal border elements
|
56
|
+
# @return [Array] a 6 element list of: [i-left, horizontal-bar, i-up/down, i-right, i-down, i-up]
|
57
|
+
def horizontal(_type)
|
58
|
+
x, i = @data[:x], @data[:i]
|
59
|
+
[maybeleft(:i), x, i, mayberight(:i), i, i]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class MarkdownBorder < AsciiBorder
|
64
|
+
def initialize
|
65
|
+
super
|
66
|
+
@top, @bottom = false, false
|
67
|
+
@data = { x: "-", y: "|", i: "|" }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class UnicodeBorder < Border
|
72
|
+
|
73
|
+
ALLOWED_SEPARATOR_BORDER_STYLES = %i[
|
74
|
+
top bot
|
75
|
+
div dash dot3 dot4
|
76
|
+
thick thick_dash thick_dot3 thick_dot4
|
77
|
+
heavy heavy_dash heavy_dot3 heavy_dot4
|
78
|
+
bold bold_dash bold_dot3 bold_dot4
|
79
|
+
double
|
80
|
+
]
|
81
|
+
|
82
|
+
HORIZONTALS = %i[x sx ax bx nx bx_dot3 bx_dot4 bx_dash x_dot3 x_dot4 x_dash]
|
83
|
+
VERTICALS = %i[y yw ye]
|
84
|
+
INTERSECTIONS = %i[nw n ne nd
|
85
|
+
aw ai ae ad au
|
86
|
+
bw bi be bd bu
|
87
|
+
w i e dn up
|
88
|
+
sw s se su]
|
89
|
+
def initialize
|
90
|
+
super
|
91
|
+
@data = {
|
92
|
+
nil => nil,
|
93
|
+
nw: "┌", nx: "─", n: "┬", ne: "┐",
|
94
|
+
yw: "│", y: "│", ye: "│",
|
95
|
+
aw: "╞", ax: "═", ai: "╪", ae: "╡", ad: '╤', au: "╧", # double
|
96
|
+
bw: "┝", bx: "━", bi: "┿", be: "┥", bd: '┯', bu: "┷", # heavy/bold/thick
|
97
|
+
w: "├", x: "─", i: "┼", e: "┤", dn: "┬", up: "┴", # normal div
|
98
|
+
sw: "└", sx: "─", s: "┴", se: "┘",
|
99
|
+
# alternative dots/dashes
|
100
|
+
x_dot4: '┈', x_dot3: '┄', x_dash: '╌',
|
101
|
+
bx_dot4: '┉', bx_dot3: '┅', bx_dash: '╍',
|
102
|
+
}
|
103
|
+
end
|
104
|
+
# Get vertical border elements
|
105
|
+
# @return [Array] 3-element list of [left, center, right]
|
106
|
+
def vertical
|
107
|
+
[maybeleft(:yw), @data[:y], mayberight(:ye)]
|
108
|
+
end
|
109
|
+
|
110
|
+
# Get horizontal border elements
|
111
|
+
# @return [Array] a 6 element list of: [i-left, horizontal-bar, i-up/down, i-right, i-down, i-up]
|
112
|
+
def horizontal(type)
|
113
|
+
raise ArgumentError, "Border type is #{type.inspect}, must be one of #{ALLOWED_SEPARATOR_BORDER_STYLES.inspect}" unless ALLOWED_SEPARATOR_BORDER_STYLES.include?(type)
|
114
|
+
lookup = case type
|
115
|
+
when :top
|
116
|
+
[:nw, :nx, :n, :ne, :n, nil]
|
117
|
+
when :bot
|
118
|
+
[:sw, :sx, :s, :se, nil, :s]
|
119
|
+
when :double
|
120
|
+
# typically used for the separator below the heading row or above a footer row)
|
121
|
+
[:aw, :ax, :ai, :ae, :ad, :au]
|
122
|
+
when :thick, :thick_dash, :thick_dot3, :thick_dot4,
|
123
|
+
:heavy, :heavy_dash, :heavy_dot3, :heavy_dot4,
|
124
|
+
:bold, :bold_dash, :bold_dot3, :bold_dot4
|
125
|
+
# alternate thick/bold border
|
126
|
+
xref = type.to_s.sub(/^(thick|heavy|bold)/,'bx').to_sym
|
127
|
+
[:bw, xref, :bi, :be, :bd, :bu]
|
128
|
+
when :dash, :dot3, :dot4
|
129
|
+
# alternate thin dividers
|
130
|
+
xref = "x_#{type}".to_sym
|
131
|
+
[:w, xref, :i, :e, :dn, :up]
|
132
|
+
else # :div (center, non-emphasized)
|
133
|
+
[:w, :x, :i, :e, :dn, :up]
|
134
|
+
end
|
135
|
+
rval = lookup.map { |key| @data.fetch(key) }
|
136
|
+
rval[0] = '' unless @left
|
137
|
+
rval[3] = '' unless @right
|
138
|
+
rval
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Unicode Border With rounded edges
|
143
|
+
class UnicodeRoundBorder < UnicodeBorder
|
144
|
+
def initialize
|
145
|
+
super
|
146
|
+
@data.merge!({nw: '╭', ne: '╮', sw: '╰', se: '╯'})
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Unicode Border with thick outer edges
|
151
|
+
class UnicodeThickEdgeBorder < UnicodeBorder
|
152
|
+
def initialize
|
153
|
+
super
|
154
|
+
@data = {
|
155
|
+
nil => nil,
|
156
|
+
nw: "┏", nx: "━", n: "┯", ne: "┓", nd: nil,
|
157
|
+
yw: "┃", y: "│", ye: "┃",
|
158
|
+
aw: "┣", ax: "═", ai: "╪", ae: "┫", ad: '╤', au: "╧", # double
|
159
|
+
bw: "┣", bx: "━", bi: "┿", be: "┫", bd: '┯', bu: "┷", # heavy/bold/thick
|
160
|
+
w: "┠", x: "─", i: "┼", e: "┨", dn: "┬", up: "┴", # normal div
|
161
|
+
sw: "┗", sx: "━", s: "┷", se: "┛", su: nil,
|
162
|
+
# alternative dots/dashes
|
163
|
+
x_dot4: '┈', x_dot3: '┄', x_dash: '╌',
|
164
|
+
bx_dot4: '┉', bx_dot3: '┅', bx_dash: '╍',
|
165
|
+
}
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
3
169
|
# A Style object holds all the formatting information for a Table object
|
4
170
|
#
|
5
171
|
# To create a table with a certain style, use either the constructor
|
@@ -22,17 +188,51 @@ module Terminal
|
|
22
188
|
# Terminal::Table::Style.defaults = {:width => 80}
|
23
189
|
#
|
24
190
|
class Style
|
191
|
+
extend Forwardable
|
192
|
+
def_delegators :@border, :vertical, :horizontal, :remove_verticals, :remove_horizontals
|
193
|
+
|
25
194
|
@@defaults = {
|
26
|
-
:
|
195
|
+
:border => AsciiBorder.new,
|
27
196
|
:padding_left => 1, :padding_right => 1,
|
28
197
|
:margin_left => '',
|
29
198
|
:width => nil, :alignment => nil,
|
30
|
-
:all_separators => false
|
199
|
+
:all_separators => false,
|
31
200
|
}
|
32
201
|
|
33
|
-
|
34
|
-
|
35
|
-
|
202
|
+
## settors/gettor for legacy ascii borders
|
203
|
+
def border_x=(val) ; @border[:x] = val ; end
|
204
|
+
def border_y=(val) ; @border[:y] = val ; end
|
205
|
+
def border_i=(val) ; @border[:i] = val ; end
|
206
|
+
def border_y ; @border[:y] ; end
|
207
|
+
def border_y_width ; Util::ansi_escape(@border[:y]).length ; end
|
208
|
+
|
209
|
+
# Accessor for instance of Border
|
210
|
+
attr_reader :border
|
211
|
+
def border=(val)
|
212
|
+
if val.is_a? Symbol
|
213
|
+
# convert symbol name like :foo_bar to get class FooBarBorder
|
214
|
+
klass_str = val.to_s.split('_').collect(&:capitalize).join + "Border"
|
215
|
+
begin
|
216
|
+
klass = Terminal::Table::const_get(klass_str)
|
217
|
+
@border = klass.new
|
218
|
+
rescue NameError
|
219
|
+
raise "Cannot lookup class Terminal::Table::#{klass_str} from symbol #{val.inspect}"
|
220
|
+
end
|
221
|
+
else
|
222
|
+
@border = val
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def border_top=(val) ; @border.top = val ; end
|
227
|
+
def border_bottom=(val) ; @border.bottom = val ; end
|
228
|
+
def border_left=(val) ; @border.left = val ; end
|
229
|
+
def border_right=(val) ; @border.right = val ; end
|
230
|
+
|
231
|
+
def border_top ; @border.top ; end
|
232
|
+
def border_bottom ; @border.bottom ; end
|
233
|
+
def border_left ; @border.left ; end
|
234
|
+
def border_right ; @border.right ; end
|
235
|
+
|
36
236
|
|
37
237
|
attr_accessor :padding_left
|
38
238
|
attr_accessor :padding_right
|
@@ -44,24 +244,41 @@ module Terminal
|
|
44
244
|
|
45
245
|
attr_accessor :all_separators
|
46
246
|
|
47
|
-
|
247
|
+
|
48
248
|
def initialize options = {}
|
49
249
|
apply self.class.defaults.merge(options)
|
50
250
|
end
|
51
251
|
|
52
252
|
def apply options
|
53
|
-
options.each
|
253
|
+
options.each do |m, v|
|
254
|
+
__send__ "#{m}=", v
|
255
|
+
end
|
54
256
|
end
|
55
|
-
|
257
|
+
|
56
258
|
class << self
|
57
259
|
def defaults
|
58
|
-
@@defaults
|
260
|
+
klass_defaults = @@defaults.dup
|
261
|
+
# border is an object that needs to be duplicated on instantiation,
|
262
|
+
# otherwise everything will be referencing the same object-id.
|
263
|
+
klass_defaults[:border] = klass_defaults[:border].dup
|
264
|
+
klass_defaults
|
59
265
|
end
|
60
|
-
|
266
|
+
|
61
267
|
def defaults= options
|
62
268
|
@@defaults = defaults.merge(options)
|
63
269
|
end
|
270
|
+
|
271
|
+
end
|
272
|
+
|
273
|
+
def on_change attr
|
274
|
+
method_name = :"#{attr}="
|
275
|
+
old_method = method method_name
|
276
|
+
define_singleton_method(method_name) do |value|
|
277
|
+
old_method.call value
|
278
|
+
yield attr.to_sym, value
|
279
|
+
end
|
64
280
|
end
|
281
|
+
|
65
282
|
end
|
66
283
|
end
|
67
284
|
end
|