tty 0.0.10 → 0.0.11

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 (56) hide show
  1. data/README.md +75 -19
  2. data/lib/tty.rb +7 -0
  3. data/lib/tty/shell/question.rb +3 -3
  4. data/lib/tty/shell/response.rb +5 -5
  5. data/lib/tty/table.rb +51 -19
  6. data/lib/tty/table/column_set.rb +42 -1
  7. data/lib/tty/table/columns.rb +167 -0
  8. data/lib/tty/table/field.rb +2 -2
  9. data/lib/tty/table/indentation.rb +54 -0
  10. data/lib/tty/table/operation/escape.rb +1 -1
  11. data/lib/tty/table/operation/filter.rb +0 -1
  12. data/lib/tty/table/operation/padding.rb +95 -0
  13. data/lib/tty/table/operation/wrapped.rb +6 -3
  14. data/lib/tty/table/operations.rb +3 -2
  15. data/lib/tty/table/orientation/horizontal.rb +27 -1
  16. data/lib/tty/table/orientation/vertical.rb +17 -1
  17. data/lib/tty/table/padder.rb +142 -0
  18. data/lib/tty/table/renderer/basic.rb +101 -31
  19. data/lib/tty/table/validatable.rb +0 -7
  20. data/lib/tty/terminal/color.rb +36 -20
  21. data/lib/tty/text/truncation.rb +16 -1
  22. data/lib/tty/text/wrapping.rb +31 -10
  23. data/lib/tty/version.rb +1 -1
  24. data/spec/tty/shell/question/argument_spec.rb +1 -1
  25. data/spec/tty/shell/question/modify_spec.rb +2 -2
  26. data/spec/tty/shell/response/read_email_spec.rb +0 -1
  27. data/spec/tty/table/border/ascii/rendering_spec.rb +34 -7
  28. data/spec/tty/table/border/null/rendering_spec.rb +34 -7
  29. data/spec/tty/table/column_set/extract_widths_spec.rb +1 -1
  30. data/spec/tty/table/column_set/widths_from_spec.rb +52 -0
  31. data/spec/tty/table/columns/enforce_spec.rb +68 -0
  32. data/spec/tty/table/columns/widths_spec.rb +33 -0
  33. data/spec/tty/table/indentation/insert_indent_spec.rb +27 -0
  34. data/spec/tty/table/operation/wrapped/call_spec.rb +2 -1
  35. data/spec/tty/table/operation/wrapped/wrap_spec.rb +3 -2
  36. data/spec/tty/table/operations/new_spec.rb +3 -5
  37. data/spec/tty/table/orientation_spec.rb +68 -22
  38. data/spec/tty/table/padder/parse_spec.rb +45 -0
  39. data/spec/tty/table/padding_spec.rb +120 -0
  40. data/spec/tty/table/renderer/ascii/indentation_spec.rb +41 -0
  41. data/spec/tty/table/renderer/ascii/padding_spec.rb +61 -0
  42. data/spec/tty/table/renderer/ascii/resizing_spec.rb +114 -0
  43. data/spec/tty/table/renderer/basic/alignment_spec.rb +18 -19
  44. data/spec/tty/table/renderer/basic/coloring_spec.rb +45 -0
  45. data/spec/tty/table/renderer/basic/indentation_spec.rb +46 -0
  46. data/spec/tty/table/renderer/basic/options_spec.rb +2 -2
  47. data/spec/tty/table/renderer/basic/padding_spec.rb +52 -0
  48. data/spec/tty/table/renderer/basic/resizing_spec.rb +96 -0
  49. data/spec/tty/table/renderer/render_spec.rb +36 -0
  50. data/spec/tty/table/renderer/unicode/indentation_spec.rb +41 -0
  51. data/spec/tty/table/renderer/unicode/padding_spec.rb +61 -0
  52. data/spec/tty/table/renderer_spec.rb +19 -0
  53. data/spec/tty/table/rotate_spec.rb +20 -6
  54. data/spec/tty/text/truncation/truncate_spec.rb +18 -3
  55. data/spec/tty/text/wrapping/wrap_spec.rb +24 -7
  56. metadata +56 -18
@@ -0,0 +1,167 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module TTY
4
+ class Table
5
+
6
+ # A class responsible for enforcing column constraints
7
+ class Columns
8
+
9
+ attr_reader :table
10
+
11
+ attr_reader :renderer
12
+
13
+ MIN_WIDTH = 1
14
+
15
+ BORDER_WIDTH = 1
16
+
17
+ # Initialize a Columns
18
+ #
19
+ # @param [TTY::Table::Renderer]
20
+ #
21
+ # @api public
22
+ def initialize(renderer)
23
+ @renderer = renderer
24
+ @table = renderer.table
25
+ end
26
+
27
+ # Estimate outside border size
28
+ #
29
+ # @return [Integer]
30
+ #
31
+ # @api public
32
+ def outside_border_size
33
+ renderer.border_class == TTY::Table::Border::Null ? 0 : 2
34
+ end
35
+
36
+ # Total border size
37
+ #
38
+ # @return [Integer]
39
+ #
40
+ # @api public
41
+ def border_size
42
+ BORDER_WIDTH * (table.column_size - 1) + outside_border_size
43
+ end
44
+
45
+ # Estimate minimum table width to be able to display content
46
+ #
47
+ # @return [Integer]
48
+ #
49
+ # @api public
50
+ def minimum_width
51
+ table.column_size * MIN_WIDTH + border_size
52
+ end
53
+
54
+ # Return column's natural unconstrained widths
55
+ #
56
+ # @return [Integer]
57
+ #
58
+ # @api public
59
+ def natural_width
60
+ renderer.column_widths.inject(0, &:+) + border_size
61
+ end
62
+
63
+ # Return the constrained column widths. Account for table field widths
64
+ # and any user defined constraints on the table width.
65
+ #
66
+ # @api public
67
+ def enforce
68
+ assert_minimum_width
69
+
70
+ if not renderer.padding.empty?
71
+ renderer.column_widths = adjust_padding
72
+ end
73
+
74
+ if natural_width <= renderer.width
75
+ renderer.column_widths = expand if renderer.resize
76
+ else
77
+ if renderer.resize
78
+ renderer.column_widths = shrink
79
+ else
80
+ rotate
81
+ renderer.column_widths = ColumnSet.widths_from(table)
82
+ end
83
+ end
84
+ end
85
+
86
+ # Adjust column widths to account for padding whitespace
87
+ #
88
+ # @api private
89
+ def adjust_padding
90
+ padding = renderer.padding
91
+ column_size = table.column_size
92
+
93
+ (0...column_size).reduce([]) do |lengths, col|
94
+ lengths + [padding.left + renderer.column_widths[col] + padding.right]
95
+ end
96
+ end
97
+
98
+ # Rotate table to vertical orientation and print information to stdout
99
+ #
100
+ # @api private
101
+ def rotate
102
+ TTY.shell.warn 'The table size exceeds the currently set width.' +
103
+ 'To avoid error either. Defaulting to vertical orientation.'
104
+ table.orientation= :vertical
105
+ table.rotate
106
+ end
107
+
108
+ # Expand column widths to match the requested width
109
+ #
110
+ # @api private
111
+ def expand
112
+ column_size = table.column_size
113
+ ratio = ((renderer.width - natural_width) / column_size.to_f).floor
114
+
115
+ widths = (0...column_size).reduce([]) do |lengths, col|
116
+ lengths + [renderer.column_widths[col] + ratio]
117
+ end
118
+ distribute_extra_width(widths)
119
+ end
120
+
121
+ # Shrink column widths to match the requested width
122
+ #
123
+ # @api private
124
+ def shrink
125
+ column_size = table.column_size
126
+ ratio = ((natural_width - renderer.width) / column_size.to_f).ceil
127
+
128
+ widths = (0...column_size).reduce([]) do |lengths, col|
129
+ lengths + [renderer.column_widths[col] - ratio]
130
+ end
131
+ distribute_extra_width(widths)
132
+ end
133
+
134
+ # Assert minimum width for the table content
135
+ #
136
+ # @raise [TTY::ResizeError]
137
+ #
138
+ # @api private
139
+ def assert_minimum_width
140
+ width = renderer.width
141
+ if width <= minimum_width
142
+ raise ResizeError,
143
+ "Table's width is too small to contain the content " +
144
+ "(min width #{minimum_width}, currently set #{width})"
145
+ end
146
+ end
147
+
148
+ # Distribute remaining width to meet the total width requirement.
149
+ #
150
+ # @param [Array[Integer]] widths
151
+ #
152
+ # @api private
153
+ def distribute_extra_width(widths)
154
+ column_size = table.column_size
155
+ extra_width = renderer.width - (widths.reduce(:+) + border_size)
156
+ per_field_width = extra_width / column_size
157
+ remaining_width = extra_width % column_size
158
+ extra = [1] * remaining_width + [0] * (column_size - remaining_width)
159
+
160
+ widths.map.with_index do |width, index|
161
+ width + per_field_width + extra[index]
162
+ end
163
+ end
164
+
165
+ end # Columns
166
+ end # Table
167
+ end # TTY
@@ -77,7 +77,7 @@ module TTY
77
77
  # @api public
78
78
  def lines
79
79
  escaped = value.to_s.scan(/(\\n|\\t|\\r)/)
80
- escaped.empty? ? value.to_s.split(/\n/) : [value.to_s]
80
+ escaped.empty? ? value.to_s.split(/\n/, -1) : [value.to_s]
81
81
  end
82
82
 
83
83
  # If the string contains unescaped new lines then the longest token
@@ -87,7 +87,7 @@ module TTY
87
87
  #
88
88
  # @api public
89
89
  def length
90
- lines.max_by(&:length).size
90
+ (lines.max_by(&:length) || '').size
91
91
  end
92
92
 
93
93
  # Extract the number of lines this value spans
@@ -0,0 +1,54 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module TTY
4
+ class Table
5
+
6
+ # A class responsible for indenting table representation
7
+ class Indentation
8
+
9
+ attr_reader :renderer
10
+
11
+ # Initialize an Indentation
12
+ #
13
+ # @api public
14
+ def initialize(renderer)
15
+ @renderer = renderer
16
+ end
17
+
18
+ # Create indentation
19
+ #
20
+ # @api public
21
+ def indentation
22
+ ' ' * renderer.indent
23
+ end
24
+
25
+ # Return a table part with indentation inserted
26
+ #
27
+ # @param [#map, #to_s] part
28
+ # the rendered table part
29
+ #
30
+ # @api public
31
+ def insert_indent(part)
32
+ if part.respond_to?(:to_a)
33
+ part.map { |line| insert_indentation(line) }
34
+ else
35
+ insert_indentation(part)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ # Insert indentation into a table renderd line
42
+ #
43
+ # @param [#to_a, #to_s] line
44
+ # the rendered table line
45
+ #
46
+ # @api public
47
+ def insert_indentation(line)
48
+ line = line.is_a?(Array) ? line[0] : line
49
+ line.insert(0, indentation) if line
50
+ end
51
+
52
+ end # Indentation
53
+ end # Table
54
+ end # TTY
@@ -19,7 +19,7 @@ module TTY
19
19
  #
20
20
  # @api public
21
21
  def call(field, row, col)
22
- field.value = field.value.gsub(/(\t|\r|\n)/) do |val|
22
+ field.value = field.value.to_s.gsub(/(\t|\r|\n)/) do |val|
23
23
  val.dump.gsub('"', '')
24
24
  end
25
25
  end
@@ -30,7 +30,6 @@ module TTY
30
30
  end
31
31
 
32
32
  end # Filter
33
-
34
33
  end # Operation
35
34
  end # Table
36
35
  end # TTY
@@ -0,0 +1,95 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module TTY
4
+ class Table
5
+ module Operation
6
+
7
+ # A class responsible for padding field with whitespace
8
+ class Padding
9
+
10
+ attr_reader :padding_top
11
+
12
+ attr_reader :padding_right
13
+
14
+ attr_reader :padding_bottom
15
+
16
+ attr_reader :padding_left
17
+
18
+ attr_reader :padding_width
19
+
20
+ attr_reader :multiline
21
+
22
+ # Initialize a Padding operation
23
+ #
24
+ # @param [TTY::Table::Padder]
25
+ #
26
+ # @api public
27
+ def initialize(padding, multiline)
28
+ @padding_top = "\n" * padding.top
29
+ @padding_right = ' ' * padding.right
30
+ @padding_bottom = "\n" * padding.bottom
31
+ @padding_left = ' ' * padding.left
32
+ @padding_width = padding.left + padding.right
33
+ @multiline = multiline
34
+ end
35
+
36
+ # Apply padding to a field
37
+ #
38
+ # @param [TTY::Table::Field] field
39
+ # the table field
40
+ #
41
+ # @param [Integer] row
42
+ # the field row index
43
+ #
44
+ # @param [Integer] col
45
+ # the field column index
46
+ #
47
+ # @return [TTY::Table::Field]
48
+ #
49
+ # @api public
50
+ def call(field, row, col)
51
+ text = field.value.to_s
52
+
53
+ text = multiline ? pad_multi_line(text) : pad_single_line(text)
54
+ text.insert(0, padding_top).insert(-1, padding_bottom)
55
+
56
+ field.value = text
57
+ end
58
+
59
+ # Apply padding to multi line text
60
+ #
61
+ # @param [String] text
62
+ #
63
+ # @return [String]
64
+ #
65
+ # @api private
66
+ def pad_multi_line(text)
67
+ text.split("\n", -1).map { |part| pad_around(part) }.join("\n")
68
+ end
69
+
70
+ # Apply padding to single line text
71
+ #
72
+ # @param [String] text
73
+ #
74
+ # @return [String]
75
+ #
76
+ # @api private
77
+ def pad_single_line(text)
78
+ pad_around(text.strip)
79
+ end
80
+
81
+ # Apply padding to left and right side of string
82
+ #
83
+ # @param [String] text
84
+ #
85
+ # @return [String]
86
+ #
87
+ # @api private
88
+ def pad_around(text)
89
+ text.insert(0, padding_left).insert(-1, padding_right)
90
+ end
91
+
92
+ end # Padding
93
+ end # Operation
94
+ end # Table
95
+ end # TTY
@@ -9,11 +9,14 @@ module TTY
9
9
 
10
10
  attr_reader :widths
11
11
 
12
+ attr_reader :padding
13
+
12
14
  # Initialize a Wrapped
13
15
  #
14
16
  # @api public
15
- def initialize(widths)
16
- @widths = widths
17
+ def initialize(widths, padding)
18
+ @widths = widths
19
+ @padding = padding.padding
17
20
  end
18
21
 
19
22
  # Apply wrapping to a field
@@ -46,7 +49,7 @@ module TTY
46
49
  #
47
50
  # @api public
48
51
  def wrap(string, width)
49
- TTY::Text.wrap(string, width)
52
+ TTY::Text.wrap(string, width, padding: padding)
50
53
  end
51
54
 
52
55
  end # Wrapped
@@ -20,8 +20,9 @@ module TTY
20
20
  # @return [Object]
21
21
  #
22
22
  # @api public
23
- def initialize(table)
23
+ def initialize(table, renderer=nil)
24
24
  @table = table
25
+ @renderer = renderer # will be available to each operation
25
26
  end
26
27
 
27
28
  # Available operations
@@ -43,7 +44,7 @@ module TTY
43
44
  # @return [Hash]
44
45
  #
45
46
  # @api public
46
- def add_operation(type, object)
47
+ def add(type, object)
47
48
  operations[type] << object
48
49
  end
49
50
 
@@ -6,14 +6,40 @@ module TTY
6
6
  # A class representing table orientation
7
7
  class Orientation
8
8
 
9
+ # A class responsible for horizontal table transformation
9
10
  class Horizontal < Orientation
10
11
 
11
12
  def transform(table)
12
13
  table.rotate_horizontal
13
14
  end
14
15
 
15
- end # Horizontal
16
+ # Slice vertical table data into horizontal
17
+ #
18
+ # @param [TTY::Table] table
19
+ #
20
+ # @api public
21
+ def slice(table)
22
+ head, body, array_h, array_b = 4.times.map { [] }
23
+ index = 0
24
+ first_column = 0
25
+ second_column = 1
26
+
27
+ (0...table.original_columns * table.original_rows).each do |col_index|
28
+ row = table.rows[index]
29
+ array_h += [row[first_column]]
30
+ array_b += [row[second_column]]
16
31
 
32
+ if col_index % table.original_columns == 2
33
+ head << array_h
34
+ body << array_b
35
+ array_h, array_b = [], []
36
+ end
37
+ index += 1
38
+ end
39
+ [head, body]
40
+ end
41
+
42
+ end # Horizontal
17
43
  end # Orientation
18
44
  end # Table
19
45
  end # TTY
@@ -6,14 +6,30 @@ module TTY
6
6
  # A class representing table orientation
7
7
  class Orientation
8
8
 
9
+ # A class responsible for vertical table transformation
9
10
  class Vertical < Orientation
10
11
 
11
12
  def transform(table)
12
13
  table.rotate_vertical
13
14
  end
14
15
 
15
- end # Vertical
16
+ # Slice horizontal table data into vertical
17
+ #
18
+ # @param [TTY::Table] table
19
+ #
20
+ # @api public
21
+ def slice(table)
22
+ header = table.header
23
+ row_size = table.row_size
24
+
25
+ head = header ? header : (0..row_size).map { |n| (n + 1).to_s }
16
26
 
27
+ (0...row_size).inject([]) do |array, index|
28
+ array + head.zip(table.rows[index]).map { |row| table.to_row(row) }
29
+ end
30
+ end
31
+
32
+ end # Vertical
17
33
  end # Orientation
18
34
  end # Table
19
35
  end # TTY