tty 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +1 -1
- data/README.md +19 -7
- data/lib/tty.rb +10 -0
- data/lib/tty/support/equatable.rb +1 -0
- data/lib/tty/support/unicode.rb +36 -0
- data/lib/tty/table.rb +3 -3
- data/lib/tty/table/border.rb +104 -0
- data/lib/tty/table/border/ascii.rb +41 -0
- data/lib/tty/table/border/null.rb +49 -0
- data/lib/tty/table/border/unicode.rb +41 -0
- data/lib/tty/table/column_set.rb +77 -0
- data/lib/tty/table/operation/alignment.rb +2 -0
- data/lib/tty/table/operation/alignment_set.rb +4 -58
- data/lib/tty/table/operation/truncation.rb +15 -19
- data/lib/tty/table/operation/wrapped.rb +34 -0
- data/lib/tty/table/renderer.rb +8 -11
- data/lib/tty/table/renderer/ascii.rb +15 -0
- data/lib/tty/table/renderer/basic.rb +21 -34
- data/lib/tty/table/renderer/unicode.rb +2 -2
- data/lib/tty/vector.rb +117 -0
- data/lib/tty/version.rb +1 -1
- data/spec/tty/table/border/ascii/rendering_spec.rb +49 -0
- data/spec/tty/table/border/new_spec.rb +21 -0
- data/spec/tty/table/border/null/rendering_spec.rb +49 -0
- data/spec/tty/table/border/unicode/rendering_spec.rb +49 -0
- data/spec/tty/table/column_set/extract_widths_spec.rb +26 -0
- data/spec/tty/table/operation/alignment_set/align_rows_spec.rb +4 -4
- data/spec/tty/table/operation/alignment_set/new_spec.rb +1 -1
- data/spec/tty/table/operation/wrapped/wrap_spec.rb +22 -0
- data/spec/tty/table/properties_spec.rb +3 -0
- data/spec/tty/table/renderer/basic/render_spec.rb +144 -33
- data/spec/tty/table/renderer/pick_renderer_spec.rb +25 -0
- data/spec/tty/table/to_s_spec.rb +51 -0
- data/spec/tty/vector/new_spec.rb +47 -0
- metadata +35 -8
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -10,8 +10,7 @@ Toolbox for developing CLI clients in Ruby.
|
|
10
10
|
|
11
11
|
Jump-start development of your command line app:
|
12
12
|
|
13
|
-
* Fully customizable table rendering with an easy-to-use API.
|
14
|
-
(status: In Progress)
|
13
|
+
* Fully customizable table rendering with an easy-to-use API. (status: In Progress)
|
15
14
|
* Terminal output colorization. (status: TODO)
|
16
15
|
* Terminal & System detection utilities. (status: In Progress)
|
17
16
|
* Text alignment/padding and diffs. (status: TODO)
|
@@ -85,15 +84,24 @@ or pass your rows in a block
|
|
85
84
|
And then to print do
|
86
85
|
|
87
86
|
```ruby
|
88
|
-
table.to_s
|
89
|
-
|
87
|
+
table.to_s
|
88
|
+
|
89
|
+
a1 a2 a3
|
90
|
+
b1 b2 b3
|
90
91
|
```
|
91
92
|
|
92
|
-
To print `
|
93
|
+
To print border around data table you need to specify `renderer` type out of `basic`, `ascii`, `unicode`. For instance to output unicode border:
|
93
94
|
|
94
|
-
```
|
95
|
-
table = TTY::Table.new renderer: 'unicode'
|
95
|
+
```
|
96
|
+
table = TTY::Table.new ['header1', 'header2'], [['a1', 'a2'], ['b1', 'b2'], renderer: 'unicode'
|
96
97
|
table.to_s
|
98
|
+
|
99
|
+
┌───────┬───────┐
|
100
|
+
│header1│header2│
|
101
|
+
├───────┼───────┤
|
102
|
+
│a1 │a2 │
|
103
|
+
│b1 │b2 │
|
104
|
+
└───────┴───────┘
|
97
105
|
```
|
98
106
|
|
99
107
|
### Terminal
|
@@ -105,6 +113,10 @@ To print `unicode` table
|
|
105
113
|
term.color? # => true or false
|
106
114
|
```
|
107
115
|
|
116
|
+
### Shell
|
117
|
+
|
118
|
+
Main responsibility is to interact with the prompt and provide convenience methods.
|
119
|
+
|
108
120
|
### System
|
109
121
|
|
110
122
|
```ruby
|
data/lib/tty.rb
CHANGED
@@ -7,15 +7,25 @@ require 'tty/support/delegatable'
|
|
7
7
|
require 'tty/support/conversion'
|
8
8
|
require 'tty/support/coercion'
|
9
9
|
require 'tty/support/equatable'
|
10
|
+
require 'tty/support/unicode'
|
10
11
|
|
11
12
|
require 'tty/color'
|
12
13
|
require 'tty/terminal'
|
13
14
|
require 'tty/system'
|
14
15
|
require 'tty/table'
|
16
|
+
require 'tty/vector'
|
17
|
+
|
18
|
+
require 'tty/table/border'
|
19
|
+
require 'tty/table/border/unicode'
|
20
|
+
require 'tty/table/border/ascii'
|
21
|
+
require 'tty/table/border/null'
|
22
|
+
|
23
|
+
require 'tty/table/column_set'
|
15
24
|
|
16
25
|
require 'tty/table/operation/alignment_set'
|
17
26
|
require 'tty/table/operation/alignment'
|
18
27
|
require 'tty/table/operation/truncation'
|
28
|
+
require 'tty/table/operation/wrapped'
|
19
29
|
|
20
30
|
module TTY
|
21
31
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
# A mixin to provide unicode support.
|
5
|
+
module Unicode
|
6
|
+
|
7
|
+
def utf8?(string)
|
8
|
+
string.unpack('U*') rescue return false
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def clean_utf8(string)
|
13
|
+
require 'iconv'
|
14
|
+
if defined? ::Iconv
|
15
|
+
converter = Iconv.new('UTF-8//IGNORE', 'UTF-8')
|
16
|
+
converter.iconv(string)
|
17
|
+
end
|
18
|
+
rescue Exception
|
19
|
+
string
|
20
|
+
end
|
21
|
+
|
22
|
+
if "".respond_to?(:encode)
|
23
|
+
def as_unicode
|
24
|
+
yield
|
25
|
+
end
|
26
|
+
else
|
27
|
+
def as_unicode
|
28
|
+
old, $KCODE = $KCODE, "U"
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
$KCODE = old
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end # Unicode
|
36
|
+
end # TTY
|
data/lib/tty/table.rb
CHANGED
@@ -111,8 +111,9 @@ module TTY
|
|
111
111
|
@rows = coerce(options.fetch :rows, [])
|
112
112
|
@renderer = pick_renderer options[:renderer]
|
113
113
|
# TODO: assert that row_size is the same as column widths & aligns
|
114
|
+
# TODO: this is where column extraction should happen!
|
114
115
|
@column_widths = options.fetch :column_widths, []
|
115
|
-
@alignments = Operation::AlignmentSet.new options[:column_aligns]
|
116
|
+
@alignments = Operation::AlignmentSet.new options[:column_aligns] || []
|
116
117
|
|
117
118
|
assert_row_sizes @rows
|
118
119
|
yield_or_eval &block if block_given?
|
@@ -267,8 +268,7 @@ module TTY
|
|
267
268
|
#
|
268
269
|
# @api public
|
269
270
|
def width
|
270
|
-
|
271
|
-
total_width
|
271
|
+
ColumnSet.new(self).extract_widths!.total_width
|
272
272
|
end
|
273
273
|
|
274
274
|
# Return true if this is an empty table, i.e. if the number of
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
|
6
|
+
# Abstract base class that is responsible for building the table border.
|
7
|
+
class Border
|
8
|
+
include Unicode
|
9
|
+
|
10
|
+
NEWLINE = "\n"
|
11
|
+
|
12
|
+
# The row cell widths
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
attr_reader :widths
|
16
|
+
private :widths
|
17
|
+
|
18
|
+
# The table row
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
attr_reader :row
|
22
|
+
private :row
|
23
|
+
|
24
|
+
# Instantiate a new object
|
25
|
+
#
|
26
|
+
# @return [Object]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
def initialize(*)
|
30
|
+
if self.class == Border
|
31
|
+
raise NotImplementedError, "#{self} is an abstract class"
|
32
|
+
else
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# A line spanning all columns marking top of a table.
|
38
|
+
#
|
39
|
+
# @return [String]
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
def top_line
|
43
|
+
render :top
|
44
|
+
end
|
45
|
+
|
46
|
+
# A line spanning all columns delemeting rows in a table.
|
47
|
+
#
|
48
|
+
# @return [String]
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
def separator
|
52
|
+
render :mid
|
53
|
+
end
|
54
|
+
|
55
|
+
# A line spanning all columns delemeting cells in a row.
|
56
|
+
#
|
57
|
+
# @return [String]
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
def row_line
|
61
|
+
self['left'] + row.join(self['right']) + self['right']
|
62
|
+
end
|
63
|
+
|
64
|
+
# A line spannig all columns marking bottom of a table.
|
65
|
+
#
|
66
|
+
# @return [String]
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
def bottom_line
|
70
|
+
render :bottom
|
71
|
+
end
|
72
|
+
|
73
|
+
protected
|
74
|
+
|
75
|
+
# Generate particular border type
|
76
|
+
#
|
77
|
+
# @param [String] type
|
78
|
+
# border type one of [:top, :bottom, :mid]
|
79
|
+
#
|
80
|
+
# @api private
|
81
|
+
def render(type)
|
82
|
+
type = type.to_s
|
83
|
+
render_line self[type],
|
84
|
+
self["#{type}_left"] || self[type],
|
85
|
+
self["#{type}_right"] || self[type],
|
86
|
+
self["#{type}_mid"]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Generate a border string
|
90
|
+
#
|
91
|
+
# @param [String] line
|
92
|
+
#
|
93
|
+
# @return [String]
|
94
|
+
#
|
95
|
+
# @api private
|
96
|
+
def render_line(line, left, right, intersection)
|
97
|
+
as_unicode do
|
98
|
+
left + widths.map { |width| line * width }.join(intersection) + right
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end # Border
|
103
|
+
end # Table
|
104
|
+
end # TTY
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
class Border
|
6
|
+
|
7
|
+
# A class that represents an ascii border.
|
8
|
+
class ASCII < Border
|
9
|
+
|
10
|
+
BORDER_TYPE = {
|
11
|
+
'top' => '-',
|
12
|
+
'top_mid' => '+',
|
13
|
+
'top_left' => '+',
|
14
|
+
'top_right' => '+',
|
15
|
+
'bottom' => '-',
|
16
|
+
'bottom_mid' => '+',
|
17
|
+
'bottom_left' => '+',
|
18
|
+
'bottom_right' => '+',
|
19
|
+
'mid' => '-',
|
20
|
+
'mid_mid' => '+',
|
21
|
+
'mid_left' => '+',
|
22
|
+
'mid_right' => '+',
|
23
|
+
'left' => '|',
|
24
|
+
'right' => '|'
|
25
|
+
}
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
def [](type)
|
29
|
+
BORDER_TYPE[type]
|
30
|
+
end
|
31
|
+
|
32
|
+
# @api private
|
33
|
+
def initialize(row)
|
34
|
+
@row = row
|
35
|
+
@widths = row.map { |cell| cell.chars.to_a.size }
|
36
|
+
end
|
37
|
+
|
38
|
+
end # ASCII
|
39
|
+
end # Border
|
40
|
+
end # Table
|
41
|
+
end # TTY
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
class Border
|
6
|
+
|
7
|
+
# A class that represents no border.
|
8
|
+
class Null < Border
|
9
|
+
|
10
|
+
# @api private
|
11
|
+
def initialize(row)
|
12
|
+
@row = row
|
13
|
+
@widths = row.map { |cell| cell.chars.to_a.size }
|
14
|
+
end
|
15
|
+
|
16
|
+
# A stub top line
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
def top_line
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
# A stub separator line
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
def separator
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# A line spanning all columns delemited by space character.
|
31
|
+
#
|
32
|
+
# @return [String]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
def row_line
|
36
|
+
row.join(' ')
|
37
|
+
end
|
38
|
+
|
39
|
+
# A stub bottom line
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
def bottom_line
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
end # Null
|
47
|
+
end # Border
|
48
|
+
end # Table
|
49
|
+
end # TTY
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
class Border
|
6
|
+
|
7
|
+
# A class that represents a unicode border.
|
8
|
+
class Unicode < Border
|
9
|
+
|
10
|
+
BORDER_TYPE = {
|
11
|
+
'top' => '─',
|
12
|
+
'top_mid' => '┬',
|
13
|
+
'top_left' => '┌',
|
14
|
+
'top_right' => '┐',
|
15
|
+
'bottom' => '─',
|
16
|
+
'bottom_mid' => '┴',
|
17
|
+
'bottom_left' => '└',
|
18
|
+
'bottom_right' => '┘',
|
19
|
+
'mid' => '─',
|
20
|
+
'mid_mid' => '┼',
|
21
|
+
'mid_left' => '├',
|
22
|
+
'mid_right' => '┤',
|
23
|
+
'left' => '│',
|
24
|
+
'right' => '│'
|
25
|
+
}
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
def [](type)
|
29
|
+
BORDER_TYPE[type]
|
30
|
+
end
|
31
|
+
|
32
|
+
# @api private
|
33
|
+
def initialize(row)
|
34
|
+
@row = row
|
35
|
+
@widths = row.map { |cell| cell.chars.to_a.size }
|
36
|
+
end
|
37
|
+
|
38
|
+
end # Unicode
|
39
|
+
end # Border
|
40
|
+
end # Table
|
41
|
+
end # TTY
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
|
6
|
+
# A class that represents table columns properties.
|
7
|
+
class ColumnSet
|
8
|
+
include Equatable
|
9
|
+
extend Delegatable
|
10
|
+
|
11
|
+
attr_reader :table
|
12
|
+
|
13
|
+
delegatable_method :table, :column_widths
|
14
|
+
|
15
|
+
def initialize(table)
|
16
|
+
@table = table
|
17
|
+
end
|
18
|
+
|
19
|
+
# Calculate total table width
|
20
|
+
#
|
21
|
+
# @return [Integer]
|
22
|
+
#
|
23
|
+
# @api public
|
24
|
+
def total_width
|
25
|
+
column_widths.reduce(:+)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Calcualte maximum column widths
|
29
|
+
#
|
30
|
+
# @return [Array] column widths
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
def extract_widths!
|
34
|
+
return column_widths unless column_widths.empty?
|
35
|
+
|
36
|
+
rows = table.to_a
|
37
|
+
data = table.header ? rows + [table.header] : rows
|
38
|
+
colcount = data.max { |row_a, row_b| row_a.size <=> row_b.size }.size
|
39
|
+
|
40
|
+
table.column_widths = find_maximas colcount, data
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Find maximum widths for each row and header if present.
|
47
|
+
#
|
48
|
+
# @param [Integer] colcount
|
49
|
+
# number of columns
|
50
|
+
# @param [Array[Array]] data
|
51
|
+
# the table's header and rows
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
def find_maximas(colcount, data)
|
55
|
+
maximas = []
|
56
|
+
start = 0
|
57
|
+
|
58
|
+
start.upto(colcount - 1) do |index|
|
59
|
+
maximas << find_maximum(data, index)
|
60
|
+
end
|
61
|
+
maximas
|
62
|
+
end
|
63
|
+
|
64
|
+
# Find a maximum column width.
|
65
|
+
#
|
66
|
+
# @param [Array] data
|
67
|
+
#
|
68
|
+
# @param [Integer] index
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
def find_maximum(data, index)
|
72
|
+
data.map { |row| row[index] ? (row[index].to_s.size) : 0 }.max
|
73
|
+
end
|
74
|
+
|
75
|
+
end # ColumnSet
|
76
|
+
end # Table
|
77
|
+
end # TTY
|