tabulo 0.1.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +171 -25
- data/lib/tabulo.rb +1 -0
- data/lib/tabulo/column.rb +11 -6
- data/lib/tabulo/table.rb +24 -8
- data/lib/tabulo/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02a658d45fe5df2a6b02a39ff1ef8b85b9406a5d
|
4
|
+
data.tar.gz: 1edee8575e0098a1d5b383f596ff8ef77e3916a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d76f4fd33ede6de1a466affd190c6377f8e0a29b2c3986c7c4c83c0ac8001b285f880589d5a74668b572e8219c570f3aaa391b0dd8839f89c478f58b9bbc5f2
|
7
|
+
data.tar.gz: c4766b9abff559fd30f65356731e18311984b459bd67e11b7dc308bf93c92a2fab4902c6ea0c13bab81ffe5459df134074d93b2beec3ec26af8d8b5a4f4899df
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## v0.2.0
|
4
|
+
|
5
|
+
* Allow columns to be initialized with `columns` option in `Table` initializer
|
6
|
+
* Removed redundant `truncate` option.
|
7
|
+
* Rename `wrap_cells_to` to `wrap_body_cells_to`.
|
8
|
+
* Improve README.
|
9
|
+
|
10
|
+
## v0.1.0
|
11
|
+
|
12
|
+
Initial release.
|
data/README.md
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
## Overview
|
4
4
|
|
5
|
-
Tabulo generates ASCII tables
|
5
|
+
Tabulo generates ASCII tables.
|
6
6
|
|
7
|
-
A `Tabulo::Table` can be printed:
|
7
|
+
A `Tabulo::Table` can, of course, be printed:
|
8
8
|
|
9
9
|
```
|
10
10
|
> puts table
|
@@ -16,7 +16,7 @@ A `Tabulo::Table` can be printed:
|
|
16
16
|
| 50000000 | 10000000 |
|
17
17
|
```
|
18
18
|
|
19
|
-
But it is also `Enumerable`, so you can process
|
19
|
+
But it is also `Enumerable`, so you can process one row at a time:
|
20
20
|
|
21
21
|
```ruby
|
22
22
|
table.each do |row|
|
@@ -25,6 +25,17 @@ table.each do |row|
|
|
25
25
|
end
|
26
26
|
```
|
27
27
|
|
28
|
+
And rows are themselves `Enumerable`, providing access to the underlying cell values:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
table.each do |row|
|
32
|
+
row.each do |cell|
|
33
|
+
# 1, 2, 50000000...
|
34
|
+
puts cell.class # Fixnum
|
35
|
+
end
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
28
39
|
## Installation
|
29
40
|
|
30
41
|
Add this line to your application's Gemfile:
|
@@ -37,23 +48,25 @@ And then execute:
|
|
37
48
|
|
38
49
|
$ bundle
|
39
50
|
|
40
|
-
Or install it yourself
|
51
|
+
Or install it yourself:
|
41
52
|
|
42
53
|
$ gem install tabulo
|
43
54
|
|
44
|
-
##
|
55
|
+
## Detailed usage
|
45
56
|
|
46
|
-
|
57
|
+
### Requiring the gem
|
47
58
|
|
48
59
|
```ruby
|
49
60
|
require 'tabulo'
|
50
61
|
```
|
51
62
|
|
52
|
-
|
63
|
+
### Configuring columns
|
64
|
+
|
65
|
+
You instantiate a `Tabulo::Table` by passing it an underlying `Enumerable` and then telling it
|
53
66
|
the columns you want to generate.
|
54
67
|
|
55
68
|
A simple case involves initializing columns from symbols corresponding to methods on members of the
|
56
|
-
`Enumerable`. In this case the symbol also provides the header for each column:
|
69
|
+
underlying `Enumerable`. In this case the symbol also provides the header for each column:
|
57
70
|
|
58
71
|
```ruby
|
59
72
|
table = Tabulo::Table.new([1, 2, 5]) do |t|
|
@@ -61,36 +74,169 @@ table = Tabulo::Table.new([1, 2, 5]) do |t|
|
|
61
74
|
t.add_column(:even?)
|
62
75
|
t.add_column(:odd?)
|
63
76
|
end
|
77
|
+
```
|
78
|
+
|
79
|
+
Or equivalently:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
Tabulo::Table.new([1, 2, 5], columns: %i(itself even odd))
|
83
|
+
```
|
84
|
+
|
85
|
+
The resulting table looks like this:
|
64
86
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
87
|
+
```
|
88
|
+
> puts table
|
89
|
+
+----------+----------+----------+
|
90
|
+
| itself | even? | odd? |
|
91
|
+
+----------+----------+----------+
|
92
|
+
| 1 | false | true |
|
93
|
+
| 2 | true | false |
|
94
|
+
| 5 | false | true |
|
72
95
|
```
|
73
96
|
|
74
|
-
Columns can also be initialized using
|
75
|
-
the first argument
|
97
|
+
Columns can also be initialized using a callable to which each object will be passed to determine
|
98
|
+
the value to be displayed in the table. In this case, the first argument to `add_column` provides
|
99
|
+
the header text:
|
76
100
|
|
77
101
|
```ruby
|
78
102
|
table = Tabulo::Table.new([1, 2, 5]) do |t|
|
79
103
|
t.add_column("N", &:itself)
|
80
104
|
t.add_column("Doubled") { |n| n * 2 }
|
105
|
+
t.add_column(:odd?) # we can mix and match
|
81
106
|
end
|
107
|
+
```
|
108
|
+
|
109
|
+
```
|
110
|
+
> puts table
|
111
|
+
+----------+----------+----------+
|
112
|
+
| N | Doubled | odd? |
|
113
|
+
+----------+----------+----------+
|
114
|
+
| 1 | 2 | true |
|
115
|
+
| 2 | 4 | false |
|
116
|
+
| 5 | 10 | true |
|
117
|
+
```
|
118
|
+
|
119
|
+
### Cell alignment
|
120
|
+
|
121
|
+
By default, column header text is center-aligned, while the content of each body cell is aligned
|
122
|
+
according to its data type. Numbers are right-aligned, text is left-aligned, and booleans (`false`
|
123
|
+
and `true`) are center-aligned. This can be customized by passing `:center`, `:left` or `:right` to
|
124
|
+
the `align_header` or `align_body` options of `add_column`, e.g.:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
table.add_column("Doubled", align_header: :left, align_body: :left) { |n| n * 2 }
|
128
|
+
```
|
129
|
+
|
130
|
+
### Column width, wrapping and truncation
|
131
|
+
|
132
|
+
By default, column width is fixed at 8 characters, plus 1 character of padding on either side.
|
133
|
+
This can be customized using the `width` option of `add_column`:
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
table.add_column(:even?, width: 5)
|
137
|
+
```
|
138
|
+
|
139
|
+
### Overflow handling
|
140
|
+
|
141
|
+
By default, if cell contents exceed their column width, they are wrapped for as many rows as
|
142
|
+
required:
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
table = Tabulo::Table.new(["hello", "abcdefghijklmnopqrstuvwxyz"], columns: %i(itself length))
|
146
|
+
```
|
147
|
+
|
148
|
+
```
|
149
|
+
> puts table
|
150
|
+
+----------+----------+
|
151
|
+
| itself | length |
|
152
|
+
+----------+----------+
|
153
|
+
| hello | 5 |
|
154
|
+
| abcdefgh | 26 |
|
155
|
+
| ijklmnop | |
|
156
|
+
| qrstuvwx | |
|
157
|
+
| yz | |
|
158
|
+
```
|
159
|
+
|
160
|
+
Wrapping behaviour is configured for the table as a whole using the `wrap_header_cells_to` option
|
161
|
+
for header cells and `wrap_body_cells_to` for body cells, both of which default to `nil`, meaning
|
162
|
+
that cells are wrapped to as many rows as required. Passing a `Fixnum` limits wrapping to the given
|
163
|
+
number of rows, with content truncated from that point on. The `~` character is appended to the
|
164
|
+
outputted cell content to show that truncation has occurred:
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
table = Tabulo::Table.new(["hello", "abcdefghijklmnopqrstuvwxyz"], wrap_body_cells_to: 1, columns: %i(itself length))
|
168
|
+
```
|
169
|
+
|
170
|
+
```
|
171
|
+
> puts table
|
172
|
+
+----------+----------+
|
173
|
+
| itself | length |
|
174
|
+
+----------+----------+
|
175
|
+
| hello | 5 |
|
176
|
+
| abcdefgh~| 26 |
|
177
|
+
```
|
82
178
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
179
|
+
### Repeating headers
|
180
|
+
|
181
|
+
By default, headers are only shown once, at the top of the table (`header_frequency: :start`). If
|
182
|
+
`header_frequency` is passed `nil`, headers are not shown at all; or, if passed a `Fixnum` N,
|
183
|
+
headers are shown at the top and then repeated every N rows. This can be handy when you're looking
|
184
|
+
at table that's taller than your terminal.
|
185
|
+
|
186
|
+
E.g.:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
table = Tabulo::Table.new(1..10, columns: %i(itself even?), header_frequency: 5)
|
90
190
|
```
|
91
191
|
|
92
|
-
|
192
|
+
```
|
193
|
+
> puts table
|
194
|
+
+----------+----------+
|
195
|
+
| itself | even? |
|
196
|
+
+----------+----------+
|
197
|
+
| 1 | false |
|
198
|
+
| 2 | true |
|
199
|
+
| 3 | false |
|
200
|
+
| 4 | true |
|
201
|
+
| 5 | false |
|
202
|
+
+----------+----------+
|
203
|
+
| itself | even? |
|
204
|
+
+----------+----------+
|
205
|
+
| 6 | true |
|
206
|
+
| 7 | false |
|
207
|
+
| 8 | true |
|
208
|
+
| 9 | false |
|
209
|
+
| 10 | true |
|
210
|
+
```
|
211
|
+
|
212
|
+
TODO: Write rdocs, and link to them here "for more".
|
213
|
+
|
214
|
+
### Using a Table Enumerator
|
215
|
+
|
216
|
+
Because it's an `Enumerable`, a `Tabulo::Table` can also give you an `Enumerator`,
|
217
|
+
which is useful when you want to step through rows one at a time. In a Rails console,
|
218
|
+
for example, you might do this:
|
219
|
+
|
220
|
+
```
|
221
|
+
> e = Tabulo::Table.new(User.find_each) do |t|
|
222
|
+
t.add_column(:id)
|
223
|
+
t.add_column(:email, width: 25)
|
224
|
+
end.to_enum # <-- make an Enumerator
|
225
|
+
...
|
226
|
+
> puts e.next
|
227
|
+
+----------+--------------------------+
|
228
|
+
| id | email |
|
229
|
+
+----------+--------------------------+
|
230
|
+
| 1 | jane@example.com |
|
231
|
+
=> nil
|
232
|
+
> puts e.next
|
233
|
+
| 2 | betty@example.net |
|
234
|
+
=> nil
|
235
|
+
```
|
93
236
|
|
237
|
+
Note the used of `.find_each`: we can start printing the table without having to load the entire
|
238
|
+
underlying collection. (The cost of supporting this behaviour is that Tabulo requires us to set
|
239
|
+
column widths up front, rather than adapting to the width of the widest value.)
|
94
240
|
|
95
241
|
## Development
|
96
242
|
|
data/lib/tabulo.rb
CHANGED
data/lib/tabulo/column.rb
CHANGED
@@ -2,15 +2,20 @@ module Tabulo
|
|
2
2
|
|
3
3
|
class Column
|
4
4
|
|
5
|
-
attr_reader :label, :
|
5
|
+
attr_reader :label, :width
|
6
6
|
|
7
7
|
def initialize(options)
|
8
8
|
@label, @header = options[:label], options[:header]
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@extractor
|
12
|
-
@
|
13
|
-
|
9
|
+
@align_header = options[:align_header] || :center
|
10
|
+
@align_body = options[:align_body] || nil
|
11
|
+
@extractor = options[:extractor] || @label.to_proc
|
12
|
+
@formatter = options[:formatter] || :to_s.to_proc
|
13
|
+
|
14
|
+
# TODO Should be able to set these default on a Table-by-Table basis.
|
15
|
+
@width = options[:width] || Table::DEFAULT_COLUMN_WIDTH
|
16
|
+
|
17
|
+
@horizontal_rule_character =
|
18
|
+
options[:horizontal_rule_character] || Table::DEFAULT_HORIZONTAL_RULE_CHARACTER
|
14
19
|
end
|
15
20
|
|
16
21
|
def header_cell
|
data/lib/tabulo/table.rb
CHANGED
@@ -3,29 +3,47 @@ module Tabulo
|
|
3
3
|
class Table
|
4
4
|
include Enumerable
|
5
5
|
|
6
|
+
DEFAULT_COLUMN_WIDTH = 8
|
7
|
+
DEFAULT_HORIZONTAL_RULE_CHARACTER = "-"
|
8
|
+
|
6
9
|
attr_reader :columns
|
7
10
|
|
8
11
|
def initialize(sources, options = { })
|
9
12
|
opts = {
|
13
|
+
columns: [],
|
10
14
|
header_frequency: :start,
|
11
15
|
|
12
16
|
# nil to wrap to no max, 1 to wrap to 1 row then truncate, etc..
|
13
17
|
wrap_header_cells_to: nil,
|
14
|
-
|
18
|
+
wrap_body_cells_to: nil
|
15
19
|
|
16
20
|
}.merge(options)
|
17
21
|
|
18
22
|
@header_frequency = opts[:header_frequency]
|
19
23
|
@wrap_header_cells_to = opts[:wrap_header_cells_to]
|
20
|
-
@
|
24
|
+
@wrap_body_cells_to = opts[:wrap_body_cells_to]
|
21
25
|
@sources = sources
|
22
26
|
@joiner = "|"
|
23
27
|
@corner_character = "+"
|
24
28
|
@horizontal_rule_character = "-"
|
25
29
|
@truncation_indicator = "~"
|
26
30
|
@padding_character = " "
|
27
|
-
@
|
28
|
-
@
|
31
|
+
@default_column_width = DEFAULT_COLUMN_WIDTH
|
32
|
+
@columns = opts[:columns].map do |item|
|
33
|
+
case item
|
34
|
+
when Column
|
35
|
+
item
|
36
|
+
else
|
37
|
+
Column.new({
|
38
|
+
label: item.to_sym,
|
39
|
+
header: item.to_s,
|
40
|
+
align_header: :center,
|
41
|
+
horizontal_rule_character: @horizontal_rule_character,
|
42
|
+
width: @default_column_width,
|
43
|
+
formatter: :to_s.to_proc
|
44
|
+
})
|
45
|
+
end
|
46
|
+
end
|
29
47
|
yield self if block_given?
|
30
48
|
end
|
31
49
|
|
@@ -35,7 +53,6 @@ module Tabulo
|
|
35
53
|
header: label.to_s,
|
36
54
|
truncate: true,
|
37
55
|
align_header: :center,
|
38
|
-
align_body: nil,
|
39
56
|
horizontal_rule_character: @horizontal_rule_character,
|
40
57
|
width: @default_column_width,
|
41
58
|
extractor: extractor || (label.respond_to?(:to_proc) ? label.to_proc : proc { nil }),
|
@@ -89,10 +106,9 @@ module Tabulo
|
|
89
106
|
def format_row(header = false, padder = @padding_character, joiner = @joiner)
|
90
107
|
cell_stacks = @columns.map do |column|
|
91
108
|
raw = yield column
|
92
|
-
wrap = (header ? @wrap_header_cells_to : @
|
93
|
-
truncate = (column.truncate && wrap)
|
109
|
+
wrap = (header ? @wrap_header_cells_to : @wrap_body_cells_to)
|
94
110
|
column_width = column.width
|
95
|
-
cell_body_length = (
|
111
|
+
cell_body_length = (wrap ? column_width * wrap : raw.length)
|
96
112
|
truncated = (cell_body_length < raw.length)
|
97
113
|
cell_body = raw[0...cell_body_length]
|
98
114
|
num_subcells = (cell_body_length.to_f / column_width).ceil
|
data/lib/tabulo/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tabulo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Harvey
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-04-
|
11
|
+
date: 2017-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -62,6 +62,7 @@ files:
|
|
62
62
|
- ".gitignore"
|
63
63
|
- ".rspec"
|
64
64
|
- ".travis.yml"
|
65
|
+
- CHANGELOG.md
|
65
66
|
- Gemfile
|
66
67
|
- LICENSE.txt
|
67
68
|
- README.md
|