tabulo 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|