tty-table 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/README.md +458 -142
  4. data/lib/tty-table.rb +6 -6
  5. data/lib/tty/table.rb +34 -34
  6. data/lib/tty/table/alignment_set.rb +73 -0
  7. data/lib/tty/table/border.rb +54 -36
  8. data/lib/tty/table/border/null.rb +4 -4
  9. data/lib/tty/table/{columns.rb → column_constraint.rb} +30 -32
  10. data/lib/tty/table/column_set.rb +18 -17
  11. data/lib/tty/table/field.rb +50 -25
  12. data/lib/tty/table/header.rb +6 -2
  13. data/lib/tty/table/indentation.rb +7 -12
  14. data/lib/tty/table/operation/alignment.rb +59 -0
  15. data/lib/tty/table/operation/escape.rb +1 -1
  16. data/lib/tty/table/operation/filter.rb +1 -1
  17. data/lib/tty/table/operation/padding.rb +12 -61
  18. data/lib/tty/table/operation/truncation.rb +2 -2
  19. data/lib/tty/table/operation/wrapped.rb +2 -5
  20. data/lib/tty/table/operations.rb +35 -17
  21. data/lib/tty/table/orientation/vertical.rb +4 -4
  22. data/lib/tty/table/renderer.rb +1 -7
  23. data/lib/tty/table/renderer/basic.rb +69 -63
  24. data/lib/tty/table/version.rb +1 -1
  25. data/spec/spec_helper.rb +3 -4
  26. data/spec/unit/access_spec.rb +8 -8
  27. data/spec/unit/{operation/alignment_set → alignment_set}/each_spec.rb +1 -1
  28. data/spec/unit/{operation/alignment_set → alignment_set}/new_spec.rb +4 -4
  29. data/spec/unit/{operation/alignment_set → alignment_set}/to_ary_spec.rb +1 -1
  30. data/spec/unit/alignment_spec.rb +71 -0
  31. data/spec/unit/border/ascii/rendering_spec.rb +12 -12
  32. data/spec/unit/border/new_spec.rb +2 -2
  33. data/spec/unit/border/null/rendering_spec.rb +2 -2
  34. data/spec/unit/border/unicode/rendering_spec.rb +10 -10
  35. data/spec/unit/{columns → column_constraint}/enforce_spec.rb +15 -12
  36. data/spec/unit/{columns → column_constraint}/widths_spec.rb +6 -6
  37. data/spec/unit/column_set/extract_widths_spec.rb +39 -6
  38. data/spec/unit/data_spec.rb +4 -6
  39. data/spec/unit/each_spec.rb +8 -23
  40. data/spec/unit/each_with_index_spec.rb +27 -33
  41. data/spec/unit/field/length_spec.rb +23 -9
  42. data/spec/unit/field/width_spec.rb +1 -1
  43. data/spec/unit/filter_spec.rb +7 -8
  44. data/spec/unit/header/new_spec.rb +6 -15
  45. data/spec/unit/indentation/indent_spec.rb +21 -0
  46. data/spec/unit/new_spec.rb +73 -0
  47. data/spec/unit/operation/{alignment_set → alignment}/call_spec.rb +1 -1
  48. data/spec/unit/operation/escape/call_spec.rb +2 -3
  49. data/spec/unit/operation/filter/call_spec.rb +2 -3
  50. data/spec/unit/operation/truncation/call_spec.rb +6 -8
  51. data/spec/unit/operation/wrapped/call_spec.rb +15 -8
  52. data/spec/unit/operations/new_spec.rb +1 -1
  53. data/spec/unit/orientation_spec.rb +6 -6
  54. data/spec/unit/padding_spec.rb +29 -32
  55. data/spec/unit/properties_spec.rb +4 -4
  56. data/spec/unit/render_repeat_spec.rb +42 -0
  57. data/spec/unit/render_spec.rb +1 -1
  58. data/spec/unit/render_with_spec.rb +3 -3
  59. data/spec/unit/renderer/ascii/coloring_spec.rb +70 -0
  60. data/spec/unit/renderer/ascii/multiline_spec.rb +101 -0
  61. data/spec/unit/renderer/ascii/padding_spec.rb +37 -10
  62. data/spec/unit/renderer/ascii/render_spec.rb +4 -4
  63. data/spec/unit/renderer/ascii/resizing_spec.rb +22 -22
  64. data/spec/unit/renderer/ascii/separator_spec.rb +1 -1
  65. data/spec/unit/renderer/basic/alignment_spec.rb +20 -20
  66. data/spec/unit/renderer/basic/coloring_spec.rb +43 -28
  67. data/spec/unit/renderer/basic/filter_spec.rb +3 -3
  68. data/spec/unit/renderer/basic/multiline_spec.rb +74 -0
  69. data/spec/unit/renderer/basic/options_spec.rb +9 -9
  70. data/spec/unit/renderer/basic/padding_spec.rb +26 -2
  71. data/spec/unit/renderer/basic/render_spec.rb +4 -4
  72. data/spec/unit/renderer/basic/resizing_spec.rb +18 -18
  73. data/spec/unit/renderer/basic/separator_spec.rb +1 -1
  74. data/spec/unit/renderer/basic/truncation_spec.rb +6 -6
  75. data/spec/unit/renderer/basic/wrapping_spec.rb +3 -3
  76. data/spec/unit/renderer/border_spec.rb +4 -4
  77. data/spec/unit/renderer/unicode/coloring_spec.rb +70 -0
  78. data/spec/unit/renderer/unicode/indentation_spec.rb +1 -1
  79. data/spec/unit/renderer/unicode/padding_spec.rb +26 -26
  80. data/spec/unit/renderer/unicode/render_spec.rb +4 -4
  81. data/spec/unit/renderer/unicode/separator_spec.rb +1 -1
  82. data/spec/unit/to_s_spec.rb +4 -11
  83. data/spec/unit/utf_spec.rb +33 -0
  84. data/tty-table.gemspec +2 -1
  85. metadata +52 -32
  86. data/lib/tty/table/operation/alignment_set.rb +0 -103
  87. data/lib/tty/table/padder.rb +0 -180
  88. data/lib/tty/table/renderer/color.rb +0 -12
  89. data/spec/unit/indentation/insert_indent_spec.rb +0 -27
  90. data/spec/unit/initialize_spec.rb +0 -88
  91. data/spec/unit/padder/parse_spec.rb +0 -45
  92. data/spec/unit/padder/to_s_spec.rb +0 -14
  93. data/spec/unit/renderer/basic/multiline_content_spec.rb +0 -135
  94. data/spec/unit/renderer/style_spec.rb +0 -72
@@ -7,24 +7,21 @@ module TTY
7
7
  # Used internally by {Renderer::Basic} to enforce correct column widths.
8
8
  #
9
9
  # @api private
10
- class Columns
11
-
12
- attr_reader :table
13
-
14
- attr_reader :renderer
15
-
10
+ class ColumnConstraint
16
11
  MIN_WIDTH = 1
17
12
 
18
13
  BORDER_WIDTH = 1
19
14
 
20
15
  # Initialize a Columns
21
16
  #
17
+ # @param [TTY::Table] table
18
+ #
22
19
  # @param [TTY::Table::Renderer] renderer
23
20
  #
24
21
  # @api public
25
- def initialize(renderer)
22
+ def initialize(table, renderer)
23
+ @table = table
26
24
  @renderer = renderer
27
- @table = renderer.table
28
25
  end
29
26
 
30
27
  # Estimate outside border size
@@ -42,7 +39,17 @@ module TTY
42
39
  #
43
40
  # @api public
44
41
  def border_size
45
- BORDER_WIDTH * (table.column_size - 1) + outside_border_size
42
+ BORDER_WIDTH * (table.columns_size - 1) + outside_border_size
43
+ end
44
+
45
+ # Measure total padding size for a table
46
+ #
47
+ # @return [Integer]
48
+ #
49
+ # @api public
50
+ def padding_size
51
+ padding = renderer.padding
52
+ (padding.left + padding.right) * (table.columns_size - 1)
46
53
  end
47
54
 
48
55
  # Estimate minimum table width to be able to display content
@@ -51,7 +58,7 @@ module TTY
51
58
  #
52
59
  # @api public
53
60
  def minimum_width
54
- table.column_size * MIN_WIDTH + border_size
61
+ table.columns_size * MIN_WIDTH + border_size
55
62
  end
56
63
 
57
64
  # Return column's natural unconstrained widths
@@ -60,7 +67,7 @@ module TTY
60
67
  #
61
68
  # @api public
62
69
  def natural_width
63
- renderer.column_widths.inject(0, &:+) + border_size
70
+ renderer.column_widths.inject(0, &:+) + border_size + padding_size
64
71
  end
65
72
 
66
73
  # Return the constrained column widths.
@@ -72,33 +79,22 @@ module TTY
72
79
  def enforce
73
80
  assert_minimum_width
74
81
 
75
- unless renderer.padding.empty?
76
- renderer.column_widths = adjust_padding
77
- end
78
-
79
82
  if natural_width <= renderer.width
80
- renderer.column_widths = expand if renderer.resize
83
+ renderer.resize ? expand : renderer.column_widths
81
84
  else
82
85
  if renderer.resize
83
- renderer.column_widths = shrink
86
+ shrink
84
87
  else
85
88
  rotate
86
- renderer.column_widths = ColumnSet.widths_from(table)
87
89
  end
88
90
  end
89
91
  end
90
92
 
91
- # Adjust column widths to account for padding whitespace
92
- #
93
- # @api private
94
- def adjust_padding
95
- padding = renderer.padding
96
- column_size = table.column_size
93
+ private
97
94
 
98
- (0...column_size).reduce([]) do |lengths, col|
99
- lengths + [padding.left + renderer.column_widths[col] + padding.right]
100
- end
101
- end
95
+ attr_reader :table
96
+
97
+ attr_reader :renderer
102
98
 
103
99
  # Rotate table to vertical orientation and print information to stdout
104
100
  #
@@ -109,13 +105,14 @@ module TTY
109
105
  'orientation.'
110
106
  table.orientation = :vertical
111
107
  table.rotate
108
+ ColumnSet.widths_from(table)
112
109
  end
113
110
 
114
111
  # Expand column widths to match the requested width
115
112
  #
116
113
  # @api private
117
114
  def expand
118
- column_size = table.column_size
115
+ column_size = table.columns_size
119
116
  ratio = ((renderer.width - natural_width) / column_size.to_f).floor
120
117
 
121
118
  widths = (0...column_size).reduce([]) do |lengths, col|
@@ -128,7 +125,7 @@ module TTY
128
125
  #
129
126
  # @api private
130
127
  def shrink
131
- column_size = table.column_size
128
+ column_size = table.columns_size
132
129
  ratio = ((natural_width - renderer.width) / column_size.to_f).ceil
133
130
 
134
131
  widths = (0...column_size).reduce([]) do |lengths, col|
@@ -155,7 +152,8 @@ module TTY
155
152
  #
156
153
  # @api private
157
154
  def distribute_extra_width(widths)
158
- column_size = table.column_size
155
+ column_size = table.columns_size
156
+ # TODO - add padding size to fully check extra width
159
157
  extra_width = renderer.width - (widths.reduce(:+) + border_size)
160
158
  per_field_width = extra_width / column_size
161
159
  remaining_width = extra_width % column_size
@@ -165,6 +163,6 @@ module TTY
165
163
  width + per_field_width + extra[index]
166
164
  end
167
165
  end
168
- end # Columns
166
+ end # ColumnConstraint
169
167
  end # Table
170
168
  end # TTY
@@ -10,13 +10,13 @@ module TTY
10
10
  class ColumnSet
11
11
  include Equatable
12
12
 
13
- attr_reader :table
14
-
15
13
  # Initialize a ColumnSet
16
14
  #
15
+ # @param [Array[Array[Object]]] data
16
+ #
17
17
  # @api public
18
18
  def initialize(table)
19
- @table = table
19
+ @data = table.data
20
20
  end
21
21
 
22
22
  # Calculate total table width
@@ -34,10 +34,8 @@ module TTY
34
34
  #
35
35
  # @api private
36
36
  def extract_widths
37
- data = table.data
38
37
  colcount = data.max { |row_a, row_b| row_a.size <=> row_b.size }.size
39
-
40
- self.class.find_maximas(colcount, data)
38
+ find_maximas(colcount)
41
39
  end
42
40
 
43
41
  # Assert data integrity for column widths
@@ -72,10 +70,10 @@ module TTY
72
70
  def self.widths_from(table, column_widths = nil)
73
71
  case column_widths
74
72
  when Array
75
- assert_widths(column_widths, table.column_size)
73
+ assert_widths(column_widths, table.columns_size)
76
74
  Array(column_widths).map(&:to_i)
77
75
  when Numeric
78
- Array.new(table.column_size, column_widths)
76
+ Array.new(table.columns_size, column_widths)
79
77
  when NilClass
80
78
  new(table).extract_widths
81
79
  else
@@ -85,20 +83,22 @@ module TTY
85
83
 
86
84
  private
87
85
 
86
+ attr_reader :data
87
+
88
88
  # Find maximum widths for each row and header if present.
89
89
  #
90
90
  # @param [Integer] colcount
91
91
  # number of columns
92
- # @param [Array[Array]] data
93
- # the table's header and rows
92
+ #
93
+ # @return [Array[Integer]]
94
94
  #
95
95
  # @api private
96
- def self.find_maximas(colcount, data)
96
+ def find_maximas(colcount)
97
97
  maximas = []
98
98
  start = 0
99
99
 
100
100
  start.upto(colcount - 1) do |col_index|
101
- maximas << find_maximum(data, col_index)
101
+ maximas << find_maximum(col_index)
102
102
  end
103
103
  maximas
104
104
  end
@@ -106,15 +106,16 @@ module TTY
106
106
  # Find a maximum column width. The calculation takes into account
107
107
  # wether the content is escaped or not.
108
108
  #
109
- # @param [Array] data
110
- # the table data
111
- #
112
109
  # @param [Integer] index
113
110
  # the column index
114
111
  #
112
+ # @return [Integer]
113
+ #
115
114
  # @api private
116
- def self.find_maximum(data, index)
117
- data.map { |row| (value = row.call(index)) ? value.length : 0 }.max
115
+ def find_maximum(index)
116
+ data.map do |row|
117
+ (field = row.call(index)) ? field.length : 0
118
+ end.max
118
119
  end
119
120
  end # ColumnSet
120
121
  end # Table
@@ -4,7 +4,8 @@ module TTY
4
4
  class Table
5
5
  # A class that represents a unique element in a table.
6
6
  #
7
- # Used internally by {Table::Row} to define internal structure.
7
+ # Used internally by {Table::Header} and {Table::Row} to
8
+ # define internal structure.
8
9
  #
9
10
  # @api private
10
11
  class Field
@@ -15,28 +16,40 @@ module TTY
15
16
  # @api public
16
17
  attr_reader :value
17
18
 
19
+ # The formatted value inside the field used for display
20
+ #
21
+ # @api public
22
+ attr_reader :content
23
+
18
24
  # The name for the value
19
25
  #
20
26
  # @api public
21
27
  attr_reader :name
22
28
 
23
- # TODO: Change to :content to separate value from formatted string
29
+ # The actual value
30
+ #
31
+ # @api public
24
32
  attr_writer :value
25
33
 
26
- # The field value width
34
+ # The formatted string
27
35
  #
28
36
  # @api public
29
- attr_reader :width
37
+ attr_writer :content
30
38
 
31
39
  # Number of columns this field spans. Defaults to 1.
32
40
  #
41
+ # @api public
33
42
  attr_reader :colspan
34
43
 
35
44
  # Number of rows this field spans. Defaults to 1.
36
45
  #
46
+ # @api public
37
47
  attr_reader :rowspan
38
48
 
39
- attr_reader :align
49
+ # The field alignment
50
+ #
51
+ # @api public
52
+ attr_reader :alignment
40
53
 
41
54
  # Initialize a Field
42
55
  #
@@ -49,15 +62,16 @@ module TTY
49
62
  # field.value # => a1
50
63
  #
51
64
  # @example
52
- # field = TTY::Table::Field.new value: 'a1', align: :center
53
- # field.value # => a1
54
- # field.align # => :center
65
+ # field = TTY::Table::Field.new value: 'a1', alignment: :center
66
+ # field.value # => a1
67
+ # field.alignment # => :center
55
68
  #
56
69
  # @api private
57
70
  def initialize(value)
58
71
  options = extract_options(value)
59
- @width = options.fetch(:width) { @value.to_s.size }
60
- @align = options.fetch(:align) { nil }
72
+ @content = @value.to_s
73
+ @width = options[:width]
74
+ @alignment = options.fetch(:alignment) { nil }
61
75
  @colspan = options.fetch(:colspan) { 1 }
62
76
  @rowspan = options.fetch(:rowspan) { 1 }
63
77
  end
@@ -76,15 +90,18 @@ module TTY
76
90
  options
77
91
  end
78
92
 
79
- # Return the width this field would normally have bar other contraints
93
+ # Reset to original value
80
94
  #
81
95
  # @api public
82
- def value_width
83
- @width
96
+ def reset!
97
+ @content = @value.to_s
84
98
  end
85
99
 
86
- def value_height
87
- @height
100
+ # The content width
101
+ #
102
+ # @api public
103
+ def width
104
+ @width || UnicodeUtils.display_width(@content)
88
105
  end
89
106
 
90
107
  # Return number of lines this value spans.
@@ -95,8 +112,8 @@ module TTY
95
112
  #
96
113
  # @api public
97
114
  def lines
98
- escaped = value.to_s.scan(/(\\n|\\t|\\r)/)
99
- escaped.empty? ? value.to_s.split(/\n/, -1) : [value.to_s]
115
+ escaped = content.scan(/(\\n|\\t|\\r)/)
116
+ escaped.empty? ? content.split(/\n/, -1) : [content]
100
117
  end
101
118
 
102
119
  # If the string contains unescaped new lines then the longest token
@@ -106,7 +123,9 @@ module TTY
106
123
  #
107
124
  # @api public
108
125
  def length
109
- (lines.max_by(&:length) || '').size
126
+ (lines.map do |line|
127
+ display_width(self.class.color.strip(line))
128
+ end << 0).max
110
129
  end
111
130
 
112
131
  # Extract the number of lines this value spans
@@ -119,20 +138,26 @@ module TTY
119
138
  end
120
139
 
121
140
  def chars
122
- value.chars
141
+ content.chars
123
142
  end
124
143
 
125
- # Render value inside this field box
144
+ # Return field content
145
+ #
146
+ # @return [String]
126
147
  #
127
148
  # @api public
128
- def render
149
+ def to_s
150
+ content
129
151
  end
130
152
 
131
- # Return field value
132
- #
133
153
  # @api public
134
- def to_s
135
- value
154
+ def self.color
155
+ @color ||= Pastel.new(enabled: true)
156
+ end
157
+
158
+ # @api public
159
+ def display_width(string)
160
+ UnicodeUtils.display_width(string)
136
161
  end
137
162
  end # Field
138
163
  end # Table
@@ -39,8 +39,8 @@ module TTY
39
39
  # Iterate over each element in the vector
40
40
  #
41
41
  # @example
42
- # vec = Vector[1,2,3]
43
- # vec.each { |element| ... }
42
+ # header = TTY::Table::Header.new [1,2,3]
43
+ # header.each { |element| ... }
44
44
  #
45
45
  # @return [self]
46
46
  #
@@ -158,6 +158,10 @@ module TTY
158
158
  def to_hash
159
159
  to_a.hash
160
160
  end
161
+
162
+ def inspect
163
+ "#<#{self.class.name} fields=#{to_a}>"
164
+ end
161
165
  end # Header
162
166
  end # Table
163
167
  end # TTY
@@ -4,21 +4,16 @@ module TTY
4
4
  class Table
5
5
  # A class responsible for indenting table representation
6
6
  class Indentation
7
-
8
- attr_reader :renderer
9
-
10
- # Initialize an Indentation
7
+ # The amount of indentation
11
8
  #
12
9
  # @api public
13
- def initialize(renderer)
14
- @renderer = renderer
15
- end
10
+ attr_accessor :indentation
16
11
 
17
- # Create indentation
12
+ # Initialize an Indentation
18
13
  #
19
14
  # @api public
20
- def indentation
21
- ' ' * renderer.indent
15
+ def initialize(indentation)
16
+ @indentation = indentation
22
17
  end
23
18
 
24
19
  # Return a table part with indentation inserted
@@ -27,7 +22,7 @@ module TTY
27
22
  # the rendered table part
28
23
  #
29
24
  # @api public
30
- def insert_indent(part)
25
+ def indent(part)
31
26
  if part.respond_to?(:to_a)
32
27
  part.map { |line| insert_indentation(line) }
33
28
  else
@@ -45,7 +40,7 @@ module TTY
45
40
  # @api public
46
41
  def insert_indentation(line)
47
42
  line = line.is_a?(Array) ? line[0] : line
48
- line.insert(0, indentation) if line
43
+ line.insert(0, ' ' * indentation) if line
49
44
  end
50
45
  end # Indentation
51
46
  end # Table