tty 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|