tabulo 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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