tabulo 0.4.2 → 0.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +79 -17
- data/lib/tabulo/column.rb +10 -4
- data/lib/tabulo/table.rb +58 -0
- data/lib/tabulo/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 452523797b11a532c291705852d73cc00c7aa93b
|
4
|
+
data.tar.gz: 889621678407822d354bbc5aab7a086aaefdf1b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b5311c3013bdba6ff2e7c6a8f0a31d0373d5167c9f34aa45caf2ca1cc5d49c8fbb5c058f166daa6b0d5da41d9d516ee87fd256893d7ce6496277bac9746f0e71
|
7
|
+
data.tar.gz: 8b515f7eca75f4ba532b482d81699b711387e822668f71b9dce86a52f7e71ff27dcdc7bda955525e4d1974346272c49d28422b5c5eb85f4a981962feaedd1961
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -26,21 +26,25 @@ end
|
|
26
26
|
| 5000000 | 10000000 |
|
27
27
|
```
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
29
|
+
Tabulo is flexible:
|
30
|
+
|
31
|
+
* Fix individual column widths, then either [wrap](#width-wrapping-truncation) or
|
32
|
+
[truncate](#width-wrapping-truncation) the overflow as you prefer.
|
33
|
+
* Alternatively, [shrinkwrap](#shrinkwrap) the table so that each column is just wide enough for
|
34
|
+
its contents.
|
35
|
+
* You can cap total table width when shrinkwrapping, to [stop it overflowing your terminal](#max-table-width)
|
36
|
+
horizontally and becoming an unreadable mess.
|
37
|
+
* Cell content [alignment](#cell-alignment) is configurable, but with useful defaults, with numbers
|
38
|
+
aligned right and strings left.
|
39
|
+
* Headers can be [repeated](#repeating-headers) as desired.
|
40
|
+
* A `Tabulo::Table` is an `Enumerable`, so you can [step through it](#enumerator) one row at a time,
|
41
|
+
without having to wait for the entire underlying collection to load.
|
42
|
+
* Each `Tabulo::Row` is also an `Enumerable`.
|
39
43
|
|
40
44
|
```ruby
|
41
45
|
table.each do |row|
|
42
46
|
row.each do |cell|
|
43
|
-
# cell => 1, 2... 2, 4... etc.
|
47
|
+
# cell => 1, 2 ... 2, 4 ... etc.
|
44
48
|
end
|
45
49
|
end
|
46
50
|
```
|
@@ -123,6 +127,7 @@ end
|
|
123
127
|
| 5 | 10 | true |
|
124
128
|
```
|
125
129
|
|
130
|
+
<a name="cell-alignment"></a>
|
126
131
|
### Cell alignment
|
127
132
|
|
128
133
|
By default, column header text is center-aligned, while the content of each body cell is aligned
|
@@ -131,19 +136,20 @@ and `true`) are center-aligned. This can be customized by passing `:center`, `:l
|
|
131
136
|
the `align_header` or `align_body` options of `add_column`, e.g.:
|
132
137
|
|
133
138
|
```ruby
|
134
|
-
|
139
|
+
table.add_column("Doubled", align_header: :left, align_body: :left) { |n| n * 2 }
|
135
140
|
```
|
136
141
|
|
142
|
+
<a name="width-wrapping-truncation"></a>
|
137
143
|
### Column width, wrapping and truncation
|
138
144
|
|
139
145
|
By default, column width is fixed at 12 characters, plus 1 character of padding on either side.
|
140
146
|
This can be adjusted on a column-by-column basis using the `width` option of `add_column`:
|
141
147
|
|
142
148
|
```ruby
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
149
|
+
table = Tabulo::Table.new([1, 2]) do |t|
|
150
|
+
t.add_column(:itself, width: 6)
|
151
|
+
t.add_column(:even?, width: 9)
|
152
|
+
end
|
147
153
|
```
|
148
154
|
|
149
155
|
```
|
@@ -159,7 +165,7 @@ If you want to set the default column width for all columns of the table to some
|
|
159
165
|
than 12, use the `column_width` option when initializing the table:
|
160
166
|
|
161
167
|
```ruby
|
162
|
-
|
168
|
+
table = Tabulo::Table.new([1, 2], columns: %i(itself even?), column_width: 6)
|
163
169
|
```
|
164
170
|
|
165
171
|
```
|
@@ -173,6 +179,60 @@ than 12, use the `column_width` option when initializing the table:
|
|
173
179
|
|
174
180
|
Widths set for individual columns always override the default column width for the table.
|
175
181
|
|
182
|
+
<a name="shrinkwrap"></a>
|
183
|
+
### Automating column widths
|
184
|
+
|
185
|
+
Instead of setting column widths "manually", you can tell the table to sort out the widths
|
186
|
+
itself, so that each column is just wide enough for its header and contents (plus a character
|
187
|
+
of padding):
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
table = Tabulo::Table.new([1, 2], columns: %i(itself even?))
|
191
|
+
table.shrinkwrap!
|
192
|
+
```
|
193
|
+
|
194
|
+
```
|
195
|
+
> puts table
|
196
|
+
+--------+-------+
|
197
|
+
| itself | even? |
|
198
|
+
+--------+-------+
|
199
|
+
| 1 | false |
|
200
|
+
| 2 | true |
|
201
|
+
```
|
202
|
+
|
203
|
+
The `shrinkwrap!` method returns the table itself, so you can "wrap-and-print" in one go:
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
puts Tabulo::Table.new([1, 2], columns: %i(itself even?)).shrinkwrap!
|
207
|
+
```
|
208
|
+
|
209
|
+
<a name="max-table-width"></a>
|
210
|
+
You can place an upper limit on the total width of the table when shrinkwrapping:
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
puts Tabulo::Table.new([1, 2], columns: %i(itself even?)).shrinkwrap!(max_table_width: 17)
|
214
|
+
```
|
215
|
+
|
216
|
+
```
|
217
|
+
+-------+-------+
|
218
|
+
| itsel | even? |
|
219
|
+
| f | |
|
220
|
+
+-------+-------+
|
221
|
+
| 1 | false |
|
222
|
+
| 2 | true |
|
223
|
+
```
|
224
|
+
|
225
|
+
If the table cannot be fit within `max_column_width`, column widths are reduced as required, with
|
226
|
+
wrapping or truncation then occuring as necessary (see [Overflow handling](#overflow-handling)).
|
227
|
+
Under the hood, a character of width is deducted column by column—the widest column being
|
228
|
+
targetted each time—until the table will fit. This is very useful when you want to ensure the
|
229
|
+
table will not overflow your terminal horizontally.
|
230
|
+
|
231
|
+
Note that shrinkwrapping necessarily involves traversing the entire collection up front as
|
232
|
+
the maximum cell width needs to be calculated for each column. You may not want to do this
|
233
|
+
if the collection is very large.
|
234
|
+
|
235
|
+
<a name="overflow-handling"></a>
|
176
236
|
### Overflow handling
|
177
237
|
|
178
238
|
By default, if cell contents exceed their column width, they are wrapped for as many rows as
|
@@ -253,6 +313,7 @@ of the underlying cell value, not the way it is formatted. This is usually the d
|
|
253
313
|
Note also that the item yielded to `.each` for each cell when enumerating over a `Tabulo::Row` is
|
254
314
|
the underlying value of that cell, not its formatted value.
|
255
315
|
|
316
|
+
<a name="repeating-headers"></a>
|
256
317
|
### Repeating headers
|
257
318
|
|
258
319
|
By default, headers are only shown once, at the top of the table (`header_frequency: :start`). If
|
@@ -286,6 +347,7 @@ table = Tabulo::Table.new(1..10, columns: %i(itself even?), header_frequency: 5)
|
|
286
347
|
| 10 | true |
|
287
348
|
```
|
288
349
|
|
350
|
+
<a name="enumerator"></a>
|
289
351
|
### Using a Table Enumerator
|
290
352
|
|
291
353
|
Because it's an `Enumerable`, a `Tabulo::Table` can also give you an `Enumerator`,
|
data/lib/tabulo/column.rb
CHANGED
@@ -3,7 +3,8 @@ module Tabulo
|
|
3
3
|
# @!visibility private
|
4
4
|
class Column
|
5
5
|
|
6
|
-
|
6
|
+
attr_accessor :width
|
7
|
+
attr_reader :header, :label
|
7
8
|
|
8
9
|
# @!visibility private
|
9
10
|
def initialize(options)
|
@@ -28,9 +29,14 @@ module Tabulo
|
|
28
29
|
# @!visibility private
|
29
30
|
def body_cell(source)
|
30
31
|
cell_datum = body_cell_value(source)
|
31
|
-
|
32
|
-
real_alignment = @align_body || infer_alignment(cell_datum)
|
33
|
-
align_cell_content(
|
32
|
+
formatted_content = @formatter.call(cell_datum)
|
33
|
+
real_alignment = (@align_body || infer_alignment(cell_datum))
|
34
|
+
align_cell_content(formatted_content, real_alignment)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @!visibility private
|
38
|
+
def formatted_cell_content(source)
|
39
|
+
@formatter.call(body_cell_value(source))
|
34
40
|
end
|
35
41
|
|
36
42
|
# @!visibility private
|
data/lib/tabulo/table.rb
CHANGED
@@ -148,6 +148,64 @@ module Tabulo
|
|
148
148
|
format_row(false, HORIZONTAL_RULE_CHARACTER, CORNER_CHARACTER, &:horizontal_rule)
|
149
149
|
end
|
150
150
|
|
151
|
+
# Reset all the column widths so that each column is *just* wide enough to accommodate
|
152
|
+
# its header text as well as the formatted content of each its cells for the entire
|
153
|
+
# collection, together with a single character of padding on either side of the column,
|
154
|
+
# without any wrapping.
|
155
|
+
#
|
156
|
+
# Note that calling this method will cause the entire source Enumerable to
|
157
|
+
# be traversed and all the column extractors and formatters to be applied in order
|
158
|
+
# to calculate the required widths.
|
159
|
+
#
|
160
|
+
# @param [Hash] options
|
161
|
+
# @option options [String] :max_table_width (nil) If provided, stops the total table
|
162
|
+
# width (including padding and borders) from expanding beyond this number of characters.
|
163
|
+
# Width is deducted from columns if required to achieve this, with one character progressively
|
164
|
+
# deducted from the width of the widest column until the target is reached. When the
|
165
|
+
# table is printed, wrapping or truncation will then occur in these columns as required
|
166
|
+
# (depending on how they were configured).
|
167
|
+
#
|
168
|
+
# @return [Table] the Table itself
|
169
|
+
def shrinkwrap!(options = { })
|
170
|
+
return self if columns.none?
|
171
|
+
max_table_width = options[:max_table_width]
|
172
|
+
|
173
|
+
header_widths = columns.map { |c| c.header.length }
|
174
|
+
|
175
|
+
column_widths = @sources.inject(header_widths) do |widths, source|
|
176
|
+
columns.map { |c| c.formatted_cell_content(source).length }.zip(widths).map(&:max)
|
177
|
+
end
|
178
|
+
|
179
|
+
columns.zip(column_widths).each do |column, width|
|
180
|
+
column.width = width
|
181
|
+
end
|
182
|
+
|
183
|
+
if max_table_width
|
184
|
+
total_columns_width = columns.inject(0) { |sum, column| sum + column.width }
|
185
|
+
total_padding = columns.count * 2
|
186
|
+
total_borders = columns.count + 1
|
187
|
+
unadjusted_table_width = total_columns_width + total_padding + total_borders
|
188
|
+
|
189
|
+
# Ensure max table width is at least wide enough to accommodate table borders and padding
|
190
|
+
# and one character of content.
|
191
|
+
# TODO Document this behaviour.
|
192
|
+
min_table_width = total_padding + total_borders + columns.count
|
193
|
+
max_table_width = min_table_width if min_table_width > max_table_width
|
194
|
+
|
195
|
+
required_reduction = [unadjusted_table_width - max_table_width, 0].max
|
196
|
+
|
197
|
+
required_reduction.times do
|
198
|
+
widest_column = columns.inject(columns.first) do |widest, column|
|
199
|
+
column.width >= widest.width ? column : widest
|
200
|
+
end
|
201
|
+
|
202
|
+
widest_column.width -= 1
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
self
|
207
|
+
end
|
208
|
+
|
151
209
|
# @!visibility private
|
152
210
|
def formatted_body_row(source, options = { with_header: false })
|
153
211
|
inner = format_row { |column| column.body_cell(source) }
|
data/lib/tabulo/version.rb
CHANGED