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