From 76245734e12965129c6c9f3f04fe4341fe113df8 Mon Sep 17 00:00:00 2001 From: Gregory Hellings Date: Thu, 18 Jan 2018 23:17:50 -0600 Subject: [PATCH 01/38] Remove spurious executable permissions These two files do not need execute permissions, so the +x flag is removed --- README.rdoc | 0 lib/terminal-table/table.rb | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 README.rdoc mode change 100755 => 100644 lib/terminal-table/table.rb diff --git a/README.rdoc b/README.rdoc old mode 100755 new mode 100644 diff --git a/lib/terminal-table/table.rb b/lib/terminal-table/table.rb old mode 100755 new mode 100644 From 562a58d990e86dc86fbc53220b495126a68c2667 Mon Sep 17 00:00:00 2001 From: Jason Scholl Date: Fri, 10 May 2019 21:00:41 -0700 Subject: [PATCH 02/38] Test in circleci --- .circleci/config.yml | 58 ++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 1 + 2 files changed, 59 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..81889c1 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,58 @@ +version: 2 +jobs: + job_template: &job_template + environment: + GEM_HOME: vendor/bundle + BUNDLER_INSTALL_ARGS: -v 1.17.3 + docker: + - image: ruby:$CIRCLE_JOB + working_directory: /tmp/project + steps: + - checkout + - restore_cache: + keys: + - v1-bundle-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLE_SHA1 }} + - v1-bundle-{{ .Environment.CIRCLE_JOB }}- + - run: + name: Bundle install + #(gem uninstall -ax bundler || true) + command: > + gem install bundler -i vendor/bundle $BUNDLER_INSTALL_ARGS + && bundle install --path vendor/bundle + - save_cache: + key: v1-bundle-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLE_SHA1 }} + paths: + - /tmp/project/vendor + - run: + name: Run Tests + command: bundle exec rake spec + + 1.9.3: + <<: *job_template + 2.0.0: + <<: *job_template + 2.1.1: + <<: *job_template + 2.2.0: + <<: *job_template + 2.3.0: + <<: *job_template + 2.4.0: + <<: *job_template + 2.5.0: + <<: *job_template + 2.6.0: + <<: *job_template + +workflows: + version: 2 + build: + jobs: + - 1.9.3 + - 2.0.0 + - 2.1.1 + - 2.2.0 + - 2.3.0 + - 2.4.0 + - 2.5.0 + - 2.6.0 diff --git a/.gitignore b/.gitignore index 863ddb8..092bc3f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ tmp *.cache doc Gemfile.lock +vendor From 015a1f41658961734e5880ccc668742ff5fdd369 Mon Sep 17 00:00:00 2001 From: Jason Scholl Date: Fri, 10 May 2019 23:30:49 -0700 Subject: [PATCH 03/38] Add circleci badge --- README.rdoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 912b424..578768f 100644 --- a/README.rdoc +++ b/README.rdoc @@ -1,4 +1,6 @@ -= TerminalTable Table +[![Circle CI](https://circleci.com/gh/tj/terminal-table.svg?style=shield)](https://circleci.com/gh/tj/terminal-table) + += Terminal Table == Description From daa7426c316b8b00adb5f1bbc7526500c310e028 Mon Sep 17 00:00:00 2001 From: luotao Date: Tue, 27 Aug 2019 15:48:21 +0800 Subject: [PATCH 04/38] Fix broken circle ci badge --- README.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 578768f..9e5bdd1 100644 --- a/README.rdoc +++ b/README.rdoc @@ -1,4 +1,4 @@ -[![Circle CI](https://circleci.com/gh/tj/terminal-table.svg?style=shield)](https://circleci.com/gh/tj/terminal-table) +{CircleCI}[https://circleci.com/gh/tj/terminal-table/tree/master] = Terminal Table From b8db685e1244571319fd5256f0c224c482d909eb Mon Sep 17 00:00:00 2001 From: luotao Date: Tue, 27 Aug 2019 15:49:23 +0800 Subject: [PATCH 05/38] Update README.rdoc --- README.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 9e5bdd1..325775b 100644 --- a/README.rdoc +++ b/README.rdoc @@ -1,4 +1,4 @@ -{CircleCI}[https://circleci.com/gh/tj/terminal-table/tree/master] +{CircleCI}[https://circleci.com/gh/tj/terminal-table] = Terminal Table From f053fe1c0db5f46ca7e3e3dde72969719c324c4a Mon Sep 17 00:00:00 2001 From: Nate Berkopec Date: Tue, 27 Aug 2019 08:45:37 +0000 Subject: [PATCH 06/38] Revert "Fix broken circle ci badge" --- README.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 325775b..578768f 100644 --- a/README.rdoc +++ b/README.rdoc @@ -1,4 +1,4 @@ -{CircleCI}[https://circleci.com/gh/tj/terminal-table] +[![Circle CI](https://circleci.com/gh/tj/terminal-table.svg?style=shield)](https://circleci.com/gh/tj/terminal-table) = Terminal Table From 6bc0e25a5fd21eddbe139b21db6647dc4966c105 Mon Sep 17 00:00:00 2001 From: Washington Botelho Date: Wed, 28 Oct 2020 00:39:42 -0300 Subject: [PATCH 07/38] adds ci build with github actions Ruby 1.9 is not present in Ubuntu 18.04 and soon ubuntu-latest will be 20.04. --- .circleci/config.yml | 58 ---------------------------------------- .github/workflows/ci.yml | 28 +++++++++++++++++++ 2 files changed, 28 insertions(+), 58 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/ci.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 81889c1..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,58 +0,0 @@ -version: 2 -jobs: - job_template: &job_template - environment: - GEM_HOME: vendor/bundle - BUNDLER_INSTALL_ARGS: -v 1.17.3 - docker: - - image: ruby:$CIRCLE_JOB - working_directory: /tmp/project - steps: - - checkout - - restore_cache: - keys: - - v1-bundle-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLE_SHA1 }} - - v1-bundle-{{ .Environment.CIRCLE_JOB }}- - - run: - name: Bundle install - #(gem uninstall -ax bundler || true) - command: > - gem install bundler -i vendor/bundle $BUNDLER_INSTALL_ARGS - && bundle install --path vendor/bundle - - save_cache: - key: v1-bundle-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLE_SHA1 }} - paths: - - /tmp/project/vendor - - run: - name: Run Tests - command: bundle exec rake spec - - 1.9.3: - <<: *job_template - 2.0.0: - <<: *job_template - 2.1.1: - <<: *job_template - 2.2.0: - <<: *job_template - 2.3.0: - <<: *job_template - 2.4.0: - <<: *job_template - 2.5.0: - <<: *job_template - 2.6.0: - <<: *job_template - -workflows: - version: 2 - build: - jobs: - - 1.9.3 - - 2.0.0 - - 2.1.1 - - 2.2.0 - - 2.3.0 - - 2.4.0 - - 2.5.0 - - 2.6.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..0d6e5bd --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,28 @@ +name: CI +on: [push] + +jobs: + test: + if: "!contains(github.event.head_commit.message, 'ci skip')" + + continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }} + + strategy: + fail-fast: false + + matrix: + os: [ubuntu] + ruby: [2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7] + + runs-on: ${{ matrix.os }}-latest + + steps: + - uses: actions/checkout@v2 + + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + ruby-version: ${{ matrix.ruby }} + + - run: bundle install + - run: bundle exec rspec From a70c21a3760a2957af7eabf912b2afeb6c03737f Mon Sep 17 00:00:00 2001 From: Washington Botelho Date: Wed, 28 Oct 2020 00:46:42 -0300 Subject: [PATCH 08/38] adds Gemfile.lock on versioning --- .gitignore | 1 - Gemfile.lock | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 Gemfile.lock diff --git a/.gitignore b/.gitignore index 092bc3f..bed16b8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,4 @@ pkg tmp *.cache doc -Gemfile.lock vendor diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..48ea8c8 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,48 @@ +PATH + remote: . + specs: + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + +GEM + remote: https://rubygems.org/ + specs: + coderay (1.1.3) + diff-lcs (1.4.4) + method_source (1.0.0) + pry (0.13.1) + coderay (~> 1.1) + method_source (~> 1.0) + rake (10.5.0) + rspec (3.9.0) + rspec-core (~> 3.9.0) + rspec-expectations (~> 3.9.0) + rspec-mocks (~> 3.9.0) + rspec-core (3.9.3) + rspec-support (~> 3.9.3) + rspec-expectations (3.9.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.9.0) + rspec-mocks (3.9.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.9.0) + rspec-support (3.9.4) + term-ansicolor (1.7.1) + tins (~> 1.0) + tins (1.0.1) + unicode-display_width (1.7.0) + +PLATFORMS + ruby + +DEPENDENCIES + bundler (~> 1.10) + pry + rake (~> 10.0) + rspec (>= 3.0) + term-ansicolor + terminal-table! + tins (~> 1.0.0) + +BUNDLED WITH + 1.17.3 From a8d3363ce404972258b8655556f1e5f9b0038687 Mon Sep 17 00:00:00 2001 From: Washington Botelho Date: Wed, 28 Oct 2020 00:47:03 -0300 Subject: [PATCH 09/38] ignores bundle folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bed16b8..01be5cf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ tmp *.cache doc vendor +/.bundle From fb15b3cb3dfb9b8bc561579818b89adaaf260584 Mon Sep 17 00:00:00 2001 From: Washington Botelho Date: Wed, 28 Oct 2020 00:52:11 -0300 Subject: [PATCH 10/38] updates bundler --- Gemfile.lock | 4 ++-- terminal-table.gemspec | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 48ea8c8..51682d9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -36,7 +36,7 @@ PLATFORMS ruby DEPENDENCIES - bundler (~> 1.10) + bundler (~> 2) pry rake (~> 10.0) rspec (>= 3.0) @@ -45,4 +45,4 @@ DEPENDENCIES tins (~> 1.0.0) BUNDLED WITH - 1.17.3 + 2.1.4 diff --git a/terminal-table.gemspec b/terminal-table.gemspec index 1ab1b20..9699a66 100644 --- a/terminal-table.gemspec +++ b/terminal-table.gemspec @@ -16,7 +16,7 @@ Gem::Specification.new do |spec| spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } spec.require_paths = ["lib"] - spec.add_development_dependency "bundler", "~> 1.10" + spec.add_development_dependency "bundler", "~> 2" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rspec", ">= 3.0" spec.add_development_dependency "term-ansicolor" From cb56a133f74c95cd137f7854528a9f4ff632da0a Mon Sep 17 00:00:00 2001 From: Washington Botelho Date: Wed, 28 Oct 2020 01:04:24 -0300 Subject: [PATCH 11/38] drops support for ruby 2.1, 2.2 and 2.3 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d6e5bd..f3b8591 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: matrix: os: [ubuntu] - ruby: [2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7] + ruby: [2.4, 2.5, 2.6, 2.7] runs-on: ${{ matrix.os }}-latest From 059ed31f393bb6b7d4183dded9ce20eb88eb897e Mon Sep 17 00:00:00 2001 From: Washington Botelho Date: Wed, 28 Oct 2020 01:06:28 -0300 Subject: [PATCH 12/38] updates rake --- Gemfile.lock | 4 ++-- terminal-table.gemspec | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 51682d9..21a6dbf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -13,7 +13,7 @@ GEM pry (0.13.1) coderay (~> 1.1) method_source (~> 1.0) - rake (10.5.0) + rake (13.0.1) rspec (3.9.0) rspec-core (~> 3.9.0) rspec-expectations (~> 3.9.0) @@ -38,7 +38,7 @@ PLATFORMS DEPENDENCIES bundler (~> 2) pry - rake (~> 10.0) + rake (~> 13.0) rspec (>= 3.0) term-ansicolor terminal-table! diff --git a/terminal-table.gemspec b/terminal-table.gemspec index 9699a66..f459215 100644 --- a/terminal-table.gemspec +++ b/terminal-table.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_development_dependency "bundler", "~> 2" - spec.add_development_dependency "rake", "~> 10.0" + spec.add_development_dependency "rake", "~> 13.0" spec.add_development_dependency "rspec", ">= 3.0" spec.add_development_dependency "term-ansicolor" spec.add_development_dependency "pry" From c09ecd4e0e0835bc9acdbeab52dd841759a981ac Mon Sep 17 00:00:00 2001 From: Washington Botelho Date: Wed, 28 Oct 2020 01:16:19 -0300 Subject: [PATCH 13/38] doc: updates ci badge --- README.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 578768f..3f4b2e3 100644 --- a/README.rdoc +++ b/README.rdoc @@ -1,4 +1,4 @@ -[![Circle CI](https://circleci.com/gh/tj/terminal-table.svg?style=shield)](https://circleci.com/gh/tj/terminal-table) +{}[https://github.com/tj/terminal-table/actions] = Terminal Table From 7f2e8d30531c97a7815f8ecd34e2819af6998189 Mon Sep 17 00:00:00 2001 From: Washington Botelho Date: Wed, 28 Oct 2020 01:16:41 -0300 Subject: [PATCH 14/38] v2.0.0 --- Gemfile.lock | 2 +- History.rdoc | 9 +++++++++ lib/terminal-table/version.rb | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 21a6dbf..401ad0e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - terminal-table (1.8.0) + terminal-table (2.0.0) unicode-display_width (~> 1.1, >= 1.1.1) GEM diff --git a/History.rdoc b/History.rdoc index 58a96f8..796cc25 100644 --- a/History.rdoc +++ b/History.rdoc @@ -1,3 +1,12 @@ +2.0.0 / 2020-10-28 +================== + +- Drops official support for Ruby 1.9.x with and of life on 2015-02-23 +- Drops official support for Ruby 2.0.x with and of life on 2016-02-24 +- Drops official support for Ruby 2.1.x with and of life on 2017-03-31 +- Drops official support for Ruby 2.2.x with and of life on 2018-03-31 +- Drops official support for Ruby 2.3.x with and of life on 2019-03-31 + 1.8.0 / 2017-05-16 ================== diff --git a/lib/terminal-table/version.rb b/lib/terminal-table/version.rb index dd1a2b8..f070a83 100644 --- a/lib/terminal-table/version.rb +++ b/lib/terminal-table/version.rb @@ -1,5 +1,5 @@ module TerminalTable class Table - VERSION = '1.8.0' + VERSION = '2.0.0' end end From b8c3e8de74069d6b8b454a7c162f3736458c7b86 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Thu, 7 Mar 2019 19:11:36 -0800 Subject: [PATCH 15/38] Use unicode-display_width without String ext --- lib/terminal-table/cell.rb | 2 +- lib/terminal-table/table.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/terminal-table/cell.rb b/lib/terminal-table/cell.rb index e269234..0a56a6a 100644 --- a/lib/terminal-table/cell.rb +++ b/lib/terminal-table/cell.rb @@ -1,4 +1,4 @@ -require 'unicode/display_width' +require 'unicode/display_width/no_string_ext' module TerminalTable class Table diff --git a/lib/terminal-table/table.rb b/lib/terminal-table/table.rb index c99275c..3368138 100644 --- a/lib/terminal-table/table.rb +++ b/lib/terminal-table/table.rb @@ -1,4 +1,4 @@ -require 'unicode/display_width' +require 'unicode/display_width/no_string_ext' module TerminalTable class Table From 08d1391a2add77d8b32c538a5a4853ce3c29b539 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Mon, 14 Dec 2020 09:42:48 -0500 Subject: [PATCH 16/38] adding in somewhat hacky support for unicode tables and enhanced table rendering --- Gemfile.lock | 25 +++--- examples/examples_unicode.rb | 86 +++++++++++++++++++++ lib/terminal-table/row.rb | 22 +++++- lib/terminal-table/separator.rb | 52 ++++++++++++- lib/terminal-table/style.rb | 130 ++++++++++++++++++++++++++++++-- lib/terminal-table/table.rb | 30 ++++++-- spec/table_spec.rb | 34 +++++---- 7 files changed, 329 insertions(+), 50 deletions(-) create mode 100644 examples/examples_unicode.rb diff --git a/Gemfile.lock b/Gemfile.lock index 401ad0e..be8aff9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -14,19 +14,19 @@ GEM coderay (~> 1.1) method_source (~> 1.0) rake (13.0.1) - rspec (3.9.0) - rspec-core (~> 3.9.0) - rspec-expectations (~> 3.9.0) - rspec-mocks (~> 3.9.0) - rspec-core (3.9.3) - rspec-support (~> 3.9.3) - rspec-expectations (3.9.3) + rspec (3.10.0) + rspec-core (~> 3.10.0) + rspec-expectations (~> 3.10.0) + rspec-mocks (~> 3.10.0) + rspec-core (3.10.0) + rspec-support (~> 3.10.0) + rspec-expectations (3.10.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-mocks (3.9.1) + rspec-support (~> 3.10.0) + rspec-mocks (3.10.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-support (3.9.4) + rspec-support (~> 3.10.0) + rspec-support (3.10.0) term-ansicolor (1.7.1) tins (~> 1.0) tins (1.0.1) @@ -34,6 +34,7 @@ GEM PLATFORMS ruby + x86_64-linux DEPENDENCIES bundler (~> 2) @@ -45,4 +46,4 @@ DEPENDENCIES tins (~> 1.0.0) BUNDLED WITH - 2.1.4 + 2.2.0 diff --git a/examples/examples_unicode.rb b/examples/examples_unicode.rb new file mode 100644 index 0000000..3a3d09d --- /dev/null +++ b/examples/examples_unicode.rb @@ -0,0 +1,86 @@ +$:.unshift File.dirname(__FILE__) + '/../lib' +require 'terminal-table/import' + +Terminal::Table::Style.defaults = { :border => Terminal::Table::UnicodeBorder.new() } + +puts +puts table(['a', 'b'], [1, 2], [3, 4]) + +puts +puts table(['name', 'content'], ['ftp.example.com', '1.1.1.1'], ['www.example.com', '|lalalala|lalala|']) + +puts +t = table ['a', 'b'] +t.style = {:padding_left => 2, :width => 80} +t << [1, 2] +t << [3, 4] +t << :separator +t << [4, 6] +puts t + +puts +user_table = table do |v| + v.title = "Contact Information" + v.headings = 'First Name', 'Last Name', 'Email' + v << %w( TJ Holowaychuk tj@vision-media.ca ) + v << %w( Bob Someone bob@vision-media.ca ) + v << %w( Joe Whatever bob@vision-media.ca ) +end +puts user_table + +puts +user_table = table do |v| + v.style.width = 80 + v.headings = 'First Name', 'Last Name', 'Email' + v << %w( TJ Holowaychuk tj@vision-media.ca ) + v << %w( Bob Someone bob@vision-media.ca ) + v << %w( Joe Whatever bob@vision-media.ca ) +end +puts user_table + +puts +user_table = table do + self.headings = 'First Name', 'Last Name', 'Email' + add_row ['TJ', 'Holowaychuk', 'tj@vision-media.ca'] + add_row ['Bob', 'Someone', 'bob@vision-media.ca'] + add_row ['Joe', 'Whatever', 'joe@vision-media.ca'] + add_separator + add_row ['Total', { :value => '3', :colspan => 2, :alignment => :right }] + align_column 1, :center +end +puts user_table + +puts +user_table = table do + self.headings = ['First Name', 'Last Name', {:value => 'Phones', :colspan => 2, :alignment => :center}] + #add_row ['Bob', 'Someone', '123', '456'] + add_row [{:value => "Bob Someone", :colspan => 3, :alignment => :center}, '123456'] + add_row :separator + add_row ['TJ', 'Holowaychuk', {:value => "No phones\navaiable", :colspan => 2, :alignment => :center}] + add_row :separator + add_row ['Joe', 'Whatever', '4324', '343242'] +end +puts user_table + +rows = [] +rows << ['Lines', 100] +rows << ['Comments', 20] +rows << ['Ruby', 70] +rows << ['JavaScript', 30] +puts table([nil, 'Lines'], *rows) + +rows = [] +rows << ['Lines', 100] +rows << ['Comments', 20] +rows << ['Ruby', 70] +rows << ['JavaScript', 30] +puts table(nil, *rows) + +rows = [] +rows << ['Lines', 100] +rows << ['Comments', 20] +rows << ['Ruby', 70] +rows << ['JavaScript', 30] +table = table([{ :value => 'Stats', :colspan => 2, :alignment => :center }], *rows) +table.align_column 1, :right +puts table diff --git a/lib/terminal-table/row.rb b/lib/terminal-table/row.rb index 8eec4e0..2cf630e 100644 --- a/lib/terminal-table/row.rb +++ b/lib/terminal-table/row.rb @@ -36,17 +36,33 @@ def height end def render - y = @table.style.border_y + vleft, vcenter, vright = @table.style.vertical (0...height).to_a.map do |line| - y + cells.map do |cell| + vleft + cells.map do |cell| cell.render(line) - end.join(y) + y + end.join(vcenter) + vright end.join("\n") end def number_of_columns @cells.collect(&:colspan).inject(0, &:+) end + + # used to find indices where we have table '+' crossings. + # in cases where the colspan > 1, then we will skip over some numbers + # if colspan is always 1, then the list should be incrementing by 1. + # + # skip 0 entry, because it's the left side. + # skip last entry, because it's the right side. + # we only care about "+/T" style crossings. + def crossings + idx = 0 + @cells[0...-1].map { |c| idx += c.colspan } + end + end + # Yay, inheritance. + class HeadingRow < Row ; end + class TitleRow < Row ; end end end diff --git a/lib/terminal-table/separator.rb b/lib/terminal-table/separator.rb index bed51db..e30c984 100644 --- a/lib/terminal-table/separator.rb +++ b/lib/terminal-table/separator.rb @@ -2,13 +2,57 @@ module TerminalTable class Table class Separator < Row + def initialize(*args) + super + @prevrow, @nextrow = nil, nil # references to adjacent rows. + end + + def row_type + return :top if @prevrow.nil? + return :bot if @nextrow.nil? + return :below_heading if @prevrow.is_a?(HeadingRow) + return :mid + end + def render - arr_x = (0...@table.number_of_columns).to_a.map do |i| - @table.style.border_x * (@table.column_width(i) + @table.cell_padding) + left_edge, ctrflat, ctrud, right_edge, ctrdn, ctrup = @table.style.horizontal(row_type) + + prev_crossings = @prevrow.respond_to?(:crossings) ? @prevrow.crossings : [] + next_crossings = @nextrow.respond_to?(:crossings) ? @nextrow.crossings : [] + rval = [left_edge] + numcols = @table.number_of_columns + (0...numcols).each do |idx| + rval << ctrflat * (@table.column_width(idx) + @table.cell_padding) + pcinc = prev_crossings.include?(idx+1) + ncinc = next_crossings.include?(idx+1) + border_center = if pcinc && ncinc + ctrud + elsif pcinc + ctrup + elsif ncinc + ctrdn + elsif !ctrud.empty? + # special case if the center-up-down intersection is empty + # which happens when verticals/intersections are removed. in that case + # we do not want to replace with a flat element so return empty-string in else block + ctrflat + else + '' + end + rval << border_center if idx < numcols-1 end - border_i = @table.style.border_i - border_i + arr_x.join(border_i) + border_i + + rval << right_edge + return rval.join + end + + # save off neighboring rows. + # TODO: rename this function, name makes no sense. + def elab(prevrow, nextrow) + @prevrow = prevrow + @nextrow = nextrow end + end end end diff --git a/lib/terminal-table/style.rb b/lib/terminal-table/style.rb index 0d1fce8..a22744b 100644 --- a/lib/terminal-table/style.rb +++ b/lib/terminal-table/style.rb @@ -1,4 +1,5 @@ -module TerminalTable +# coding: utf-8 +module Terminal class Table # A Style object holds all the formatting information for a Table object # @@ -21,19 +22,107 @@ class Table # # TerminalTable::Table::Style.defaults = {:width => 80} # + class Border + attr_accessor :data + def []=(key, val) + @data[key] = val + end + def [](key) + @data[key] + end + def initialize_dup(other) + super + @data = other.data.dup + end + def remove_verticals + self.class.const_get("VERTICALS").each { |key| @data[key] = "" } + self.class.const_get("INTERSECTIONS").each { |key| @data[key] = "" } + end + end + + class AsciiBorder < Border + VERTICALS = %i[border_y] + INTERSECTIONS = %i[border_i] + + def initialize + @data = { border_x: "-", border_y: "|", border_i: "+" } + end + + # @return [Array] 3-element list of [left, center, right] + def vertical + y = @data[:border_y] + [y, y, y] # left, center, right + end + + # @return [Array] a 6 element list of: [i-left, horizontal-bar, i-up/down, i-right, i-down, i-up] + def horizontal(_type) + x, i = @data[:border_x], @data[:border_i] + [i, x, i, i, i, i] + end + end + + class UnicodeBorder < Border + VERTICALS = %i[border_y border_yw border_ye] + INTERSECTIONS = %i[border_nw border_n border_ne border_nd + border_hw border_hi border_he border_hd border_hu + border_w border_i border_e border_dn border_up + border_sw border_s border_se border_su] + def initialize + @data = { + border_nw: "┌", border_nx: "─", border_n: "┬", border_ne: "┐", border_nd: nil, + border_yw: "│", border_y: "│", border_ye: "│", + border_hw: "╞", border_hx: "═", border_hi: "╪", border_he: "╡", border_hd: '╤', border_hu: "╧", + border_w: "├", border_x: "─", border_i: "┼", border_e: "┤", border_dn: "┬", border_up: "┴", + border_sw: "└", border_sx: "─", border_s: "┴", border_se: "┘", border_su: nil, + } + end + # @return [Array] 3-element list of [left, center, right] + def vertical + [@data[:border_yw], @data[:border_y], @data[:border_ye]] + end + + # @return [Array] a 6 element list of: [i-left, horizontal-bar, i-up/down, i-right, i-down, i-up] + def horizontal(type) + case type + when :below_heading + [@data[:border_hw], @data[:border_hx], @data[:border_hi], @data[:border_he], @data[:border_hd] || @data[:border_dn], @data[:border_hu] || @data[:border_up] ] + when :top + [@data[:border_nw], @data[:border_nx], @data[:border_n], @data[:border_ne], @data[:border_nd] || @data[:border_dn], nil ] + when :bot + [@data[:border_sw], @data[:border_sx], @data[:border_s], @data[:border_se], nil, @data[:border_su] || @data[:border_up] ] + else + [@data[:border_w], @data[:border_x], @data[:border_i], @data[:border_e], @data[:border_dn], @data[:border_up] ] + end + end + end + class Style @@defaults = { - :border_x => "-", :border_y => "|", :border_i => "+", + :border => AsciiBorder.new, :border_top => true, :border_bottom => true, :padding_left => 1, :padding_right => 1, :margin_left => '', :width => nil, :alignment => nil, - :all_separators => false + :all_separators => false, } + #@@defaults.merge!(@@ascii_borders) + + def border_x=(val) + @border[:border_x] = val + end + def border_y=(val) + @border[:border_y] = val + end + def border_i=(val) + @border[:border_i] = val + end + def border_y + @border[:border_y] + end + + #attr_reader :border_x, :border_y, :border_i + attr_accessor :border - attr_accessor :border_x - attr_accessor :border_y - attr_accessor :border_i attr_accessor :border_top attr_accessor :border_bottom @@ -53,17 +142,40 @@ def initialize options = {} end def apply options - options.each { |m, v| __send__ "#{m}=", v } + + options.each do |m, v| + #p "applying #{m.inspect} #{v}" + __send__ "#{m}=", v + end + end + + # Get vertical border elements from Border instance + def vertical + @border.vertical + end + + # Get horizontal border elements from Border instance + def horizontal(args) + @border.horizontal(args) + end + + def remove_verticals + @border.remove_verticals end class << self def defaults - @@defaults + klass_defaults = @@defaults.dup + # border is an object that needs to be duplicated on instantiation, + # otherwise everything will be referencing the same object-id. + klass_defaults[:border] = klass_defaults[:border].dup + return klass_defaults end def defaults= options @@defaults = defaults.merge(options) end + end def on_change attr @@ -74,6 +186,8 @@ def on_change attr yield attr.to_sym, value end end + + end end end diff --git a/lib/terminal-table/table.rb b/lib/terminal-table/table.rb index 3368138..c5a90f2 100644 --- a/lib/terminal-table/table.rb +++ b/lib/terminal-table/table.rb @@ -112,7 +112,7 @@ def number_of_columns def headings= arrays arrays = [arrays] unless arrays.first.is_a?(Array) @headings = arrays.map do |array| - row = Row.new(self, array) + row = HeadingRow.new(self, array) require_column_widths_recalc row end @@ -122,24 +122,38 @@ def headings= arrays # Render the table. def render - separator = Separator.new(self) - buffer = style.border_top ? [separator] : [] + + buffer = style.border_top ? [Separator.new(self)] : [] unless @title.nil? - buffer << Row.new(self, [title_cell_options]) - buffer << separator + buffer << TitleRow.new(self, [title_cell_options]) + buffer << Separator.new(self) end @headings.each do |row| unless row.cells.empty? buffer << row - buffer << separator + buffer << Separator.new(self) end end if style.all_separators - buffer += @rows.product([separator]).flatten + buffer += @rows.product([Separator.new(self)]).flatten else buffer += @rows - buffer << separator if style.border_bottom + buffer << Separator.new(self) if style.border_bottom + end + buffer.each_with_index do |r,idx| + #p [:zzz, r.class] + if r.is_a?(Separator) + prevrow = idx>0 ? buffer[idx-1] : nil + nextrow = buffer.fetch(idx+1, nil) + r.elab(prevrow, nextrow) + end end + + #buffer.each do |r| + # p(rr: r.edge) if r.is_a?(Separator) + #end + + buffer.map { |r| style.margin_left + r.render.rstrip }.join("\n") end alias :to_s :render diff --git a/spec/table_spec.rb b/spec/table_spec.rb index c0106d4..23bdb8e 100755 --- a/spec/table_spec.rb +++ b/spec/table_spec.rb @@ -41,7 +41,7 @@ module TerminalTable | 1 | 2 | 3 | | 4 | 5 | 6 | | 7 | 88 | - +---+---+----+ + +-------+----+ EOF end @@ -86,8 +86,8 @@ module TerminalTable it "should render separators" do @table.headings = ['Char', 'Num'] @table << ['a', 1] - separator = TerminalTable::Table::Separator.new(@table) - separator.render.should eq '+------+-----+' + separator = Terminal::Table::Separator.new(@table) + separator.render.should eq '+------------+' end it "should add separator" do @@ -131,6 +131,8 @@ module TerminalTable @table << ['b', 2] @table << ['c', 3] @table.style.padding_right.should eq 2 + p @table.style + @table.render.should eq <<-EOF.deindent yyx======x=====x yy:Char :Num : @@ -150,6 +152,8 @@ module TerminalTable @table << ['c', 3] @table.style.width = 21 @table.style.alignment = :right + p @table.style + @table.render.should eq <<-EOF.deindent +-----------+-------+ | Character | Num | @@ -195,7 +199,7 @@ module TerminalTable @table << ['b', 2] @table << ['c', 3] @table.render.should eq <<-EOF.deindent - +------+-----+ + +------------+ | Title | +------+-----+ | Char | Num | @@ -418,7 +422,7 @@ module TerminalTable @table.headings = ['one', { :value => 'two', :alignment => :center, :colspan => 2}] @table.rows = [['a', 1, 2], ['b', 3, 4], ['c', 3, 4]] @table.render.should eq <<-EOF.deindent - +-----+---+---+ + +-----+-------+ | one | two | +-----+---+---+ | a | 1 | 2 | @@ -438,7 +442,7 @@ module TerminalTable | a | 1 | 2 | | b | 3 | 4 | | joined | c | - +-----+-----+-------+ + +-----------+-------+ EOF end @@ -452,7 +456,7 @@ module TerminalTable | a | 1 | 2 | | b | 3 | 4 | | c | joined | - +-----+-----+-------+ + +-----+-------------+ EOF end @@ -466,7 +470,7 @@ module TerminalTable | a | 1 | 2 | | b | 3 | 4 | | c | joined that is very very long | - +-----+---------------+---------------+ + +-----+-------------------------------+ EOF end @@ -477,7 +481,7 @@ module TerminalTable +-------+---+---+ | 12345 | 2 | 3 | | 123456789 | 4 | - +-------+---+---+ + +-----------+---+ EOF end @@ -494,7 +498,7 @@ module TerminalTable | 123456789 | 4 | | | 123 | 444444444 | | 0 | | - +-------+---+-----+-----+ + +-----------------+-----+ EOF end @@ -509,7 +513,7 @@ module TerminalTable | 4 | 5555555 | | 66666666 | 777 | | 20202020202020202020 | - +-------+-------+------+ + +----------------------+ EOF end @@ -533,7 +537,7 @@ module TerminalTable @table.rows = [['a', 1, 2, 3], ['b', 4, 5, 6], ['c', 7, 8, 9]] @table.render.should eq <<-EOF.deindent - +------+---+---+---+ + +------+-----------+ | name | values | +------+---+---+---+ | a | 1 | 2 | 3 | @@ -560,7 +564,7 @@ module TerminalTable end @table.render.should eq <<-EOF.deindent - +--------+----+----+-------+------+-----+----+ + +--------+---------+--------------+----------+ | name | Values | Other values | Column 3 | +--------+----+----+-------+------+-----+----+ | row #1 | 0 | 1 | 2 | 3 | 4 | 5 | @@ -572,11 +576,11 @@ module TerminalTable it "should render a table with only X cell borders" do @table.style = {:border_x => "-", :border_y => "", :border_i => ""} + #@table.style.remove_verticals # new methodology is to use remove_verticals method. @table.headings = ['name', { :value => 'values', :alignment => :right, :colspan => 3}] @table.headings = ['name', { :value => 'values', :colspan => 3}] @table.rows = [['a', 1, 2, 3], ['b', 4, 5, 6], ['c', 7, 8, 9]] - @table.render.should eq <<-EOF.strip --------------- name values @@ -612,7 +616,7 @@ module TerminalTable @table.rows = [['a', 1, 2, 3], ['b', 4, 5, 6], ['c', 7, 8, 9]] @table.render.should eq <<-EOF.deindent - +------+---+---+---+ + +------+-----------+ | name | values | +------+---+---+---+ | a | 1 | 2 | 3 | From f98a6a434d0e1fec81ff10c775d3176f3cf5c48f Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Mon, 14 Dec 2020 21:31:00 -0500 Subject: [PATCH 17/38] adding/fixing tests for unicode tables --- lib/terminal-table/separator.rb | 1 + lib/terminal-table/style.rb | 11 +- lib/terminal-table/table.rb | 6 +- spec/table_spec.rb | 2 - spec/unicode_table_spec.rb | 688 ++++++++++++++++++++++++++++++++ 5 files changed, 704 insertions(+), 4 deletions(-) create mode 100755 spec/unicode_table_spec.rb diff --git a/lib/terminal-table/separator.rb b/lib/terminal-table/separator.rb index e30c984..f814054 100644 --- a/lib/terminal-table/separator.rb +++ b/lib/terminal-table/separator.rb @@ -16,6 +16,7 @@ def row_type def render left_edge, ctrflat, ctrud, right_edge, ctrdn, ctrup = @table.style.horizontal(row_type) + #p [row_type, left_edge, ctrflat, ctrud, right_edge, ctrdn, ctrup, @prevrow.class, @nextrow.class] prev_crossings = @prevrow.respond_to?(:crossings) ? @prevrow.crossings : [] next_crossings = @nextrow.respond_to?(:crossings) ? @nextrow.crossings : [] diff --git a/lib/terminal-table/style.rb b/lib/terminal-table/style.rb index a22744b..790c8a4 100644 --- a/lib/terminal-table/style.rb +++ b/lib/terminal-table/style.rb @@ -38,9 +38,13 @@ def remove_verticals self.class.const_get("VERTICALS").each { |key| @data[key] = "" } self.class.const_get("INTERSECTIONS").each { |key| @data[key] = "" } end + def remove_horizontals + self.class.const_get("HORIZONTALS").each { |key| @data[key] = "" } + end end class AsciiBorder < Border + HORIZONTALS = %i[border_x] VERTICALS = %i[border_y] INTERSECTIONS = %i[border_i] @@ -62,6 +66,7 @@ def horizontal(_type) end class UnicodeBorder < Border + HORIZONTALS = %i[border_x border_sx border_hx border_nx] VERTICALS = %i[border_y border_yw border_ye] INTERSECTIONS = %i[border_nw border_n border_ne border_nd border_hw border_hi border_he border_hd border_hu @@ -90,7 +95,7 @@ def horizontal(type) [@data[:border_nw], @data[:border_nx], @data[:border_n], @data[:border_ne], @data[:border_nd] || @data[:border_dn], nil ] when :bot [@data[:border_sw], @data[:border_sx], @data[:border_s], @data[:border_se], nil, @data[:border_su] || @data[:border_up] ] - else + else # center [@data[:border_w], @data[:border_x], @data[:border_i], @data[:border_e], @data[:border_dn], @data[:border_up] ] end end @@ -163,6 +168,10 @@ def remove_verticals @border.remove_verticals end + def remove_horizontals + @border.remove_horizontals + end + class << self def defaults klass_defaults = @@defaults.dup diff --git a/lib/terminal-table/table.rb b/lib/terminal-table/table.rb index c5a90f2..76c991f 100644 --- a/lib/terminal-table/table.rb +++ b/lib/terminal-table/table.rb @@ -135,7 +135,11 @@ def render end end if style.all_separators - buffer += @rows.product([Separator.new(self)]).flatten + @rows.each do |row| + buffer << row + buffer << Separator.new(self) + end + #buffer += @rows.product([Separator.new(self)]).flatten else buffer += @rows buffer << Separator.new(self) if style.border_bottom diff --git a/spec/table_spec.rb b/spec/table_spec.rb index 23bdb8e..8b99cf3 100755 --- a/spec/table_spec.rb +++ b/spec/table_spec.rb @@ -131,7 +131,6 @@ module TerminalTable @table << ['b', 2] @table << ['c', 3] @table.style.padding_right.should eq 2 - p @table.style @table.render.should eq <<-EOF.deindent yyx======x=====x @@ -152,7 +151,6 @@ module TerminalTable @table << ['c', 3] @table.style.width = 21 @table.style.alignment = :right - p @table.style @table.render.should eq <<-EOF.deindent +-----------+-------+ diff --git a/spec/unicode_table_spec.rb b/spec/unicode_table_spec.rb new file mode 100755 index 0000000..1962f00 --- /dev/null +++ b/spec/unicode_table_spec.rb @@ -0,0 +1,688 @@ +# encoding: utf-8 +require 'spec_helper' + +module Terminal + describe Table do + before :each do + @table = Table.new + @table.style = { :border => Terminal::Table::UnicodeBorder.new() } + + end + + it "should select columns" do + @table << ['foo', 'bar'] + @table << ['big foo', 'big foo bar'] + @table.column(1).should eq ['bar', 'big foo bar'] + end + + it "should select the column with headings at an index" do + @table << [1,2,3] + @table << [4,5,6] + @table.column_with_headings(2).should eq [3,6] + end + + #it "should select the columns with colspans > 1 in the index" do + # @table << [1,{:value => 2, :colspan => 2}] + # @table << [{:value => 3, :colspan => 2}, 4] + #end + + it "should account for the colspan when selecting columns" do + @table << [1,2,3] + @table << [{:value => "4,5", :colspan => 2}, 6] + @table.column_with_headings(0).should eq [1,"4,5"] + @table.column_with_headings(1).should eq [2,"4,5"] + @table.column_with_headings(2).should eq [3,6] + end + + it "should render tables with colspan properly" do + @table << [1,2,3] + @table << [4,5,6] + @table << [{:value => "7", :colspan => 2}, 88] + @table.render.should eq <<-EOF.deindent + ┌───┬───┬────┐ + │ 1 │ 2 │ 3 │ + │ 4 │ 5 │ 6 │ + │ 7 │ 88 │ + └───────┴────┘ + EOF + end + + it "should count columns" do + @table << [1, {:value => "2", :colspan => 2}] + @table << [{:value => "3", :colspan => 2}, 4] + @table.number_of_columns.should eq 3 + end + + it "should iterate columns" do + @table << [1, 2, 3] + @table << [4, 5, 6] + @table.columns.should eq [[1, 4], [2, 5], [3, 6]] + end + + it "should select columns" do + @table.headings = ['one', 'two'] + @table << ['foo', 'bar'] + @table << ['big foo', 'big foo bar'] + @table.column(1).should eq ['bar', 'big foo bar'] + end + + it "should select columns when using hashes" do + @table.headings = ['one', 'two'] + @table.rows = [[{ :value => 'a', :align => :left }, 1], ['b', 2], ['c', 3]] + @table.column(0).should eq ['a', 'b', 'c'] + end + + it "should find column length" do + @table << ['foo', 'bar', 'a'] + @table << ['big foo', 'big foo bar'] + @table.column_width(1).should eq 11 + end + + it "should find column length with headings" do + @table.headings = ['one', 'super long heading'] + @table << ['foo', 'bar', 'a'] + @table << ['big foo', 'big foo bar'] + @table.column_width(1).should eq 18 + end + + it "should render separators" do + @table.headings = ['Char', 'Num'] + @table << ['a', 1] + separator = Terminal::Table::Separator.new(@table) + ##ascii## separator.render.should eq '+------------+' + # Ambiguous what this should be... + # Technically it is a both a 'top' and a 'bottom', however the logic + # for top has priority. :/ + separator.render.should eq '┌────────────┐' + end + + it "should add separator" do + @table << ['a', 1] + @table.add_separator + @table << ['b', 2] + @table.rows.size.should eq 2 + end + + it "should render an empty table properly" do + @table.render.should eq <<-EOF.deindent + ┌┐ + └┘ + EOF + end + + it "should render properly" do + @table.headings = ['Char', 'Num'] + @table << ['a', 1] + @table << ['b', 2] + @table << ['c', 3] + @table.render.should eq <<-EOF.deindent + ┌──────┬─────┐ + │ Char │ Num │ + ╞══════╪═════╡ + │ a │ 1 │ + │ b │ 2 │ + │ c │ 3 │ + └──────┴─────┘ + EOF + end + + it "should render styles properly" do + @table.headings = ['Char', 'Num'] + @table.style = { + :padding_left => 0, :padding_right => 2, + :margin_left => 'y' * 2 + } + @table.style.border[:border_hi] = "x" + @table.style.border[:border_y] = ":" + @table.style.border[:border_nw] = "1" + @table.style.border[:border_ne] = "2" + @table.style.border[:border_sw] = "3" + @table.style.border[:border_se] = "4" + @table.style.border[:border_up] = "^" + @table.style.border[:border_hw] = "5" + @table.style.border[:border_he] = "6" + + @table << ['a', 1] + @table << ['b', 2] + @table << ['c', 3] + @table.style.padding_right.should eq 2 + + @table.render.should eq <<-EOF.deindent + yy1──────┬─────2 + yy│Char :Num │ + yy5══════x═════6 + yy│a :1 │ + yy│b :2 │ + yy│c :3 │ + yy3──────^─────4 + EOF + end + + + it "should render default alignment properly" do + @table.headings = ['Character', 'Num'] + @table << ['a', 1] + @table << ['b', 2] + @table << ['c', 3] + @table.style.width = 21 + @table.style.alignment = :right + + @table.render.should eq <<-EOF.deindent + ┌───────────┬───────┐ + │ Character │ Num │ + ╞═══════════╪═══════╡ + │ a │ 1 │ + │ b │ 2 │ + │ c │ 3 │ + └───────────┴───────┘ + EOF + end + + it "should render width properly" do + @table.headings = ['Char', 'Num'] + @table << ['a', 1] + @table << ['b', 2] + @table << ['c', 3] + @table.style.width = 21 + @table.render.should eq <<-EOF.deindent + ┌─────────┬─────────┐ + │ Char │ Num │ + ╞═════════╪═════════╡ + │ a │ 1 │ + │ b │ 2 │ + │ c │ 3 │ + └─────────┴─────────┘ + EOF + end + + it "should raise an error if the table width exceeds style width" do + @table.headings = ['Char', 'Num'] + @table << ['a', 1] + @table << ['b', 2] + @table << ['c', 3] + @table << ['d', 'x' * 22] + @table.style.width = 21 + lambda { @table.render }.should raise_error "Table width exceeds wanted width of 21 characters." + end + + it "should render title properly" do + @table.title = "Title" + @table.headings = ['Char', 'Num'] + @table << ['a', 1] + @table << ['b', 2] + @table << ['c', 3] + @table.render.should eq <<-EOF.deindent + ┌────────────┐ + │ Title │ + ├──────┬─────┤ + │ Char │ Num │ + ╞══════╪═════╡ + │ a │ 1 │ + │ b │ 2 │ + │ c │ 3 │ + └──────┴─────┘ + EOF + end + + it "should render properly without headings" do + @table << ['a', 1] + @table << ['b', 2] + @table << ['c', 3] + @table.render.should eq <<-EOF.deindent + ┌───┬───┐ + │ a │ 1 │ + │ b │ 2 │ + │ c │ 3 │ + └───┴───┘ + EOF + end + + it "should render separators" do + @table.headings = ['Char', 'Num'] + @table << ['a', 1] + @table << ['b', 2] + @table.add_separator + @table << ['c', 3] + @table.render.should eq <<-EOF.deindent + ┌──────┬─────┐ + │ Char │ Num │ + ╞══════╪═════╡ + │ a │ 1 │ + │ b │ 2 │ + ├──────┼─────┤ + │ c │ 3 │ + └──────┴─────┘ + EOF + end + + it "should align columns with separators" do + @table.headings = ['Char', 'Num'] + @table << ['a', 1] + @table << ['b', 2] + @table.add_separator + @table << ['c', 3] + @table.align_column 1, :right + @table.render.should eq <<-EOF.deindent + ┌──────┬─────┐ + │ Char │ Num │ + ╞══════╪═════╡ + │ a │ 1 │ + │ b │ 2 │ + ├──────┼─────┤ + │ c │ 3 │ + └──────┴─────┘ + EOF + end + + + it "should render properly using block syntax" do + table = Terminal::Table.new do |t| + t << ['a', 1] + t << ['b', 2] + t << ['c', 3] + end + table.render.should eq <<-EOF.deindent + +---+---+ + | a | 1 | + | b | 2 | + | c | 3 | + +---+---+ + EOF + end + + it "should render properly using instance_eval block syntax" do + table = Terminal::Table.new do + add_row ['a', 1] + add_row ['b', 2] + add_row ['c', 3] + end + table.render.should eq <<-EOF.deindent + +---+---+ + | a | 1 | + | b | 2 | + | c | 3 | + +---+---+ + EOF + end + + it "should render multi-row headings properly" do + @table.headings = [['Char', 'Num'], [{ :value => "2nd heading", :colspan => 2 }]] + @table << ['a', 1] + @table << ['b', 2] + @table << ['c', 3] + @table.render.should eq <<-EOF.deindent + ┌──────┬──────┐ + │ Char │ Num │ + ╞══════╧══════╡ + │ 2nd heading │ + ╞══════╤══════╡ + │ a │ 1 │ + │ b │ 2 │ + │ c │ 3 │ + └──────┴──────┘ + EOF + end + + it "should allows a hash of options for creation" do + headings = ['Char', 'Num'] + rows = [['a', 1], ['b', 2], ['c', 3]] + Terminal::Table.new(:rows => rows, :headings => headings).render.should eq <<-EOF.deindent + +------+-----+ + | Char | Num | + +------+-----+ + | a | 1 | + | b | 2 | + | c | 3 | + +------+-----+ + EOF + end + + it "should flex for large cells" do + @table.headings = ['Just some characters', 'Num'] + @table.rows = [['a', 1], ['b', 2], ['c', 3]] + @table.render.should eq <<-EOF.deindent + ┌──────────────────────┬─────┐ + │ Just some characters │ Num │ + ╞══════════════════════╪═════╡ + │ a │ 1 │ + │ b │ 2 │ + │ c │ 3 │ + └──────────────────────┴─────┘ + EOF + end + + it "should allow alignment of headings and cells" do + @table.headings = ['Characters', {:value => 'Nums', :alignment => :right} ] + @table << [{:value => 'a', :alignment => :center}, 1] + @table << ['b', 222222222222222] + @table << ['c', 3] + @table.render.should eq <<-EOF.deindent + ┌────────────┬─────────────────┐ + │ Characters │ Nums │ + ╞════════════╪═════════════════╡ + │ a │ 1 │ + │ b │ 222222222222222 │ + │ c │ 3 │ + └────────────┴─────────────────┘ + EOF + + end + + it "should align columns, but allow specifics to remain" do + @table.headings = ['Just some characters', 'Num'] + @table.rows = [[{:value => 'a', :alignment => :left}, 1], ['b', 2], ['c', 3]] + @table.align_column 0, :center + @table.align_column 1, :right + @table.render.should eq <<-EOF.deindent + ┌──────────────────────┬─────┐ + │ Just some characters │ Num │ + ╞══════════════════════╪═════╡ + │ a │ 1 │ + │ b │ 2 │ + │ c │ 3 │ + └──────────────────────┴─────┘ + EOF + end + + describe "#==" do + it "should be equal to itself" do + t = Table.new + t.should eq t + end + + # it "should be equal with two empty tables" do + # table_one = Table.new + # table_two = Table.new + # + # table_one.should eq table_two + # table_two.should eq table_one + # end + + it "should not be equal with different headings" do + table_one = Table.new + table_two = Table.new + + table_one.headings << "a" + + table_one.should_not eq table_two + table_two.should_not eq table_one + end + + it "should not be equal with different rows" do + table_one = Table.new + table_two = Table.new + + table_one.should_not eq table_two + table_two.should_not eq table_one + end + + it "should not be equal if the other object does not respond_to? :headings" do + table_one = Table.new + table_two = Object.new + allow(table_two).to receive(:rows).and_return([]) + table_one.should_not eq table_two + end + + it "should not be equal if the other object does not respond_to? :rows" do + table_one = Table.new + table_two = Object.new + allow(table_two).to receive(:rows).and_return([]) + table_one.should_not eq table_two + end + end + + it "should handle colspan inside heading" do + @table.headings = ['one', { :value => 'two', :alignment => :center, :colspan => 2}] + @table.rows = [['a', 1, 2], ['b', 3, 4], ['c', 3, 4]] + @table.render.should eq <<-EOF.deindent + ┌─────┬───────┐ + │ one │ two │ + ╞═════╪═══╤═══╡ + │ a │ 1 │ 2 │ + │ b │ 3 │ 4 │ + │ c │ 3 │ 4 │ + └─────┴───┴───┘ + EOF + end + + it "should handle colspan inside cells" do + @table.headings = ['one', 'two', 'three'] + @table.rows = [['a', 1, 2], ['b', 3, 4], [{:value => "joined", :colspan => 2,:alignment => :center}, 'c']] + @table.render.should eq <<-EOF.deindent + ┌─────┬─────┬───────┐ + │ one │ two │ three │ + ╞═════╪═════╪═══════╡ + │ a │ 1 │ 2 │ + │ b │ 3 │ 4 │ + │ joined │ c │ + └───────────┴───────┘ + EOF + end + + it "should handle colspan inside cells regardless of colspan position" do + @table.headings = ['one', 'two', 'three'] + @table.rows = [['a', 1, 2], ['b', 3, 4], ['c', {:value => "joined", :colspan => 2,:alignment => :center}]] + @table.render.should eq <<-EOF.deindent + ┌─────┬─────┬───────┐ + │ one │ two │ three │ + ╞═════╪═════╪═══════╡ + │ a │ 1 │ 2 │ + │ b │ 3 │ 4 │ + │ c │ joined │ + └─────┴─────────────┘ + EOF + end + + it "should handle overflowing colspans" do + @table.headings = ['one', 'two', 'three'] + @table.rows = [['a', 1, 2], ['b', 3, 4], ['c', {:value => "joined that is very very long", :colspan => 2,:alignment => :center}]] + @table.render.should eq <<-EOF.deindent + ┌─────┬───────────────┬───────────────┐ + │ one │ two │ three │ + ╞═════╪═══════════════╪═══════════════╡ + │ a │ 1 │ 2 │ + │ b │ 3 │ 4 │ + │ c │ joined that is very very long │ + └─────┴───────────────────────────────┘ + EOF + end + + it "should only increase column size for multi-column if it is unavoidable" do + @table << [12345,2,3] + @table << [{:value => 123456789, :colspan => 2}, 4] + @table.render.should eq <<-EOF.deindent + ┌───────┬───┬───┐ + │ 12345 │ 2 │ 3 │ + │ 123456789 │ 4 │ + └───────────┴───┘ + EOF + end + + it "should handle extreme case with complex table layout" do + @table << [1, 2, 33, 4] + @table << [12345, {:value => 54321, :colspan => 2}, ''] + @table << [{:value => 123456789, :colspan => 2}, 4, ''] + @table << [{:value => 123, :colspan => 2}, {:value => 444444444, :colspan => 2}] + @table << [{:value => 0, :colspan => 3}, ''] + @table.render.should eq <<-EOF.deindent + ┌───────┬───┬─────┬─────┐ + │ 1 │ 2 │ 33 │ 4 │ + │ 12345 │ 54321 │ │ + │ 123456789 │ 4 │ │ + │ 123 │ 444444444 │ + │ 0 │ │ + └─────────────────┴─────┘ + EOF + end + + it "should try to evenly divide contents over colspans" do + @table << [1, 2, 3] + @table << [4, {:value => 5555555, :colspan => 2}] + @table << [{:value => 66666666 , :colspan => 2}, 777] + @table << [{:value => 20202020202020202020, :colspan => 3}] + @table.render.should eq <<-EOF.deindent + ┌───────┬───────┬──────┐ + │ 1 │ 2 │ 3 │ + │ 4 │ 5555555 │ + │ 66666666 │ 777 │ + │ 20202020202020202020 │ + └──────────────────────┘ + EOF + end + + it "should handle colspan 1" do + @table.headings = ['name', { :value => 'values', :colspan => 1}] + @table.rows = [['a', 1], ['b', 4], ['c', 7]] + @table.render.should eq <<-EOF.deindent + ┌──────┬────────┐ + │ name │ values │ + ╞══════╪════════╡ + │ a │ 1 │ + │ b │ 4 │ + │ c │ 7 │ + └──────┴────────┘ + EOF + end + + it "should handle big colspan" do + @table.headings = ['name', { :value => 'values', :alignment => :right, :colspan => 3}] + @table.headings = ['name', { :value => 'values', :colspan => 3}] + @table.rows = [['a', 1, 2, 3], ['b', 4, 5, 6], ['c', 7, 8, 9]] + + @table.render.should eq <<-EOF.deindent + ┌──────┬───────────┐ + │ name │ values │ + ╞══════╪═══╤═══╤═══╡ + │ a │ 1 │ 2 │ 3 │ + │ b │ 4 │ 5 │ 6 │ + │ c │ 7 │ 8 │ 9 │ + └──────┴───┴───┴───┘ + EOF + end + + it "should handle multiple colspan" do + @table.headings = [ + 'name', + { :value => 'Values', :alignment => :right, :colspan => 2}, + { :value => 'Other values', :alignment => :right, :colspan => 2}, + { :value => 'Column 3', :alignment => :right, :colspan => 2} + ] + + 3.times do |row_index| + row = ["row ##{row_index+1}"] + 6.times do |i| + row << row_index*6 + i + end + @table.add_row(row) + end + + @table.render.should eq <<-EOF.deindent + ┌────────┬─────────┬──────────────┬──────────┐ + │ name │ Values │ Other values │ Column 3 │ + ╞════════╪════╤════╪═══════╤══════╪═════╤════╡ + │ row #1 │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ + │ row #2 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ + │ row #3 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ + └────────┴────┴────┴───────┴──────┴─────┴────┘ + EOF + end + + it "should render a table with only X cell borders" do + #@table.style = {:border_x => "-", :border_y => "", :border_i => ""} + @table.style.remove_verticals # new methodology is to use remove_verticals method. + + @table.headings = ['name', { :value => 'values', :alignment => :right, :colspan => 3}] + @table.headings = ['name', { :value => 'values', :colspan => 3}] + @table.rows = [['a', 1, 2, 3], ['b', 4, 5, 6], ['c', 7, 8, 9]] + @table.render.should eq <<-EOF.strip +─────────────── + name values +═══════════════ + a 1 2 3 + b 4 5 6 + c 7 8 9 +─────────────── + EOF + end + + it "should render a table without cell borders" do + #@table.style = {:border_x => "", :border_y => "", :border_i => ""} + @table.style.remove_verticals # new methodology is to use remove_verticals method. + @table.style.remove_horizontals # new methodology is to use remove_horizontal method. + + @table.headings = ['name', { :value => 'values', :alignment => :right, :colspan => 3}] + @table.headings = ['name', { :value => 'values', :colspan => 3}] + @table.rows = [['a', 1, 2, 3], ['b', 4, 5, 6], ['c', 7, 8, 9]] + + @table.render.should eq <<-EOF + + name values + + a 1 2 3 + b 4 5 6 + c 7 8 9 + EOF + end + + it "should render a table with all separators" do + @table.style = {:all_separators => true} + @table.headings = ['name', { :value => 'values', :alignment => :right, :colspan => 3}] + @table.headings = ['name', { :value => 'values', :colspan => 3}] + @table.rows = [['a', 1, 2, 3], ['b', 4, 5, 6], ['c', 7, 8, 9]] + + @table.render.should eq <<-EOF.deindent + ┌──────┬───────────┐ + │ name │ values │ + ╞══════╪═══╤═══╤═══╡ + │ a │ 1 │ 2 │ 3 │ + ├──────┼───┼───┼───┤ + │ b │ 4 │ 5 │ 6 │ + ├──────┼───┼───┼───┤ + │ c │ 7 │ 8 │ 9 │ + └──────┴───┴───┴───┘ + EOF + end + + it "should render a table with fullwidth characters" do + @table.headings = ['COL 1', 'COL 2', 'COL 3', 'COL4'] + @table << ['中文', 'にっぽんご', '한국어', 'ABC'] + @table << ['Chinese','Japanese','Korean', '.......'] + @table.render.should eq <<-EOF.deindent + ┌─────────┬────────────┬────────┬──────────┐ + │ COL 1 │ COL 2 │ COL 3 │ COL4 │ + ╞═════════╪════════════╪════════╪══════════╡ + │ 中文 │ にっぽんご │ 한국어 │ ABC │ + │ Chinese │ Japanese │ Korean │ ....... │ + └─────────┴────────────┴────────┴──────────┘ + EOF + end + + it "should allow to not generate top border" do + @table.style = { :border_top => false } + @table.headings = ['name', 'value'] + @table.rows = [['a', 1], ['b', 2], ['c', 3]] + @table.render.should eq <<-EOF.deindent + │ name │ value │ + ╞══════╪═══════╡ + │ a │ 1 │ + │ b │ 2 │ + │ c │ 3 │ + └──────┴───────┘ + EOF + end + + it "should allow to not generate bottom border" do + @table.style = { :border_bottom => false } + @table.headings = ['name', 'value'] + @table.rows = [['a', 1], ['b', 2], ['c', 3]] + @table.render.should eq <<-EOF.deindent + ┌──────┬───────┐ + │ name │ value │ + ╞══════╪═══════╡ + │ a │ 1 │ + │ b │ 2 │ + │ c │ 3 │ + EOF + end + end +end From f154385c9c62d437b0abc565cd621d4795f29070 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Mon, 14 Dec 2020 22:02:34 -0500 Subject: [PATCH 18/38] adding in alternative border styles --- examples/examples_unicode.rb | 2 +- lib/terminal-table/style.rb | 158 ++++++++++++++++++----------------- spec/unicode_table_spec.rb | 18 ++-- 3 files changed, 90 insertions(+), 88 deletions(-) diff --git a/examples/examples_unicode.rb b/examples/examples_unicode.rb index 3a3d09d..7779e64 100644 --- a/examples/examples_unicode.rb +++ b/examples/examples_unicode.rb @@ -1,7 +1,7 @@ $:.unshift File.dirname(__FILE__) + '/../lib' require 'terminal-table/import' -Terminal::Table::Style.defaults = { :border => Terminal::Table::UnicodeBorder.new() } +Terminal::Table::Style.defaults = { :border => Terminal::Table::UnicodeThickEdgeBorder.new() } puts puts table(['a', 'b'], [1, 2], [3, 4]) diff --git a/lib/terminal-table/style.rb b/lib/terminal-table/style.rb index 790c8a4..2cc1f00 100644 --- a/lib/terminal-table/style.rb +++ b/lib/terminal-table/style.rb @@ -1,27 +1,9 @@ # coding: utf-8 +require 'forwardable' + module Terminal class Table - # A Style object holds all the formatting information for a Table object - # - # To create a table with a certain style, use either the constructor - # option :style, the Table#style object or the Table#style= method - # - # All these examples have the same effect: - # - # # by constructor - # @table = Table.new(:style => {:padding_left => 2, :width => 40}) - # - # # by object - # @table.style.padding_left = 2 - # @table.style.width = 40 - # - # # by method - # @table.style = {:padding_left => 2, :width => 40} - # - # To set a default style for all tables created afterwards use Style.defaults= - # - # TerminalTable::Table::Style.defaults = {:width => 80} - # + class Border attr_accessor :data def []=(key, val) @@ -44,64 +26,113 @@ def remove_horizontals end class AsciiBorder < Border - HORIZONTALS = %i[border_x] - VERTICALS = %i[border_y] - INTERSECTIONS = %i[border_i] + HORIZONTALS = %i[x] + VERTICALS = %i[y] + INTERSECTIONS = %i[i] def initialize - @data = { border_x: "-", border_y: "|", border_i: "+" } + @data = { x: "-", y: "|", i: "+" } end + # Get vertical border elements # @return [Array] 3-element list of [left, center, right] def vertical - y = @data[:border_y] + y = @data[:y] [y, y, y] # left, center, right end + # Get horizontal border elements # @return [Array] a 6 element list of: [i-left, horizontal-bar, i-up/down, i-right, i-down, i-up] def horizontal(_type) - x, i = @data[:border_x], @data[:border_i] + x, i = @data[:x], @data[:i] [i, x, i, i, i, i] end end class UnicodeBorder < Border - HORIZONTALS = %i[border_x border_sx border_hx border_nx] - VERTICALS = %i[border_y border_yw border_ye] - INTERSECTIONS = %i[border_nw border_n border_ne border_nd - border_hw border_hi border_he border_hd border_hu - border_w border_i border_e border_dn border_up - border_sw border_s border_se border_su] + HORIZONTALS = %i[x sx hx nx] + VERTICALS = %i[y yw ye] + INTERSECTIONS = %i[nw n ne nd + hw hi he hd hu + w i e dn up + sw s se su] def initialize @data = { - border_nw: "┌", border_nx: "─", border_n: "┬", border_ne: "┐", border_nd: nil, - border_yw: "│", border_y: "│", border_ye: "│", - border_hw: "╞", border_hx: "═", border_hi: "╪", border_he: "╡", border_hd: '╤', border_hu: "╧", - border_w: "├", border_x: "─", border_i: "┼", border_e: "┤", border_dn: "┬", border_up: "┴", - border_sw: "└", border_sx: "─", border_s: "┴", border_se: "┘", border_su: nil, + nw: "┌", nx: "─", n: "┬", ne: "┐", + yw: "│", y: "│", ye: "│", + hw: "╞", hx: "═", hi: "╪", he: "╡", hd: '╤', hu: "╧", + w: "├", x: "─", i: "┼", e: "┤", dn: "┬", up: "┴", + sw: "└", sx: "─", s: "┴", se: "┘", } end + # Get vertical border elements # @return [Array] 3-element list of [left, center, right] def vertical - [@data[:border_yw], @data[:border_y], @data[:border_ye]] + [@data[:yw], @data[:y], @data[:ye]] end + # Get horizontal border elements # @return [Array] a 6 element list of: [i-left, horizontal-bar, i-up/down, i-right, i-down, i-up] def horizontal(type) case type when :below_heading - [@data[:border_hw], @data[:border_hx], @data[:border_hi], @data[:border_he], @data[:border_hd] || @data[:border_dn], @data[:border_hu] || @data[:border_up] ] + [@data[:hw], @data[:hx], @data[:hi], @data[:he], @data[:hd], @data[:hu] ] when :top - [@data[:border_nw], @data[:border_nx], @data[:border_n], @data[:border_ne], @data[:border_nd] || @data[:border_dn], nil ] + [@data[:nw], @data[:nx], @data[:n], @data[:ne], @data[:n], nil ] when :bot - [@data[:border_sw], @data[:border_sx], @data[:border_s], @data[:border_se], nil, @data[:border_su] || @data[:border_up] ] + [@data[:sw], @data[:sx], @data[:s], @data[:se], nil, @data[:s] || @data[:up] ] else # center - [@data[:border_w], @data[:border_x], @data[:border_i], @data[:border_e], @data[:border_dn], @data[:border_up] ] + [@data[:w], @data[:x], @data[:i], @data[:e], @data[:dn], @data[:up] ] end end end + # Unicode Border With rounded edges + class UnicodeRoundBorder < UnicodeBorder + def initialize + super + @data.merge!({nw: '╭', ne: '╮', sw: '╰', se: '╯'}) + end + end + + # Unicode Border with thick outer edges + class UnicodeThickEdgeBorder < UnicodeBorder + def initialize + @data = { + nw: "┏", nx: "━", n: "┯", ne: "┓", nd: nil, + yw: "┃", y: "│", ye: "┃", + hw: "┣", hx: "═", hi: "╪", he: "┫", hd: '╤', hu: "╧", + w: "┠", x: "─", i: "┼", e: "┨", dn: "┬", up: "┴", + sw: "┗", sx: "━", s: "┷", se: "┛", su: nil, + } + end + end + + # A Style object holds all the formatting information for a Table object + # + # To create a table with a certain style, use either the constructor + # option :style, the Table#style object or the Table#style= method + # + # All these examples have the same effect: + # + # # by constructor + # @table = Table.new(:style => {:padding_left => 2, :width => 40}) + # + # # by object + # @table.style.padding_left = 2 + # @table.style.width = 40 + # + # # by method + # @table.style = {:padding_left => 2, :width => 40} + # + # To set a default style for all tables created afterwards use Style.defaults= + # + # Terminal::Table::Style.defaults = {:width => 80} + # class Style + extend Forwardable + def_delegators :@border, :vertical, :horizontal, :remove_verticals, :remove_horizontals + @@defaults = { :border => AsciiBorder.new, :border_top => true, :border_bottom => true, @@ -110,22 +141,14 @@ class Style :width => nil, :alignment => nil, :all_separators => false, } - #@@defaults.merge!(@@ascii_borders) - def border_x=(val) - @border[:border_x] = val - end - def border_y=(val) - @border[:border_y] = val - end - def border_i=(val) - @border[:border_i] = val - end - def border_y - @border[:border_y] - end + ## settors/gettor for legacy ascii borders + def border_x=(val) ; @border[:x] = val ; end + def border_y=(val) ; @border[:y] = val ; end + def border_i=(val) ; @border[:i] = val ; end + def border_y ; @border[:y] ; end - #attr_reader :border_x, :border_y, :border_i + # Accessor for instance of Border attr_accessor :border attr_accessor :border_top @@ -147,31 +170,11 @@ def initialize options = {} end def apply options - options.each do |m, v| - #p "applying #{m.inspect} #{v}" __send__ "#{m}=", v end end - # Get vertical border elements from Border instance - def vertical - @border.vertical - end - - # Get horizontal border elements from Border instance - def horizontal(args) - @border.horizontal(args) - end - - def remove_verticals - @border.remove_verticals - end - - def remove_horizontals - @border.remove_horizontals - end - class << self def defaults klass_defaults = @@defaults.dup @@ -196,7 +199,6 @@ def on_change attr end end - end end end diff --git a/spec/unicode_table_spec.rb b/spec/unicode_table_spec.rb index 1962f00..8006751 100755 --- a/spec/unicode_table_spec.rb +++ b/spec/unicode_table_spec.rb @@ -132,15 +132,15 @@ module Terminal :padding_left => 0, :padding_right => 2, :margin_left => 'y' * 2 } - @table.style.border[:border_hi] = "x" - @table.style.border[:border_y] = ":" - @table.style.border[:border_nw] = "1" - @table.style.border[:border_ne] = "2" - @table.style.border[:border_sw] = "3" - @table.style.border[:border_se] = "4" - @table.style.border[:border_up] = "^" - @table.style.border[:border_hw] = "5" - @table.style.border[:border_he] = "6" + @table.style.border[:hi] = "x" + @table.style.border[:y] = ":" + @table.style.border[:nw] = "1" + @table.style.border[:ne] = "2" + @table.style.border[:sw] = "3" + @table.style.border[:se] = "4" + @table.style.border[:up] = "^" + @table.style.border[:hw] = "5" + @table.style.border[:he] = "6" @table << ['a', 1] @table << ['b', 2] From 4d59511710b957755c1f043284a3615ef68fcdef Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Sat, 19 Dec 2020 13:11:30 -0500 Subject: [PATCH 19/38] addressing some open issues; changing documentation to markdown, adding in unicode explanation and examples; creating additional tests; --- .gitignore | 3 + README.md | 355 +++++++++++++++++++++++++++++++++++ examples/examples_unicode.rb | 3 +- examples/issue100.rb | 34 ++++ examples/issue111.rb | 4 + examples/issue95.rb | 42 +++++ lib/terminal-table.rb | 4 +- lib/terminal-table/cell.rb | 12 +- lib/terminal-table/style.rb | 83 ++++++-- lib/terminal-table/table.rb | 6 +- lib/terminal-table/util.rb | 13 ++ spec/style_spec.rb | 29 +++ spec/table_spec.rb | 29 +++ spec/unicode_table_spec.rb | 2 +- 14 files changed, 583 insertions(+), 36 deletions(-) create mode 100644 README.md create mode 100755 examples/issue100.rb create mode 100755 examples/issue111.rb create mode 100755 examples/issue95.rb create mode 100644 lib/terminal-table/util.rb create mode 100644 spec/style_spec.rb diff --git a/.gitignore b/.gitignore index 01be5cf..ea325b8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ tmp doc vendor /.bundle + +# tempfiles +*~ diff --git a/README.md b/README.md new file mode 100644 index 0000000..c8137ad --- /dev/null +++ b/README.md @@ -0,0 +1,355 @@ +[](https://github.com/tj/terminal-table/actions) + +# Terminal Table + +## Description + +Terminal Table is a fast and simple, yet feature rich table generator +written in Ruby. It supports ASCII and Unicode formatted tables. + +## Installation + +``` +$ gem install terminal-table +``` +## Usage + +### Basics + +To use Terminal Table: + +```ruby +require 'terminal-table' +``` +To generate a table, provide an array of arrays (which are interpreted as +rows): + +```ruby +rows = [] +rows << ['One', 1] +rows << ['Two', 2] +rows << ['Three', 3] +table = Terminal::Table.new :rows => rows + +# > puts table +# +# +-------+---+ +# | One | 1 | +# | Two | 2 | +# | Three | 3 | +# +-------+---+ +``` +The constructor can also be given a block which is either yielded the Table +object or instance evaluated: + +```ruby +table = Terminal::Table.new do |t| + t.rows = rows +end + +table = Terminal::Table.new do + self.rows = rows +end +``` +Adding rows one by one: + +```ruby +table = Terminal::Table.new do |t| + t << ['One', 1] + t.add_row ['Two', 2] +end +``` +To add separators between rows: + +```ruby +table = Terminal::Table.new do |t| + t << ['One', 1] + t << :separator + t.add_row ['Two', 2] + t.add_separator + t.add_row ['Three', 3] +end + +# > puts table +# +# +-------+---+ +# | One | 1 | +# +-------+---+ +# | Two | 2 | +# +-------+---+ +# | Three | 3 | +# +-------+---+ +``` +Cells can handle multiline content: + +```ruby +table = Terminal::Table.new do |t| + t << ['One', 1] + t << :separator + t.add_row ["Two\nDouble", 2] + t.add_separator + t.add_row ['Three', 3] +end + +# > puts table +# +# +--------+---+ +# | One | 1 | +# +--------+---+ +# | Two | 2 | +# | Double | | +# +--------+---+ +# | Three | 3 | +# +--------+---+ +``` +### Head + +To add a head to the table: + +```ruby +table = Terminal::Table.new :headings => ['Word', 'Number'], :rows => rows + +# > puts table +# +# +-------+--------+ +# | Word | Number | +# +-------+--------+ +# | One | 1 | +# | Two | 2 | +# | Three | 3 | +# +-------+--------+ +``` +### Title + +To add a title to the table: + +```ruby +table = Terminal::Table.new :title => "Cheatsheet", :headings => ['Word', 'Number'], :rows => rows + +# > puts table +# +# +------------+--------+ +# | Cheatsheet | +# +------------+--------+ +# | Word | Number | +# +------------+--------+ +# | One | 1 | +# | Two | 2 | +# | Three | 3 | +# +------------+--------+ +``` +### Alignment + +To align the second column to the right: + +```ruby +table.align_column(1, :right) + +# > puts table +# +# +-------+--------+ +# | Word | Number | +# +-------+--------+ +# | One | 1 | +# | Two | 2 | +# | Three | 3 | +# +-------+--------+ +``` +To align an individual cell, you specify the cell value in a hash along the +alignment: + +```ruby +table << ["Four", {:value => 4.0, :alignment => :center}] + +# > puts table +# +# +-------+--------+ +# | Word | Number | +# +-------+--------+ +# | One | 1 | +# | Two | 2 | +# | Three | 3 | +# | Four | 4.0 | +# +-------+--------+ +``` +### Style + +To specify style options: + +```ruby +table = Terminal::Table.new :headings => ['Word', 'Number'], :rows => rows, :style => {:width => 80} + +# > puts table +# +# +--------------------------------------+---------------------------------------+ +# | Word | Number | +# +--------------------------------------+---------------------------------------+ +# | One | 1 | +# | Two | 2 | +# | Three | 3 | +# +--------------------------------------+---------------------------------------+ +``` +And change styles on the fly: + +```ruby +table.style = {:width => 40, :padding_left => 3, :border_x => "=", :border_i => "x"} + +# > puts table +# +# x====================x=================x +# | Cheatsheet | +# x====================x=================x +# | Word | Number | +# x====================x=================x +# | One | 1 | +# | Two | 2 | +# | Three | 3 | +# x====================x=================x +``` +You can also use styles to add a separator after every row: + +```ruby +table = Terminal::Table.new do |t| + t.add_row [1, 'One'] + t.add_row [2, 'Two'] + t.add_row [3, 'Three'] + t.style = {:all_separators => true} +end + +# > puts table +# +# +---+-------+ +# | 1 | One | +# +---+-------+ +# | 2 | Two | +# +---+-------+ +# | 3 | Three | +# +---+-------+ +``` +You can also use styles to disable top and bottom borders of the table. + +```ruby +table = Terminal::Table.new do |t| + t.headings = ['id', 'name'] + t.rows = [[1, 'One'], [2, 'Two'], [3, 'Three']] + t.style = { :border_top => false, :border_bottom => false } +end + +# > puts table +# | id | name | +# +----+-------+ +# | 1 | One | +# | 2 | Two | +# | 3 | Three | +``` + +And also to disable left and right borders of the table. + +```ruby +table = Terminal::Table.new do |t| + t.headings = ['id', 'name'] + t.rows = [[1, 'One'], [2, 'Two'], [3, 'Three']] + t.style = { :border_left => false, :border_right => false } +end + +# > puts table +# ----+------- +# id | name +# ----+------- +# 1 | One +# 2 | Two +# 3 | Three +# ----+------- +``` + +To change the default style options: + +```ruby +Terminal::Table::Style.defaults = {:width => 80} +``` +All Table objects created afterwards will inherit these defaults. + +### Constructor options and setter methods + +Valid options for the constructor are `:rows`, `:headings`, `:style` and `:title` - +and all options can also be set on the created table object by their setter +method: + +```ruby +table = Terminal::Table.new +table.title = "Cheatsheet" +table.headings = ['Word', 'Number'] +table.rows = rows +table.style = {:width => 40} +``` + +## New Formatting + +### Unicode Table Borders +A support for Unicode 'box art' borders presented a challenge, as the basics of terminal-table only handled three border types: horizontal (x), vertical (y), and intersection (i). For proper box-art, it became necessary to enable different types of corners/edges for multiple intersection types. + +For the sake of backward compatiblity, the previous interface is still supported, this gem has been around a long time and making breaking changes would be inconvenient. The new interface is required for any complex and/or unicode style bordering. A few variations on border style are supported via some new classes and creation of additional classes (or modification of characters used in existing ones) will allow for customized border types. + +The simplest way to use an alternate border is one of the following: +``` +table.style = { :border => :unicode } +table.style = { :border => :unicode_round } +table.style = { :border => :unicode_thick_edge } +``` + +These are a convenience wrapper around setting border using an instance of a class that inherits from Table::Terminal::Border +``` +table.style = { :border => Terminal::Table::UnicodeBorder.new() } +table.style = { :border => Terminal::Table::UnicodeRoundBorder.new() } +table.style = { :border => Terminal::Table::UnicodeThickEdgeBorder.new() } +``` + +If you define a custom class and wish to use the symbol shortcut, you must namespace within `Terminal::Table` and end your class name with `Border`. + +### Markdown Compatiblity +Per popular request, markdown formatted tables can be generated by using the following border style: + +``` +table.style = { :border => :markdown } +``` + +### Ascii Borders +Ascii borders are default, but can be explicitly set with: +``` +table.style = { :border => :ascii } +``` + +### Customizing Borders +Inside the `UnicodeBorder` class, there are definitions for a variety of corner/intersection types. + +``` +@data = { +nw: "┌", nx: "─", n: "┬", ne: "┐", +yw: "│", y: "│", ye: "│", +hw: "╞", hx: "═", hi: "╪", he: "╡", hd: '╤', hu: "╧", +w: "├", x: "─", i: "┼", e: "┤", dn: "┬", up: "┴", +sw: "└", sx: "─", s: "┴", se: "┘", +} +``` + +Note that many are defined as directional (:nw == north-west), others defined in terms of 'x' or 'y'. +The border that separates headings (below each heading) are defined with 'h*' entries. + +When defining a new set of borders, it's probably easiest to define a new class that inherits from UnicodeBorder and replaces the @data Hash. +However, these corners can be these can be overridden with: + +``` +table.style = {border: :unicode} +table.style.border[:nw] = '*' # override the north-west corner of the table +``` + +## More examples + +For more examples, please see the `examples` directory included in the +source distribution. + +## Author + +TJ Holowaychuk + +Unicode table support by Ben Bowers https://github.com/nanobowers diff --git a/examples/examples_unicode.rb b/examples/examples_unicode.rb index 7779e64..045b666 100644 --- a/examples/examples_unicode.rb +++ b/examples/examples_unicode.rb @@ -1,7 +1,8 @@ $:.unshift File.dirname(__FILE__) + '/../lib' require 'terminal-table/import' -Terminal::Table::Style.defaults = { :border => Terminal::Table::UnicodeThickEdgeBorder.new() } +Terminal::Table::Style.defaults = { :border => :unicode_round } +# Terminal::Table::UnicodeThickEdgeBorder.new() puts puts table(['a', 'b'], [1, 2], [3, 4]) diff --git a/examples/issue100.rb b/examples/issue100.rb new file mode 100755 index 0000000..41527be --- /dev/null +++ b/examples/issue100.rb @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby + +# Methods to suppress left/right borders using border_left & border_right + +require_relative "../lib/terminal-table" +table = Terminal::Table.new do |t| + t.headings = ['id', 'name'] + t.rows = [[1, 'One'], [2, 'Two'], [3, 'Three']] + t.style = { :border_left => false, :border_top => false, :border_bottom => false } +end + +puts table +puts + +# no right +table.style = {:border_right => false } +puts table +puts + +# no right +table.style = {:border_left => true } +puts table +puts + +table.style.border = Terminal::Table::UnicodeBorder.new +puts table + + +table.style = {:border_right => false, :border_left => true } +puts table + +table.style = {:border_right => true, :border_left => false } +puts table + diff --git a/examples/issue111.rb b/examples/issue111.rb new file mode 100755 index 0000000..a7a6c71 --- /dev/null +++ b/examples/issue111.rb @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +require_relative "../lib/terminal-table" +puts Terminal::Table.new(headings: ['heading A', 'heading B'], rows: [['a', 'b'], ['a', 'b']], style: {border: Terminal::Table::MarkdownBorder.new}) + diff --git a/examples/issue95.rb b/examples/issue95.rb new file mode 100755 index 0000000..e6ddbf8 --- /dev/null +++ b/examples/issue95.rb @@ -0,0 +1,42 @@ +#!/usr/bin/env ruby +require 'colorize' +require_relative '../lib/terminal-table.rb' + +original_sample_data = [ +["Sep 2016", 33, [-38, -53.52], 46, [-25, -35.21]], +["Oct 2016", 35, [2, 6.06], 50, [4, 8.69]] +] + +table = Terminal::Table.new headings: ["Month".cyan,"Monthly IT".cyan,"IT Difference OPM".cyan, +"Monthly OOT".cyan,"OOT Difference OPM".cyan], rows: original_sample_data + +table.style = { padding_left: 2, padding_right: 2, border_x: "-".blue, border_y: "|".blue, border_i: "+".blue } + +puts table + +puts "" +puts "^ good table" +puts "v wonky table" +puts "" + +split_column_sample_data = [ +["Sep 2016", 33, -38, -53.52, 46, -25, -35.21], +["Oct 2016", 35, 2, 6.06, 50, 4, 8.69] +] + +table = Terminal::Table.new headings: ["Month".cyan,"Monthly IT".cyan, +{value: "IT Difference OPM".cyan, colspan: 2}, "Monthly OOT".cyan, +{value: "OOT Difference OPM".cyan, colspan: 2}], rows: split_column_sample_data + +table.style = { padding_left: 2, padding_right: 2, border_x: "-".blue, border_y: "|".blue, border_i: "+".blue } + +puts table + + +table = Terminal::Table.new headings: ["Month","Monthly IT", +{value: "IT Difference OPM", colspan: 2}, "Monthly OOT", +{value: "OOT Difference OPM", colspan: 2}], rows: split_column_sample_data + +table.style = { padding_left: 2, padding_right: 2, border_x: "-".blue, border_y: "|".cyan, border_i: "+" } + +puts table diff --git a/lib/terminal-table.rb b/lib/terminal-table.rb index c8238e1..ebea259 100644 --- a/lib/terminal-table.rb +++ b/lib/terminal-table.rb @@ -21,6 +21,6 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -%w(cell row separator style table table_helper version).each do |file| - require "terminal-table/#{file}" +%w(cell row separator style table table_helper util version).each do |file| + require_relative "./terminal-table/#{file}" end diff --git a/lib/terminal-table/cell.rb b/lib/terminal-table/cell.rb index 0a56a6a..59c5be5 100644 --- a/lib/terminal-table/cell.rb +++ b/lib/terminal-table/cell.rb @@ -57,7 +57,7 @@ def lines def render(line = 0) left = " " * @table.style.padding_left right = " " * @table.style.padding_right - display_width = Unicode::DisplayWidth.of(escape(lines[line])) + display_width = Unicode::DisplayWidth.of(Util::ansi_escape(lines[line])) render_width = lines[line].to_s.size - display_width + width align("#{left}#{lines[line]}#{right}", alignment, render_width + @table.cell_padding) end @@ -68,7 +68,7 @@ def render(line = 0) # removes all ANSI escape sequences (e.g. color) def value_for_column_width_recalc - lines.map{ |s| escape(s) }.max_by{ |s| Unicode::DisplayWidth.of(s) } + lines.map{ |s| Util::ansi_escape(s) }.max_by{ |s| Unicode::DisplayWidth.of(s) } end ## @@ -81,14 +81,6 @@ def width end inner_width + padding end - - ## - # removes all ANSI escape sequences (e.g. color) - def escape(line) - line.to_s.gsub(/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/, ''). - gsub(/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/, ''). - gsub(/(\x03|\x1a)/, '') - end end end end diff --git a/lib/terminal-table/style.rb b/lib/terminal-table/style.rb index 2cc1f00..3a2b8e7 100644 --- a/lib/terminal-table/style.rb +++ b/lib/terminal-table/style.rb @@ -5,7 +5,10 @@ module Terminal class Table class Border - attr_accessor :data + attr_accessor :data, :top, :bottom, :left, :right + def initialize + @top, @bottom, @left, @right = true, true, true, true + end def []=(key, val) @data[key] = val end @@ -23,6 +26,13 @@ def remove_verticals def remove_horizontals self.class.const_get("HORIZONTALS").each { |key| @data[key] = "" } end + + # If @left, return the edge else empty-string. + def maybeleft(key) ; @left ? @data[key] : '' ; end + + # If @right, return the edge else empty-string. + def mayberight(key) ; @right ? @data[key] : '' ; end + end class AsciiBorder < Border @@ -31,21 +41,29 @@ class AsciiBorder < Border INTERSECTIONS = %i[i] def initialize + super @data = { x: "-", y: "|", i: "+" } end # Get vertical border elements # @return [Array] 3-element list of [left, center, right] def vertical - y = @data[:y] - [y, y, y] # left, center, right + [maybeleft(:y), @data[:y], mayberight(:y)] # left, center, right end # Get horizontal border elements # @return [Array] a 6 element list of: [i-left, horizontal-bar, i-up/down, i-right, i-down, i-up] def horizontal(_type) x, i = @data[:x], @data[:i] - [i, x, i, i, i, i] + [maybeleft(:i), x, i, mayberight(:i), i, i] + end + end + + class MarkdownBorder < AsciiBorder + def initialize + super + @top, @bottom = false, false + @data = { x: "-", y: "|", i: "|" } end end @@ -57,6 +75,7 @@ class UnicodeBorder < Border w i e dn up sw s se su] def initialize + super @data = { nw: "┌", nx: "─", n: "┬", ne: "┐", yw: "│", y: "│", ye: "│", @@ -68,22 +87,25 @@ def initialize # Get vertical border elements # @return [Array] 3-element list of [left, center, right] def vertical - [@data[:yw], @data[:y], @data[:ye]] + [maybeleft(:yw), @data[:y], mayberight(:ye)] end # Get horizontal border elements # @return [Array] a 6 element list of: [i-left, horizontal-bar, i-up/down, i-right, i-down, i-up] def horizontal(type) - case type - when :below_heading - [@data[:hw], @data[:hx], @data[:hi], @data[:he], @data[:hd], @data[:hu] ] - when :top - [@data[:nw], @data[:nx], @data[:n], @data[:ne], @data[:n], nil ] - when :bot - [@data[:sw], @data[:sx], @data[:s], @data[:se], nil, @data[:s] || @data[:up] ] - else # center - [@data[:w], @data[:x], @data[:i], @data[:e], @data[:dn], @data[:up] ] - end + rval = case type + when :below_heading + [@data[:hw], @data[:hx], @data[:hi], @data[:he], @data[:hd], @data[:hu] ] + when :top + [@data[:nw], @data[:nx], @data[:n], @data[:ne], @data[:n], nil ] + when :bot + [@data[:sw], @data[:sx], @data[:s], @data[:se], nil, @data[:s] || @data[:up] ] + else # center + [@data[:w], @data[:x], @data[:i], @data[:e], @data[:dn], @data[:up] ] + end + rval[0] = '' unless @left + rval[3] = '' unless @right + rval end end @@ -98,6 +120,7 @@ def initialize # Unicode Border with thick outer edges class UnicodeThickEdgeBorder < UnicodeBorder def initialize + super @data = { nw: "┏", nx: "━", n: "┯", ne: "┓", nd: nil, yw: "┃", y: "│", ye: "┃", @@ -135,7 +158,6 @@ class Style @@defaults = { :border => AsciiBorder.new, - :border_top => true, :border_bottom => true, :padding_left => 1, :padding_right => 1, :margin_left => '', :width => nil, :alignment => nil, @@ -147,12 +169,35 @@ def border_x=(val) ; @border[:x] = val ; end def border_y=(val) ; @border[:y] = val ; end def border_i=(val) ; @border[:i] = val ; end def border_y ; @border[:y] ; end + def border_y_width ; Util::ansi_escape(@border[:y]).length ; end # Accessor for instance of Border - attr_accessor :border + attr_reader :border + def border=(val) + if val.is_a? Symbol + # convert symbol name like :foo_bar to get class FooBarBorder + klass_str = val.to_s.split('_').collect(&:capitalize).join + "Border" + begin + klass = Terminal::Table::const_get(klass_str) + @border = klass.new + rescue NameError + raise "Cannot lookup class Terminal::Table::#{klass_str} from symbol #{val.inspect}" + end + else + @border = val + end + end + + def border_top=(val) ; @border.top = val ; end + def border_bottom=(val) ; @border.bottom = val ; end + def border_left=(val) ; @border.left = val ; end + def border_right=(val) ; @border.right = val ; end + + def border_top ; @border.top ; end + def border_bottom ; @border.bottom ; end + def border_left ; @border.left ; end + def border_right ; @border.right ; end - attr_accessor :border_top - attr_accessor :border_bottom attr_accessor :padding_left attr_accessor :padding_right diff --git a/lib/terminal-table/table.rb b/lib/terminal-table/table.rb index 76c991f..22d1e9a 100644 --- a/lib/terminal-table/table.rb +++ b/lib/terminal-table/table.rb @@ -52,7 +52,7 @@ def add_separator end def cell_spacing - cell_padding + style.border_y.length + cell_padding + style.border_y_width end def cell_padding @@ -200,7 +200,7 @@ def == other private def columns_width - column_widths.inject(0) { |s, i| s + i + cell_spacing } + style.border_y.length + column_widths.inject(0) { |s, i| s + i + cell_spacing } + style.border_y_width end def recalc_column_widths @@ -319,7 +319,7 @@ def recalc_column_widths full_width = dp[n_cols][0].keys.first unless style.width.nil? - new_width = style.width - space_width - style.border_y.length + new_width = style.width - space_width - style.border_y_width if new_width < full_width raise "Table width exceeds wanted width " + "of #{style.width} characters." diff --git a/lib/terminal-table/util.rb b/lib/terminal-table/util.rb new file mode 100644 index 0000000..a584c3d --- /dev/null +++ b/lib/terminal-table/util.rb @@ -0,0 +1,13 @@ +module Terminal + class Table + module Util + # removes all ANSI escape sequences (e.g. color) + def ansi_escape(line) + line.to_s.gsub(/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/, ''). + gsub(/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/, ''). + gsub(/(\x03|\x1a)/, '') + end + module_function :ansi_escape + end + end +end diff --git a/spec/style_spec.rb b/spec/style_spec.rb new file mode 100644 index 0000000..2e3005f --- /dev/null +++ b/spec/style_spec.rb @@ -0,0 +1,29 @@ +# encoding: utf-8 +require 'spec_helper' + +module Terminal + describe Table::Style do + + before :each do + @table = Table.new + end + + border_map = { + :markdown => Terminal::Table::MarkdownBorder, + :ascii => Terminal::Table::AsciiBorder, + :unicode => Terminal::Table::UnicodeBorder, + :unicode_thick_edge => Terminal::Table::UnicodeThickEdgeBorder, + :unicode_round => Terminal::Table::UnicodeRoundBorder, + } + + border_map.each do |name_sym, klass| + it "should enumerate a #{name_sym} table" do + @table.style.border = name_sym + @table.style.border.should be_a klass + @table.style.border = klass.new + @table.style.border.should be_a klass + end + end + + end +end diff --git a/spec/table_spec.rb b/spec/table_spec.rb index 8b99cf3..7467863 100755 --- a/spec/table_spec.rb +++ b/spec/table_spec.rb @@ -667,5 +667,34 @@ module TerminalTable | c | 3 | EOF end + + it "should allow to not generate left and right border" do + @table.style = { :border_left => false, :border_right => false } + @table.headings = ['name', 'value'] + @table.rows = [['a', 1], ['b', 2], ['c', 3]] + @table.render.should eq <<-EOF.chomp +------+------- + name | value +------+------- + a | 1 + b | 2 + c | 3 +------+------- + EOF + end + + it "create markdown compatible tables" do + @table.style.border = :markdown # Terminal::Table::MarkdownBorder.new + @table.headings = ['name', 'value'] + @table.rows = [['a', 1], ['b', 2], ['c', 3]] + @table.render.should eq <<-EOF.deindent +| name | value | +|------|-------| +| a | 1 | +| b | 2 | +| c | 3 | + EOF + end + end end diff --git a/spec/unicode_table_spec.rb b/spec/unicode_table_spec.rb index 8006751..f8b8d42 100755 --- a/spec/unicode_table_spec.rb +++ b/spec/unicode_table_spec.rb @@ -138,7 +138,7 @@ module Terminal @table.style.border[:ne] = "2" @table.style.border[:sw] = "3" @table.style.border[:se] = "4" - @table.style.border[:up] = "^" + @table.style.border[:s] = "^" @table.style.border[:hw] = "5" @table.style.border[:he] = "6" From 35a7d6f89ae6ac6cf989234ea7932e3e59372f43 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Fri, 22 Jan 2021 18:40:24 -0500 Subject: [PATCH 20/38] updated based on @nateberkopec PR comments, updated History.rdoc --- History.rdoc | 36 +++++++++++++++++++++++++++++++++ lib/terminal-table/separator.rb | 10 ++++----- lib/terminal-table/style.rb | 2 +- lib/terminal-table/table.rb | 9 +-------- 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/History.rdoc b/History.rdoc index 796cc25..4206c0d 100644 --- a/History.rdoc +++ b/History.rdoc @@ -1,3 +1,39 @@ +x.x.x / 2020-01-22 + +- Support for (optional) Unicode border styles on tables. In order to support decent looking Unicode borders, different types of intersections get different types of intersection characters. This has the side effect of subtle formatting differences even for the ASCII table border case due to removal of certain intersections near colspans. + +For example, previously the output of a table may be: + +------+-----+ + | Title | + +------+-----+ + | Char | Num | + +------+-----+ + | a | 1 | + | b | 2 | + | c | 3 | + +------+-----+ + +And now the `+` character above the word Title is removed, as it is no longer considered an intersection: + + +------------+ + | Title | + +------+-----+ + | Char | Num | + +------+-----+ + | a | 1 | + | b | 2 | + +------+-----+ + +- The default border remains an ASCII border for backwards compatibility, however multiple border classes are included / documented, and user defined border types can be applied as needed. + +In support of this update, the following issues were addressed: +- colspan creates conflict with colorize (#95) +- Use nice UTF box-drawing characters by default (#99) + - Note that `AsciiBorder` is stll the default +- Border-left and border-right style (#100) +- Helper function to style as Markdown (#111) + - Achieved using `MarkdownBorder` + 2.0.0 / 2020-10-28 ================== diff --git a/lib/terminal-table/separator.rb b/lib/terminal-table/separator.rb index f814054..b283ff7 100644 --- a/lib/terminal-table/separator.rb +++ b/lib/terminal-table/separator.rb @@ -11,7 +11,7 @@ def row_type return :top if @prevrow.nil? return :bot if @nextrow.nil? return :below_heading if @prevrow.is_a?(HeadingRow) - return :mid + :mid end def render @@ -44,12 +44,12 @@ def render end rval << right_edge - return rval.join + rval.join end - # save off neighboring rows. - # TODO: rename this function, name makes no sense. - def elab(prevrow, nextrow) + # Save off neighboring rows, so that we can use them later in determining + # which types of table edges to use. + def save_adjacent_rows(prevrow, nextrow) @prevrow = prevrow @nextrow = nextrow end diff --git a/lib/terminal-table/style.rb b/lib/terminal-table/style.rb index 3a2b8e7..e3f8bd7 100644 --- a/lib/terminal-table/style.rb +++ b/lib/terminal-table/style.rb @@ -226,7 +226,7 @@ def defaults # border is an object that needs to be duplicated on instantiation, # otherwise everything will be referencing the same object-id. klass_defaults[:border] = klass_defaults[:border].dup - return klass_defaults + klass_defaults end def defaults= options diff --git a/lib/terminal-table/table.rb b/lib/terminal-table/table.rb index 22d1e9a..98a0df0 100644 --- a/lib/terminal-table/table.rb +++ b/lib/terminal-table/table.rb @@ -139,25 +139,18 @@ def render buffer << row buffer << Separator.new(self) end - #buffer += @rows.product([Separator.new(self)]).flatten else buffer += @rows buffer << Separator.new(self) if style.border_bottom end buffer.each_with_index do |r,idx| - #p [:zzz, r.class] if r.is_a?(Separator) prevrow = idx>0 ? buffer[idx-1] : nil nextrow = buffer.fetch(idx+1, nil) - r.elab(prevrow, nextrow) + r.save_adjacent_rows(prevrow, nextrow) end end - #buffer.each do |r| - # p(rr: r.edge) if r.is_a?(Separator) - #end - - buffer.map { |r| style.margin_left + r.render.rstrip }.join("\n") end alias :to_s :render From a451abcbfaba4ea460efeec8da994a764ee654db Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Sun, 24 Jan 2021 08:31:31 -0500 Subject: [PATCH 21/38] up-revving version to 3.0.0 --- Gemfile.lock | 2 +- History.rdoc | 2 +- lib/terminal-table/version.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index be8aff9..593f378 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - terminal-table (2.0.0) + terminal-table (3.0.0) unicode-display_width (~> 1.1, >= 1.1.1) GEM diff --git a/History.rdoc b/History.rdoc index 4206c0d..b4838ac 100644 --- a/History.rdoc +++ b/History.rdoc @@ -1,4 +1,4 @@ -x.x.x / 2020-01-22 +3.0.0 / 2020-01-22 - Support for (optional) Unicode border styles on tables. In order to support decent looking Unicode borders, different types of intersections get different types of intersection characters. This has the side effect of subtle formatting differences even for the ASCII table border case due to removal of certain intersections near colspans. diff --git a/lib/terminal-table/version.rb b/lib/terminal-table/version.rb index f070a83..44c1ba7 100644 --- a/lib/terminal-table/version.rb +++ b/lib/terminal-table/version.rb @@ -1,5 +1,5 @@ module TerminalTable class Table - VERSION = '2.0.0' + VERSION = '3.0.0' end end From 12ace180218b69e31fcd22fef24a7e4829351aad Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Sun, 24 Jan 2021 18:55:56 -0500 Subject: [PATCH 22/38] A re-think of how to handle separators in order to support both header and footer or other emphasized (strong) separators. Removed class inheritance and logic for previous/next row and replaced with adding a non-default border_type into the table elaboration logic. Also split the table elaboration (of implicit separators) from the rendering portion so that it can be intercepted in the case where a user would want to modify the elaborated separators. --- examples/examples.rb | 0 examples/examples_unicode.rb | 2 ++ examples/strong_separator.rb | 23 ++++++++++++++ lib/terminal-table/row.rb | 4 +-- lib/terminal-table/separator.rb | 13 +++----- lib/terminal-table/style.rb | 9 ++++-- lib/terminal-table/table.rb | 53 +++++++++++++++++++++++---------- spec/unicode_table_spec.rb | 7 ++--- 8 files changed, 76 insertions(+), 35 deletions(-) mode change 100644 => 100755 examples/examples.rb mode change 100644 => 100755 examples/examples_unicode.rb create mode 100755 examples/strong_separator.rb diff --git a/examples/examples.rb b/examples/examples.rb old mode 100644 new mode 100755 diff --git a/examples/examples_unicode.rb b/examples/examples_unicode.rb old mode 100644 new mode 100755 index 045b666..ad8455f --- a/examples/examples_unicode.rb +++ b/examples/examples_unicode.rb @@ -1,3 +1,5 @@ +#!/bin/env ruby + $:.unshift File.dirname(__FILE__) + '/../lib' require 'terminal-table/import' diff --git a/examples/strong_separator.rb b/examples/strong_separator.rb new file mode 100755 index 0000000..52ad195 --- /dev/null +++ b/examples/strong_separator.rb @@ -0,0 +1,23 @@ +#!/usr/bin/env ruby +require_relative "../lib/terminal-table" + +# +# An example of how to manually add separators with non-default +# border_type to enable a footer row. +# +table = Terminal::Table.new do |t| + # set the style + t.style = { border: :unicode_thick_edge } + + # header row + t.headings = ['fruit', 'count'] + + # some row data + t.add_row ['apples', 7] + t.add_row ['bananas', 19] + t.add_separator border_type: :strong + # footer row + t.add_row ['total', 26] +end + +puts table.render diff --git a/lib/terminal-table/row.rb b/lib/terminal-table/row.rb index 2cf630e..bb645a8 100644 --- a/lib/terminal-table/row.rb +++ b/lib/terminal-table/row.rb @@ -61,8 +61,6 @@ def crossings end end - # Yay, inheritance. - class HeadingRow < Row ; end - class TitleRow < Row ; end + end end diff --git a/lib/terminal-table/separator.rb b/lib/terminal-table/separator.rb index b283ff7..22ed579 100644 --- a/lib/terminal-table/separator.rb +++ b/lib/terminal-table/separator.rb @@ -2,21 +2,16 @@ module TerminalTable class Table class Separator < Row - def initialize(*args) + def initialize(*args, border_type: :mid) super @prevrow, @nextrow = nil, nil # references to adjacent rows. + @border_type = border_type end - def row_type - return :top if @prevrow.nil? - return :bot if @nextrow.nil? - return :below_heading if @prevrow.is_a?(HeadingRow) - :mid - end + attr_accessor :border_type def render - left_edge, ctrflat, ctrud, right_edge, ctrdn, ctrup = @table.style.horizontal(row_type) - #p [row_type, left_edge, ctrflat, ctrud, right_edge, ctrdn, ctrup, @prevrow.class, @nextrow.class] + left_edge, ctrflat, ctrud, right_edge, ctrdn, ctrup = @table.style.horizontal(border_type) prev_crossings = @prevrow.respond_to?(:crossings) ? @prevrow.crossings : [] next_crossings = @nextrow.respond_to?(:crossings) ? @nextrow.crossings : [] diff --git a/lib/terminal-table/style.rb b/lib/terminal-table/style.rb index e3f8bd7..9f305b3 100644 --- a/lib/terminal-table/style.rb +++ b/lib/terminal-table/style.rb @@ -5,6 +5,7 @@ module Terminal class Table class Border + attr_accessor :data, :top, :bottom, :left, :right def initialize @top, @bottom, @left, @right = true, true, true, true @@ -68,6 +69,9 @@ def initialize end class UnicodeBorder < Border + + ALLOWED_SEPARATOR_BORDER_STYLES = %i[top bot mid strong] + HORIZONTALS = %i[x sx hx nx] VERTICALS = %i[y yw ye] INTERSECTIONS = %i[nw n ne nd @@ -93,14 +97,15 @@ def vertical # Get horizontal border elements # @return [Array] a 6 element list of: [i-left, horizontal-bar, i-up/down, i-right, i-down, i-up] def horizontal(type) + raise ArgumentError, "Border type must be one of #{ALLOWED_SEPARATOR_BORDER_STYLES.inspect}, was #{type.inspect}" unless ALLOWED_SEPARATOR_BORDER_STYLES.include?(type) rval = case type - when :below_heading + when :strong # (typically used for the separator below the heading row or above a footer row) [@data[:hw], @data[:hx], @data[:hi], @data[:he], @data[:hd], @data[:hu] ] when :top [@data[:nw], @data[:nx], @data[:n], @data[:ne], @data[:n], nil ] when :bot [@data[:sw], @data[:sx], @data[:s], @data[:se], nil, @data[:s] || @data[:up] ] - else # center + else # :mid (center, unbolded) [@data[:w], @data[:x], @data[:i], @data[:e], @data[:dn], @data[:up] ] end rval[0] = '' unless @left diff --git a/lib/terminal-table/table.rb b/lib/terminal-table/table.rb index 98a0df0..9892523 100644 --- a/lib/terminal-table/table.rb +++ b/lib/terminal-table/table.rb @@ -10,6 +10,9 @@ class Table # Generates a ASCII table with the given _options_. def initialize options = {}, &block + # elaborated/elab_rows should be set just before rendering the table + @elaborated = false + @elab_rows = [] @headings = [] @rows = [] @column_widths = [] @@ -47,8 +50,8 @@ def add_row array ## # Add a separator. - def add_separator - self << :separator + def add_separator(border_type: :mid) + @rows << Separator.new(self, border_type: border_type) end def cell_spacing @@ -112,46 +115,64 @@ def number_of_columns def headings= arrays arrays = [arrays] unless arrays.first.is_a?(Array) @headings = arrays.map do |array| - row = HeadingRow.new(self, array) + row = Row.new(self, array) require_column_widths_recalc row end end ## - # Render the table. + # Elaborate rows to form an Array of Rows and Separators with adjacency properties added. + # + # This is separated from the String rendering so that certain features may be tweaked + # before the String is built. - def render + def elaborate_rows - buffer = style.border_top ? [Separator.new(self)] : [] + buffer = style.border_top ? [Separator.new(self, border_type: :top)] : [] unless @title.nil? - buffer << TitleRow.new(self, [title_cell_options]) + buffer << Row.new(self, [title_cell_options]) buffer << Separator.new(self) end @headings.each do |row| unless row.cells.empty? buffer << row - buffer << Separator.new(self) + buffer << Separator.new(self, border_type: :strong) end end if style.all_separators - @rows.each do |row| + @rows.each_with_index do |row, idx| + # last separator is bottom, others are :mid + border_type = (idx == @rows.size - 1) ? :bot : :mid buffer << row - buffer << Separator.new(self) + buffer << Separator.new(self, border_type: border_type) end else buffer += @rows - buffer << Separator.new(self) if style.border_bottom + buffer << Separator.new(self, border_type: :bot) if style.border_bottom end - buffer.each_with_index do |r,idx| + + # After all implicit Separators are inserted we need to save off the + # adjacent rows so that we can decide what type of intersections to use + # based on column spans in the adjacent row(s). + buffer.each_with_index do |r, idx| if r.is_a?(Separator) - prevrow = idx>0 ? buffer[idx-1] : nil - nextrow = buffer.fetch(idx+1, nil) - r.save_adjacent_rows(prevrow, nextrow) + prev_row = idx > 0 ? buffer[idx - 1] : nil + next_row = buffer.fetch(idx + 1, nil) + r.save_adjacent_rows(prev_row, next_row) end end - buffer.map { |r| style.margin_left + r.render.rstrip }.join("\n") + @elaborated = true + @elab_rows = buffer + end + + ## + # Render the table. + + def render + elaborate_rows unless @elaborated + @elab_rows.map { |r| style.margin_left + r.render.rstrip }.join("\n") end alias :to_s :render diff --git a/spec/unicode_table_spec.rb b/spec/unicode_table_spec.rb index f8b8d42..d948f59 100755 --- a/spec/unicode_table_spec.rb +++ b/spec/unicode_table_spec.rb @@ -89,11 +89,8 @@ module Terminal @table.headings = ['Char', 'Num'] @table << ['a', 1] separator = Terminal::Table::Separator.new(@table) - ##ascii## separator.render.should eq '+------------+' - # Ambiguous what this should be... - # Technically it is a both a 'top' and a 'bottom', however the logic - # for top has priority. :/ - separator.render.should eq '┌────────────┐' + # For UnicodeBorder this now defaults to a :mid border_type + separator.render.should eq '├────────────┤' end it "should add separator" do From 624f5f48ac464d4f147cef91e66a1c396bf56273 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Mon, 25 Jan 2021 09:06:05 -0500 Subject: [PATCH 23/38] Adding tests for adding border_type on separators and post-elaboration tweaking of the Separator border_type. --- lib/terminal-table/separator.rb | 16 +++++++++-- lib/terminal-table/table.rb | 18 ++++++------- spec/unicode_table_spec.rb | 48 +++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 12 deletions(-) diff --git a/lib/terminal-table/separator.rb b/lib/terminal-table/separator.rb index 22ed579..17c342a 100644 --- a/lib/terminal-table/separator.rb +++ b/lib/terminal-table/separator.rb @@ -2,13 +2,25 @@ module TerminalTable class Table class Separator < Row - def initialize(*args, border_type: :mid) + ## + # `prevrow`, `nextrow` contain references to adjacent rows. + # + # `border_type` is a symbol used to control which type of border is used + # on the separator (:top for top-edge, :bot for bottom-edge, + # :mid for interior, and :strong for emphasized-interior) + # + # `implicit` is false for user-added separators, and true for + # implicit/auto-generated separators. + + def initialize(*args, border_type: :mid, implicit: false) super - @prevrow, @nextrow = nil, nil # references to adjacent rows. + @prevrow, @nextrow = nil, nil @border_type = border_type + @implicit = implicit end attr_accessor :border_type + attr_reader :implicit def render left_edge, ctrflat, ctrud, right_edge, ctrdn, ctrup = @table.style.horizontal(border_type) diff --git a/lib/terminal-table/table.rb b/lib/terminal-table/table.rb index 9892523..7dadc9e 100644 --- a/lib/terminal-table/table.rb +++ b/lib/terminal-table/table.rb @@ -7,12 +7,10 @@ class Table attr_reader :headings ## - # Generates a ASCII table with the given _options_. + # Generates a ASCII/Unicode table with the given _options_. def initialize options = {}, &block - # elaborated/elab_rows should be set just before rendering the table @elaborated = false - @elab_rows = [] @headings = [] @rows = [] @column_widths = [] @@ -129,15 +127,15 @@ def headings= arrays def elaborate_rows - buffer = style.border_top ? [Separator.new(self, border_type: :top)] : [] + buffer = style.border_top ? [Separator.new(self, border_type: :top, implicit: true)] : [] unless @title.nil? buffer << Row.new(self, [title_cell_options]) - buffer << Separator.new(self) + buffer << Separator.new(self, implicit: true) end @headings.each do |row| unless row.cells.empty? buffer << row - buffer << Separator.new(self, border_type: :strong) + buffer << Separator.new(self, border_type: :strong, implicit: true) end end if style.all_separators @@ -145,11 +143,11 @@ def elaborate_rows # last separator is bottom, others are :mid border_type = (idx == @rows.size - 1) ? :bot : :mid buffer << row - buffer << Separator.new(self, border_type: border_type) + buffer << Separator.new(self, border_type: border_type, implicit: true) end else buffer += @rows - buffer << Separator.new(self, border_type: :bot) if style.border_bottom + buffer << Separator.new(self, border_type: :bot, implicit: true) if style.border_bottom end # After all implicit Separators are inserted we need to save off the @@ -164,7 +162,7 @@ def elaborate_rows end @elaborated = true - @elab_rows = buffer + @rows = buffer end ## @@ -172,7 +170,7 @@ def elaborate_rows def render elaborate_rows unless @elaborated - @elab_rows.map { |r| style.margin_left + r.render.rstrip }.join("\n") + @rows.map { |r| style.margin_left + r.render.rstrip }.join("\n") end alias :to_s :render diff --git a/spec/unicode_table_spec.rb b/spec/unicode_table_spec.rb index d948f59..02a5f84 100755 --- a/spec/unicode_table_spec.rb +++ b/spec/unicode_table_spec.rb @@ -681,5 +681,53 @@ module Terminal │ c │ 3 │ EOF end + + it "should be able to make a footer row" do + @table.headings = ['name', 'value'] + @table.rows = [['a', 1], ['b', 2], ['c', 3]] + @table.add_separator(border_type: :strong) + @table.add_row ['tot', 6] + @table.render.should eq <<-EOF.deindent + ┌──────┬───────┐ + │ name │ value │ + ╞══════╪═══════╡ + │ a │ 1 │ + │ b │ 2 │ + │ c │ 3 │ + ╞══════╪═══════╡ + │ tot │ 6 │ + └──────┴───────┘ + EOF + end + + it "should be able to change separators after elaboration" do + @table.style = { all_separators: true } + @table.title = 'Animals' + @table.headings = ['name', 'value'] + @table.rows = [['rat', 1], ['cat', 2], ['dog', 3]] + @table.add_row ['totl', 6] + # Elaborate here, and we will modify the border-types + # post elaboration. + rows = @table.elaborate_rows + rows[2].border_type = :strong # emphasize below title + rows[4].border_type = :mid # de-emphasize below header + rows[-3].border_type = :strong # emphasize above footer + @table.render.should eq <<-EOF.deindent + ┌──────────────┐ + │ Animals │ + ╞══════╤═══════╡ + │ name │ value │ + ├──────┼───────┤ + │ rat │ 1 │ + ├──────┼───────┤ + │ cat │ 2 │ + ├──────┼───────┤ + │ dog │ 3 │ + ╞══════╪═══════╡ + │ totl │ 6 │ + └──────┴───────┘ + EOF + end + end end From a451d0dd345ef7e297bfbb6838e935e09912a77d Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Tue, 26 Jan 2021 09:46:59 -0500 Subject: [PATCH 24/38] new and improved methods for border separation including dash/dot3/dot4 support --- README.md | 40 +++++++++++++- lib/terminal-table/separator.rb | 4 +- lib/terminal-table/style.rb | 68 ++++++++++++++++------- lib/terminal-table/table.rb | 8 +-- spec/unicode_table_spec.rb | 96 +++++++++++++++++++++++++++++---- 5 files changed, 180 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index c8137ad..b584328 100644 --- a/README.md +++ b/README.md @@ -324,16 +324,19 @@ Inside the `UnicodeBorder` class, there are definitions for a variety of corner/ ``` @data = { +nil => nil, nw: "┌", nx: "─", n: "┬", ne: "┐", yw: "│", y: "│", ye: "│", -hw: "╞", hx: "═", hi: "╪", he: "╡", hd: '╤', hu: "╧", +aw: "╞", ax: "═", ai: "╪", ae: "╡", ad: '╤', au: "╧", +bw: "┝", bx: "━", bi: "┿", be: "┥", bd: '┯', bu: "┷", w: "├", x: "─", i: "┼", e: "┤", dn: "┬", up: "┴", sw: "└", sx: "─", s: "┴", se: "┘", } ``` Note that many are defined as directional (:nw == north-west), others defined in terms of 'x' or 'y'. -The border that separates headings (below each heading) are defined with 'h*' entries. +The border that separates headings (below each heading) is of type `:double` and is defined with 'a*' entries. +Alternate `:heavy` types that can be applied to separators can be defined with 'b*' entries. When defining a new set of borders, it's probably easiest to define a new class that inherits from UnicodeBorder and replaces the @data Hash. However, these corners can be these can be overridden with: @@ -343,6 +346,39 @@ table.style = {border: :unicode} table.style.border[:nw] = '*' # override the north-west corner of the table ``` +### Customizing row separators + +Row-separators can now be customized in a variety of ways. The default separator's border_type is referred to as `:mid`. Additional `:strong` / `:strong_a` and `:strong_b` separator styles can be applied to separate sections (e.g. header/footer/title). + +The separator border_type may be specified when a user-defined separator added. Alternatively, borders may be adjusted after the table's rows are elaborated, but before the table is rendered. + +Separator `border_type`s can be adjusted to be heavy, use double-lines, and different dash/dot styles. The border type should be one of: + + div dash dot3 dot4 + thick thick_dash thick_dot3 thick_dot4 + heavy heavy_dash heavy_dot3 heavy_dot4 + bold bold_dash bold_dot3 bold_dot4 + double + + +To manually set the separator border_type, the `add_separator` method may be called. +```ruby +add_separator(border_type: :heavy_dash) +``` + +Alternatively, if `style: :all_separators` is used, it may be necessary to elaborate the Rows prior to rendering. +```ruby +table = Terminal::Table.new do |t| + t.add_row [1, 'One'] + t.add_row [2, 'Two'] + t.add_row [3, 'Three'] + t.style = {:all_separators => true} +end +rows = table.elaborate_rows +rows[2].border_type = :heavy # modify separator row: emphasize below title +puts table.render +``` + ## More examples For more examples, please see the `examples` directory included in the diff --git a/lib/terminal-table/separator.rb b/lib/terminal-table/separator.rb index 17c342a..90c34fd 100644 --- a/lib/terminal-table/separator.rb +++ b/lib/terminal-table/separator.rb @@ -7,12 +7,12 @@ class Separator < Row # # `border_type` is a symbol used to control which type of border is used # on the separator (:top for top-edge, :bot for bottom-edge, - # :mid for interior, and :strong for emphasized-interior) + # :div for interior, and :strong for emphasized-interior) # # `implicit` is false for user-added separators, and true for # implicit/auto-generated separators. - def initialize(*args, border_type: :mid, implicit: false) + def initialize(*args, border_type: :div, implicit: false) super @prevrow, @nextrow = nil, nil @border_type = border_type diff --git a/lib/terminal-table/style.rb b/lib/terminal-table/style.rb index 9f305b3..d65d3ed 100644 --- a/lib/terminal-table/style.rb +++ b/lib/terminal-table/style.rb @@ -70,22 +70,35 @@ def initialize class UnicodeBorder < Border - ALLOWED_SEPARATOR_BORDER_STYLES = %i[top bot mid strong] - - HORIZONTALS = %i[x sx hx nx] + ALLOWED_SEPARATOR_BORDER_STYLES = %i[ + top bot + div dash dot3 dot4 + thick thick_dash thick_dot3 thick_dot4 + heavy heavy_dash heavy_dot3 heavy_dot4 + bold bold_dash bold_dot3 bold_dot4 + double + ] + + HORIZONTALS = %i[x sx ax bx nx bx_dot3 bx_dot4 bx_dash x_dot3 x_dot4 x_dash] VERTICALS = %i[y yw ye] INTERSECTIONS = %i[nw n ne nd - hw hi he hd hu + aw ai ae ad au + bw bi be bd bu w i e dn up sw s se su] def initialize super @data = { + nil => nil, nw: "┌", nx: "─", n: "┬", ne: "┐", yw: "│", y: "│", ye: "│", - hw: "╞", hx: "═", hi: "╪", he: "╡", hd: '╤', hu: "╧", - w: "├", x: "─", i: "┼", e: "┤", dn: "┬", up: "┴", + aw: "╞", ax: "═", ai: "╪", ae: "╡", ad: '╤', au: "╧", # double + bw: "┝", bx: "━", bi: "┿", be: "┥", bd: '┯', bu: "┷", # heavy/bold/thick + w: "├", x: "─", i: "┼", e: "┤", dn: "┬", up: "┴", # normal div sw: "└", sx: "─", s: "┴", se: "┘", + # alternative dots/dashes + x_dot4: '┈', x_dot3: '┄', x_dash: '╌', + bx_dot4: '┉', bx_dot3: '┅', bx_dash: '╍', } end # Get vertical border elements @@ -97,17 +110,29 @@ def vertical # Get horizontal border elements # @return [Array] a 6 element list of: [i-left, horizontal-bar, i-up/down, i-right, i-down, i-up] def horizontal(type) - raise ArgumentError, "Border type must be one of #{ALLOWED_SEPARATOR_BORDER_STYLES.inspect}, was #{type.inspect}" unless ALLOWED_SEPARATOR_BORDER_STYLES.include?(type) - rval = case type - when :strong # (typically used for the separator below the heading row or above a footer row) - [@data[:hw], @data[:hx], @data[:hi], @data[:he], @data[:hd], @data[:hu] ] - when :top - [@data[:nw], @data[:nx], @data[:n], @data[:ne], @data[:n], nil ] - when :bot - [@data[:sw], @data[:sx], @data[:s], @data[:se], nil, @data[:s] || @data[:up] ] - else # :mid (center, unbolded) - [@data[:w], @data[:x], @data[:i], @data[:e], @data[:dn], @data[:up] ] - end + raise ArgumentError, "Border type is #{type.inspect}, must be one of #{ALLOWED_SEPARATOR_BORDER_STYLES.inspect}" unless ALLOWED_SEPARATOR_BORDER_STYLES.include?(type) + lookup = case type + when :top + [:nw, :nx, :n, :ne, :n, nil] + when :bot + [:sw, :sx, :s, :se, nil, :s] + when :double + # typically used for the separator below the heading row or above a footer row) + [:aw, :ax, :ai, :ae, :ad, :au] + when :thick, :thick_dash, :thick_dot3, :thick_dot4, + :heavy, :heavy_dash, :heavy_dot3, :heavy_dot4, + :bold, :bold_dash, :bold_dot3, :bold_dot4 + # alternate thick/bold border + xref = type.to_s.sub(/^(thick|heavy|bold)/,'bx').to_sym + [:bw, xref, :bi, :be, :bd, :bu] + when :dash, :dot3, :dot4 + # alternate thin dividers + xref = "x_#{type}".to_sym + [:w, xref, :i, :e, :dn, :up] + else # :div (center, non-emphasized) + [:w, :x, :i, :e, :dn, :up] + end + rval = lookup.map { |key| @data.fetch(key) } rval[0] = '' unless @left rval[3] = '' unless @right rval @@ -127,11 +152,16 @@ class UnicodeThickEdgeBorder < UnicodeBorder def initialize super @data = { + nil => nil, nw: "┏", nx: "━", n: "┯", ne: "┓", nd: nil, yw: "┃", y: "│", ye: "┃", - hw: "┣", hx: "═", hi: "╪", he: "┫", hd: '╤', hu: "╧", - w: "┠", x: "─", i: "┼", e: "┨", dn: "┬", up: "┴", + aw: "┣", ax: "═", ai: "╪", ae: "┫", ad: '╤', au: "╧", # double + bw: "┣", bx: "━", bi: "┿", be: "┫", bd: '┯', bu: "┷", # heavy/bold/thick + w: "┠", x: "─", i: "┼", e: "┨", dn: "┬", up: "┴", # normal div sw: "┗", sx: "━", s: "┷", se: "┛", su: nil, + # alternative dots/dashes + x_dot4: '┈', x_dot3: '┄', x_dash: '╌', + bx_dot4: '┉', bx_dot3: '┅', bx_dash: '╍', } end end diff --git a/lib/terminal-table/table.rb b/lib/terminal-table/table.rb index 7dadc9e..70cad6d 100644 --- a/lib/terminal-table/table.rb +++ b/lib/terminal-table/table.rb @@ -48,7 +48,7 @@ def add_row array ## # Add a separator. - def add_separator(border_type: :mid) + def add_separator(border_type: :div) @rows << Separator.new(self, border_type: border_type) end @@ -135,13 +135,13 @@ def elaborate_rows @headings.each do |row| unless row.cells.empty? buffer << row - buffer << Separator.new(self, border_type: :strong, implicit: true) + buffer << Separator.new(self, border_type: :double, implicit: true) end end if style.all_separators @rows.each_with_index do |row, idx| - # last separator is bottom, others are :mid - border_type = (idx == @rows.size - 1) ? :bot : :mid + # last separator is bottom, others are :div + border_type = (idx == @rows.size - 1) ? :bot : :div buffer << row buffer << Separator.new(self, border_type: border_type, implicit: true) end diff --git a/spec/unicode_table_spec.rb b/spec/unicode_table_spec.rb index 02a5f84..cf517ec 100755 --- a/spec/unicode_table_spec.rb +++ b/spec/unicode_table_spec.rb @@ -89,7 +89,7 @@ module Terminal @table.headings = ['Char', 'Num'] @table << ['a', 1] separator = Terminal::Table::Separator.new(@table) - # For UnicodeBorder this now defaults to a :mid border_type + # For UnicodeBorder this now defaults to a :div border_type separator.render.should eq '├────────────┤' end @@ -129,15 +129,15 @@ module Terminal :padding_left => 0, :padding_right => 2, :margin_left => 'y' * 2 } - @table.style.border[:hi] = "x" + @table.style.border[:ai] = "x" @table.style.border[:y] = ":" @table.style.border[:nw] = "1" @table.style.border[:ne] = "2" @table.style.border[:sw] = "3" @table.style.border[:se] = "4" @table.style.border[:s] = "^" - @table.style.border[:hw] = "5" - @table.style.border[:he] = "6" + @table.style.border[:aw] = "5" + @table.style.border[:ae] = "6" @table << ['a', 1] @table << ['b', 2] @@ -685,7 +685,7 @@ module Terminal it "should be able to make a footer row" do @table.headings = ['name', 'value'] @table.rows = [['a', 1], ['b', 2], ['c', 3]] - @table.add_separator(border_type: :strong) + @table.add_separator(border_type: :double) @table.add_row ['tot', 6] @table.render.should eq <<-EOF.deindent ┌──────┬───────┐ @@ -709,13 +709,13 @@ module Terminal # Elaborate here, and we will modify the border-types # post elaboration. rows = @table.elaborate_rows - rows[2].border_type = :strong # emphasize below title - rows[4].border_type = :mid # de-emphasize below header - rows[-3].border_type = :strong # emphasize above footer + rows[2].border_type = :heavy # emphasize below title + rows[4].border_type = :div # de-emphasize below header + rows[-3].border_type = :double # emphasize above footer @table.render.should eq <<-EOF.deindent ┌──────────────┐ │ Animals │ - ╞══════╤═══════╡ + ┝━━━━━━┯━━━━━━━┥ │ name │ value │ ├──────┼───────┤ │ rat │ 1 │ @@ -729,5 +729,83 @@ module Terminal EOF end + it "should be able manually specify separator borders" do + @table.style = { border: :unicode_round } + @table.title = 'Animals' + @table.headings = ['name', 'value'] + @table.add_row ['rat', 1] + @table.add_separator + @table.add_row ['cat', 2] + @table.add_separator(border_type: :bold) + @table.add_row ['dog', 3] + @table.add_separator(border_type: :double) + @table.add_row ['totl', 6] + @table.render.should eq <<-EOF.deindent + ╭──────────────╮ + │ Animals │ + ├──────┬───────┤ + │ name │ value │ + ╞══════╪═══════╡ + │ rat │ 1 │ + ├──────┼───────┤ + │ cat │ 2 │ + ┝━━━━━━┿━━━━━━━┥ + │ dog │ 3 │ + ╞══════╪═══════╡ + │ totl │ 6 │ + ╰──────┴───────╯ + EOF + end + + it "should test many separator borders" do + @table.style = { border: :unicode_thick_edge } + @table.title = 'Borders' + @table.headings = ['name', 'value'] + @table.add_row ['1st', 1] + @table.add_separator(border_type: :heavy) + @table.add_separator(border_type: :heavy_dash) + @table.add_separator(border_type: :heavy_dot3) + @table.add_separator(border_type: :heavy_dot4) + @table.add_separator(border_type: :thick) + @table.add_separator(border_type: :thick_dash) + @table.add_separator(border_type: :thick_dot3) + @table.add_separator(border_type: :thick_dot4) + @table.add_separator(border_type: :bold) + @table.add_separator(border_type: :bold_dash) + @table.add_separator(border_type: :bold_dot3) + @table.add_separator(border_type: :bold_dot4) + @table.add_separator(border_type: :dash) + @table.add_separator(border_type: :dot3) + @table.add_separator(border_type: :dot4) + @table.add_separator(border_type: :double) + @table.add_row ['last', 'N'] + @table.render.should eq <<-EOF.deindent + ┏━━━━━━━━━━━━━━┓ + ┃ Borders ┃ + ┠──────┬───────┨ + ┃ name │ value ┃ + ┣══════╪═══════┫ + ┃ 1st │ 1 ┃ + ┣━━━━━━┿━━━━━━━┫ + ┣╍╍╍╍╍╍┿╍╍╍╍╍╍╍┫ + ┣┅┅┅┅┅┅┿┅┅┅┅┅┅┅┫ + ┣┉┉┉┉┉┉┿┉┉┉┉┉┉┉┫ + ┣━━━━━━┿━━━━━━━┫ + ┣╍╍╍╍╍╍┿╍╍╍╍╍╍╍┫ + ┣┅┅┅┅┅┅┿┅┅┅┅┅┅┅┫ + ┣┉┉┉┉┉┉┿┉┉┉┉┉┉┉┫ + ┣━━━━━━┿━━━━━━━┫ + ┣╍╍╍╍╍╍┿╍╍╍╍╍╍╍┫ + ┣┅┅┅┅┅┅┿┅┅┅┅┅┅┅┫ + ┣┉┉┉┉┉┉┿┉┉┉┉┉┉┉┫ + ┠╌╌╌╌╌╌┼╌╌╌╌╌╌╌┨ + ┠┄┄┄┄┄┄┼┄┄┄┄┄┄┄┨ + ┠┈┈┈┈┈┈┼┈┈┈┈┈┈┈┨ + ┣══════╪═══════┫ + ┃ last │ N ┃ + ┗━━━━━━┷━━━━━━━┛ + EOF + end + end end From 7605247ffb81c8971fd15f1b69b7429e9b65edc7 Mon Sep 17 00:00:00 2001 From: Nate Berkopec Date: Wed, 27 Jan 2021 13:02:21 -0700 Subject: [PATCH 25/38] Fixup history, delete old Readme --- History.rdoc | 2 +- README.rdoc | 249 --------------------------------------------------- 2 files changed, 1 insertion(+), 250 deletions(-) delete mode 100644 README.rdoc diff --git a/History.rdoc b/History.rdoc index b4838ac..96c8ce5 100644 --- a/History.rdoc +++ b/History.rdoc @@ -1,4 +1,4 @@ -3.0.0 / 2020-01-22 +3.0.0 / 2020-01-27 - Support for (optional) Unicode border styles on tables. In order to support decent looking Unicode borders, different types of intersections get different types of intersection characters. This has the side effect of subtle formatting differences even for the ASCII table border case due to removal of certain intersections near colspans. diff --git a/README.rdoc b/README.rdoc deleted file mode 100644 index 3f4b2e3..0000000 --- a/README.rdoc +++ /dev/null @@ -1,249 +0,0 @@ -{}[https://github.com/tj/terminal-table/actions] - -= Terminal Table - -== Description - -TerminalTable Table is a fast and simple, yet feature rich ASCII table generator written in Ruby. - -== Installation - - $ gem install terminal-table - -== Usage - -=== Basics - -To use TerminalTable Table: - - require 'terminal-table' - -To generate a table, provide an array of arrays (which are interpreted as rows): - - rows = [] - rows << ['One', 1] - rows << ['Two', 2] - rows << ['Three', 3] - table = TerminalTable::Table.new :rows => rows - - # > puts table - # - # +-------+---+ - # | One | 1 | - # | Two | 2 | - # | Three | 3 | - # +-------+---+ - - -The constructor can also be given a block which is either yielded the Table object or instance evaluated: - - table = TerminalTable::Table.new do |t| - t.rows = rows - end - - table = TerminalTable::Table.new do - self.rows = rows - end - -Adding rows one by one: - - table = TerminalTable::Table.new do |t| - t << ['One', 1] - t.add_row ['Two', 2] - end - -To add separators between rows: - - table = TerminalTable::Table.new do |t| - t << ['One', 1] - t << :separator - t.add_row ['Two', 2] - t.add_separator - t.add_row ['Three', 3] - end - - # > puts table - # - # +-------+---+ - # | One | 1 | - # +-------+---+ - # | Two | 2 | - # +-------+---+ - # | Three | 3 | - # +-------+---+ - -Cells can handle multiline content: - - table = TerminalTable::Table.new do |t| - t << ['One', 1] - t << :separator - t.add_row ["Two\nDouble", 2] - t.add_separator - t.add_row ['Three', 3] - end - - # > puts table - # - # +--------+---+ - # | One | 1 | - # +--------+---+ - # | Two | 2 | - # | Double | | - # +--------+---+ - # | Three | 3 | - # +--------+---+ - -=== Head - -To add a head to the table: - - table = TerminalTable::Table.new :headings => ['Word', 'Number'], :rows => rows - - # > puts table - # - # +-------+--------+ - # | Word | Number | - # +-------+--------+ - # | One | 1 | - # | Two | 2 | - # | Three | 3 | - # +-------+--------+ - -=== Title - -To add a title to the table: - - table = TerminalTable::Table.new :title => "Cheatsheet", :headings => ['Word', 'Number'], :rows => rows - - # > puts table - # - # +------------+--------+ - # | Cheatsheet | - # +------------+--------+ - # | Word | Number | - # +------------+--------+ - # | One | 1 | - # | Two | 2 | - # | Three | 3 | - # +------------+--------+ - -=== Alignment - -To align the second column to the right: - - table.align_column(1, :right) - - # > puts table - # - # +-------+--------+ - # | Word | Number | - # +-------+--------+ - # | One | 1 | - # | Two | 2 | - # | Three | 3 | - # +-------+--------+ - -To align an individual cell, you specify the cell value in a hash along the alignment: - - table << ["Four", {:value => 4.0, :alignment => :center}] - - # > puts table - # - # +-------+--------+ - # | Word | Number | - # +-------+--------+ - # | One | 1 | - # | Two | 2 | - # | Three | 3 | - # | Four | 4.0 | - # +-------+--------+ - -=== Style - -To specify style options: - - table = TerminalTable::Table.new :headings => ['Word', 'Number'], :rows => rows, :style => {:width => 80} - - # > puts table - # - # +--------------------------------------+---------------------------------------+ - # | Word | Number | - # +--------------------------------------+---------------------------------------+ - # | One | 1 | - # | Two | 2 | - # | Three | 3 | - # +--------------------------------------+---------------------------------------+ - -And change styles on the fly: - - table.style = {:width => 40, :padding_left => 3, :border_x => "=", :border_i => "x"} - - # > puts table - # - # x====================x=================x - # | Cheatsheet | - # x====================x=================x - # | Word | Number | - # x====================x=================x - # | One | 1 | - # | Two | 2 | - # | Three | 3 | - # x====================x=================x - -You can also use styles to add a separator after every row: - - table = TerminalTable::Table.new do |t| - t.add_row [1, 'One'] - t.add_row [2, 'Two'] - t.add_row [3, 'Three'] - t.style = {:all_separators => true} - end - - # > puts table - # - # +---+-------+ - # | 1 | One | - # +---+-------+ - # | 2 | Two | - # +---+-------+ - # | 3 | Three | - # +---+-------+ - -You can also use styles to disable top and bottom borders of the table - - table = TerminalTable::Table.new do |t| - t.headings = ['id', 'name'] - t.rows = [[1, 'One'], [2, 'Two'], [3, 'Three']] - t.style = { :border_top => false, :border_bottom => false } - end - - # > puts table - # | id | name | - # +----+-------+ - # | 1 | One | - # | 2 | Two | - # | 3 | Three | - -To change the default style options: - - TerminalTable::Table::Style.defaults = {:width => 80} - -All Table objects created afterwards will inherit these defaults. - -=== Constructor options and setter methods - -Valid options for the constructor are :rows, :headings, :style and :title - and all options can also be set on the created table object by their setter method: - - table = TerminalTable::Table.new - table.title = "Cheatsheet" - table.headings = ['Word', 'Number'] - table.rows = rows - table.style = {:width => 40} - -== More examples - -For more examples, please see the examples/examples.rb file included in the source distribution. - -== Author - -TJ Holowaychuk From 998fea306552a12831fc5a0a3012cd10dd736a08 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Wed, 3 Feb 2021 09:12:50 -0500 Subject: [PATCH 26/38] fixing issue #118 --- examples/issue118.rb | 36 ++++++++++++++++++ lib/terminal-table/cell.rb | 8 ++++ lib/terminal-table/row.rb | 2 +- spec/unicode_table_spec.rb | 78 ++++++++++++++++++++++++++++---------- 4 files changed, 104 insertions(+), 20 deletions(-) create mode 100755 examples/issue118.rb diff --git a/examples/issue118.rb b/examples/issue118.rb new file mode 100755 index 0000000..e94eb71 --- /dev/null +++ b/examples/issue118.rb @@ -0,0 +1,36 @@ +#!/usr/bin/env ruby + +require_relative '../lib/terminal-table' + +puts Terminal::Table.new(headings: ['a', 'b', 'c', 'd'], style: { border: :unicode }) + +puts + +tbl = Terminal::Table.new do |t| + t.style = { border: :unicode } + t.add_separator + t.add_separator + t.add_row ['x','y','z'] + t.add_separator + t.add_separator +end +puts tbl + +puts + +puts Terminal::Table.new(headings: [['a', 'b', 'c', 'd'], ['cat','dog','frog','mouse']], style: { border: :unicode }) + +puts + +puts Terminal::Table.new(headings: ['a', 'b', 'c', 'd']) + +puts + +tbl = Terminal::Table.new do |t| + t.add_separator + t.add_separator + t.add_row ['x','y','z'] + t.add_separator + t.add_separator +end +puts tbl diff --git a/lib/terminal-table/cell.rb b/lib/terminal-table/cell.rb index 59c5be5..50bf6db 100644 --- a/lib/terminal-table/cell.rb +++ b/lib/terminal-table/cell.rb @@ -81,6 +81,14 @@ def width end inner_width + padding end + + def inspect + fields = %i[alignment colspan index value width].map do |name| + val = self.instance_variable_get('@'+name.to_s) + "@#{name}=#{val.inspect}" + end.join(', ') + return "#<#{self.class} #{fields}>" + end end end end diff --git a/lib/terminal-table/row.rb b/lib/terminal-table/row.rb index bb645a8..faef8d0 100644 --- a/lib/terminal-table/row.rb +++ b/lib/terminal-table/row.rb @@ -12,7 +12,7 @@ class Row ## # Initialize with _width_ and _options_. - def initialize table, array = [] + def initialize table, array = [], **_kwargs @cell_index = 0 @table = table @cells = [] diff --git a/spec/unicode_table_spec.rb b/spec/unicode_table_spec.rb index cf517ec..c920791 100755 --- a/spec/unicode_table_spec.rb +++ b/spec/unicode_table_spec.rb @@ -756,8 +756,8 @@ module Terminal ╰──────┴───────╯ EOF end - - it "should test many separator borders" do + + it "should test many separator borders" do @table.style = { border: :unicode_thick_edge } @table.title = 'Borders' @table.headings = ['name', 'value'] @@ -786,26 +786,66 @@ module Terminal ┃ name │ value ┃ ┣══════╪═══════┫ ┃ 1st │ 1 ┃ - ┣━━━━━━┿━━━━━━━┫ - ┣╍╍╍╍╍╍┿╍╍╍╍╍╍╍┫ - ┣┅┅┅┅┅┅┿┅┅┅┅┅┅┅┫ - ┣┉┉┉┉┉┉┿┉┉┉┉┉┉┉┫ - ┣━━━━━━┿━━━━━━━┫ - ┣╍╍╍╍╍╍┿╍╍╍╍╍╍╍┫ - ┣┅┅┅┅┅┅┿┅┅┅┅┅┅┅┫ - ┣┉┉┉┉┉┉┿┉┉┉┉┉┉┉┫ - ┣━━━━━━┿━━━━━━━┫ - ┣╍╍╍╍╍╍┿╍╍╍╍╍╍╍┫ - ┣┅┅┅┅┅┅┿┅┅┅┅┅┅┅┫ - ┣┉┉┉┉┉┉┿┉┉┉┉┉┉┉┫ - ┠╌╌╌╌╌╌┼╌╌╌╌╌╌╌┨ - ┠┄┄┄┄┄┄┼┄┄┄┄┄┄┄┨ - ┠┈┈┈┈┈┈┼┈┈┈┈┈┈┈┨ - ┣══════╪═══════┫ + ┣━━━━━━┷━━━━━━━┫ + ┣╍╍╍╍╍╍╍╍╍╍╍╍╍╍┫ + ┣┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ + ┣┉┉┉┉┉┉┉┉┉┉┉┉┉┉┫ + ┣━━━━━━━━━━━━━━┫ + ┣╍╍╍╍╍╍╍╍╍╍╍╍╍╍┫ + ┣┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ + ┣┉┉┉┉┉┉┉┉┉┉┉┉┉┉┫ + ┣━━━━━━━━━━━━━━┫ + ┣╍╍╍╍╍╍╍╍╍╍╍╍╍╍┫ + ┣┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ + ┣┉┉┉┉┉┉┉┉┉┉┉┉┉┉┫ + ┠╌╌╌╌╌╌╌╌╌╌╌╌╌╌┨ + ┠┄┄┄┄┄┄┄┄┄┄┄┄┄┄┨ + ┠┈┈┈┈┈┈┈┈┈┈┈┈┈┈┨ + ┣══════╤═══════┫ ┃ last │ N ┃ ┗━━━━━━┷━━━━━━━┛ EOF - end + end + + it "should allow headings with no rows (issue #118.a)" do + @table.headings = ['a', 'b', 'c', 'd'] + @table.render.should eq <<-EOF.deindent + ┌───┬───┬───┬───┐ + │ a │ b │ c │ d │ + ╞═══╧═══╧═══╧═══╡ + └───────────────┘ + EOF + end + + it "should allow multiple headings with no rows (issue #118.b)" do + @table.headings = [['a', 'b', 'c', 'd'], ['cat','dog','frog','mouse']] + @table.render.should eq <<-EOF.deindent + ┌─────┬─────┬──────┬───────┐ + │ a │ b │ c │ d │ + ╞═════╪═════╪══════╪═══════╡ + │ cat │ dog │ frog │ mouse │ + ╞═════╧═════╧══════╧═══════╡ + └──────────────────────────┘ + EOF + end + + it "should not create spurrious vertical intersections for adjacent separators (issue #118.c)" do + @table.add_separator + @table.add_separator + @table.add_row ['x','y','z'] + @table.add_separator + @table.add_separator + @table.render.should eq <<-EOF.deindent + ┌───────────┐ + ├───────────┤ + ├───┬───┬───┤ + │ x │ y │ z │ + ├───┴───┴───┤ + ├───────────┤ + └───────────┘ + EOF + end + end end From 2f06506c384a5420826118784b3c58b9c2fdfb57 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Tue, 2 Feb 2021 18:49:39 -0800 Subject: [PATCH 27/38] Support unicode-display_width v2.0.0 --- Gemfile.lock | 4 ++-- lib/terminal-table/cell.rb | 2 +- lib/terminal-table/table.rb | 2 +- terminal-table.gemspec | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 593f378..1754d17 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,7 +2,7 @@ PATH remote: . specs: terminal-table (3.0.0) - unicode-display_width (~> 1.1, >= 1.1.1) + unicode-display_width (>= 1.1.1, < 3) GEM remote: https://rubygems.org/ @@ -30,7 +30,7 @@ GEM term-ansicolor (1.7.1) tins (~> 1.0) tins (1.0.1) - unicode-display_width (1.7.0) + unicode-display_width (2.0.0) PLATFORMS ruby diff --git a/lib/terminal-table/cell.rb b/lib/terminal-table/cell.rb index 50bf6db..cfa7573 100644 --- a/lib/terminal-table/cell.rb +++ b/lib/terminal-table/cell.rb @@ -1,4 +1,4 @@ -require 'unicode/display_width/no_string_ext' +require 'unicode/display_width' module TerminalTable class Table diff --git a/lib/terminal-table/table.rb b/lib/terminal-table/table.rb index 70cad6d..60581c6 100644 --- a/lib/terminal-table/table.rb +++ b/lib/terminal-table/table.rb @@ -1,4 +1,4 @@ -require 'unicode/display_width/no_string_ext' +require 'unicode/display_width' module TerminalTable class Table diff --git a/terminal-table.gemspec b/terminal-table.gemspec index f459215..1c4d761 100644 --- a/terminal-table.gemspec +++ b/terminal-table.gemspec @@ -22,5 +22,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency "term-ansicolor" spec.add_development_dependency "pry" - spec.add_runtime_dependency "unicode-display_width", ["~> 1.1", ">= 1.1.1"] + spec.add_runtime_dependency "unicode-display_width", [">= 1.1.1", "< 3"] end From 60008ca574d604f47a6ac8bf5b96d4528496222d Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Thu, 4 Feb 2021 08:10:06 -0500 Subject: [PATCH 28/38] fixing misstatement in README, adding csv-to-terminal-table recipe in the examples dir and README --- README.md | 22 +++++++++++++++++++++- examples/data.csv | 4 ++++ examples/show_csv_table.rb | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 examples/data.csv create mode 100755 examples/show_csv_table.rb diff --git a/README.md b/README.md index b584328..0dc659a 100644 --- a/README.md +++ b/README.md @@ -348,7 +348,7 @@ table.style.border[:nw] = '*' # override the north-west corner of the table ### Customizing row separators -Row-separators can now be customized in a variety of ways. The default separator's border_type is referred to as `:mid`. Additional `:strong` / `:strong_a` and `:strong_b` separator styles can be applied to separate sections (e.g. header/footer/title). +Row-separators can now be customized in a variety of ways. The default separator's border_type is referred to as `:div`. Additional separator border types (e.g. `:double`, `:heavy`, `:dash` - see full list below) can be applied to separate the sections (e.g. header/footer/title). The separator border_type may be specified when a user-defined separator added. Alternatively, borders may be adjusted after the table's rows are elaborated, but before the table is rendered. @@ -379,6 +379,26 @@ rows[2].border_type = :heavy # modify separator row: emphasize below title puts table.render ``` +## Example: Displaying a small CSV spreadsheet + +See `examples/show_csv_table.rb` in source distribution + +```ruby +#!/usr/bin/env ruby +require "csv" +require "terminal-table" +use_stdin = ARGV[0].nil? || (ARGV[0] == '-') +io_object = use_stdin ? $stdin : File.open(ARGV[0], 'r') +csv = CSV.new(io_object) +csv_array = csv.to_a +user_table = Terminal::Table.new do |v| + v.style = { :border => :unicode_round } # >= v3.0.0 + v.title = "Some Title" + v.headings = csv_array[0] + v.rows = csv_array[1..-1] +end +puts user_table +``` ## More examples For more examples, please see the `examples` directory included in the diff --git a/examples/data.csv b/examples/data.csv new file mode 100644 index 0000000..2af8d47 --- /dev/null +++ b/examples/data.csv @@ -0,0 +1,4 @@ +First Name,Last Name,Email +TJ,Holowaychuk,tj@vision-media.ca +Bob,Someone,bob@vision-media.ca +Joe,Whatever,joe@vision-media.ca diff --git a/examples/show_csv_table.rb b/examples/show_csv_table.rb new file mode 100755 index 0000000..1bb368a --- /dev/null +++ b/examples/show_csv_table.rb @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby + +require "csv" +$LOAD_PATH << "#{__dir__}/../lib" +require "terminal-table" + +# +# Usage: +# ./show_csv_table.rb data.csv +# cat data.csv | ./show_csv_table.rb +# cat data.csv | ./show_csv_table.rb - +# +# +# Reads a CSV from $stdin if no argument given, or argument is '-' +# otherwise interprets first cmdline argument as the CSV filename +# +use_stdin = ARGV[0].nil? || (ARGV[0] == '-') +io_object = use_stdin ? $stdin : File.open(ARGV[0], 'r') +csv = CSV.new(io_object) + +# +# Convert to an array for use w/ terminal-table +# The assumption is that this is a pretty small spreadsheet. +# +csv_array = csv.to_a + +user_table = Terminal::Table.new do |v| + v.style = { :border => :unicode_round } # >= v3.0.0 + v.title = "Some Title" + v.headings = csv_array[0] + v.rows = csv_array[1..-1] +end + +puts user_table From d67126b5fda941f8a66f4278fe7cec153a733c5f Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Thu, 4 Feb 2021 20:35:38 -0500 Subject: [PATCH 29/38] further cleanup of the readme --- README.md | 57 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 0dc659a..de7fc08 100644 --- a/README.md +++ b/README.md @@ -63,10 +63,10 @@ To add separators between rows: ```ruby table = Terminal::Table.new do |t| - t << ['One', 1] - t << :separator + t << ['One', 1] # Using << (push) as an alias for add_row + t << :separator # Using << with :separator as an alias for add_separator t.add_row ['Two', 2] - t.add_separator + t.add_separator # Note - this version allows setting the separator's border_type t.add_row ['Three', 3] end @@ -128,7 +128,7 @@ table = Terminal::Table.new :title => "Cheatsheet", :headings => ['Word', 'Numbe # > puts table # -# +------------+--------+ +# +---------------------+ # | Cheatsheet | # +------------+--------+ # | Word | Number | @@ -196,7 +196,7 @@ table.style = {:width => 40, :padding_left => 3, :border_x => "=", :border_i => # > puts table # -# x====================x=================x +# x======================================x # | Cheatsheet | # x====================x=================x # | Word | Number | @@ -286,9 +286,9 @@ table.style = {:width => 40} ## New Formatting ### Unicode Table Borders -A support for Unicode 'box art' borders presented a challenge, as the basics of terminal-table only handled three border types: horizontal (x), vertical (y), and intersection (i). For proper box-art, it became necessary to enable different types of corners/edges for multiple intersection types. +Support for Unicode 'box art' borders presented a challenge, as the original terminal-table only handled three border types: horizontal (x), vertical (y), and intersection (i). For proper box-art, it became necessary to enable different types of corners/edges for multiple intersection types. -For the sake of backward compatiblity, the previous interface is still supported, this gem has been around a long time and making breaking changes would be inconvenient. The new interface is required for any complex and/or unicode style bordering. A few variations on border style are supported via some new classes and creation of additional classes (or modification of characters used in existing ones) will allow for customized border types. +For the sake of backward compatiblity, the previous interface is still supported, as this gem has been around a long time and making breaking changes would have been inconvenient. The new interface is required for any complex and/or Unicode style bordering. A few variations on border style are supported via some new classes and creation of additional classes (or modification of characters used in existing ones) will allow for customized border types. The simplest way to use an alternate border is one of the following: ``` @@ -307,7 +307,7 @@ table.style = { :border => Terminal::Table::UnicodeThickEdgeBorder.new() } If you define a custom class and wish to use the symbol shortcut, you must namespace within `Terminal::Table` and end your class name with `Border`. ### Markdown Compatiblity -Per popular request, markdown formatted tables can be generated by using the following border style: +Per popular request, Markdown formatted tables can be generated by using the following border style: ``` table.style = { :border => :markdown } @@ -320,37 +320,40 @@ table.style = { :border => :ascii } ``` ### Customizing Borders -Inside the `UnicodeBorder` class, there are definitions for a variety of corner/intersection types. +Inside the `UnicodeBorder` class, there are definitions for a variety of corner/intersection and divider types. -``` +```ruby @data = { -nil => nil, -nw: "┌", nx: "─", n: "┬", ne: "┐", -yw: "│", y: "│", ye: "│", -aw: "╞", ax: "═", ai: "╪", ae: "╡", ad: '╤', au: "╧", -bw: "┝", bx: "━", bi: "┿", be: "┥", bd: '┯', bu: "┷", -w: "├", x: "─", i: "┼", e: "┤", dn: "┬", up: "┴", -sw: "└", sx: "─", s: "┴", se: "┘", + nil => nil, + nw: "┌", nx: "─", n: "┬", ne: "┐", + yw: "│", y: "│", ye: "│", + aw: "╞", ax: "═", ai: "╪", ae: "╡", ad: '╤', au: "╧", # double + bw: "┝", bx: "━", bi: "┿", be: "┥", bd: '┯', bu: "┷", # heavy/bold/thick + w: "├", x: "─", i: "┼", e: "┤", dn: "┬", up: "┴", # normal div + sw: "└", sx: "─", s: "┴", se: "┘", + # alternative dots/dashes + x_dot4: '┈', x_dot3: '┄', x_dash: '╌', + bx_dot4: '┉', bx_dot3: '┅', bx_dash: '╍', } ``` Note that many are defined as directional (:nw == north-west), others defined in terms of 'x' or 'y'. -The border that separates headings (below each heading) is of type `:double` and is defined with 'a*' entries. -Alternate `:heavy` types that can be applied to separators can be defined with 'b*' entries. +The border that separates headings (below each heading) is of type `:double` and is defined with `a*` entries. +Alternate `:heavy` types that can be applied to separators can be defined with `b*` entries. -When defining a new set of borders, it's probably easiest to define a new class that inherits from UnicodeBorder and replaces the @data Hash. -However, these corners can be these can be overridden with: +When defining a new set of borders, it's probably easiest to define a new class that inherits from UnicodeBorder and replaces the `@data` Hash. +However, these elements can be these can be overridden by poking setting the Hash, should the need arise: ``` table.style = {border: :unicode} -table.style.border[:nw] = '*' # override the north-west corner of the table +table.style.border[:nw] = '*' # Override the north-west corner of the table ``` ### Customizing row separators Row-separators can now be customized in a variety of ways. The default separator's border_type is referred to as `:div`. Additional separator border types (e.g. `:double`, `:heavy`, `:dash` - see full list below) can be applied to separate the sections (e.g. header/footer/title). -The separator border_type may be specified when a user-defined separator added. Alternatively, borders may be adjusted after the table's rows are elaborated, but before the table is rendered. +The separator's `border_type` may be specified when a user-defined separator is added. Alternatively, borders may be adjusted after the table's rows are elaborated, but before the table is rendered. Separator `border_type`s can be adjusted to be heavy, use double-lines, and different dash/dot styles. The border type should be one of: @@ -360,13 +363,12 @@ Separator `border_type`s can be adjusted to be heavy, use double-lines, and diff bold bold_dash bold_dot3 bold_dot4 double - To manually set the separator border_type, the `add_separator` method may be called. ```ruby add_separator(border_type: :heavy_dash) ``` -Alternatively, if `style: :all_separators` is used, it may be necessary to elaborate the Rows prior to rendering. +Alternatively, if `style: :all_separators` is used at the table level, it may be necessary to elaborate the implicit Separator rows prior to rendering. ```ruby table = Terminal::Table.new do |t| t.add_row [1, 'One'] @@ -381,7 +383,7 @@ puts table.render ## Example: Displaying a small CSV spreadsheet -See `examples/show_csv_table.rb` in source distribution +This example code demonstrates using Terminal-table and CSV to display a small spreadsheet. ```ruby #!/usr/bin/env ruby @@ -399,6 +401,9 @@ user_table = Terminal::Table.new do |v| end puts user_table ``` + +See also `examples/show_csv_table.rb` in the source distribution. + ## More examples For more examples, please see the `examples` directory included in the From baef02f7083eb7b4a5f1c2e00500a9d143933a2b Mon Sep 17 00:00:00 2001 From: Alexandre ZANNI <16578570+noraj@users.noreply.github.com> Date: Thu, 25 Feb 2021 15:45:47 +0100 Subject: [PATCH 30/38] badges update - add badge for gem version (including a link to rubygems, more convenient) - fix CI badge to be pure markdown --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index de7fc08..5f0ecc9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -[](https://github.com/tj/terminal-table/actions) +[![CI status](https://github.com/tj/terminal-table/workflows/CI/badge.svg)](https://github.com/tj/terminal-table/actions) +[![Gem Version](https://badge.fury.io/rb/terminal-table.svg)](https://badge.fury.io/rb/terminal-table) # Terminal Table From 06e9531fe7bafe7cd94741c845ed3b8f71075851 Mon Sep 17 00:00:00 2001 From: Dmitriy Voropaev Date: Fri, 26 Mar 2021 17:18:58 +0400 Subject: [PATCH 31/38] fix shebang in examples/examples_unicode.rb --- examples/examples_unicode.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/examples_unicode.rb b/examples/examples_unicode.rb index ad8455f..56717e3 100755 --- a/examples/examples_unicode.rb +++ b/examples/examples_unicode.rb @@ -1,4 +1,4 @@ -#!/bin/env ruby +#!/usr/bin/env ruby $:.unshift File.dirname(__FILE__) + '/../lib' require 'terminal-table/import' From 4198ec54c4c907b826faf68060c25b8d8ba168ee Mon Sep 17 00:00:00 2001 From: Nate Berkopec Date: Mon, 10 May 2021 08:16:02 -0600 Subject: [PATCH 32/38] v3.0.1 --- Gemfile.lock | 4 ++-- History.rdoc | 7 +++++++ lib/terminal-table/version.rb | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1754d17..af232e5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - terminal-table (3.0.0) + terminal-table (3.0.1) unicode-display_width (>= 1.1.1, < 3) GEM @@ -46,4 +46,4 @@ DEPENDENCIES tins (~> 1.0.0) BUNDLED WITH - 2.2.0 + 2.2.3 diff --git a/History.rdoc b/History.rdoc index 96c8ce5..b74ec5f 100644 --- a/History.rdoc +++ b/History.rdoc @@ -1,4 +1,11 @@ +3.0.1 / 2021-05-10 +================== + +- Support for unicode-display_width 2.0 +- Fix issue where last row of an empty table changed format + 3.0.0 / 2020-01-27 +================== - Support for (optional) Unicode border styles on tables. In order to support decent looking Unicode borders, different types of intersections get different types of intersection characters. This has the side effect of subtle formatting differences even for the ASCII table border case due to removal of certain intersections near colspans. diff --git a/lib/terminal-table/version.rb b/lib/terminal-table/version.rb index 44c1ba7..ac597f2 100644 --- a/lib/terminal-table/version.rb +++ b/lib/terminal-table/version.rb @@ -1,5 +1,5 @@ module TerminalTable class Table - VERSION = '3.0.0' + VERSION = '3.0.1' end end From f2d993909201d878c965f410c59ce03f8595aafe Mon Sep 17 00:00:00 2001 From: Ivan Kuchin Date: Tue, 14 Sep 2021 18:05:34 +0200 Subject: [PATCH 33/38] fix align_column for nil values and colspan I've noticed the problem for table containing nil values: $stdout << Terminal::Table.new(headings: %w[xxx xxx]) do add_row [nil, 1] add_row [2, 3] add_row [4, 5] align_column 0, :right end doesn't change the alignment for the cell with 4: +-----+-----+ | xxx | xxx | +-----+-----+ | | 1 | | 2 | 3 | | 4 | 5 | +-----+-----+ The `align_method` internally uses `column` which without second argument returns a list of non null values at specific column taking colspan into account. But afterwards the value is ignored and cells are fetched by index, which doesn't work well if there are cells with custom colspan in the table: $stdout << Terminal::Table.new(headings: %w[xxx] * 4) do add_row [1, 2, 3, 4] add_row [5, {:value => 6, :colspan => 2}, 7] add_row [{:value => 8, :colspan => 2}, 9, :a] add_row [{:value => :b, :colspan => 2}, {:value => :c, :colspan => 2}] add_row [{:value => :d, :colspan => 3}, :e] align_column 1, :right end prints: +-----+-----+-----+-----+ | xxx | xxx | xxx | xxx | +-----+-----+-----+-----+ | 1 | 2 | 3 | 4 | | 5 | 6 | 7 | | 8 | 9 | a | | b | c | | d | e | +-----------------+-----+ --- lib/terminal-table/table.rb | 6 ++---- spec/table_spec.rb | 28 ++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/terminal-table/table.rb b/lib/terminal-table/table.rb index 60581c6..2c5edbd 100644 --- a/lib/terminal-table/table.rb +++ b/lib/terminal-table/table.rb @@ -27,10 +27,8 @@ def initialize options = {}, &block # Align column _n_ to the given _alignment_ of :center, :left, or :right. def align_column n, alignment - r = rows - column(n).each_with_index do |col, i| - cell = r[i][n] - next unless cell + # nil forces the column method to return the cell itself + column(n, nil).each do |cell| cell.alignment = alignment unless cell.alignment? end end diff --git a/spec/table_spec.rb b/spec/table_spec.rb index 7467863..9b6e85a 100755 --- a/spec/table_spec.rb +++ b/spec/table_spec.rb @@ -355,20 +355,44 @@ module TerminalTable it "should align columns, but allow specifics to remain" do @table.headings = ['Just some characters', 'Num'] - @table.rows = [[{:value => 'a', :alignment => :left}, 1], ['b', 2], ['c', 3]] + @table.rows = [[nil, 0], [{:value => 'a', :alignment => :left}, nil], ['b', 2], ['c', 3]] @table.align_column 0, :center @table.align_column 1, :right @table.render.should eq <<-EOF.deindent +----------------------+-----+ | Just some characters | Num | +----------------------+-----+ - | a | 1 | + | | 0 | + | a | | | b | 2 | | c | 3 | +----------------------+-----+ EOF end + it "should align columns taking colspan into account" do + @table.headings = ['xxx'] * 4 + @table.rows = [ + [1, 2, 3, 4], + [5, {:value => 6, :colspan => 2}, 7], + [{:value => 8, :colspan => 2}, 9, :a], + [{:value => :b, :colspan => 2}, {:value => :c, :colspan => 2}], + [{:value => :d, :colspan => 3}, :e], + ] + @table.align_column 3, :right + @table.render.should eq <<-EOF.deindent + +-----+-----+-----+-----+ + | xxx | xxx | xxx | xxx | + +-----+-----+-----+-----+ + | 1 | 2 | 3 | 4 | + | 5 | 6 | 7 | + | 8 | 9 | a | + | b | c | + | d | e | + +-----------------+-----+ + EOF + end + describe "#==" do it "should be equal to itself" do t = Table.new From 5c51ffce6bec530168c4f3b83a96b5eae26619ff Mon Sep 17 00:00:00 2001 From: Nate Berkopec Date: Sun, 19 Sep 2021 12:49:30 -0600 Subject: [PATCH 34/38] 3.0.2 --- History.rdoc | 5 +++++ lib/terminal-table/version.rb | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/History.rdoc b/History.rdoc index b74ec5f..06432e1 100644 --- a/History.rdoc +++ b/History.rdoc @@ -1,3 +1,8 @@ +3.0.2 / 2021-09-19 +================== + +- fix align_column for nil values and colspan + 3.0.1 / 2021-05-10 ================== diff --git a/lib/terminal-table/version.rb b/lib/terminal-table/version.rb index ac597f2..e14ff06 100644 --- a/lib/terminal-table/version.rb +++ b/lib/terminal-table/version.rb @@ -1,5 +1,5 @@ module TerminalTable class Table - VERSION = '3.0.1' + VERSION = '3.0.2' end end From 6c3a58696f1e109aa097ee558811b3c10d56d078 Mon Sep 17 00:00:00 2001 From: Nate Berkopec Date: Sun, 19 Sep 2021 12:50:38 -0600 Subject: [PATCH 35/38] remove lock --- .gitignore | 1 + Gemfile.lock | 49 ------------------------------------------------- 2 files changed, 1 insertion(+), 49 deletions(-) delete mode 100644 Gemfile.lock diff --git a/.gitignore b/.gitignore index ea325b8..b91d764 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ tmp doc vendor /.bundle +Gemfile.lock # tempfiles *~ diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index af232e5..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,49 +0,0 @@ -PATH - remote: . - specs: - terminal-table (3.0.1) - unicode-display_width (>= 1.1.1, < 3) - -GEM - remote: https://rubygems.org/ - specs: - coderay (1.1.3) - diff-lcs (1.4.4) - method_source (1.0.0) - pry (0.13.1) - coderay (~> 1.1) - method_source (~> 1.0) - rake (13.0.1) - rspec (3.10.0) - rspec-core (~> 3.10.0) - rspec-expectations (~> 3.10.0) - rspec-mocks (~> 3.10.0) - rspec-core (3.10.0) - rspec-support (~> 3.10.0) - rspec-expectations (3.10.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-mocks (3.10.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-support (3.10.0) - term-ansicolor (1.7.1) - tins (~> 1.0) - tins (1.0.1) - unicode-display_width (2.0.0) - -PLATFORMS - ruby - x86_64-linux - -DEPENDENCIES - bundler (~> 2) - pry - rake (~> 13.0) - rspec (>= 3.0) - term-ansicolor - terminal-table! - tins (~> 1.0.0) - -BUNDLED WITH - 2.2.3 From 9b36e9a0be309c87d183c46212a6fde93067962b Mon Sep 17 00:00:00 2001 From: Peter Goldstein Date: Fri, 10 Jun 2022 23:08:14 -0700 Subject: [PATCH 36/38] Add Ruby 3.0 and 3.1 to the CI matrix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3b8591..ac24c6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: matrix: os: [ubuntu] - ruby: [2.4, 2.5, 2.6, 2.7] + ruby: [2.4, 2.5, 2.6, 2.7, '3.0', 3.1] runs-on: ${{ matrix.os }}-latest From 04008326d8b17f289b36daeb1f58eafcba73de02 Mon Sep 17 00:00:00 2001 From: Peter Goldstein Date: Wed, 28 Dec 2022 23:04:08 -0500 Subject: [PATCH 37/38] Adds Ruby 3.2 to the CI matrix. Also updates the checkout action version. --- .github/dependabot.yml | 6 ++++++ .github/workflows/ci.yml | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5ace460 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac24c6d..e7c1abe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,12 +12,12 @@ jobs: matrix: os: [ubuntu] - ruby: [2.4, 2.5, 2.6, 2.7, '3.0', 3.1] + ruby: [2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2] runs-on: ${{ matrix.os }}-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 with: From a8232dd5b38a277ec56d163d8eed4433c1d76a58 Mon Sep 17 00:00:00 2001 From: Zoran Kovacevic Date: Fri, 8 Sep 2017 09:08:16 +0200 Subject: [PATCH 38/38] Renaming Terminal to TerminalTable --- README.md | 46 ++++++++++++++++++------------------- lib/terminal-table/style.rb | 8 +++---- lib/terminal-table/util.rb | 2 +- spec/style_spec.rb | 18 +++++++-------- spec/table_spec.rb | 4 ++-- spec/unicode_table_spec.rb | 26 ++++++++++----------- 6 files changed, 52 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 5f0ecc9..73b0a12 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ rows = [] rows << ['One', 1] rows << ['Two', 2] rows << ['Three', 3] -table = Terminal::Table.new :rows => rows +table = TerminalTable::Table.new :rows => rows # > puts table # @@ -44,18 +44,18 @@ The constructor can also be given a block which is either yielded the Table object or instance evaluated: ```ruby -table = Terminal::Table.new do |t| +table = TerminalTable::Table.new do |t| t.rows = rows end -table = Terminal::Table.new do +table = TerminalTable::Table.new do self.rows = rows end ``` Adding rows one by one: ```ruby -table = Terminal::Table.new do |t| +table = TerminalTable::Table.new do |t| t << ['One', 1] t.add_row ['Two', 2] end @@ -63,7 +63,7 @@ end To add separators between rows: ```ruby -table = Terminal::Table.new do |t| +table = TerminalTable::Table.new do |t| t << ['One', 1] # Using << (push) as an alias for add_row t << :separator # Using << with :separator as an alias for add_separator t.add_row ['Two', 2] @@ -84,7 +84,7 @@ end Cells can handle multiline content: ```ruby -table = Terminal::Table.new do |t| +table = TerminalTable::Table.new do |t| t << ['One', 1] t << :separator t.add_row ["Two\nDouble", 2] @@ -108,7 +108,7 @@ end To add a head to the table: ```ruby -table = Terminal::Table.new :headings => ['Word', 'Number'], :rows => rows +table = TerminalTable::Table.new :headings => ['Word', 'Number'], :rows => rows # > puts table # @@ -125,7 +125,7 @@ table = Terminal::Table.new :headings => ['Word', 'Number'], :rows => rows To add a title to the table: ```ruby -table = Terminal::Table.new :title => "Cheatsheet", :headings => ['Word', 'Number'], :rows => rows +table = TerminalTable::Table.new :title => "Cheatsheet", :headings => ['Word', 'Number'], :rows => rows # > puts table # @@ -178,7 +178,7 @@ table << ["Four", {:value => 4.0, :alignment => :center}] To specify style options: ```ruby -table = Terminal::Table.new :headings => ['Word', 'Number'], :rows => rows, :style => {:width => 80} +table = TerminalTable::Table.new :headings => ['Word', 'Number'], :rows => rows, :style => {:width => 80} # > puts table # @@ -210,7 +210,7 @@ table.style = {:width => 40, :padding_left => 3, :border_x => "=", :border_i => You can also use styles to add a separator after every row: ```ruby -table = Terminal::Table.new do |t| +table = TerminalTable::Table.new do |t| t.add_row [1, 'One'] t.add_row [2, 'Two'] t.add_row [3, 'Three'] @@ -230,7 +230,7 @@ end You can also use styles to disable top and bottom borders of the table. ```ruby -table = Terminal::Table.new do |t| +table = TerminalTable::Table.new do |t| t.headings = ['id', 'name'] t.rows = [[1, 'One'], [2, 'Two'], [3, 'Three']] t.style = { :border_top => false, :border_bottom => false } @@ -247,7 +247,7 @@ end And also to disable left and right borders of the table. ```ruby -table = Terminal::Table.new do |t| +table = TerminalTable::Table.new do |t| t.headings = ['id', 'name'] t.rows = [[1, 'One'], [2, 'Two'], [3, 'Three']] t.style = { :border_left => false, :border_right => false } @@ -266,7 +266,7 @@ end To change the default style options: ```ruby -Terminal::Table::Style.defaults = {:width => 80} +TerminalTable::Table::Style.defaults = {:width => 80} ``` All Table objects created afterwards will inherit these defaults. @@ -277,7 +277,7 @@ and all options can also be set on the created table object by their setter method: ```ruby -table = Terminal::Table.new +table = TerminalTable::Table.new table.title = "Cheatsheet" table.headings = ['Word', 'Number'] table.rows = rows @@ -298,14 +298,14 @@ table.style = { :border => :unicode_round } table.style = { :border => :unicode_thick_edge } ``` -These are a convenience wrapper around setting border using an instance of a class that inherits from Table::Terminal::Border +These are a convenience wrapper around setting border using an instance of a class that inherits from Table::TerminalTable::Border ``` -table.style = { :border => Terminal::Table::UnicodeBorder.new() } -table.style = { :border => Terminal::Table::UnicodeRoundBorder.new() } -table.style = { :border => Terminal::Table::UnicodeThickEdgeBorder.new() } +table.style = { :border => TerminalTable::Table::UnicodeBorder.new() } +table.style = { :border => TerminalTable::Table::UnicodeRoundBorder.new() } +table.style = { :border => TerminalTable::Table::UnicodeThickEdgeBorder.new() } ``` -If you define a custom class and wish to use the symbol shortcut, you must namespace within `Terminal::Table` and end your class name with `Border`. +If you define a custom class and wish to use the symbol shortcut, you must namespace within `TerminalTable::Table` and end your class name with `Border`. ### Markdown Compatiblity Per popular request, Markdown formatted tables can be generated by using the following border style: @@ -327,7 +327,7 @@ Inside the `UnicodeBorder` class, there are definitions for a variety of corner/ @data = { nil => nil, nw: "┌", nx: "─", n: "┬", ne: "┐", - yw: "│", y: "│", ye: "│", + yw: "│", y: "│", ye: "│", aw: "╞", ax: "═", ai: "╪", ae: "╡", ad: '╤', au: "╧", # double bw: "┝", bx: "━", bi: "┿", be: "┥", bd: '┯', bu: "┷", # heavy/bold/thick w: "├", x: "─", i: "┼", e: "┤", dn: "┬", up: "┴", # normal div @@ -358,7 +358,7 @@ The separator's `border_type` may be specified when a user-defined separator is Separator `border_type`s can be adjusted to be heavy, use double-lines, and different dash/dot styles. The border type should be one of: - div dash dot3 dot4 + div dash dot3 dot4 thick thick_dash thick_dot3 thick_dot4 heavy heavy_dash heavy_dot3 heavy_dot4 bold bold_dash bold_dot3 bold_dot4 @@ -371,7 +371,7 @@ add_separator(border_type: :heavy_dash) Alternatively, if `style: :all_separators` is used at the table level, it may be necessary to elaborate the implicit Separator rows prior to rendering. ```ruby -table = Terminal::Table.new do |t| +table = TerminalTable::Table.new do |t| t.add_row [1, 'One'] t.add_row [2, 'Two'] t.add_row [3, 'Three'] @@ -394,7 +394,7 @@ use_stdin = ARGV[0].nil? || (ARGV[0] == '-') io_object = use_stdin ? $stdin : File.open(ARGV[0], 'r') csv = CSV.new(io_object) csv_array = csv.to_a -user_table = Terminal::Table.new do |v| +user_table = TerminalTable::Table.new do |v| v.style = { :border => :unicode_round } # >= v3.0.0 v.title = "Some Title" v.headings = csv_array[0] diff --git a/lib/terminal-table/style.rb b/lib/terminal-table/style.rb index d65d3ed..5485808 100644 --- a/lib/terminal-table/style.rb +++ b/lib/terminal-table/style.rb @@ -1,7 +1,7 @@ # coding: utf-8 require 'forwardable' -module Terminal +module TerminalTable class Table class Border @@ -185,7 +185,7 @@ def initialize # # To set a default style for all tables created afterwards use Style.defaults= # - # Terminal::Table::Style.defaults = {:width => 80} + # TerminalTable::Table::Style.defaults = {:width => 80} # class Style extend Forwardable @@ -213,10 +213,10 @@ def border=(val) # convert symbol name like :foo_bar to get class FooBarBorder klass_str = val.to_s.split('_').collect(&:capitalize).join + "Border" begin - klass = Terminal::Table::const_get(klass_str) + klass = TerminalTable::Table::const_get(klass_str) @border = klass.new rescue NameError - raise "Cannot lookup class Terminal::Table::#{klass_str} from symbol #{val.inspect}" + raise "Cannot lookup class TerminalTable::Table::#{klass_str} from symbol #{val.inspect}" end else @border = val diff --git a/lib/terminal-table/util.rb b/lib/terminal-table/util.rb index a584c3d..e8c2d16 100644 --- a/lib/terminal-table/util.rb +++ b/lib/terminal-table/util.rb @@ -1,4 +1,4 @@ -module Terminal +module TerminalTable class Table module Util # removes all ANSI escape sequences (e.g. color) diff --git a/spec/style_spec.rb b/spec/style_spec.rb index 2e3005f..a7f710e 100644 --- a/spec/style_spec.rb +++ b/spec/style_spec.rb @@ -1,21 +1,21 @@ # encoding: utf-8 require 'spec_helper' -module Terminal +module TerminalTable describe Table::Style do - + before :each do @table = Table.new end border_map = { - :markdown => Terminal::Table::MarkdownBorder, - :ascii => Terminal::Table::AsciiBorder, - :unicode => Terminal::Table::UnicodeBorder, - :unicode_thick_edge => Terminal::Table::UnicodeThickEdgeBorder, - :unicode_round => Terminal::Table::UnicodeRoundBorder, + :markdown => TerminalTable::Table::MarkdownBorder, + :ascii => TerminalTable::Table::AsciiBorder, + :unicode => TerminalTable::Table::UnicodeBorder, + :unicode_thick_edge => TerminalTable::Table::UnicodeThickEdgeBorder, + :unicode_round => TerminalTable::Table::UnicodeRoundBorder, } - + border_map.each do |name_sym, klass| it "should enumerate a #{name_sym} table" do @table.style.border = name_sym @@ -24,6 +24,6 @@ module Terminal @table.style.border.should be_a klass end end - + end end diff --git a/spec/table_spec.rb b/spec/table_spec.rb index 9b6e85a..34fabc9 100755 --- a/spec/table_spec.rb +++ b/spec/table_spec.rb @@ -86,7 +86,7 @@ module TerminalTable it "should render separators" do @table.headings = ['Char', 'Num'] @table << ['a', 1] - separator = Terminal::Table::Separator.new(@table) + separator = TerminalTable::Table::Separator.new(@table) separator.render.should eq '+------------+' end @@ -719,6 +719,6 @@ module TerminalTable | c | 3 | EOF end - + end end diff --git a/spec/unicode_table_spec.rb b/spec/unicode_table_spec.rb index c920791..09397f6 100755 --- a/spec/unicode_table_spec.rb +++ b/spec/unicode_table_spec.rb @@ -1,11 +1,11 @@ # encoding: utf-8 require 'spec_helper' -module Terminal +module TerminalTable describe Table do before :each do @table = Table.new - @table.style = { :border => Terminal::Table::UnicodeBorder.new() } + @table.style = { :border => TerminalTable::Table::UnicodeBorder.new() } end @@ -88,7 +88,7 @@ module Terminal it "should render separators" do @table.headings = ['Char', 'Num'] @table << ['a', 1] - separator = Terminal::Table::Separator.new(@table) + separator = TerminalTable::Table::Separator.new(@table) # For UnicodeBorder this now defaults to a :div border_type separator.render.should eq '├────────────┤' end @@ -138,7 +138,7 @@ module Terminal @table.style.border[:s] = "^" @table.style.border[:aw] = "5" @table.style.border[:ae] = "6" - + @table << ['a', 1] @table << ['b', 2] @table << ['c', 3] @@ -273,7 +273,7 @@ module Terminal it "should render properly using block syntax" do - table = Terminal::Table.new do |t| + table = TerminalTable::Table.new do |t| t << ['a', 1] t << ['b', 2] t << ['c', 3] @@ -288,7 +288,7 @@ module Terminal end it "should render properly using instance_eval block syntax" do - table = Terminal::Table.new do + table = TerminalTable::Table.new do add_row ['a', 1] add_row ['b', 2] add_row ['c', 3] @@ -323,7 +323,7 @@ module Terminal it "should allows a hash of options for creation" do headings = ['Char', 'Num'] rows = [['a', 1], ['b', 2], ['c', 3]] - Terminal::Table.new(:rows => rows, :headings => headings).render.should eq <<-EOF.deindent + TerminalTable::Table.new(:rows => rows, :headings => headings).render.should eq <<-EOF.deindent +------+-----+ | Char | Num | +------+-----+ @@ -606,7 +606,7 @@ module Terminal #@table.style = {:border_x => "", :border_y => "", :border_i => ""} @table.style.remove_verticals # new methodology is to use remove_verticals method. @table.style.remove_horizontals # new methodology is to use remove_horizontal method. - + @table.headings = ['name', { :value => 'values', :alignment => :right, :colspan => 3}] @table.headings = ['name', { :value => 'values', :colspan => 3}] @table.rows = [['a', 1, 2, 3], ['b', 4, 5, 6], ['c', 7, 8, 9]] @@ -699,7 +699,7 @@ module Terminal └──────┴───────┘ EOF end - + it "should be able to change separators after elaboration" do @table.style = { all_separators: true } @table.title = 'Animals' @@ -756,7 +756,7 @@ module Terminal ╰──────┴───────╯ EOF end - + it "should test many separator borders" do @table.style = { border: :unicode_thick_edge } @table.title = 'Borders' @@ -806,7 +806,7 @@ module Terminal ┗━━━━━━┷━━━━━━━┛ EOF end - + it "should allow headings with no rows (issue #118.a)" do @table.headings = ['a', 'b', 'c', 'd'] @table.render.should eq <<-EOF.deindent @@ -816,7 +816,7 @@ module Terminal └───────────────┘ EOF end - + it "should allow multiple headings with no rows (issue #118.b)" do @table.headings = [['a', 'b', 'c', 'd'], ['cat','dog','frog','mouse']] @table.render.should eq <<-EOF.deindent @@ -846,6 +846,6 @@ module Terminal EOF end - + end end