tabulo 1.2.2 → 1.3.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 +1 -1
- data/CHANGELOG.md +11 -0
- data/README.md +187 -38
- data/VERSION +1 -1
- data/lib/tabulo.rb +2 -1
- data/lib/tabulo/column.rb +1 -1
- data/lib/tabulo/deprecation.rb +32 -0
- data/lib/tabulo/row.rb +3 -0
- data/lib/tabulo/table.rb +113 -42
- data/lib/tabulo/version.rb +1 -1
- data/tabulo.gemspec +4 -2
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ca950eccef1e0d5bcd333ce42584fd4a95020316e80d2f684b721f0cb6ec00a
|
4
|
+
data.tar.gz: ea4c7f96ebce149b131db590fdeb75a54152576717007024556047a36004469f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f83f8889ad5f72954bc0a16a743c6ecdb00b732fdac29d179a43874eba68b021f180be981bbce82773bf4f8f0e968a92c03400cf9af74cf8a6bb23ed585cf77
|
7
|
+
data.tar.gz: f9a47229fe11cfc79ea265f4280ca84666e7e09c7d325db4e4b4feb9c2a854f76a9c1647fdcbd2eff3a5073fd327044be75758af839f227750ab1fd24aa2eccc
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### v1.3.0
|
4
|
+
|
5
|
+
* More ergonomic Table initializer, allowing you to specify columns directly as varargs rather
|
6
|
+
than as an array passed to `columns:` option (the latter is now deprecated)
|
7
|
+
* New `#pack` method to autosize table, capping total table width at width of terminal
|
8
|
+
by default (replaces `#shrinkwrap!` method, now deprecated)
|
9
|
+
* Ability to set table-level defaults for column header and body cell alignments
|
10
|
+
* Accessor methods for `source` attribute, representing the underlying collection
|
11
|
+
being tabulated, facilitating reuse of the same table to tabulate different collections
|
12
|
+
* Documentation improvements
|
13
|
+
|
3
14
|
### v1.2.2
|
4
15
|
|
5
16
|
* Improve documentation.
|
data/README.md
CHANGED
@@ -10,8 +10,21 @@
|
|
10
10
|
|
11
11
|
Tabulo is a Ruby library for generating ASCII tables.
|
12
12
|
|
13
|
+
*Quick API:*
|
14
|
+
|
15
|
+
```
|
16
|
+
> puts Tabulo::Table.new(User.all, :id, :first_name, :last_name)
|
17
|
+
+--------------+--------------+--------------+
|
18
|
+
| id | first_name | last_name |
|
19
|
+
+--------------+--------------+--------------+
|
20
|
+
| 1 | John | Citizen |
|
21
|
+
| 2 | Jane | Doe |
|
22
|
+
```
|
23
|
+
|
24
|
+
*Full API:*
|
25
|
+
|
13
26
|
```ruby
|
14
|
-
underyling_enumerable = [1, 2, 50000000]
|
27
|
+
underyling_enumerable = [1, 2, 50000000]
|
15
28
|
|
16
29
|
table = Tabulo::Table.new(underlying_enumerable) do |t|
|
17
30
|
t.add_column("N") { |n| n }
|
@@ -31,13 +44,10 @@ end
|
|
31
44
|
|
32
45
|
## Features
|
33
46
|
|
34
|
-
* A [DRY interface](#configuring-columns): by being "column based", it is designed to spare the
|
35
|
-
developer the burden of syncing the ordering within the header row with that of the body rows.
|
36
47
|
* Lets you set [fixed column widths](#fixed-column-widths), then either [wrap](#overflow-handling) or
|
37
48
|
[truncate](#overflow-handling) the overflow.
|
38
|
-
* Alternatively, [
|
39
|
-
|
40
|
-
[stop it overflowing your terminal horizontally](#max-table-width).
|
49
|
+
* Alternatively, ["pack"](#pack) the table so that each column is automatically just wide enough for its
|
50
|
+
contents, but [without overflowing the terminal horizontally](#max-table-width).
|
41
51
|
* Alignment of cell content is [configurable](#cell-alignment), but has helpful content-based defaults
|
42
52
|
(numbers right, strings left).
|
43
53
|
* Headers are [repeatable](#repeating-headers).
|
@@ -47,6 +57,8 @@ end
|
|
47
57
|
* Each `Tabulo::Row` is also an `Enumerable`, [providing access](#accessing-cell-values) to the underlying cell values.
|
48
58
|
* Tabulate any `Enumerable`: the underlying collection need not be an array.
|
49
59
|
* [Customize](#additional-configuration-options) border and divider characters.
|
60
|
+
* Use a [DRY interface](#configuring-columns): by being "column based", it is designed to spare the
|
61
|
+
developer the burden of syncing the ordering within the header row with that of the body rows.
|
50
62
|
|
51
63
|
Tabulo has also been ported to Crystal (with some modifications): see [Tablo](https://github.com/hutou/tablo).
|
52
64
|
|
@@ -54,7 +66,7 @@ Tabulo has also been ported to Crystal (with some modifications): see [Tablo](ht
|
|
54
66
|
|
55
67
|
* [Overview](#overview)
|
56
68
|
* [Features](#features)
|
57
|
-
* [Table of
|
69
|
+
* [Table of contents](#table-of-contents)
|
58
70
|
* [Installation](#installation)
|
59
71
|
* [Detailed usage](#detailed-usage)
|
60
72
|
* [Requiring the gem](#requiring-the-gem)
|
@@ -69,8 +81,9 @@ Tabulo has also been ported to Crystal (with some modifications): see [Tablo](ht
|
|
69
81
|
* [Repeating headers](#repeating-headers)
|
70
82
|
* [Using a Table Enumerator](#using-a-table-enumerator)
|
71
83
|
* [Accessing cell values](#accessing-cell-values)
|
84
|
+
* [Accessing the underlying enumerable](#accessing-sources)
|
72
85
|
* [Additional configuration options](#additional-configuration-options)
|
73
|
-
* [
|
86
|
+
* [Motivation](#motivation)
|
74
87
|
* [Contributing](#contributing)
|
75
88
|
* [License](#license)
|
76
89
|
|
@@ -114,10 +127,10 @@ table = Tabulo::Table.new([1, 2, 5]) do |t|
|
|
114
127
|
end
|
115
128
|
```
|
116
129
|
|
117
|
-
Or equivalently:
|
130
|
+
Or equivalently, using the "quick API":
|
118
131
|
|
119
132
|
```ruby
|
120
|
-
table = Tabulo::Table.new([1, 2, 5],
|
133
|
+
table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?)
|
121
134
|
```
|
122
135
|
|
123
136
|
```
|
@@ -157,11 +170,20 @@ end
|
|
157
170
|
|
158
171
|
By default, column header text is center-aligned, while the content of each body cell is aligned
|
159
172
|
according to its data type. Numbers are right-aligned, text is left-aligned, and booleans (`false`
|
160
|
-
and `true`) are center-aligned.
|
161
|
-
|
173
|
+
and `true`) are center-aligned.
|
174
|
+
|
175
|
+
This default behaviour can be set at the table level, by passing `:center`, `:left` or `:right`
|
176
|
+
to the `align_header` or `align_body` options when initializing the table:
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
table = Tabulo::Table.new([1, 2], :itself, :even?, align_header: :left, align_body: :right)
|
180
|
+
```
|
181
|
+
|
182
|
+
The table-level alignment settings can be overridden for individual columns by
|
183
|
+
passing using similarly-named options passed to `add_column`, e.g.:
|
162
184
|
|
163
185
|
```ruby
|
164
|
-
table.add_column("Doubled", align_header: :
|
186
|
+
table.add_column("Doubled", align_header: :right, align_body: :left) { |n| n * 2 }
|
165
187
|
```
|
166
188
|
|
167
189
|
### Column width, wrapping and truncation
|
@@ -192,7 +214,7 @@ If you want to set the default column width for all columns of the table to some
|
|
192
214
|
than 12, use the `column_width` option when initializing the table:
|
193
215
|
|
194
216
|
```ruby
|
195
|
-
table = Tabulo::Table.new([1, 2],
|
217
|
+
table = Tabulo::Table.new([1, 2], :itself, :even?, column_width: 6)
|
196
218
|
```
|
197
219
|
|
198
220
|
```
|
@@ -213,7 +235,7 @@ The single character of padding either side of each column is not counted in the
|
|
213
235
|
The amount of this padding can be configured for the table as a whole, using the `column_padding`
|
214
236
|
option passed to `Table.new`.
|
215
237
|
|
216
|
-
<a name="
|
238
|
+
<a name="pack"></a>
|
217
239
|
#### Automating column widths
|
218
240
|
|
219
241
|
Instead of setting column widths "manually", you can tell the table to sort out the widths
|
@@ -221,8 +243,8 @@ itself, so that each column is just wide enough for its header and contents (plu
|
|
221
243
|
of padding):
|
222
244
|
|
223
245
|
```ruby
|
224
|
-
table = Tabulo::Table.new([1, 2],
|
225
|
-
table.
|
246
|
+
table = Tabulo::Table.new([1, 2], :itself, :even?)
|
247
|
+
table.pack
|
226
248
|
```
|
227
249
|
|
228
250
|
```
|
@@ -234,17 +256,17 @@ table.shrinkwrap!
|
|
234
256
|
| 2 | true |
|
235
257
|
```
|
236
258
|
|
237
|
-
The `
|
259
|
+
The `pack` method returns the table itself, so you can "pack-and-print" in one go:
|
238
260
|
|
239
261
|
```ruby
|
240
|
-
puts Tabulo::Table.new([1, 2],
|
262
|
+
puts Tabulo::Table.new([1, 2], :itself, :even?).pack
|
241
263
|
```
|
242
264
|
|
243
265
|
<a name="max-table-width"></a>
|
244
|
-
You can place an upper limit on the total width of the table when
|
266
|
+
You can manually place an upper limit on the total width of the table when packing:
|
245
267
|
|
246
268
|
```ruby
|
247
|
-
puts Tabulo::Table.new([1, 2],
|
269
|
+
puts Tabulo::Table.new([1, 2], :itself, :even?).pack(max_table_width: 17)
|
248
270
|
```
|
249
271
|
|
250
272
|
```
|
@@ -256,17 +278,23 @@ puts Tabulo::Table.new([1, 2], columns: %i[itself even?]).shrinkwrap!(max_table_
|
|
256
278
|
| 2 | true |
|
257
279
|
```
|
258
280
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
targetted each time—until the table will fit. This is very useful when you want to ensure the
|
263
|
-
table will not overflow your terminal horizontally.
|
281
|
+
Or if you simply call `pack` with no parameters (or if you explicitly call
|
282
|
+
`pack(max_table_width: :auto)`), the table width will automatically be capped at the
|
283
|
+
width of your terminal.
|
264
284
|
|
265
|
-
|
285
|
+
If you want the table width not to be capped at all, call `pack(max_table_width: nil)`.
|
286
|
+
|
287
|
+
If the table cannot be fit within the width of the terminal, or the specified maximum width,
|
288
|
+
then column widths are reduced as required, with wrapping or truncation then occuring as
|
289
|
+
necessary (see [Overflow handling](#overflow-handling)). Under the hood, a character of width
|
290
|
+
is deducted column by column—the widest column being targetted each time—until
|
291
|
+
the table will fit.
|
292
|
+
|
293
|
+
Note that `pack`ing the table necessarily involves traversing the entire collection up front as
|
266
294
|
the maximum cell width needs to be calculated for each column. You may not want to do this
|
267
|
-
if the collection is very large. Note also the effect of `
|
295
|
+
if the collection is very large. Note also the effect of `pack` is to fix the column widths
|
268
296
|
as appropriate to the formatted cell contents given the state of the underlying collection
|
269
|
-
_at the point of
|
297
|
+
_at the point of packing_. If the underlying collection changes between that point, and when
|
270
298
|
the table is printed, then the columns will _not_ be resized yet again on printing. This is a
|
271
299
|
consequence of the table always being essentially a "live view" on the underlying collection:
|
272
300
|
formatted contents are never cached within the table itself.
|
@@ -280,7 +308,7 @@ required:
|
|
280
308
|
```ruby
|
281
309
|
table = Tabulo::Table.new(
|
282
310
|
["hello", "abcdefghijklmnopqrstuvwxyz"],
|
283
|
-
|
311
|
+
:itself, :length
|
284
312
|
)
|
285
313
|
```
|
286
314
|
|
@@ -304,8 +332,8 @@ outputted cell content to show that truncation has occurred:
|
|
304
332
|
```ruby
|
305
333
|
table = Tabulo::Table.new(
|
306
334
|
["hello", "abcdefghijklmnopqrstuvwxyz"],
|
307
|
-
|
308
|
-
|
335
|
+
:itself, :length,
|
336
|
+
wrap_body_cells_to: 1
|
309
337
|
)
|
310
338
|
```
|
311
339
|
|
@@ -363,7 +391,7 @@ at table that's taller than your terminal.
|
|
363
391
|
E.g.:
|
364
392
|
|
365
393
|
```ruby
|
366
|
-
table = Tabulo::Table.new(1..10,
|
394
|
+
table = Tabulo::Table.new(1..10, :itself, :even?, header_frequency: 5)
|
367
395
|
```
|
368
396
|
|
369
397
|
```
|
@@ -411,7 +439,7 @@ end.to_enum # <-- make an Enumerator
|
|
411
439
|
```
|
412
440
|
|
413
441
|
Note the use of `.find_each`: we can start printing the table without having to load the entire
|
414
|
-
underlying collection. (This is negated if we [
|
442
|
+
underlying collection. (This is negated if we [pack](#pack) the table, however, since
|
415
443
|
in that case the entire collection must be traversed up front in order for column widths to be
|
416
444
|
calculated.)
|
417
445
|
|
@@ -422,7 +450,7 @@ is itself an `Enumerable` comprising the underlying the values of each cell. A `
|
|
422
450
|
also be converted to a `Hash` for keyed access. For example:
|
423
451
|
|
424
452
|
```ruby
|
425
|
-
table = Tabulo::Table.new(1..5,
|
453
|
+
table = Tabulo::Table.new(1..5, :itself, :even?, :odd?)
|
426
454
|
|
427
455
|
table.each do |row|
|
428
456
|
row.each { |cell| puts cell } # 1...2...3...4...5
|
@@ -449,6 +477,43 @@ table.each do |row|
|
|
449
477
|
end
|
450
478
|
```
|
451
479
|
|
480
|
+
<a name="accessing-sources"></a>
|
481
|
+
### Accessing the underlying enumerable
|
482
|
+
|
483
|
+
The underlying enumerable for a table can be retrieved by calling the `sources` getter:
|
484
|
+
|
485
|
+
```ruby
|
486
|
+
table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?)
|
487
|
+
```
|
488
|
+
|
489
|
+
```
|
490
|
+
> table.sources
|
491
|
+
=> [1, 2, 5]
|
492
|
+
```
|
493
|
+
|
494
|
+
There is also a corresponding setter, meaning you can reuse the same table to tabulate
|
495
|
+
a different data set, without having to reconfigure the columns and other options from scratch:
|
496
|
+
|
497
|
+
```ruby
|
498
|
+
table.sources = [50, 60]
|
499
|
+
```
|
500
|
+
|
501
|
+
```
|
502
|
+
> table.sources
|
503
|
+
=> [50, 60]
|
504
|
+
```
|
505
|
+
|
506
|
+
In addition, the element of the underlying enumerable corresponding to a particular
|
507
|
+
row can be accessed by calling the `source` method on that row:
|
508
|
+
|
509
|
+
```ruby
|
510
|
+
table.each do |row|
|
511
|
+
puts row.source # 50...60...
|
512
|
+
end
|
513
|
+
|
514
|
+
```
|
515
|
+
|
516
|
+
<a name="additional-configuration-options"></a>
|
452
517
|
### Additional configuration options
|
453
518
|
|
454
519
|
The characters used for horizontal dividers, vertical dividers and corners, which default to `-`,
|
@@ -470,7 +535,7 @@ This will output a bottom border that's appropriately sized for the table.
|
|
470
535
|
This mechanism can also be used to output a horizontal divider after each row:
|
471
536
|
|
472
537
|
```ruby
|
473
|
-
table = Tabulo::Table.new(1..3,
|
538
|
+
table = Tabulo::Table.new(1..3, :itself, :even?)
|
474
539
|
```
|
475
540
|
|
476
541
|
```
|
@@ -486,6 +551,90 @@ table = Tabulo::Table.new(1..3, columns: %i[itself even?])
|
|
486
551
|
+--------------+--------------+
|
487
552
|
```
|
488
553
|
|
554
|
+
<a name="motivation"></a>
|
555
|
+
## Motivation
|
556
|
+
|
557
|
+
There are other terminal table generators for Ruby. Popular among these are:
|
558
|
+
|
559
|
+
* [tty-table](https://github.com/piotrmurach/tty-table)
|
560
|
+
* [terminal-table](https://github.com/tj/terminal-table)
|
561
|
+
* [table\_print](https://github.com/arches/table_print)
|
562
|
+
|
563
|
+
*DISCLAIMER: My comments regarding these other libraries are based only on my own,
|
564
|
+
possibly-flawed, reading of the documentation for, and experimentation with, these libraries at the
|
565
|
+
time of my writing this. Their APIs, features or documentation may well change between when I write this, and
|
566
|
+
when you read it—possibly in ways that address the issues I describe here!*
|
567
|
+
|
568
|
+
These are excellent libraries, and each has its strengths. However, I personally found that for the
|
569
|
+
common use case of printing a table on the basis of some underlying collection (such as an
|
570
|
+
ActiveRecord query result), using these libraries in practice was more cumbersome than it needed to
|
571
|
+
be.
|
572
|
+
|
573
|
+
For example, suppose we have called `User.all` from the Rails console, and want to print
|
574
|
+
a table showing the email, first name, last name and ID of each user,
|
575
|
+
with column headings. Also, we want the ID column to be right-aligned, because it's a number.
|
576
|
+
|
577
|
+
In `terminal-table`, we could achieve this as follows:
|
578
|
+
|
579
|
+
```ruby
|
580
|
+
rows = User.all.map { |u| [u.email, u.first_name, u.last_name, u.id] }
|
581
|
+
headings = ["email", "first name", "last name", "id"]
|
582
|
+
table = Terminal::Table.new(headings: headings, rows: rows)
|
583
|
+
table.align_column(3, :right)
|
584
|
+
puts table
|
585
|
+
```
|
586
|
+
|
587
|
+
The problem here is that there is no single source of knowledge about which columns
|
588
|
+
appear, and in which order. If we want to add another column to the left of "email",
|
589
|
+
we need to amend the rows map, and the headings map, and the index passed to `align_column`.
|
590
|
+
We bear the burden of keeping these three in sync. This is not be a big deal for small one-off
|
591
|
+
tables, but for tables that have many columns, or that are constructed
|
592
|
+
dynamically based on user input or other runtime factors determining the columns
|
593
|
+
to be included, this can be a hassle and a source of brittleness.
|
594
|
+
|
595
|
+
`tty-table` has a somewhat different API to `terminal-table`. It offers both a
|
596
|
+
"row-based" and a "column-based" method of initializing a table. We won't cover
|
597
|
+
the row-based method here, but it is similar to `terminal-table`'s in that it
|
598
|
+
burdens the developer with syncing the column ordering across multiple code
|
599
|
+
locations. The "column-based" API for `tty-table`, on the other hand, looks like this:
|
600
|
+
|
601
|
+
```ruby
|
602
|
+
users = User.all
|
603
|
+
table = TTY::Table.new [
|
604
|
+
{
|
605
|
+
"email" => users.map(&:email),
|
606
|
+
"first name" => users.map(&:first_name),
|
607
|
+
"last name" => users.map(&:last_name),
|
608
|
+
"id" => users.map(&:id),
|
609
|
+
}
|
610
|
+
]
|
611
|
+
puts table
|
612
|
+
```
|
613
|
+
|
614
|
+
While this doesn't seem too bad, it does mean that the underlying collection (`users`) has to
|
615
|
+
be traversed multiple times, once for each column, which is inefficient, particularly
|
616
|
+
if the underlying collection is large. In addition, it's not clear how to pass separate
|
617
|
+
formatting information for each column when initializing in this way. (Perhaps there is a way to do
|
618
|
+
this, but if there is, it doesn't seem to be documented.) So it seems we still have to use
|
619
|
+
`table.align_column(3, :right)`, which again burdens us with keeping the column index
|
620
|
+
passed to `align_column` in sync.
|
621
|
+
|
622
|
+
Finally, there is [table\_print](https://github.com/arches/table_print). This is a
|
623
|
+
handy gem for quickly tabulating ActiveRecord collections from the Rails
|
624
|
+
console. `table_print` is similar to `tabulo` in that it has a column-based API, so it doesn't
|
625
|
+
suffer from the multiple-source-of-knowledge issue in regards to column orderings. However, as far
|
626
|
+
as I can tell, it lacks certain other useful features, such as the ability to repeat headers every N
|
627
|
+
rows, the automatic alignment of columns based on cell content (numbers right, strings left), and a
|
628
|
+
quick and easy way to automatically resize columns to accommodate cell content without overflowing
|
629
|
+
the terminal. Also, as of the time of writing, `table_print`'s last significant commit (ignoring a
|
630
|
+
deprecation warning fix in April 2018) was in March 2016.
|
631
|
+
|
632
|
+
I don't mean to disparage any of these other projects—they each have their strengths—but
|
633
|
+
rather to explain my motivation for developing and maintaining Tabulo despite their existence, and
|
634
|
+
to provide reasons one might consider for using `tabulo` rather than another terminal table
|
635
|
+
gem.
|
636
|
+
|
637
|
+
<a name="contributing"></a>
|
489
638
|
## Contributing
|
490
639
|
|
491
640
|
Issues and pull requests are welcome on GitHub at https://github.com/matt-harvey/tabulo.
|
@@ -503,13 +652,13 @@ The gem is available as open source under the terms of the [MIT
|
|
503
652
|
License](http://opensource.org/licenses/MIT).
|
504
653
|
|
505
654
|
[Gem Version]: https://rubygems.org/gems/tabulo
|
506
|
-
[Documentation]: http://www.rubydoc.info/gems/tabulo/1.
|
655
|
+
[Documentation]: http://www.rubydoc.info/gems/tabulo/1.3.0
|
507
656
|
[Build Status]: https://travis-ci.org/matt-harvey/tabulo
|
508
657
|
[Coverage Status]: https://coveralls.io/r/matt-harvey/tabulo
|
509
658
|
[Code Climate]: https://codeclimate.com/github/matt-harvey/tabulo
|
510
659
|
|
511
660
|
[GV img]: https://img.shields.io/gem/v/tabulo.svg
|
512
|
-
[DC img]: https://img.shields.io/badge/documentation-v1.
|
661
|
+
[DC img]: https://img.shields.io/badge/documentation-v1.3.0-blue.svg
|
513
662
|
[BS img]: https://img.shields.io/travis/matt-harvey/tabulo.svg
|
514
663
|
[CS img]: https://img.shields.io/coveralls/matt-harvey/tabulo.svg
|
515
664
|
[CC img]: https://codeclimate.com/github/matt-harvey/tabulo/badges/gpa.svg
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.3.0
|
data/lib/tabulo.rb
CHANGED
data/lib/tabulo/column.rb
CHANGED
@@ -22,7 +22,7 @@ module Tabulo
|
|
22
22
|
def body_subcells(source)
|
23
23
|
cell_datum = body_cell_value(source)
|
24
24
|
formatted_content = @formatter.call(cell_datum)
|
25
|
-
real_alignment = (@align_body
|
25
|
+
real_alignment = (@align_body == :auto ? infer_alignment(cell_datum) : @align_body)
|
26
26
|
infilled_subcells(formatted_content, real_alignment)
|
27
27
|
end
|
28
28
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Tabulo
|
2
|
+
|
3
|
+
module Deprecation
|
4
|
+
|
5
|
+
# @!visibility private
|
6
|
+
def self.skipping_warnings
|
7
|
+
@skipping_warnings ||= false
|
8
|
+
end
|
9
|
+
|
10
|
+
# @!visibility private
|
11
|
+
def self.skipping_warnings=(v)
|
12
|
+
@skipping_warnings = v
|
13
|
+
end
|
14
|
+
|
15
|
+
# @!visibility private
|
16
|
+
def self.without_warnings
|
17
|
+
original = skipping_warnings
|
18
|
+
self.skipping_warnings = true
|
19
|
+
yield
|
20
|
+
ensure
|
21
|
+
self.skipping_warnings = original
|
22
|
+
end
|
23
|
+
|
24
|
+
# @!visibility private
|
25
|
+
def self.warn(deprecated, replacement, stack_level = 1)
|
26
|
+
return if skipping_warnings
|
27
|
+
|
28
|
+
kaller = Kernel.caller[stack_level]
|
29
|
+
Kernel.warn "#{kaller}: [DEPRECATION] #{deprecated} is deprecated. Please use #{replacement} instead."
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/tabulo/row.rb
CHANGED
data/lib/tabulo/table.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "tty-screen"
|
2
|
+
|
1
3
|
module Tabulo
|
2
4
|
|
3
5
|
# Represents a table primarily intended for "pretty-printing" in a fixed-width font.
|
@@ -30,11 +32,15 @@ module Tabulo
|
|
30
32
|
# @!visibility private
|
31
33
|
attr_reader :column_registry
|
32
34
|
|
35
|
+
# @return [Enumerable] the underlying enumerable from which the table derives its data
|
36
|
+
attr_accessor :sources
|
37
|
+
|
33
38
|
# @param [Enumerable] sources the underlying Enumerable from which the table will derive its data
|
34
|
-
# @param [Array[Symbol]]
|
39
|
+
# @param [Array[Symbol]] cols Specifies the initial columns. The Symbols provided must
|
35
40
|
# be unique. Each element of the Array will be used to create a column whose content is
|
36
41
|
# created by calling the corresponding method on each element of sources. Note
|
37
42
|
# the {#add_column} method is a much more flexible way to set up columns on the table.
|
43
|
+
# @param [Array[Symbol]] columns <b>DEPRECATED</b> Use {cols} instead.
|
38
44
|
# @param [Integer, nil] column_width The default column width for columns in this
|
39
45
|
# table, not excluding padding. If <tt>nil</tt>, then {DEFAULT_COLUMN_WIDTH} will be used.
|
40
46
|
# @param [:start, nil, Integer] header_frequency Controls the display of column headers.
|
@@ -70,13 +76,27 @@ module Tabulo
|
|
70
76
|
# a single-character String, raises {InvalidTruncationIndicatorError}.
|
71
77
|
# @param [nil, Integer] column_padding Determines the amount of blank space with which to pad either
|
72
78
|
# of each column. Defaults to 1.
|
73
|
-
# @
|
79
|
+
# @param [:left, :right, :center] align_header (:center) Determines the alignment of header text
|
80
|
+
# for columns in this Table. Can be overridden for individual columns using the
|
81
|
+
# <tt>align_header</tt> option passed to {#add_column}
|
82
|
+
# @param [:left, :right, :center, :auto] align_body (:auto) Determines the alignment of body cell
|
83
|
+
# (i.e. non-header) content within columns in this Table. Can be overridden for individual columns
|
84
|
+
# using the <tt>align_body</tt> option passed to {#add_column}. If passed <tt>:auto</tt>,
|
85
|
+
# alignment is determined by cell content, with numbers aligned right, booleans
|
86
|
+
# center-aligned, and other values left-aligned.
|
87
|
+
# @return [Table] a new {Table}
|
74
88
|
# @raise [InvalidColumnLabelError] if non-unique Symbols are provided to columns.
|
75
89
|
# @raise [InvalidHorizontalRuleCharacterError] if invalid argument passed to horizontal_rule_character.
|
76
90
|
# @raise [InvalidVerticalRuleCharacterError] if invalid argument passed to vertical_rule_character.
|
77
|
-
def initialize(sources, columns: [], column_width: nil, column_padding: nil, header_frequency: :start,
|
91
|
+
def initialize(sources, *cols, columns: [], column_width: nil, column_padding: nil, header_frequency: :start,
|
78
92
|
wrap_header_cells_to: nil, wrap_body_cells_to: nil, horizontal_rule_character: nil,
|
79
|
-
vertical_rule_character: nil, intersection_character: nil, truncation_indicator: nil
|
93
|
+
vertical_rule_character: nil, intersection_character: nil, truncation_indicator: nil,
|
94
|
+
align_header: :center, align_body: :auto)
|
95
|
+
|
96
|
+
if columns.any?
|
97
|
+
Deprecation.warn("`columns' option to Tabulo::Table#initialize",
|
98
|
+
"the variable length parameter `cols'", 2)
|
99
|
+
end
|
80
100
|
|
81
101
|
@sources = sources
|
82
102
|
@header_frequency = header_frequency
|
@@ -84,6 +104,8 @@ module Tabulo
|
|
84
104
|
@wrap_body_cells_to = wrap_body_cells_to
|
85
105
|
@default_column_width = (column_width || DEFAULT_COLUMN_WIDTH)
|
86
106
|
@column_padding = (column_padding || DEFAULT_COLUMN_PADDING)
|
107
|
+
@align_header = align_header
|
108
|
+
@align_body = align_body
|
87
109
|
|
88
110
|
@horizontal_rule_character = validate_character(horizontal_rule_character,
|
89
111
|
DEFAULT_HORIZONTAL_RULE_CHARACTER, InvalidHorizontalRuleCharacterError, "horizontal rule character")
|
@@ -95,6 +117,7 @@ module Tabulo
|
|
95
117
|
DEFAULT_TRUNCATION_INDICATOR, InvalidTruncationIndicatorError, "truncation indicator")
|
96
118
|
|
97
119
|
@column_registry = { }
|
120
|
+
cols.each { |item| add_column(item) }
|
98
121
|
columns.each { |item| add_column(item) }
|
99
122
|
|
100
123
|
yield self if block_given?
|
@@ -109,13 +132,18 @@ module Tabulo
|
|
109
132
|
# for this column.
|
110
133
|
# @param [nil, #to_s] header (nil) Text to be displayed in the column header. If passed nil,
|
111
134
|
# the column's label will also be used as its header text.
|
112
|
-
# @param [:left, :center, :right] align_header (
|
113
|
-
# should be aligned.
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
#
|
135
|
+
# @param [:left, :center, :right, nil] align_header (nil) Specifies how the header text
|
136
|
+
# should be aligned. If <tt>nil</tt> is passed, then the alignment is determined
|
137
|
+
# by the Table-level setting passed to the <tt>align_header</tt> (which itself defaults
|
138
|
+
# to <tt>:center</tt>). Otherwise, this option determines the alignment of the header
|
139
|
+
# content for this column.
|
140
|
+
# @param [:left, :center, :right, :auto, nil] align_body (nil) Specifies how the cell body contents
|
141
|
+
# should be aligned. If <tt>nil</tt> is passed, then the alignment is determined
|
142
|
+
# by the Table-level setting passed to the <tt>align_body</tt> option on Table initialization
|
143
|
+
# (which itself defaults to <tt>:auto</tt>). Otherwise this option determines the alignment of
|
144
|
+
# this column. If <tt>:auto</tt> is passed, the alignment is determined by the type of the cell
|
145
|
+
# value, with numbers aligned right, booleans center-aligned, and other values left-aligned.
|
146
|
+
# Note header text alignment is configured separately using the :align_header param.
|
119
147
|
# @param [Integer] width (nil) Specifies the width of the column, excluding padding. If
|
120
148
|
# nil, then the column will take the width provided by the `column_width` param
|
121
149
|
# with which the Table was initialized.
|
@@ -132,7 +160,7 @@ module Tabulo
|
|
132
160
|
# @raise [InvalidColumnLabelError] if label has already been used for another column in this
|
133
161
|
# Table. (This is case-sensitive, but is insensitive to whether a String or Symbol is passed
|
134
162
|
# to the label parameter.)
|
135
|
-
def add_column(label, header: nil, align_header:
|
163
|
+
def add_column(label, header: nil, align_header: nil, align_body: nil,
|
136
164
|
width: nil, formatter: :to_s.to_proc, &extractor)
|
137
165
|
|
138
166
|
column_label = label.to_sym
|
@@ -144,8 +172,8 @@ module Tabulo
|
|
144
172
|
@column_registry[column_label] =
|
145
173
|
Column.new(
|
146
174
|
header: (header || label).to_s,
|
147
|
-
align_header: align_header,
|
148
|
-
align_body: align_body,
|
175
|
+
align_header: align_header || @align_header,
|
176
|
+
align_body: align_body || @align_body,
|
149
177
|
width: (width || @default_column_width),
|
150
178
|
formatter: formatter,
|
151
179
|
extractor: (extractor || label.to_proc)
|
@@ -221,19 +249,21 @@ module Tabulo
|
|
221
249
|
# is called. If the source Enumerable changes between that point, and the point when
|
222
250
|
# the Table is printed, then columns will *not* be resized yet again on printing.
|
223
251
|
#
|
224
|
-
# @param [nil, Numeric] max_table_width (
|
225
|
-
# width (including padding and borders) from expanding beyond
|
252
|
+
# @param [nil, Numeric] max_table_width (:auto) With no args, or if passed <tt>:auto</tt>,
|
253
|
+
# stops the total table width (including padding and borders) from expanding beyond the
|
254
|
+
# bounds of the terminal screen.
|
255
|
+
# If passed <tt>nil</tt>, the table width will not be capped.
|
226
256
|
# Width is deducted from columns if required to achieve this, with one character progressively
|
227
257
|
# deducted from the width of the widest column until the target is reached. When the
|
228
258
|
# table is printed, wrapping or truncation will then occur in these columns as required
|
229
|
-
# (depending on how they were configured).
|
230
|
-
# max_table_width, the table will always be left wide
|
231
|
-
# 1 character's width of content, 1 character of left padding and
|
232
|
-
# in each column, together with border characters (1 on each side
|
233
|
-
# adjacent columns). I.e. there is a certain width below width the
|
234
|
-
# shrink itself.
|
259
|
+
# (depending on how they were configured).
|
260
|
+
# Note that regardless of the value passed to max_table_width, the table will always be left wide
|
261
|
+
# enough to accommodate at least 1 character's width of content, 1 character of left padding and
|
262
|
+
# 1 character of right padding in each column, together with border characters (1 on each side
|
263
|
+
# of the table and 1 between adjacent columns). I.e. there is a certain width below width the
|
264
|
+
# Table will refuse to shrink itself.
|
235
265
|
# @return [Table] the Table itself
|
236
|
-
def
|
266
|
+
def pack(max_table_width: :auto)
|
237
267
|
return self if column_registry.none?
|
238
268
|
columns = column_registry.values
|
239
269
|
|
@@ -247,30 +277,48 @@ module Tabulo
|
|
247
277
|
end
|
248
278
|
|
249
279
|
if max_table_width
|
250
|
-
|
251
|
-
|
252
|
-
total_borders = column_registry.count + 1
|
253
|
-
unadjusted_table_width = total_columns_width + total_padding + total_borders
|
254
|
-
|
255
|
-
# Ensure max table width is at least wide enough to accommodate table borders and padding
|
256
|
-
# and one character of content.
|
257
|
-
min_table_width = total_padding + total_borders + column_registry.count
|
258
|
-
max_table_width = min_table_width if min_table_width > max_table_width
|
259
|
-
|
260
|
-
required_reduction = [unadjusted_table_width - max_table_width, 0].max
|
261
|
-
|
262
|
-
required_reduction.times do
|
263
|
-
widest_column = columns.inject(columns.first) do |widest, column|
|
264
|
-
column.width >= widest.width ? column : widest
|
265
|
-
end
|
266
|
-
|
267
|
-
widest_column.width -= 1
|
268
|
-
end
|
280
|
+
max_table_width = TTY::Screen.width if max_table_width == :auto
|
281
|
+
shrink_to(max_table_width)
|
269
282
|
end
|
270
283
|
|
271
284
|
self
|
272
285
|
end
|
273
286
|
|
287
|
+
# @deprecated Use {#pack} instead.
|
288
|
+
#
|
289
|
+
# Reset all the column widths so that each column is *just* wide enough to accommodate
|
290
|
+
# its header text as well as the formatted content of each its cells for the entire
|
291
|
+
# collection, together with a single character of padding on either side of the column,
|
292
|
+
# without any wrapping.
|
293
|
+
#
|
294
|
+
# Note that calling this method will cause the entire source Enumerable to
|
295
|
+
# be traversed and all the column extractors and formatters to be applied in order
|
296
|
+
# to calculate the required widths.
|
297
|
+
#
|
298
|
+
# Note also that this method causes column widths to be fixed as appropriate to the
|
299
|
+
# formatted cell contents given the state of the source Enumerable at the point it
|
300
|
+
# is called. If the source Enumerable changes between that point, and the point when
|
301
|
+
# the Table is printed, then columns will *not* be resized yet again on printing.
|
302
|
+
#
|
303
|
+
# @param [nil, Numeric] max_table_width (nil) If provided, stops the total table
|
304
|
+
# width (including padding and borders) from expanding beyond this number of characters.
|
305
|
+
# If passed <tt>:auto</tt>, the table width will automatically be capped at the current
|
306
|
+
# terminal width.
|
307
|
+
# Width is deducted from columns if required to achieve this, with one character progressively
|
308
|
+
# deducted from the width of the widest column until the target is reached. When the
|
309
|
+
# table is printed, wrapping or truncation will then occur in these columns as required
|
310
|
+
# (depending on how they were configured).
|
311
|
+
# Note that regardless of the value passed to max_table_width, the table will always be left wide
|
312
|
+
# enough to accommodate at least 1 character's width of content, 1 character of left padding and
|
313
|
+
# 1 character of right padding in each column, together with border characters (1 on each side
|
314
|
+
# of the table and 1 between adjacent columns). I.e. there is a certain width below width the
|
315
|
+
# Table will refuse to shrink itself.
|
316
|
+
# @return [Table] the Table itself
|
317
|
+
def shrinkwrap!(max_table_width: nil)
|
318
|
+
Deprecation.warn("`Tabulo::Table#shrinkwrap!'", "`#pack'")
|
319
|
+
pack(max_table_width: max_table_width)
|
320
|
+
end
|
321
|
+
|
274
322
|
# @!visibility private
|
275
323
|
def formatted_body_row(source, with_header: false)
|
276
324
|
cells = column_registry.map { |_, column| column.body_subcells(source) }
|
@@ -284,6 +332,29 @@ module Tabulo
|
|
284
332
|
|
285
333
|
private
|
286
334
|
|
335
|
+
# @!visibility private
|
336
|
+
def shrink_to(max_table_width)
|
337
|
+
columns = column_registry.values
|
338
|
+
total_columns_width = columns.inject(0) { |sum, column| sum + column.width }
|
339
|
+
total_padding = column_registry.count * @column_padding * 2
|
340
|
+
total_borders = column_registry.count + 1
|
341
|
+
unadjusted_table_width = total_columns_width + total_padding + total_borders
|
342
|
+
|
343
|
+
# Ensure max table width is at least wide enough to accommodate table borders and padding
|
344
|
+
# and one character of content.
|
345
|
+
min_table_width = total_padding + total_borders + column_registry.count
|
346
|
+
max_table_width = min_table_width if min_table_width > max_table_width
|
347
|
+
required_reduction = [unadjusted_table_width - max_table_width, 0].max
|
348
|
+
|
349
|
+
required_reduction.times do
|
350
|
+
widest_column = columns.inject(columns.first) do |widest, column|
|
351
|
+
column.width >= widest.width ? column : widest
|
352
|
+
end
|
353
|
+
|
354
|
+
widest_column.width -= 1
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
287
358
|
# @!visibility private
|
288
359
|
def body_row(source, with_header: false)
|
289
360
|
Row.new(self, source, with_header: with_header)
|
data/lib/tabulo/version.rb
CHANGED
data/tabulo.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Matthew Harvey"]
|
10
10
|
spec.email = ["software@matthewharvey.net"]
|
11
11
|
|
12
|
-
spec.summary = "Enumerable ASCII table"
|
13
|
-
spec.description = "Enumerable ASCII table"
|
12
|
+
spec.summary = "Enumerable ASCII terminal table"
|
13
|
+
spec.description = "Enumerable ASCII terminal table"
|
14
14
|
spec.homepage = "https://github.com/matt-harvey/tabulo"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
@@ -28,6 +28,8 @@ Gem::Specification.new do |spec|
|
|
28
28
|
"changelog_uri" => "https://raw.githubusercontent.com/matt-harvey/tabulo/master/CHANGELOG.md"
|
29
29
|
}
|
30
30
|
|
31
|
+
spec.add_runtime_dependency "tty-screen", "0.6.5"
|
32
|
+
|
31
33
|
spec.add_development_dependency "bundler"
|
32
34
|
spec.add_development_dependency "rake", "~> 11.0"
|
33
35
|
spec.add_development_dependency "rspec", "~> 3.0"
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tabulo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Harvey
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tty-screen
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.6.5
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.6.5
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,7 +150,7 @@ dependencies:
|
|
136
150
|
- - "~>"
|
137
151
|
- !ruby/object:Gem::Version
|
138
152
|
version: '1.0'
|
139
|
-
description: Enumerable ASCII table
|
153
|
+
description: Enumerable ASCII terminal table
|
140
154
|
email:
|
141
155
|
- software@matthewharvey.net
|
142
156
|
executables: []
|
@@ -160,6 +174,7 @@ files:
|
|
160
174
|
- bin/setup
|
161
175
|
- lib/tabulo.rb
|
162
176
|
- lib/tabulo/column.rb
|
177
|
+
- lib/tabulo/deprecation.rb
|
163
178
|
- lib/tabulo/exceptions.rb
|
164
179
|
- lib/tabulo/row.rb
|
165
180
|
- lib/tabulo/table.rb
|
@@ -190,5 +205,5 @@ rubyforge_project:
|
|
190
205
|
rubygems_version: 2.7.6
|
191
206
|
signing_key:
|
192
207
|
specification_version: 4
|
193
|
-
summary: Enumerable ASCII table
|
208
|
+
summary: Enumerable ASCII terminal table
|
194
209
|
test_files: []
|