table_tennis 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 03c38407a65288ded066cc1986f53453d8dafe6e51096ea597e843c8ad115afb
4
+ data.tar.gz: a76db1bbb2a0ef775a629f71f6f76c2ee5c67a42255ba9856c5ccb9f66bb89bf
5
+ SHA512:
6
+ metadata.gz: 51d515f45a6f62d6ede57e5b8d752ccd5a637dc107d170e526693600d91e9a91988cef1ee570104c413951759cf673e80d569076544a137398ac09bf1b2735e7
7
+ data.tar.gz: 3e269c16f3a95e4e948d55d8713d5349d2e5b5527b97c0fd5722d8cee88e17f182078d720b9bafb6d233dc9feb76f209a8b17780549ad03468817dcaaeb2aedb
@@ -0,0 +1,26 @@
1
+ name: test
2
+
3
+ on:
4
+ push:
5
+ paths-ignore:
6
+ - README.md
7
+ - 'screenshots/**'
8
+ pull_request:
9
+ workflow_dispatch:
10
+
11
+ jobs:
12
+ test:
13
+ strategy:
14
+ max-parallel: 3
15
+ matrix:
16
+ os: [macos, ubuntu, windows]
17
+ ruby-version: [3.0, 3.4]
18
+ runs-on: ${{ matrix.os }}-latest
19
+ steps:
20
+ - uses: actions/checkout@v3
21
+ - uses: taiki-e/install-action@just
22
+ - uses: ruby/setup-ruby@v1
23
+ with:
24
+ bundler-cache: true
25
+ ruby-version: ${{ matrix.ruby-version }}
26
+ - run: just ci
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .envrc
2
+ .tool-versions
3
+ .vscode
4
+ *.gem
5
+ terminals/
data/.rubocop.yml ADDED
@@ -0,0 +1,58 @@
1
+ require:
2
+ - standard
3
+
4
+ inherit_gem:
5
+ standard: config/ruby-3.3.yml
6
+ standard-custom: config/base.yml
7
+ standard-performance: config/base.yml
8
+
9
+ plugins:
10
+ - standard-custom
11
+ - standard-performance
12
+ - rubocop-performance
13
+
14
+ AllCops:
15
+ NewCops: enable
16
+ SuggestExtensions: false
17
+
18
+ #
19
+ # fight with standardrb!
20
+ #
21
+
22
+ # we like these, don't remove
23
+ Bundler/OrderedGems: { Enabled: true } # sort gems in gemfile
24
+ Layout/EmptyLineBetweenDefs: { AllowAdjacentOneLineDefs: true }
25
+ Lint/NonLocalExitFromIterator: { Enabled: false }
26
+ Lint/RedundantDirGlobSort: { Enabled: true } # glob is already sorted
27
+ Performance/RegexpMatch: { Enabled: false }
28
+ Style/HashSyntax: { EnforcedShorthandSyntax: always } # use modern hash syntax
29
+ Style/NestedTernaryOperator: { Enabled: false } # we do this sometimes
30
+ Style/NonNilCheck: { Enabled: false } # allow x != nil for clarity
31
+ Style/RedundantAssignment: { Enabled: false } # allows s=xxx;s=yyy;s
32
+ Style/RedundantReturn: { Enabled: false } # sometines we do this while working on something
33
+ Style/StringConcatenation: { Enabled: true } # prefer interpolation
34
+ Style/TrailingCommaInArrayLiteral: { EnforcedStyleForMultiline: consistent_comma } # commas!!
35
+ Style/TrailingCommaInHashLiteral: { EnforcedStyleForMultiline: consistent_comma } # commas!!
36
+
37
+ #
38
+ # These are rules that are not enabled by default (in standardrb) but we tend to
39
+ # write code this way. We don't often trigger these, but it matches our style.
40
+ #
41
+
42
+ Lint/MissingSuper: { Enabled: true }
43
+ Naming/FileName: { Enabled: true }
44
+ Naming/MemoizedInstanceVariableName: { Enabled: true }
45
+ Naming/MethodName: { Enabled: true }
46
+ Performance/MapCompact: { Enabled: true }
47
+ Performance/SelectMap: { Enabled: true }
48
+ Style/BlockDelimiters: { Enabled: true }
49
+ Style/CollectionCompact: { Enabled: true }
50
+ Style/CollectionMethods: { Enabled: true }
51
+ Style/HashEachMethods: { Enabled: true }
52
+ Style/HashTransformKeys: { Enabled: true }
53
+ Style/HashTransformValues: { Enabled: true }
54
+ Style/MinMax: { Enabled: true }
55
+ Style/PreferredHashMethods: { Enabled: true }
56
+ Style/SelectByRegexp: { Enabled: true }
57
+ Style/SymbolArray: { Enabled: true }
58
+ Style/WordArray: { Enabled: true }
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "https://rubygems.org"
2
+ gemspec
3
+
4
+ group :development, :test do
5
+ gem "amazing_print"
6
+ gem "minitest"
7
+ gem "minitest-hooks"
8
+ gem "mocha"
9
+ gem "ostruct" # required for Ruby 3.5+
10
+ gem "pry"
11
+ gem "rake"
12
+ gem "simplecov", require: false
13
+ gem "standard", require: false
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,122 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ table_tennis (0.0.1)
5
+ csv (~> 3.3)
6
+ ffi (~> 1.17)
7
+ memo_wise (~> 1.11)
8
+ paint (~> 2.3)
9
+ unicode-display_width (~> 3.1)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ amazing_print (1.7.2)
15
+ ast (2.4.3)
16
+ coderay (1.1.3)
17
+ csv (3.3.2)
18
+ docile (1.4.1)
19
+ ffi (1.17.1)
20
+ ffi (1.17.1-aarch64-linux-gnu)
21
+ ffi (1.17.1-aarch64-linux-musl)
22
+ ffi (1.17.1-arm-linux-gnu)
23
+ ffi (1.17.1-arm-linux-musl)
24
+ ffi (1.17.1-arm64-darwin)
25
+ ffi (1.17.1-x86-linux-gnu)
26
+ ffi (1.17.1-x86-linux-musl)
27
+ ffi (1.17.1-x86_64-darwin)
28
+ ffi (1.17.1-x86_64-linux-gnu)
29
+ ffi (1.17.1-x86_64-linux-musl)
30
+ json (2.10.2)
31
+ language_server-protocol (3.17.0.4)
32
+ lint_roller (1.1.0)
33
+ memo_wise (1.11.0)
34
+ method_source (1.1.0)
35
+ minitest (5.25.5)
36
+ minitest-hooks (1.5.2)
37
+ minitest (> 5.3)
38
+ mocha (2.7.1)
39
+ ruby2_keywords (>= 0.0.5)
40
+ ostruct (0.6.1)
41
+ paint (2.3.0)
42
+ parallel (1.26.3)
43
+ parser (3.3.7.4)
44
+ ast (~> 2.4.1)
45
+ racc
46
+ prism (1.4.0)
47
+ pry (0.15.2)
48
+ coderay (~> 1.1)
49
+ method_source (~> 1.0)
50
+ racc (1.8.1)
51
+ rainbow (3.1.1)
52
+ rake (13.2.1)
53
+ regexp_parser (2.10.0)
54
+ rubocop (1.73.2)
55
+ json (~> 2.3)
56
+ language_server-protocol (~> 3.17.0.2)
57
+ lint_roller (~> 1.1.0)
58
+ parallel (~> 1.10)
59
+ parser (>= 3.3.0.2)
60
+ rainbow (>= 2.2.2, < 4.0)
61
+ regexp_parser (>= 2.9.3, < 3.0)
62
+ rubocop-ast (>= 1.38.0, < 2.0)
63
+ ruby-progressbar (~> 1.7)
64
+ unicode-display_width (>= 2.4.0, < 4.0)
65
+ rubocop-ast (1.43.0)
66
+ parser (>= 3.3.7.2)
67
+ prism (~> 1.4)
68
+ rubocop-performance (1.24.0)
69
+ lint_roller (~> 1.1)
70
+ rubocop (>= 1.72.1, < 2.0)
71
+ rubocop-ast (>= 1.38.0, < 2.0)
72
+ ruby-progressbar (1.13.0)
73
+ ruby2_keywords (0.0.5)
74
+ simplecov (0.22.0)
75
+ docile (~> 1.1)
76
+ simplecov-html (~> 0.11)
77
+ simplecov_json_formatter (~> 0.1)
78
+ simplecov-html (0.13.1)
79
+ simplecov_json_formatter (0.1.4)
80
+ standard (1.47.0)
81
+ language_server-protocol (~> 3.17.0.2)
82
+ lint_roller (~> 1.0)
83
+ rubocop (~> 1.73.0)
84
+ standard-custom (~> 1.0.0)
85
+ standard-performance (~> 1.7)
86
+ standard-custom (1.0.2)
87
+ lint_roller (~> 1.0)
88
+ rubocop (~> 1.50)
89
+ standard-performance (1.7.0)
90
+ lint_roller (~> 1.1)
91
+ rubocop-performance (~> 1.24.0)
92
+ unicode-display_width (3.1.4)
93
+ unicode-emoji (~> 4.0, >= 4.0.4)
94
+ unicode-emoji (4.0.4)
95
+
96
+ PLATFORMS
97
+ aarch64-linux-gnu
98
+ aarch64-linux-musl
99
+ arm-linux-gnu
100
+ arm-linux-musl
101
+ arm64-darwin
102
+ ruby
103
+ x86-linux-gnu
104
+ x86-linux-musl
105
+ x86_64-darwin
106
+ x86_64-linux-gnu
107
+ x86_64-linux-musl
108
+
109
+ DEPENDENCIES
110
+ amazing_print
111
+ minitest
112
+ minitest-hooks
113
+ mocha
114
+ ostruct
115
+ pry
116
+ rake
117
+ simplecov
118
+ standard
119
+ table_tennis!
120
+
121
+ BUNDLED WITH
122
+ 2.6.5
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 gurgeous
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # TableTennis [![test](https://github.com/gurgeous/table_tennis/actions/workflows/test.yml/badge.svg)](https://github.com/gurgeous/table_tennis/actions/workflows/test.yml)
2
+
3
+ TableTennis is a Ruby library for printing stylish tables in your terminal.
4
+
5
+ ```rb
6
+ require "table_tennis"
7
+
8
+ options = { title: "Star Wars People", zebra: true, color_scale: :height }
9
+ puts TableTennis.new(Starwars.all, options)
10
+ ```
11
+
12
+ Prints this lovely table in your terminal:
13
+
14
+ ![starwars](./screenshots/dark.png)
15
+
16
+ ### Installation
17
+
18
+ ```ruby
19
+ # install gem
20
+ $ gem install table_tennis
21
+
22
+ # or add to your Gemfile
23
+ gem "table_tennis"
24
+ ```
25
+
26
+ ### Important Features
27
+
28
+ - auto-themes to pick light or dark based on your terminal background
29
+ - auto-layout to fit your terminal window
30
+ - auto-format floats and dates
31
+ - auto-color numeric columns
32
+ - titles, row numbers, zebra stripes...
33
+
34
+ ### Themes
35
+
36
+ TableTennis examines the background color of your terminal to pick either the dark or light theme. You can also specify `:dark` or `:light` manually, or even an `:ansi` theme to use your terminal's default colors. This feature is [surprisingly complicated](https://github.com/gurgeous/table_tennis/blob/main/lib/table_tennis/util/termbg.rb).
37
+
38
+ ![themes](./screenshots/themes.png)
39
+
40
+ ### Rows (Your Data)
41
+
42
+ Construct your table with an array of rows. Rows are hashes, ActiveRecord objects, structs, Data records, or anything that responds to `to_h`. It also supports oddballs like arrays (as rows) or even a single hash.
43
+
44
+ ```ruby
45
+ puts TableTennis.new([{a: "hello", b: "world"}, {a: "foo", b: "bar"})
46
+ puts TableTennis.new(Recipe.all.to_a) # activerecord
47
+ puts TableTennis.new(array_of_structs) # these use to_h
48
+ puts TableTennis.new([[1,2],[3,4]]]) # array of arrays
49
+ puts TableTennis.new(authors[0]) # single hash
50
+ ```
51
+
52
+ ### Big List of Options
53
+
54
+ Here is a more complex example to get you started:
55
+
56
+ ```ruby
57
+ options = {
58
+ color_scales: :commission,
59
+ columns: %i[ name commission bday phone ],
60
+ mark: -> { _1[:name] =~ /jane|john/i },
61
+ row_numbers: true,
62
+ save: "/tmp/people.csv",
63
+ search: "february",
64
+ title: "Employees",
65
+ zebra: true,
66
+ }
67
+ ```
68
+
69
+ | option | default | details |
70
+ | ------ | ------- | ------- |
71
+ | `color_scales` | ─ | Color code a column of floats, similar to the "conditional formatting" feature in Google Sheets. See [docs below](#color-scales). |
72
+ | `color` | `nil` | Are ANSI colors enabled? Specify `true` or `false`, or leave it as nil to autodetect. Autodetect will turn on color unless redirecting to a file. When using autodetect, you can force it on by setting `ENV["FORCE_COLOR"]`, or off with `ENV["NO_COLOR"]`. |
73
+ | `columns` | `nil` | Manually set which columns to include. Leave unset to show all columns.
74
+ | `digits` | `3` | Format floats to this number of digits. TableTennis will look for either `Float` cells or string floats. |
75
+ | `layout` | `true` | This controls column widths. Leave unset or use `true` for autolayout. Autolayout will shrink the table to fit inside the terminal. `false` turns off layout and columns will be full width. Use an int to fix all columns to a certain width, or a hash to just set a few. |
76
+ | `mark` | ─ | `mark` is a way to highlight specific columns with a nice color. For example, use `mark: ->(row) { row[:planet] == "tatooine" }` to highlight those rows. Your lambda can also return a specific color if you want.
77
+ | `placeholder` | `"—"` | Put this into empty cells. |
78
+ | `row_numbers` | `false` | Show row numbers in the table. |
79
+ | `save` | ─ | If you set this to a file path, TableTennis will save your table as a CSV file too. Useful if you want to do something else with the data. |
80
+ | `search` | ─ | string/regex to highlight in output |
81
+ | `strftime` | see → | strftime string for formatting Date/Time objects. The default is `"%Y-%m-%d"`, which looks like `2025-04-21` |
82
+ | `theme` | nil | When unset, will be autodetected based on terminal background color. If autodetect fails the theme defaults to :dark. You can also manually specify `:dark`, `:light` or `:ansi`. If colors are turned off this setting has no effect.|
83
+ | `title` | ─ | Add a title line to the table. |
84
+ | `zebra` | `false` | Turn on zebra stripes. |
85
+
86
+ ### Color Scales
87
+
88
+ Color scales are useful for visualizing numeric columns. The `:color_scales` option can be a single column, an array of column names, or a hash from column names to colors. The scale defaults to `:b` (blue) if you don't use a hash.
89
+
90
+ ```ruby
91
+ puts TableTennis.new(rows, color_scales: :price)
92
+ puts TableTennis.new(rows, color_scales: [ :price, :quantity ])
93
+ puts TableTennis.new(rows, color_scales: { price: :b, quantity: :r })
94
+ ```
95
+
96
+ The color names are abbreviations, so `:gyr` goes from green to yellow to red. Here is the full list of supported color scales - `%i[g y r b gw yw rw bw rg gr gyr]`. For clarity this screenshot uses sorted columns, but note that TableTennis never does any sorting:
97
+
98
+ ![scales](./screenshots/scales.png)
99
+
100
+ ### Tips
101
+
102
+ Use **mark** to highlight certain rows. Maybe you need to find the droids? Or **search** to highlight text. I almost always use **row numbers** and **zebra stripes** too.
103
+
104
+ ```ruby
105
+ puts TableTennis.new(rows, mark: ->(row) { row[:homeworld] =~ /droids/i })
106
+ puts TableTennis.new(rows, search: /hope.*empire/i })
107
+ puts TableTennis.new(rows, row_numbers: true, zebra: true)
108
+ ```
109
+
110
+ | `:mark` | `:search` | `:row_numbers` and `:zebra` |
111
+ | - | - | - |
112
+ | ![droids](./screenshots/droids.png) | ![hope](./screenshots/hope.png) | ![row numbers](./screenshots/row_numbers.png) |
113
+
114
+ ### Advanced Usage
115
+
116
+ TableTennis can be configured a few different ways:
117
+
118
+ ```ruby
119
+
120
+ TableTennis.defaults = { title: "All Tables Have This Name" }
121
+ TableTennis.new(rows, title: "An Amazing Title")
122
+ TableTennis.new do |t|
123
+ t.title = "Yet Another Way To Set Things Up"
124
+ end
125
+ ```
126
+
127
+ Tables usually get `puts` to $stdout, but there are other ways to do it:
128
+
129
+ ```ruby
130
+ # Uses `to_s`, so there can be a pause before output shows up. Best for small tables.
131
+ puts TableTennis.new(rows)
132
+
133
+ # Write to `$stdout` one row at a time. Prefer this for tables over 10,000 rows.
134
+ TableTennis.new(rows).render
135
+
136
+ # Render to any I/O stream ($stdout/$stderr, an open file, StringIO...)
137
+ TableTennis.new(rows).render(io)
138
+ ```
139
+
140
+ ### Similar Tools
141
+
142
+ We love CSV tools and use them all the time! Here are a few that we rely on:
143
+
144
+ - [bat](https://github.com/sharkdp/bat) - syntax highlights csv files, and many others
145
+ - [csvlens](https://github.com/YS-L/csvlens) & [tidy viewer](https://github.com/alexhallam/tv) - great viewers for CSV files, beautiful and fun
146
+ - [qsv](https://github.com/dathere/qsv) - filter, sort, combine, join... (a fork of [xsv](https://github.com/BurntSushi/xsv))
147
+ - [Terminal::Table](https://github.com/tj/terminal-table) - wonderful rubygem for pretty printing tables, great for non-hash data like confusion matrices
148
+ - [visidata](https://www.visidata.org) - the best for poking around large files, it does everything
149
+
150
+ ### Special Thanks
151
+
152
+ - [termbg](https://github.com/dalance/termbg) and [termenv](https://github.com/muesli/termenv) for showing how to safely detect the terminal background color. These libraries are widely used for Rust/Go, but as far as I know nothing similar exists for Ruby.
153
+ - The [Paint gem](https://github.com/janlelis/paint) for help with ansi colors.
154
+ - Google Sheets for providing nice color scales
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/setup"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do
5
+ # let the user pass *.rb on the command line
6
+ files = ARGV.grep(%r{^test/.*\.rb$}).sort
7
+ _1.test_files = files.empty? ? FileList["test/**/test_*.rb"] : files
8
+ # autoload test_helper
9
+ _1.ruby_opts = ["-w", "-r#{File.realpath("test/test_helper.rb")}"]
10
+ end
11
+
12
+ task default: :test
data/justfile ADDED
@@ -0,0 +1,75 @@
1
+
2
+ # read gem version
3
+ gemver := `grep -Eo "[0-9]+\.[0-9]+\.[0-9]+" lib/table_tennis/version.rb`
4
+
5
+ #
6
+ # dev
7
+ #
8
+
9
+ default:
10
+ @just --list
11
+
12
+ check: lint test
13
+
14
+ ci: check
15
+
16
+ demo-watch *ARGS:
17
+ @watchexec --stop-timeout=0 --clear clear table-tennis-demo {{ARGS}}
18
+
19
+ format:
20
+ @just banner format...
21
+ bundle exec rubocop -a
22
+
23
+ image_optim:
24
+ @bundle exec image_optim --allow-lossy --svgo-precision=1 -r .
25
+
26
+ lint:
27
+ @just banner lint...
28
+ bundle exec rubocop
29
+
30
+ pry:
31
+ bundle exec pry -I lib -r table_tennis.rb
32
+
33
+ test *ARGS:
34
+ @just banner rake test {{ARGS}}
35
+ @bundle exec rake test {{ARGS}}
36
+
37
+ test-watch *ARGS:
38
+ @watchexec --stop-timeout=0 --clear clear just test "{{ARGS}}"
39
+
40
+ #
41
+ # coverage/profiling
42
+ #
43
+
44
+ coverage:
45
+ COVERAGE=1 just test
46
+ open /tmp/coverage/index.html
47
+
48
+ #
49
+ # gem tasks
50
+ #
51
+
52
+ # you can test locally from another project by dropping gem file into vendor/cache
53
+ gem-push: check-git-status
54
+ @just banner gem build...
55
+ gem build table_tennis.gemspec
56
+ @just banner tag...
57
+ git tag -a "v{{gemver}}" -m "Tagging {{gemver}}"
58
+ git push --tags
59
+ @just banner gem push...
60
+ gem push "table_tennis-{{gemver}}.gem"
61
+
62
+ #
63
+ # util
64
+ #
65
+
66
+ banner *ARGS: (_banner BG_GREEN ARGS)
67
+ warning *ARGS: (_banner BG_YELLOW ARGS)
68
+ fatal *ARGS: (_banner BG_RED ARGS)
69
+ @exit 1
70
+ _banner color *ARGS:
71
+ @msg=$(printf "[%s] %s" $(date +%H:%M:%S) "{{ARGS}}") ; \
72
+ printf "{{color+BOLD+WHITE}}%-72s{{ NORMAL }}\n" "$msg"
73
+
74
+ check-git-status:
75
+ @if [ ! -z "$(git status --porcelain)" ]; then just fatal "git status is dirty, bailing."; fi
@@ -0,0 +1,41 @@
1
+ module TableTennis
2
+ #
3
+ # A single column in a table. The data is actually stored in the rows, but it
4
+ # can be enumerated from here. Mostly used for layout calculations.
5
+ #
6
+
7
+ class Column
8
+ include Enumerable
9
+ prepend MemoWise
10
+
11
+ attr_reader :name
12
+ attr_accessor :header, :width
13
+
14
+ def initialize(name, data)
15
+ @name, @data = name, data
16
+ @header = name.to_s
17
+ end
18
+
19
+ def each(&block)
20
+ return to_enum(__method__) unless block_given?
21
+ @data.rows.each { yield(_1[name]) }
22
+ self
23
+ end
24
+
25
+ def each_index(&block)
26
+ return to_enum(__method__) unless block_given?
27
+ @data.rows.each_index { yield(_1) }
28
+ end
29
+
30
+ def map!(&block) = @data.rows.each { _1[name] = yield(_1[name]) }
31
+
32
+ def truncate(stop)
33
+ @header = Util::Strings.truncate(header, stop)
34
+ map! { Util::Strings.truncate(_1, stop) }
35
+ end
36
+
37
+ def measure
38
+ [2, max_by(&:length)&.length, header.length].max
39
+ end
40
+ end
41
+ end