tabulo 2.7.0 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 632d2d2d3b7d0120410d0afd2b8f1d1b341fd1b6daa7802b15b6f791640d409e
4
- data.tar.gz: f5720b528f1c4384118196619ea923b317ec3ff7d374fa87d79808361031e7cc
3
+ metadata.gz: d58a5b883e9c9685c3cff492c6ad49b88ce921c5947596c5fe9199a5818dbfab
4
+ data.tar.gz: d9e2d10ea6314b73bdc3ad5a41bf790211ad7abe1e42899c52bc8bf2b405e8e6
5
5
  SHA512:
6
- metadata.gz: 283e3ac39971cfdf9df9968fe0e40b00d049865fe7f49ef0403d71e35ea8f49a43d85579cb9f12d080e0bacafa48c943d2e57176309be99809272983cd9db2d7
7
- data.tar.gz: 32256e95de32d81a65d64e49a6f1eb42c33d7ccca2c76a5d6ce5cb6d0beee8c9d5588aa6d5df2c68a59ac8805e5b5048851a0959ddf281eaf891771ee162158c
6
+ metadata.gz: 3fb526cb0ddc9d63033eb40aa81a0aa2f39a306e051074693fcc4d08c95a216d13ffa27c92a1eb7bed08d3daef431bff6d15faa839b87580f830b916409c46a7
7
+ data.tar.gz: 587ba915a07b2e6d67d58296a7147fab9017d038facf0976a628baa71f8ebdf6a08f3d25559ca372630026452d65d12f83ced616c78cd33fca9e7e31ff0ed597
@@ -23,9 +23,10 @@ jobs:
23
23
  '2.3.8',
24
24
  '2.4.10',
25
25
  '2.5.9',
26
- '2.6.7',
27
- '2.7.3',
28
- '3.0.1',
26
+ '2.6.9',
27
+ '2.7.5',
28
+ '3.0.3',
29
+ '3.1.0',
29
30
  ]
30
31
  steps:
31
32
  - uses: actions/checkout@v2
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
  **/.*.swp
11
11
  coverage
12
12
  gh-md-toc
13
+ .ruby-version
data/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ### v2.8.0
4
+
5
+ * Add `except:` param to `Tabulo::Table#pack` method, allowing specific
6
+ columns to be excluded from the action of `.pack`
7
+ * Provide method `Tabulo::Table#autosize_columns`, allowing columns to be auto-sized
8
+ to fit their contents' widths, without having to call `.pack` (which also has
9
+ other effects on the table). This method also has an `except:` param allowing columns
10
+ to be excluded from its action.
11
+ * Provide method `Tabulo::Table#shrink_to`, allowing the table's width to be reduced
12
+ so as not to exceed a given target number of characters (or the argument `:screen`
13
+ meaning "width of terminal"), independently of the `.pack` method.
14
+ This method also has an `except:` param allowing columns to be excluded from its action.
15
+ * Fix `max_table_width:` param to `.pack` not being respected if table title was
16
+ wider than terminal.
17
+ * Documentation improvements
18
+ * Fix broken documentation links
19
+ * Add Ruby 3.1 to CI config
20
+
21
+ ### v2.7.3
22
+
23
+ * Fix malformed YARD documentation for `Tabulo::Table#initialize` method
24
+
25
+ ### v2.7.2
26
+
27
+ * Minor documentation improvements and tweaks
28
+ * Upgrade Ruby patch versions in CI config
29
+
30
+ ### v2.7.1
31
+
32
+ * Dependency version upgrades
33
+ * Minor documentation improvements and tweaks
34
+
3
35
  ### v2.7.0
4
36
 
5
37
  * Add `wrap_preserve` option, allowing whole words to be preserved when wrapping.
data/README.md CHANGED
@@ -4,11 +4,9 @@
4
4
  [![Documentation][DC img]][Documentation]
5
5
  [![Build Status][BS img]][Build Status]
6
6
  [![Coverage Status][CS img]][Coverage Status]
7
- [![Code Climate][CC img]][Code Climate]
8
7
  [![Awesome][AR img]][Awesome Ruby]
9
8
 
10
- Tabulo is a Ruby library for generating plain text tables (also known as “terminal tables”
11
- or “ASCII tables”). It is both highly configurable and very easy to use.
9
+ Tabulo is a Ruby library for generating plain text tables. It is both highly configurable and very easy to use.
12
10
 
13
11
  <a name="overview"></a>
14
12
  ## Overview
@@ -76,15 +74,16 @@ Tabulo has also been ported to Crystal (with some modifications): see [Tablo](ht
76
74
 
77
75
  * [Overview](#overview)
78
76
  * [Features](#features)
79
- * [Table of contents](#table-of-contents)
77
+ * [Table of contents](#contents)
80
78
  * [Installation](#installation)
81
79
  * [Detailed usage](#detailed-usage)
82
80
  * [Creating a table](#table-initialization)
83
81
  * [Adding columns](#adding-columns)
84
82
  * [Quick API](#quick-api)
85
- * [Full API](#quick-api)
83
+ * [Full API](#full-api)
86
84
  * [Column labels _vs_ headers](#labels-headers)
87
85
  * [Positioning columns](#column-positioning)
86
+ * [Extracting column content from a hash or array](#from-arrays-hashes)
88
87
  * [Removing columns](#removing-columns)
89
88
  * [Adding a title](#title)
90
89
  * [Cell alignment](#cell-alignment)
@@ -93,6 +92,7 @@ Tabulo has also been ported to Crystal (with some modifications): see [Tablo](ht
93
92
  * [Automating column widths](#automating-column-widths)
94
93
  * [Configuring padding](#configuring-padding)
95
94
  * [Overflow handling](#overflow-handling)
95
+ * [Wrapping at word boundaries](#preserve-words)
96
96
  * [Manual cell wrapping](#manual-wrapping)
97
97
  * [Formatting cell values](#formatting-cell-values)
98
98
  * [Colours and other styling](#colours-and-styling)
@@ -102,7 +102,7 @@ Tabulo has also been ported to Crystal (with some modifications): see [Tablo](ht
102
102
  * [Setting default styles](#default-styles)
103
103
  * [Styling borders](#styling-borders)
104
104
  * [Repeating headers](#repeating-headers)
105
- * [Using a Table Enumerator](#using-a-table-enumerator)
105
+ * [Using a Table Enumerator](#enumerator)
106
106
  * [Accessing cell values](#accessing-cell-values)
107
107
  * [Accessing the underlying enumerable](#accessing-sources)
108
108
  * [Transposing rows and columns](#transposition)
@@ -113,6 +113,7 @@ Tabulo has also been ported to Crystal (with some modifications): see [Tablo](ht
113
113
  * [Contributing](#contributing)
114
114
  * [License](#license)
115
115
 
116
+ <a name="installation"></a>
116
117
  ## Installation [&#x2191;](#contents)
117
118
 
118
119
  Add this line to your application&#8217;s Gemfile:
@@ -135,6 +136,7 @@ To use the gem, you need to require it in your source code as follows:
135
136
  require 'tabulo'
136
137
  ```
137
138
 
139
+ <a name="detailed-usage"></a>
138
140
  <a name="table-initialization"></a>
139
141
  ### Creating a table [&#x2191;](#contents)
140
142
 
@@ -281,6 +283,69 @@ table.add_column(:even?, before: :odd?)
281
283
  +--------------+--------------+--------------+
282
284
  ```
283
285
 
286
+ <a name="from-arrays-hashes"></a>
287
+ #### Extracting column content from a hash or array [&#x2191;](#contents)
288
+
289
+ Sometimes the data source for the table may be a collection of hashes or arrays. For example:
290
+
291
+ ```ruby
292
+ data = [
293
+ { english: "hello", portuguese: "bom dia" },
294
+ { english: "goodbye", portuguese: "adeus" },
295
+ ]
296
+ ```
297
+
298
+ or
299
+
300
+ ```ruby
301
+ data = [
302
+ ["hello", "bom dia"],
303
+ ["goodbye", "adeus"],
304
+ ]
305
+ ```
306
+
307
+ To tabulate such a collection, simply use the same mechanism as described above, passing a block to
308
+ the `add_column` method to tell Tabulo how to extract the data for each column from a row. For
309
+ example, to tabulate the first example above, you could do something like this:
310
+
311
+ ```ruby
312
+ table = Tabulo::Table.new(data) do |t|
313
+ t.add_column("English") { |h| h[:english] }
314
+ t.add_column("Portuguese") { |h| h[:portuguese] }
315
+ end
316
+
317
+ puts table
318
+ ```
319
+
320
+ For the second example, you could do the following:
321
+
322
+ ```ruby
323
+ table = Tabulo::Table.new(data) do |t|
324
+ t.add_column("English") { |a| a[0] }
325
+ t.add_column("Portuguese") { |a| a[1] }
326
+ end
327
+
328
+ puts table
329
+ ```
330
+
331
+ In both cases, the output will be as follows:
332
+
333
+ ```
334
+ +--------------+--------------+
335
+ | English | Portuguese |
336
+ +--------------+--------------+
337
+ | hello | bom dia |
338
+ | goodbye | adeus |
339
+ +--------------+--------------+
340
+ ```
341
+
342
+ If you have previously used other terminal tabulation libraries, you may be accustomed to being _required_
343
+ to place your data into an array of hashes or arrays before you can tabulate them. Tabulo, however,
344
+ offers an API that is more general and flexible than this; your data source can be _any_
345
+ enumerable collection (not just an array), and each item in that collection can be _any_ object (not
346
+ necessarily an array or a hash). However, as shown above, it is still straightforward to tabulate an
347
+ array of hashes or arrays, if your data source happens to take that form.
348
+
284
349
  <a name="removing-columns"></a>
285
350
  ### Removing columns [&#x2191;](#contents)
286
351
 
@@ -345,9 +410,11 @@ If a table title is present, it is center-aligned by default. This can be change
345
410
  table = Tabulo::Table.new([1, 2], :itself, :even?, title: "Numbers", align_title: :left)
346
411
  ```
347
412
 
413
+ <a name="column-width-wrapping-and-truncation"></a>
348
414
  ### Column width, wrapping and truncation [&#x2191;](#contents)
349
415
 
350
416
  <a name="fixed-column-widths"></a>
417
+ <a name="configuring-fixed-widths"></a>
351
418
  #### Configuring fixed widths [&#x2191;](#contents)
352
419
 
353
420
  By default, column width is fixed at 12 characters, plus 1 character of padding on either side.
@@ -390,6 +457,7 @@ table = Tabulo::Table.new([1, 2], :itself, :even?, column_width: 6)
390
457
  Widths set for individual columns always override the default column width for the table.
391
458
 
392
459
  <a name="pack"></a>
460
+ <a name="automating-column-widths"></a>
393
461
  #### Automating column widths [&#x2191;](#contents)
394
462
 
395
463
  Instead of setting column widths &ldquo;manually&rdquo;, you can tell the table to sort out the widths
@@ -466,6 +534,16 @@ necessary (see [Overflow handling](#overflow-handling)). Under the hood, a chara
466
534
  is deducted column by column&mdash;the widest column being targetted each time&mdash;until
467
535
  the table will fit.
468
536
 
537
+ To resize only specific columns, `pack` takes an `except:` argument, which can be a single column
538
+ label or an Array of column labels. E.g. `pack(except: :id)` will exclude the `id` column from
539
+ resizing and let it keep its current width. This is useful if you want to prevent the addition of
540
+ linebreaks in your data. When using this option, other columns might be shrunk more to still make
541
+ the table fit within the `max_table_width`.
542
+
543
+ For even finer-grained control over column and table resizing, see the
544
+ for the [`#autosize_columns`](https://www.rubydoc.info/gems/tabulo/2.8.0/Tabulo/Table#autosize_columns-instance_method)
545
+ and [`#shrink_to`](https://www.rubydoc.info/gems/tabulo/2.8.0/Tabulo/Table#autosize_columns-instance_method) methods.
546
+
469
547
  Note that `pack`ing the table necessarily involves traversing the entire collection up front as
470
548
  the maximum cell width needs to be calculated for each column. You may not want to do this
471
549
  if the collection is very large.
@@ -729,7 +807,7 @@ the table.
729
807
  The `formatter` callback also has an alternative, 2-parameter version. If `formatter` is passed
730
808
  a 2-parameter callable, the second parameter will be given a `CellData` instance,
731
809
  containing additional information about the cell that may be useful in determining how to format
732
- it&mdash;see the [documentation](https://www.rubydoc.info/gems/tabulo/2.7.0/Tabulo/CellData.html)
810
+ it&mdash;see the [documentation](https://www.rubydoc.info/gems/tabulo/2.8.0/Tabulo/CellData.html)
733
811
  for details.
734
812
 
735
813
  <a name="colours-and-styling"></a>
@@ -775,7 +853,7 @@ number is even). The second parameter represents the formatted string value of t
775
853
  content after any processing by the [formatter](#formatting-cell-values). The third and fourth
776
854
  parameters are optional, and contain further information about the cell and its contents that may be useful in
777
855
  determining how to style it. See the
778
- [documentation](https://www.rubydoc.info/gems/tabulo/2.7.0/Tabulo/Table#add_column-instance_method) for details.
856
+ [documentation](https://www.rubydoc.info/gems/tabulo/2.8.0/Tabulo/Table#add_column-instance_method) for details.
779
857
 
780
858
  If the content of a cell is wrapped over multiple lines, then the `styler` will be called once
781
859
  per line, so that each line of the cell will have the escape sequence applied to it separately
@@ -795,7 +873,7 @@ table.add_column(:even?, header_styler: -> (s) { "\033[32m#{s}\033[0m" })
795
873
  ```
796
874
 
797
875
  The `header_styler` option accepts a 1-, 2- or 3-parameter callable. See the
798
- [documentation](https://www.rubydoc.info/gems/tabulo/2.7.0/Tabulo/Table#add_column-instance_method)
876
+ [documentation](https://www.rubydoc.info/gems/tabulo/2.8.0/Tabulo/Table#add_column-instance_method)
799
877
  for details.
800
878
 
801
879
  <a name="styling-title"></a>
@@ -809,7 +887,7 @@ table = Tabulo::Table.new(1..5, :itself, :even?, :odd?, title: "Numbers", title_
809
887
  ```
810
888
 
811
889
  The `title_styler` option accepts a 1- or 2-parameter callable. See the
812
- [documentation](https://www.rubydoc.info/gems/tabulo/2.7.0/Tabulo/Table#initialize-instance_method)
890
+ [documentation](https://www.rubydoc.info/gems/tabulo/2.8.0/Tabulo/Table#initialize-instance_method)
813
891
  for details.
814
892
 
815
893
  <a name="styling-borders"></a>
@@ -875,6 +953,12 @@ table = Tabulo::Table.new(1..10, :itself, :even?, header_frequency: 5)
875
953
 
876
954
  Note that if the table has a [title](#title), it will not be repeated; only column headers are repeated.
877
955
 
956
+ One can achieve even finer-grained control over printing of headers within the table body by stepping
957
+ through the table a row at a time (using `.each` or other methods of `Enumerable`) and calling the
958
+ the [`formatted_header`](https://www.rubydoc.info/gems/tabulo/Tabulo/Table#formatted_header-instance_method)
959
+ method in combination with [`horizontal_rule`](https://www.rubydoc.info/gems/tabulo/Tabulo%2FTable:horizontal_rule)
960
+ to produce headers at arbitrary points in the output.
961
+
878
962
  <a name="enumerator"></a>
879
963
  ### Using a Table Enumerator [&#x2191;](#contents)
880
964
 
@@ -1004,7 +1088,7 @@ a new table in which the rows and columns are swapped:
1004
1088
  By default, a header row is added to the new table, showing the string value of the element
1005
1089
  represented in that column. This can be configured, however, along with other aspects of
1006
1090
  `transpose`&#8217;s behaviour. For details, see the
1007
- [documentation](https://www.rubydoc.info/gems/tabulo/2.7.0/Tabulo/Table#transpose-instance_method).
1091
+ [documentation](https://www.rubydoc.info/gems/tabulo/2.8.0/Tabulo/Table#transpose-instance_method).
1008
1092
 
1009
1093
  <a name="borders"></a>
1010
1094
  ### Configuring borders [&#x2191;](#contents)
@@ -1222,6 +1306,11 @@ If you want a line before every row, pass `1` to `row_divider_frequency`. For ex
1222
1306
  +--------------+--------------+--------------+
1223
1307
  ```
1224
1308
 
1309
+ In addition to these options, it is also possible to print horizontal dividers at any chosen
1310
+ point in the table output, by stepping through the table one row at a time
1311
+ and calling the [`horizontal_rule`](https://www.rubydoc.info/gems/tabulo/Tabulo%2FTable:horizontal_rule)
1312
+ method as required.
1313
+
1225
1314
  <a name="freezing-a-table"></a>
1226
1315
  ### Using a table as a snapshot rather than as a dynamic view [&#x2191;](#contents)
1227
1316
 
@@ -1389,21 +1478,20 @@ install dependencies.
1389
1478
  `bundle exec rake spec` will run the test suite. For a list of other Rake tasks that are available in
1390
1479
  the development environment, run `bundle exec rake -T`.
1391
1480
 
1481
+ <a name="license"></a>
1392
1482
  ## License [&#x2191;](#contents)
1393
1483
 
1394
1484
  The gem is available as open source under the terms of the [MIT
1395
1485
  License](http://opensource.org/licenses/MIT).
1396
1486
 
1397
1487
  [Gem Version]: https://rubygems.org/gems/tabulo
1398
- [Documentation]: http://www.rubydoc.info/gems/tabulo/2.7.0
1488
+ [Documentation]: http://www.rubydoc.info/gems/tabulo
1399
1489
  [Build Status]: https://github.com/matt-harvey/tabulo/actions/workflows/tests.yml
1400
1490
  [Coverage Status]: https://coveralls.io/github/matt-harvey/tabulo
1401
- [Code Climate]: https://codeclimate.com/github/matt-harvey/tabulo
1402
1491
  [Awesome Ruby]: https://github.com/markets/awesome-ruby#cli-utilities
1403
1492
 
1404
1493
  [GV img]: https://img.shields.io/gem/v/tabulo.svg
1405
- [DC img]: https://img.shields.io/badge/documentation-v2.7.0-blue.svg
1494
+ [DC img]: https://img.shields.io/badge/documentation-v2.8.0-blue.svg
1406
1495
  [BS img]: https://github.com/matt-harvey/tabulo/actions/workflows/tests.yml/badge.svg
1407
1496
  [CS img]: https://img.shields.io/coveralls/matt-harvey/tabulo.svg
1408
- [CC img]: https://codeclimate.com/github/matt-harvey/tabulo/badges/gpa.svg
1409
1497
  [AR img]: https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.7.0
1
+ 2.8.0
data/lib/tabulo/table.rb CHANGED
@@ -125,7 +125,7 @@ module Tabulo
125
125
  # defaults to {DEFAULT_TRUNCATION_INDICATOR}. If passed something other than <tt>nil</tt> or
126
126
  # a single-character String, raises {InvalidTruncationIndicatorError}.
127
127
  # @param [Symbol] wrap_preserve Specifies what unit of text the wrapping mechanism will try to
128
- # preserve intact when wrapping column content when the column width is reached.
128
+ # preserve intact when wrapping column content when the column width is reached:
129
129
  # * If passed `:rune` (the default), then it will wrap at the "character" level (approximately
130
130
  # speaking, the Unicode grapheme cluster level). This means the maximum number of what
131
131
  # readers usually think of as "characters" will be fit on each line, within the column's allocated
@@ -207,11 +207,10 @@ module Tabulo
207
207
  # by the Table-level setting passed to the <tt>align_header</tt> (which itself defaults
208
208
  # to <tt>:center</tt>). Otherwise, this option determines the alignment of the header
209
209
  # content for this column.
210
- # @param [Symbol, String, Integer, nil] before The label of the column before (i.e. to
211
- # the left of) which the new column should inserted. If <tt>nil</tt> is passed, it will be
212
- # inserted after all other columns. If there is no column with the given label, then an
213
- # {InvalidColumnLabelError} will be raised. A non-Integer labelled column can be identified
214
- # in either String or Symbol form for this purpose.
210
+ # @param [Symbol, Integer, nil] before The label of the column before (i.e. to the left of) which the
211
+ # new column should inserted. If <tt>nil</tt> is passed, it will be inserted after all other columns.
212
+ # If there is no column with the given label, then an {InvalidColumnLabelError} will be raised.
213
+ # A non-Integer labelled column can be identified in Symbol form for this purpose.
215
214
  # @param [#to_proc] formatter A lambda or other callable object that
216
215
  # will be passed the calculated value of each cell to determine how it should be displayed. This
217
216
  # is distinct from the extractor and the styler (see below).
@@ -420,7 +419,7 @@ module Tabulo
420
419
  end
421
420
 
422
421
  # @return [String] a graphical representation of the Table column headers formatted with fixed
423
- # width plain text.
422
+ # width plain text, excluding any horizontal borders above or below.
424
423
  def formatted_header
425
424
  cells = get_columns.map(&:header_cell)
426
425
  format_row(cells, @wrap_header_cells_to)
@@ -453,8 +452,10 @@ module Tabulo
453
452
 
454
453
  # Resets all the column widths so that each column is *just* wide enough to accommodate
455
454
  # its header text as well as the formatted content of each its cells for the entire
456
- # collection, together with a single character of padding on either side of the column,
457
- # without any wrapping. In addition, if the table has a title but is not wide enough to
455
+ # collection, together with padding (by default 1 character on either side of the column),
456
+ # without wrapping. Other adjustments may also be performed (see below).
457
+ #
458
+ # In addition, if the table has a title but is not wide enough to
458
459
  # accommodate (without wrapping) the title text (with a character of padding either side),
459
460
  # widens the columns roughly evenly until the table as a whole is just wide enough to
460
461
  # accommodate the title text.
@@ -468,7 +469,7 @@ module Tabulo
468
469
  # is called. If the source Enumerable changes between that point, and the point when
469
470
  # the Table is printed, then columns will *not* be resized yet again on printing.
470
471
  #
471
- # @param [nil, Numeric] max_table_width With no args, or if passed <tt>:auto</tt>,
472
+ # @param [nil, :auto, Numeric] max_table_width With no args, or if passed <tt>:auto</tt>,
472
473
  # stops the total table width (including padding and borders) from expanding beyond the
473
474
  # bounds of the terminal screen.
474
475
  # If passed <tt>nil</tt>, the table width will not be capped.
@@ -476,34 +477,104 @@ module Tabulo
476
477
  # deducted from the width of the widest column until the target is reached. When the
477
478
  # table is printed, wrapping or truncation will then occur in these columns as required
478
479
  # (depending on how they were configured).
479
- # Note that regardless of the value passed to max_table_width, the table will always be left wide
480
- # enough to accommodate at least 1 character's width of content, 1 character of left padding and
481
- # 1 character of right padding in each column, together with border characters (1 on each side
482
- # of the table and 1 between adjacent columns). I.e. there is a certain width below width the
480
+ # Note that regardless of the value passed to `max_table_width`, the table will always be left wide
481
+ # enough to accommodate at least 1 character's width of content for each column, and the padding
482
+ # configured for each column (by default 1 character either side), together with border characters
483
+ # (1 on each side of the table and 1 between adjacent columns). I.e. there is a certain width below width the
483
484
  # Table will refuse to shrink itself.
485
+ # @param [nil, Symbol, Integer, Array[Symbol|Integer]] except If passed one or multiple column labels,
486
+ # these columns will be excluded from resizing and will keep their current width.
487
+ # (Note if passing integers, these are not necessarily positional: only columns _explicitly_
488
+ # given an integer label will have these as labels.)
484
489
  # @return [Table] the Table itself
485
- def pack(max_table_width: :auto)
486
- get_columns.each { |column| column.width = Util.wrapped_width(column.header) }
490
+ def pack(max_table_width: :auto, except: nil)
491
+ autosize_columns(except: except)
492
+
493
+ max_width = nil
494
+ if max_table_width
495
+ max_width = (max_table_width == :auto ? TTY::Screen.width : max_table_width)
496
+ shrink_to(max_width, except: except)
497
+ end
498
+
499
+ if @title
500
+ border_edge_width = (@border == :blank ? 0 : 2)
501
+ all_columns = get_columns
502
+ min_width =
503
+ Unicode::DisplayWidth.of(@title) +
504
+ all_columns.first.left_padding +
505
+ all_columns.last.right_padding +
506
+ border_edge_width
487
507
 
508
+ min_width = max_width if max_width && max_width < min_width
509
+ expand_to(min_width, except: except)
510
+ end
511
+
512
+ self
513
+ end
514
+
515
+ # Resets all the column widths so that each column is *just* wide enough to accommodate
516
+ # its header text as well as the formatted content of each its cells for the entire
517
+ # collection, together with padding (by default 1 character either side), without wrapping.
518
+ #
519
+ # @param [nil, Symbol, Integer, Array[Symbol|Integer]] except If passed one or multiple column labels,
520
+ # these columns will be excluded from resizing and will keep their current width.
521
+ # (Note if using integers, these are not necessarily positional: only columns _explicitly_
522
+ # given an integer label will have these as labels.)
523
+ # @return [Table] the Table itself
524
+ def autosize_columns(except: nil)
525
+ columns = get_columns(except: except)
526
+ columns.each { |column| column.width = Util.wrapped_width(column.header) }
488
527
  @sources.each_with_index do |source, row_index|
489
- get_columns.each_with_index do |column, column_index|
528
+ columns.each_with_index do |column, column_index|
490
529
  cell = column.body_cell(source, row_index: row_index, column_index: column_index)
491
530
  cell_width = Util.wrapped_width(cell.formatted_content)
492
531
  column.width = Util.max(column.width, cell_width)
493
532
  end
494
533
  end
534
+ self
535
+ end
495
536
 
496
- shrink_to(max_table_width == :auto ? TTY::Screen.width : max_table_width) if max_table_width
537
+ # If `max_table_width` is passed an integer, then column widths will be adjusted downward so
538
+ # that the total table width is reduced to the passed target width. (If the total width of the
539
+ # table exceeds the passed `max_table_width`, then this method is a no-op.)
540
+ #
541
+ # Width is deducted from columns if required to achieve this, with one character progressively
542
+ # deducted from the width of the widest column until the target is reached. When the
543
+ # table is printed, wrapping or truncation will then occur in these columns as required
544
+ # (depending on how they were configured).
545
+ #
546
+ # Note that regardless of the value passed to `max_table_width`, the table will always be left wide
547
+ # enough to accommodate at least 1 character's width of content for each column, and the padding
548
+ # configured for each column (by default 1 character either side), together with border characters
549
+ # (1 on each side of the table and 1 between adjacent columns). I.e. there is a certain width below width the
550
+ # Table will refuse to shrink itself.
551
+ #
552
+ # If `max_table_width` is passed the symbol `:screen`, then this method will behave as if it
553
+ # were passed an integer, with that integer being the width of the terminal.
554
+ #
555
+ # @param [Integer, :screen] the desired maximum table width
556
+ # @param [nil, Symbol, Integer, Array[Symbol|Integer]] except If passed one or multiple column labels,
557
+ # these columns will be excluded from resizing and will keep their current width.
558
+ # (Note if passing integers, these are not necessarily positional: only columns _explicitly_
559
+ # given an integer label will have these as labels.)
560
+ # @return [Table] the Table itself
561
+ def shrink_to(max_table_width, except: nil)
562
+ min_content_width_per_column = 1
563
+ min_total_column_content_width = num_columns * min_content_width_per_column
564
+ min_table_width = total_padding_width + total_borders_width + min_total_column_content_width
497
565
 
498
- if @title
499
- border_edge_width = (@border == :blank ? 0 : 2)
500
- columns = get_columns
501
- expand_to(
502
- Unicode::DisplayWidth.of(@title) +
503
- columns.first.left_padding +
504
- columns.last.right_padding +
505
- border_edge_width
506
- )
566
+ max_table_width = (max_table_width == :screen ? TTY::Screen.width : max_table_width)
567
+ max_table_width = Util.max(min_table_width, max_table_width)
568
+
569
+ required_reduction = Util.max(total_table_width - max_table_width, 0)
570
+
571
+ shrinkable_columns = get_columns(except: except)
572
+ required_reduction.times do
573
+ widest_column = shrinkable_columns.inject(shrinkable_columns.first) do |widest, column|
574
+ column.width >= widest.width ? column : widest
575
+ end
576
+
577
+ widest_column.width -= 1
507
578
  end
508
579
 
509
580
  self
@@ -609,8 +680,13 @@ module Tabulo
609
680
  private
610
681
 
611
682
  # @!visibility private
612
- def get_columns
613
- column_registry.values
683
+ def get_columns(except: nil)
684
+ if except
685
+ column_labels = except ? column_registry.keys - Array(except) : column_registry.keys
686
+ column_labels.map { |label| column_registry[label] }
687
+ else
688
+ column_registry.values
689
+ end
614
690
  end
615
691
 
616
692
  # @!visibility private
@@ -695,16 +771,35 @@ module Tabulo
695
771
  end
696
772
 
697
773
  # @!visibility private
698
- def expand_to(min_table_width)
699
- columns = get_columns
700
- num_columns = columns.count
701
- total_columns_padded_width = columns.inject(0) { |sum, column| sum + column.padded_width }
702
- total_borders = num_columns + 1
703
- unadjusted_table_width = total_columns_padded_width + total_borders
704
- required_increase = Util.max(min_table_width - unadjusted_table_width, 0)
774
+ # @return [Integer] the total combined width of padding characters
775
+ def total_padding_width
776
+ get_columns.inject(0) { |sum, column| sum + column.total_padding }
777
+ end
705
778
 
779
+ # @!visibility private
780
+ # @return [Integer] the total combined width of vertical border characters
781
+ def total_borders_width
782
+ num_columns + 1
783
+ end
784
+
785
+ # @!visibility private
786
+ # @return [Integer] the total combined width of column contents (excludes borders and padding)
787
+ def total_column_content_width
788
+ get_columns.inject(0) { |sum, column| sum + column.width }
789
+ end
790
+
791
+ # @!visibility private
792
+ # @return [Integer] the total actual width of the table as a whole
793
+ def total_table_width
794
+ total_column_content_width + total_padding_width + total_borders_width
795
+ end
796
+
797
+ # @!visibility private
798
+ def expand_to(min_table_width, except: nil)
799
+ required_increase = Util.max(min_table_width - total_table_width, 0)
800
+ expandable_columns = get_columns(except: except)
706
801
  required_increase.times do
707
- narrowest_column = columns.inject(columns.first) do |narrowest, column|
802
+ narrowest_column = expandable_columns.inject(expandable_columns.first) do |narrowest, column|
708
803
  column.width <= narrowest.width ? column : narrowest
709
804
  end
710
805
 
@@ -713,27 +808,8 @@ module Tabulo
713
808
  end
714
809
 
715
810
  # @!visibility private
716
- def shrink_to(max_table_width)
717
- columns = get_columns
718
- num_columns = columns.count
719
- total_columns_padded_width = columns.inject(0) { |sum, column| sum + column.padded_width }
720
- total_padding = columns.inject(0) { |sum, column| sum + column.total_padding }
721
- total_borders = num_columns + 1
722
- unadjusted_table_width = total_columns_padded_width + total_borders
723
-
724
- # Ensure max table width is at least wide enough to accommodate table borders and padding
725
- # and one character of content.
726
- min_table_width = total_padding + total_borders + column_registry.count
727
- max_table_width = Util.max(min_table_width, max_table_width)
728
- required_reduction = Util.max(unadjusted_table_width - max_table_width, 0)
729
-
730
- required_reduction.times do
731
- widest_column = columns.inject(columns.first) do |widest, column|
732
- column.width >= widest.width ? column : widest
733
- end
734
-
735
- widest_column.width -= 1
736
- end
811
+ def num_columns
812
+ column_registry.count
737
813
  end
738
814
 
739
815
  # @!visibility private
@@ -1,3 +1,3 @@
1
1
  module Tabulo
2
- VERSION = "2.7.0"
2
+ VERSION = "2.8.0"
3
3
  end
data/tabulo.gemspec CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  }
30
30
 
31
31
  spec.add_runtime_dependency "tty-screen", "0.8.1"
32
- spec.add_runtime_dependency "unicode-display_width", "2.0.0"
32
+ spec.add_runtime_dependency "unicode-display_width", "2.1.0"
33
33
 
34
34
  spec.add_development_dependency "bundler"
35
35
  spec.add_development_dependency "rake", "~> 12.3.3"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tabulo
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.0
4
+ version: 2.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Harvey
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-18 00:00:00.000000000 Z
11
+ date: 2022-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tty-screen
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 2.0.0
33
+ version: 2.1.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 2.0.0
40
+ version: 2.1.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -206,7 +206,7 @@ licenses:
206
206
  metadata:
207
207
  source_code_uri: https://github.com/matt-harvey/tabulo
208
208
  changelog_uri: https://raw.githubusercontent.com/matt-harvey/tabulo/master/CHANGELOG.md
209
- post_install_message:
209
+ post_install_message:
210
210
  rdoc_options: []
211
211
  require_paths:
212
212
  - lib
@@ -221,8 +221,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
221
221
  - !ruby/object:Gem::Version
222
222
  version: '0'
223
223
  requirements: []
224
- rubygems_version: 3.1.2
225
- signing_key:
224
+ rubygems_version: 3.2.3
225
+ signing_key:
226
226
  specification_version: 4
227
227
  summary: Terminal table generator
228
228
  test_files: []