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.
- data/README.md +75 -19
- data/lib/tty.rb +7 -0
- data/lib/tty/shell/question.rb +3 -3
- data/lib/tty/shell/response.rb +5 -5
- data/lib/tty/table.rb +51 -19
- data/lib/tty/table/column_set.rb +42 -1
- data/lib/tty/table/columns.rb +167 -0
- data/lib/tty/table/field.rb +2 -2
- data/lib/tty/table/indentation.rb +54 -0
- data/lib/tty/table/operation/escape.rb +1 -1
- data/lib/tty/table/operation/filter.rb +0 -1
- data/lib/tty/table/operation/padding.rb +95 -0
- data/lib/tty/table/operation/wrapped.rb +6 -3
- data/lib/tty/table/operations.rb +3 -2
- data/lib/tty/table/orientation/horizontal.rb +27 -1
- data/lib/tty/table/orientation/vertical.rb +17 -1
- data/lib/tty/table/padder.rb +142 -0
- data/lib/tty/table/renderer/basic.rb +101 -31
- data/lib/tty/table/validatable.rb +0 -7
- data/lib/tty/terminal/color.rb +36 -20
- data/lib/tty/text/truncation.rb +16 -1
- data/lib/tty/text/wrapping.rb +31 -10
- data/lib/tty/version.rb +1 -1
- data/spec/tty/shell/question/argument_spec.rb +1 -1
- data/spec/tty/shell/question/modify_spec.rb +2 -2
- data/spec/tty/shell/response/read_email_spec.rb +0 -1
- data/spec/tty/table/border/ascii/rendering_spec.rb +34 -7
- data/spec/tty/table/border/null/rendering_spec.rb +34 -7
- data/spec/tty/table/column_set/extract_widths_spec.rb +1 -1
- data/spec/tty/table/column_set/widths_from_spec.rb +52 -0
- data/spec/tty/table/columns/enforce_spec.rb +68 -0
- data/spec/tty/table/columns/widths_spec.rb +33 -0
- data/spec/tty/table/indentation/insert_indent_spec.rb +27 -0
- data/spec/tty/table/operation/wrapped/call_spec.rb +2 -1
- data/spec/tty/table/operation/wrapped/wrap_spec.rb +3 -2
- data/spec/tty/table/operations/new_spec.rb +3 -5
- data/spec/tty/table/orientation_spec.rb +68 -22
- data/spec/tty/table/padder/parse_spec.rb +45 -0
- data/spec/tty/table/padding_spec.rb +120 -0
- data/spec/tty/table/renderer/ascii/indentation_spec.rb +41 -0
- data/spec/tty/table/renderer/ascii/padding_spec.rb +61 -0
- data/spec/tty/table/renderer/ascii/resizing_spec.rb +114 -0
- data/spec/tty/table/renderer/basic/alignment_spec.rb +18 -19
- data/spec/tty/table/renderer/basic/coloring_spec.rb +45 -0
- data/spec/tty/table/renderer/basic/indentation_spec.rb +46 -0
- data/spec/tty/table/renderer/basic/options_spec.rb +2 -2
- data/spec/tty/table/renderer/basic/padding_spec.rb +52 -0
- data/spec/tty/table/renderer/basic/resizing_spec.rb +96 -0
- data/spec/tty/table/renderer/render_spec.rb +36 -0
- data/spec/tty/table/renderer/unicode/indentation_spec.rb +41 -0
- data/spec/tty/table/renderer/unicode/padding_spec.rb +61 -0
- data/spec/tty/table/renderer_spec.rb +19 -0
- data/spec/tty/table/rotate_spec.rb +20 -6
- data/spec/tty/text/truncation/truncate_spec.rb +18 -3
- data/spec/tty/text/wrapping/wrap_spec.rb +24 -7
- metadata +56 -18
data/README.md
CHANGED
@@ -16,7 +16,8 @@ Toolbox for developing CLI clients in Ruby. This library provides a fluid interf
|
|
16
16
|
Jump-start development of your command line app:
|
17
17
|
|
18
18
|
* Table rendering with an easy-to-use API. [status: In Progress]
|
19
|
-
* Terminal output colorization
|
19
|
+
* Terminal output colorization. [status: ✔ ]
|
20
|
+
* Terminal output paging. [status: ✔ ]
|
20
21
|
* System & command detection utilities. [status: In Progress]
|
21
22
|
* Text manipulation(wrapping/truncation) [status: In Progress]
|
22
23
|
* Shell user interface. [status: In Progress]
|
@@ -106,22 +107,28 @@ This will use so called `basic` renderer with default options.
|
|
106
107
|
However, you can include other customization options such as
|
107
108
|
|
108
109
|
```ruby
|
109
|
-
column_widths # array of maximum columns widths
|
110
|
-
column_aligns # array of cell alignments out of :left, :center and :right, default :left
|
111
|
-
width # constrain the table total width, otherwise dynamically calculated based on content and terminal size
|
112
|
-
renderer # enforce display type out of :basic, :color, :unicode, :ascii
|
113
110
|
border # hash of border properties out of :characters, :style, :separator keys
|
114
111
|
border_class # a type of border to use
|
115
|
-
|
112
|
+
column_widths # array of maximum columns widths
|
113
|
+
column_aligns # array of cell alignments out of :left, :center and :right, default :left
|
116
114
|
filter # a proc object that is applied to every field in a row
|
115
|
+
indent # indentation applied to rendered table
|
116
|
+
multiline # if true will wrap text at new line or column width,
|
117
|
+
# when false will escape special characters
|
117
118
|
orientation # either :horizontal or :vertical
|
119
|
+
padding # array of integers to set table fields padding
|
120
|
+
renderer # enforce display type out of :basic, :color, :unicode, :ascii
|
121
|
+
resize # if true will expand/shrink table column sizes to match the width,
|
122
|
+
# otherwise if false rotate table vertically
|
123
|
+
width # constrain the table total width, otherwise dynamically
|
124
|
+
# calculated from content and terminal size
|
118
125
|
```
|
119
126
|
|
120
127
|
#### Multiline
|
121
128
|
|
122
129
|
Renderer options may include `multiline` parameter. The `true` value will cause the table fields wrap at their natural line breaks or in case when the column widths are set the content will wrap.
|
123
130
|
|
124
|
-
```
|
131
|
+
```ruby
|
125
132
|
table = TTY::Table.new [ ["First", '1'], ["Multi\nLine\nContent", '2'], ["Third", '3']]
|
126
133
|
table.render :ascii, multiline: true
|
127
134
|
# =>
|
@@ -184,7 +191,7 @@ Next pass the border class to your table instance `render_with` method
|
|
184
191
|
```ruby
|
185
192
|
table = TTY::Table.new ['header1', 'header2'], [['a1', 'a2'], ['b1', 'b2']
|
186
193
|
table.render_with MyBorder
|
187
|
-
|
194
|
+
# =>
|
188
195
|
$header1$header2$
|
189
196
|
$a1 $a2 $
|
190
197
|
* * *
|
@@ -200,7 +207,7 @@ table.render do |renderer|
|
|
200
207
|
mid_mid ' '
|
201
208
|
end
|
202
209
|
end
|
203
|
-
|
210
|
+
# =>
|
204
211
|
header1 header2
|
205
212
|
======= =======
|
206
213
|
a1 a2
|
@@ -214,7 +221,7 @@ table = TTY::Table.new ['header1', 'header2'], [['a1', 'a2'], ['b1', 'b2']]
|
|
214
221
|
table.render do |renderer|
|
215
222
|
renderer.border.separator = :each_row
|
216
223
|
end
|
217
|
-
|
224
|
+
# =>
|
218
225
|
+-------+-------+
|
219
226
|
|header1|header2|
|
220
227
|
+-------+-------+
|
@@ -258,13 +265,38 @@ table = TTY::Table.new do |t|
|
|
258
265
|
end
|
259
266
|
```
|
260
267
|
|
261
|
-
####
|
268
|
+
#### Padding
|
262
269
|
|
263
|
-
|
270
|
+
By default padding is not applied. You can add `padding` to table fields like so
|
264
271
|
|
265
272
|
```ruby
|
266
|
-
|
267
|
-
|
273
|
+
heaer = ['Field', 'Type', 'Null', 'Key', 'Default', 'Extra']
|
274
|
+
rows = [['id', 'int(11)', 'YES', 'nil', 'NULL', '']]
|
275
|
+
table = TTY::Table.new(header, rows)
|
276
|
+
table.render { |renderer| renderer.padding= [0,1,0,1] }
|
277
|
+
# =>
|
278
|
+
+-------+---------+------+-----+---------+-------+
|
279
|
+
| Field | Type | Null | Key | Default | Extra |
|
280
|
+
+-------+---------+------+-----+---------+-------+
|
281
|
+
| id | int(11) | YES | nil | NULL | |
|
282
|
+
+-------+---------+------+-----+---------+-------+
|
283
|
+
```
|
284
|
+
|
285
|
+
or you can set specific padding using `right`, `left`, `top`, `bottom` helpers. However, when adding top or bottom padding a `multiline` option needs to be set to `true` to allow for rows to span multiple lines. For example
|
286
|
+
|
287
|
+
```ruby
|
288
|
+
table.render { |renderer|
|
289
|
+
renderer.multiline = true
|
290
|
+
renderer.padding.top = 1
|
291
|
+
}
|
292
|
+
# =>
|
293
|
+
+-----+-------+----+---+-------+-----+
|
294
|
+
| | | | | | |
|
295
|
+
|Field|Type |Null|Key|Default|Extra|
|
296
|
+
+-----+-------+----+---+-------+-----+
|
297
|
+
| | | | | | |
|
298
|
+
|id |int(11)|YES |nil|NULL | |
|
299
|
+
+-----+-------+----+---+-------+-----+
|
268
300
|
```
|
269
301
|
|
270
302
|
#### Filter
|
@@ -282,7 +314,7 @@ table.render do |renderer|
|
|
282
314
|
end
|
283
315
|
end
|
284
316
|
end
|
285
|
-
|
317
|
+
# =>
|
286
318
|
+-------+-------+
|
287
319
|
|header1|header2|
|
288
320
|
+-------+-------+
|
@@ -292,17 +324,36 @@ end
|
|
292
324
|
+-------+-------+
|
293
325
|
```
|
294
326
|
|
295
|
-
To
|
327
|
+
To color even fields red on green background add filter like so
|
296
328
|
|
297
329
|
```ruby
|
298
330
|
table.render do |renderer|
|
299
|
-
renderer.filter =
|
300
|
-
|
301
|
-
TTY.color.set val, :red, :on_green
|
331
|
+
renderer.filter = proc do |val, row_index, col_index|
|
332
|
+
col_index % 2 == 1 ? TTY.color.set(val, :red, :on_green) : val
|
302
333
|
end
|
303
334
|
end
|
304
335
|
```
|
305
336
|
|
337
|
+
#### Width
|
338
|
+
|
339
|
+
To control table's column sizes pass `width`, `resize` options. By default table's natural column widths are calculated from the content. If the total table width does not fit in terminal window then the table is rotated vertically to preserve content.
|
340
|
+
|
341
|
+
The `resize` property will force the table to expand/shrink to match the terminal width or custom `width`. On its own the `width` property will not resize table but only enforce table vertical rotation if content overspills.
|
342
|
+
|
343
|
+
```ruby
|
344
|
+
header = ['h1', 'h2', 'h3']
|
345
|
+
rows = [['aaa1', 'aa2', 'aaaaaaa3'], ['b1', 'b2', 'b3']]
|
346
|
+
table = TTY::Table.new header, rows
|
347
|
+
table.render width: 80, resize: true
|
348
|
+
# =>
|
349
|
+
+---------+-------+------------+
|
350
|
+
|h1 |h2 |h3 |
|
351
|
+
+---------+-------+------------+
|
352
|
+
|aaa1 |aa2 |aaaaaaa3 |
|
353
|
+
|b1 |b2 |b3 |
|
354
|
+
+---------+-------+------------+
|
355
|
+
```
|
356
|
+
|
306
357
|
### Terminal
|
307
358
|
|
308
359
|
To read general terminal properties you can use on of the helpers
|
@@ -403,15 +454,20 @@ Reading answers and converting them into required types can be done with custom
|
|
403
454
|
|
404
455
|
```ruby
|
405
456
|
read_bool # return true or false for strings such as "Yes", "No"
|
457
|
+
read_char # return first character
|
406
458
|
read_date # return date type
|
407
459
|
read_datetime # return datetime type
|
408
460
|
read_email # validate answer against email regex
|
461
|
+
read_file # return a File object
|
409
462
|
read_float # return decimal or error if cannot convert
|
410
463
|
read_int # return integer or error if cannot convert
|
411
464
|
read_multiple # return multiple line string
|
412
465
|
read_password # return string with echo turned off
|
413
466
|
read_range # return range type
|
467
|
+
read_regex # return regex expression
|
414
468
|
read_string # return string
|
469
|
+
read_symbol # return symbol
|
470
|
+
read_text # return multiline string
|
415
471
|
```
|
416
472
|
|
417
473
|
For example, if we wanted to ask a user for a single digit in given range
|
data/lib/tty.rb
CHANGED
@@ -62,10 +62,13 @@ require 'tty/table/border/null'
|
|
62
62
|
require 'tty/table/border/row_line'
|
63
63
|
|
64
64
|
require 'tty/table/column_set'
|
65
|
+
require 'tty/table/columns'
|
65
66
|
require 'tty/table/orientation'
|
66
67
|
require 'tty/table/orientation/horizontal'
|
67
68
|
require 'tty/table/orientation/vertical'
|
68
69
|
require 'tty/table/transformation'
|
70
|
+
require 'tty/table/indentation'
|
71
|
+
require 'tty/table/padder'
|
69
72
|
|
70
73
|
require 'tty/table/operations'
|
71
74
|
require 'tty/table/operation/alignment_set'
|
@@ -74,6 +77,7 @@ require 'tty/table/operation/truncation'
|
|
74
77
|
require 'tty/table/operation/wrapped'
|
75
78
|
require 'tty/table/operation/filter'
|
76
79
|
require 'tty/table/operation/escape'
|
80
|
+
require 'tty/table/operation/padding'
|
77
81
|
|
78
82
|
module TTY
|
79
83
|
|
@@ -95,6 +99,9 @@ module TTY
|
|
95
99
|
# Raised when the table orientation is unkown
|
96
100
|
class InvalidOrientationError < ArgumentError; end
|
97
101
|
|
102
|
+
# Raised when the table cannot be resized
|
103
|
+
class ResizeError < ArgumentError; end
|
104
|
+
|
98
105
|
# Raised when the passed in validation argument is of wrong type
|
99
106
|
class ValidationCoercion < TypeError; end
|
100
107
|
|
data/lib/tty/shell/question.rb
CHANGED
@@ -153,7 +153,6 @@ module TTY
|
|
153
153
|
self
|
154
154
|
end
|
155
155
|
|
156
|
-
|
157
156
|
# Reset question object.
|
158
157
|
#
|
159
158
|
# @api public
|
@@ -170,7 +169,7 @@ module TTY
|
|
170
169
|
#
|
171
170
|
# @api public
|
172
171
|
def modify(*rules)
|
173
|
-
@modifier = Modifier.new
|
172
|
+
@modifier = Modifier.new(*rules)
|
174
173
|
self
|
175
174
|
end
|
176
175
|
|
@@ -281,6 +280,7 @@ module TTY
|
|
281
280
|
return default_value if !value && default?
|
282
281
|
|
283
282
|
check_required value
|
283
|
+
return if value.nil?
|
284
284
|
check_valid value unless valid_values.empty?
|
285
285
|
within? value
|
286
286
|
validation.valid_value? value
|
@@ -293,7 +293,7 @@ module TTY
|
|
293
293
|
#
|
294
294
|
# @api private
|
295
295
|
def check_required(value)
|
296
|
-
if required? && !default? &&
|
296
|
+
if required? && !default? && value.nil?
|
297
297
|
raise ArgumentRequired, 'No value provided for required'
|
298
298
|
end
|
299
299
|
end
|
data/lib/tty/shell/response.rb
CHANGED
@@ -46,9 +46,9 @@ module TTY
|
|
46
46
|
if question.mask? && question.echo?
|
47
47
|
reader.getc(question.mask)
|
48
48
|
else
|
49
|
-
TTY.terminal.echo(question.echo)
|
49
|
+
TTY.terminal.echo(question.echo) do
|
50
50
|
question.character? ? reader.getc(question.mask) : reader.gets
|
51
|
-
|
51
|
+
end
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -59,7 +59,7 @@ module TTY
|
|
59
59
|
#
|
60
60
|
# @api public
|
61
61
|
def read_string(error=nil)
|
62
|
-
question.evaluate_response
|
62
|
+
question.evaluate_response(String(read_input).strip)
|
63
63
|
end
|
64
64
|
|
65
65
|
# Read answer's first character
|
@@ -165,10 +165,10 @@ module TTY
|
|
165
165
|
#
|
166
166
|
# @api public
|
167
167
|
def read_multiple
|
168
|
-
response =
|
168
|
+
response = ''
|
169
169
|
loop do
|
170
170
|
value = question.evaluate_response read_input
|
171
|
-
break if !value || value ==
|
171
|
+
break if !value || value == ''
|
172
172
|
next if value !~ /\S/
|
173
173
|
response << value
|
174
174
|
end
|
data/lib/tty/table.rb
CHANGED
@@ -39,6 +39,20 @@ module TTY
|
|
39
39
|
# @api public
|
40
40
|
attr_reader :orientation
|
41
41
|
|
42
|
+
# The table original row count
|
43
|
+
#
|
44
|
+
# @reutrn [Integer]
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
attr_reader :original_rows
|
48
|
+
|
49
|
+
# The table original column count
|
50
|
+
#
|
51
|
+
# @reutrn [Integer]
|
52
|
+
#
|
53
|
+
# @api public
|
54
|
+
attr_reader :original_columns
|
55
|
+
|
42
56
|
# Subset of safe methods that both Array and Hash implement
|
43
57
|
def_delegators(:data, :assoc, :flatten, :include?, :index,
|
44
58
|
:length, :select, :to_a, :values_at, :pretty_print, :rassoc)
|
@@ -100,11 +114,12 @@ module TTY
|
|
100
114
|
validate_options! options
|
101
115
|
@header = (value = options[:header]) ? Header.new(value) : nil
|
102
116
|
@rows = coerce(options.fetch(:rows) { Row.new([]) })
|
103
|
-
@orientation = Orientation.coerce(options.fetch(:orientation) { :horizontal })
|
104
117
|
@rotated = false
|
105
|
-
|
118
|
+
self.orientation = options.fetch(:orientation) { :horizontal }
|
119
|
+
|
106
120
|
assert_row_sizes @rows
|
107
|
-
|
121
|
+
orientation.transform(self)
|
122
|
+
|
108
123
|
yield_or_eval(&block) if block_given?
|
109
124
|
end
|
110
125
|
|
@@ -149,33 +164,37 @@ module TTY
|
|
149
164
|
#
|
150
165
|
# @api private
|
151
166
|
def rotate_vertical
|
152
|
-
@
|
153
|
-
@
|
154
|
-
@
|
167
|
+
@original_columns = column_size
|
168
|
+
@original_rows = row_size
|
169
|
+
@rows = orientation.slice(self)
|
170
|
+
@header = [] if header
|
171
|
+
@rotated = true
|
155
172
|
end
|
156
173
|
|
157
174
|
# Rotate the table horizontally
|
158
175
|
#
|
159
176
|
# @api private
|
160
177
|
def rotate_horizontal
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
178
|
+
if rotated?
|
179
|
+
head, body = orientation.slice(self)
|
180
|
+
if header && header.empty?
|
181
|
+
@header = head[0]
|
182
|
+
@rows = body.map {|row| to_row(row, @header)}
|
183
|
+
else
|
184
|
+
@rows = body.map { |row| to_row(row) }
|
185
|
+
end
|
167
186
|
end
|
168
187
|
end
|
169
188
|
|
170
189
|
# Lookup element of the table given a row(i) and column(j)
|
171
190
|
#
|
172
191
|
# @api public
|
173
|
-
def [](
|
174
|
-
return row(
|
175
|
-
if
|
176
|
-
rows.fetch(
|
192
|
+
def [](row_index, column_index=false)
|
193
|
+
return row(row_index) unless column_index
|
194
|
+
if row_index >= 0 && column_index >= 0
|
195
|
+
rows.fetch(row_index) { return nil }[column_index]
|
177
196
|
else
|
178
|
-
raise TTY::Table::TupleMissing.new(
|
197
|
+
raise TTY::Table::TupleMissing.new(row_index, column_index)
|
179
198
|
end
|
180
199
|
end
|
181
200
|
alias at []
|
@@ -185,8 +204,8 @@ module TTY
|
|
185
204
|
# Set table value at row(i) and column(j)
|
186
205
|
#
|
187
206
|
# @api private
|
188
|
-
def []=(
|
189
|
-
@rows[
|
207
|
+
def []=(row_index, column_index, val)
|
208
|
+
@rows[row_index][column_index] = val
|
190
209
|
end
|
191
210
|
private :[]=
|
192
211
|
|
@@ -313,6 +332,7 @@ module TTY
|
|
313
332
|
# @return [Integer]
|
314
333
|
#
|
315
334
|
# @api public
|
335
|
+
# TODO: renmae to columns_size
|
316
336
|
def column_size
|
317
337
|
return rows[0].size if rows.size > 0
|
318
338
|
return 0
|
@@ -370,6 +390,18 @@ module TTY
|
|
370
390
|
render(:basic)
|
371
391
|
end
|
372
392
|
|
393
|
+
# Return renderer for this table
|
394
|
+
#
|
395
|
+
# @param [Symbol] type
|
396
|
+
# the renderer type
|
397
|
+
#
|
398
|
+
# @param [Hash] options
|
399
|
+
# the renderer options
|
400
|
+
#
|
401
|
+
def renderer(type=:basic, options={})
|
402
|
+
@renderer ||= Renderer.select(type).new(self, options)
|
403
|
+
end
|
404
|
+
|
373
405
|
# Render a given table. This method takes options which will be passed
|
374
406
|
# to the renderer prior to rendering, which allows the caller to set any
|
375
407
|
# table rendering variables.
|
data/lib/tty/table/column_set.rb
CHANGED
@@ -37,6 +37,47 @@ module TTY
|
|
37
37
|
self.class.find_maximas(colcount, data)
|
38
38
|
end
|
39
39
|
|
40
|
+
# Assert data integrity for column widths
|
41
|
+
#
|
42
|
+
# @param [Array] column_widths
|
43
|
+
#
|
44
|
+
# @param [Integer] table_widths
|
45
|
+
#
|
46
|
+
# @raise [TTY::InvalidArgument]
|
47
|
+
#
|
48
|
+
# @api public
|
49
|
+
def self.assert_widths(column_widths, table_widths)
|
50
|
+
if column_widths.empty?
|
51
|
+
raise InvalidArgument, 'Value for :column_widths must be a non-empty array'
|
52
|
+
end
|
53
|
+
if column_widths.size != table_widths
|
54
|
+
raise InvalidArgument, 'Value for :column_widths must match table column count'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Converts column widths to array format or infers default widths
|
59
|
+
#
|
60
|
+
# @param [TTY::Table] table
|
61
|
+
#
|
62
|
+
# @param [Array, Numeric, NilClass] column_widths
|
63
|
+
#
|
64
|
+
# @return [Array[Integer]]
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
def self.widths_from(table, column_widths = nil)
|
68
|
+
case column_widths
|
69
|
+
when Array
|
70
|
+
assert_widths(column_widths, table.column_size)
|
71
|
+
Array(column_widths).map(&:to_i)
|
72
|
+
when Numeric
|
73
|
+
Array.new(table.column_size, column_widths)
|
74
|
+
when NilClass
|
75
|
+
new(table).extract_widths
|
76
|
+
else
|
77
|
+
raise TypeError, 'Invalid type for column widths'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
40
81
|
private
|
41
82
|
|
42
83
|
# Find maximum widths for each row and header if present.
|
@@ -68,7 +109,7 @@ module TTY
|
|
68
109
|
#
|
69
110
|
# @api private
|
70
111
|
def self.find_maximum(data, index)
|
71
|
-
data.map { |row| (value=row.call(index)) ? value.length : 0 }.max
|
112
|
+
data.map { |row| (value = row.call(index)) ? value.length : 0 }.max
|
72
113
|
end
|
73
114
|
|
74
115
|
end # ColumnSet
|