tabularize 0.1.1 → 0.2.0

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