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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2db6b288685b76e7cab29fc52f1f0d265cb77310392449681bd15950231fb3d7
4
- data.tar.gz: 8a9477cc4c7f04a0219bac645c774bd5569f1cd2baff5bfb17ffba57e22b497b
3
+ metadata.gz: 6ca950eccef1e0d5bcd333ce42584fd4a95020316e80d2f684b721f0cb6ec00a
4
+ data.tar.gz: ea4c7f96ebce149b131db590fdeb75a54152576717007024556047a36004469f
5
5
  SHA512:
6
- metadata.gz: 292313dfb4ec2e67e0f0f879c61f9b3797b407d9c8bf1a1e049126b509cedeca557b4cb0d0e4498658438ced29a0faf0aab868ee821cda92a9cea6959a81e75c
7
- data.tar.gz: 8714b8dae9a6373d0953a496816e14264af2c78e680b8854866e513776f6aa81827d9beb4c29c190455b3911326495a4cfbe5b712175c54b5087e7927531737b
6
+ metadata.gz: 2f83f8889ad5f72954bc0a16a743c6ecdb00b732fdac29d179a43874eba68b021f180be981bbce82773bf4f8f0e968a92c03400cf9af74cf8a6bb23ed585cf77
7
+ data.tar.gz: f9a47229fe11cfc79ea265f4280ca84666e7e09c7d325db4e4b4feb9c2a854f76a9c1647fdcbd2eff3a5073fd327044be75758af839f227750ab1fd24aa2eccc
@@ -4,6 +4,6 @@ rvm:
4
4
  - 2.1.10
5
5
  - 2.2.10
6
6
  - 2.3.8
7
- - 2.4.5
7
+ - 2.4.6
8
8
  - 2.5.5
9
9
  - 2.6.2
@@ -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] # need not be an array
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, [shrinkwrap](#shrinkwrap) the table so that each column is just wide enough for its contents.
39
- * Put an upper limit on total table width when shrinkwrapping, to
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 Contents](#table-of-contents)
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
- * [Development](#development)
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], columns: %i[itself even? odd?])
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. This can be customized by passing `:center`, `:left` or `:right` to
161
- the `align_header` or `align_body` options of `add_column`, e.g.:
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: :left, align_body: :left) { |n| n * 2 }
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], columns: %i[itself even?], column_width: 6)
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="shrinkwrap"></a>
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], columns: %i[itself even?])
225
- table.shrinkwrap!
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 `shrinkwrap!` method returns the table itself, so you can "wrap-and-print" in one go:
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], columns: %i[itself even?]).shrinkwrap!
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 shrinkwrapping:
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], columns: %i[itself even?]).shrinkwrap!(max_table_width: 17)
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
- If the table cannot be fit within `max_table_width`, column widths are reduced as required, with
260
- wrapping or truncation then occuring as necessary (see [Overflow handling](#overflow-handling)).
261
- Under the hood, a character of width is deducted column by column&mdash;the widest column being
262
- targetted each time&mdash;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
- Note that shrinkwrapping necessarily involves traversing the entire collection up front as
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&mdash;the widest column being targetted each time&mdash;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 `shrinkwrap!` is to fix the column widths
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 shrinkwrapping_. If the underlying collection changes between that point, and when
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
- columns: %i[itself length]
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
- wrap_body_cells_to: 1,
308
- columns: %i[itself length]
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, columns: %i[itself even?], header_frequency: 5)
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 [shrinkwrap](#shrinkwrap) the table, however, since
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, columns: %i[itself even? odd?])
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, columns: %i[itself even?])
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&mdash;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&mdash;they each have their strengths&mdash;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.2.2
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.2.2-blue.svg
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.2.2
1
+ 1.3.0
@@ -1,5 +1,6 @@
1
- require "tabulo/version"
1
+ require "tabulo/deprecation"
2
2
  require "tabulo/exceptions"
3
3
  require "tabulo/table"
4
+ require "tabulo/version"
4
5
  require "tabulo/row"
5
6
  require "tabulo/column"
@@ -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 || infer_alignment(cell_datum))
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
@@ -3,6 +3,9 @@ module Tabulo
3
3
  class Row
4
4
  include Enumerable
5
5
 
6
+ # @return the element of the {Table}'s underlying enumerable to which this {Row} corresponds
7
+ attr_reader :source
8
+
6
9
  # @!visibility private
7
10
  def initialize(table, source, with_header: true)
8
11
  @table = table
@@ -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]] columns Specifies the initial columns. The Symbols provided must
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
- # @return [Table] a new Table
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 (:center) Specifies how the header text
113
- # should be aligned.
114
- # @param [:left, :center, :right, nil] align_body (nil) Specifies how the cell body contents
115
- # should be aligned. Possible If <tt>nil</tt> is passed, then the alignment is determined
116
- # by the type of the cell value, with numbers aligned right, booleans center-aligned, and
117
- # other values left-aligned. Note header text alignment is configured separately using the
118
- # :align_header param.
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: :center, align_body: nil,
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 (nil) If provided, stops the total table
225
- # width (including padding and borders) from expanding beyond this number of characters.
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). Note that regardless of the value passed to
230
- # max_table_width, the table will always be left wide enough to accommodate at least
231
- # 1 character's width of content, 1 character of left padding and 1 character of right padding
232
- # in each column, together with border characters (1 on each side of the table and 1 between
233
- # adjacent columns). I.e. there is a certain width below width the Table will refuse to
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 shrinkwrap!(max_table_width: nil)
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
- total_columns_width = columns.inject(0) { |sum, column| sum + column.width }
251
- total_padding = column_registry.count * @column_padding * 2
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)
@@ -1,3 +1,3 @@
1
1
  module Tabulo
2
- VERSION = "1.2.2"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -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.2.2
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-03-26 00:00:00.000000000 Z
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: []