tabulo 2.6.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f9ebd03570ab58159573421765c5fa7260dbaec7a7f573a28b746a2c022dd3c8
4
+ data.tar.gz: 85c7d4de41779701baceb6db3b0b66b47025f0c3f99a8181f9948306d5f826bb
5
+ SHA512:
6
+ metadata.gz: eabf52fe920bdbc2867184f3feee0d03e33f79d101d95b4b96095e390786d8443d22c1ab51a10e5a26645c45687f4a36f18c5dc8b35cc72d9162c90c3fb243cc
7
+ data.tar.gz: d991da9c53378a981623c5622be156f970f37e6f42a3dd22b02d51b11020e1302a5b5209117a4a6b9d74e36c555f73f0a2f67eb2b6ff2dafcf7b14d172eb2e00
data/.ackrc ADDED
@@ -0,0 +1,10 @@
1
+ # Additional options for `ack` search utility
2
+
3
+ ## Directories to ignore ##
4
+
5
+ # Documentation generator output
6
+ --ignore-directory=is:.yardoc
7
+ --ignore-directory=is:doc
8
+
9
+ # Generated coverage reports
10
+ --ignore-directory=is:coverage
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ **/.*.swp
11
+ coverage
12
+ gh-md-toc
@@ -0,0 +1,22 @@
1
+ --- !ruby/object:RDoc::Options
2
+ encoding: UTF-8
3
+ static_path: []
4
+ rdoc_include:
5
+ - "."
6
+ charset: UTF-8
7
+ exclude:
8
+ hyperlink_all: false
9
+ line_numbers: false
10
+ locale:
11
+ locale_dir: locale
12
+ locale_name:
13
+ main_page:
14
+ markup: tomdoc
15
+ output_decoration: true
16
+ page_dir:
17
+ show_hash: false
18
+ tab_width: 8
19
+ template_stylesheets: []
20
+ title:
21
+ visibility: :protected
22
+ webcvs:
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,10 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.1.10
5
+ - 2.2.10
6
+ - 2.3.8
7
+ - 2.4.9
8
+ - 2.5.8
9
+ - 2.6.6
10
+ - 2.7.1
@@ -0,0 +1,2 @@
1
+ --markup-provider=redcarpet
2
+ --markup=markdown
@@ -0,0 +1,249 @@
1
+ # Changelog
2
+
3
+ ### v2.6.0
4
+
5
+ * Add an additional, optional parameter to `styler`, `header_styler` and `title_styler`
6
+ callbacks, which will receive the index (0, 1 or etc.) of the line within the cell
7
+ being styled.
8
+ * Allow padding to be configured on a column-by-column basis.
9
+ * Minor documentation improvements.
10
+
11
+ ### v2.5.0
12
+
13
+ * Add option of table title, together with options for styling and aligning the title
14
+
15
+ ### v2.4.1
16
+
17
+ * Fix warnings under Ruby 2.7
18
+ * Fix minor error in README
19
+ * Minor documentation tweaks
20
+
21
+ ### v2.4.0
22
+
23
+ * Add additional, optional `CellData` parameter to `styler` and `formatter` callbacks
24
+ * Add optional `column_index` parameter to `header_styler` callback
25
+ * Add optional `row_index` parameter to `extractor` callback
26
+ * Add `rake yard` Rake task for generating YARD documentation
27
+ * Minor documentation fixes
28
+ * Upgrade dependency version: `unicode-display_width` gem to 1.7.0
29
+
30
+ ### v2.3.3
31
+
32
+ * Fix styler option on Table initializer, which had no effect
33
+
34
+ ### v2.3.2
35
+
36
+ * Update Rake version to address vulnerability CVE-2020-8130
37
+
38
+ ### v2.3.1
39
+
40
+ * Documentation improvements
41
+ * Update dependency versions
42
+ * Minor refactoring
43
+ * Update Ruby gem description and summary
44
+
45
+ ### v2.3.0
46
+
47
+ * Provide `#remove_column` method.
48
+ * Provide `before` option to `#add_column`, to allow insertion of column into non-final position.
49
+ * Provide `styler` and `header_styler` options in table initializer, to enable default stylers
50
+ to be set for all columns.
51
+ * Documentation improvements and code tidy-ups.
52
+
53
+ ### v2.2.0
54
+
55
+ * New `column_formatter` option on `Tabulo::Table` initializer, enabling the table's default column
56
+ formatter to be customized.
57
+ * New `row_divider_frequency` option on `Tabulo::Table` initializer, to add a horizontal dividing line
58
+ after every N rows.
59
+
60
+ ### v2.1.1
61
+
62
+ * Fix issue where blank lines appear in table when certain border types (e.g. `:classic`) are
63
+ used with a non-nil `border_styler`.
64
+ * Minor documentation fix
65
+
66
+ ### v2.1.0
67
+
68
+ * New `reduced_ascii` and `reduced_modern` border options
69
+ * Fix `column_width` option not properly inherited from original table by the new table created
70
+ by calling #transpose.
71
+
72
+ ### v2.0.2
73
+
74
+ * Minor documentation fixes
75
+
76
+ ### v2.0.1
77
+
78
+ * Minor documentation fix
79
+
80
+ ### v2.0.0
81
+
82
+ #### New features
83
+
84
+ * New `border` option for `Tabulo::Table` initializer allows for better customization of border and
85
+ divider characters, using a preset list of options, viz.: `:ascii`, `:modern`, `:markdown`,
86
+ `:blank` and `:classic`. In particular, the `:modern` border option uses smoothly drawn Unicode
87
+ line characters; and the `:markdown` option renders a GitHub-flavoured Markdown table.
88
+ * `Tabulo::Table#horizontal_rule` method accepts `:top`, `:bottom` and `:middle` options to allow
89
+ the appropriate border characters to be used depending on its intended position in the table.
90
+ * When iterating a `Tabulo::Row`, it's now possible to get the formatted string value of an individual
91
+ `Tabulo::Cell`, not just its underlying "raw" value.
92
+ * Column padding can now optionally be configured separately for left and right column sides, by
93
+ passing a 2-element Array to the `column_padding` option of the `Tabulo::Table` initializer.
94
+
95
+ #### Breaking changes
96
+
97
+ * A `Tabulo::Row` is now a collection of `Tabulo::Cell`, not a collection of underlying "raw"
98
+ values. This makes it easier to get at both formatted string values and underlying "raw" values of
99
+ `Cell`s when traversing a `Row`. To get at the raw underlying value, call `Tabulo::Cell#value`.
100
+ * Remove deprecated `columns` option from `Tabulo::Table` initializer
101
+ (existing `cols` positional parameter now renamed to `columns`).
102
+ * Remove deprecated `shrinkwrap!` method (use `pack` instead).
103
+ * By default, table now has a border line at the bottom. Pass `:classic` to the `border` option of
104
+ the `Tabulo::Table` initializer to get the old behaviour.
105
+ * Removal of `horizontal_rule_character`, `vertical_rule_character` and `intersection` character
106
+ options from `Tabulo::Table` initializer, and from `Tabulo::Table#transpose` method. Use the
107
+ `border` option instead.
108
+
109
+ #### Other noteworthy changes
110
+
111
+ * Test coverage is now at exactly 100%
112
+ * `hirb` gem now mentioned in README
113
+
114
+ ### v1.5.1
115
+
116
+ * Dependency version upgrades
117
+ * Minor documentation fixes
118
+
119
+ ### v1.5.0
120
+
121
+ * Support use of ANSI escape sequences to add colours and
122
+ other styling to table elements without breaking the formatting.
123
+ * Major refactor, moving various computations into a new Cell class.
124
+
125
+ ### v1.4.1
126
+
127
+ * Minor documentation fix
128
+
129
+ ### v1.4.0
130
+
131
+ * New `#transpose` function to produce a new Table in which the rows and
132
+ columns are transposed relative to the original one.
133
+ * Properly handle multibyte characters when calculating widths, wrapping etc..
134
+
135
+ ### v1.3.0
136
+
137
+ * More ergonomic Table initializer, allowing you to specify columns directly as varargs rather
138
+ than as an array passed to `columns:` option (the latter is now deprecated)
139
+ * New `#pack` method to autosize table, capping total table width at width of terminal
140
+ by default (replaces `#shrinkwrap!` method, now deprecated)
141
+ * Ability to set table-level defaults for column header and body cell alignments
142
+ * Accessor methods for `source` attribute, representing the underlying collection
143
+ being tabulated, facilitating reuse of the same table to tabulate different collections
144
+ * Documentation improvements
145
+
146
+ ### v1.2.2
147
+
148
+ * Improve documentation.
149
+
150
+ ### v1.2.1
151
+
152
+ * Improve documentation in README.
153
+ * Update Travis config.
154
+ * Change homepage in Gemspec
155
+
156
+ ### v1.2.0
157
+
158
+ * Allow customization of padding.
159
+
160
+ ### v1.1.0
161
+
162
+ * Allow customization of horizontal divider, vertical divider and intersection characters.
163
+
164
+ ### v1.0.1
165
+
166
+ * Fix deprecation warnings.
167
+ * Update bundler version development dependency.
168
+
169
+ ### v1.0.0
170
+
171
+ * Decision to release stable version!
172
+ * Minor implementation and documentation tweaks.
173
+
174
+ ### v0.6.3
175
+
176
+ * Throw an exception if column labels are not unique.
177
+
178
+ ### v0.6.2
179
+
180
+ * Explicitly support only Ruby >= 2.1.10.
181
+
182
+ ### v0.6.1
183
+
184
+ * Fix Table#shrinkwrap! handling of newlines within header cell content.
185
+ * README now correctly formatted by rubydoc.info.
186
+
187
+ ### v0.6.0
188
+
189
+ * Correctly handle newlines in cell content.
190
+ * Use keyword arguments instead of option hashes.
191
+ * Write remaining pending specs.
192
+
193
+ ### v0.5.1
194
+
195
+ * Unsuccessful attempt to fix broken appearance of http://www.rubydoc.info/gems/tabulo/0.5.1
196
+
197
+ ### v0.5.0
198
+
199
+ * Add Table#shrinkwrap! method to automate column widths so they "just fit".
200
+ * Improve documentation.
201
+
202
+ ### v0.4.2
203
+
204
+ * Improve README.
205
+ * Fix error when printing a Table, or a Row thereof, when the Table doesn't
206
+ have any columns.
207
+ * Remove unused development dependency on yard-tomdoc.
208
+ * Write more specs.
209
+
210
+ ### v0.4.1
211
+
212
+ * Update README to reflect default column width of 12.
213
+
214
+ ### v0.4.0
215
+
216
+ * Increase default column width from 8 to 12
217
+ * Allow default column width to be configured when initializing a Table
218
+ * Minor code tidy-ups, including removal of undocumented ability for
219
+ Table#add_column to accept a Column instance directly.
220
+
221
+ ### v0.3.1
222
+
223
+ * Fix width and other options ignored by Table#add_column.
224
+
225
+ ### v0.3.0
226
+
227
+ * Rename Table#header_row to Table#formatted_header
228
+ * Improve documentation, and use Yardoc instead of Tomdoc
229
+ * Remove Tabulo::Column from the publicly documented API.
230
+
231
+ ### v0.2.2
232
+
233
+ * Write documentation
234
+
235
+ ### v0.2.1
236
+
237
+ * Code tidy-ups
238
+ * Tidy-ups and improvements to README, including adding badges for test coverage etc..
239
+
240
+ ### v0.2.0
241
+
242
+ * Allow columns to be initialized with `columns` option in `Table` initializer
243
+ * Removed redundant `truncate` option.
244
+ * Rename `wrap_cells_to` to `wrap_body_cells_to`.
245
+ * Improve README.
246
+
247
+ ### v0.1.0
248
+
249
+ Initial release.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tabulo.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Matthew Harvey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,1353 @@
1
+ # Tabulo
2
+
3
+ [![Gem Version][GV img]][Gem Version]
4
+ [![Documentation][DC img]][Documentation]
5
+ [![Coverage Status][CS img]][Coverage Status]
6
+ [![Build Status][BS img]][Build Status]
7
+ [![Code Climate][CC img]][Code Climate]
8
+ [![Awesome][AR img]][Awesome Ruby]
9
+
10
+ Tabulo is a Ruby library for generating plain text tables (also known as “terminal tables”
11
+ or “ASCII tables”). It is both highly configurable and very easy to use.
12
+
13
+ <a name="overview"></a>
14
+ ## Overview
15
+
16
+ _Quick API:_
17
+
18
+ ```
19
+ > puts Tabulo::Table.new(User.all, :id, :first_name, :last_name, border: :modern).pack
20
+ ┌────┬────────────┬───────────┐
21
+ │ id │ first_name │ last_name │
22
+ ├────┼────────────┼───────────┤
23
+ │ 1 │ John │ Citizen │
24
+ │ 2 │ Jane │ Doe │
25
+ └────┴────────────┴───────────┘
26
+ ```
27
+
28
+ _Full API:_
29
+
30
+ ```
31
+ table = Tabulo::Table.new(User.all, border: :modern) do |t|
32
+ t.add_column("ID", &:id)
33
+ t.add_column("First name", &:first_name)
34
+ t.add_column("Last name") { |user| user.last_name.upcase }
35
+ end
36
+ ```
37
+
38
+ ```
39
+ > puts table.pack
40
+ ┌────┬────────────┬───────────┐
41
+ │ ID │ First name │ Last name │
42
+ ├────┼────────────┼───────────┤
43
+ │ 1 │ John │ CITIZEN │
44
+ │ 2 │ Jane │ DOE │
45
+ └────┴────────────┴───────────┘
46
+ ```
47
+
48
+ <a name="features"></a>
49
+ ## Features
50
+
51
+ * Presents a [DRY API](#adding-columns) that is column-based, not row-based, meaning header and body rows are
52
+ automatically in sync
53
+ * Lets you set [fixed column widths](#fixed-column-widths), then either [wrap](#overflow-handling)
54
+ or [truncate](#overflow-handling) the overflow
55
+ * Alternatively, [&ldquo;pack&rdquo;](#pack) the table so that columns are auto-sized to their
56
+ contents, but [without overflowing the terminal](#max-table-width)
57
+ * Cell alignment is [configurable](#cell-alignment), but has helpful content-based defaults (numbers right, strings
58
+ left)
59
+ * Tabulate any `Enumerable`: the underlying collection need not be an array
60
+ * [Step through](#enumerator) your table a row at a time, printing as you go, without waiting for the
61
+ underlying collection to load. In other words, have a [streaming interface](#enumerator) for free.
62
+ * Add an optional [title](#title) to your table
63
+ * The header row can be [repeated](#repeating-headers) at arbitrary intervals
64
+ * Newlines within cell content are correctly handled
65
+ * Multibyte Unicode characters are correctly handled
66
+ * Apply [colours](#colours-and-styling) and other styling to table content and borders, without breaking the table
67
+ * Easily [transpose](#transposition) the table, so that rows are swapped with columns
68
+ * Choose from multiple [border configurations](#borders), including Markdown, &ldquo;ASCII&rdquo;, and smoothly
69
+ joined Unicode border characters
70
+
71
+ Tabulo has also been ported to Crystal (with some modifications): see [Tablo](https://github.com/hutou/tablo).
72
+
73
+ <a name="contents"></a>
74
+ ## Contents
75
+
76
+ * [Overview](#overview)
77
+ * [Features](#features)
78
+ * [Table of contents](#table-of-contents)
79
+ * [Installation](#installation)
80
+ * [Detailed usage](#detailed-usage)
81
+ * [Creating a table](#table-initialization)
82
+ * [Adding columns](#adding-columns)
83
+ * [Quick API](#quick-api)
84
+ * [Full API](#quick-api)
85
+ * [Column labels _vs_ headers](#labels-headers)
86
+ * [Positioning columns](#column-positioning)
87
+ * [Removing columns](#removing-columns)
88
+ * [Adding a title](#title)
89
+ * [Cell alignment](#cell-alignment)
90
+ * [Column width, wrapping and truncation](#column-width-wrapping-and-truncation)
91
+ * [Configuring fixed widths](#configuring-fixed-widths)
92
+ * [Automating column widths](#automating-column-widths)
93
+ * [Configuring padding](#configuring-padding)
94
+ * [Overflow handling](#overflow-handling)
95
+ * [Manual cell wrapping](#manual-wrapping)
96
+ * [Formatting cell values](#formatting-cell-values)
97
+ * [Colours and other styling](#colours-and-styling)
98
+ * [Styling cell content](#styling-cell-content)
99
+ * [Styling column headers](#styling-column-headers)
100
+ * [Styling the table title](#styling-title)
101
+ * [Setting default styles](#default-styles)
102
+ * [Styling borders](#styling-borders)
103
+ * [Repeating headers](#repeating-headers)
104
+ * [Using a Table Enumerator](#using-a-table-enumerator)
105
+ * [Accessing cell values](#accessing-cell-values)
106
+ * [Accessing the underlying enumerable](#accessing-sources)
107
+ * [Transposing rows and columns](#transposition)
108
+ * [Border configuration](#borders)
109
+ * [Row dividers](#dividers)
110
+ * [Using a table as a snapshot rather than as a dynamic view](#freezing-a-table)
111
+ * [Comparison with other libraries](#motivation)
112
+ * [Contributing](#contributing)
113
+ * [License](#license)
114
+
115
+ ## Installation [&#x2191;](#contents)
116
+
117
+ Add this line to your application&#8217;s Gemfile:
118
+
119
+ ```ruby
120
+ gem 'tabulo'
121
+ ```
122
+
123
+ And then execute:
124
+
125
+ $ bundle
126
+
127
+ Or install it yourself:
128
+
129
+ $ gem install tabulo
130
+
131
+ To use the gem, you need to require it in your source code as follows:
132
+
133
+ ```ruby
134
+ require 'tabulo'
135
+ ```
136
+
137
+ <a name="table-initialization"></a>
138
+ ### Creating a table [&#x2191;](#contents)
139
+
140
+ You instantiate a `Tabulo::Table` by passing it an underlying `Enumerable`, being the collection of
141
+ things that you want to tabulate. Each member of this collection will end up
142
+ corresponding to a row of the table. The collection can be any `Enumerable`, for example a Ruby
143
+ `Array`, or an ActiveRecord relation:
144
+
145
+ ```ruby
146
+ table = Tabulo::Table.new([1, 2, 5])
147
+ other_table = Tabulo::Table.new(User.all)
148
+ ```
149
+
150
+ For the table to be useful, however, it must also contain columns&hellip;
151
+
152
+ <a name="adding-columns"></a>
153
+ ### Adding columns [&#x2191;](#contents)
154
+
155
+ <a name="quick-api"></a>
156
+ #### Quick API [&#x2191;](#contents)
157
+
158
+ When the columns correspond to methods on members of the underlying enumerable, you can use
159
+ the &ldquo;quick API&rdquo;, by passing a symbol directly to `Tabulo::Table.new` for each column.
160
+ This symbol also provides the column header:
161
+
162
+ ```ruby
163
+ table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?)
164
+ ```
165
+
166
+ ```
167
+ > puts table
168
+ +--------------+--------------+--------------+
169
+ | itself | even? | odd? |
170
+ +--------------+--------------+--------------+
171
+ | 1 | false | true |
172
+ | 2 | true | false |
173
+ | 5 | false | true |
174
+ +--------------+--------------+--------------+
175
+ ```
176
+
177
+ <a name="full-api"></a>
178
+ #### Full API [&#x2191;](#contents)
179
+
180
+ Columns can also be added to the table one-by-one using `add_column`. This &ldquo;full API&rdquo; is
181
+ more verbose, but provides greater configurability:
182
+
183
+ ```ruby
184
+ table = Tabulo::Table.new([1, 2, 5])
185
+ table.add_column(:itself)
186
+ table.add_column(:even?)
187
+ table.add_column(:odd?)
188
+ ```
189
+
190
+ Alternatively, you can pass an initialization block to `new`:
191
+
192
+ ```ruby
193
+ table = Tabulo::Table.new([1, 2, 5]) do |t|
194
+ t.add_column(:itself)
195
+ t.add_column(:even?)
196
+ t.add_column(:odd?)
197
+ end
198
+ ```
199
+
200
+ With the full API, columns can also be initialized using a callable to which each object will be
201
+ passed to determine the value to be displayed in the table. In this case, the first argument to
202
+ `add_column` provides the header text:
203
+
204
+ ```ruby
205
+ table = Tabulo::Table.new([1, 2, 5]) do |t|
206
+ t.add_column("N", &:itself)
207
+ t.add_column("Doubled") { |n| n * 2 }
208
+ t.add_column(:odd?)
209
+ end
210
+ ```
211
+
212
+ ```
213
+ > puts table
214
+ +--------------+--------------+--------------+
215
+ | N | Doubled | odd? |
216
+ +--------------+--------------+--------------+
217
+ | 1 | 2 | true |
218
+ | 2 | 4 | false |
219
+ | 5 | 10 | true |
220
+ +--------------+--------------+--------------+
221
+ ```
222
+
223
+ The `add_column` method can be passed a single parameter callable, as shown in the above example,
224
+ with the parameter representing the member of the underyling enumerable; or it can be passed
225
+ 2-parameter callable, with the second parameter representing the (0-based) index of each row. This can be
226
+ useful if you want to display a row number in one of the columns:
227
+
228
+ ```ruby
229
+ table = Tabulo::Table.new(["a", "b", "c"]) do |t|
230
+ t.add_column("Row") { |letter, row_index| row_index }
231
+ t.add_column("Value", &:itself)
232
+ end
233
+ ```
234
+
235
+ ```
236
+ > puts table
237
+ +--------------+--------------+
238
+ | Row | Value |
239
+ +--------------+--------------+
240
+ | 0 | a |
241
+ | 1 | b |
242
+ | 2 | c |
243
+ +--------------+--------------+
244
+ ```
245
+
246
+ <a name="labels-headers"></a>
247
+ #### Column labels _vs_ headers [&#x2191;](#contents)
248
+
249
+ The first argument to `add_column` is the called the _label_ for that column. It serves as the
250
+ column&#8217;s unique identifier: only one column may have a given label per table.
251
+ (`String`s and `Symbol`s are interchangeable for this purpose.) The label also forms the header shown
252
+ at the top of the column, unless a separate `header:` argument is explicitly passed:
253
+
254
+ ```ruby
255
+ table.add_column(:itself, header: "N")
256
+ table.add_column(:itself2, header: "N", &:itself) # header need not be unique
257
+ # table.add_column(:itself) # would raise Tabulo::InvalidColumnLabelError, as label must be unique
258
+ ```
259
+
260
+ <a name="column-positioning"></a>
261
+ #### Positioning columns [&#x2191;](#contents)
262
+
263
+ By default, each new column is added to the right of all the other columns so far added to the
264
+ table. However, if you want to insert a new column into some other position, you can use the
265
+ `before` option, passing the label of the column to the left of which you want the new column to be added:
266
+
267
+ ```ruby
268
+ table = Tabulo::Table.new([1, 2, 3], :itself, :odd?)
269
+ table.add_column(:even?, before: :odd?)
270
+ ```
271
+
272
+ ```
273
+ > puts table
274
+ +--------------+--------------+--------------+
275
+ | itself | even? | odd? |
276
+ +--------------+--------------+--------------+
277
+ | 1 | false | true |
278
+ | 2 | true | false |
279
+ | 5 | false | true |
280
+ +--------------+--------------+--------------+
281
+ ```
282
+
283
+ <a name="removing-columns"></a>
284
+ ### Removing columns [&#x2191;](#contents)
285
+
286
+ There is also a `#remove_column` method, for deleting an existing column from a table. Pass it
287
+ the label of the column you want to remove:
288
+
289
+ ```ruby
290
+ table.remove_column(:even?)
291
+ ```
292
+
293
+ <a name="title"></a>
294
+ ### Adding a title [&#x2191;](#contents)
295
+
296
+ You can give your table a title, using the `title` option:
297
+
298
+ ```ruby
299
+ table = Tabulo::Table.new([1, 2, 3], :itself, :even?, :odd?, title: "Numbers")
300
+ ```
301
+
302
+ ```
303
+ > puts table
304
+ +--------------------------------------------+
305
+ | Numbers |
306
+ +--------------+--------------+--------------+
307
+ | itself | even? | odd? |
308
+ +--------------+--------------+--------------+
309
+ | 1 | false | true |
310
+ | 2 | true | false |
311
+ | 3 | false | true |
312
+ +--------------+--------------+--------------+
313
+ ```
314
+
315
+ There is a caveat: Using the `title` option with the `:markdown` [border type](#borders) will cause
316
+ the rendered table to cease being valid Markdown, as unfortunately almost no markdown engines support
317
+ adding a captions (i.e. titles) to tables.
318
+
319
+ <a name="cell-alignment"></a>
320
+ ### Cell alignment [&#x2191;](#contents)
321
+
322
+ By default, column header text is center-aligned, while the content of each body cell is aligned
323
+ according to its data type. Numbers are right-aligned, text is left-aligned, and booleans (`false`
324
+ and `true`) are center-aligned.
325
+
326
+ This default behaviour can be set at the table level, by passing `:center`, `:left` or `:right`
327
+ to the `align_header` or `align_body` options when initializing the table:
328
+
329
+ ```ruby
330
+ table = Tabulo::Table.new([1, 2], :itself, :even?, align_header: :left, align_body: :right)
331
+ ```
332
+
333
+ The table-level alignment settings can be overridden for individual columns by
334
+ passing similarly-named options to `add_column`, e.g.:
335
+
336
+ ```ruby
337
+ table.add_column("Doubled", align_header: :right, align_body: :left) { |n| n * 2 }
338
+ ```
339
+
340
+ If a table title is present, it is center-aligned by default. This can be changed using the
341
+ `align_title` option when initializing the table:
342
+
343
+ ```ruby
344
+ table = Tabulo::Table.new([1, 2], :itself, :even?, title: "Numbers", align_title: :left)
345
+ ```
346
+
347
+ ### Column width, wrapping and truncation [&#x2191;](#contents)
348
+
349
+ <a name="fixed-column-widths"></a>
350
+ #### Configuring fixed widths [&#x2191;](#contents)
351
+
352
+ By default, column width is fixed at 12 characters, plus 1 character of padding on either side.
353
+ This can be adjusted on a column-by-column basis using the `width` option of `add_column`:
354
+
355
+ ```ruby
356
+ table = Tabulo::Table.new([1, 2]) do |t|
357
+ t.add_column(:itself, width: 6)
358
+ t.add_column(:even?, width: 9)
359
+ end
360
+ ```
361
+
362
+ ```
363
+ > puts table
364
+ +--------+-----------+
365
+ | itself | even? |
366
+ +--------+-----------+
367
+ | 1 | false |
368
+ | 2 | true |
369
+ +--------+-----------+
370
+ ```
371
+
372
+ If you want to set the default column width for all columns of the table to something other
373
+ than 12, use the `column_width` option when initializing the table:
374
+
375
+ ```ruby
376
+ table = Tabulo::Table.new([1, 2], :itself, :even?, column_width: 6)
377
+ ```
378
+
379
+ ```
380
+ > puts table
381
+ +--------+--------+
382
+ | itself | even? |
383
+ +--------+--------+
384
+ | 1 | false |
385
+ | 2 | true |
386
+ +--------+--------+
387
+ ```
388
+
389
+ Widths set for individual columns always override the default column width for the table.
390
+
391
+ <a name="pack"></a>
392
+ #### Automating column widths [&#x2191;](#contents)
393
+
394
+ Instead of setting column widths &ldquo;manually&rdquo;, you can tell the table to sort out the widths
395
+ itself, so that each column is just wide enough for its header and contents (plus a character
396
+ of padding on either side):
397
+
398
+ ```ruby
399
+ table = Tabulo::Table.new(["short", "here is a longer phrase"], :itself, :size)
400
+ table.pack
401
+ ```
402
+
403
+ ```
404
+ > puts table
405
+ +-------------------------+------+
406
+ | itself | size |
407
+ +-------------------------+------+
408
+ | short | 5 |
409
+ | here is a longer phrase | 23 |
410
+ +-------------------------+------+
411
+ ```
412
+
413
+ If the table [title](#title) happens to be too long to for the existing width of the table, `pack`
414
+ will also arrange for the table to be widened sufficiently to accommodate it without wrapping:
415
+
416
+ ```ruby
417
+ table = Tabulo::Table.new(["a", "b"], :itself, :size, title: "Here are some letters of the alphabet")
418
+ table.pack
419
+ ```
420
+
421
+ ```
422
+ > puts table
423
+ +---------------------------------------+
424
+ | Here are some letters of the alphabet |
425
+ +-------------------+-------------------+
426
+ | itself | size |
427
+ +-------------------+-------------------+
428
+ | a | 1 |
429
+ | b | 1 |
430
+ +-------------------+-------------------+
431
+ ```
432
+
433
+ The `pack` method returns the table itself, so you can &ldquo;pack-and-print&rdquo; in one go:
434
+
435
+ ```ruby
436
+ puts Tabulo::Table.new(["short", "here is a longer phrase"], :itself, :size).pack
437
+ ```
438
+
439
+ <a name="max-table-width"></a>
440
+ You can manually place an upper limit on the total width of the table when packing:
441
+
442
+ ```ruby
443
+ puts Tabulo::Table.new(["short", "here is a longer phrase"], :itself, :size).pack(max_table_width: 24)
444
+ ```
445
+
446
+ ```
447
+ +---------------+------+
448
+ | itself | size |
449
+ +---------------+------+
450
+ | short | 5 |
451
+ | here is a lon | 23 |
452
+ | ger phrase | |
453
+ +---------------+------+
454
+ ```
455
+
456
+ Or if you simply call `pack` with no arguments (or if you explicitly call
457
+ `pack(max_table_width: :auto)`), the table width will automatically be capped at the
458
+ width of your terminal.
459
+
460
+ If you want the table width not to be capped at all, call `pack(max_table_width: nil)`.
461
+
462
+ If the table cannot be fit within the width of the terminal, or the specified maximum width,
463
+ then column widths are reduced as required, with wrapping or truncation then occuring as
464
+ necessary (see [Overflow handling](#overflow-handling)). Under the hood, a character of width
465
+ is deducted column by column&mdash;the widest column being targetted each time&mdash;until
466
+ the table will fit.
467
+
468
+ Note that `pack`ing the table necessarily involves traversing the entire collection up front as
469
+ the maximum cell width needs to be calculated for each column. You may not want to do this
470
+ if the collection is very large.
471
+
472
+ Note also the effect of `pack` is to fix the column widths as appropriate to the formatted cell
473
+ contents given the state of the underlying collection _at the point of packing_. If the underlying
474
+ collection changes between that point, and when the table is printed, then the columns will _not_ be
475
+ resized yet again on printing. This is a consequence of the table always being essentially a
476
+ &ldquo;live view&rdquo; on the underlying collection: formatted contents are never cached within the
477
+ table itself. There are [ways around this](#freezing-a-table), however, if this is not the desired
478
+ behaviour&mdash;see [below](#freezing-a-table).
479
+
480
+ <a name="configuring-padding"></a>
481
+ #### Configuring padding [&#x2191;](#contents)
482
+
483
+ The single character of padding either side of each column is not counted in the column width.
484
+ The amount of this extra padding can be configured for the table as a whole, using the `column_padding`
485
+ option passed to `Table.new`&mdash;the default value of this option being `1`.
486
+
487
+ Passing a single integer to this option causes the given amount of padding to be applied to each
488
+ side of each column. For example:
489
+
490
+ ```ruby
491
+ table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?, column_padding: 0)
492
+ ```
493
+
494
+ ```
495
+ > puts table
496
+ +------------+------------+------------+
497
+ | itself | even? | odd? |
498
+ +------------+------------+------------+
499
+ | 1| false | true |
500
+ | 2| true | false |
501
+ | 5| false | true |
502
+ +------------+------------+------------+
503
+ ```
504
+
505
+ Passing an array of _two_ integers to this option configures the left and right padding for each
506
+ column, according to the first and second element of the array, respectively. For example:
507
+
508
+ ```ruby
509
+ table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?, column_padding: [0, 2])
510
+ ```
511
+
512
+ ```
513
+ > puts table
514
+ +--------------+--------------+--------------+
515
+ | itself | even? | odd? |
516
+ +--------------+--------------+--------------+
517
+ | 1 | false | true |
518
+ | 2 | true | false |
519
+ | 5 | false | true |
520
+ +--------------+--------------+--------------+
521
+ ```
522
+
523
+ Padding can also be configured on a column-by-column basis, using the `padding` option when calling
524
+ `add_column`:
525
+
526
+ ```ruby
527
+ table = Tabulo::Table.new([1, 2, 5], :itself, :even?)
528
+ table.add_column(:odd?, padding: 2)
529
+ ```
530
+
531
+ ```
532
+ > puts table
533
+ +--------------+--------------+------------------+
534
+ | itself | even? | odd? |
535
+ +--------------+--------------+------------------+
536
+ | 1 | false | true |
537
+ | 2 | true | false |
538
+ | 5 | false | true |
539
+ +--------------+--------------+------------------+
540
+ ```
541
+
542
+ This column-level `padding` setting always overrides any table-level `column_padding` setting, for
543
+ the column in question.
544
+
545
+ <a name="overflow-handling"></a>
546
+ #### Overflow handling [&#x2191;](#contents)
547
+
548
+ By default, if cell contents exceed their column width, they are wrapped for as many rows as
549
+ required:
550
+
551
+ ```ruby
552
+ table = Tabulo::Table.new(
553
+ ["hello", "abcdefghijklmnopqrstuvwxyz"],
554
+ :itself, :length
555
+ )
556
+ ```
557
+
558
+ ```
559
+ > puts table
560
+ +--------------+--------------+
561
+ | itself | length |
562
+ +--------------+--------------+
563
+ | hello | 5 |
564
+ | abcdefghijkl | 26 |
565
+ | mnopqrstuvwx | |
566
+ | yz | |
567
+ +--------------+--------------+
568
+ ```
569
+
570
+ Wrapping behaviour is configured for the table as a whole using the `wrap_header_cells_to` option
571
+ for header cells and `wrap_body_cells_to` for body cells, both of which default to `nil`, meaning
572
+ that cells are wrapped to as many rows as required. Passing an `Integer` limits wrapping to the given
573
+ number of rows, with content truncated from that point on. The `~` character is appended to the
574
+ outputted cell content to show that truncation has occurred:
575
+
576
+ ```ruby
577
+ table = Tabulo::Table.new(
578
+ ["hello", "abcdefghijklmnopqrstuvwxyz"],
579
+ :itself, :length,
580
+ wrap_body_cells_to: 1
581
+ )
582
+ ```
583
+
584
+ ```
585
+ > puts table
586
+ +--------------+--------------+
587
+ | itself | length |
588
+ +--------------+--------------+
589
+ | hello | 5 |
590
+ | abcdefghijkl~| 26 |
591
+ +--------------+--------------+
592
+ ```
593
+
594
+ The character used to indicate truncation, which defaults to `~`, can be configured using the
595
+ `truncation_indicator` option passed to `Table.new`.
596
+
597
+ <a name="manual-wrapping"></a>
598
+ #### Manual cell wrapping [&#x2191;](#contents)
599
+
600
+ You can &ldquo;manually&rdquo; wrap the content of a title, header or body cell at a particular
601
+ point, simply by placing a newline character at that point:
602
+
603
+ ```ruby
604
+ table = Tabulo::Table.new(1..3) do |t|
605
+ t.add_column("The number\nitself", &:itself)
606
+ t.add_column("Even?", &:even?)
607
+ t.add_column("Odd?", &:odd?)
608
+ end
609
+ ```
610
+
611
+ ```
612
+ > puts table
613
+ +--------------+--------------+--------------+
614
+ | The number | Even? | Odd? |
615
+ | itself | | |
616
+ +--------------+--------------+--------------+
617
+ | 1 | false | true |
618
+ | 2 | true | false |
619
+ | 3 | false | true |
620
+ +--------------+--------------+--------------+
621
+ ```
622
+
623
+ <a name="formatting-cell-values"></a>
624
+ ### Formatting cell values [&#x2191;](#contents)
625
+
626
+ While the callable passed to `add_column` determines the underyling, calculated value in each
627
+ cell of the column, there is a separate concept, of a &ldquo;formatter&rdquo;, that determines how
628
+ that value will be visually displayed. By default, `.to_s` is called on the underlying cell value to
629
+ &ldquo;format&rdquo; it; however, you can format it differently by passing another callable to the
630
+ `formatter` option of `add_column`:
631
+
632
+ ```ruby
633
+ table = Tabulo::Table.new(1..3) do |t|
634
+ t.add_column("N", &:itself)
635
+ t.add_column("Reciprocal", formatter: -> (n) { "%.2f" % n }) do |n|
636
+ 1.0 / n
637
+ end
638
+ end
639
+ ```
640
+
641
+ ```
642
+ > puts table
643
+ +--------------+--------------+
644
+ | N | Reciprocal |
645
+ +--------------+--------------+
646
+ | 1 | 1.00 |
647
+ | 2 | 0.50 |
648
+ | 3 | 0.33 |
649
+ +--------------+--------------+
650
+ ```
651
+
652
+ Note the numbers in the &ldquo;Reciprocal&rdquo; column in this example are still right-aligned, even though
653
+ the callable passed to `formatter` returns a String. Default cell alignment is determined by the type
654
+ of the underlying cell value, not the way it is formatted. This is usually the desired result.
655
+
656
+ If you want to set the default formatter for all columns of the table to something other than
657
+ `#to_s`, use the `formatter` option when initializing the table:
658
+
659
+ ```ruby
660
+ table = Tabulo::Table.new(1..3, formatter: -> (n) { "%.2f" % n }) do |t|
661
+ t.add_column("N", &:itself)
662
+ t.add_column("Reciprocal") { |n| 1.0 / n }
663
+ t.add_column("Half") { |n| n / 2.0 }
664
+ end
665
+ ```
666
+
667
+ ```
668
+ > puts table
669
+ +--------------+--------------+--------------+
670
+ | N | Reciprocal | Half |
671
+ +--------------+--------------+--------------+
672
+ | 1.00 | 1.00 | 0.50 |
673
+ | 2.00 | 0.50 | 1.00 |
674
+ | 3.00 | 0.33 | 1.50 |
675
+ +--------------+--------------+--------------+
676
+ ```
677
+
678
+ Formatters set for individual columns on calling `#add_column` always override the default formatter for
679
+ the table.
680
+
681
+ The `formatter` callback also has an alternative, 2-parameter version. If `formatter` is passed
682
+ a 2-parameter callable, the second parameter will be given a `CellData` instance,
683
+ containing additional information about the cell that may be useful in determining how to format
684
+ it&mdash;see the [documentation](https://www.rubydoc.info/gems/tabulo/2.6.0/Tabulo/CellData.html)
685
+ for details.
686
+
687
+ <a name="colours-and-styling"></a>
688
+ ### Colours and other styling [&#x2191;](#contents)
689
+
690
+ <a name="styling-cell-content"></a>
691
+ #### Styling cell content [&#x2191;](#contents)
692
+
693
+ In most terminals, if you want to print text that is coloured, or has certain other styles such as
694
+ underlining, you need to use ANSI escape sequences, either directly, or by means of a library such
695
+ as [Rainbow](http://github.com/sickill/rainbow) that uses them internally. Tabulo needs to properly
696
+ account for escape sequences when performing the width calculations required to render tables.
697
+ The `styler` option on the `add_column` method is intended to facilitate this.
698
+
699
+ For example, suppose you have a table to which you want to add a column that
700
+ displays `true` in green if a given number is even, or else displays `false` in red.
701
+ You can achieve this as follows using raw ANSI escape codes:
702
+
703
+ ```ruby
704
+ table.add_column(
705
+ :even?,
706
+ styler: -> (cell_value, s) { cell_value ? "\033[32m#{s}\033[0m" : "\033[31m#{s}\033[0m" }
707
+ )
708
+ ```
709
+
710
+ Or, if you are using the [rainbow](https://github.com/sickill/rainbow) gem for colouring, you
711
+ could do the following:
712
+
713
+ ```ruby
714
+ require "rainbow"
715
+
716
+ # ...
717
+
718
+ table.add_column(
719
+ :even?,
720
+ styler: -> (cell_value, s) { cell_value ? Rainbow(s).green : Rainbow(s).red }
721
+ )
722
+ ```
723
+
724
+ The `styler` option should be passed a callable that takes either two, three or four parameters.
725
+ The first parameter represents the underlying value of the cell (in this case a boolean indicating whether the
726
+ number is even). The second parameter represents the formatted string value of that cell, i.e. the cell
727
+ content after any processing by the [formatter](#formatting-cell-values). The third and fourth
728
+ parameters are optional, and contain further information about the cell and its contents that may be useful in
729
+ determining how to style it. See the [documentation](https://www.rubydoc.info/gems/tabulo/2.6.0/Tabulo/CellData.html)
730
+ for details.
731
+
732
+ If the content of a cell is wrapped over multiple lines, then the `styler` will be called once
733
+ per line, so that each line of the cell will have the escape sequence applied to it separately
734
+ (ensuring the styling doesn&#8217;t bleed into neighbouring cells).
735
+
736
+ If the content of a cell has been [truncated](#overflow-handling), then whatever colours or other
737
+ styling apply to the cell content will also be applied the truncation indicator character.
738
+
739
+ <a name="styling-column-headers"></a>
740
+ #### Styling column headers [&#x2191;](#contents)
741
+
742
+ If you want to apply colours or other styling to the content of a column header, as opposed
743
+ to cells in the table body, use the `header_styler` option, e.g.:
744
+
745
+ ```ruby
746
+ table.add_column(:even?, header_styler: -> (s) { "\033[32m#{s}\033[0m" })
747
+ ```
748
+
749
+ The `header_styler` option accepts a 1-, 2- or 3-parameter callable. See the
750
+ [documentation](https://www.rubydoc.info/gems/tabulo/2.6.0/Tabulo/Table#add_column-instance_method)
751
+ for details.
752
+
753
+ <a name="styling-title"></a>
754
+ #### Styling the table title [&#x2191;](#contents)
755
+
756
+ To apply colours or other styling to the table title, if present, use the `title_styler` option
757
+ when initializing the table. This accepts a single-parameter callable:
758
+
759
+ ```ruby
760
+ table = Tabulo::Table.new(1..5, :itself, :even?, :odd?, title: "Numbers", title_styler: -> (s) { "\033[32m#{s}\033[0m" })
761
+ ```
762
+
763
+ The `title_styler` option accepts a 1- or 2-parameter callable. See the
764
+ [documentation](https://www.rubydoc.info/gems/tabulo/2.6.0/Tabulo/Table#initialize-instance_method)
765
+ for details.
766
+
767
+ <a name="styling-borders"></a>
768
+ #### Styling borders [&#x2191;](#contents)
769
+
770
+ Styling can also be applied to borders and dividing lines, using the `border_styler` option when
771
+ initializing the table, e.g.:
772
+
773
+ ```ruby
774
+ table = Tabulo::Table.new(1..5, :itself, :even?, :odd?, border_styler: -> (s) { "\033[32m#{s}\033[0m" })
775
+ ```
776
+
777
+ <a name="default-styles"></a>
778
+ #### Setting default styles [&#x2191;](#contents)
779
+
780
+ By default, no styling is applied to the headers or body content of a column unless configured to do
781
+ so via the `header_styler` or `styler` option when calling `add_column` for that particular column.
782
+ It is possible to apply styling by default to _all_ columns in a table, however, as the table initializer
783
+ also accepts both a `header_styler` and a `styler` option. For example, if you want all the header text
784
+ in the table to be green, you could do:
785
+
786
+ ```ruby
787
+ table = Tabulo::Table.new(1..5, :itself, :even?, :odd?, header_styler: -> (s) { "\033[32m#{s}\033[0m" })
788
+ ```
789
+
790
+ Now, all columns in the table will automatically have green header text, unless overridden by another
791
+ header styler being passed to `#add_column`.
792
+
793
+ <a name="repeating-headers"></a>
794
+ ### Repeating headers [&#x2191;](#contents)
795
+
796
+ By default, headers are only shown once, at the top of the table (`header_frequency: :start`). If
797
+ `header_frequency` is passed `nil`, headers are not shown at all; or, if passed an `Integer` N,
798
+ headers are shown at the top and then repeated every N rows. This can be handy when you&#8217;re looking
799
+ at table that&#8217;s taller than your terminal.
800
+
801
+ E.g.:
802
+
803
+ ```ruby
804
+ table = Tabulo::Table.new(1..10, :itself, :even?, header_frequency: 5)
805
+ ```
806
+
807
+ ```
808
+ > puts table
809
+ +--------------+--------------+
810
+ | itself | even? |
811
+ +--------------+--------------+
812
+ | 1 | false |
813
+ | 2 | true |
814
+ | 3 | false |
815
+ | 4 | true |
816
+ | 5 | false |
817
+ +--------------+--------------+
818
+ | itself | even? |
819
+ +--------------+--------------+
820
+ | 6 | true |
821
+ | 7 | false |
822
+ | 8 | true |
823
+ | 9 | false |
824
+ | 10 | true |
825
+ +--------------+--------------+
826
+ ```
827
+
828
+ Note that if the table has a [title](#title), it will not be repeated; only column headers are repeated.
829
+
830
+ <a name="enumerator"></a>
831
+ ### Using a Table Enumerator [&#x2191;](#contents)
832
+
833
+ Because it&#8217;s an `Enumerable`, a `Tabulo::Table` can also give you an `Enumerator`,
834
+ which is useful when you want to step through rows one at a time. In a Rails console,
835
+ for example, you might do this:
836
+
837
+ ```
838
+ > e = Tabulo::Table.new(User.find_each) do |t|
839
+ t.add_column(:id)
840
+ t.add_column(:email, width: 24)
841
+ end.to_enum # <-- make an Enumerator
842
+ ...
843
+ > puts e.next
844
+ +--------------+--------------------------+
845
+ | id | email |
846
+ +--------------+--------------------------+
847
+ | 1 | jane@example.com |
848
+ => nil
849
+ > puts e.next
850
+ | 2 | betty@example.net |
851
+ => nil
852
+ ```
853
+
854
+ Note the use of `.find_each`: we can start printing the table without having to load the entire
855
+ underlying collection. (This is negated if we [pack](#pack) the table, however, since
856
+ in that case the entire collection must be traversed up front in order for column widths to be
857
+ calculated.)
858
+
859
+ <a name="accessing-cell-values"></a>
860
+ ### Accessing cell values [&#x2191;](#contents)
861
+
862
+ Each `Tabulo::Table` is an `Enumerable` of which each element is a `Tabulo::Row`. Each `Tabulo::Row`
863
+ is itself an `Enumerable`, of `Tabulo::Cell`. The `Tabulo::Cell#value` method will return the
864
+ underlying value of the cell; while `Tabulo::Cell#formatted_content` will return its formatted content
865
+ as a string.
866
+
867
+ A `Tabulo::Row` can also
868
+ be converted to a `Hash` for keyed access. For example:
869
+
870
+ ```ruby
871
+ table = Tabulo::Table.new(1..3, :itself, :even?, :odd?)
872
+
873
+ table.each do |row|
874
+ row.each { |cell| puts cell.value } # 1, false, true...2, true, false...3, false, true
875
+ puts row.to_h[:even?].value # false...true...false
876
+ end
877
+ ```
878
+
879
+ The column label (being the first argument that was passed to `add_column`, converted if necessary
880
+ to a `Symbol`), always forms the key for the purposes of this `Hash`:
881
+
882
+ ```ruby
883
+ table = Tabulo::Table.new(1..3) do |t|
884
+ t.add_column("Number") { |n| n }
885
+ t.add_column(:doubled, header: "Number X 2") { |n| n * 2 }
886
+ end
887
+
888
+ table.each do |row|
889
+ cells = row.to_h
890
+ puts cells[:Number].value # 1...2...3...
891
+ puts cells[:doubled].value # 2...4...6...
892
+ end
893
+ ```
894
+
895
+ <a name="accessing-sources"></a>
896
+ ### Accessing the underlying enumerable [&#x2191;](#contents)
897
+
898
+ The underlying enumerable for a table can be retrieved by calling the `sources` getter:
899
+
900
+ ```ruby
901
+ table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?)
902
+ ```
903
+
904
+ ```
905
+ > table.sources
906
+ => [1, 2, 5]
907
+ ```
908
+
909
+ There is also a corresponding setter, meaning you can reuse the same table to tabulate
910
+ a different data set, without having to reconfigure the columns and other options from scratch:
911
+
912
+ ```ruby
913
+ table.sources = [50, 60]
914
+ ```
915
+
916
+ ```
917
+ > table.sources
918
+ => [50, 60]
919
+ ```
920
+
921
+ In addition, the element of the underlying enumerable corresponding to a particular
922
+ row can be accessed by calling the `source` method on that row:
923
+
924
+ ```ruby
925
+ table.each do |row|
926
+ puts row.source # 50...60...
927
+ end
928
+ ```
929
+
930
+ <a name="transposition"></a>
931
+ ### Transposing rows and columns [&#x2191;](#contents)
932
+
933
+ By default, Tabulo generates a table in which each row corresponds to a _record_, i.e. an element of
934
+ the underlying enumerable, and each column to a _field_. However, there are times when one instead
935
+ wants each row to represent a field, and each column a record. This is generally the case when there
936
+ are a small number or records but a large number of fields. To produce such a table, we can first
937
+ initialize an ordinary table, specifying fields as columns, and then call `transpose`, which returns
938
+ a new table in which the rows and columns are swapped:
939
+
940
+ ```ruby
941
+ > puts Tabulo::Table.new(-1..1, :even?, :odd?, :zero?, :pred, :succ, :abs).transpose
942
+ ```
943
+ ```
944
+ +-------+--------------+--------------+--------------+
945
+ | | -1 | 0 | 1 |
946
+ +-------+--------------+--------------+--------------+
947
+ | even? | false | true | false |
948
+ | odd? | true | false | true |
949
+ | zero? | false | true | false |
950
+ | pred | -2 | -1 | 0 |
951
+ | succ | 0 | 1 | 2 |
952
+ | abs | 1 | 0 | 1 |
953
+ +-------+--------------+--------------+--------------+
954
+ ```
955
+
956
+ By default, a header row is added to the new table, showing the string value of the element
957
+ represented in that column. This can be configured, however, along with other aspects of
958
+ `transpose`&#8217;s behaviour. For details, see the
959
+ [documentation](https://www.rubydoc.info/gems/tabulo/2.6.0/Tabulo/Table#transpose-instance_method).
960
+
961
+ <a name="borders"></a>
962
+ ### Configuring borders [&#x2191;](#contents)
963
+
964
+ You can configure the kind of border and divider characters that are used when the table is printed.
965
+ This is done using the `border` option passed to `Table.new`. The options are as follows.
966
+
967
+ `:ascii`&mdash;this is the default; the table is drawn entirely with characters in the ASCII set:
968
+
969
+ ```
970
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :ascii)
971
+ +--------------+--------------+--------------+
972
+ | itself | even? | odd? |
973
+ +--------------+--------------+--------------+
974
+ | 1 | false | true |
975
+ | 2 | true | false |
976
+ | 3 | false | true |
977
+ +--------------+--------------+--------------+
978
+
979
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :ascii, title: "Numbers")
980
+ +--------------------------------------------+
981
+ | Numbers |
982
+ +--------------+--------------+--------------+
983
+ | itself | even? | odd? |
984
+ +--------------+--------------+--------------+
985
+ | 1 | false | true |
986
+ | 2 | true | false |
987
+ | 3 | false | true |
988
+ +--------------+--------------+--------------+
989
+ ```
990
+
991
+ `:modern`&mdash;uses smoothly joined Unicode characters:
992
+
993
+ ```
994
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :modern)
995
+ ┌──────────────┬──────────────┬──────────────┐
996
+ │ itself │ even? │ odd? │
997
+ ├──────────────┼──────────────┼──────────────┤
998
+ │ 1 │ false │ true │
999
+ │ 2 │ true │ false │
1000
+ │ 3 │ false │ true │
1001
+ └──────────────┴──────────────┴──────────────┘
1002
+
1003
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :modern, title: "Numbers")
1004
+ ┌────────────────────────────────────────────┐
1005
+ │ Numbers │
1006
+ ├──────────────┬──────────────┬──────────────┤
1007
+ │ itself │ even? │ odd? │
1008
+ ├──────────────┼──────────────┼──────────────┤
1009
+ │ 1 │ false │ true │
1010
+ │ 2 │ true │ false │
1011
+ │ 3 │ false │ true │
1012
+ └──────────────┴──────────────┴──────────────┘
1013
+ ```
1014
+
1015
+ `:markdown`&mdash;renders a GitHub flavoured Markdown table:
1016
+
1017
+ ```
1018
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :markdown)
1019
+ | itself | even? | odd? |
1020
+ |--------------|--------------|--------------|
1021
+ | 1 | false | true |
1022
+ | 2 | true | false |
1023
+ | 3 | false | true |
1024
+
1025
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :markdown, title: "Numbers")
1026
+ | Numbers |
1027
+ | itself | even? | odd? |
1028
+ |--------------|--------------|--------------|
1029
+ | 1 | false | true |
1030
+ | 2 | true | false |
1031
+ | 3 | false | true |
1032
+ ```
1033
+
1034
+ _However_, note that when a table is rendered using the `:markdown` border type in combination with a
1035
+ (non-`nil`) `title`, the result will be _invalid Markdown_. This is because Markdown engines do not
1036
+ generally support adding a caption (i.e. title) element to tables.
1037
+
1038
+ `:blank`&mdash;no border or divider characters are printed:
1039
+
1040
+ ```
1041
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :blank)
1042
+ itself even? odd?
1043
+ 1 false true
1044
+ 2 true false
1045
+ 3 false true
1046
+
1047
+
1048
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :blank, title: "Numbers")
1049
+ Numbers
1050
+ itself even? odd?
1051
+ 1 false true
1052
+ 2 true false
1053
+ 3 false true
1054
+ ```
1055
+
1056
+ `:reduced_ascii`&mdash;similar to `:ascii`, but without vertical lines:
1057
+
1058
+ ```
1059
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :reduced_modern)
1060
+ -------------- -------------- --------------
1061
+ itself even? odd?
1062
+ -------------- -------------- --------------
1063
+ 1 false true
1064
+ 2 true false
1065
+ 3 false true
1066
+ -------------- -------------- --------------
1067
+
1068
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :reduced_modern, title: "Numbers")
1069
+ --------------------------------------------
1070
+ Numbers
1071
+ -------------- -------------- --------------
1072
+ itself even? odd?
1073
+ -------------- -------------- --------------
1074
+ 1 false true
1075
+ 2 true false
1076
+ 3 false true
1077
+ -------------- -------------- --------------
1078
+ ```
1079
+
1080
+ `:reduced_modern`&mdash;similar to `:modern`, but without vertical lines:
1081
+
1082
+ ```
1083
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :reduced_ascii)
1084
+ ────────────── ────────────── ──────────────
1085
+ itself even? odd?
1086
+ ────────────── ────────────── ──────────────
1087
+ 1 false true
1088
+ 2 true false
1089
+ 3 false true
1090
+ ────────────── ────────────── ──────────────
1091
+
1092
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :reduced_ascii, title: "Numbers")
1093
+ ────────────────────────────────────────────
1094
+ Numbers
1095
+ ────────────── ────────────── ──────────────
1096
+ itself even? odd?
1097
+ ────────────── ────────────── ──────────────
1098
+ 1 false true
1099
+ 2 true false
1100
+ 3 false true
1101
+ ────────────── ────────────── ──────────────
1102
+ ```
1103
+
1104
+ `:classic`&mdash;reproduces the default behaviour in Tabulo v1; this is like the `:ascii` option,
1105
+ but without a bottom border:
1106
+
1107
+ ```
1108
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :classic)
1109
+ +--------------+--------------+--------------+
1110
+ | itself | even? | odd? |
1111
+ +--------------+--------------+--------------+
1112
+ | 1 | false | true |
1113
+ | 2 | true | false |
1114
+ | 3 | false | true |
1115
+
1116
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :classic, title: "Numbers")
1117
+ +--------------------------------------------+
1118
+ | Numbers |
1119
+ +--------------+--------------+--------------+
1120
+ | itself | even? | odd? |
1121
+ +--------------+--------------+--------------+
1122
+ | 1 | false | true |
1123
+ | 2 | true | false |
1124
+ | 3 | false | true |
1125
+ ```
1126
+
1127
+ Note that, by default, none of the border options includes lines drawn _between_ rows in the body of the table.
1128
+ These are configured via a separate option: see [below](#dividers).
1129
+
1130
+ <a name="dividers"></a>
1131
+ ### Row dividers [&#x2191;](#contents)
1132
+
1133
+ To add lines between rows in the table body, use the `row_divider_frequency` option when initializing
1134
+ the table. The default value for this option is `nil`, meaning there are no dividing lines between
1135
+ rows. But if this option passed is a positive integer N, then a dividing line is inserted before
1136
+ every Nth row. For example:
1137
+
1138
+ ```
1139
+ > puts Tabulo::Table.new(1..6, :itself, :even?, :odd?, row_divider_frequency: 2)
1140
+ +--------------+--------------+--------------+
1141
+ | itself | even? | odd? |
1142
+ +--------------+--------------+--------------+
1143
+ | 1 | false | true |
1144
+ | 2 | true | false |
1145
+ +--------------+--------------+--------------+
1146
+ | 3 | false | true |
1147
+ | 4 | true | false |
1148
+ +--------------+--------------+--------------+
1149
+ | 5 | false | true |
1150
+ | 6 | true | false |
1151
+ +--------------+--------------+--------------+
1152
+ ```
1153
+
1154
+ If you want a line before every row, pass `1` to `row_divider_frequency`. For example:
1155
+
1156
+ ```
1157
+ > puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :modern, row_divider_frequency: 1)
1158
+ ┌──────────────┬──────────────┬──────────────┐
1159
+ │ itself │ even? │ odd? │
1160
+ ├──────────────┼──────────────┼──────────────┤
1161
+ │ 1 │ false │ true │
1162
+ ├──────────────┼──────────────┼──────────────┤
1163
+ │ 2 │ true │ false │
1164
+ ├──────────────┼──────────────┼──────────────┤
1165
+ │ 3 │ false │ true │
1166
+ └──────────────┴──────────────┴──────────────┘
1167
+ ```
1168
+
1169
+ <a name="freezing-a-table"></a>
1170
+ ### Using a table as a snapshot rather than as a dynamic view [&#x2191;](#contents)
1171
+
1172
+ The nature of a `Tabulo::Table` is that of a dynamic view onto the underlying `sources` enumerable
1173
+ from which it was initialized (or which was subsequently assigned to its `sources` attribute). That
1174
+ is, if the contents of the `sources` enumerable change subsequent to initialization of or assignment to
1175
+ `sources`, then the table when printed will show the `sources` as they are at the time of printing,
1176
+ not as they were at the time of initialization or assignment. For example:
1177
+
1178
+ ```ruby
1179
+ arr = [1, 2]
1180
+ table = Tabulo::Table.new(arr, :itself, :even?, :odd?)
1181
+ ```
1182
+
1183
+ ```
1184
+ > puts table
1185
+ +--------------+--------------+--------------+
1186
+ | itself | even? | odd? |
1187
+ +--------------+--------------+--------------+
1188
+ | 1 | false | true |
1189
+ | 2 | true | false |
1190
+ +--------------+--------------+--------------+
1191
+ ```
1192
+
1193
+ ```ruby
1194
+ arr << 3
1195
+ ```
1196
+
1197
+ ```
1198
+ > puts table
1199
+ +--------------+--------------+--------------+
1200
+ | itself | even? | odd? |
1201
+ +--------------+--------------+--------------+
1202
+ | 1 | false | true |
1203
+ | 2 | true | false |
1204
+ | 3 | false | true |
1205
+ +--------------+--------------+--------------+
1206
+ ```
1207
+
1208
+ In this example, even though no direct mutations have been made to `table`, the result
1209
+ of calling `puts table` has changed, in virtue of a mutation on the underyling enumerable `arr`.
1210
+
1211
+ A similar behaviour can be seen when `sources` is an ActiveRecord query, and there
1212
+ are changes to the relevant database table(s) such that the result of the query changes. This is
1213
+ worth bearing in mind when calling [`pack`](#pack) on a table, since if the `sources` enumerable
1214
+ changes between `pack`ing and printing, then the column widths calculated by the `pack` method
1215
+ may no longer be &ldquo;just right&rdquo; given the changed `sources`.
1216
+
1217
+ If this is not the desired behaviour, there are ways around this. For example, if dealing with an
1218
+ ActiveRecord relation, you can convert the query to a plain array before initializing the table:
1219
+
1220
+ ```ruby
1221
+ sources = User.all.to_a
1222
+ table = Tabulo::Table.new(sources, :id, :first_name, :last_name)
1223
+ table.pack
1224
+ puts table
1225
+ ```
1226
+
1227
+ Passing an `Array` rather than the ActiveRecord query directly means that if there are changes to
1228
+ the content of the `users` database table, these will not be reflected in the rendered content of
1229
+ the `Tabulo::Table` (unless some of the `Tabulo::Table` columns are based on callables that perform
1230
+ further database queries when called&hellip;).
1231
+
1232
+ Note that it is also possible simply to store the string value of a table for later use,
1233
+ rather than the table itself:
1234
+
1235
+ ```ruby
1236
+ rendered_table = Tabulo::Table.new(1..10, :itself, :even?, :odd?).pack.to_s
1237
+ ```
1238
+
1239
+ <a name="motivation"></a>
1240
+ ## Comparison with other libraries [&#x2191;](#contents)
1241
+
1242
+ There are other libraries for generating plain text tables in Ruby. Popular among these are:
1243
+
1244
+ * [terminal-table](https://github.com/tj/terminal-table)
1245
+ * [tty-table](https://github.com/piotrmurach/tty-table)
1246
+ * [table\_print](https://github.com/arches/table_print)
1247
+ * [hirb](https://github.com/cldwalker/hirb)
1248
+
1249
+ *DISCLAIMER: My comments regarding these other libraries are based only on my own, possibly flawed
1250
+ reading of the documentation for, and experimentation with, these libraries at the time of my
1251
+ writing this. Their APIs, features or documentation may well change between when I write this, and
1252
+ when you read it. Please consult the libraries&#8217; own documentation for yourself, rather than relying
1253
+ on these comments.*
1254
+
1255
+ While these libraries have their strengths, I have personally found that, for the common use case of
1256
+ printing a table on the basis of some underlying enumerable collection (such as an ActiveRecord
1257
+ query result), using these libraries feels more cumbersome than it could be.
1258
+
1259
+ For example, suppose we have called `User.all` from the Rails console, and want to print
1260
+ a table showing the email, first name, last name and ID of each user,
1261
+ with column headings. Also, we want the ID column to be right-aligned, because it&#8217;s a number.
1262
+
1263
+ In **terminal-table**, we could achieve this as follows:
1264
+
1265
+ ```ruby
1266
+ rows = User.all.map { |u| [u.email, u.first_name, u.last_name, u.id] }
1267
+ headings = ["email", "first name", "last name", "id"]
1268
+ table = Terminal::Table.new(headings: headings, rows: rows)
1269
+ table.align_column(3, :right)
1270
+ puts table
1271
+ ```
1272
+
1273
+ The problem here is that there is no single source of knowledge about which columns
1274
+ appear, and in which order. If we want to add another column to the left of &ldquo;email&rdquo;,
1275
+ we need to amend the rows array, and the headings array, and the index passed to `align_column`.
1276
+ We bear the burden of keeping these three in sync. This is not be a big deal for small one-off
1277
+ tables, but for tables that have many columns, or that are constructed
1278
+ dynamically based on user input or other runtime factors determining the columns
1279
+ to be included, this can be a hassle and a source of brittleness.
1280
+
1281
+ **tty-table** has a somewhat different API to `terminal-table`. It offers both a
1282
+ &ldquo;row-based&rdquo; and a &ldquo;column-based&rdquo; method of initializing a table. The row-based method
1283
+ is similar to `terminal-table`&#8217;s in that it burdens the developer with syncing the
1284
+ column ordering across multiple code locations. The &ldquo;column-based&rdquo; API for `tty-table`, on
1285
+ the other hand, seems to avoid this problem. One way of using it is like this:
1286
+
1287
+ ```ruby
1288
+ users = User.all
1289
+ table = TTY::Table.new [
1290
+ {
1291
+ "email" => users.map(&:email),
1292
+ "first name" => users.map(&:first_name),
1293
+ "last name" => users.map(&:last_name),
1294
+ "id" => users.map(&:id),
1295
+ }
1296
+ ]
1297
+ puts table
1298
+ ```
1299
+
1300
+ While this doesn&#8217;t seem too bad, it does mean that the underlying collection (`users`) has to
1301
+ be traversed multiple times, once for each column, which is inefficient, particularly
1302
+ if the underlying collection is large. In addition, it&#8217;s not clear how to pass separate
1303
+ formatting information for each column when initializing in this way. (Perhaps there is a way to do
1304
+ this, but if there is, it doesn&#8217;t seem to be documented.) So it seems we still have to use
1305
+ `table.align_column(3, :right)`, which again burdens us with keeping the column index
1306
+ passed to `align_column` in sync.
1307
+
1308
+ As for **table\_print**, this is a handy gem for quickly tabulating ActiveRecord
1309
+ collections from the Rails console. `table_print` is similar to `tabulo` in that it has a
1310
+ column-based API, so it doesn&#8217;t suffer from the multiple-source-of-knowledge issue in regards to
1311
+ column orderings. However, it lacks certain other useful features, such as the ability to repeat
1312
+ headers every N rows, the automatic alignment of columns based on cell content (numbers right,
1313
+ strings left), and a quick and easy way to automatically resize columns to accommodate cell content
1314
+ without overflowing the terminal. Also, as of the time of writing, `table_print`&#8217;s last significant
1315
+ commit (ignoring a deprecation warning fix in April 2018) was in March 2016.
1316
+
1317
+ Finally, it is worth mentioning the **hirb** library. This is similar to `table_print`, in that
1318
+ it&#8217;s
1319
+ well suited to quickly displaying ActiveRecord collections from the Rails console. However, like
1320
+ `table_print`, there are certain useful features it&#8217;s lacking; and using it outside the console
1321
+ environment seems cumbersome. Moreover, it seems no longer to be maintained. At the time of writing,
1322
+ its last commit was in March 2015.
1323
+
1324
+ <a name="contributing"></a>
1325
+ ## Contributing [&#x2191;](#contents)
1326
+
1327
+ Issues and pull requests are welcome on GitHub at https://github.com/matt-harvey/tabulo.
1328
+
1329
+ To start working on Tabulo, `git clone` and `cd` into your fork of the repo, then run `bin/setup` to
1330
+ install dependencies.
1331
+
1332
+ `bin/console` will give you an interactive prompt that will allow you to experiment; and
1333
+ `bundle exec rake spec` will run the test suite. For a list of other Rake tasks that are available in
1334
+ the development environment, run `bundle exec rake -T`.
1335
+
1336
+ ## License [&#x2191;](#contents)
1337
+
1338
+ The gem is available as open source under the terms of the [MIT
1339
+ License](http://opensource.org/licenses/MIT).
1340
+
1341
+ [Gem Version]: https://rubygems.org/gems/tabulo
1342
+ [Documentation]: http://www.rubydoc.info/gems/tabulo/2.6.0
1343
+ [Build Status]: https://travis-ci.org/matt-harvey/tabulo
1344
+ [Coverage Status]: https://coveralls.io/r/matt-harvey/tabulo
1345
+ [Code Climate]: https://codeclimate.com/github/matt-harvey/tabulo
1346
+ [Awesome Ruby]: https://github.com/markets/awesome-ruby#cli-utilities
1347
+
1348
+ [GV img]: https://img.shields.io/gem/v/tabulo.svg
1349
+ [DC img]: https://img.shields.io/badge/documentation-v2.6.0-blue.svg
1350
+ [BS img]: https://img.shields.io/travis/matt-harvey/tabulo.svg
1351
+ [CS img]: https://img.shields.io/coveralls/matt-harvey/tabulo.svg
1352
+ [CC img]: https://codeclimate.com/github/matt-harvey/tabulo/badges/gpa.svg
1353
+ [AR img]: https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg