tinytable 0.1.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.
- data/.gitignore +20 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +129 -0
- data/Rakefile +7 -0
- data/lib/tinytable.rb +10 -0
- data/lib/tinytable/cell.rb +24 -0
- data/lib/tinytable/constants.rb +5 -0
- data/lib/tinytable/layout.rb +61 -0
- data/lib/tinytable/row.rb +37 -0
- data/lib/tinytable/table.rb +79 -0
- data/lib/tinytable/text_formatter.rb +109 -0
- data/lib/tinytable/version.rb +3 -0
- data/spec/integration/integration_spec.rb +141 -0
- data/spec/tinytable/cell_spec.rb +24 -0
- data/spec/tinytable/layout_spec.rb +51 -0
- data/spec/tinytable/row_spec.rb +24 -0
- data/spec/tinytable/table_spec.rb +129 -0
- data/spec/tinytable/text_formatter_spec.rb +103 -0
- data/tinytable.gemspec +20 -0
- metadata +93 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Leo Cassarani
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# TinyTable
|
2
|
+
|
3
|
+
TinyTable is a Ruby gem that lets you create and output simple ASCII tables with minimal effort. It draws heavy inspiration from [Ruport](https://github.com/ruport/ruport) but, unlike Ruport, it has a very simple and focused API, and no external dependencies.
|
4
|
+
|
5
|
+
TinyTable is particularly well-suited to formatting the results of a Rake task or test suite. The output of `rake stats` is a perfect example of the kind of thing you might choose to use TinyTable for.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'tinytable'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install tinytable
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Create a `TinyTable::Table` and start adding rows to it:
|
24
|
+
|
25
|
+
table = TinyTable::Table.new
|
26
|
+
table << ["Controllers", 169, 140]
|
27
|
+
table << ["Helpers", 48, 43]
|
28
|
+
table << ["Models", 149, 120]
|
29
|
+
puts table.to_text
|
30
|
+
|
31
|
+
This will output:
|
32
|
+
|
33
|
+
+-------------+-----+-----+
|
34
|
+
| Controllers | 169 | 140 |
|
35
|
+
| Helpers | 48 | 43 |
|
36
|
+
| Models | 149 | 120 |
|
37
|
+
+-------------+-----+-----+
|
38
|
+
|
39
|
+
By default, columns are left-aligned. However, you can specify the text alignment of each column by using its index (starting from 0):
|
40
|
+
|
41
|
+
table.align(0, TinyTable::CENTER)
|
42
|
+
table.align(1, TinyTable::RIGHT)
|
43
|
+
table.align(2, TinyTable::LEFT)
|
44
|
+
|
45
|
+
Output:
|
46
|
+
|
47
|
+
+-------------+-----+-----+
|
48
|
+
| Controllers | 169 | 140 |
|
49
|
+
| Helpers | 48 | 43 |
|
50
|
+
| Models | 149 | 120 |
|
51
|
+
+-------------+-----+-----+
|
52
|
+
|
53
|
+
### Headers and Footers
|
54
|
+
|
55
|
+
Tables can have headers and footers:
|
56
|
+
|
57
|
+
table = TinyTable::Table.new
|
58
|
+
|
59
|
+
table.header = %w[Name Lines LOC]
|
60
|
+
table << ["Controllers", 169, 140]
|
61
|
+
table << ["Helpers", 48, 43]
|
62
|
+
table.footer = ["Total", 217, 183]
|
63
|
+
|
64
|
+
table.align(1, TinyTable::RIGHT)
|
65
|
+
table.align(2, TinyTable::RIGHT)
|
66
|
+
|
67
|
+
puts table.to_text
|
68
|
+
|
69
|
+
Output:
|
70
|
+
|
71
|
+
+-------------+-------+-----+
|
72
|
+
| Name | Lines | LOC |
|
73
|
+
+-------------+-------+-----+
|
74
|
+
| Controllers | 169 | 140 |
|
75
|
+
| Helpers | 48 | 43 |
|
76
|
+
+-------------+-------+-----+
|
77
|
+
| Total | 217 | 183 |
|
78
|
+
+-------------+-------+-----+
|
79
|
+
|
80
|
+
Header titles can also be passed into the constructor for the table.
|
81
|
+
|
82
|
+
When a table has a header, new rows can be added as hashes, and columns can be referred to by their header title when specifying text alignment:
|
83
|
+
|
84
|
+
table = TinyTable::Table.new("City", "County")
|
85
|
+
table.add "City" => "London", "County" => "Greater London"
|
86
|
+
table.add "County" => "Yorkshire", "City" => "Sheffield"
|
87
|
+
table.align("County", TinyTable::RIGHT)
|
88
|
+
puts table.to_text
|
89
|
+
|
90
|
+
Output:
|
91
|
+
|
92
|
+
+-----------+----------------+
|
93
|
+
| City | County |
|
94
|
+
+-----------+----------------+
|
95
|
+
| London | Greater London |
|
96
|
+
| Sheffield | Yorkshire |
|
97
|
+
+-----------+----------------+
|
98
|
+
|
99
|
+
### Partial Rows
|
100
|
+
|
101
|
+
Rows can contain empty cells. When supplying an array, you can use `nil` to skip a column, or you can simply supply a smaller array if you want to skip the columns at the end. When using the hash syntax with header titles, just omit the value for the keys you wish to skip.
|
102
|
+
|
103
|
+
table = TinyTable::Table.new("City", "County")
|
104
|
+
table.add [nil, "Shropshire"]
|
105
|
+
table.add ["Sheffield"]
|
106
|
+
table.add "City" => "Bristol"
|
107
|
+
puts table.to_text
|
108
|
+
|
109
|
+
Output:
|
110
|
+
|
111
|
+
+-----------+------------+
|
112
|
+
| City | County |
|
113
|
+
+-----------+------------+
|
114
|
+
| | Shropshire |
|
115
|
+
| Sheffield | |
|
116
|
+
| Bristol | |
|
117
|
+
+-----------+------------+
|
118
|
+
|
119
|
+
## Contributing
|
120
|
+
|
121
|
+
1. Fork it
|
122
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
123
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
124
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
125
|
+
5. Create new Pull Request
|
126
|
+
|
127
|
+
## Copyright
|
128
|
+
|
129
|
+
Copyright (c) 2012 Leo Cassarani. See [LICENSE](https://github.com/leocassarani/tinytable/blob/master/LICENSE) for details.
|
data/Rakefile
ADDED
data/lib/tinytable.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module TinyTable
|
2
|
+
class Cell
|
3
|
+
attr_reader :alignment
|
4
|
+
|
5
|
+
def initialize(text, alignment)
|
6
|
+
@text = text || ''
|
7
|
+
@alignment = alignment
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(obj)
|
11
|
+
return false unless obj.is_a? Cell
|
12
|
+
text == obj.text &&
|
13
|
+
alignment == obj.alignment
|
14
|
+
end
|
15
|
+
|
16
|
+
def text
|
17
|
+
@text.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def width
|
21
|
+
text.length
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module TinyTable
|
2
|
+
class Layout
|
3
|
+
attr_reader :column_count, :max_column_widths
|
4
|
+
|
5
|
+
def initialize(table)
|
6
|
+
@table = table
|
7
|
+
end
|
8
|
+
|
9
|
+
def analyze
|
10
|
+
@column_count = 0
|
11
|
+
@max_column_widths = []
|
12
|
+
|
13
|
+
analyze_header
|
14
|
+
analyze_rows
|
15
|
+
analyze_footer
|
16
|
+
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def analyze_header
|
23
|
+
if @table.has_header?
|
24
|
+
analyze_row(@table.header)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def analyze_rows
|
29
|
+
if @table.has_rows?
|
30
|
+
@table.each_row do |row|
|
31
|
+
analyze_row(row)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def analyze_footer
|
37
|
+
if @table.has_footer?
|
38
|
+
analyze_row(@table.footer)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def analyze_row(row)
|
43
|
+
if row.cell_count > @column_count
|
44
|
+
@column_count = row.cell_count
|
45
|
+
end
|
46
|
+
|
47
|
+
row.each_cell_with_index do |cell, i|
|
48
|
+
analyze_cell(cell, i)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def analyze_cell(cell, i)
|
53
|
+
@max_column_widths[i] ||= 0
|
54
|
+
max_width = @max_column_widths[i]
|
55
|
+
|
56
|
+
if cell.width > max_width
|
57
|
+
@max_column_widths[i] = cell.width
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'tinytable/constants'
|
3
|
+
|
4
|
+
module TinyTable
|
5
|
+
class Row
|
6
|
+
extend Forwardable
|
7
|
+
def_delegators :@cells, :empty?, :map
|
8
|
+
|
9
|
+
attr_reader :cells
|
10
|
+
|
11
|
+
def initialize(cells, alignments = [])
|
12
|
+
@cells = cells || []
|
13
|
+
@alignments = alignments
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(obj)
|
17
|
+
return false unless obj.is_a? Row
|
18
|
+
@cells == obj.cells
|
19
|
+
end
|
20
|
+
|
21
|
+
def cell_count
|
22
|
+
@cells.count
|
23
|
+
end
|
24
|
+
|
25
|
+
def each_cell_with_index(&block)
|
26
|
+
@cells.length.times do |idx|
|
27
|
+
block.call(cell_at(idx), idx)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def cell_at(idx)
|
32
|
+
text = @cells.fetch(idx, '')
|
33
|
+
alignment = @alignments[idx] || LEFT
|
34
|
+
Cell.new(text, alignment)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module TinyTable
|
2
|
+
class Table
|
3
|
+
attr_writer :header, :footer
|
4
|
+
attr_reader :rows
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
header = args.first.is_a?(Array) ? args.first : args
|
8
|
+
@header = header
|
9
|
+
@rows = []
|
10
|
+
@alignments = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(*args)
|
14
|
+
row = extract_row_args(args)
|
15
|
+
rows << row
|
16
|
+
end
|
17
|
+
alias_method :<<, :add
|
18
|
+
|
19
|
+
def align(column, alignment)
|
20
|
+
idx = case column
|
21
|
+
when String
|
22
|
+
@header.index(column)
|
23
|
+
when Fixnum
|
24
|
+
column
|
25
|
+
else
|
26
|
+
raise ArgumentError.new("Received a #{column.class} but expecting a Fixnum or String")
|
27
|
+
end
|
28
|
+
@alignments[idx] = alignment unless idx.nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_header?
|
32
|
+
!(header.nil? || header.empty?)
|
33
|
+
end
|
34
|
+
|
35
|
+
def has_rows?
|
36
|
+
!rows.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_footer?
|
40
|
+
!(footer.nil? || footer.empty?)
|
41
|
+
end
|
42
|
+
|
43
|
+
def header
|
44
|
+
Row.new(@header, @alignments)
|
45
|
+
end
|
46
|
+
|
47
|
+
def each_row(&block)
|
48
|
+
rows.each do |cells|
|
49
|
+
row = Row.new(cells, @alignments)
|
50
|
+
block.call(row)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def footer
|
55
|
+
Row.new(@footer, @alignments)
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_text
|
59
|
+
TinyTable::TextFormatter.new(self).render
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def extract_row_args(args)
|
65
|
+
case args.first
|
66
|
+
when Array
|
67
|
+
args.first
|
68
|
+
when Hash
|
69
|
+
if has_header?
|
70
|
+
header.map { |key| args.first[key] }
|
71
|
+
else
|
72
|
+
raise ArgumentError.new("Received a Hash but no header row was given")
|
73
|
+
end
|
74
|
+
else
|
75
|
+
args
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require File.expand_path('../constants', __FILE__)
|
2
|
+
|
3
|
+
module TinyTable
|
4
|
+
class TextFormatter
|
5
|
+
CORNER = '+'
|
6
|
+
VERTICAL = '|'
|
7
|
+
HORIZONTAL = '-'
|
8
|
+
PADDING = ' '
|
9
|
+
|
10
|
+
STRING_ALIGN = {
|
11
|
+
LEFT => :ljust,
|
12
|
+
CENTER => :center,
|
13
|
+
RIGHT => :rjust
|
14
|
+
}
|
15
|
+
|
16
|
+
def initialize(table)
|
17
|
+
@table = table
|
18
|
+
@output = ''
|
19
|
+
end
|
20
|
+
|
21
|
+
def render
|
22
|
+
clear_output
|
23
|
+
precompute_table_layout
|
24
|
+
|
25
|
+
render_header
|
26
|
+
render_rows
|
27
|
+
render_footer
|
28
|
+
|
29
|
+
@output
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def render_header
|
35
|
+
if @table.has_header?
|
36
|
+
hr
|
37
|
+
render_row(@table.header)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def render_rows
|
42
|
+
if @table.has_rows?
|
43
|
+
hr
|
44
|
+
@table.each_row { |row| render_row(row) }
|
45
|
+
hr
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def render_footer
|
50
|
+
if @table.has_footer?
|
51
|
+
render_row(@table.footer)
|
52
|
+
hr
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def hr
|
57
|
+
append CORNER
|
58
|
+
@column_widths.each do |w|
|
59
|
+
append HORIZONTAL * (w + 2)
|
60
|
+
append CORNER
|
61
|
+
end
|
62
|
+
new_line
|
63
|
+
end
|
64
|
+
|
65
|
+
def render_row(row)
|
66
|
+
append VERTICAL
|
67
|
+
|
68
|
+
@column_count.times do |i|
|
69
|
+
cell = row.cell_at(i)
|
70
|
+
render_cell(cell, i)
|
71
|
+
end
|
72
|
+
|
73
|
+
new_line
|
74
|
+
end
|
75
|
+
|
76
|
+
def render_cell(cell, i)
|
77
|
+
append PADDING
|
78
|
+
|
79
|
+
method = STRING_ALIGN[cell.alignment]
|
80
|
+
text = cell.text.__send__(method, @column_widths[i], PADDING)
|
81
|
+
append text
|
82
|
+
|
83
|
+
append PADDING
|
84
|
+
append VERTICAL
|
85
|
+
end
|
86
|
+
|
87
|
+
def append(text)
|
88
|
+
@output << text.to_s
|
89
|
+
end
|
90
|
+
|
91
|
+
def new_line
|
92
|
+
append "\n"
|
93
|
+
end
|
94
|
+
|
95
|
+
def clear_output
|
96
|
+
if RUBY_VERSION < "1.9"
|
97
|
+
@output = ''
|
98
|
+
else
|
99
|
+
@output.clear
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def precompute_table_layout
|
104
|
+
layout = Layout.new(@table).analyze
|
105
|
+
@column_count = layout.column_count
|
106
|
+
@column_widths = layout.max_column_widths
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require File.expand_path('../../../lib/tinytable', __FILE__)
|
2
|
+
|
3
|
+
describe "a tiny table" do
|
4
|
+
it "supports tables with no header or footer" do
|
5
|
+
table = TinyTable::Table.new
|
6
|
+
table << ["London", "Greater London"]
|
7
|
+
table << ["Birmingham", "West Midlands"]
|
8
|
+
table << ["Manchester", "Greater Manchester"]
|
9
|
+
table.to_text.should == <<-EOF
|
10
|
+
+------------+--------------------+
|
11
|
+
| London | Greater London |
|
12
|
+
| Birmingham | West Midlands |
|
13
|
+
| Manchester | Greater Manchester |
|
14
|
+
+------------+--------------------+
|
15
|
+
EOF
|
16
|
+
end
|
17
|
+
|
18
|
+
it "supports a header" do
|
19
|
+
table = TinyTable::Table.new(%w[City County])
|
20
|
+
table << ["Liverpool", "Merseyside"]
|
21
|
+
table << ["Newcastle", "Tyne & Wear"]
|
22
|
+
table << ["Nottingham", "Nottinghamshire"]
|
23
|
+
table.to_text.should == <<-EOF
|
24
|
+
+------------+-----------------+
|
25
|
+
| City | County |
|
26
|
+
+------------+-----------------+
|
27
|
+
| Liverpool | Merseyside |
|
28
|
+
| Newcastle | Tyne & Wear |
|
29
|
+
| Nottingham | Nottinghamshire |
|
30
|
+
+------------+-----------------+
|
31
|
+
EOF
|
32
|
+
end
|
33
|
+
|
34
|
+
it "supports a footer" do
|
35
|
+
table = TinyTable::Table.new
|
36
|
+
table << ["Londoners", 8_294_058]
|
37
|
+
table << ["Brummies", 2_293_099]
|
38
|
+
table << ["Mancunians", 1_741_961]
|
39
|
+
table.footer = ["Total", 12_329_118]
|
40
|
+
table.to_text.should == <<-EOF
|
41
|
+
+------------+----------+
|
42
|
+
| Londoners | 8294058 |
|
43
|
+
| Brummies | 2293099 |
|
44
|
+
| Mancunians | 1741961 |
|
45
|
+
+------------+----------+
|
46
|
+
| Total | 12329118 |
|
47
|
+
+------------+----------+
|
48
|
+
EOF
|
49
|
+
end
|
50
|
+
|
51
|
+
it "supports both a header and a footer" do
|
52
|
+
table = TinyTable::Table.new
|
53
|
+
table.header = %w[City County Population]
|
54
|
+
table << ["London", "Greater London", 8_294_058]
|
55
|
+
table << ["Birmingham", "West Midlands", 2_293_099]
|
56
|
+
table << ["Manchester", "Greater Manchester", 1_741_961]
|
57
|
+
table.footer = ["Total", nil, 12_329_118]
|
58
|
+
table.to_text.should == <<-EOF
|
59
|
+
+------------+--------------------+------------+
|
60
|
+
| City | County | Population |
|
61
|
+
+------------+--------------------+------------+
|
62
|
+
| London | Greater London | 8294058 |
|
63
|
+
| Birmingham | West Midlands | 2293099 |
|
64
|
+
| Manchester | Greater Manchester | 1741961 |
|
65
|
+
+------------+--------------------+------------+
|
66
|
+
| Total | | 12329118 |
|
67
|
+
+------------+--------------------+------------+
|
68
|
+
EOF
|
69
|
+
end
|
70
|
+
|
71
|
+
it "returns an empty string if the table is completely empty" do
|
72
|
+
TinyTable::Table.new.to_text.should == ''
|
73
|
+
end
|
74
|
+
|
75
|
+
it "correctly handles rows with fewer cells than the rest" do
|
76
|
+
table = TinyTable::Table.new("City", "County", "Mayor")
|
77
|
+
table.add "London", "Greater London", "Boris Johnson"
|
78
|
+
table.add "Sheffield", "Yorkshire"
|
79
|
+
table.add "Bristol"
|
80
|
+
table.to_text.should == <<-EOF
|
81
|
+
+-----------+----------------+---------------+
|
82
|
+
| City | County | Mayor |
|
83
|
+
+-----------+----------------+---------------+
|
84
|
+
| London | Greater London | Boris Johnson |
|
85
|
+
| Sheffield | Yorkshire | |
|
86
|
+
| Bristol | | |
|
87
|
+
+-----------+----------------+---------------+
|
88
|
+
EOF
|
89
|
+
end
|
90
|
+
|
91
|
+
it "supports an alternative syntax based on argument lists" do
|
92
|
+
table = TinyTable::Table.new("City", "County", "Population")
|
93
|
+
table.add "London", "Greater London", 8_294_058
|
94
|
+
table.add "Birmingham", "West Midlands", 2_293_099
|
95
|
+
table.add "Manchester", "Greater Manchester", 1_741_961
|
96
|
+
table.footer = "Total", nil, 12_329_118
|
97
|
+
table.to_text.should == <<-EOF
|
98
|
+
+------------+--------------------+------------+
|
99
|
+
| City | County | Population |
|
100
|
+
+------------+--------------------+------------+
|
101
|
+
| London | Greater London | 8294058 |
|
102
|
+
| Birmingham | West Midlands | 2293099 |
|
103
|
+
| Manchester | Greater Manchester | 1741961 |
|
104
|
+
+------------+--------------------+------------+
|
105
|
+
| Total | | 12329118 |
|
106
|
+
+------------+--------------------+------------+
|
107
|
+
EOF
|
108
|
+
end
|
109
|
+
|
110
|
+
it "supports an alternative syntax based on hashes" do
|
111
|
+
table = TinyTable::Table.new("City", "County", "Mayor")
|
112
|
+
table.add "City" => "London", "Mayor" => "Boris Johnson", "County" => "Greater London"
|
113
|
+
table.add "City" => "Sheffield", "County" => "Yorkshire"
|
114
|
+
table.add "Mayor" => "Peter Soulsby", "City" => "Leicester"
|
115
|
+
table.to_text.should == <<-EOF
|
116
|
+
+-----------+----------------+---------------+
|
117
|
+
| City | County | Mayor |
|
118
|
+
+-----------+----------------+---------------+
|
119
|
+
| London | Greater London | Boris Johnson |
|
120
|
+
| Sheffield | Yorkshire | |
|
121
|
+
| Leicester | | Peter Soulsby |
|
122
|
+
+-----------+----------------+---------------+
|
123
|
+
EOF
|
124
|
+
end
|
125
|
+
|
126
|
+
it "allows the user to specify the alignment of column text" do
|
127
|
+
table = TinyTable::Table.new("City", "County", "Population")
|
128
|
+
table.align("City", TinyTable::CENTER)
|
129
|
+
table.align("Population", TinyTable::RIGHT)
|
130
|
+
table.add "London", "Greater London", 8_294_058
|
131
|
+
table.add "Sheffield", "Yorkshire", 2_293_099
|
132
|
+
table.to_text.should == <<-EOF
|
133
|
+
+-----------+----------------+------------+
|
134
|
+
| City | County | Population |
|
135
|
+
+-----------+----------------+------------+
|
136
|
+
| London | Greater London | 8294058 |
|
137
|
+
| Sheffield | Yorkshire | 2293099 |
|
138
|
+
+-----------+----------------+------------+
|
139
|
+
EOF
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path('../../../lib/tinytable/cell', __FILE__)
|
2
|
+
require File.expand_path('../../../lib/tinytable/constants', __FILE__)
|
3
|
+
|
4
|
+
describe TinyTable::Cell do
|
5
|
+
it "converts numbers to strings" do
|
6
|
+
TinyTable::Cell.new(123, TinyTable::LEFT).text.should == "123"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "returns an empty string if its text was initialized as nil" do
|
10
|
+
TinyTable::Cell.new(nil, TinyTable::LEFT).text.should == ''
|
11
|
+
end
|
12
|
+
|
13
|
+
it "is equal to another cell with same (string) text and alignment" do
|
14
|
+
a1 = TinyTable::Cell.new(:a, TinyTable::LEFT)
|
15
|
+
a2 = TinyTable::Cell.new('a', TinyTable::LEFT)
|
16
|
+
a1.should == a2
|
17
|
+
end
|
18
|
+
|
19
|
+
it "is different from another cell with different text or alignment" do
|
20
|
+
a = TinyTable::Cell.new('a', TinyTable::LEFT)
|
21
|
+
b = TinyTable::Cell.new('b', TinyTable::LEFT)
|
22
|
+
a.should_not == b
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.expand_path('../../../lib/tinytable/layout', __FILE__)
|
2
|
+
require File.expand_path('../../../lib/tinytable/row', __FILE__)
|
3
|
+
require File.expand_path('../../../lib/tinytable/cell', __FILE__)
|
4
|
+
|
5
|
+
describe TinyTable::Layout do
|
6
|
+
let(:table) { mock(:table, :has_header? => false, :has_rows? => true, :has_footer? => false) }
|
7
|
+
let(:layout) { TinyTable::Layout.new(table).analyze }
|
8
|
+
|
9
|
+
it "knows how many columns a table has" do
|
10
|
+
row1 = TinyTable::Row.new(["One"])
|
11
|
+
row2 = TinyTable::Row.new(["One", "Two"])
|
12
|
+
row3 = TinyTable::Row.new(["One", "Two", "Three"])
|
13
|
+
table.stub(:each_row).and_yield(row1).and_yield(row2).and_yield(row3)
|
14
|
+
layout.column_count.should == 3
|
15
|
+
end
|
16
|
+
|
17
|
+
it "knows the maximum width for each column in the table" do
|
18
|
+
row1 = TinyTable::Row.new(["123", "1", "12"])
|
19
|
+
row2 = TinyTable::Row.new(["12345", "123", "1"])
|
20
|
+
row3 = TinyTable::Row.new(["1234567", "12", "1"])
|
21
|
+
table.stub(:each_row).and_yield(row1).and_yield(row2).and_yield(row3)
|
22
|
+
layout.max_column_widths.should == [7, 3, 2]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "includes the header row when calculating maximum column widths" do
|
26
|
+
table.stub(:has_header?) { true }
|
27
|
+
table.stub(:header) { TinyTable::Row.new(["12345", "1"]) }
|
28
|
+
table.stub(:each_row).and_yield(TinyTable::Row.new(["123", "12"]))
|
29
|
+
layout.max_column_widths.should == [5, 2]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "includes the footer row when calculating maximum column widths" do
|
33
|
+
table.stub(:has_footer?) { true }
|
34
|
+
table.stub(:footer) { TinyTable::Row.new(["123", "12345"]) }
|
35
|
+
table.stub(:each_row).and_yield(TinyTable::Row.new(["1234", "12"]))
|
36
|
+
layout.max_column_widths.should == [4, 5]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "correctly calculates the width of cells containing an integer" do
|
40
|
+
row1 = TinyTable::Row.new(["London", 8_294_058])
|
41
|
+
row2 = TinyTable::Row.new(["Liverpool", 830_112])
|
42
|
+
table.stub(:each_row).and_yield(row1).and_yield(row2)
|
43
|
+
layout.max_column_widths.should == [9, 7]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "correctly deals with completely empty tables" do
|
47
|
+
table.stub(:has_rows?) { false }
|
48
|
+
layout.max_column_widths.should == []
|
49
|
+
layout.column_count.should be_zero
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path('../../../lib/tinytable/row', __FILE__)
|
2
|
+
require File.expand_path('../../../lib/tinytable/cell', __FILE__)
|
3
|
+
|
4
|
+
describe TinyTable::Row do
|
5
|
+
it "returns cells with the correct alignment" do
|
6
|
+
row = TinyTable::Row.new(['a', 'b'], [TinyTable::RIGHT, TinyTable::LEFT])
|
7
|
+
row.cell_at(0).should == TinyTable::Cell.new('a', TinyTable::RIGHT)
|
8
|
+
row.cell_at(1).should == TinyTable::Cell.new('b', TinyTable::LEFT)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "defaults to left alignment unless otherwise specified" do
|
12
|
+
row = TinyTable::Row.new(['a'])
|
13
|
+
row.cell_at(0).should == TinyTable::Cell.new('a', TinyTable::LEFT)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "knows if it's empty" do
|
17
|
+
TinyTable::Row.new([]).should be_empty
|
18
|
+
TinyTable::Row.new(['a']).should_not be_empty
|
19
|
+
end
|
20
|
+
|
21
|
+
it "is equal to another row with the same cells" do
|
22
|
+
TinyTable::Row.new(['a']).should == TinyTable::Row.new(['a'])
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require File.expand_path('../../../lib/tinytable/table', __FILE__)
|
2
|
+
require File.expand_path('../../../lib/tinytable/row', __FILE__)
|
3
|
+
require File.expand_path('../../../lib/tinytable/cell', __FILE__)
|
4
|
+
|
5
|
+
module TinyTable
|
6
|
+
class TextFormatter ; end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe TinyTable::Table do
|
10
|
+
let(:table) { TinyTable::Table.new }
|
11
|
+
let(:row) { %w[Liverpool Merseyside] }
|
12
|
+
|
13
|
+
it "stores and recalls rows" do
|
14
|
+
table << row
|
15
|
+
table.rows.should == [row]
|
16
|
+
end
|
17
|
+
|
18
|
+
it "exposes an iterator over the rows" do
|
19
|
+
row_obj = mock(:row_obj)
|
20
|
+
TinyTable::Row.stub(:new).with(row, anything) { row_obj }
|
21
|
+
table << row
|
22
|
+
table.each_row do |r|
|
23
|
+
r.should == row_obj
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "knows whether it contains any rows" do
|
28
|
+
table.should_not have_rows
|
29
|
+
table << row
|
30
|
+
table.should have_rows
|
31
|
+
end
|
32
|
+
|
33
|
+
it "uses a TextFormatter to render an ASCII representation of the table" do
|
34
|
+
formatter = mock(:formatter, :render => "ASCII table")
|
35
|
+
TinyTable::TextFormatter.stub(:new).with(table) { formatter }
|
36
|
+
table.to_text.should == "ASCII table"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "stores and recalls a header row" do
|
40
|
+
table.header = ["City", "County"]
|
41
|
+
table.header.should == TinyTable::Row.new(["City", "County"])
|
42
|
+
end
|
43
|
+
|
44
|
+
it "doesn't return the header along with regular rows" do
|
45
|
+
table.header = ["City", "County"]
|
46
|
+
table << row
|
47
|
+
table.rows.should == [row]
|
48
|
+
end
|
49
|
+
|
50
|
+
it "stores and recalls a footer row" do
|
51
|
+
table.footer = ["Total", "123.45"]
|
52
|
+
table.footer.should == TinyTable::Row.new(["Total", "123.45"])
|
53
|
+
end
|
54
|
+
|
55
|
+
it "doesn't return the footer along with regular rows" do
|
56
|
+
table << row
|
57
|
+
table.footer = ["Total", "123.45"]
|
58
|
+
table.rows.should == [row]
|
59
|
+
end
|
60
|
+
|
61
|
+
it "supports a number of different ways to add new rows" do
|
62
|
+
table.add "London", "Greater London"
|
63
|
+
table.add ["Birmingham", "West Midlands"]
|
64
|
+
table << ["Manchester", "Greater Manchester"]
|
65
|
+
table.rows.should == [
|
66
|
+
["London", "Greater London"],
|
67
|
+
["Birmingham", "West Midlands"],
|
68
|
+
["Manchester", "Greater Manchester"]
|
69
|
+
]
|
70
|
+
end
|
71
|
+
|
72
|
+
it "supports a number of different ways to set the header" do
|
73
|
+
table.header = ["City", "County"]
|
74
|
+
table.header.should == TinyTable::Row.new(["City", "County"])
|
75
|
+
|
76
|
+
table.header = "City", "County"
|
77
|
+
table.header.should == TinyTable::Row.new(["City", "County"])
|
78
|
+
|
79
|
+
table = TinyTable::Table.new("City", "County")
|
80
|
+
table.header.should == TinyTable::Row.new(["City", "County"])
|
81
|
+
|
82
|
+
table = TinyTable::Table.new ["City", "County"]
|
83
|
+
table.header.should == TinyTable::Row.new(["City", "County"])
|
84
|
+
end
|
85
|
+
|
86
|
+
it "supports a number of different ways to set the footer" do
|
87
|
+
table.footer = ["Total", "300"]
|
88
|
+
table.footer.should == TinyTable::Row.new(["Total", "300"])
|
89
|
+
|
90
|
+
table.footer = "Total", "300"
|
91
|
+
table.footer.should == TinyTable::Row.new(["Total", "300"])
|
92
|
+
end
|
93
|
+
|
94
|
+
it "allows a row to be entered as a hash with header titles as its keys" do
|
95
|
+
table.header = "City", "County", "Population"
|
96
|
+
table.add 'City' => "Reading", 'Population' => 373_836, 'County' => "Berkshire"
|
97
|
+
table << { 'County' => "Hampshire", 'City' => "Winchester" }
|
98
|
+
table.rows.should == [
|
99
|
+
["Reading", "Berkshire", 373_836],
|
100
|
+
["Winchester", "Hampshire", nil]
|
101
|
+
]
|
102
|
+
end
|
103
|
+
|
104
|
+
it "raises an ArgumentError if a hash is given without a header row" do
|
105
|
+
lambda {
|
106
|
+
table.add 'City' => "Reading", 'County' => "Berkshire"
|
107
|
+
}.should raise_error(ArgumentError)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "allows the user to specify the alignment for a column" do
|
111
|
+
table.header = "City"
|
112
|
+
table.align("City", TinyTable::RIGHT)
|
113
|
+
table << ["London"]
|
114
|
+
table.each_row do |row|
|
115
|
+
row.cell_at(0).alignment.should == TinyTable::RIGHT
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
it "can use integers to specify the column to align" do
|
120
|
+
table.header = "City", "County"
|
121
|
+
table.align(0, TinyTable::CENTER)
|
122
|
+
table.align(1, TinyTable::RIGHT)
|
123
|
+
table << ["London", "Greater London"]
|
124
|
+
table.each_row do |row|
|
125
|
+
row.cell_at(0).alignment.should == TinyTable::CENTER
|
126
|
+
row.cell_at(1).alignment.should == TinyTable::RIGHT
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require File.expand_path('../../../lib/tinytable/text_formatter', __FILE__)
|
2
|
+
require File.expand_path('../../../lib/tinytable/row', __FILE__)
|
3
|
+
require File.expand_path('../../../lib/tinytable/cell', __FILE__)
|
4
|
+
|
5
|
+
module TinyTable
|
6
|
+
class Layout ; end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe TinyTable::TextFormatter do
|
10
|
+
def make_row(row)
|
11
|
+
TinyTable::Row.new(row)
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:table) { mock(:table, :has_header? => false,
|
15
|
+
:has_rows? => true,
|
16
|
+
:has_footer? => false) }
|
17
|
+
let(:layout) { mock(:layout, :column_count => 2) }
|
18
|
+
let(:formatter) { TinyTable::TextFormatter.new(table) }
|
19
|
+
before do
|
20
|
+
layout.stub(:analyze) { layout }
|
21
|
+
TinyTable::Layout.stub(:new).with(table) { layout }
|
22
|
+
end
|
23
|
+
|
24
|
+
it "formats a TinyTable as an ASCII table" do
|
25
|
+
table.stub(:each_row).and_yield(make_row %w[Liverpool Merseyside])
|
26
|
+
layout.stub(:max_column_widths) { ["Liverpool".length, "Merseyside".length] }
|
27
|
+
formatter.render.should == <<-EOF
|
28
|
+
+-----------+------------+
|
29
|
+
| Liverpool | Merseyside |
|
30
|
+
+-----------+------------+
|
31
|
+
EOF
|
32
|
+
end
|
33
|
+
|
34
|
+
it "adjusts the width of the table to fit the largest row" do
|
35
|
+
row1 = make_row %w[Liverpool Merseyside]
|
36
|
+
row2 = make_row %w[Nottingham Nottinghamshire]
|
37
|
+
table.stub(:each_row).and_yield(row1).and_yield(row2)
|
38
|
+
layout.stub(:max_column_widths) { ["Nottingham".length, "Nottinghamshire".length] }
|
39
|
+
formatter.render.should == <<-EOF
|
40
|
+
+------------+-----------------+
|
41
|
+
| Liverpool | Merseyside |
|
42
|
+
| Nottingham | Nottinghamshire |
|
43
|
+
+------------+-----------------+
|
44
|
+
EOF
|
45
|
+
end
|
46
|
+
|
47
|
+
it "renders the header row if the table has one" do
|
48
|
+
table.stub(:has_header?) { true }
|
49
|
+
table.stub(:header) { make_row %w[City County] }
|
50
|
+
table.stub(:each_row).and_yield(make_row %w[Southampton Hampshire])
|
51
|
+
layout.stub(:max_column_widths) { ["Southampton".length, "Hampshire".length] }
|
52
|
+
|
53
|
+
formatter.render.should == <<-EOF
|
54
|
+
+-------------+-----------+
|
55
|
+
| City | County |
|
56
|
+
+-------------+-----------+
|
57
|
+
| Southampton | Hampshire |
|
58
|
+
+-------------+-----------+
|
59
|
+
EOF
|
60
|
+
end
|
61
|
+
|
62
|
+
it "renders the footer row if the table has one" do
|
63
|
+
row1 = make_row %w[Londoners 8294058]
|
64
|
+
row2 = make_row %w[Brummies 2293099]
|
65
|
+
|
66
|
+
table.stub(:has_footer?) { true }
|
67
|
+
table.stub(:footer) { make_row %w[Total 10587157] }
|
68
|
+
table.stub(:each_row).and_yield(row1).and_yield(row2)
|
69
|
+
|
70
|
+
layout.stub(:max_column_widths) { ["Londoners".length, "10587157".length] }
|
71
|
+
formatter.render.should == <<-EOF
|
72
|
+
+-----------+----------+
|
73
|
+
| Londoners | 8294058 |
|
74
|
+
| Brummies | 2293099 |
|
75
|
+
+-----------+----------+
|
76
|
+
| Total | 10587157 |
|
77
|
+
+-----------+----------+
|
78
|
+
EOF
|
79
|
+
end
|
80
|
+
|
81
|
+
it "renders empty cells if a row has fewer columns than the rest" do
|
82
|
+
row1 = make_row ["London", "Greater London", "Boris Johnson"]
|
83
|
+
row2 = make_row ["Sheffield", "Yorkshire"]
|
84
|
+
table.stub(:each_row).and_yield(row1).and_yield(row2)
|
85
|
+
|
86
|
+
layout.stub(:max_column_widths) { ["Sheffield".length, "Greater London".length, "Boris Johnson".length] }
|
87
|
+
layout.stub(:column_count) { 3 }
|
88
|
+
|
89
|
+
formatter.render.should == <<-EOF
|
90
|
+
+-----------+----------------+---------------+
|
91
|
+
| London | Greater London | Boris Johnson |
|
92
|
+
| Sheffield | Yorkshire | |
|
93
|
+
+-----------+----------------+---------------+
|
94
|
+
EOF
|
95
|
+
end
|
96
|
+
|
97
|
+
it "returns an empty string if the table is completely empty" do
|
98
|
+
table.stub(:has_rows?) { false }
|
99
|
+
layout.stub(:max_column_widths) { [] }
|
100
|
+
layout.stub(:column_count) { 0 }
|
101
|
+
formatter.render.should == ''
|
102
|
+
end
|
103
|
+
end
|
data/tinytable.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/tinytable/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Leo Cassarani"]
|
6
|
+
gem.email = ["leo.cassarani@me.com"]
|
7
|
+
gem.description = %q{Simple ASCII table generation in Ruby.}
|
8
|
+
gem.summary = %q{TinyTable lets you create and output simple ASCII tables with minimal effort.}
|
9
|
+
gem.homepage = "https://github.com/leocassarani/tinytable"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "tinytable"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = TinyTable::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency "rake"
|
19
|
+
gem.add_development_dependency "rspec"
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tinytable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Leo Cassarani
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: &70326742572440 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70326742572440
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &70326742571820 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70326742571820
|
36
|
+
description: Simple ASCII table generation in Ruby.
|
37
|
+
email:
|
38
|
+
- leo.cassarani@me.com
|
39
|
+
executables: []
|
40
|
+
extensions: []
|
41
|
+
extra_rdoc_files: []
|
42
|
+
files:
|
43
|
+
- .gitignore
|
44
|
+
- Gemfile
|
45
|
+
- LICENSE
|
46
|
+
- README.md
|
47
|
+
- Rakefile
|
48
|
+
- lib/tinytable.rb
|
49
|
+
- lib/tinytable/cell.rb
|
50
|
+
- lib/tinytable/constants.rb
|
51
|
+
- lib/tinytable/layout.rb
|
52
|
+
- lib/tinytable/row.rb
|
53
|
+
- lib/tinytable/table.rb
|
54
|
+
- lib/tinytable/text_formatter.rb
|
55
|
+
- lib/tinytable/version.rb
|
56
|
+
- spec/integration/integration_spec.rb
|
57
|
+
- spec/tinytable/cell_spec.rb
|
58
|
+
- spec/tinytable/layout_spec.rb
|
59
|
+
- spec/tinytable/row_spec.rb
|
60
|
+
- spec/tinytable/table_spec.rb
|
61
|
+
- spec/tinytable/text_formatter_spec.rb
|
62
|
+
- tinytable.gemspec
|
63
|
+
homepage: https://github.com/leocassarani/tinytable
|
64
|
+
licenses: []
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ! '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
requirements: []
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 1.8.17
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: TinyTable lets you create and output simple ASCII tables with minimal effort.
|
87
|
+
test_files:
|
88
|
+
- spec/integration/integration_spec.rb
|
89
|
+
- spec/tinytable/cell_spec.rb
|
90
|
+
- spec/tinytable/layout_spec.rb
|
91
|
+
- spec/tinytable/row_spec.rb
|
92
|
+
- spec/tinytable/table_spec.rb
|
93
|
+
- spec/tinytable/text_formatter_spec.rb
|