├── .coveralls.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .travis.yml ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── kosi.gemspec ├── lib ├── kosi.rb ├── kosi │ ├── align.rb │ ├── char_option.rb │ ├── connector_char.rb │ ├── header.rb │ ├── horizontal_border_char.rb │ ├── options.rb │ ├── separate_each_row.rb │ ├── validators.rb │ ├── validators │ │ ├── each_array_length_validator.rb │ │ └── row_type_validator.rb │ ├── version.rb │ └── vertical_border_char.rb └── table.rb └── spec ├── kosi ├── align_spec.rb ├── connector_char_spec.rb ├── header_spec.rb ├── horizontal_border_char_spec.rb ├── separate_each_row_spec.rb ├── validators │ ├── each_array_length_validator_spec.rb │ └── row_type_validator_spec.rb └── vertical_border_char_spec.rb ├── spec_helper.rb └── table_spec.rb /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | *.bundle 19 | *.so 20 | *.o 21 | *.a 22 | mkmf.log 23 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format progress 3 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - !ruby/regexp /spec\/*\.rb$/ 4 | 5 | AsciiComments: 6 | Enabled: false 7 | 8 | RegexpLiteral: 9 | MaxSlashes: 0 10 | 11 | ClassLength: 12 | Max: 150 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | before_install: 3 | - gem update --system 4 | - gem update bundler 5 | - gem --version 6 | rvm: 7 | - 2.3.8 8 | - 2.4.10 9 | - 2.5.9 10 | - 2.6.10 11 | - 2.7.6 12 | - 3.0.4 13 | - 3.1.2 -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v1.3.0 2 | 2022/10/20 3 | 4 | ### Update Feature 5 | * Ruby2.6, 2.7, 3.0, 3.1をサポート 6 | * https://github.com/tbpgr/kosi/pull/6 7 | * janlelis/unicode-display_widthの警告対応 8 | * https://github.com/tbpgr/kosi/pull/7 9 | 10 | ## v1.2.0 11 | 2018/01/18 12 | 13 | ### Update Feature 14 | * Ruby2.0, 2.1, 2.2のサポートを終了 15 | * https://github.com/tbpgr/kosi/commit/8216113c810d8170743c2fe27e921870d62e9d71 16 | 17 | ## v.1.1.0 18 | 2018/01/08 19 | 20 | ### Update Feature 21 | * unicode gem の代わりに unicode-display_width と unicode-emoji を使用 by 22 | * https://github.com/tbpgr/kosi/pull/3 23 | * Ruby 2.5対応等 24 | * https://github.com/tbpgr/kosi/pull/4 25 | 26 | ## v.1.0.1 27 | 2017/08/22 28 | 29 | ### Update Feature 30 | * Gem のアップデート by pocke 31 | * https://github.com/tbpgr/kosi/pull/2 32 | 33 | ## v.1.0.0 34 | 2015/01/17 35 | 36 | ### Update Feature 37 | * Unicode の判定を unicode gem の Unicode.width に変更 38 | 39 | ## v.0.0.2 40 | 2014/06/10 41 | 42 | ### Update Feature 43 | * ANSI Escape Sequence利用時にフォーマットが崩れないように変更 44 | 45 | ## v.0.0.1 46 | 2014/06/09 47 | 48 | * first release 49 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 tbpgr 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kosi 2 | 3 | [![Gem Version](https://badge.fury.io/rb/kosi.svg)](http://badge.fury.io/rb/kosi) 4 | [![Gem Downloads](https://img.shields.io/gem/dt/kosi.svg)](http://badge.fury.io/rb/kosi) 5 | [![Build Status](https://travis-ci.org/tbpgr/kosi.png?branch=master)](https://travis-ci.org/tbpgr/kosi) 6 | [![Coverage Status](https://coveralls.io/repos/tbpgr/kosi/badge.png)](https://coveralls.io/r/tbpgr/kosi) 7 | [![Code Climate](https://codeclimate.com/github/tbpgr/kosi.png)](https://codeclimate.com/github/tbpgr/kosi) 8 | 9 | ターミナルアプリケーション用表フォーマットサポートツール。格子。 10 | 11 | ## :notes: Images 12 | ### :baby_chick: Before 13 | 14 | :rage1::rage1::rage1:,:alien:,:older_woman::older_woman: 15 | :older_man:,:dog:,:baby::baby::baby::baby::baby::baby::baby: 16 | :cat::cat:,:octocat::octocat::octocat::octocat::octocat::octocat::octocat:,:octopus::octopus::octopus::octopus::octopus: 17 | 18 | ### :chicken: After 19 | +----------+--------------------------+--------------------------+ 20 | |:rage1::rage1::rage1:|:alien::grey_question::grey_question::grey_question::grey_question::grey_question::grey_question:|:older_woman::older_woman::grey_question::grey_question::grey_question::grey_question::grey_question:| 21 | |:older_man::grey_question::grey_question:|:dog::grey_question::grey_question::grey_question::grey_question::grey_question::grey_question:|:baby::baby::baby::baby::baby::baby::baby:| 22 | |:cat::cat::grey_question:|:octocat::octocat::octocat::octocat::octocat::octocat::octocat:|:octopus::octopus::octopus::octopus::octopus::grey_question::grey_question:| 23 | +----------+--------------------------+--------------------------+ 24 | 25 | ## :cloud::arrow_down: Installation 26 | 27 | Add this line to your application's Gemfile: 28 | 29 | gem 'kosi' 30 | 31 | And then execute: 32 | 33 | $ bundle 34 | 35 | Or install it yourself as: 36 | 37 | $ gem install kosi 38 | 39 | ## :man: Description 40 | 2次元配列をテーブルフォーマットにして出力します。 41 | :link: [terminal-table gem](https://github.com/tj/terminal-table) の日本語対応版にあたります。 42 | (terminal-tableはASCII対応のみなので全角文字が混ざるとテーブルレイアウトが崩れる) 43 | 44 | ## :o2: Options 45 | ### :icecream: Align 46 | 配置指定。右寄せ、左寄せ、中央を選択可能。 47 | 48 | | 設定可能パラメータ | 説明 | 49 | |:-----------|:-----------| 50 | |Kosi::Align::TYPE::RIGHT|右寄せ| 51 | |Kosi::Align::TYPE::LEFT|左寄せ。デフォルト| 52 | |Kosi::Align::TYPE::CENTER|中央| 53 | 54 | ### :icecream: ConnectorChar 55 | 表の結合部に表示するテキスト。1文字で指定。 56 | 下記で言うところの 「+」がConnectorChar。 57 | 58 | ~~~ 59 | +-----+------+-------+ 60 | |a |b |c | 61 | +-----+------+-------+ 62 | ~~~ 63 | 64 | ### :icecream: Header 65 | 表のヘッダー。配列で指定。 66 | デフォルトはヘッダーなし。 67 | 68 | ### :icecream: HorizontalBorderChar 69 | 水平線を1文字で設定。 70 | デフォルトは「-」(半角ハイフン) 71 | 72 | ### :icecream: SeparateEachRow 73 | 各行に区切り線を入れるかどうか。 74 | デフォルトは「false」 75 | 76 | ### :icecream: VerticalBorderChar 77 | 垂直線を1文字で設定。 78 | デフォルトは「|」(パイプ) 79 | 80 | ## :scroll: Usage 81 | ### :shaved_ice: オプション指定なし 82 | ~~~ruby 83 | require 'kosi' 84 | 85 | kosi = Kosi::Table.new 86 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333']]) 87 | ~~~ 88 | 89 | * 出力 90 | ※GitHubの表示上ずれているかもしれませんが、等幅フォント利用時にそろいます。 91 | 92 | ~~~ 93 | +-----+------+-------+ 94 | |a |b |c | 95 | |ほゲ1|ひゲ22|へゲ333| 96 | +-----+------+-------+ 97 | ~~~ 98 | 99 | ### :shaved_ice: Align指定 100 | ~~~ruby 101 | require 'kosi' 102 | 103 | kosi = Kosi::Table.new({align: Kosi::Align::TYPE::CENTER}) 104 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333']]) 105 | kosi = Kosi::Table.new({align: Kosi::Align::TYPE::RIGHT}) 106 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333']]) 107 | kosi = Kosi::Table.new({align: Kosi::Align::TYPE::LEFT}) 108 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333']]) 109 | ~~~ 110 | 111 | * 出力 112 | 113 | ~~~ 114 | +-----+------+-------+ 115 | | a | b | c | 116 | |ほゲ1|ひゲ22|へゲ333| 117 | +-----+------+-------+ 118 | +-----+------+-------+ 119 | | a| b| c| 120 | |ほゲ1|ひゲ22|へゲ333| 121 | +-----+------+-------+ 122 | +-----+------+-------+ 123 | |a |b |c | 124 | |ほゲ1|ひゲ22|へゲ333| 125 | +-----+------+-------+ 126 | ~~~ 127 | 128 | ### :shaved_ice: ConnectorChar指定 129 | ~~~ruby 130 | require 'kosi' 131 | 132 | kosi = Kosi::Table.new 133 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333']]) 134 | kosi = Kosi::Table.new({connector_char: 'x'}) 135 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333']]) 136 | kosi = Kosi::Table.new({connector_char: '$'}) 137 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333']]) 138 | ~~~ 139 | 140 | * 出力 141 | 142 | ~~~ 143 | +-----+------+-------+ 144 | |a |b |c | 145 | |ほゲ1|ひゲ22|へゲ333| 146 | +-----+------+-------+ 147 | x-----x------x-------x 148 | |a |b |c | 149 | |ほゲ1|ひゲ22|へゲ333| 150 | x-----x------x-------x 151 | $-----$------$-------$ 152 | |a |b |c | 153 | |ほゲ1|ひゲ22|へゲ333| 154 | $-----$------$-------$ 155 | ~~~ 156 | 157 | 158 | ### :shaved_ice: Header指定 159 | ~~~ruby 160 | require 'kosi' 161 | 162 | kosi = Kosi::Table.new 163 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333']]) 164 | kosi = Kosi::Table.new({header: %w{column1 column2 column3}}) 165 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333']]) 166 | ~~~ 167 | 168 | 169 | * 出力 170 | 171 | ~~~ 172 | +-----+------+-------+ 173 | |a |b |c | 174 | |ほゲ1|ひゲ22|へゲ333| 175 | +-----+------+-------+ 176 | +-------+-------+-------+ 177 | |column1|column2|column3| 178 | +-------+-------+-------+ 179 | |a |b |c | 180 | |ほゲ1 |ひゲ22 |へゲ333| 181 | +-------+-------+-------+ 182 | ~~~ 183 | 184 | ### :shaved_ice: HorizontalBorderChar指定 185 | ~~~ruby 186 | require 'kosi' 187 | 188 | kosi = Kosi::Table.new 189 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333']]) 190 | kosi = Kosi::Table.new({horizontal_border_char: '*'}) 191 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333']]) 192 | ~~~ 193 | 194 | * 出力 195 | 196 | ~~~ 197 | +-----+------+-------+ 198 | |a |b |c | 199 | |ほゲ1|ひゲ22|へゲ333| 200 | +-----+------+-------+ 201 | +*****+******+*******+ 202 | |a |b |c | 203 | |ほゲ1|ひゲ22|へゲ333| 204 | +*****+******+*******+ 205 | ~~~ 206 | 207 | ### :shaved_ice: SeparateEachRow指定 208 | ~~~ruby 209 | require 'kosi' 210 | 211 | kosi = Kosi::Table.new 212 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333'], [*'a'..'c']]) 213 | kosi = Kosi::Table.new({separate_each_row: true}) 214 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333'], [*'a'..'c']]) 215 | ~~~ 216 | 217 | * 出力 218 | 219 | ~~~ 220 | +-----+------+-------+ 221 | |a |b |c | 222 | |ほゲ1|ひゲ22|へゲ333| 223 | |a |b |c | 224 | +-----+------+-------+ 225 | +-----+------+-------+ 226 | |a |b |c | 227 | +-----+------+-------+ 228 | |ほゲ1|ひゲ22|へゲ333| 229 | +-----+------+-------+ 230 | |a |b |c | 231 | +-----+------+-------+ 232 | ~~~ 233 | 234 | ### :shaved_ice: VerticalBorderChar指定 235 | ~~~ruby 236 | require 'kosi' 237 | 238 | kosi = Kosi::Table.new 239 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333']]) 240 | kosi = Kosi::Table.new({vertical_border_char: '#'}) 241 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333']]) 242 | ~~~ 243 | 244 | * 出力 245 | 246 | ~~~ 247 | +-----+------+-------+ 248 | |a |b |c | 249 | |ほゲ1|ひゲ22|へゲ333| 250 | +-----+------+-------+ 251 | +-----+------+-------+ 252 | #a #b #c # 253 | #ほゲ1#ひゲ22#へゲ333# 254 | +-----+------+-------+ 255 | ~~~ 256 | 257 | ### :shaved_ice: 複合オプション 258 | 様々なオプションを一気に指定してみます 259 | 260 | ~~~ruby 261 | require 'kosi' 262 | 263 | kosi = Kosi::Table.new( 264 | { 265 | align: Kosi::Align::TYPE::CENTER, 266 | connector_char: 'x', 267 | header: %w{column1 column2 column3}, 268 | horizontal_border_char: '*', 269 | vertical_border_char: '#', 270 | separate_each_row: true 271 | } 272 | ) 273 | print kosi.render([[*'a'..'c'], ['ほゲ1', 'ひゲ22', 'へゲ333'], [*'a'..'c']]) 274 | ~~~ 275 | 276 | * 出力 277 | 278 | ~~~ 279 | x*******x*******x*******x 280 | #column1#column2#column3# 281 | x*******x*******x*******x 282 | # a # b # c # 283 | x*******x*******x*******x 284 | # ほゲ1 #ひゲ22 #へゲ333# 285 | x*******x*******x*******x 286 | # a # b # c # 287 | x*******x*******x*******x 288 | ~~~ 289 | 290 | ## :two_men_holding_hands: Contributing :two_women_holding_hands: 291 | 292 | 1. Fork it ( https://github.com/tbpgr/kosi/fork ) 293 | 2. Create your feature branch (`git checkout -b my-new-feature`) 294 | 3. Commit your changes (`git commit -am 'Add some feature'`) 295 | 4. Push to the branch (`git push origin my-new-feature`) 296 | 5. Create a new Pull Request 297 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rspec/core' 3 | require 'rspec/core/rake_task' 4 | task default: [:spec] 5 | 6 | RSpec::Core::RakeTask.new(:spec) do |spec| 7 | spec.pattern = 'spec/**/*_spec.rb' 8 | end 9 | -------------------------------------------------------------------------------- /kosi.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'kosi/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'kosi' 8 | spec.version = Kosi::VERSION 9 | spec.authors = ['tbpgr'] 10 | spec.email = ['tbpgr@tbpgr.jp'] 11 | spec.summary = %q(terminal table format for japanese) 12 | spec.description = %q(terminal table format for japanese) 13 | spec.homepage = 'https://github.com/tbpgr/kosi' 14 | spec.license = 'MIT' 15 | 16 | spec.files = `git ls-files -z`.split("\x0") 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ['lib'] 20 | 21 | spec.required_ruby_version = '>= 2.3' 22 | 23 | spec.add_runtime_dependency 'unicode-display_width' 24 | spec.add_runtime_dependency 'unicode-emoji' 25 | spec.add_development_dependency 'bundler' 26 | spec.add_development_dependency 'rake' 27 | spec.add_development_dependency 'rspec', '~> 3.6.0' 28 | spec.add_development_dependency 'simplecov', '~> 0.14.1' 29 | spec.add_development_dependency 'coveralls' 30 | end 31 | -------------------------------------------------------------------------------- /lib/kosi.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'kosi/align' 3 | require 'kosi/connector_char' 4 | require 'kosi/header' 5 | require 'kosi/horizontal_border_char' 6 | require 'kosi/options' 7 | require 'kosi/separate_each_row' 8 | require 'kosi/vertical_border_char' 9 | require 'table' 10 | 11 | # TableFormat for Terminal(Use Japanese Characters) 12 | module Kosi 13 | end 14 | -------------------------------------------------------------------------------- /lib/kosi/align.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'kosi/options' 3 | 4 | # TableFormat for Terminal(Use Japanese Characters) 5 | module Kosi 6 | # Align 7 | class Align 8 | attr_reader :value 9 | # Align Type 10 | module TYPE 11 | CENTER = :center 12 | RIGHT = :right 13 | LEFT = :left 14 | DEFAULT = LEFT 15 | end 16 | 17 | def initialize(options) 18 | @value = options[OptionKeys::ALIGN] || TYPE::DEFAULT 19 | end 20 | 21 | def apply(text, max_value, diff) 22 | pos = max_value - diff 23 | case @value 24 | when TYPE::CENTER 25 | text.center(pos) 26 | when TYPE::RIGHT 27 | text.rjust(pos) 28 | when TYPE::LEFT 29 | text.ljust(pos) 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/kosi/char_option.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'kosi/options' 3 | 4 | # TableFormat for Terminal(Use Japanese Characters) 5 | module Kosi 6 | # CharOption 7 | class CharOption 8 | attr_reader :value 9 | 10 | def initialize(options) 11 | @value = String(options[key]) 12 | @value = default if @value.empty? 13 | unless @value.size == 1 14 | fail ArgumentError, format(invalid_char_msg, @value.size, @value) 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/kosi/connector_char.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'kosi/options' 3 | require 'kosi/char_option' 4 | 5 | # TableFormat for Terminal(Use Japanese Characters) 6 | module Kosi 7 | # ConnectorChar 8 | class ConnectorChar < CharOption 9 | DEFAULT = '+' 10 | INVALID_CHAR_MSG = \ 11 | 'connector char must be 1 length(%d) char %s' 12 | 13 | def default 14 | DEFAULT 15 | end 16 | 17 | def key 18 | OptionKeys::CONNECTOR_CHAR 19 | end 20 | 21 | private 22 | 23 | def invalid_char_msg 24 | INVALID_CHAR_MSG 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/kosi/header.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'kosi/options' 3 | 4 | # TableFormat for Terminal(Use Japanese Characters) 5 | module Kosi 6 | # Header 7 | class Header 8 | attr_reader :value 9 | 10 | def initialize(options) 11 | @value = options[OptionKeys::HEADER] 12 | end 13 | 14 | def empty? 15 | @value.nil? 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/kosi/horizontal_border_char.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'kosi/options' 3 | require 'kosi/char_option' 4 | 5 | # TableFormat for Terminal(Use Japanese Characters) 6 | module Kosi 7 | # HorizontalBorderChar 8 | class HorizontalBorderChar < CharOption 9 | DEFAULT = '-' 10 | INVALID_CHAR_MSG = \ 11 | 'horizontal_border_char must be 1 length(%d) char %s' 12 | 13 | def default 14 | DEFAULT 15 | end 16 | 17 | def key 18 | OptionKeys::HORIZONTAL_BORDER_CHAR 19 | end 20 | 21 | private 22 | 23 | def invalid_char_msg 24 | INVALID_CHAR_MSG 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/kosi/options.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # TableFormat for Terminal(Use Japanese Characters) 4 | module Kosi 5 | # Options 6 | module OptionKeys 7 | ALIGN = :align 8 | HEADER = :header 9 | HAS_HEADER = :has_header 10 | CONNECTOR_CHAR = :connector_char 11 | HORIZONTAL_BORDER_CHAR = :horizontal_border_char 12 | VERTICAL_BORDER_CHAR = :vertical_border_char 13 | SEPARATE_EACH_ROW = :separate_each_row 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/kosi/separate_each_row.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'kosi/options' 3 | 4 | # TableFormat for Terminal(Use Japanese Characters) 5 | module Kosi 6 | # SeparateEachRow 7 | class SeparateEachRow 8 | attr_reader :value 9 | DEFAULT = false 10 | 11 | def initialize(options) 12 | @value = options[OptionKeys::SEPARATE_EACH_ROW] || DEFAULT 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/kosi/validators.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'kosi/validators/each_array_length_validator' 3 | require 'kosi/validators/row_type_validator' 4 | -------------------------------------------------------------------------------- /lib/kosi/validators/each_array_length_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Kosi 4 | module Kosi 5 | # Validators 6 | module Validators 7 | # EachArrayLength Validator 8 | # if row class is not Array, fail ArgumentError. 9 | class EachArrayLength 10 | INVALID_ARRAY_LENGTH_MESSAGE = \ 11 | 'invalid array length.each array must be same length' 12 | def self.validate(row) 13 | row_sizes = row.map(&:size) 14 | return if row_sizes.uniq.size == 0 15 | unless row_sizes.uniq.size == 1 16 | fail ArgumentError, INVALID_ARRAY_LENGTH_MESSAGE 17 | end 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/kosi/validators/row_type_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Kosi 4 | module Kosi 5 | # Validators 6 | module Validators 7 | # RowType Validator 8 | # if row class is not Array, fail ArgumentError. 9 | class RowType 10 | INVALID_ROW_MESSAGE = 'invalid row class %s. row class must be Array.' 11 | def self.validate(row) 12 | unless row.is_a?(Array) 13 | message = format(INVALID_ROW_MESSAGE, row.class) 14 | fail ArgumentError, message 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/kosi/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # TableFormat for Terminal(Use Japanese Characters) 3 | module Kosi 4 | VERSION = '1.3.0' 5 | end 6 | -------------------------------------------------------------------------------- /lib/kosi/vertical_border_char.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'kosi/options' 3 | require 'kosi/char_option' 4 | 5 | # TableFormat for Terminal(Use Japanese Characters) 6 | module Kosi 7 | # VerticalBorderChar 8 | class VerticalBorderChar < CharOption 9 | DEFAULT = '|' 10 | INVALID_CHAR_MSG = \ 11 | 'vertical_border_char must be 1 length(%d) char %s' 12 | 13 | def default 14 | DEFAULT 15 | end 16 | 17 | def key 18 | OptionKeys::VERTICAL_BORDER_CHAR 19 | end 20 | 21 | private 22 | 23 | def invalid_char_msg 24 | INVALID_CHAR_MSG 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/table.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'kosi' 3 | require 'kosi/validators' 4 | require 'unicode/display_width' 5 | require 'unicode/emoji' 6 | 7 | # TableFormat for Terminal(Use Japanese Characters) 8 | module Kosi 9 | # Table 10 | class Table 11 | attr_reader :align, :header, :connector_char, :horizontal_border_char, 12 | :vertical_border_char, :separate_each_row 13 | 14 | def initialize(options = {}) 15 | @align = Kosi::Align.new(options) 16 | @header = Kosi::Header.new(options) 17 | @connector_char = Kosi::ConnectorChar.new(options) 18 | @horizontal_border_char = Kosi::HorizontalBorderChar.new(options) 19 | @vertical_border_char = Kosi::VerticalBorderChar.new(options) 20 | @separate_each_row = Kosi::SeparateEachRow.new(options) 21 | end 22 | 23 | def render(rows) 24 | check_rows_type(rows) 25 | check_each_array_length(rows) 26 | header = read_header 27 | escaped_rows = escape_ansi_escape_sequence(rows) 28 | max_lengths = get_max_lengths(escaped_rows, header) 29 | table_format_rows = get_table_format_rows(rows, max_lengths, header) 30 | table_format_rows.join("\n") + "\n" 31 | end 32 | 33 | private 34 | 35 | def check_rows_type(rows) 36 | Validators::RowType.validate(rows) 37 | rows.each { |row|Validators::RowType.validate(row) } 38 | end 39 | 40 | def check_each_array_length(rows) 41 | Validators::EachArrayLength.validate(rows) 42 | end 43 | 44 | def get_max_lengths(rows, header) 45 | tmp_rows = rows.dup 46 | tmp_rows << header unless @header.empty? 47 | tmp_rows.reduce([]) do |max_lengths, row| 48 | row.each_with_index do |e, i| 49 | s = ascii1_other2_size(String(e)) 50 | next unless greater?(s, max_lengths[i]) 51 | max_lengths[i] = s 52 | end 53 | max_lengths 54 | end 55 | end 56 | 57 | def greater?(one, other) 58 | return true if other.nil? 59 | one > other 60 | end 61 | 62 | def ascii1_other2_size(column) 63 | column.split('').reduce(0) do |a, e| 64 | a += Unicode::DisplayWidth.of(e, 1, {}, emoji: true) 65 | a 66 | end 67 | end 68 | 69 | def get_table_format_rows(rows, max_lengths, header) 70 | top_bottom_line = top_bottom(max_lengths) 71 | results = [top_bottom_line] 72 | append_header(results, header, max_lengths, top_bottom_line) 73 | rows.each do |col| 74 | results += formated_row(col, max_lengths, top_bottom_line) 75 | end 76 | results << top_bottom_line unless @separate_each_row.value 77 | results 78 | end 79 | 80 | def formated_row(col, max_lengths, top_bottom_line) 81 | results = [] 82 | row_results = get_columns(col, max_lengths) 83 | virtical = @vertical_border_char.value 84 | results << "#{virtical}#{row_results.join(virtical)}#{virtical}" 85 | results << top_bottom_line if @separate_each_row.value 86 | results 87 | end 88 | 89 | def append_header(results, header, max_lengths, top_bottom_line) 90 | unless @header.empty? 91 | results << formated_header(header, max_lengths) 92 | results << top_bottom_line 93 | end 94 | end 95 | 96 | def top_bottom(max_lengths) 97 | results = max_lengths.reduce([]) do |ret, column_size| 98 | ret << @horizontal_border_char.value * column_size 99 | ret 100 | end 101 | con = @connector_char.value 102 | "#{con}#{results.join(con)}#{con}" 103 | end 104 | 105 | def read_header 106 | return nil if @header.empty? 107 | @header.value 108 | end 109 | 110 | def escape_ansi_escape_sequence(rows) 111 | rows.reduce([]) do |r, e| 112 | r << e.map do |f|f.to_s.gsub(/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/, '') 113 | .gsub(/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/, '') 114 | .gsub(/(\x03|\x1a)/, '') 115 | end 116 | r 117 | end 118 | end 119 | 120 | def formated_header(header, max_lengths) 121 | header_results = [] 122 | header.size.times do |i| 123 | column = String(header[i]) 124 | diff = ascii1_other2_size(column) - column.size 125 | header_results << @align.apply(column, max_lengths[i], diff) 126 | end 127 | virtical = @vertical_border_char.value 128 | header_line = header_results.join(virtical) 129 | "#{virtical}#{header_line}#{virtical}" 130 | end 131 | 132 | def get_columns(columns, max_lengths) 133 | column_results = [] 134 | columns.size.times do |i| 135 | column = String(columns[i]) 136 | diff = ascii1_other2_size(column) - column.size 137 | column_results << @align.apply(column, max_lengths[i], diff) 138 | end 139 | column_results 140 | end 141 | end 142 | end 143 | -------------------------------------------------------------------------------- /spec/kosi/align_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | require 'kosi' 4 | 5 | describe Kosi::Align do 6 | context :apply do 7 | cases = [ 8 | { 9 | case_no: 1, 10 | case_title: 'no options case', 11 | options: {}, 12 | text: 'h', 13 | max_value: 3, 14 | diff: 0, 15 | expected_option: Kosi::Align::TYPE::LEFT, 16 | expected: 'h ' 17 | }, 18 | { 19 | case_no: 2, 20 | case_title: 'align options right case', 21 | options: { Kosi::OptionKeys::ALIGN => Kosi::Align::TYPE::RIGHT }, 22 | text: 'h', 23 | max_value: 3, 24 | diff: 0, 25 | expected_option: Kosi::Align::TYPE::RIGHT, 26 | expected: ' h' 27 | }, 28 | { 29 | case_no: 3, 30 | case_title: 'align options left case', 31 | options: { Kosi::OptionKeys::ALIGN => Kosi::Align::TYPE::LEFT }, 32 | text: 'h', 33 | max_value: 3, 34 | diff: 0, 35 | expected_option: Kosi::Align::TYPE::LEFT, 36 | expected: 'h ' 37 | }, 38 | { 39 | case_no: 4, 40 | case_title: 'align options center case', 41 | options: { Kosi::OptionKeys::ALIGN => Kosi::Align::TYPE::CENTER }, 42 | text: 'h', 43 | max_value: 3, 44 | diff: 0, 45 | expected_option: Kosi::Align::TYPE::CENTER, 46 | expected: ' h ' 47 | } 48 | ] 49 | 50 | cases.each do |c| 51 | it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do 52 | begin 53 | case_before c 54 | 55 | # -- given -- 56 | header = Kosi::Align.new(c[:options]) 57 | 58 | # -- when -- 59 | actual = header.apply(c[:text], c[:max_value], c[:diff]) 60 | 61 | # -- then -- 62 | expect(actual).to eq(c[:expected]) 63 | ensure 64 | case_after c 65 | end 66 | end 67 | 68 | def case_before(c) 69 | # implement each case before 70 | end 71 | 72 | def case_after(c) 73 | # implement each case after 74 | end 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /spec/kosi/connector_char_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'kosi' 3 | 4 | describe Kosi::ConnectorChar do 5 | context :value do 6 | cases = [ 7 | { 8 | case_no: 1, 9 | case_title: 'no options case', 10 | options: {}, 11 | expected: Kosi::ConnectorChar::DEFAULT 12 | }, 13 | { 14 | case_no: 2, 15 | case_title: 'valid connector char options case', 16 | options: { Kosi::OptionKeys::CONNECTOR_CHAR => '@' }, 17 | expected: '@' 18 | }, 19 | { 20 | case_no: 3, 21 | case_title: 'invalid connector char options case', 22 | options: { Kosi::OptionKeys::CONNECTOR_CHAR => '++' }, 23 | expect_error: true 24 | } 25 | ] 26 | 27 | cases.each do |c| 28 | it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do 29 | begin 30 | case_before c 31 | 32 | # -- given -- 33 | if c[:expect_error] 34 | expect { Kosi::ConnectorChar.new(c[:options]) } 35 | .to raise_error(ArgumentError) 36 | next 37 | end 38 | connector_char = Kosi::ConnectorChar.new(c[:options]) 39 | 40 | # -- when -- 41 | actual = connector_char.value 42 | 43 | # -- then -- 44 | expect(actual).to eq(c[:expected]) 45 | ensure 46 | case_after c 47 | end 48 | end 49 | 50 | def case_before(c) 51 | # implement each case before 52 | end 53 | 54 | def case_after(c) 55 | # implement each case after 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/kosi/header_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | require 'kosi' 4 | 5 | describe Kosi::Header do 6 | context :value do 7 | cases = [ 8 | { 9 | case_no: 1, 10 | case_title: 'no options case', 11 | options: {}, 12 | expected: nil 13 | }, 14 | { 15 | case_no: 2, 16 | case_title: 'align options left case', 17 | options: { Kosi::OptionKeys::HEADER => %w(header1 header2) }, 18 | expected: %w(header1 header2) 19 | } 20 | ] 21 | 22 | cases.each do |c| 23 | it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do 24 | begin 25 | case_before c 26 | 27 | # -- given -- 28 | header = Kosi::Header.new(c[:options]) 29 | 30 | # -- when -- 31 | actual = header.value 32 | 33 | # -- then -- 34 | expect(actual).to eq(c[:expected]) 35 | ensure 36 | case_after c 37 | end 38 | end 39 | 40 | def case_before(c) 41 | # implement each case before 42 | end 43 | 44 | def case_after(c) 45 | # implement each case after 46 | end 47 | end 48 | end 49 | 50 | context :empty? do 51 | cases = [ 52 | { 53 | case_no: 1, 54 | case_title: 'no options case', 55 | options: {}, 56 | expected: true 57 | }, 58 | { 59 | case_no: 2, 60 | case_title: 'align options left case', 61 | options: { Kosi::OptionKeys::HEADER => %w(header1 header2) }, 62 | expected: false 63 | } 64 | ] 65 | 66 | cases.each do |c| 67 | it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do 68 | begin 69 | case_before c 70 | 71 | # -- given -- 72 | header = Kosi::Header.new(c[:options]) 73 | 74 | # -- when -- 75 | actual = header.empty? 76 | 77 | # -- then -- 78 | expect(actual).to eq(c[:expected]) 79 | ensure 80 | case_after c 81 | end 82 | end 83 | 84 | def case_before(c) 85 | # implement each case before 86 | end 87 | 88 | def case_after(c) 89 | # implement each case after 90 | end 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /spec/kosi/horizontal_border_char_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'kosi' 3 | 4 | describe Kosi::HorizontalBorderChar do 5 | context :value do 6 | cases = [ 7 | { 8 | case_no: 1, 9 | case_title: 'no options case', 10 | options: {}, 11 | expected: Kosi::HorizontalBorderChar::DEFAULT 12 | }, 13 | { 14 | case_no: 2, 15 | case_title: 'valid horizontal_border_char options case', 16 | options: { Kosi::OptionKeys::HORIZONTAL_BORDER_CHAR => '@' }, 17 | expected: '@' 18 | }, 19 | { 20 | case_no: 3, 21 | case_title: 'invalid horizontal_border_char options case', 22 | options: { Kosi::OptionKeys::HORIZONTAL_BORDER_CHAR => '--' }, 23 | expect_error: true 24 | } 25 | ] 26 | 27 | cases.each do |c| 28 | it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do 29 | begin 30 | case_before c 31 | 32 | # -- given -- 33 | if c[:expect_error] 34 | expect { Kosi::HorizontalBorderChar.new(c[:options]) } 35 | .to raise_error(ArgumentError) 36 | next 37 | end 38 | horizontal_border_char = Kosi::HorizontalBorderChar.new(c[:options]) 39 | 40 | # -- when -- 41 | actual = horizontal_border_char.value 42 | 43 | # -- then -- 44 | expect(actual).to eq(c[:expected]) 45 | ensure 46 | case_after c 47 | end 48 | end 49 | 50 | def case_before(c) 51 | # implement each case before 52 | end 53 | 54 | def case_after(c) 55 | # implement each case after 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/kosi/separate_each_row_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | require 'kosi' 4 | 5 | describe Kosi::SeparateEachRow do 6 | context :value do 7 | cases = [ 8 | { 9 | case_no: 1, 10 | case_title: 'no options case', 11 | options: {}, 12 | expected: Kosi::SeparateEachRow::DEFAULT 13 | }, 14 | { 15 | case_no: 2, 16 | case_title: 'seprate_each_row options true case', 17 | options: { Kosi::OptionKeys::SEPARATE_EACH_ROW => true }, 18 | expected: true 19 | }, 20 | { 21 | case_no: 3, 22 | case_title: 'seprate_each_row options false case', 23 | options: { Kosi::OptionKeys::SEPARATE_EACH_ROW => false }, 24 | expected: false 25 | } 26 | ] 27 | 28 | cases.each do |c| 29 | it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do 30 | begin 31 | case_before c 32 | 33 | # -- given -- 34 | separate_each_row = Kosi::SeparateEachRow.new(c[:options]) 35 | 36 | # -- when -- 37 | actual = separate_each_row.value 38 | 39 | # -- then -- 40 | expect(actual).to eq(c[:expected]) 41 | ensure 42 | case_after c 43 | end 44 | end 45 | 46 | def case_before(c) 47 | # implement each case before 48 | end 49 | 50 | def case_after(c) 51 | # implement each case after 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/kosi/validators/each_array_length_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | require 'kosi/validators/each_array_length_validator' 4 | 5 | describe Kosi::Validators::EachArrayLength do 6 | context :validate do 7 | cases = [ 8 | { 9 | case_no: 1, 10 | case_title: 'Same Length Array case', 11 | inputs: [[*1..5], [*6..10]], 12 | }, 13 | { 14 | case_no: 2, 15 | case_title: 'Zero Length Array case', 16 | inputs: [[], []], 17 | }, 18 | { 19 | case_no: 3, 20 | case_title: 'Different Length Array case', 21 | inputs: [[*1..5], [*6..11]], 22 | expect_error: true, 23 | error_class: ArgumentError 24 | }, 25 | ] 26 | 27 | cases.each do |c| 28 | it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do 29 | begin 30 | case_before c 31 | 32 | # -- given -- 33 | # nothing 34 | 35 | # -- when/then -- 36 | if c[:expect_error] 37 | expect { Kosi::Validators::EachArrayLength.validate(c[:inputs]) }.to raise_error(c[:error_class]) 38 | next 39 | else 40 | expect { Kosi::Validators::EachArrayLength.validate(c[:inputs]) }.not_to raise_error 41 | end 42 | ensure 43 | case_after c 44 | end 45 | end 46 | 47 | def case_before(c) 48 | # implement each case before 49 | end 50 | 51 | def case_after(c) 52 | # implement each case after 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /spec/kosi/validators/row_type_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | require 'kosi/validators/row_type_validator' 4 | 5 | describe Kosi::Validators::RowType do 6 | context :validate do 7 | cases = [ 8 | { 9 | case_no: 1, 10 | case_title: 'Array case', 11 | inputs: ['Array'], 12 | }, 13 | { 14 | case_no: 2, 15 | case_title: 'Not Array case', 16 | inputs: 'not Array', 17 | expect_error: true, 18 | error_class: ArgumentError 19 | }, 20 | ] 21 | 22 | cases.each do |c| 23 | it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do 24 | begin 25 | case_before c 26 | 27 | # -- given -- 28 | # nothing 29 | 30 | # -- when/then -- 31 | if c[:expect_error] 32 | expect { Kosi::Validators::RowType.validate(c[:inputs]) }.to raise_error(c[:error_class]) 33 | next 34 | else 35 | expect { Kosi::Validators::RowType.validate(c[:inputs]) }.not_to raise_error 36 | end 37 | ensure 38 | case_after c 39 | end 40 | end 41 | 42 | def case_before(c) 43 | # implement each case before 44 | end 45 | 46 | def case_after(c) 47 | # implement each case after 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /spec/kosi/vertical_border_char_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'kosi' 3 | 4 | describe Kosi::VerticalBorderChar do 5 | context :value do 6 | cases = [ 7 | { 8 | case_no: 1, 9 | case_title: 'no options case', 10 | options: {}, 11 | expected: Kosi::VerticalBorderChar::DEFAULT 12 | }, 13 | { 14 | case_no: 2, 15 | case_title: 'valid vertical_border_char options case', 16 | options: { Kosi::OptionKeys::VERTICAL_BORDER_CHAR => '@' }, 17 | expected: '@' 18 | }, 19 | { 20 | case_no: 3, 21 | case_title: 'invalid vertical_border_char options case', 22 | options: { Kosi::OptionKeys::VERTICAL_BORDER_CHAR => '||' }, 23 | expect_error: true 24 | } 25 | ] 26 | 27 | cases.each do |c| 28 | it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do 29 | begin 30 | case_before c 31 | 32 | # -- given -- 33 | if c[:expect_error] 34 | expect { Kosi::VerticalBorderChar.new(c[:options]) } 35 | .to raise_error(ArgumentError) 36 | next 37 | end 38 | vertical_border_char = Kosi::VerticalBorderChar.new(c[:options]) 39 | 40 | # -- when -- 41 | actual = vertical_border_char.value 42 | 43 | # -- then -- 44 | expect(actual).to eq(c[:expected]) 45 | ensure 46 | case_after c 47 | end 48 | end 49 | 50 | def case_before(c) 51 | # implement each case before 52 | end 53 | 54 | def case_after(c) 55 | # implement each case after 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'simplecov' 3 | require 'coveralls' 4 | Coveralls.wear! 5 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ 6 | SimpleCov::Formatter::HTMLFormatter, 7 | Coveralls::SimpleCov::Formatter 8 | ]) 9 | SimpleCov.start do 10 | add_filter '/spec/' 11 | end 12 | RSpec.configure do |config| 13 | config.run_all_when_everything_filtered = true 14 | config.filter_run :focus 15 | end 16 | -------------------------------------------------------------------------------- /spec/table_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | require 'kosi' 4 | 5 | describe Kosi::Table do 6 | context :initialize do 7 | cases = [ 8 | { 9 | case_no: 1, 10 | case_title: 'no options case', 11 | options: nil, 12 | attr_name: '@align', 13 | expected: Kosi::Align::TYPE::LEFT 14 | }, 15 | { 16 | case_no: 2, 17 | case_title: 'align options left case', 18 | options: { Kosi::OptionKeys::ALIGN => Kosi::Align::TYPE::RIGHT }, 19 | attr_name: '@align', 20 | expected: Kosi::Align::TYPE::RIGHT 21 | } 22 | ] 23 | 24 | cases.each do |c| 25 | it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do 26 | begin 27 | case_before c 28 | 29 | # -- given -- 30 | kosi_table = \ 31 | if c[:options] 32 | Kosi::Table.new(c[:options]) 33 | else 34 | Kosi::Table.new 35 | end 36 | 37 | # -- when -- 38 | actual = kosi_table.instance_variable_get(c[:attr_name]).value 39 | 40 | # -- then -- 41 | expect(actual).to eq(c[:expected]) 42 | ensure 43 | case_after c 44 | end 45 | end 46 | 47 | def case_before(c) 48 | # implement each case before 49 | end 50 | 51 | def case_after(c) 52 | # implement each case after 53 | end 54 | end 55 | end 56 | 57 | context :render do 58 | cases = [ 59 | { 60 | case_no: 1, 61 | case_title: 'valid case', 62 | options: nil, 63 | inputs: [[1, 2, 3], [1111, '222アア', '33イイ']], 64 | expected: <<-EOS 65 | +----+-------+------+ 66 | |1 |2 |3 | 67 | |1111|222アア|33イイ| 68 | +----+-------+------+ 69 | EOS 70 | }, 71 | { 72 | case_no: 2, 73 | case_title: 'use header case', 74 | options: { header: %w(header1 header2 header3) }, 75 | inputs: [[1, 2, 3], [1111, '222アア', '33イイ']], 76 | expected: <<-EOS 77 | +-------+-------+-------+ 78 | |header1|header2|header3| 79 | +-------+-------+-------+ 80 | |1 |2 |3 | 81 | |1111 |222アア|33イイ | 82 | +-------+-------+-------+ 83 | EOS 84 | }, 85 | { 86 | case_no: 3, 87 | case_title: 'Enable SeparateEachRow case', 88 | options: { separate_each_row: true }, 89 | inputs: [[1, 2, 3], [1111, '222アア', '33イイ'], [1, 2, 3]], 90 | expected: <<-EOS 91 | +----+-------+------+ 92 | |1 |2 |3 | 93 | +----+-------+------+ 94 | |1111|222アア|33イイ| 95 | +----+-------+------+ 96 | |1 |2 |3 | 97 | +----+-------+------+ 98 | EOS 99 | }, 100 | { 101 | case_no: 4, 102 | case_title: 'use ANSI Escape sequence case', 103 | options: nil, 104 | inputs: [["\e[31mhello\e[0m"]], 105 | expected: <<-EOS 106 | +-----+ 107 | |\e[31mhello\e[0m| 108 | +-----+ 109 | EOS 110 | }, 111 | { 112 | case_no: 5, 113 | case_title: 'Unicode width case', 114 | options: nil, 115 | inputs: [['$100'], ['¥100']], 116 | expected: <<-EOS 117 | +----+ 118 | |$100| 119 | |¥100| 120 | +----+ 121 | EOS 122 | }, 123 | ] 124 | 125 | cases.each do |c| 126 | it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do 127 | begin 128 | case_before c 129 | 130 | # -- given -- 131 | kosi_table = \ 132 | if c[:options] 133 | Kosi::Table.new(c[:options]) 134 | else 135 | Kosi::Table.new 136 | end 137 | 138 | # -- when -- 139 | actual = kosi_table.render(c[:inputs]) 140 | 141 | # -- then -- 142 | expect(actual).to eq(c[:expected]) 143 | ensure 144 | case_after c 145 | end 146 | end 147 | 148 | def case_before(c) 149 | # implement each case before 150 | end 151 | 152 | def case_after(c) 153 | # implement each case after 154 | end 155 | end 156 | end 157 | end 158 | --------------------------------------------------------------------------------