tabulo 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/README.md +208 -62
- data/VERSION +1 -1
- data/lib/tabulo/column.rb +25 -16
- data/lib/tabulo/row.rb +2 -2
- data/lib/tabulo/table.rb +82 -22
- data/lib/tabulo/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87e2e8bb108886b594c80a32396ab586c90792ae6e5efeb5769e457b13d6c96d
|
4
|
+
data.tar.gz: 03c93ac0d5419fb654244ab574d9e7aa43263f40ca71e008f6d43772a76ffece
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae04026433b44638cd777f483fb0e8b0494370cc71fb84c5bc91cc38c65c92aa4e68183b48db1e92e53b3e3ede6b680975190d2626510f0ad31b6f651bb9538d
|
7
|
+
data.tar.gz: 5a543577d7d678ccee445789d588147a778fa45899876872749ad1ced9119e99369b43b1e7b642d2f9eae27c41d2e904de9d7765513b9b813824deac5e8d958c
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### v2.3.0
|
4
|
+
|
5
|
+
* Provide `#remove_column` method.
|
6
|
+
* Provide `before` option to `#add_column`, to allow insertion of column into non-final position.
|
7
|
+
* Provide `styler` and `header_styler` options in table initializer, to enable default stylers
|
8
|
+
to be set for all columns.
|
9
|
+
* Documentation improvements and code tidy-ups.
|
10
|
+
|
3
11
|
### v2.2.0
|
4
12
|
|
5
13
|
* New `column_formatter` option on `Tabulo::Table` initializer, enabling the table's default column
|
@@ -15,7 +23,7 @@
|
|
15
23
|
|
16
24
|
### v2.1.0
|
17
25
|
|
18
|
-
* New
|
26
|
+
* New `reduced_ascii` and `reduced_modern` border options
|
19
27
|
* Fix `column_width` option not properly inherited from original table by the new table created
|
20
28
|
by calling #transpose.
|
21
29
|
|
data/README.md
CHANGED
@@ -7,13 +7,9 @@
|
|
7
7
|
[![Code Climate][CC img]][Code Climate]
|
8
8
|
[![Awesome][AR img]][Awesome Ruby]
|
9
9
|
|
10
|
-
Tabulo is a terminal table generator for Ruby.
|
10
|
+
Tabulo is a terminal table generator for Ruby. It is both highly configurable and very easy to use.
|
11
11
|
|
12
|
-
|
13
|
-
readable tables, even from large and unwieldy data sets and streams.
|
14
|
-
|
15
|
-
Tabulo is both easy to use and feature rich, allowing you to quickly tabulate any enumerable
|
16
|
-
collection, with options for automatic column sizing, a variety of border styles, and more.
|
12
|
+
_Quick API:_
|
17
13
|
|
18
14
|
```
|
19
15
|
> puts Tabulo::Table.new(User.all, :id, :first_name, :last_name, border: :modern).pack
|
@@ -25,12 +21,35 @@ collection, with options for automatic column sizing, a variety of border styles
|
|
25
21
|
└────┴────────────┴───────────┘
|
26
22
|
```
|
27
23
|
|
24
|
+
_Full API:_
|
25
|
+
|
26
|
+
```
|
27
|
+
table = Tabulo::Table.new(User.all, border: :modern) do |t|
|
28
|
+
t.add_column("ID", &:id)
|
29
|
+
t.add_column("First name", &:first_name)
|
30
|
+
t.add_column("Last name") { |user| user.last_name.upcase }
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
```
|
35
|
+
> puts table.pack
|
36
|
+
┌────┬────────────┬───────────┐
|
37
|
+
│ ID │ First name │ Last name │
|
38
|
+
├────┼────────────┼───────────┤
|
39
|
+
│ 1 │ John │ CITIZEN │
|
40
|
+
│ 2 │ Jane │ DOE │
|
41
|
+
└────┴────────────┴───────────┘
|
42
|
+
```
|
43
|
+
|
28
44
|
## Features
|
29
45
|
|
46
|
+
* Presents a [DRY initialization interface](#configuring-columns): by being “column based” rather than
|
47
|
+
“row based”, it spares the developer the burden of syncing the ordering within the header row
|
48
|
+
with that of the body rows.
|
30
49
|
* Lets you set [fixed column widths](#fixed-column-widths), then either [wrap](#overflow-handling)
|
31
50
|
or [truncate](#overflow-handling) the overflow.
|
32
|
-
* Alternatively, [
|
33
|
-
its contents, but [without overflowing the terminal horizontally](#max-table-width).
|
51
|
+
* Alternatively, you can [“pack”](#pack) the table so that each column is automatically just
|
52
|
+
wide enough for its contents, but [without overflowing the terminal horizontally](#max-table-width).
|
34
53
|
* Alignment of cell content is [configurable](#cell-alignment), but has helpful content-based
|
35
54
|
defaults (numbers right, strings left).
|
36
55
|
* Tabulate any `Enumerable`: the underlying collection need not be an array.
|
@@ -44,9 +63,8 @@ collection, with options for automatic column sizing, a variety of border styles
|
|
44
63
|
* Multibyte Unicode characters are correctly handled.
|
45
64
|
* Apply [colours](#colours-and-styling) and other styling to table content and borders, without breaking the table.
|
46
65
|
* Easily [transpose](#transposition) the table, so that rows are swapped with columns.
|
47
|
-
* Choose from multiple [border configurations](#borders), including Markdown,
|
48
|
-
|
49
|
-
designed to spare the developer the burden of syncing the ordering within the header row with that of the body rows.
|
66
|
+
* Choose from multiple [border configurations](#borders), including Markdown, “ASCII”, and smoothly
|
67
|
+
joined Unicode border characters.
|
50
68
|
|
51
69
|
Tabulo has also been ported to Crystal (with some modifications): see [Tablo](https://github.com/hutou/tablo).
|
52
70
|
|
@@ -65,7 +83,11 @@ Tabulo has also been ported to Crystal (with some modifications): see [Tablo](ht
|
|
65
83
|
* [Automating column widths](#automating-column-widths)
|
66
84
|
* [Overflow handling](#overflow-handling)
|
67
85
|
* [Formatting cell values](#formatting-cell-values)
|
68
|
-
* [Colours and styling](#colours-and-styling)
|
86
|
+
* [Colours and other styling](#colours-and-styling)
|
87
|
+
* [Styling cell content](#styling-cell-content)
|
88
|
+
* [Styling column headers](#styling-column-headers)
|
89
|
+
* [Setting default styles](#default-styles)
|
90
|
+
* [Styling borders](#border-styling)
|
69
91
|
* [Repeating headers](#repeating-headers)
|
70
92
|
* [Using a Table Enumerator](#using-a-table-enumerator)
|
71
93
|
* [Accessing cell values](#accessing-cell-values)
|
@@ -73,13 +95,14 @@ Tabulo has also been ported to Crystal (with some modifications): see [Tablo](ht
|
|
73
95
|
* [Transposing rows and columns](#transposition)
|
74
96
|
* [Border configuration](#borders)
|
75
97
|
* [Row dividers](#dividers)
|
98
|
+
* [Using a table as a snapshot rather than as a dynamic view](#freezing-a-table)
|
76
99
|
* [Comparison with other libraries](#motivation)
|
77
100
|
* [Contributing](#contributing)
|
78
101
|
* [License](#license)
|
79
102
|
|
80
103
|
## Installation
|
81
104
|
|
82
|
-
Add this line to your application
|
105
|
+
Add this line to your application’s Gemfile:
|
83
106
|
|
84
107
|
```ruby
|
85
108
|
gem 'tabulo'
|
@@ -126,7 +149,7 @@ table = Tabulo::Table.new([1, 2, 5]) do |t|
|
|
126
149
|
end
|
127
150
|
```
|
128
151
|
|
129
|
-
When the columns correspond to methods, you can also use the
|
152
|
+
When the columns correspond to methods, you can also use the “quick API”, by passing a symbol
|
130
153
|
directly to `new` for each column:
|
131
154
|
|
132
155
|
```ruby
|
@@ -167,6 +190,30 @@ end
|
|
167
190
|
+--------------+--------------+--------------+
|
168
191
|
```
|
169
192
|
|
193
|
+
By default, each new column is added to the right of all the other columns so far added to the
|
194
|
+
table. However, if you want to insert a new column into some other position, you can use the
|
195
|
+
`before` option, passing the label of the column to the left of which you want the new column to be added:
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
table = Table::Table.new([1, 2, 3], :itself, :odd?)
|
199
|
+
table.add_column(:even?, before: :odd?)
|
200
|
+
```
|
201
|
+
|
202
|
+
```
|
203
|
+
> puts table
|
204
|
+
+--------------+--------------+--------------+
|
205
|
+
| itself | even? | odd? |
|
206
|
+
+--------------+--------------+--------------+
|
207
|
+
| 1 | false | true |
|
208
|
+
| 2 | true | false |
|
209
|
+
| 5 | false | true |
|
210
|
+
+--------------+--------------+--------------+
|
211
|
+
```
|
212
|
+
|
213
|
+
There is also a `#remove_column` method, for deleting an existing column from a table; see the
|
214
|
+
[documentation](https://www.rubydoc.info/gems/tabulo/2.3.0/Tabulo/Table#remove_column-instance_method)
|
215
|
+
for details.
|
216
|
+
|
170
217
|
<a name="cell-alignment"></a>
|
171
218
|
### Cell alignment
|
172
219
|
|
@@ -278,7 +325,7 @@ table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?, column_padding: [0,
|
|
278
325
|
<a name="pack"></a>
|
279
326
|
#### Automating column widths
|
280
327
|
|
281
|
-
Instead of setting column widths
|
328
|
+
Instead of setting column widths “manually”, you can tell the table to sort out the widths
|
282
329
|
itself, so that each column is just wide enough for its header and contents (plus a character
|
283
330
|
of padding):
|
284
331
|
|
@@ -297,7 +344,7 @@ table.pack
|
|
297
344
|
+--------+-------+
|
298
345
|
```
|
299
346
|
|
300
|
-
The `pack` method returns the table itself, so you can
|
347
|
+
The `pack` method returns the table itself, so you can “pack-and-print” in one go:
|
301
348
|
|
302
349
|
```ruby
|
303
350
|
puts Tabulo::Table.new([1, 2], :itself, :even?).pack
|
@@ -334,12 +381,15 @@ the table will fit.
|
|
334
381
|
|
335
382
|
Note that `pack`ing the table necessarily involves traversing the entire collection up front as
|
336
383
|
the maximum cell width needs to be calculated for each column. You may not want to do this
|
337
|
-
if the collection is very large.
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
384
|
+
if the collection is very large.
|
385
|
+
|
386
|
+
Note also the effect of `pack` is to fix the column widths as appropriate to the formatted cell
|
387
|
+
contents given the state of the underlying collection _at the point of packing_. If the underlying
|
388
|
+
collection changes between that point, and when the table is printed, then the columns will _not_ be
|
389
|
+
resized yet again on printing. This is a consequence of the table always being essentially a
|
390
|
+
“live view” on the underlying collection: formatted contents are never cached within the
|
391
|
+
table itself. There are [ways around this](#freezing-a-table), however, if this is not the desired
|
392
|
+
behaviour—see [below](#freezing-a-table).
|
343
393
|
|
344
394
|
<a name="overflow-handling"></a>
|
345
395
|
#### Overflow handling
|
@@ -397,10 +447,10 @@ The character used to indicate truncation, which defaults to `~`, can be configu
|
|
397
447
|
### Formatting cell values
|
398
448
|
|
399
449
|
While the callable passed to `add_column` determines the underyling, calculated value in each
|
400
|
-
cell of the column, there is a separate concept, of a
|
401
|
-
be visually displayed. By default, `.to_s` is called on the underlying cell value to
|
402
|
-
it; however, you can format it differently by passing another callable to the
|
403
|
-
of `add_column`:
|
450
|
+
cell of the column, there is a separate concept, of a “formatter”, that determines how
|
451
|
+
that value will be visually displayed. By default, `.to_s` is called on the underlying cell value to
|
452
|
+
“format” it; however, you can format it differently by passing another callable to the
|
453
|
+
`formatter` option of `add_column`:
|
404
454
|
|
405
455
|
```ruby
|
406
456
|
table = Tabulo::Table.new(1..3) do |t|
|
@@ -422,7 +472,7 @@ end
|
|
422
472
|
+--------------+--------------+
|
423
473
|
```
|
424
474
|
|
425
|
-
Note the numbers in the
|
475
|
+
Note the numbers in the “Reciprocal” column in this example are still right-aligned, even though
|
426
476
|
the callable passed to `formatter` returns a String. Default cell alignment is determined by the type
|
427
477
|
of the underlying cell value, not the way it is formatted. This is usually the desired result.
|
428
478
|
|
@@ -452,7 +502,10 @@ Formatters set for individual columns on calling `#add_column` always override t
|
|
452
502
|
the table.
|
453
503
|
|
454
504
|
<a name="colours-and-styling"></a>
|
455
|
-
### Colours and styling
|
505
|
+
### Colours and other styling
|
506
|
+
|
507
|
+
<a name="styling-cell-content"></a>
|
508
|
+
#### Styling cell content
|
456
509
|
|
457
510
|
In most terminals, if you want to print text that is coloured, or has certain other styles such as
|
458
511
|
underlining, you need to use ANSI escape sequences, either directly, or by means of a library such
|
@@ -485,13 +538,19 @@ table.add_column(
|
|
485
538
|
)
|
486
539
|
```
|
487
540
|
|
488
|
-
The `styler` option should be passed a callable that takes two parameters: the
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
541
|
+
The `styler` option should be passed a callable that takes two parameters: the first represents
|
542
|
+
the underlying value of the cell (in this case a boolean indicating whether the number is even);
|
543
|
+
and the second represents the formatted string value of that cell, i.e. the cell content after
|
544
|
+
any processing by the [formatter](#formatting-cell-values). If the content of a cell is wrapped
|
545
|
+
over multiple lines, then the `styler` will be called once per line, so that each line of the
|
546
|
+
cell will have the escape sequence applied to it separately (ensuring the styling doesn’t bleed
|
547
|
+
into neighbouring cells).
|
548
|
+
|
549
|
+
If the content of a cell has been [truncated](#overflow-handling), then whatever colours or other styling
|
550
|
+
apply to the cell content will also be applied the truncation indicator character.
|
551
|
+
|
552
|
+
<a name="styling-column-headers"></a>
|
553
|
+
#### Styling column headers
|
495
554
|
|
496
555
|
If you want to apply colours or other styling to the content of a column header, as opposed
|
497
556
|
to cells in the table body, use the `header_styler` option, e.g.:
|
@@ -500,23 +559,39 @@ to cells in the table body, use the `header_styler` option, e.g.:
|
|
500
559
|
table.add_column(:even?, header_styler: -> (s) { "\033[32m#{s}\033[0m" })
|
501
560
|
```
|
502
561
|
|
503
|
-
|
504
|
-
|
562
|
+
<a name="default-styles"></a>
|
563
|
+
#### Setting default styles
|
564
|
+
|
565
|
+
By default, no styling is applied to the headers or body content of a column unless configured to do
|
566
|
+
so via the `header_styler` or `styler` option when calling `add_column` for that particular column.
|
567
|
+
It is possible to apply styling by default to _all_ columns in a table, however, as the table initializer
|
568
|
+
also accepts both a `header_styler` and a `styler` option. For example, if you want all the header text
|
569
|
+
in the table to be green, you could do:
|
505
570
|
|
506
571
|
```ruby
|
507
|
-
table = Tabulo::Table.new(1..5, :itself, :even?, :odd?,
|
572
|
+
table = Tabulo::Table.new(1..5, :itself, :even?, :odd?, header_styler: -> (s) { "\033[32m#{s}\033[0m" })
|
508
573
|
```
|
509
574
|
|
510
|
-
|
511
|
-
|
575
|
+
Now, all columns in the table will have automatically have green header text, unless overridden
|
576
|
+
by another header styler being passed to `#add_column`.
|
577
|
+
|
578
|
+
<a name="border-styling"></a>
|
579
|
+
#### Styling borders
|
580
|
+
|
581
|
+
Styling can also be applied to borders and dividing lines, using the `border_styler` option when
|
582
|
+
initializing the table, e.g.:
|
583
|
+
|
584
|
+
```ruby
|
585
|
+
table = Tabulo::Table.new(1..5, :itself, :even?, :odd?, border_styler: -> (s) { "\033[32m#{s}\033[0m" })
|
586
|
+
```
|
512
587
|
|
513
588
|
<a name="repeating-headers"></a>
|
514
589
|
### Repeating headers
|
515
590
|
|
516
591
|
By default, headers are only shown once, at the top of the table (`header_frequency: :start`). If
|
517
592
|
`header_frequency` is passed `nil`, headers are not shown at all; or, if passed an `Integer` N,
|
518
|
-
headers are shown at the top and then repeated every N rows. This can be handy when you
|
519
|
-
at table that
|
593
|
+
headers are shown at the top and then repeated every N rows. This can be handy when you’re looking
|
594
|
+
at table that’s taller than your terminal.
|
520
595
|
|
521
596
|
E.g.:
|
522
597
|
|
@@ -548,7 +623,7 @@ table = Tabulo::Table.new(1..10, :itself, :even?, header_frequency: 5)
|
|
548
623
|
<a name="enumerator"></a>
|
549
624
|
### Using a Table Enumerator
|
550
625
|
|
551
|
-
Because it
|
626
|
+
Because it’s an `Enumerable`, a `Tabulo::Table` can also give you an `Enumerator`,
|
552
627
|
which is useful when you want to step through rows one at a time. In a Rails console,
|
553
628
|
for example, you might do this:
|
554
629
|
|
@@ -596,8 +671,8 @@ end
|
|
596
671
|
|
597
672
|
The first argument to `add_column` always provides the key for the purpose of accessing the `Hash`
|
598
673
|
form of a `Tabulo::Row`. (If the provided argument was a `String`, it will be converted to a
|
599
|
-
`Symbol` for purposes of accessing this `Hash`.) This key serves as a sort of
|
600
|
-
the column; and it need not be the same as the column header. If we want the header to be different
|
674
|
+
`Symbol` for purposes of accessing this `Hash`.) This key serves as a sort of “logical label”
|
675
|
+
for the column; and it need not be the same as the column header. If we want the header to be different
|
601
676
|
to the label, we can achieve this using the `header` option to `add_column`:
|
602
677
|
|
603
678
|
```ruby
|
@@ -676,8 +751,8 @@ a new table in which the rows and columns are swapped:
|
|
676
751
|
|
677
752
|
By default, a header row is added to the new table, showing the string value of the element
|
678
753
|
represented in that column. This can be configured, however, along with other aspects of
|
679
|
-
`transpose
|
680
|
-
[documentation](https://www.rubydoc.info/gems/tabulo/2.
|
754
|
+
`transpose`’s behaviour. For details, see the
|
755
|
+
[documentation](https://www.rubydoc.info/gems/tabulo/2.3.0/Tabulo/Table#transpose-instance_method).
|
681
756
|
|
682
757
|
<a name="borders"></a>
|
683
758
|
### Configuring borders
|
@@ -813,6 +888,76 @@ If you want a line before every row, pass `1` to `row_divider_frequency`. For ex
|
|
813
888
|
└──────────────┴──────────────┴──────────────┘
|
814
889
|
```
|
815
890
|
|
891
|
+
<a name="freezing-a-table"></a>
|
892
|
+
### Using a table as a snapshot rather than as a dynamic view
|
893
|
+
|
894
|
+
The nature of a `Tabulo::Table` is that of a dynamic view onto the underlying `sources` enumerable
|
895
|
+
from which it was initialized (or which was subsequently assigned to its `sources` attribute). That
|
896
|
+
is, if the contents of the `sources` enumerable change subsequent to initialization of or assignment to
|
897
|
+
`sources`, then the table when printed will show the `sources` as they are at the time of printing,
|
898
|
+
not as they were at the time of initialization or assignment. For example:
|
899
|
+
|
900
|
+
```ruby
|
901
|
+
arr = [1, 2]
|
902
|
+
table = Tabulo::Table.new(arr, :itself, :even?, :odd?)
|
903
|
+
```
|
904
|
+
|
905
|
+
```
|
906
|
+
> puts table
|
907
|
+
+--------------+--------------+--------------+
|
908
|
+
| itself | even? | odd? |
|
909
|
+
+--------------+--------------+--------------+
|
910
|
+
| 1 | false | true |
|
911
|
+
| 2 | true | false |
|
912
|
+
+--------------+--------------+--------------+
|
913
|
+
```
|
914
|
+
|
915
|
+
```ruby
|
916
|
+
arr << 3
|
917
|
+
```
|
918
|
+
|
919
|
+
```
|
920
|
+
> puts table
|
921
|
+
+--------------+--------------+--------------+
|
922
|
+
| itself | even? | odd? |
|
923
|
+
+--------------+--------------+--------------+
|
924
|
+
| 1 | false | true |
|
925
|
+
| 2 | true | false |
|
926
|
+
| 3 | false | true |
|
927
|
+
+--------------+--------------+--------------+
|
928
|
+
```
|
929
|
+
|
930
|
+
In this example, even though no direct mutations have been made to `table`, the result
|
931
|
+
of calling `puts table` has changed, in virtue of a mutation on the underyling enumerable `arr`.
|
932
|
+
|
933
|
+
A similar behaviour can be seen when `sources` is an ActiveRecord query, and there
|
934
|
+
are changes to the relevant database table(s) such that the result of the query changes. This is
|
935
|
+
worth bearing in mind when calling [`pack`](#pack) on a table, since if the `sources` enumerable
|
936
|
+
changes between `pack`ing and printing, then the column widths calculated by the `pack` method
|
937
|
+
may no longer be “just right” given the changed `sources`.
|
938
|
+
|
939
|
+
If this is not the desired behaviour, there are ways around this. For example, if dealing with an
|
940
|
+
ActiveRecord relation, you can convert the query to a plain array before initializing the table:
|
941
|
+
|
942
|
+
```ruby
|
943
|
+
sources = User.all.to_a
|
944
|
+
table = Tabulo::Table.new(sources, :id, :first_name, :last_name)
|
945
|
+
table.pack
|
946
|
+
puts table
|
947
|
+
```
|
948
|
+
|
949
|
+
Passing an `Array` rather than the ActiveRecord query directly means that if there are changes to
|
950
|
+
the content of the `users` database table, these will not be reflected in the rendered content of
|
951
|
+
the `Tabulo::Table` (unless some of the `Tabulo::Table` columns are based on callables that perform
|
952
|
+
further database queries when called…).
|
953
|
+
|
954
|
+
Note that it is also possible simply to store the string value of a table for later use,
|
955
|
+
rather than the table itself:
|
956
|
+
|
957
|
+
```ruby
|
958
|
+
rendered_table = Tabulo::Table.new(1..10, :itself, :even?, :odd?).pack.to_s
|
959
|
+
```
|
960
|
+
|
816
961
|
<a name="motivation"></a>
|
817
962
|
## Comparison with other libraries
|
818
963
|
|
@@ -826,7 +971,7 @@ There are other libraries for generating plain text tables in Ruby. Popular amon
|
|
826
971
|
*DISCLAIMER: My comments regarding these other libraries are based only on my own, possibly flawed
|
827
972
|
reading of the documentation for, and experimentation with, these libraries at the time of my
|
828
973
|
writing this. Their APIs, features or documentation may well change between when I write this, and
|
829
|
-
when you read it. Please consult the libraries
|
974
|
+
when you read it. Please consult the libraries’ own documentation for yourself, rather than relying
|
830
975
|
on these comments.*
|
831
976
|
|
832
977
|
While these libraries have their strengths, I have personally found that, for the common use case of
|
@@ -835,7 +980,7 @@ query result), using these libraries feels more cumbersome than it could be.
|
|
835
980
|
|
836
981
|
For example, suppose we have called `User.all` from the Rails console, and want to print
|
837
982
|
a table showing the email, first name, last name and ID of each user,
|
838
|
-
with column headings. Also, we want the ID column to be right-aligned, because it
|
983
|
+
with column headings. Also, we want the ID column to be right-aligned, because it’s a number.
|
839
984
|
|
840
985
|
In **terminal-table**, we could achieve this as follows:
|
841
986
|
|
@@ -848,7 +993,7 @@ puts table
|
|
848
993
|
```
|
849
994
|
|
850
995
|
The problem here is that there is no single source of knowledge about which columns
|
851
|
-
appear, and in which order. If we want to add another column to the left of
|
996
|
+
appear, and in which order. If we want to add another column to the left of “email”,
|
852
997
|
we need to amend the rows array, and the headings array, and the index passed to `align_column`.
|
853
998
|
We bear the burden of keeping these three in sync. This is not be a big deal for small one-off
|
854
999
|
tables, but for tables that have many columns, or that are constructed
|
@@ -856,9 +1001,9 @@ dynamically based on user input or other runtime factors determining the columns
|
|
856
1001
|
to be included, this can be a hassle and a source of brittleness.
|
857
1002
|
|
858
1003
|
**tty-table** has a somewhat different API to `terminal-table`. It offers both a
|
859
|
-
|
860
|
-
is similar to `terminal-table
|
861
|
-
column ordering across multiple code locations. The
|
1004
|
+
“row-based” and a “column-based” method of initializing a table. The row-based method
|
1005
|
+
is similar to `terminal-table`’s in that it burdens the developer with syncing the
|
1006
|
+
column ordering across multiple code locations. The “column-based” API for `tty-table`, on
|
862
1007
|
the other hand, seems to avoid this problem. One way of using it is like this:
|
863
1008
|
|
864
1009
|
```ruby
|
@@ -874,26 +1019,27 @@ table = TTY::Table.new [
|
|
874
1019
|
puts table
|
875
1020
|
```
|
876
1021
|
|
877
|
-
While this doesn
|
1022
|
+
While this doesn’t seem too bad, it does mean that the underlying collection (`users`) has to
|
878
1023
|
be traversed multiple times, once for each column, which is inefficient, particularly
|
879
|
-
if the underlying collection is large. In addition, it
|
1024
|
+
if the underlying collection is large. In addition, it’s not clear how to pass separate
|
880
1025
|
formatting information for each column when initializing in this way. (Perhaps there is a way to do
|
881
|
-
this, but if there is, it doesn
|
1026
|
+
this, but if there is, it doesn’t seem to be documented.) So it seems we still have to use
|
882
1027
|
`table.align_column(3, :right)`, which again burdens us with keeping the column index
|
883
1028
|
passed to `align_column` in sync.
|
884
1029
|
|
885
1030
|
As for **table\_print**, this is a handy gem for quickly tabulating ActiveRecord
|
886
1031
|
collections from the Rails console. `table_print` is similar to `tabulo` in that it has a
|
887
|
-
column-based API, so it doesn
|
1032
|
+
column-based API, so it doesn’t suffer from the multiple-source-of-knowledge issue in regards to
|
888
1033
|
column orderings. However, it lacks certain other useful features, such as the ability to repeat
|
889
1034
|
headers every N rows, the automatic alignment of columns based on cell content (numbers right,
|
890
1035
|
strings left), and a quick and easy way to automatically resize columns to accommodate cell content
|
891
|
-
without overflowing the terminal. Also, as of the time of writing, `table_print
|
1036
|
+
without overflowing the terminal. Also, as of the time of writing, `table_print`’s last significant
|
892
1037
|
commit (ignoring a deprecation warning fix in April 2018) was in March 2016.
|
893
1038
|
|
894
|
-
Finally, it is worth mentioning the **hirb** library. This is similar to `table_print`, in that
|
1039
|
+
Finally, it is worth mentioning the **hirb** library. This is similar to `table_print`, in that
|
1040
|
+
it’s
|
895
1041
|
well suited to quickly displaying ActiveRecord collections from the Rails console. However, like
|
896
|
-
`table_print`, there are certain useful features it
|
1042
|
+
`table_print`, there are certain useful features it’s lacking; and using it outside the console
|
897
1043
|
environment seems cumbersome. Moreover, it seems no longer to be maintained. At the time of writing,
|
898
1044
|
its last commit was in March 2015.
|
899
1045
|
|
@@ -915,14 +1061,14 @@ The gem is available as open source under the terms of the [MIT
|
|
915
1061
|
License](http://opensource.org/licenses/MIT).
|
916
1062
|
|
917
1063
|
[Gem Version]: https://rubygems.org/gems/tabulo
|
918
|
-
[Documentation]: http://www.rubydoc.info/gems/tabulo/2.
|
1064
|
+
[Documentation]: http://www.rubydoc.info/gems/tabulo/2.3.0
|
919
1065
|
[Build Status]: https://travis-ci.org/matt-harvey/tabulo
|
920
1066
|
[Coverage Status]: https://coveralls.io/r/matt-harvey/tabulo
|
921
1067
|
[Code Climate]: https://codeclimate.com/github/matt-harvey/tabulo
|
922
1068
|
[Awesome Ruby]: https://github.com/markets/awesome-ruby#cli-utilities
|
923
1069
|
|
924
1070
|
[GV img]: https://img.shields.io/gem/v/tabulo.svg
|
925
|
-
[DC img]: https://img.shields.io/badge/documentation-v2.
|
1071
|
+
[DC img]: https://img.shields.io/badge/documentation-v2.3.0-blue.svg
|
926
1072
|
[BS img]: https://img.shields.io/travis/matt-harvey/tabulo.svg
|
927
1073
|
[CS img]: https://img.shields.io/coveralls/matt-harvey/tabulo.svg
|
928
1074
|
[CC img]: https://codeclimate.com/github/matt-harvey/tabulo/badges/gpa.svg
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.3.0
|
data/lib/tabulo/column.rb
CHANGED
@@ -6,16 +6,23 @@ module Tabulo
|
|
6
6
|
attr_accessor :width
|
7
7
|
attr_reader :header
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
|
9
|
+
def initialize(
|
10
|
+
align_body:,
|
11
|
+
align_header:,
|
12
|
+
extractor:,
|
13
|
+
formatter:,
|
14
|
+
header:,
|
15
|
+
header_styler:,
|
16
|
+
padding_character:,
|
17
|
+
styler:,
|
18
|
+
truncation_indicator:,
|
19
|
+
width:)
|
11
20
|
|
12
|
-
@header = header
|
13
|
-
@width = width
|
14
|
-
@align_header = align_header
|
15
21
|
@align_body = align_body
|
16
|
-
@
|
22
|
+
@align_header = align_header
|
17
23
|
@extractor = extractor
|
18
|
-
@
|
24
|
+
@formatter = formatter
|
25
|
+
@header = header
|
19
26
|
|
20
27
|
@header_styler =
|
21
28
|
if header_styler
|
@@ -24,31 +31,33 @@ module Tabulo
|
|
24
31
|
-> (_, s) { s }
|
25
32
|
end
|
26
33
|
|
27
|
-
@truncation_indicator = truncation_indicator
|
28
34
|
@padding_character = padding_character
|
35
|
+
@styler = styler || -> (_, s) { s }
|
36
|
+
@truncation_indicator = truncation_indicator
|
37
|
+
@width = width
|
29
38
|
end
|
30
39
|
|
31
40
|
def header_cell
|
32
41
|
Cell.new(
|
33
|
-
value: @header,
|
34
|
-
formatter: -> (s) { s },
|
35
42
|
alignment: @align_header,
|
36
|
-
|
43
|
+
formatter: -> (s) { s },
|
44
|
+
padding_character: @padding_character,
|
37
45
|
styler: @header_styler,
|
38
46
|
truncation_indicator: @truncation_indicator,
|
39
|
-
|
47
|
+
value: @header,
|
48
|
+
width: @width,
|
40
49
|
)
|
41
50
|
end
|
42
51
|
|
43
52
|
def body_cell(source)
|
44
53
|
Cell.new(
|
45
|
-
value: body_cell_value(source),
|
46
|
-
formatter: @formatter,
|
47
54
|
alignment: @align_body,
|
48
|
-
|
55
|
+
formatter: @formatter,
|
56
|
+
padding_character: @padding_character,
|
49
57
|
styler: @styler,
|
50
58
|
truncation_indicator: @truncation_indicator,
|
51
|
-
|
59
|
+
value: body_cell_value(source),
|
60
|
+
width: @width,
|
52
61
|
)
|
53
62
|
end
|
54
63
|
|
data/lib/tabulo/row.rb
CHANGED
@@ -7,11 +7,11 @@ module Tabulo
|
|
7
7
|
attr_reader :source
|
8
8
|
|
9
9
|
# @!visibility private
|
10
|
-
def initialize(table, source,
|
10
|
+
def initialize(table, source, divider: false, header: :top)
|
11
11
|
@table = table
|
12
12
|
@source = source
|
13
|
-
@header = header
|
14
13
|
@divider = divider
|
14
|
+
@header = header
|
15
15
|
end
|
16
16
|
|
17
17
|
# Calls the given block once for each {Cell} in the {Row}, passing that {Cell} as parameter.
|
data/lib/tabulo/table.rb
CHANGED
@@ -74,17 +74,21 @@ module Tabulo
|
|
74
74
|
# element indicates the amount to apply to the right.
|
75
75
|
# @param [Integer, nil] column_width The default column width for columns in this
|
76
76
|
# table, not excluding padding. If <tt>nil</tt>, then {DEFAULT_COLUMN_WIDTH} will be used.
|
77
|
-
# @param [nil, #to_proc] formatter (:to_s.to_proc) The default
|
78
|
-
#
|
77
|
+
# @param [nil, #to_proc] formatter (:to_s.to_proc) The default formatter for columns in this
|
78
|
+
# table. See `formatter` option of {#add_column} for details.
|
79
79
|
# @param [:start, nil, Integer] header_frequency (:start) Controls the display of column headers.
|
80
80
|
# If passed <tt>:start</tt>, headers will be shown at the top of the table only. If passed <tt>nil</tt>,
|
81
81
|
# headers will not be shown. If passed an Integer N (> 0), headers will be shown at the top of the table,
|
82
82
|
# then repeated every N rows.
|
83
|
+
# @param [nil, #to_proc] header_styler (nil) The default header styler for columns in this
|
84
|
+
# table. See `header_styler` option of {#add_column} for details.
|
83
85
|
# @param [nil, Integer] row_divider_frequency (nil) Controls the display of horizontal row dividers within
|
84
86
|
# the table body. If passed <tt>nil</tt>, dividers will not be shown. If passed an Integer N (> 0),
|
85
87
|
# dividers will be shown after every N rows. The characters used to form the dividers are
|
86
88
|
# determined by the `border` option, and are the same as those used to form the bottom edge of the
|
87
89
|
# header row.
|
90
|
+
# @param [nil, #to_proc] styler (nil) The default styler for columns in this table. See `styler`
|
91
|
+
# option of {#add_column} for details.
|
88
92
|
# @param [nil, String] truncation_indicator Determines the character used to indicate that a
|
89
93
|
# cell's content has been truncated. If omitted or passed <tt>nil</tt>,
|
90
94
|
# defaults to {DEFAULT_TRUNCATION_INDICATOR}. If passed something other than <tt>nil</tt> or
|
@@ -114,7 +118,9 @@ module Tabulo
|
|
114
118
|
column_width: nil,
|
115
119
|
formatter: :to_s.to_proc,
|
116
120
|
header_frequency: :start,
|
121
|
+
header_styler: nil,
|
117
122
|
row_divider_frequency: nil,
|
123
|
+
styler: nil,
|
118
124
|
truncation_indicator: nil,
|
119
125
|
wrap_body_cells_to: nil,
|
120
126
|
wrap_header_cells_to: nil)
|
@@ -139,6 +145,7 @@ module Tabulo
|
|
139
145
|
@column_width = (column_width || DEFAULT_COLUMN_WIDTH)
|
140
146
|
@formatter = formatter
|
141
147
|
@header_frequency = header_frequency
|
148
|
+
@header_styler = header_styler
|
142
149
|
@row_divider_frequency = row_divider_frequency
|
143
150
|
@truncation_indicator = validate_character(truncation_indicator,
|
144
151
|
DEFAULT_TRUNCATION_INDICATOR, InvalidTruncationIndicatorError, "truncation indicator")
|
@@ -171,6 +178,11 @@ module Tabulo
|
|
171
178
|
# by the Table-level setting passed to the <tt>align_header</tt> (which itself defaults
|
172
179
|
# to <tt>:center</tt>). Otherwise, this option determines the alignment of the header
|
173
180
|
# content for this column.
|
181
|
+
# @param [Symbol, String, Integer, nil] before (nil) The label of the column before (i.e. to
|
182
|
+
# the left of) which the new column should inserted. If <tt>nil</tt> is passed, it will be
|
183
|
+
# inserted after all other columns. If there is no column with the given label, then an
|
184
|
+
# {InvalidColumnLabelError} will be raised. A non-Integer labelled column can be identified
|
185
|
+
# in either String or Symbol form for this purpose.
|
174
186
|
# @param [#to_proc] formatter (nil) A lambda or other callable object that
|
175
187
|
# will be passed the calculated value of each cell to determine how it should be displayed. This
|
176
188
|
# is distinct from the extractor (see below). For example, if the extractor for this column
|
@@ -222,6 +234,7 @@ module Tabulo
|
|
222
234
|
label,
|
223
235
|
align_body: nil,
|
224
236
|
align_header: nil,
|
237
|
+
before: nil,
|
225
238
|
formatter: nil,
|
226
239
|
header: nil,
|
227
240
|
header_styler: nil,
|
@@ -241,19 +254,43 @@ module Tabulo
|
|
241
254
|
raise InvalidColumnLabelError, "Column label already used in this table."
|
242
255
|
end
|
243
256
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
+
column = Column.new(
|
258
|
+
align_body: align_body || @align_body,
|
259
|
+
align_header: align_header || @align_header,
|
260
|
+
extractor: extractor || label.to_proc,
|
261
|
+
formatter: formatter || @formatter,
|
262
|
+
header: (header || label).to_s,
|
263
|
+
header_styler: header_styler || @header_styler,
|
264
|
+
padding_character: PADDING_CHARACTER,
|
265
|
+
styler: styler || @styler,
|
266
|
+
truncation_indicator: @truncation_indicator,
|
267
|
+
width: width || @column_width,
|
268
|
+
)
|
269
|
+
|
270
|
+
add_column_before(column, column_label, before)
|
271
|
+
end
|
272
|
+
|
273
|
+
# Removes the column identifed by the passed label.
|
274
|
+
#
|
275
|
+
# @example
|
276
|
+
# table = Table.new(1..10, :itself, :even?, :odd?)
|
277
|
+
# table.add_column(:even2, header: "even?") { |n| n.even? }
|
278
|
+
# table.remove_column(:even2)
|
279
|
+
# table.remove_column(:odd?)
|
280
|
+
#
|
281
|
+
# @param [Symbol, String, Integer] label The unique identifier for the column to be removed,
|
282
|
+
# corresponding to the label that was passed as the first parameter to {#add_column} (or was
|
283
|
+
# used in the table initializer) when the column was originally added. For columns that were
|
284
|
+
# originally added with a String or Symbol label, either a String or Symbol form of that label
|
285
|
+
# can be passed to {#remove_column}, indifferently. For example, if the label passed to
|
286
|
+
# {#add_column} had been `"height"`, then that column could be removed by passing either
|
287
|
+
# `"height"` or `:height` to {#remove_column}. (However, if an Integer was originally passed
|
288
|
+
# as the label to {#add_column}, then only that same Integer, as an Integer, can be passed to
|
289
|
+
# {#remove_column} to remove that column.)
|
290
|
+
# @return [true, false] If the label identifies a column in the table, then the column will be
|
291
|
+
# removed and true will be returned; otherwise no column will be removed, and false will be returned.
|
292
|
+
def remove_column(label)
|
293
|
+
!!column_registry.delete(Integer === label ? label : label.to_sym)
|
257
294
|
end
|
258
295
|
|
259
296
|
# @return [String] a graphical "ASCII" representation of the Table, suitable for
|
@@ -420,15 +457,15 @@ module Tabulo
|
|
420
457
|
# @return [Table] a new {Table}
|
421
458
|
# @raise [InvalidBorderError] if invalid argument passed to `border` parameter.
|
422
459
|
def transpose(opts = {})
|
423
|
-
default_opts = [:
|
424
|
-
:
|
425
|
-
:
|
460
|
+
default_opts = [:align_body, :align_header, :border, :border_styler, :column_padding, :column_width,
|
461
|
+
:formatter, :header_frequency, :row_divider_frequency, :truncation_indicator, :wrap_body_cells_to,
|
462
|
+
:wrap_header_cells_to].map do |sym|
|
426
463
|
[sym, instance_variable_get("@#{sym}")]
|
427
464
|
end.to_h
|
428
465
|
|
429
466
|
initializer_opts = default_opts.merge(Util.slice_hash(opts, *default_opts.keys))
|
430
|
-
default_extra_opts = {
|
431
|
-
|
467
|
+
default_extra_opts = { field_names_body_alignment: :right, field_names_header: "",
|
468
|
+
field_names_header_alignment: :right, field_names_width: nil, headers: :to_s.to_proc }
|
432
469
|
extra_opts = default_extra_opts.merge(Util.slice_hash(opts, *default_extra_opts.keys))
|
433
470
|
|
434
471
|
# The underlying enumerable for the new table, is the columns of the original table.
|
@@ -440,8 +477,9 @@ module Tabulo
|
|
440
477
|
width_opt = extra_opts[:field_names_width]
|
441
478
|
field_names_width = (width_opt.nil? ? fields.map { |f| f.header.length }.max : width_opt)
|
442
479
|
|
443
|
-
t.add_column(:dummy,
|
444
|
-
extra_opts[:field_names_header_alignment],
|
480
|
+
t.add_column(:dummy, align_body: extra_opts[:field_names_body_alignment],
|
481
|
+
align_header: extra_opts[:field_names_header_alignment], header: extra_opts[:field_names_header],
|
482
|
+
width: field_names_width, &:header)
|
445
483
|
|
446
484
|
# Add a column to the new table for each of the original table's sources
|
447
485
|
sources.each_with_index do |source, i|
|
@@ -476,6 +514,28 @@ module Tabulo
|
|
476
514
|
|
477
515
|
private
|
478
516
|
|
517
|
+
# @!visibility private
|
518
|
+
def add_column_before(column, label, before)
|
519
|
+
if before == nil
|
520
|
+
@column_registry[label] = column
|
521
|
+
else
|
522
|
+
old_column_entries = @column_registry.to_a
|
523
|
+
new_column_entries = []
|
524
|
+
found = false
|
525
|
+
old_column_entries.each do |entry|
|
526
|
+
if entry[0] == before
|
527
|
+
found = true
|
528
|
+
new_column_entries << [label, column]
|
529
|
+
end
|
530
|
+
new_column_entries << entry
|
531
|
+
end
|
532
|
+
unless found
|
533
|
+
raise InvalidColumnLabelError, "There is no column with label #{before}"
|
534
|
+
end
|
535
|
+
@column_registry = new_column_entries.to_h
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
479
539
|
# @!visibility private
|
480
540
|
def total_column_padding
|
481
541
|
@left_column_padding + @right_column_padding
|
data/lib/tabulo/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tabulo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Harvey
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-02-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tty-screen
|