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.
Files changed (35) hide show
  1. data/.travis.yml +1 -1
  2. data/README.md +19 -7
  3. data/lib/tty.rb +10 -0
  4. data/lib/tty/support/equatable.rb +1 -0
  5. data/lib/tty/support/unicode.rb +36 -0
  6. data/lib/tty/table.rb +3 -3
  7. data/lib/tty/table/border.rb +104 -0
  8. data/lib/tty/table/border/ascii.rb +41 -0
  9. data/lib/tty/table/border/null.rb +49 -0
  10. data/lib/tty/table/border/unicode.rb +41 -0
  11. data/lib/tty/table/column_set.rb +77 -0
  12. data/lib/tty/table/operation/alignment.rb +2 -0
  13. data/lib/tty/table/operation/alignment_set.rb +4 -58
  14. data/lib/tty/table/operation/truncation.rb +15 -19
  15. data/lib/tty/table/operation/wrapped.rb +34 -0
  16. data/lib/tty/table/renderer.rb +8 -11
  17. data/lib/tty/table/renderer/ascii.rb +15 -0
  18. data/lib/tty/table/renderer/basic.rb +21 -34
  19. data/lib/tty/table/renderer/unicode.rb +2 -2
  20. data/lib/tty/vector.rb +117 -0
  21. data/lib/tty/version.rb +1 -1
  22. data/spec/tty/table/border/ascii/rendering_spec.rb +49 -0
  23. data/spec/tty/table/border/new_spec.rb +21 -0
  24. data/spec/tty/table/border/null/rendering_spec.rb +49 -0
  25. data/spec/tty/table/border/unicode/rendering_spec.rb +49 -0
  26. data/spec/tty/table/column_set/extract_widths_spec.rb +26 -0
  27. data/spec/tty/table/operation/alignment_set/align_rows_spec.rb +4 -4
  28. data/spec/tty/table/operation/alignment_set/new_spec.rb +1 -1
  29. data/spec/tty/table/operation/wrapped/wrap_spec.rb +22 -0
  30. data/spec/tty/table/properties_spec.rb +3 -0
  31. data/spec/tty/table/renderer/basic/render_spec.rb +144 -33
  32. data/spec/tty/table/renderer/pick_renderer_spec.rb +25 -0
  33. data/spec/tty/table/to_s_spec.rb +51 -0
  34. data/spec/tty/vector/new_spec.rb +47 -0
  35. metadata +35 -8
@@ -17,4 +17,4 @@ matrix:
17
17
  - rvm: ruby-head
18
18
  - rvm: jruby-head
19
19
  - rvm: jruby-19mode
20
- - rvm: rbx-18mode
20
+ - rvm: rbx-19mode
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 # => a1 a2 a3
89
- # b1 b2 b3
87
+ table.to_s
88
+
89
+ a1 a2 a3
90
+ b1 b2 b3
90
91
  ```
91
92
 
92
- To print `unicode` table
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
- ```ruby
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
 
@@ -8,6 +8,7 @@ module TTY
8
8
  # Hook into module inclusion.
9
9
  #
10
10
  # @param [Module] base
11
+ # the module or class including Equatable
11
12
  #
12
13
  # @return [self]
13
14
  #
@@ -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
@@ -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
- render(self)
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