tabulo 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +78 -38
- data/VERSION +1 -1
- data/_config.yml +1 -1
- data/lib/tabulo.rb +1 -0
- data/lib/tabulo/column.rb +19 -4
- data/lib/tabulo/deprecation.rb +1 -0
- data/lib/tabulo/row.rb +1 -1
- data/lib/tabulo/table.rb +94 -8
- data/lib/tabulo/util.rb +15 -0
- data/lib/tabulo/version.rb +1 -1
- data/tabulo.gemspec +1 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53f13b68dff441391dc7250a607d3d449f52cf2735ba2259bf01ac60d996014e
|
4
|
+
data.tar.gz: 2a3b0f1cd2225cf8ef4d143f115de50a24f23253888770d04c9329b76f5e15e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48358f0322bbd3bf6c85305e885d1a6afa6028a0688d8d9442eb1c67bd46870866c120fcb1aed4c33ce176e9cb72f60f61d657044a71a21f1c2960689225ed51
|
7
|
+
data.tar.gz: 55f578f021f9b7516fef26ef0b7f4c583d0400c7bc8a1d069cdee609752a734698c73719294608a16bbb1709d9461657b8b354e8622233ba34b341653814ac95
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### v1.4.0
|
4
|
+
|
5
|
+
* New `#transpose` function to produced a new Table in which the rows and
|
6
|
+
columns are transposed relative to the original one.
|
7
|
+
* Properly handle multibyte characters when calculating widths, wrapping etc..
|
8
|
+
|
3
9
|
### v1.3.0
|
4
10
|
|
5
11
|
* More ergonomic Table initializer, allowing you to specify columns directly as varargs rather
|
data/README.md
CHANGED
@@ -5,10 +5,14 @@
|
|
5
5
|
[![Coverage Status][CS img]][Coverage Status]
|
6
6
|
[![Build Status][BS img]][Build Status]
|
7
7
|
[![Code Climate][CC img]][Code Climate]
|
8
|
+
[![Awesome][AR img]][Awesome Ruby]
|
8
9
|
|
9
|
-
|
10
|
+
Tabulo is a terminal table generator for Ruby.
|
11
|
+
|
12
|
+
It offers a DRY, "column-centric" interface, and is designed to make it very easy it produce highly
|
13
|
+
readable tables, even from large and unwieldy data sets and streams.
|
10
14
|
|
11
|
-
|
15
|
+
## Overview
|
12
16
|
|
13
17
|
*Quick API:*
|
14
18
|
|
@@ -44,21 +48,25 @@ end
|
|
44
48
|
|
45
49
|
## Features
|
46
50
|
|
47
|
-
* Lets you set [fixed column widths](#fixed-column-widths), then either [wrap](#overflow-handling)
|
48
|
-
[truncate](#overflow-handling) the overflow.
|
49
|
-
* Alternatively, ["pack"](#pack) the table so that each column is automatically just wide enough for
|
50
|
-
contents, but [without overflowing the terminal horizontally](#max-table-width).
|
51
|
-
* Alignment of cell content is [configurable](#cell-alignment), but has helpful content-based
|
52
|
-
(numbers right, strings left).
|
53
|
-
* Headers are [repeatable](#repeating-headers).
|
54
|
-
* Newlines within cell content are correctly handled.
|
55
|
-
* A `Tabulo::Table` is an `Enumerable`, so you can [step through it](#enumerator) a row at a time,
|
56
|
-
printing as you go, without waiting for the entire underlying collection to load.
|
57
|
-
* Each `Tabulo::Row` is also an `Enumerable`, [providing access](#accessing-cell-values) to the underlying cell values.
|
51
|
+
* Lets you set [fixed column widths](#fixed-column-widths), then either [wrap](#overflow-handling)
|
52
|
+
or [truncate](#overflow-handling) the overflow.
|
53
|
+
* Alternatively, ["pack"](#pack) the table so that each column is automatically just wide enough for
|
54
|
+
its contents, but [without overflowing the terminal horizontally](#max-table-width).
|
55
|
+
* Alignment of cell content is [configurable](#cell-alignment), but has helpful content-based
|
56
|
+
defaults (numbers right, strings left).
|
58
57
|
* Tabulate any `Enumerable`: the underlying collection need not be an array.
|
58
|
+
* Since a `Tabulo::Table` is itself also an `Enumerable`, you can [step through it](#enumerator) a
|
59
|
+
row at a time, printing as you go, without waiting for the entire underlying collection to load.
|
60
|
+
In other words, you get a [streaming interface](#enumerator) for free.
|
61
|
+
* Each `Tabulo::Row` is also an `Enumerable`, [providing access](#accessing-cell-values) to the
|
62
|
+
underlying cell values.
|
63
|
+
* The header row can be [repeated](#repeating-headers) at arbitrary intervals.
|
64
|
+
* Newlines within cell content are correctly handled.
|
65
|
+
* Multibyte characters are correctly handled.
|
66
|
+
* Easily [transpose](#transposition) the table, so that rows are swapped with columns.
|
59
67
|
* [Customize](#additional-configuration-options) border and divider characters.
|
60
|
-
* Use a [DRY interface](#configuring-columns): by being "column based", it is
|
61
|
-
developer the burden of syncing the ordering within the header row with that of the body rows.
|
68
|
+
* Use a [DRY initialization interface](#configuring-columns): by being "column based", it is
|
69
|
+
designed to spare the developer the burden of syncing the ordering within the header row with that of the body rows.
|
62
70
|
|
63
71
|
Tabulo has also been ported to Crystal (with some modifications): see [Tablo](https://github.com/hutou/tablo).
|
64
72
|
|
@@ -82,6 +90,7 @@ Tabulo has also been ported to Crystal (with some modifications): see [Tablo](ht
|
|
82
90
|
* [Using a Table Enumerator](#using-a-table-enumerator)
|
83
91
|
* [Accessing cell values](#accessing-cell-values)
|
84
92
|
* [Accessing the underlying enumerable](#accessing-sources)
|
93
|
+
* [Transposing rows and columns](#transposition)
|
85
94
|
* [Additional configuration options](#additional-configuration-options)
|
86
95
|
* [Motivation](#motivation)
|
87
96
|
* [Contributing](#contributing)
|
@@ -180,7 +189,7 @@ table = Tabulo::Table.new([1, 2], :itself, :even?, align_header: :left, align_bo
|
|
180
189
|
```
|
181
190
|
|
182
191
|
The table-level alignment settings can be overridden for individual columns by
|
183
|
-
passing
|
192
|
+
passing similarly-named options to `add_column`, e.g.:
|
184
193
|
|
185
194
|
```ruby
|
186
195
|
table.add_column("Doubled", align_header: :right, align_body: :left) { |n| n * 2 }
|
@@ -278,7 +287,7 @@ puts Tabulo::Table.new([1, 2], :itself, :even?).pack(max_table_width: 17)
|
|
278
287
|
| 2 | true |
|
279
288
|
```
|
280
289
|
|
281
|
-
Or if you simply call `pack` with no
|
290
|
+
Or if you simply call `pack` with no arguments (or if you explicitly call
|
282
291
|
`pack(max_table_width: :auto)`), the table width will automatically be capped at the
|
283
292
|
width of your terminal.
|
284
293
|
|
@@ -458,11 +467,11 @@ table.each do |row|
|
|
458
467
|
end
|
459
468
|
```
|
460
469
|
|
461
|
-
The first argument to `add_column
|
462
|
-
|
463
|
-
|
464
|
-
header. If we want the header to be different
|
465
|
-
using the `header` option to `add_column`:
|
470
|
+
The first argument to `add_column` always provides the key for the purpose of accessing the `Hash`
|
471
|
+
form of a `Tabulo::Row`. (If the provided argument was a `String`, it will be converted to a
|
472
|
+
`Symbol` for purposes of accessing this `Hash`.) This key serves as a sort of "logical label" for
|
473
|
+
the column; and it need not be the same as the column header. If we want the header to be different
|
474
|
+
to the label, we can achieve this using the `header` option to `add_column`:
|
466
475
|
|
467
476
|
```ruby
|
468
477
|
table = Tabulo::Table.new(1..5) do |t|
|
@@ -510,9 +519,38 @@ row can be accessed by calling the `source` method on that row:
|
|
510
519
|
table.each do |row|
|
511
520
|
puts row.source # 50...60...
|
512
521
|
end
|
522
|
+
```
|
523
|
+
|
524
|
+
<a name="transposition"></a>
|
525
|
+
### Transposing rows and columns
|
513
526
|
|
527
|
+
By default, Tabulo generates a table in which each row corresponds to a _record_, i.e. an element of
|
528
|
+
the underlying enumerable, and each column to a _field_. However, there are times when one instead
|
529
|
+
wants each row to represent a field, and each column a record. This is generally the case when there
|
530
|
+
are a small number or records but a large number of fields. To produce such a table, we can first
|
531
|
+
initialize an ordinary table, specifying fields as columns, and then call `transpose`, which returns
|
532
|
+
a new table in which the rows and columns are swapped:
|
533
|
+
|
534
|
+
```ruby
|
535
|
+
> puts Tabulo::Table.new(-1..1, :even?, :odd?, :zero?, :pred, :succ, :abs).transpose
|
536
|
+
```
|
537
|
+
```
|
538
|
+
+-------+--------------+--------------+--------------+
|
539
|
+
| | -1 | 0 | 1 |
|
540
|
+
+-------+--------------+--------------+--------------+
|
541
|
+
| even? | false | true | false |
|
542
|
+
| odd? | true | false | true |
|
543
|
+
| zero? | false | true | false |
|
544
|
+
| pred | -2 | -1 | 0 |
|
545
|
+
| succ | 0 | 1 | 2 |
|
546
|
+
| abs | 1 | 0 | 1 |
|
514
547
|
```
|
515
548
|
|
549
|
+
By default, a header row is added to the new table, showing the string value of the element
|
550
|
+
represented in that column. This can be configured, however, along with other aspects of
|
551
|
+
`transpose`'s behaviour. For details, see the documentation. (TODO Link to
|
552
|
+
documentation for #transpose, once available at rubydoc site.)
|
553
|
+
|
516
554
|
<a name="additional-configuration-options"></a>
|
517
555
|
### Additional configuration options
|
518
556
|
|
@@ -556,19 +594,19 @@ table = Tabulo::Table.new(1..3, :itself, :even?)
|
|
556
594
|
|
557
595
|
There are other terminal table generators for Ruby. Popular among these are:
|
558
596
|
|
559
|
-
* [tty-table](https://github.com/piotrmurach/tty-table)
|
560
597
|
* [terminal-table](https://github.com/tj/terminal-table)
|
598
|
+
* [tty-table](https://github.com/piotrmurach/tty-table)
|
561
599
|
* [table\_print](https://github.com/arches/table_print)
|
562
600
|
|
563
|
-
*DISCLAIMER: My comments regarding these other libraries are based only on my own,
|
564
|
-
|
565
|
-
|
566
|
-
when you read it
|
601
|
+
*DISCLAIMER: My comments regarding these other libraries are based only on my own, possibly flawed
|
602
|
+
reading of the documentation for, and experimentation with, these libraries at the time of my
|
603
|
+
writing this. Their APIs, features or documentation may well change between when I write this, and
|
604
|
+
when you read it. Please consult the libraries' own documentation for yourself, rather than relying
|
605
|
+
on these comments.*
|
567
606
|
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
be.
|
607
|
+
While these libraries have their strengths, I personally found that for the common use case of
|
608
|
+
printing a table on the basis of some underlying enumerable collection (such as an ActiveRecord
|
609
|
+
query result), using these libraries felt more cumbersome than it needed to be.
|
572
610
|
|
573
611
|
For example, suppose we have called `User.all` from the Rails console, and want to print
|
574
612
|
a table showing the email, first name, last name and ID of each user,
|
@@ -586,17 +624,17 @@ puts table
|
|
586
624
|
|
587
625
|
The problem here is that there is no single source of knowledge about which columns
|
588
626
|
appear, and in which order. If we want to add another column to the left of "email",
|
589
|
-
we need to amend the rows
|
627
|
+
we need to amend the rows array, and the headings array, and the index passed to `align_column`.
|
590
628
|
We bear the burden of keeping these three in sync. This is not be a big deal for small one-off
|
591
629
|
tables, but for tables that have many columns, or that are constructed
|
592
630
|
dynamically based on user input or other runtime factors determining the columns
|
593
631
|
to be included, this can be a hassle and a source of brittleness.
|
594
632
|
|
595
633
|
`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.
|
597
|
-
|
598
|
-
|
599
|
-
|
634
|
+
"row-based" and a "column-based" method of initializing a table. The row-based method
|
635
|
+
is similar to `terminal-table`'s in that it burdens the developer with syncing the
|
636
|
+
column ordering across multiple code locations. The "column-based" API for `tty-table`, on
|
637
|
+
the other hand, seems to avoid this problem. One way of using it is like this:
|
600
638
|
|
601
639
|
```ruby
|
602
640
|
users = User.all
|
@@ -652,13 +690,15 @@ The gem is available as open source under the terms of the [MIT
|
|
652
690
|
License](http://opensource.org/licenses/MIT).
|
653
691
|
|
654
692
|
[Gem Version]: https://rubygems.org/gems/tabulo
|
655
|
-
[Documentation]: http://www.rubydoc.info/gems/tabulo/1.
|
693
|
+
[Documentation]: http://www.rubydoc.info/gems/tabulo/1.4.0
|
656
694
|
[Build Status]: https://travis-ci.org/matt-harvey/tabulo
|
657
695
|
[Coverage Status]: https://coveralls.io/r/matt-harvey/tabulo
|
658
696
|
[Code Climate]: https://codeclimate.com/github/matt-harvey/tabulo
|
697
|
+
[Awesome Ruby]: https://github.com/markets/awesome-ruby#cli-utilities
|
659
698
|
|
660
699
|
[GV img]: https://img.shields.io/gem/v/tabulo.svg
|
661
|
-
[DC img]: https://img.shields.io/badge/documentation-v1.
|
700
|
+
[DC img]: https://img.shields.io/badge/documentation-v1.4.0
|
662
701
|
[BS img]: https://img.shields.io/travis/matt-harvey/tabulo.svg
|
663
702
|
[CS img]: https://img.shields.io/coveralls/matt-harvey/tabulo.svg
|
664
703
|
[CC img]: https://codeclimate.com/github/matt-harvey/tabulo/badges/gpa.svg
|
704
|
+
[AR img]: https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.4.0
|
data/_config.yml
CHANGED
@@ -1 +1 @@
|
|
1
|
-
theme: jekyll-theme-
|
1
|
+
theme: jekyll-theme-dinky
|
data/lib/tabulo.rb
CHANGED
data/lib/tabulo/column.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "unicode/display_width"
|
2
|
+
|
1
3
|
module Tabulo
|
2
4
|
|
3
5
|
# @!visibility private
|
@@ -38,15 +40,28 @@ module Tabulo
|
|
38
40
|
|
39
41
|
def infilled_subcells(str, real_alignment)
|
40
42
|
str.split($/, -1).flat_map do |substr|
|
41
|
-
|
42
|
-
|
43
|
-
|
43
|
+
substr_grapheme_clusters = substr.scan(/\X/)
|
44
|
+
subsubcells = []
|
45
|
+
current_subsubcell_grapheme_clusters = []
|
46
|
+
current_subsubcell_display_width = 0
|
47
|
+
substr_grapheme_clusters.each do |sgc|
|
48
|
+
sgc_display_width = Unicode::DisplayWidth.of(sgc)
|
49
|
+
if sgc_display_width + current_subsubcell_display_width > width
|
50
|
+
subsubcells << current_subsubcell_grapheme_clusters.join("")
|
51
|
+
current_subsubcell_grapheme_clusters.clear
|
52
|
+
current_subsubcell_display_width = 0
|
53
|
+
end
|
54
|
+
|
55
|
+
current_subsubcell_grapheme_clusters << sgc
|
56
|
+
current_subsubcell_display_width += sgc_display_width
|
44
57
|
end
|
58
|
+
subsubcells << current_subsubcell_grapheme_clusters.join("")
|
59
|
+
subsubcells.map { |s| align_cell_content(s, real_alignment) }
|
45
60
|
end
|
46
61
|
end
|
47
62
|
|
48
63
|
def align_cell_content(content, real_alignment)
|
49
|
-
padding = [@width - content
|
64
|
+
padding = [@width - Unicode::DisplayWidth.of(content), 0].max
|
50
65
|
left_padding, right_padding =
|
51
66
|
case real_alignment
|
52
67
|
when :center
|
data/lib/tabulo/deprecation.rb
CHANGED
data/lib/tabulo/row.rb
CHANGED
@@ -43,7 +43,7 @@ module Tabulo
|
|
43
43
|
# @return a Hash representation of the {Row}, with column labels acting
|
44
44
|
# as keys and the calculated cell values (before formatting) providing the values.
|
45
45
|
# @example
|
46
|
-
# table = Tabulo::Table.new([1, 10], columns: %i
|
46
|
+
# table = Tabulo::Table.new([1, 10], columns: %i[itself even?])
|
47
47
|
# row = table.first
|
48
48
|
# row.to_h # => { :itself => 1, :even? => false }
|
49
49
|
def to_h
|
data/lib/tabulo/table.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "tty-screen"
|
2
|
+
require "unicode/display_width"
|
2
3
|
|
3
4
|
module Tabulo
|
4
5
|
|
@@ -40,7 +41,7 @@ module Tabulo
|
|
40
41
|
# be unique. Each element of the Array will be used to create a column whose content is
|
41
42
|
# created by calling the corresponding method on each element of sources. Note
|
42
43
|
# 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
|
44
|
+
# @param [Array[Symbol]] columns <b>DEPRECATED</b> Use <tt>cols</tt> instead.
|
44
45
|
# @param [Integer, nil] column_width The default column width for columns in this
|
45
46
|
# table, not excluding padding. If <tt>nil</tt>, then {DEFAULT_COLUMN_WIDTH} will be used.
|
46
47
|
# @param [:start, nil, Integer] header_frequency Controls the display of column headers.
|
@@ -125,11 +126,12 @@ module Tabulo
|
|
125
126
|
|
126
127
|
# Adds a column to the Table.
|
127
128
|
#
|
128
|
-
# @param [Symbol, String] label A unique identifier for this column, which by
|
129
|
-
# also be used as the column header text (see also the header param). If the
|
129
|
+
# @param [Symbol, String, Integer] label A unique identifier for this column, which by
|
130
|
+
# default will also be used as the column header text (see also the header param). If the
|
130
131
|
# extractor argument is not also provided, then the label argument should correspond to
|
131
132
|
# a method to be called on each item in the table sources to provide the content
|
132
|
-
# for this column.
|
133
|
+
# for this column. If a String is passed as the label, then it will be converted to
|
134
|
+
# a Symbol for the purpose of serving as this label.
|
133
135
|
# @param [nil, #to_s] header (nil) Text to be displayed in the column header. If passed nil,
|
134
136
|
# the column's label will also be used as its header text.
|
135
137
|
# @param [:left, :center, :right, nil] align_header (nil) Specifies how the header text
|
@@ -163,7 +165,13 @@ module Tabulo
|
|
163
165
|
def add_column(label, header: nil, align_header: nil, align_body: nil,
|
164
166
|
width: nil, formatter: :to_s.to_proc, &extractor)
|
165
167
|
|
166
|
-
column_label =
|
168
|
+
column_label =
|
169
|
+
case label
|
170
|
+
when Integer, Symbol
|
171
|
+
label
|
172
|
+
when String
|
173
|
+
label.to_sym
|
174
|
+
end
|
167
175
|
|
168
176
|
if column_registry.include?(column_label)
|
169
177
|
raise InvalidColumnLabelError, "Column label already used in this table."
|
@@ -284,6 +292,83 @@ module Tabulo
|
|
284
292
|
self
|
285
293
|
end
|
286
294
|
|
295
|
+
# Creates a new {Table} from the current Table, transposed, that is rotated 90 degrees,
|
296
|
+
# relative to the current Table, so that the header names of the current Table form the
|
297
|
+
# content of left-most column of the new Table, and each column thereafter corresponds to one of the
|
298
|
+
# elements of the current Table's <tt>sources</tt>, with the header of that column being the String
|
299
|
+
# value of that element.
|
300
|
+
#
|
301
|
+
# @example
|
302
|
+
# puts Tabulo::Table.new(-1..1, :even?, :odd?, :abs).transpose
|
303
|
+
# # => +-------+--------------+--------------+--------------+
|
304
|
+
# # | | -1 | 0 | 1 |
|
305
|
+
# # +-------+--------------+--------------+--------------+
|
306
|
+
# # | even? | false | true | false |
|
307
|
+
# # | odd? | true | false | true |
|
308
|
+
# # | abs | 1 | 0 | 1 |
|
309
|
+
#
|
310
|
+
# @param [Hash] opts Options for configuring the new, transposed {Table}.
|
311
|
+
# The following options are the same as the keyword params for the {#initialize} method for
|
312
|
+
# {Table}: <tt>column_width</tt>, <tt>column_padding</tt>, <tt>header_frequency</tt>,
|
313
|
+
# <tt>wrap_header_cells_to</tt>, <tt>wrap_body_cells_to</tt>, <tt>horizontal_rule_character</tt>,
|
314
|
+
# <tt>vertical_rule_character</tt>, <tt>intersection_character</tt>, <tt>truncation_indicator</tt>,
|
315
|
+
# <tt>align_header</tt>, <tt>align_body</tt>.
|
316
|
+
# These are applied in the same way as documented for {#initialize}, when creating the
|
317
|
+
# new, transposed Table. Any options not specified explicitly in the call to {#transpose}
|
318
|
+
# will inherit their values from the original {Table} (with the exception of settings
|
319
|
+
# for the left-most column, containing the field names, which are determined as described
|
320
|
+
# below). In addition, the following options also apply to {#transpose}:
|
321
|
+
# @option opts [nil, Integer] :field_names_width Determines the width of the left-most column of the
|
322
|
+
# new Table, which contains the names of "fields" (corresponding to the original Table's
|
323
|
+
# column headings). If this is not provided, then by default this column will be made just
|
324
|
+
# wide enough to accommodate its contents.
|
325
|
+
# @option opts [String] :field_names_header ("") By default the left-most column will have a
|
326
|
+
# blank header; but this can be overridden by passing a String to this option.
|
327
|
+
# @option opts [:left, :center, :right] :field_names_header_alignment (:right) Specifies how the
|
328
|
+
# header text of the left-most column (if it has header text) should be aligned.
|
329
|
+
# @option opts [:left, :center, :right] :field_names_body_alignment (:right) Specifies how the
|
330
|
+
# body text of the left-most column should be aligned.
|
331
|
+
# @option opts [#to_proc] :headers (:to_s.to_proc) A lambda or other callable object that
|
332
|
+
# will be passed in turn each of the elements of the current Table's <tt>sources</tt>
|
333
|
+
# Enumerable, to determine the text to be displayed in the header of each column of the
|
334
|
+
# new Table (other than the left-most column's header, which is determined as described
|
335
|
+
# above).
|
336
|
+
# @return [Table] a new {Table}
|
337
|
+
# @raise [InvalidHorizontalRuleCharacterError] if invalid argument passed to horizontal_rule_character.
|
338
|
+
# @raise [InvalidVerticalRuleCharacterError] if invalid argument passed to vertical_rule_character.
|
339
|
+
def transpose(opts = {})
|
340
|
+
default_opts = [:column_width, :column_padding, :header_frequency, :wrap_header_cells_to,
|
341
|
+
:wrap_body_cells_to, :horizontal_rule_character, :vertical_rule_character,
|
342
|
+
:intersection_character, :truncation_indicator, :align_header, :align_body].map do |sym|
|
343
|
+
[sym, instance_variable_get("@#{sym}")]
|
344
|
+
end.to_h
|
345
|
+
|
346
|
+
initializer_opts = default_opts.merge(Util.slice_hash(opts, *default_opts.keys))
|
347
|
+
default_extra_opts = { field_names_width: nil, field_names_header: "",
|
348
|
+
field_names_body_alignment: :right, field_names_header_alignment: :right, headers: :to_s.to_proc }
|
349
|
+
extra_opts = default_extra_opts.merge(Util.slice_hash(opts, *default_extra_opts.keys))
|
350
|
+
|
351
|
+
# The underlying enumerable for the new table, is the columns of the original table.
|
352
|
+
fields = column_registry.values
|
353
|
+
|
354
|
+
Table.new(fields, **initializer_opts) do |t|
|
355
|
+
|
356
|
+
# Left hand column of new table, containing field names
|
357
|
+
width_opt = extra_opts[:field_names_width]
|
358
|
+
field_names_width = (width_opt.nil? ? fields.map { |f| f.header.length }.max : width_opt)
|
359
|
+
|
360
|
+
t.add_column(:dummy, header: extra_opts[:field_names_header], width: field_names_width, align_header:
|
361
|
+
extra_opts[:field_names_header_alignment], align_body: extra_opts[:field_names_body_alignment], &:header)
|
362
|
+
|
363
|
+
# Add a column to the new table for each of the original table's sources
|
364
|
+
sources.each_with_index do |source, i|
|
365
|
+
t.add_column(i, header: extra_opts[:headers].call(source)) do |original_column|
|
366
|
+
original_column.body_cell_value(source)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
287
372
|
# @deprecated Use {#pack} instead.
|
288
373
|
#
|
289
374
|
# Reset all the column widths so that each column is *just* wide enough to accommodate
|
@@ -427,7 +512,7 @@ module Tabulo
|
|
427
512
|
when nil
|
428
513
|
; # do nothing
|
429
514
|
when String
|
430
|
-
if c
|
515
|
+
if Unicode::DisplayWidth.of(c) != 1
|
431
516
|
raise exception_class, "#{message_fragment} is neither nil nor a single-character String"
|
432
517
|
end
|
433
518
|
else
|
@@ -440,8 +525,9 @@ module Tabulo
|
|
440
525
|
# @return [Integer] the length of the longest segment of str when split by newlines
|
441
526
|
def wrapped_width(str)
|
442
527
|
segments = str.split($/)
|
443
|
-
segments.inject(1) do |
|
444
|
-
length
|
528
|
+
segments.inject(1) do |longest_length_so_far, segment|
|
529
|
+
length = Unicode::DisplayWidth.of(segment)
|
530
|
+
longest_length_so_far > length ? longest_length_so_far : length
|
445
531
|
end
|
446
532
|
end
|
447
533
|
end
|
data/lib/tabulo/util.rb
ADDED
data/lib/tabulo/version.rb
CHANGED
data/tabulo.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tabulo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.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-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tty-screen
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.6.5
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: unicode-display_width
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.5.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.5.0
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -178,6 +192,7 @@ files:
|
|
178
192
|
- lib/tabulo/exceptions.rb
|
179
193
|
- lib/tabulo/row.rb
|
180
194
|
- lib/tabulo/table.rb
|
195
|
+
- lib/tabulo/util.rb
|
181
196
|
- lib/tabulo/version.rb
|
182
197
|
- tabulo.gemspec
|
183
198
|
homepage: https://github.com/matt-harvey/tabulo
|