tabulo 1.5.1 → 2.0.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/.travis.yml +3 -3
- data/CHANGELOG.md +34 -0
- data/README.md +159 -93
- data/VERSION +1 -1
- data/lib/tabulo.rb +1 -0
- data/lib/tabulo/border.rb +166 -0
- data/lib/tabulo/cell.rb +21 -7
- data/lib/tabulo/exceptions.rb +3 -12
- data/lib/tabulo/row.rb +6 -11
- data/lib/tabulo/table.rb +92 -125
- data/lib/tabulo/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02cf8fc58fb0164abdbf0fda2d80c4487c9be3bb17ecccfefc05bdf4bd381dd4
|
4
|
+
data.tar.gz: 826091614ae6734712d7320a6ab5c5fe169555646dd0f34e398cbc6fcbc414fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff79da1b58dd5cfe72214041b271a962ae43ad398cd00e4ac42b10af113128ade8e4a9026a09144f8d1dabffcb511c54740d4358d8ba57fcde060f81c7512835
|
7
|
+
data.tar.gz: f9b7dbabe3dbe44732b0e914a42b5be28ec5989183f4e576b7ece3227b36cfbd7508a659f78f804153c8de2512135496c5a9dd3f165144254841153f738328b8
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,39 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### v2.0.0
|
4
|
+
|
5
|
+
#### New features
|
6
|
+
|
7
|
+
* New `border` option for `Tabulo::Table` initializer allows for better customization of border and
|
8
|
+
divider characters, using a preset list of options, viz.: `:ascii`, `:modern`, `:markdown`,
|
9
|
+
`:blank` and `:classic`. In particular, the `:modern` border option uses smoothly drawn Unicode
|
10
|
+
line characters; and the `:markdown` option renders a GitHub-flavoured Markdown table.
|
11
|
+
* `Tabulo::Table#horizontal_rule` method accepts `:top`, `:bottom` and `:middle` options to allow
|
12
|
+
the appropriate border characters to be used depending on its intended position in the table.
|
13
|
+
* When iterating a `Tabulo::Row`, it's now possible to get the formatted string value of an individual
|
14
|
+
`Tabulo::Cell`, not just its underlying "raw" value.
|
15
|
+
* Column padding can now optionally be configured separately for left and right column sides, by
|
16
|
+
passing a 2-element Array to the `column_padding` option of the `Tabulo::Table` initializer.
|
17
|
+
|
18
|
+
#### Breaking changes
|
19
|
+
|
20
|
+
* A `Tabulo::Row` is now a collection of `Tabulo::Cell`, not a collection of underlying "raw"
|
21
|
+
values. This makes it easier to get at both formatted string values and underlying "raw" values of
|
22
|
+
`Cell`s when traversing a `Row`. To get at the raw underlying value, call `Tabulo::Cell#value`.
|
23
|
+
* Remove deprecated `columns` option from `Tabulo::Table` initializer
|
24
|
+
(existing `cols` positional parameter now renamed to `columns`).
|
25
|
+
* Remove deprecated `shrinkwrap!` method (use `pack` instead).
|
26
|
+
* By default, table now has a border line at the bottom. Pass `:classic` to the `border` option of
|
27
|
+
the `Tabulo::Table` initializer to get the old behaviour.
|
28
|
+
* Removal of `horizontal_rule_character`, `vertical_rule_character` and `intersection` character
|
29
|
+
options from `Tabulo::Table` initializer, and from `Tabulo::Table#transpose` method. Use the
|
30
|
+
`border` option instead.
|
31
|
+
|
32
|
+
#### Other noteworthy changes
|
33
|
+
|
34
|
+
* Test coverage is now at exactly 100%
|
35
|
+
* `hirb` gem now mentioned in README
|
36
|
+
|
3
37
|
### v1.5.1
|
4
38
|
|
5
39
|
* Dependency version upgrades
|
data/README.md
CHANGED
@@ -12,38 +12,17 @@ Tabulo is a terminal table generator for Ruby.
|
|
12
12
|
It offers a DRY, "column-centric" interface, and is designed to make it very easy to produce highly
|
13
13
|
readable tables, even from large and unwieldy data sets and streams.
|
14
14
|
|
15
|
-
|
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.
|
16
17
|
|
17
|
-
*Quick API:*
|
18
|
-
|
19
|
-
```
|
20
|
-
> puts Tabulo::Table.new(User.all, :id, :first_name, :last_name)
|
21
|
-
+--------------+--------------+--------------+
|
22
|
-
| id | first_name | last_name |
|
23
|
-
+--------------+--------------+--------------+
|
24
|
-
| 1 | John | Citizen |
|
25
|
-
| 2 | Jane | Doe |
|
26
18
|
```
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
t.add_column("N") { |n| n }
|
35
|
-
t.add_column("Doubled") { |n| n * 2 }
|
36
|
-
end
|
37
|
-
```
|
38
|
-
|
39
|
-
```
|
40
|
-
> puts table
|
41
|
-
+--------------+--------------+
|
42
|
-
| N | Doubled |
|
43
|
-
+--------------+--------------+
|
44
|
-
| 1 | 2 |
|
45
|
-
| 2 | 4 |
|
46
|
-
| 5000000 | 10000000 |
|
19
|
+
> puts Tabulo::Table.new(User.all, :id, :first_name, :last_name, border: :modern).pack
|
20
|
+
┌────┬────────────┬───────────┐
|
21
|
+
│ id │ first_name │ last_name │
|
22
|
+
├────┼────────────┼───────────┤
|
23
|
+
│ 1 │ John │ Citizen │
|
24
|
+
│ 2 │ Jane │ Doe │
|
25
|
+
└────┴────────────┴───────────┘
|
47
26
|
```
|
48
27
|
|
49
28
|
## Features
|
@@ -62,10 +41,10 @@ end
|
|
62
41
|
underlying cell values.
|
63
42
|
* The header row can be [repeated](#repeating-headers) at arbitrary intervals.
|
64
43
|
* Newlines within cell content are correctly handled.
|
65
|
-
* Multibyte characters are correctly handled.
|
66
|
-
* Apply [colours](#colours-and-styling) and other styling to table content without breaking the table.
|
44
|
+
* Multibyte Unicode characters are correctly handled.
|
45
|
+
* Apply [colours](#colours-and-styling) and other styling to table content and borders, without breaking the table.
|
67
46
|
* Easily [transpose](#transposition) the table, so that rows are swapped with columns.
|
68
|
-
* [
|
47
|
+
* Choose from multiple [border configurations](#borders), including Markdown, "ASCII", and smoothly joined Unicode border characters.
|
69
48
|
* Use a [DRY initialization interface](#configuring-columns): by being "column based", it is
|
70
49
|
designed to spare the developer the burden of syncing the ordering within the header row with that of the body rows.
|
71
50
|
|
@@ -93,8 +72,8 @@ Tabulo has also been ported to Crystal (with some modifications): see [Tablo](ht
|
|
93
72
|
* [Accessing cell values](#accessing-cell-values)
|
94
73
|
* [Accessing the underlying enumerable](#accessing-sources)
|
95
74
|
* [Transposing rows and columns](#transposition)
|
96
|
-
* [
|
97
|
-
* [
|
75
|
+
* [Border configuration](#borders)
|
76
|
+
* [Comparison with other libraries](#motivation)
|
98
77
|
* [Contributing](#contributing)
|
99
78
|
* [License](#license)
|
100
79
|
|
@@ -162,6 +141,7 @@ table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?)
|
|
162
141
|
| 1 | false | true |
|
163
142
|
| 2 | true | false |
|
164
143
|
| 5 | false | true |
|
144
|
+
+--------------+--------------+--------------+
|
165
145
|
```
|
166
146
|
|
167
147
|
Columns can also be initialized using a callable to which each object will be passed to determine
|
@@ -184,6 +164,7 @@ end
|
|
184
164
|
| 1 | 2 | true |
|
185
165
|
| 2 | 4 | false |
|
186
166
|
| 5 | 10 | true |
|
167
|
+
+--------------+--------------+--------------+
|
187
168
|
```
|
188
169
|
|
189
170
|
<a name="cell-alignment"></a>
|
@@ -229,6 +210,7 @@ end
|
|
229
210
|
+--------+-----------+
|
230
211
|
| 1 | false |
|
231
212
|
| 2 | true |
|
213
|
+
+--------+-----------+
|
232
214
|
```
|
233
215
|
|
234
216
|
If you want to set the default column width for all columns of the table to something other
|
@@ -245,6 +227,7 @@ table = Tabulo::Table.new([1, 2], :itself, :even?, column_width: 6)
|
|
245
227
|
+--------+--------+
|
246
228
|
| 1 | false |
|
247
229
|
| 2 | true |
|
230
|
+
+--------+--------+
|
248
231
|
```
|
249
232
|
|
250
233
|
Widths set for individual columns always override the default column width for the table.
|
@@ -253,8 +236,44 @@ Widths set for individual columns always override the default column width for t
|
|
253
236
|
#### Configuring padding
|
254
237
|
|
255
238
|
The single character of padding either side of each column is not counted in the column width.
|
256
|
-
The amount of this padding can be configured for the table as a whole, using the `column_padding`
|
257
|
-
option passed to `Table.new`.
|
239
|
+
The amount of this extra padding can be configured for the table as a whole, using the `column_padding`
|
240
|
+
option passed to `Table.new`—the default value of this option being `1`.
|
241
|
+
|
242
|
+
Passing a single integer to this option causes the given amount of padding to be applied to each
|
243
|
+
side of each column. For example:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?, column_padding: 0)
|
247
|
+
```
|
248
|
+
|
249
|
+
```
|
250
|
+
> puts table
|
251
|
+
+------------+------------+------------+
|
252
|
+
| itself | even? | odd? |
|
253
|
+
+------------+------------+------------+
|
254
|
+
| 1| false | true |
|
255
|
+
| 2| true | false |
|
256
|
+
| 5| false | true |
|
257
|
+
+------------+------------+------------+
|
258
|
+
```
|
259
|
+
|
260
|
+
Passing an array of _two_ integers to this option configures the left and right padding for each
|
261
|
+
column, according to the first and second element of the array, respectively. For example:
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?, column_padding: [0, 2])
|
265
|
+
```
|
266
|
+
|
267
|
+
```
|
268
|
+
> puts table
|
269
|
+
+--------------+--------------+--------------+
|
270
|
+
| itself | even? | odd? |
|
271
|
+
+--------------+--------------+--------------+
|
272
|
+
| 1 | false | true |
|
273
|
+
| 2 | true | false |
|
274
|
+
| 5 | false | true |
|
275
|
+
+--------------+--------------+--------------+
|
276
|
+
```
|
258
277
|
|
259
278
|
<a name="pack"></a>
|
260
279
|
#### Automating column widths
|
@@ -275,6 +294,7 @@ table.pack
|
|
275
294
|
+--------+-------+
|
276
295
|
| 1 | false |
|
277
296
|
| 2 | true |
|
297
|
+
+--------+-------+
|
278
298
|
```
|
279
299
|
|
280
300
|
The `pack` method returns the table itself, so you can "pack-and-print" in one go:
|
@@ -297,6 +317,7 @@ puts Tabulo::Table.new([1, 2], :itself, :even?).pack(max_table_width: 17)
|
|
297
317
|
+-------+-------+
|
298
318
|
| 1 | false |
|
299
319
|
| 2 | true |
|
320
|
+
+-------+-------+
|
300
321
|
```
|
301
322
|
|
302
323
|
Or if you simply call `pack` with no arguments (or if you explicitly call
|
@@ -342,6 +363,7 @@ table = Tabulo::Table.new(
|
|
342
363
|
| abcdefghijkl | 26 |
|
343
364
|
| mnopqrstuvwx | |
|
344
365
|
| yz | |
|
366
|
+
+--------------+--------------+
|
345
367
|
```
|
346
368
|
|
347
369
|
Wrapping behaviour is configured for the table as a whole using the `wrap_header_cells_to` option
|
@@ -365,6 +387,7 @@ table = Tabulo::Table.new(
|
|
365
387
|
+--------------+--------------+
|
366
388
|
| hello | 5 |
|
367
389
|
| abcdefghijkl~| 26 |
|
390
|
+
+--------------+--------------+
|
368
391
|
```
|
369
392
|
|
370
393
|
<a name="formatting-cell-values"></a>
|
@@ -393,6 +416,7 @@ end
|
|
393
416
|
| 1 | 1.00 |
|
394
417
|
| 2 | 0.50 |
|
395
418
|
| 3 | 0.33 |
|
419
|
+
+--------------+--------------+
|
396
420
|
```
|
397
421
|
|
398
422
|
Note the numbers in the "Reciprocal" column in this example are still right-aligned, even though
|
@@ -493,6 +517,7 @@ table = Tabulo::Table.new(1..10, :itself, :even?, header_frequency: 5)
|
|
493
517
|
| 8 | true |
|
494
518
|
| 9 | false |
|
495
519
|
| 10 | true |
|
520
|
+
+--------------+--------------+
|
496
521
|
```
|
497
522
|
|
498
523
|
<a name="enumerator"></a>
|
@@ -526,16 +551,21 @@ calculated.)
|
|
526
551
|
|
527
552
|
<a name="accessing-cell-values"></a>
|
528
553
|
### Accessing cell values
|
554
|
+
|
529
555
|
Each `Tabulo::Table` is an `Enumerable` of which each element is a `Tabulo::Row`. Each `Tabulo::Row`
|
530
|
-
is itself an `Enumerable
|
556
|
+
is itself an `Enumerable`, of `Tabulo::Cell`. The `Tabulo::Cell#value` method will return the
|
557
|
+
underlying value of the cell; while `Tabulo::Cell#formatted_value` will return its formatted content
|
558
|
+
as a string.
|
559
|
+
|
560
|
+
A `Tabulo::Row` can also
|
531
561
|
be converted to a `Hash` for keyed access. For example:
|
532
562
|
|
533
563
|
```ruby
|
534
564
|
table = Tabulo::Table.new(1..5, :itself, :even?, :odd?)
|
535
565
|
|
536
566
|
table.each do |row|
|
537
|
-
row.each { |cell| puts cell } # 1...2...3...4...5
|
538
|
-
puts row.to_h[:even?] # false...true...false...true...false
|
567
|
+
row.each { |cell| puts cell.value } # 1...2...3...4...5
|
568
|
+
puts row.to_h[:even?].value # false...true...false...true...false
|
539
569
|
end
|
540
570
|
```
|
541
571
|
|
@@ -553,8 +583,8 @@ end
|
|
553
583
|
|
554
584
|
table.each do |row|
|
555
585
|
cells = row.to_h
|
556
|
-
puts cells[:Number] # 1...2...3...4...5
|
557
|
-
puts cells[:doubled] # 2...4...6...8...10
|
586
|
+
puts cells[:Number].value # 1...2...3...4...5
|
587
|
+
puts cells[:doubled].value # 2...4...6...8...10
|
558
588
|
end
|
559
589
|
```
|
560
590
|
|
@@ -616,59 +646,95 @@ a new table in which the rows and columns are swapped:
|
|
616
646
|
| pred | -2 | -1 | 0 |
|
617
647
|
| succ | 0 | 1 | 2 |
|
618
648
|
| abs | 1 | 0 | 1 |
|
649
|
+
+-------+--------------+--------------+--------------+
|
619
650
|
```
|
620
651
|
|
621
652
|
By default, a header row is added to the new table, showing the string value of the element
|
622
653
|
represented in that column. This can be configured, however, along with other aspects of
|
623
654
|
`transpose`'s behaviour. For details, see the
|
624
|
-
[documentation](https://www.rubydoc.info/gems/tabulo/
|
655
|
+
[documentation](https://www.rubydoc.info/gems/tabulo/2.0.0/Tabulo/Table#transpose-instance_method).
|
625
656
|
|
626
|
-
<a name="
|
627
|
-
###
|
657
|
+
<a name="borders"></a>
|
658
|
+
### Configuring borders
|
628
659
|
|
629
|
-
|
630
|
-
|
631
|
-
`vertical_rule_character` and `intersection_character` options passed to `Table.new`.
|
660
|
+
You can configure the kind of border and divider characters that are used when the table is printed.
|
661
|
+
This is done using the `border` option passed to `Table.new`. The options are as follows.
|
632
662
|
|
633
|
-
|
634
|
-
`truncation_indicator` option passed to `Table.new`.
|
663
|
+
`:ascii`—this is the default; the table is drawn entirely with characters in the ASCII set:
|
635
664
|
|
636
|
-
|
665
|
+
```
|
666
|
+
> puts Tabulo::Table.new(1...3, :itself, :even?, :odd?, border: :ascii)
|
667
|
+
+--------------+--------------+--------------+
|
668
|
+
| itself | even? | odd? |
|
669
|
+
+--------------+--------------+--------------+
|
670
|
+
| 1 | false | true |
|
671
|
+
| 2 | true | false |
|
672
|
+
| 3 | false | true |
|
673
|
+
+--------------+--------------+--------------+
|
674
|
+
```
|
637
675
|
|
638
|
-
|
639
|
-
|
640
|
-
|
676
|
+
`:modern`—uses smoothly joined Unicode characters:
|
677
|
+
|
678
|
+
```
|
679
|
+
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :modern)
|
680
|
+
┌──────────────┬──────────────┬──────────────┐
|
681
|
+
│ itself │ even? │ odd? │
|
682
|
+
├──────────────┼──────────────┼──────────────┤
|
683
|
+
│ 1 │ false │ true │
|
684
|
+
│ 2 │ true │ false │
|
685
|
+
│ 3 │ false │ true │
|
686
|
+
└──────────────┴──────────────┴──────────────┘
|
641
687
|
```
|
642
688
|
|
643
|
-
|
689
|
+
`:markdown`—renders a GitHub flavoured Markdown table:
|
644
690
|
|
645
|
-
|
691
|
+
```
|
692
|
+
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :markdown)
|
693
|
+
| itself | even? | odd? |
|
694
|
+
|--------------|--------------|--------------|
|
695
|
+
| 1 | false | true |
|
696
|
+
| 2 | true | false |
|
697
|
+
| 3 | false | true |
|
698
|
+
```
|
646
699
|
|
647
|
-
|
648
|
-
|
700
|
+
`:blank`—no border or divider characters are printed:
|
701
|
+
|
702
|
+
```
|
703
|
+
> puts Tabulo::Table.new(1...3, :itself, :even?, :odd?, border: :blank)
|
704
|
+
itself even? odd?
|
705
|
+
1 false true
|
706
|
+
2 true false
|
707
|
+
3 false true
|
649
708
|
```
|
650
709
|
|
710
|
+
`:classic`—reproduces the default behaviour in Tabulo v1; this is like the `:ascii` option,
|
711
|
+
but without a bottom border:
|
712
|
+
|
651
713
|
```
|
652
|
-
>
|
653
|
-
|
654
|
-
| itself | even? |
|
655
|
-
|
656
|
-
| 1 | false |
|
657
|
-
|
658
|
-
|
|
659
|
-
+--------------+--------------+
|
660
|
-
| 3 | false |
|
661
|
-
+--------------+--------------+
|
714
|
+
> puts Tabulo::Table.new(1...3, :itself, :even?, :odd?, border: :classic)
|
715
|
+
+--------------+--------------+--------------+
|
716
|
+
| itself | even? | odd? |
|
717
|
+
+--------------+--------------+--------------+
|
718
|
+
| 1 | false | true |
|
719
|
+
| 2 | true | false |
|
720
|
+
| 3 | false | true |
|
662
721
|
```
|
663
722
|
|
723
|
+
<a name="additional-configuration-options"></a>
|
724
|
+
### Other configuration options
|
725
|
+
|
726
|
+
The character used to indicate truncation, which defaults to `~`, can be configured using the
|
727
|
+
`truncation_indicator` option passed to `Table.new`.
|
728
|
+
|
664
729
|
<a name="motivation"></a>
|
665
|
-
##
|
730
|
+
## Comparison with other libraries
|
666
731
|
|
667
|
-
There are other
|
732
|
+
There are other libraries for generating plain text tables in Ruby. Popular among these are:
|
668
733
|
|
669
734
|
* [terminal-table](https://github.com/tj/terminal-table)
|
670
735
|
* [tty-table](https://github.com/piotrmurach/tty-table)
|
671
736
|
* [table\_print](https://github.com/arches/table_print)
|
737
|
+
* [hirb](https://github.com/cldwalker/hirb)
|
672
738
|
|
673
739
|
*DISCLAIMER: My comments regarding these other libraries are based only on my own, possibly flawed
|
674
740
|
reading of the documentation for, and experimentation with, these libraries at the time of my
|
@@ -676,15 +742,15 @@ writing this. Their APIs, features or documentation may well change between when
|
|
676
742
|
when you read it. Please consult the libraries' own documentation for yourself, rather than relying
|
677
743
|
on these comments.*
|
678
744
|
|
679
|
-
While these libraries have their strengths, I personally found that for the common use case of
|
745
|
+
While these libraries have their strengths, I have personally found that, for the common use case of
|
680
746
|
printing a table on the basis of some underlying enumerable collection (such as an ActiveRecord
|
681
|
-
query result), using these libraries
|
747
|
+
query result), using these libraries feels more cumbersome than it could be.
|
682
748
|
|
683
749
|
For example, suppose we have called `User.all` from the Rails console, and want to print
|
684
750
|
a table showing the email, first name, last name and ID of each user,
|
685
751
|
with column headings. Also, we want the ID column to be right-aligned, because it's a number.
|
686
752
|
|
687
|
-
In
|
753
|
+
In **terminal-table**, we could achieve this as follows:
|
688
754
|
|
689
755
|
```ruby
|
690
756
|
rows = User.all.map { |u| [u.email, u.first_name, u.last_name, u.id] }
|
@@ -702,7 +768,7 @@ tables, but for tables that have many columns, or that are constructed
|
|
702
768
|
dynamically based on user input or other runtime factors determining the columns
|
703
769
|
to be included, this can be a hassle and a source of brittleness.
|
704
770
|
|
705
|
-
|
771
|
+
**tty-table** has a somewhat different API to `terminal-table`. It offers both a
|
706
772
|
"row-based" and a "column-based" method of initializing a table. The row-based method
|
707
773
|
is similar to `terminal-table`'s in that it burdens the developer with syncing the
|
708
774
|
column ordering across multiple code locations. The "column-based" API for `tty-table`, on
|
@@ -729,20 +795,20 @@ this, but if there is, it doesn't seem to be documented.) So it seems we still h
|
|
729
795
|
`table.align_column(3, :right)`, which again burdens us with keeping the column index
|
730
796
|
passed to `align_column` in sync.
|
731
797
|
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
798
|
+
As for **table\_print**, this is a handy gem for quickly tabulating ActiveRecord
|
799
|
+
collections from the Rails console. `table_print` is similar to `tabulo` in that it has a
|
800
|
+
column-based API, so it doesn't suffer from the multiple-source-of-knowledge issue in regards to
|
801
|
+
column orderings. However, it lacks certain other useful features, such as the ability to repeat
|
802
|
+
headers every N rows, the automatic alignment of columns based on cell content (numbers right,
|
803
|
+
strings left), and a quick and easy way to automatically resize columns to accommodate cell content
|
804
|
+
without overflowing the terminal. Also, as of the time of writing, `table_print`'s last significant
|
805
|
+
commit (ignoring a deprecation warning fix in April 2018) was in March 2016.
|
806
|
+
|
807
|
+
Finally, it is worth mentioning the **hirb** library. This is similar to `table_print`, in that it's
|
808
|
+
well suited to quickly displaying ActiveRecord collections from the Rails console. However, like
|
809
|
+
`table_print`, there are certain useful features it's lacking; and using it outside the console
|
810
|
+
environment seems cumbersome. Moreover, it seems no longer to be maintained. At the time of writing,
|
811
|
+
its last commit was in March 2015.
|
746
812
|
|
747
813
|
<a name="contributing"></a>
|
748
814
|
## Contributing
|
@@ -752,9 +818,9 @@ Issues and pull requests are welcome on GitHub at https://github.com/matt-harvey
|
|
752
818
|
To start working on Tabulo, `git clone` and `cd` into your fork of the repo, then run `bin/setup` to
|
753
819
|
install dependencies.
|
754
820
|
|
755
|
-
`bin/console` will give you an interactive prompt that will allow you to experiment; and
|
756
|
-
will run the test suite. For a list of other Rake tasks that are available in
|
757
|
-
environment, run `rake -T`.
|
821
|
+
`bin/console` will give you an interactive prompt that will allow you to experiment; and
|
822
|
+
`bundle exec rake spec` will run the test suite. For a list of other Rake tasks that are available in
|
823
|
+
the development environment, run `bundle exec rake -T`.
|
758
824
|
|
759
825
|
## License
|
760
826
|
|
@@ -762,14 +828,14 @@ The gem is available as open source under the terms of the [MIT
|
|
762
828
|
License](http://opensource.org/licenses/MIT).
|
763
829
|
|
764
830
|
[Gem Version]: https://rubygems.org/gems/tabulo
|
765
|
-
[Documentation]: http://www.rubydoc.info/gems/tabulo/
|
831
|
+
[Documentation]: http://www.rubydoc.info/gems/tabulo/2.0.0
|
766
832
|
[Build Status]: https://travis-ci.org/matt-harvey/tabulo
|
767
833
|
[Coverage Status]: https://coveralls.io/r/matt-harvey/tabulo
|
768
834
|
[Code Climate]: https://codeclimate.com/github/matt-harvey/tabulo
|
769
835
|
[Awesome Ruby]: https://github.com/markets/awesome-ruby#cli-utilities
|
770
836
|
|
771
837
|
[GV img]: https://img.shields.io/gem/v/tabulo.svg
|
772
|
-
[DC img]: https://img.shields.io/badge/documentation-
|
838
|
+
[DC img]: https://img.shields.io/badge/documentation-v2.0.0-blue.svg
|
773
839
|
[BS img]: https://img.shields.io/travis/matt-harvey/tabulo.svg
|
774
840
|
[CS img]: https://img.shields.io/coveralls/matt-harvey/tabulo.svg
|
775
841
|
[CC img]: https://codeclimate.com/github/matt-harvey/tabulo/badges/gpa.svg
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.0.0
|
data/lib/tabulo.rb
CHANGED
@@ -0,0 +1,166 @@
|
|
1
|
+
module Tabulo
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
class Border
|
5
|
+
|
6
|
+
STYLES = {
|
7
|
+
ascii: {
|
8
|
+
corner_top_left: "+",
|
9
|
+
corner_top_right: "+",
|
10
|
+
corner_bottom_right: "+",
|
11
|
+
corner_bottom_left: "+",
|
12
|
+
edge_top: "-",
|
13
|
+
edge_right: "|",
|
14
|
+
edge_bottom: "-",
|
15
|
+
edge_left: "|",
|
16
|
+
tee_top: "+",
|
17
|
+
tee_right: "+",
|
18
|
+
tee_bottom: "+",
|
19
|
+
tee_left: "+",
|
20
|
+
divider_vertical: "|",
|
21
|
+
divider_horizontal: "-",
|
22
|
+
intersection: "+",
|
23
|
+
},
|
24
|
+
classic: {
|
25
|
+
corner_top_left: "+",
|
26
|
+
corner_top_right: "+",
|
27
|
+
edge_top: "-",
|
28
|
+
edge_right: "|",
|
29
|
+
edge_left: "|",
|
30
|
+
tee_top: "+",
|
31
|
+
tee_right: "+",
|
32
|
+
tee_left: "+",
|
33
|
+
divider_vertical: "|",
|
34
|
+
divider_horizontal: "-",
|
35
|
+
intersection: "+",
|
36
|
+
},
|
37
|
+
markdown: {
|
38
|
+
corner_top_left: "",
|
39
|
+
corner_top_right: "",
|
40
|
+
corner_bottom_right: "",
|
41
|
+
corner_bottom_left: "",
|
42
|
+
edge_top: "",
|
43
|
+
edge_right: "|",
|
44
|
+
edge_bottom: "",
|
45
|
+
edge_left: "|",
|
46
|
+
tee_top: "",
|
47
|
+
tee_right: "|",
|
48
|
+
tee_bottom: "",
|
49
|
+
tee_left: "|",
|
50
|
+
divider_vertical: "|",
|
51
|
+
divider_horizontal: "-",
|
52
|
+
intersection: "|",
|
53
|
+
},
|
54
|
+
modern: {
|
55
|
+
corner_top_left: "┌",
|
56
|
+
corner_top_right: "┐",
|
57
|
+
corner_bottom_right: "┘",
|
58
|
+
corner_bottom_left: "└",
|
59
|
+
edge_top: "─",
|
60
|
+
edge_right: "│",
|
61
|
+
edge_bottom: "─",
|
62
|
+
edge_left: "│",
|
63
|
+
tee_top: "┬",
|
64
|
+
tee_right: "┤",
|
65
|
+
tee_bottom: "┴",
|
66
|
+
tee_left: "├",
|
67
|
+
divider_vertical: "│",
|
68
|
+
divider_horizontal: "─",
|
69
|
+
intersection: "┼",
|
70
|
+
},
|
71
|
+
blank: {
|
72
|
+
},
|
73
|
+
}
|
74
|
+
|
75
|
+
# @!visibility private
|
76
|
+
def self.from(initializer, styler = nil)
|
77
|
+
new(options(initializer).merge(styler: styler))
|
78
|
+
end
|
79
|
+
|
80
|
+
# @!visibility private
|
81
|
+
def horizontal_rule(column_widths, position = :bottom)
|
82
|
+
left, center, right, segment =
|
83
|
+
case position
|
84
|
+
when :top
|
85
|
+
[@corner_top_left, @tee_top, @corner_top_right, @edge_top]
|
86
|
+
when :middle
|
87
|
+
[@tee_left, @intersection, @tee_right, @divider_horizontal]
|
88
|
+
when :bottom
|
89
|
+
[@corner_bottom_left, @tee_bottom, @corner_bottom_right, @edge_bottom]
|
90
|
+
end
|
91
|
+
segments = column_widths.map { |width| segment * width }
|
92
|
+
style("#{left}#{segments.join(center)}#{right}")
|
93
|
+
end
|
94
|
+
|
95
|
+
# @!visibility private
|
96
|
+
def join_cell_contents(cells)
|
97
|
+
styled_divider_vertical = style(@divider_vertical)
|
98
|
+
styled_edge_left = style(@edge_left)
|
99
|
+
styled_edge_right = style(@edge_right)
|
100
|
+
styled_edge_left + cells.join(styled_divider_vertical) + styled_edge_right
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def self.options(kind)
|
106
|
+
opts = STYLES[kind]
|
107
|
+
return opts if opts
|
108
|
+
raise InvalidBorderError
|
109
|
+
end
|
110
|
+
|
111
|
+
# @param [nil, #to_proc] styler (nil) A lambda or other callable object taking
|
112
|
+
# a single parameter, representing a section of the table's borders (which for this purpose
|
113
|
+
# include any horizontal and vertical lines inside the table), and returning a string.
|
114
|
+
# If passed <tt>nil</tt>, then no additional styling will be applied to borders. If passed a
|
115
|
+
# callable, then that callable will be called for each border section, with the
|
116
|
+
# resulting string rendered in place of that border. The extra width of the string returned by the
|
117
|
+
# {styler} is not taken into consideration by the internal table rendering calculations
|
118
|
+
# Thus it can be used to apply ANSI escape codes to border characters, to colour the borders
|
119
|
+
# for example, without breaking the table formatting.
|
120
|
+
# @return [Border] a new {Border}
|
121
|
+
def initialize(
|
122
|
+
corner_top_left: "",
|
123
|
+
corner_top_right: "",
|
124
|
+
corner_bottom_right: "",
|
125
|
+
corner_bottom_left: "",
|
126
|
+
edge_top: "",
|
127
|
+
edge_right: "",
|
128
|
+
edge_bottom: "",
|
129
|
+
edge_left: "",
|
130
|
+
tee_top: "",
|
131
|
+
tee_right: "",
|
132
|
+
tee_bottom: "",
|
133
|
+
tee_left: "",
|
134
|
+
divider_vertical: "",
|
135
|
+
divider_horizontal: "",
|
136
|
+
intersection: "",
|
137
|
+
styler: nil)
|
138
|
+
|
139
|
+
@corner_top_left = corner_top_left
|
140
|
+
@corner_top_right = corner_top_right
|
141
|
+
@corner_bottom_right = corner_bottom_right
|
142
|
+
@corner_bottom_left = corner_bottom_left
|
143
|
+
|
144
|
+
@edge_top = edge_top
|
145
|
+
@edge_right = edge_right
|
146
|
+
@edge_bottom = edge_bottom
|
147
|
+
@edge_left = edge_left
|
148
|
+
|
149
|
+
@tee_top = tee_top
|
150
|
+
@tee_right = tee_right
|
151
|
+
@tee_bottom = tee_bottom
|
152
|
+
@tee_left = tee_left
|
153
|
+
|
154
|
+
@divider_vertical = divider_vertical
|
155
|
+
@divider_horizontal = divider_horizontal
|
156
|
+
|
157
|
+
@intersection = intersection
|
158
|
+
|
159
|
+
@styler = styler
|
160
|
+
end
|
161
|
+
|
162
|
+
def style(s)
|
163
|
+
@styler ? @styler.call(s) : s
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
data/lib/tabulo/cell.rb
CHANGED
@@ -2,9 +2,13 @@ require "unicode/display_width"
|
|
2
2
|
|
3
3
|
module Tabulo
|
4
4
|
|
5
|
-
#
|
5
|
+
# Represents a single cell within the body of a {Table}.
|
6
6
|
class Cell
|
7
7
|
|
8
|
+
# @return the underlying value for this Cell
|
9
|
+
attr_reader :value
|
10
|
+
|
11
|
+
# @!visibility private
|
8
12
|
def initialize(value:, formatter:, alignment:, width:, styler:, truncation_indicator:, padding_character:)
|
9
13
|
@value = value
|
10
14
|
@formatter = formatter
|
@@ -15,18 +19,23 @@ module Tabulo
|
|
15
19
|
@padding_character = padding_character
|
16
20
|
end
|
17
21
|
|
22
|
+
# @!visibility private
|
18
23
|
def height
|
19
24
|
subcells.size
|
20
25
|
end
|
21
26
|
|
22
|
-
|
27
|
+
# @!visibility private
|
28
|
+
def padded_truncated_subcells(target_height, padding_amount_left, padding_amount_right)
|
29
|
+
total_padding_amount = padding_amount_left + padding_amount_right
|
23
30
|
truncated = (height > target_height)
|
24
31
|
(0...target_height).map do |subcell_index|
|
25
|
-
append_truncator = (truncated && (
|
26
|
-
padded_subcell(subcell_index,
|
32
|
+
append_truncator = (truncated && (total_padding_amount != 0) && (subcell_index + 1 == target_height))
|
33
|
+
padded_subcell(subcell_index, padding_amount_left, padding_amount_right, append_truncator)
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
37
|
+
# @return [String] the content of the Cell, after applying the formatter for this Column (but
|
38
|
+
# without applying any wrapping or the styler).
|
30
39
|
def formatted_content
|
31
40
|
@formatted_content ||= @formatter.call(@value)
|
32
41
|
end
|
@@ -37,9 +46,14 @@ module Tabulo
|
|
37
46
|
@subcells ||= calculate_subcells
|
38
47
|
end
|
39
48
|
|
40
|
-
def padded_subcell(subcell_index,
|
41
|
-
lpad = @padding_character *
|
42
|
-
rpad =
|
49
|
+
def padded_subcell(subcell_index, padding_amount_left, padding_amount_right, append_truncator)
|
50
|
+
lpad = @padding_character * padding_amount_left
|
51
|
+
rpad =
|
52
|
+
if append_truncator
|
53
|
+
styled_truncation_indicator + padding(padding_amount_right - 1)
|
54
|
+
else
|
55
|
+
padding(padding_amount_right)
|
56
|
+
end
|
43
57
|
inner = subcell_index < height ? subcells[subcell_index] : padding(@width)
|
44
58
|
"#{lpad}#{inner}#{rpad}"
|
45
59
|
end
|
data/lib/tabulo/exceptions.rb
CHANGED
@@ -3,19 +3,10 @@ module Tabulo
|
|
3
3
|
# Error indicating that the label of a column is invalid.
|
4
4
|
class InvalidColumnLabelError < StandardError; end
|
5
5
|
|
6
|
-
# Error indicating that an attempt was made to use an invalid horizontal rule character
|
7
|
-
# for the table.
|
8
|
-
class InvalidHorizontalRuleCharacterError < StandardError; end
|
9
|
-
|
10
|
-
# Error indicating that an attempt was made to use an invalid vertical rule character
|
11
|
-
# for the table.
|
12
|
-
class InvalidVerticalRuleCharacterError < StandardError; end
|
13
|
-
|
14
|
-
# Error indicating that an attempt was made to use an invalid intersection character for
|
15
|
-
# the table.
|
16
|
-
class InvalidIntersectionCharacterError < StandardError; end
|
17
|
-
|
18
6
|
# Error indicating that an attempt was made to use an invalid truncation indicator for
|
19
7
|
# the table.
|
20
8
|
class InvalidTruncationIndicatorError < StandardError; end
|
9
|
+
|
10
|
+
# Error indicating the table border configuration is invalid.
|
11
|
+
class InvalidBorderError < StandardError; end
|
21
12
|
end
|
data/lib/tabulo/row.rb
CHANGED
@@ -7,10 +7,10 @@ module Tabulo
|
|
7
7
|
attr_reader :source
|
8
8
|
|
9
9
|
# @!visibility private
|
10
|
-
def initialize(table, source,
|
10
|
+
def initialize(table, source, header: :top)
|
11
11
|
@table = table
|
12
12
|
@source = source
|
13
|
-
@
|
13
|
+
@header = header
|
14
14
|
end
|
15
15
|
|
16
16
|
# Calls the given block once for each cell in the {Row}, passing that cell as parameter.
|
@@ -25,7 +25,7 @@ module Tabulo
|
|
25
25
|
# end
|
26
26
|
def each
|
27
27
|
@table.column_registry.each do |_, column|
|
28
|
-
yield column.
|
28
|
+
yield column.body_cell(@source)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
@@ -34,20 +34,15 @@ module Tabulo
|
|
34
34
|
# in the {Table} and how the {Table} was configured with respect to header frequency).
|
35
35
|
def to_s
|
36
36
|
if @table.column_registry.any?
|
37
|
-
@table.formatted_body_row(@source,
|
37
|
+
@table.formatted_body_row(@source, header: @header)
|
38
38
|
else
|
39
39
|
""
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
# @return a Hash representation of the {Row}, with column labels acting
|
44
|
-
# as keys and the calculated cell values (before formatting) providing the values.
|
45
|
-
# @example
|
46
|
-
# table = Tabulo::Table.new([1, 10], columns: %i[itself even?])
|
47
|
-
# row = table.first
|
48
|
-
# row.to_h # => { :itself => 1, :even? => false }
|
43
|
+
# @return a Hash representation of the {Row}, with column labels acting as keys and the {Cell}s the values.
|
49
44
|
def to_h
|
50
|
-
@table.column_registry.map { |label, column| [label, column.
|
45
|
+
@table.column_registry.map { |label, column| [label, column.body_cell(@source)] }.to_h
|
51
46
|
end
|
52
47
|
end
|
53
48
|
end
|
data/lib/tabulo/table.rb
CHANGED
@@ -10,19 +10,13 @@ module Tabulo
|
|
10
10
|
include Enumerable
|
11
11
|
|
12
12
|
# @!visibility public
|
13
|
-
|
14
|
-
|
15
|
-
# @!visibility public
|
16
|
-
DEFAULT_COLUMN_PADDING = 1
|
13
|
+
DEFAULT_BORDER = :ascii
|
17
14
|
|
18
15
|
# @!visibility public
|
19
|
-
|
20
|
-
|
21
|
-
# @!visibility public
|
22
|
-
DEFAULT_VERTICAL_RULE_CHARACTER = "|"
|
16
|
+
DEFAULT_COLUMN_WIDTH = 12
|
23
17
|
|
24
18
|
# @!visibility public
|
25
|
-
|
19
|
+
DEFAULT_COLUMN_PADDING = 1
|
26
20
|
|
27
21
|
# @!visibility public
|
28
22
|
DEFAULT_TRUNCATION_INDICATOR = "~"
|
@@ -37,11 +31,10 @@ module Tabulo
|
|
37
31
|
attr_accessor :sources
|
38
32
|
|
39
33
|
# @param [Enumerable] sources the underlying Enumerable from which the table will derive its data
|
40
|
-
# @param [Array[Symbol]]
|
34
|
+
# @param [Array[Symbol]] columns Specifies the initial columns. The Symbols provided must
|
41
35
|
# be unique. Each element of the Array will be used to create a column whose content is
|
42
36
|
# created by calling the corresponding method on each element of sources. Note
|
43
37
|
# the {#add_column} method is a much more flexible way to set up columns on the table.
|
44
|
-
# @param [Array[Symbol]] columns <b>DEPRECATED</b> Use <tt>cols</tt> instead.
|
45
38
|
# @param [Integer, nil] column_width The default column width for columns in this
|
46
39
|
# table, not excluding padding. If <tt>nil</tt>, then {DEFAULT_COLUMN_WIDTH} will be used.
|
47
40
|
# @param [:start, nil, Integer] header_frequency Controls the display of column headers.
|
@@ -59,24 +52,15 @@ module Tabulo
|
|
59
52
|
# headers), if their content is longer than the column's fixed width. If passed <tt>nil</tt>, content will
|
60
53
|
# be wrapped for as many rows as required to accommodate it. If passed an Integer N (> 0), content will be
|
61
54
|
# wrapped up to N rows and then truncated thereafter.
|
62
|
-
# @param [nil, String] horizontal_rule_character Determines the character used to draw
|
63
|
-
# horizontal lines where required in the table. If omitted or passed <tt>nil</tt>, defaults to
|
64
|
-
# {DEFAULT_HORIZONTAL_RULE_CHARACTER}. If passed something other than <tt>nil</tt> or a single-character
|
65
|
-
# String, raises {InvalidHorizontalRuleCharacterError}.
|
66
|
-
# @param [nil, String] vertical_rule_character Determines the character used to draw
|
67
|
-
# vertical lines where required in the table. If omitted or passed <tt>nil</tt>, defaults to
|
68
|
-
# {DEFAULT_VERTICAL_RULE_CHARACTER}. If passed something other than <tt>nil</tt> or a single-character
|
69
|
-
# String, raises {InvalidVerticalRuleCharacterError}.
|
70
|
-
# @param [nil, String] intersection_character Determines the character used to draw
|
71
|
-
# line intersections and corners where required in the table. If omitted or passed <tt>nil</tt>,
|
72
|
-
# defaults to {DEFAULT_INTERSECTION_CHARACTER}. If passed something other than <tt>nil</tt> or
|
73
|
-
# a single-character String, raises {InvalidIntersectionCharacterError}.
|
74
55
|
# @param [nil, String] truncation_indicator Determines the character used to indicate that a
|
75
56
|
# cell's content has been truncated. If omitted or passed <tt>nil</tt>,
|
76
57
|
# defaults to {DEFAULT_TRUNCATION_INDICATOR}. If passed something other than <tt>nil</tt> or
|
77
58
|
# a single-character String, raises {InvalidTruncationIndicatorError}.
|
78
|
-
# @param [nil, Integer] column_padding Determines the amount of blank space with which to pad
|
79
|
-
# of each column.
|
59
|
+
# @param [nil, Integer, Array] column_padding (1) Determines the amount of blank space with which to pad
|
60
|
+
# either side of each column. If passed an Integer, then the given amount of padding is
|
61
|
+
# applied to each side of each column. If passed a two-element Array, then the first element of the
|
62
|
+
# Array indicates the amount of padding to apply to the left of each column, and the second
|
63
|
+
# element indicates the amount to apply to the right.
|
80
64
|
# @param [:left, :right, :center] align_header (:center) Determines the alignment of header text
|
81
65
|
# for columns in this Table. Can be overridden for individual columns using the
|
82
66
|
# <tt>align_header</tt> option passed to {#add_column}
|
@@ -85,9 +69,20 @@ module Tabulo
|
|
85
69
|
# using the <tt>align_body</tt> option passed to {#add_column}. If passed <tt>:auto</tt>,
|
86
70
|
# alignment is determined by cell content, with numbers aligned right, booleans
|
87
71
|
# center-aligned, and other values left-aligned.
|
72
|
+
# @param [:ascii, :markdown, :modern, :blank, nil] border (nil) Determines the characters used
|
73
|
+
# for the Table border, including both the characters around the outside of table, and the lines drawn
|
74
|
+
# within the table to separate columns from each other and the header row from the Table body.
|
75
|
+
# Possible values are:
|
76
|
+
# - `:ascii` Uses ASCII characters only
|
77
|
+
# - `:markdown` Produces as a GitHub-flavoured Markdown table
|
78
|
+
# - `:modern` Uses non-ASCII Unicode characters to render a border with smooth continuous lines
|
79
|
+
# - `:blank` No border characters are rendered
|
80
|
+
# - `:classic` Like `:ascii`, but does not have a horizontal line at the bottom of the
|
81
|
+
# table. This reproduces the default behaviour in `tabulo` v1.
|
82
|
+
# If <tt>nil</tt>, then {DEFAULT_BORDER} will be used.
|
88
83
|
# @param [nil, #to_proc] border_styler (nil) A lambda or other callable object taking
|
89
84
|
# a single parameter, representing a section of the table's borders (which for this purpose
|
90
|
-
# include any horizontal and vertical lines inside the table).
|
85
|
+
# include any horizontal and vertical lines inside the table), and returning a string.
|
91
86
|
# If passed <tt>nil</tt>, then no additional styling will be applied to borders. If passed a
|
92
87
|
# callable, then that callable will be called for each border section, with the
|
93
88
|
# resulting string rendered in place of that border. The extra width of the string returned by the
|
@@ -96,38 +91,36 @@ module Tabulo
|
|
96
91
|
# for example, without breaking the table formatting.
|
97
92
|
# @return [Table] a new {Table}
|
98
93
|
# @raise [InvalidColumnLabelError] if non-unique Symbols are provided to columns.
|
99
|
-
# @raise [
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
vertical_rule_character: nil, intersection_character: nil, truncation_indicator: nil,
|
104
|
-
align_header: :center, align_body: :auto, border_styler: nil)
|
105
|
-
|
106
|
-
if columns.any?
|
107
|
-
Deprecation.warn("`columns' option to Tabulo::Table#initialize", "the variable length parameter `cols'", 2)
|
108
|
-
end
|
94
|
+
# @raise [InvalidBorderError] if invalid option passed to `border` parameter.
|
95
|
+
def initialize(sources, *columns, column_width: nil, column_padding: nil, header_frequency: :start,
|
96
|
+
wrap_header_cells_to: nil, wrap_body_cells_to: nil, truncation_indicator: nil, align_header: :center,
|
97
|
+
align_body: :auto, border: nil, border_styler: nil)
|
109
98
|
|
110
99
|
@sources = sources
|
111
100
|
@header_frequency = header_frequency
|
112
101
|
@wrap_header_cells_to = wrap_header_cells_to
|
113
102
|
@wrap_body_cells_to = wrap_body_cells_to
|
114
103
|
@default_column_width = (column_width || DEFAULT_COLUMN_WIDTH)
|
115
|
-
@column_padding = (column_padding || DEFAULT_COLUMN_PADDING)
|
116
104
|
@align_header = align_header
|
117
105
|
@align_body = align_body
|
106
|
+
|
107
|
+
@column_padding = (column_padding || DEFAULT_COLUMN_PADDING)
|
108
|
+
@left_column_padding, @right_column_padding =
|
109
|
+
case @column_padding
|
110
|
+
when Array
|
111
|
+
@column_padding
|
112
|
+
else
|
113
|
+
[@column_padding, @column_padding]
|
114
|
+
end
|
115
|
+
|
116
|
+
@border = (border || DEFAULT_BORDER)
|
118
117
|
@border_styler = border_styler
|
118
|
+
@border_instance = Border.from(@border, @border_styler)
|
119
119
|
|
120
|
-
@horizontal_rule_character = validate_character(horizontal_rule_character,
|
121
|
-
DEFAULT_HORIZONTAL_RULE_CHARACTER, InvalidHorizontalRuleCharacterError, "horizontal rule character")
|
122
|
-
@vertical_rule_character = validate_character(vertical_rule_character,
|
123
|
-
DEFAULT_VERTICAL_RULE_CHARACTER, InvalidVerticalRuleCharacterError, "vertical rule character")
|
124
|
-
@intersection_character = validate_character(intersection_character,
|
125
|
-
DEFAULT_INTERSECTION_CHARACTER, InvalidIntersectionCharacterError, "intersection character")
|
126
120
|
@truncation_indicator = validate_character(truncation_indicator,
|
127
121
|
DEFAULT_TRUNCATION_INDICATOR, InvalidTruncationIndicatorError, "truncation indicator")
|
128
122
|
|
129
123
|
@column_registry = { }
|
130
|
-
cols.each { |item| add_column(item) }
|
131
124
|
columns.each { |item| add_column(item) }
|
132
125
|
|
133
126
|
yield self if block_given?
|
@@ -233,7 +226,9 @@ module Tabulo
|
|
233
226
|
# display in a fixed-width font.
|
234
227
|
def to_s
|
235
228
|
if column_registry.any?
|
236
|
-
|
229
|
+
bottom_edge = horizontal_rule(:bottom)
|
230
|
+
rows = map(&:to_s)
|
231
|
+
bottom_edge.empty? ? join_lines(rows) : join_lines(rows + [bottom_edge])
|
237
232
|
else
|
238
233
|
""
|
239
234
|
end
|
@@ -250,16 +245,20 @@ module Tabulo
|
|
250
245
|
# were not disabled when the Table was initialized).
|
251
246
|
def each
|
252
247
|
@sources.each_with_index do |source, index|
|
253
|
-
|
248
|
+
header =
|
254
249
|
case @header_frequency
|
255
250
|
when :start
|
256
|
-
index == 0
|
251
|
+
:top if index == 0
|
257
252
|
when Integer
|
258
|
-
index
|
253
|
+
if index == 0
|
254
|
+
:top
|
255
|
+
elsif index % @header_frequency == 0
|
256
|
+
:middle
|
257
|
+
end
|
259
258
|
else
|
260
259
|
@header_frequency
|
261
260
|
end
|
262
|
-
yield body_row(source,
|
261
|
+
yield body_row(source, header: header)
|
263
262
|
end
|
264
263
|
end
|
265
264
|
|
@@ -269,20 +268,26 @@ module Tabulo
|
|
269
268
|
format_row(cells, @wrap_header_cells_to)
|
270
269
|
end
|
271
270
|
|
271
|
+
# @param [:top, :middle, :bottom] align_body (:bottom) Specifies the position
|
272
|
+
# for which the resulting horizontal dividing line is intended to be printed.
|
273
|
+
# This determines the border characters that are used to construct the line.
|
272
274
|
# @return [String] an "ASCII" graphical representation of a horizontal
|
273
|
-
# dividing line suitable for printing at
|
274
|
-
#
|
275
|
-
#
|
275
|
+
# dividing line suitable for printing at the top, bottom or middle of the
|
276
|
+
# table.
|
277
|
+
# @example Print a horizontal divider between each pair of rows, and again
|
278
|
+
# at the bottom:
|
279
|
+
#
|
280
|
+
# table.each_with_index do |row, i|
|
281
|
+
# puts table.horizontal_rule(:middle) unless i == 0
|
276
282
|
# puts row
|
277
|
-
# puts table.horizontal_rule
|
278
283
|
# end
|
284
|
+
# puts table.horizontal_rule(:bottom)
|
279
285
|
#
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
styled_border(surround_join(inner, @intersection_character))
|
286
|
+
# It may be that `:top`, `:middle` and `:bottom` all look the same. Whether
|
287
|
+
# this is the case depends on the characters used for the table border.
|
288
|
+
def horizontal_rule(position = :bottom)
|
289
|
+
column_widths = column_registry.map { |_, column| column.width + total_column_padding }
|
290
|
+
@border_instance.horizontal_rule(column_widths, position)
|
286
291
|
end
|
287
292
|
|
288
293
|
# Reset all the column widths so that each column is *just* wide enough to accommodate
|
@@ -352,9 +357,8 @@ module Tabulo
|
|
352
357
|
# @param [Hash] opts Options for configuring the new, transposed {Table}.
|
353
358
|
# The following options are the same as the keyword params for the {#initialize} method for
|
354
359
|
# {Table}: <tt>column_width</tt>, <tt>column_padding</tt>, <tt>header_frequency</tt>,
|
355
|
-
# <tt>wrap_header_cells_to</tt>, <tt>wrap_body_cells_to</tt>, <tt>
|
356
|
-
# <tt>
|
357
|
-
# <tt>align_header</tt>, <tt>align_body</tt>.
|
360
|
+
# <tt>wrap_header_cells_to</tt>, <tt>wrap_body_cells_to</tt>, <tt>border</tt>,
|
361
|
+
# <tt>border_styler</tt>, <tt>truncation_indicator</tt>, <tt>align_header</tt>, <tt>align_body</tt>.
|
358
362
|
# These are applied in the same way as documented for {#initialize}, when creating the
|
359
363
|
# new, transposed Table. Any options not specified explicitly in the call to {#transpose}
|
360
364
|
# will inherit their values from the original {Table} (with the exception of settings
|
@@ -376,12 +380,11 @@ module Tabulo
|
|
376
380
|
# new Table (other than the left-most column's header, which is determined as described
|
377
381
|
# above).
|
378
382
|
# @return [Table] a new {Table}
|
379
|
-
# @raise [
|
380
|
-
# @raise [InvalidVerticalRuleCharacterError] if invalid argument passed to vertical_rule_character.
|
383
|
+
# @raise [InvalidBorderError] if invalid argument passed to `border` parameter.
|
381
384
|
def transpose(opts = {})
|
382
385
|
default_opts = [:column_width, :column_padding, :header_frequency, :wrap_header_cells_to,
|
383
|
-
:wrap_body_cells_to, :
|
384
|
-
:
|
386
|
+
:wrap_body_cells_to, :truncation_indicator, :align_header, :align_body, :border,
|
387
|
+
:border_styler].map do |sym|
|
385
388
|
[sym, instance_variable_get("@#{sym}")]
|
386
389
|
end.to_h
|
387
390
|
|
@@ -411,47 +414,17 @@ module Tabulo
|
|
411
414
|
end
|
412
415
|
end
|
413
416
|
|
414
|
-
# @deprecated Use {#pack} instead.
|
415
|
-
#
|
416
|
-
# Reset all the column widths so that each column is *just* wide enough to accommodate
|
417
|
-
# its header text as well as the formatted content of each its cells for the entire
|
418
|
-
# collection, together with a single character of padding on either side of the column,
|
419
|
-
# without any wrapping.
|
420
|
-
#
|
421
|
-
# Note that calling this method will cause the entire source Enumerable to
|
422
|
-
# be traversed and all the column extractors and formatters to be applied in order
|
423
|
-
# to calculate the required widths.
|
424
|
-
#
|
425
|
-
# Note also that this method causes column widths to be fixed as appropriate to the
|
426
|
-
# formatted cell contents given the state of the source Enumerable at the point it
|
427
|
-
# is called. If the source Enumerable changes between that point, and the point when
|
428
|
-
# the Table is printed, then columns will *not* be resized yet again on printing.
|
429
|
-
#
|
430
|
-
# @param [nil, Numeric] max_table_width (nil) If provided, stops the total table
|
431
|
-
# width (including padding and borders) from expanding beyond this number of characters.
|
432
|
-
# If passed <tt>:auto</tt>, the table width will automatically be capped at the current
|
433
|
-
# terminal width.
|
434
|
-
# Width is deducted from columns if required to achieve this, with one character progressively
|
435
|
-
# deducted from the width of the widest column until the target is reached. When the
|
436
|
-
# table is printed, wrapping or truncation will then occur in these columns as required
|
437
|
-
# (depending on how they were configured).
|
438
|
-
# Note that regardless of the value passed to max_table_width, the table will always be left wide
|
439
|
-
# enough to accommodate at least 1 character's width of content, 1 character of left padding and
|
440
|
-
# 1 character of right padding in each column, together with border characters (1 on each side
|
441
|
-
# of the table and 1 between adjacent columns). I.e. there is a certain width below width the
|
442
|
-
# Table will refuse to shrink itself.
|
443
|
-
# @return [Table] the Table itself
|
444
|
-
def shrinkwrap!(max_table_width: nil)
|
445
|
-
Deprecation.warn("`Tabulo::Table#shrinkwrap!'", "`#pack'")
|
446
|
-
pack(max_table_width: max_table_width)
|
447
|
-
end
|
448
|
-
|
449
417
|
# @!visibility private
|
450
|
-
def formatted_body_row(source,
|
418
|
+
def formatted_body_row(source, header: nil)
|
451
419
|
cells = column_registry.map { |_, column| column.body_cell(source) }
|
452
420
|
inner = format_row(cells, @wrap_body_cells_to)
|
453
|
-
if
|
454
|
-
join_lines([
|
421
|
+
if header
|
422
|
+
join_lines([
|
423
|
+
horizontal_rule(header == :top ? :top : :middle),
|
424
|
+
formatted_header,
|
425
|
+
horizontal_rule(:middle),
|
426
|
+
inner,
|
427
|
+
].reject(&:empty?))
|
455
428
|
else
|
456
429
|
inner
|
457
430
|
end
|
@@ -459,11 +432,16 @@ module Tabulo
|
|
459
432
|
|
460
433
|
private
|
461
434
|
|
435
|
+
# @!visibility private
|
436
|
+
def total_column_padding
|
437
|
+
@left_column_padding + @right_column_padding
|
438
|
+
end
|
439
|
+
|
462
440
|
# @!visibility private
|
463
441
|
def shrink_to(max_table_width)
|
464
442
|
columns = column_registry.values
|
465
443
|
total_columns_width = columns.inject(0) { |sum, column| sum + column.width }
|
466
|
-
total_padding = column_registry.count *
|
444
|
+
total_padding = column_registry.count * total_column_padding
|
467
445
|
total_borders = column_registry.count + 1
|
468
446
|
unadjusted_table_width = total_columns_width + total_padding + total_borders
|
469
447
|
|
@@ -483,8 +461,8 @@ module Tabulo
|
|
483
461
|
end
|
484
462
|
|
485
463
|
# @!visibility private
|
486
|
-
def body_row(source,
|
487
|
-
Row.new(self, source,
|
464
|
+
def body_row(source, header: nil)
|
465
|
+
Row.new(self, source, header: header)
|
488
466
|
end
|
489
467
|
|
490
468
|
# @!visibility private
|
@@ -503,25 +481,14 @@ module Tabulo
|
|
503
481
|
def format_row(cells, wrap_cells_to)
|
504
482
|
max_cell_height = cells.map(&:height).max
|
505
483
|
row_height = ([wrap_cells_to, max_cell_height].compact.min || 1)
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
# @!visibility private
|
513
|
-
def styled_border(str)
|
514
|
-
@border_styler ? @border_styler.call(str) : str
|
515
|
-
end
|
516
|
-
|
517
|
-
# @!visibility private
|
518
|
-
def surround(str, ch)
|
519
|
-
"#{ch}#{str}#{ch}"
|
520
|
-
end
|
484
|
+
subcell_stacks = cells.map do |cell|
|
485
|
+
cell.padded_truncated_subcells(row_height, @left_column_padding, @right_column_padding)
|
486
|
+
end
|
487
|
+
subrows = subcell_stacks.transpose.map do |subrow_components|
|
488
|
+
@border_instance.join_cell_contents(subrow_components)
|
489
|
+
end
|
521
490
|
|
522
|
-
|
523
|
-
def surround_join(arr, ch)
|
524
|
-
surround(arr.join(ch), ch)
|
491
|
+
join_lines(subrows)
|
525
492
|
end
|
526
493
|
|
527
494
|
# @!visibility private
|
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:
|
4
|
+
version: 2.0.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: 2019-
|
11
|
+
date: 2019-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tty-screen
|
@@ -187,6 +187,7 @@ files:
|
|
187
187
|
- bin/console
|
188
188
|
- bin/setup
|
189
189
|
- lib/tabulo.rb
|
190
|
+
- lib/tabulo/border.rb
|
190
191
|
- lib/tabulo/cell.rb
|
191
192
|
- lib/tabulo/column.rb
|
192
193
|
- lib/tabulo/deprecation.rb
|