tabularize 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,8 @@
1
+ 0.2.0
2
+ -----
3
+ - Tabularize::Table class for easy tabularization (doh!)
4
+ - Correctly handles ANSI codes
5
+
1
6
  0.1.1
2
7
  -----
3
8
  - :unicode_display option added for CJK wide characters.
@@ -1,12 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tabularize (0.1.1)
4
+ tabularize (0.2.0)
5
5
  unicode-display_width (~> 0.1.1)
6
6
 
7
7
  GEM
8
8
  remote: http://rubygems.org/
9
9
  specs:
10
+ ansi (1.4.3)
10
11
  awesome_print (1.0.2)
11
12
  unicode-display_width (0.1.1)
12
13
 
@@ -15,5 +16,6 @@ PLATFORMS
15
16
  ruby
16
17
 
17
18
  DEPENDENCIES
19
+ ansi
18
20
  awesome_print
19
21
  tabularize!
data/README.md CHANGED
@@ -2,6 +2,7 @@ tabularize
2
2
  ==========
3
3
 
4
4
  Formatting tabular data with paddings.
5
+ `tabularize` correctly handles CJK wide characters and ANSI codes.
5
6
 
6
7
  Inspired by tabular.vim (https://github.com/godlygeek/tabular)
7
8
 
@@ -12,10 +13,75 @@ Installation
12
13
  gem install tabularize
13
14
  ```
14
15
 
15
- Basic usage
16
- -----------
16
+ Table generator
17
+ ---------------
17
18
 
18
- ### Formatting CSV data
19
+ ```ruby
20
+ require 'tabularize'
21
+
22
+ table = Tabularize.new
23
+ table << %w[Name Dept Location Phone]
24
+ table.separator!
25
+ table << ['John Doe', 'Finance', 'Los Angeles CA 90089', '555-1555']
26
+ table << ['Average Joe', 'Engineering', 'Somewhere over the rainbow', 'N/A']
27
+ table << ['홍길동', '탁상 3부', '서울역 3번 출구 김씨 옆자리', 'N/A']
28
+ puts table
29
+ ```
30
+
31
+ ```
32
+ +-------------+-------------+-----------------------------+----------+
33
+ | Name | Dept | Location | Phone |
34
+ +-------------+-------------+-----------------------------+----------+
35
+ | John Doe | Finance | Los Angeles CA 90089 | 555-1555 |
36
+ | Average Joe | Engineering | Somewhere over the rainbow | N/A |
37
+ | 홍길동 | 탁상 3부 | 서울역 3번 출구 김씨 옆자리 | N/A |
38
+ +-------------+-------------+-----------------------------+----------+
39
+ ```
40
+
41
+ ### With options: Padding, border and alignment
42
+
43
+ * Padding
44
+ * `:pad` Padding character
45
+ * `:pad_left` Size of left padding
46
+ * `:pad_right` Size of right padding
47
+ * Border
48
+ * `:hborder` Character for horizontal border
49
+ * `:vborder` Character for vertical border
50
+ * `:iborder` Character for intersection point
51
+ * Alignment
52
+ * `:align` Cell alignment. `:left`, `:center`, `:right`, or Array of the three options
53
+
54
+ ```ruby
55
+ table = Tabularize.new :pad => '.', :pad_left => 2, :pad_right => 0,
56
+ :hborder => '~', :vborder => 'I', :iborder => '#',
57
+ :align => [:left, :center, :right]
58
+ table << %w[Name Dept Location Phone]
59
+ table.separator!
60
+ table << ['John Doe', 'Finance', 'Los Angeles CA 90089', '555-1555']
61
+ table << ['Average Joe', 'Engineering', 'Somewhere over the rainbow', 'N/A']
62
+ table << ['홍길동', '탁상 3부', '서울역 3번 출구 김씨 옆자리', 'N/A']
63
+ puts table
64
+ ```
65
+
66
+ ```
67
+ #~~~~~~~~~~~~~#~~~~~~~~~~~~~#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#~~~~~~~~~~#
68
+ I..Name.......I.....Dept....I.....................LocationI.....PhoneI
69
+ #~~~~~~~~~~~~~#~~~~~~~~~~~~~#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#~~~~~~~~~~#
70
+ I..John Doe...I....Finance..I.........Los Angeles CA 90089I..555-1555I
71
+ I..Average JoeI..EngineeringI...Somewhere over the rainbowI.......N/AI
72
+ I..홍길동.....I...탁상 3부..I..서울역 3번 출구 김씨 옆자리I.......N/AI
73
+ #~~~~~~~~~~~~~#~~~~~~~~~~~~~#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#~~~~~~~~~~#
74
+ ```
75
+
76
+ Tabularize.it
77
+ -------------
78
+ `Tabularize` table generator is built on top of low-level `Tabularize.it` class method.
79
+ (actually it was the only method provided in the earlier versions prior to 0.2.0)
80
+
81
+ It simply returns two-dimensional array of padded and aligned Strings,
82
+ so the rest of the formatting is pretty much up to you.
83
+
84
+ ### Example: formatting CSV data
19
85
 
20
86
  #### Sample data in CSV
21
87
 
@@ -24,6 +90,7 @@ Name|Dept|Location|Phone
24
90
  John Doe|Finance|Los Angeles, CA 90089|555-1555
25
91
  Average Joe|Engineering|Somewhere over the rainbow|N/A
26
92
  Hong Gildong|HR|Nowhere|555-5555
93
+ 홍길동|탁상 3부|서울역 3번 출구 김씨 옆자리|N/A
27
94
  ```
28
95
 
29
96
  #### Tabularize.it
@@ -80,7 +147,7 @@ ap Tabularize.it(data).map { |row| row.join ' | ' }
80
147
  ```ruby
81
148
  puts Tabularize.it(data, :align => :right).map { |row| row.join ' | ' }
82
149
  puts
83
- puts Tabularize.it(data, :align => :center).map { |row| row.join ' | ' }
150
+ puts Tabularize.it(data, :align => [:left, :center]).map { |row| row.join ' | ' }
84
151
  ```
85
152
 
86
153
  ```
@@ -89,8 +156,8 @@ puts Tabularize.it(data, :align => :center).map { |row| row.join ' | ' }
89
156
  Average Joe | Engineering | Somewhere over the rainbow | N/A
90
157
  Hong Gildong | HR | Nowhere | 555-5555
91
158
 
92
- Name | Dept | Location | Phone
93
- John Doe | Finance | Los Angeles, CA 90089 | 555-1555
159
+ Name | Dept | Location | Phone
160
+ John Doe | Finance | Los Angeles, CA 90089 | 555-1555
94
161
  Average Joe | Engineering | Somewhere over the rainbow | N/A
95
162
  Hong Gildong | HR | Nowhere | 555-5555
96
163
  ```
@@ -106,20 +173,17 @@ Name________ | Dept_______ | Location__________________ | Phone___
106
173
  John Doe____ | Finance____ | Los Angeles, CA 90089_____ | 555-1555
107
174
  Average Joe_ | Engineering | Somewhere over the rainbow | N/A_____
108
175
  Hong Gildong | HR_________ | Nowhere___________________ | 555-5555
176
+ 홍길동______ | 탁상 3부___ | 서울역 3번 출구 김씨 옆자리 | N/A
109
177
  ```
110
178
 
111
- #### CJK wide characters
179
+ ANSI codes and CJK wide characters
180
+ ----------------------------------
181
+ `tabularize` correctly calculates each cell width even in the presence of ANSI codes and CJK wide characters.
182
+ If your data doesn't have any of them, unset `:unicode` and `:ansi` options
183
+ so that `tabularize` can process data more efficiently.
112
184
 
113
185
  ```ruby
114
- puts Tabularize.it(data, :unicode_display => true).map { |row| row.join ' | ' }
115
- ```
116
-
117
- ```
118
- Name | Dept | Location | Phone
119
- John Doe | Finance | Los Angeles, CA 90089 | 555-1555
120
- Average Joe | Engineering | Somewhere over the rainbow | N/A
121
- Hong Gildong | HR | Nowhere | 555-5555
122
- 홍길동 | 탁상 3부 | 서울역 3번 출구 김씨 옆자리 | N/A
186
+ table = Tabularize.new :unicode => false, :ansi => false
123
187
  ```
124
188
 
125
189
  Copyright
@@ -1,12 +1,88 @@
1
1
  require "tabularize/version"
2
+ require 'stringio'
3
+ require 'unicode/display_width'
2
4
 
3
- module Tabularize
5
+ class Tabularize
4
6
  DEFAULT_OPTIONS = {
5
- :pad => ' ',
6
- :align => :left,
7
- :unicode_display => false
7
+ :align => :left,
8
+ :pad => ' ',
9
+ :pad_left => 0,
10
+ :pad_right => 0,
11
+
12
+ :hborder => '-',
13
+ :vborder => '|',
14
+ :iborder => '+',
15
+
16
+ :unicode => true,
17
+ :ansi => true,
18
+ }
19
+
20
+ DEFAULT_OPTIONS_GENERATOR = {
21
+ :pad_left => 1,
22
+ :pad_right => 1,
8
23
  }
9
24
 
25
+ # @since 0.2.0
26
+ def initialize options = {}
27
+ @rows = []
28
+ @seps = Hash.new { |h, k| h[k] = 0 }
29
+ @options = DEFAULT_OPTIONS.
30
+ merge(DEFAULT_OPTIONS_GENERATOR).
31
+ merge(options)
32
+ end
33
+
34
+ # @since 0.2.0
35
+ def separator!
36
+ @seps[@rows.length] += 1
37
+ nil
38
+ end
39
+
40
+ # @param [Array] row
41
+ # @since 0.2.0
42
+ def << row
43
+ @rows << row
44
+ nil
45
+ end
46
+
47
+ # @return [String]
48
+ # @since 0.2.0
49
+ def to_s
50
+ rows = Tabularize.it(@rows, @options)
51
+ return nil if rows.empty?
52
+
53
+ h = @options[:hborder]
54
+ v = @options[:vborder]
55
+ i = @options[:iborder]
56
+ u = @options[:unicode]
57
+ a = @options[:ansi]
58
+
59
+ separator = i + rows[0].map { |c|
60
+ h * Tabularize.cell_width(c, u, a)
61
+ }.join( i ) + i
62
+
63
+ output = StringIO.new
64
+ output.puts separator
65
+ rows.each_with_index do |row, idx|
66
+ @seps[idx].times do
67
+ output.puts separator
68
+ end
69
+ output.puts v + row.join(v) + v
70
+ end
71
+ output.puts separator
72
+ output.string
73
+ end
74
+
75
+ # Returns the display width of a String
76
+ # @param [String] str Input String
77
+ # @param [Boolean] unicode Set to true when the given String can include CJK wide characters
78
+ # @param [Boolean] ansi Set to true When the given String can include ANSI codes
79
+ # @return [Fixnum] Display width of the given String
80
+ # @since 0.2.0
81
+ def self.cell_width str, unicode, ansi
82
+ str = str.gsub(/\e\[\d*(?:;\d+)*m/, '') if ansi
83
+ str.send(unicode ? :display_width : :length)
84
+ end
85
+
10
86
  # Formats two-dimensional tabular data.
11
87
  # One-dimensional data (e.g. Array of Strings) is treated as tabular data
12
88
  # of which each row has only one column.
@@ -19,19 +95,24 @@ module Tabularize
19
95
 
20
96
  options = DEFAULT_OPTIONS.merge(options)
21
97
  pad = options[:pad].to_s
22
- align = options[:align]
23
- unicode = options[:unicode_display]
24
- raise ArgumentError.new("Invalid padding") unless pad.length == 1
25
- raise ArgumentError.new("Invalid alignment") unless
26
- [:left, :right, :center].include?(align)
27
-
28
- l =
29
- if unicode
30
- require 'unicode/display_width'
31
- :display_width
32
- else
33
- :length
34
- end
98
+ padl = options[:pad_left]
99
+ padr = options[:pad_right]
100
+ align = [options[:align]].flatten
101
+ unicode = options[:unicode]
102
+ ansi = options[:ansi]
103
+
104
+ unless pad.length == 1
105
+ raise ArgumentError.new("Invalid padding")
106
+ end
107
+ unless padl.is_a?(Fixnum) && padl >= 0
108
+ raise ArgumentError.new(":pad_left must be a non-negative Fixnum")
109
+ end
110
+ unless padr.is_a?(Fixnum) && padr >= 0
111
+ raise ArgumentError.new(":pad_right must be a non-negative Fixnum")
112
+ end
113
+ unless align.all? { |a| [:left, :right, :center].include?(a) }
114
+ raise ArgumentError.new("Invalid alignment")
115
+ end
35
116
 
36
117
  rows = []
37
118
  max_widths = []
@@ -39,26 +120,35 @@ module Tabularize
39
120
  rows << row = [*row].map(&:to_s).map(&:chomp)
40
121
 
41
122
  row.each_with_index do |cell, idx|
42
- max_widths[idx] = [ cell.send(l), max_widths[idx] || 0 ].max
123
+ max_widths[idx] = [ Tabularize.cell_width(cell, unicode, ansi), max_widths[idx] || 0 ].max
43
124
  end
44
125
  end
45
126
 
46
127
  rows.map { |row|
47
128
  idx = -1
48
129
  row.map { |str|
130
+ alen =
131
+ if ansi
132
+ Tabularize.cell_width(str, false, false) -
133
+ Tabularize.cell_width(str, false, true)
134
+ else
135
+ 0
136
+ end
137
+ slen = str.length - alen
138
+
49
139
  idx += 1
50
140
  w = max_widths[idx]
51
- if unicode
52
- w += str.length - str.display_width
53
- end
54
- case align
55
- when :left
56
- str.ljust(w, pad)
57
- when :right
58
- str.rjust(w, pad)
59
- when :center
60
- str.rjust((w - str.length) / 2 + str.length, pad).ljust(w, pad)
61
- end
141
+ w += str.length - str.display_width if unicode
142
+ pad * padl +
143
+ case align[idx] || align.last
144
+ when :left
145
+ str.ljust(w + alen, pad)
146
+ when :right
147
+ str.rjust(w + alen, pad)
148
+ when :center
149
+ str.rjust((w - slen) / 2 + slen + alen, pad).ljust(w + alen, pad)
150
+ end +
151
+ pad * padr
62
152
  }
63
153
  }
64
154
  end
@@ -1,3 +1,3 @@
1
- module Tabularize
2
- VERSION = "0.1.1"
1
+ class Tabularize
2
+ VERSION = "0.2.0"
3
3
  end
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.require_paths = ["lib"]
20
20
 
21
21
  # specify any dependencies here; for example:
22
- s.add_development_dependency "awesome_print"
22
+ s.add_development_dependency 'awesome_print'
23
+ s.add_development_dependency 'ansi'
23
24
  s.add_runtime_dependency 'unicode-display_width', '~> 0.1.1'
24
25
  end
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require 'rubygems'
5
+ require 'ansi'
6
+
7
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
8
+ require 'tabularize'
9
+
10
+ table = Tabularize.new
11
+ table = Tabularize.new :pad => '.', :pad_left => 2, :pad_right => 0,
12
+ :hborder => '~', :vborder => 'I', :iborder => '#',
13
+ :align => [:left, :center, :right]
14
+ table << %w[Name Dept Location Phone]
15
+ table.separator!
16
+ table << ['John Doe', 'Finance', 'Los Angeles CA 90089', '555-1555']
17
+ table << ['Average Joe', 'Engineering', 'Somewhere over the rainbow', 'N/A']
18
+ table << ['홍길동', '탁상 3부', '서울역 3번 출구 김씨 옆자리', 'N/A']
19
+ puts table
@@ -6,6 +6,7 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
6
  require 'tabularize'
7
7
  require 'awesome_print'
8
8
  require 'csv'
9
+ require 'ansi'
9
10
 
10
11
  class TestTabularize < Test::Unit::TestCase
11
12
  DATA0 = %w[a aa aaa aaaa aaaaa]
@@ -104,21 +105,24 @@ class TestTabularize < Test::Unit::TestCase
104
105
  # TODO: Need assertion
105
106
  def test_tabularize_csv
106
107
  {
107
- 'test.csv' => false,
108
- 'test_unicode.csv' => true
109
- }.each do |file, unicode|
108
+ 'test.csv' => [false, false],
109
+ 'test_unicode.csv' => [true, false],
110
+ 'test_unicode_ansi.csv' => [true, true]
111
+ }.each do |file, unicode_ansi|
112
+ unicode, ansi = unicode_ansi
113
+ opts = { :unicode => unicode, :ansi => ansi }
110
114
  data = CSV.read(File.join(File.dirname(__FILE__), file), :col_sep => '|')
111
115
  ap data
112
- output = Tabularize.it(data, :unicode_display => unicode).map { |row| row.join ' | ' }
116
+ output = Tabularize.it(data, opts).map { |row| row.join '|' }
113
117
  ap output
114
118
  puts
115
119
  puts output
116
120
 
117
- puts Tabularize.it(data, :align => :right, :unicode_display => unicode).map { |row| row.join ' | ' }
121
+ puts Tabularize.it(data, opts.merge(:align => :right)).map { |row| row.join '|' }
118
122
  puts
119
- puts Tabularize.it(data, :align => :center, :unicode_display => unicode).map { |row| row.join ' | ' }
123
+ puts Tabularize.it(data, opts.merge(:align => :center)).map { |row| row.join '|' }
120
124
  puts
121
- puts Tabularize.it(data, :pad => '_', :unicode_display => unicode).map { |row| row.join ' | ' }
125
+ puts Tabularize.it(data, opts.merge(:pad => '_')).map { |row| row.join '|' }
122
126
 
123
127
  end
124
128
  end
@@ -127,6 +131,27 @@ class TestTabularize < Test::Unit::TestCase
127
131
  assert_raise(ArgumentError) { Tabularize.it(5) }
128
132
  assert_raise(ArgumentError) { Tabularize.it("hello") }
129
133
  assert_raise(ArgumentError) { Tabularize.it([1, 2, 3], :align => :noidea) }
134
+ assert_raise(ArgumentError) { Tabularize.it([1, 2, 3], :align => [:center, :top]) }
130
135
  assert_raise(ArgumentError) { Tabularize.it([1, 2, 3], :pad => 'long') }
136
+ assert_raise(ArgumentError) { Tabularize.it([1, 2, 3], :pad => ' ', :pad_left => -1) }
137
+ assert_raise(ArgumentError) { Tabularize.it([1, 2, 3], :pad => ' ', :pad_left => '') }
138
+ assert_raise(ArgumentError) { Tabularize.it([1, 2, 3], :pad => ' ', :pad_right => -1) }
139
+ assert_raise(ArgumentError) { Tabularize.it([1, 2, 3], :pad => ' ', :pad_right => '') }
140
+ end
141
+
142
+ def test_table
143
+ table = Tabularize.new :align => :center, :pad => ',',
144
+ :pad_left => 3, :pad_right => 5, :hborder => '=', :vborder => 'I', :iborder => '#'
145
+ table << DATA2[0]
146
+ table.separator!
147
+ table << DATA2[0]
148
+ table.separator!
149
+ table.separator!
150
+ table << DATA2[1]
151
+ table.separator!
152
+ table << DATA2[2]
153
+ table << DATA2[3]
154
+
155
+ puts table.to_s
131
156
  end
132
157
  end
@@ -0,0 +1,5 @@
1
+ Name|Dept|Location|Phone
2
+ John Doe|Finance|Los Angeles, CA 90089|555-1555
3
+ Average Joe|Engineering|Somewhere over the rainbow|N/A
4
+ Hong Gildong|HR|Nowhere|555-5555
5
+ 홍길동|탁상 3부|서울역 3번 출구 김씨 옆자리|N/A
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tabularize
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-14 00:00:00.000000000 Z
12
+ date: 2012-07-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: awesome_print
@@ -27,6 +27,22 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: ansi
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
30
46
  - !ruby/object:Gem::Dependency
31
47
  name: unicode-display_width
32
48
  requirement: !ruby/object:Gem::Requirement
@@ -60,9 +76,11 @@ files:
60
76
  - lib/tabularize.rb
61
77
  - lib/tabularize/version.rb
62
78
  - tabularize.gemspec
79
+ - test/readme.rb
63
80
  - test/test.csv
64
81
  - test/test_tabularize.rb
65
82
  - test/test_unicode.csv
83
+ - test/test_unicode_ansi.csv
66
84
  homepage: ''
67
85
  licenses: []
68
86
  post_install_message:
@@ -83,12 +101,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
101
  version: '0'
84
102
  requirements: []
85
103
  rubyforge_project: tabularize
86
- rubygems_version: 1.8.18
104
+ rubygems_version: 1.8.24
87
105
  signing_key:
88
106
  specification_version: 3
89
107
  summary: Formatting tabular data
90
108
  test_files:
109
+ - test/readme.rb
91
110
  - test/test.csv
92
111
  - test/test_tabularize.rb
93
112
  - test/test_unicode.csv
94
- has_rdoc:
113
+ - test/test_unicode_ansi.csv