tabulo 0.6.2 → 0.6.3
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 +4 -0
- data/README.md +11 -5
- data/VERSION +1 -1
- data/lib/tabulo.rb +1 -0
- data/lib/tabulo/column.rb +2 -13
- data/lib/tabulo/exceptions.rb +5 -0
- data/lib/tabulo/row.rb +3 -3
- data/lib/tabulo/table.rb +60 -28
- data/lib/tabulo/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6756f8ae92c219bb324358e9046e86ec756d3923
|
4
|
+
data.tar.gz: de3d6bedfcbea40b81d607d00ca07be75e4277a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b5f10a9d672d15083bf96cb7d6f5e8a21030e98318fc13cd98d82ec9c22f5d83ea25d65cb792273c5840db056b7b6a0a961d71676a10bac430f5f874517521b6
|
7
|
+
data.tar.gz: c46bcc143a0447af136aab26c7dc960b1c4f53afdf72e27a57522428dab7d936ccc95cd8b2a4bbe19726100fd28de4547f978ed1280be9a491bc88bc9a18547d
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -226,7 +226,12 @@ table will not overflow your terminal horizontally.
|
|
226
226
|
|
227
227
|
Note that shrinkwrapping necessarily involves traversing the entire collection up front as
|
228
228
|
the maximum cell width needs to be calculated for each column. You may not want to do this
|
229
|
-
if the collection is very large.
|
229
|
+
if the collection is very large. Note also the effect of `shrinkwrap!` is to fix the column widths
|
230
|
+
as appropriate to the formatted cell contents given the state of the underlying collection
|
231
|
+
_at the point of shrinkwrapping_. If the underlying collection changes between that point, and when
|
232
|
+
the table is printed, then the columns will _not_ be resized yet again on printing. This is a
|
233
|
+
consequence of the table always being essentially a "live view" on the underlying collection:
|
234
|
+
formatted contents are never cached within the table itself.
|
230
235
|
|
231
236
|
<a name="overflow-handling"></a>
|
232
237
|
#### Overflow handling
|
@@ -368,8 +373,9 @@ end.to_enum # <-- make an Enumerator
|
|
368
373
|
```
|
369
374
|
|
370
375
|
Note the use of `.find_each`: we can start printing the table without having to load the entire
|
371
|
-
underlying collection. (
|
372
|
-
|
376
|
+
underlying collection. (This is negated if we [shrinkwrap](#shrinkwrap) the table, however, since
|
377
|
+
in that case the entire collection must be traversed up front in order for column widths to be
|
378
|
+
calculated.)
|
373
379
|
|
374
380
|
## Development
|
375
381
|
|
@@ -391,11 +397,11 @@ License](http://opensource.org/licenses/MIT).
|
|
391
397
|
[Dependency Status]: https://gemnasium.com/matt-harvey/tabulo
|
392
398
|
[Coverage Status]: https://coveralls.io/r/matt-harvey/tabulo
|
393
399
|
[Code Climate]: https://codeclimate.com/github/matt-harvey/tabulo
|
394
|
-
[Documentation]: http://www.rubydoc.info/
|
400
|
+
[Documentation]: http://www.rubydoc.info/github/matt-harvey/tabulo
|
395
401
|
|
396
402
|
[GV img]: https://img.shields.io/gem/v/tabulo.svg?style=plastic
|
397
403
|
[BS img]: https://img.shields.io/travis/matt-harvey/tabulo.svg?style=plastic
|
398
404
|
[DS img]: https://img.shields.io/gemnasium/matt-harvey/tabulo.svg?style=plastic
|
399
405
|
[CS img]: https://img.shields.io/coveralls/matt-harvey/tabulo.svg?style=plastic
|
400
406
|
[CC img]: https://img.shields.io/codeclimate/github/matt-harvey/tabulo.svg?style=plastic
|
401
|
-
[DC img]: https://img.shields.io/badge/docs-
|
407
|
+
[DC img]: https://img.shields.io/badge/docs-latest-blue.svg?style=plastic
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.6.
|
1
|
+
0.6.3
|
data/lib/tabulo.rb
CHANGED
data/lib/tabulo/column.rb
CHANGED
@@ -4,13 +4,10 @@ module Tabulo
|
|
4
4
|
class Column
|
5
5
|
|
6
6
|
attr_accessor :width
|
7
|
-
attr_reader :header
|
7
|
+
attr_reader :header
|
8
8
|
|
9
|
-
|
10
|
-
def initialize(label:, header:, width:, align_header:, align_body:,
|
11
|
-
formatter:, extractor:)
|
9
|
+
def initialize(header:, width:, align_header:, align_body:, formatter:, extractor:)
|
12
10
|
|
13
|
-
@label = label
|
14
11
|
@header = header
|
15
12
|
@width = width
|
16
13
|
@align_header = align_header
|
@@ -19,17 +16,14 @@ module Tabulo
|
|
19
16
|
@extractor = extractor
|
20
17
|
end
|
21
18
|
|
22
|
-
# @!visibility private
|
23
19
|
def header_subcells
|
24
20
|
infilled_subcells(@header, @align_header)
|
25
21
|
end
|
26
22
|
|
27
|
-
# @!visibility private
|
28
23
|
def horizontal_rule
|
29
24
|
Table::HORIZONTAL_RULE_CHARACTER * width
|
30
25
|
end
|
31
26
|
|
32
|
-
# @!visibility private
|
33
27
|
def body_subcells(source)
|
34
28
|
cell_datum = body_cell_value(source)
|
35
29
|
formatted_content = @formatter.call(cell_datum)
|
@@ -37,19 +31,16 @@ module Tabulo
|
|
37
31
|
infilled_subcells(formatted_content, real_alignment)
|
38
32
|
end
|
39
33
|
|
40
|
-
# @!visibility private
|
41
34
|
def formatted_cell_content(source)
|
42
35
|
@formatter.call(body_cell_value(source))
|
43
36
|
end
|
44
37
|
|
45
|
-
# @!visibility private
|
46
38
|
def body_cell_value(source)
|
47
39
|
@extractor.call(source)
|
48
40
|
end
|
49
41
|
|
50
42
|
private
|
51
43
|
|
52
|
-
# @!visibility private
|
53
44
|
def infilled_subcells(str, real_alignment)
|
54
45
|
str.split($/, -1).flat_map do |substr|
|
55
46
|
num_subsubcells = [1, (substr.length.to_f / width).ceil].max
|
@@ -59,7 +50,6 @@ module Tabulo
|
|
59
50
|
end
|
60
51
|
end
|
61
52
|
|
62
|
-
# @!visibility private
|
63
53
|
def align_cell_content(content, real_alignment)
|
64
54
|
padding = [@width - content.length, 0].max
|
65
55
|
left_padding, right_padding =
|
@@ -76,7 +66,6 @@ module Tabulo
|
|
76
66
|
"#{' ' * left_padding}#{content}#{' ' * right_padding}"
|
77
67
|
end
|
78
68
|
|
79
|
-
# @!visibility private
|
80
69
|
def infer_alignment(cell_datum)
|
81
70
|
case cell_datum
|
82
71
|
when Numeric
|
data/lib/tabulo/row.rb
CHANGED
@@ -22,7 +22,7 @@ module Tabulo
|
|
22
22
|
# cell.class # => Fixnum, => FalseClass
|
23
23
|
# end
|
24
24
|
def each
|
25
|
-
@table.
|
25
|
+
@table.column_registry.each do |label, column|
|
26
26
|
yield column.body_cell_value(@source)
|
27
27
|
end
|
28
28
|
end
|
@@ -31,7 +31,7 @@ module Tabulo
|
|
31
31
|
# any column headers that appear just above it in the {Table} (depending on where this Row is
|
32
32
|
# in the {Table} and how the {Table} was configured with respect to header frequency).
|
33
33
|
def to_s
|
34
|
-
if @table.
|
34
|
+
if @table.column_registry.any?
|
35
35
|
@table.formatted_body_row(@source, with_header: @with_header)
|
36
36
|
else
|
37
37
|
""
|
@@ -45,7 +45,7 @@ module Tabulo
|
|
45
45
|
# row = table.first
|
46
46
|
# row.to_h # => { :itself => 1, :even? => false }
|
47
47
|
def to_h
|
48
|
-
@table.
|
48
|
+
@table.column_registry.map { |label, column| [label, column.body_cell_value(@source)] }.to_h
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
data/lib/tabulo/table.rb
CHANGED
@@ -6,19 +6,30 @@ module Tabulo
|
|
6
6
|
class Table
|
7
7
|
include Enumerable
|
8
8
|
|
9
|
+
# @!visibility private
|
9
10
|
DEFAULT_COLUMN_WIDTH = 12
|
11
|
+
|
12
|
+
# @!visibility private
|
10
13
|
HORIZONTAL_RULE_CHARACTER = "-"
|
14
|
+
|
15
|
+
# @!visibility private
|
11
16
|
VERTICAL_RULE_CHARACTER = "|"
|
17
|
+
|
18
|
+
# @!visibility private
|
12
19
|
CORNER_CHARACTER = "+"
|
20
|
+
|
21
|
+
# @!visibility private
|
13
22
|
PADDING_CHARACTER = " "
|
23
|
+
|
24
|
+
# @!visibility private
|
14
25
|
TRUNCATION_INDICATOR = "~"
|
15
26
|
|
16
27
|
# @!visibility private
|
17
|
-
attr_reader :
|
28
|
+
attr_reader :column_registry
|
18
29
|
|
19
30
|
# @param [Enumerable] sources the underlying Enumerable from which the table will derive its data
|
20
|
-
# @param [Array[Symbol]] columns Specifies the initial columns.
|
21
|
-
# Each element of the Array will be used to create a column whose content is
|
31
|
+
# @param [Array[Symbol]] columns Specifies the initial columns. The Symbols provided must
|
32
|
+
# be unique. Each element of the Array will be used to create a column whose content is
|
22
33
|
# created by calling the corresponding method on each element of sources. Note
|
23
34
|
# the {#add_column} method is a much more flexible way to set up columns on the table.
|
24
35
|
# @param [Fixnum, nil] column_width The default column width for columns in this
|
@@ -36,6 +47,7 @@ module Tabulo
|
|
36
47
|
# be wrapped for as many rows as required to accommodate it. If passed a Fixnum N (> 0), content will be
|
37
48
|
# wrapped up to N rows and then truncated thereafter.
|
38
49
|
# @return [Table] a new Table
|
50
|
+
# @raise [InvalidColumnLabelError] if non-unique Symbols are provided to columns.
|
39
51
|
def initialize(sources, columns: [], column_width: nil, header_frequency: :start,
|
40
52
|
wrap_header_cells_to: nil, wrap_body_cells_to: nil)
|
41
53
|
|
@@ -46,7 +58,7 @@ module Tabulo
|
|
46
58
|
|
47
59
|
@default_column_width = (column_width || DEFAULT_COLUMN_WIDTH)
|
48
60
|
|
49
|
-
@
|
61
|
+
@column_registry = { }
|
50
62
|
columns.each { |item| add_column(item) }
|
51
63
|
|
52
64
|
yield self if block_given?
|
@@ -81,24 +93,33 @@ module Tabulo
|
|
81
93
|
# that will be passed each of the Table sources to determine the value in each cell of this
|
82
94
|
# column. If this is not provided, then the column label will be treated as a method to be
|
83
95
|
# called on each source item to determine each cell's value.
|
96
|
+
# @raise [InvalidColumnLabelError] if label has already been used for another column in this
|
97
|
+
# Table. (This is case-sensitive, but is insensitive to whether a String or Symbol is passed
|
98
|
+
# to the label parameter.)
|
84
99
|
def add_column(label, header: nil, align_header: :center, align_body: nil,
|
85
100
|
width: nil, formatter: :to_s.to_proc, &extractor)
|
86
101
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
102
|
+
column_label = label.to_sym
|
103
|
+
|
104
|
+
if column_registry.include?(column_label)
|
105
|
+
raise InvalidColumnLabelError, "Column label already used in this table."
|
106
|
+
end
|
107
|
+
|
108
|
+
@column_registry[column_label] =
|
109
|
+
Column.new(
|
110
|
+
header: (header || label).to_s,
|
111
|
+
align_header: align_header,
|
112
|
+
align_body: align_body,
|
113
|
+
width: (width || @default_column_width),
|
114
|
+
formatter: formatter,
|
115
|
+
extractor: (extractor || label.to_proc)
|
116
|
+
)
|
96
117
|
end
|
97
118
|
|
98
119
|
# @return [String] a graphical "ASCII" representation of the Table, suitable for
|
99
120
|
# display in a fixed-width font.
|
100
121
|
def to_s
|
101
|
-
if
|
122
|
+
if column_registry.any?
|
102
123
|
join_lines(map(&:to_s))
|
103
124
|
else
|
104
125
|
""
|
@@ -131,7 +152,7 @@ module Tabulo
|
|
131
152
|
|
132
153
|
# @return [String] an "ASCII" graphical representation of the Table column headers.
|
133
154
|
def formatted_header
|
134
|
-
cells =
|
155
|
+
cells = column_registry.map { |label, column| column.header_subcells }
|
135
156
|
format_row(cells, @wrap_header_cells_to)
|
136
157
|
end
|
137
158
|
|
@@ -144,7 +165,9 @@ module Tabulo
|
|
144
165
|
# end
|
145
166
|
#
|
146
167
|
def horizontal_rule
|
147
|
-
inner =
|
168
|
+
inner = column_registry.map do |label, column|
|
169
|
+
surround(column.horizontal_rule, HORIZONTAL_RULE_CHARACTER)
|
170
|
+
end
|
148
171
|
surround_join(inner, CORNER_CHARACTER)
|
149
172
|
end
|
150
173
|
|
@@ -157,6 +180,11 @@ module Tabulo
|
|
157
180
|
# be traversed and all the column extractors and formatters to be applied in order
|
158
181
|
# to calculate the required widths.
|
159
182
|
#
|
183
|
+
# Note also that this method causes column widths to be fixed as appropriate to the
|
184
|
+
# formatted cell contents given the state of the source Enumerable at the point it
|
185
|
+
# is called. If the source Enumerable changes between that point, and the point when
|
186
|
+
# the Table is printed, then columns will *not* be resized yet again on printing.
|
187
|
+
#
|
160
188
|
# @param [nil, Numeric] max_table_width (nil) If provided, stops the total table
|
161
189
|
# width (including padding and borders) from expanding beyond this number of characters.
|
162
190
|
# Width is deducted from columns if required to achieve this, with one character progressively
|
@@ -170,36 +198,41 @@ module Tabulo
|
|
170
198
|
# shrink itself.
|
171
199
|
# @return [Table] the Table itself
|
172
200
|
def shrinkwrap!(max_table_width: nil)
|
173
|
-
return self if
|
201
|
+
return self if column_registry.none?
|
174
202
|
|
175
203
|
wrapped_width = -> (str) { str.split($/).map(&:length).max || 1 }
|
176
204
|
|
177
|
-
|
205
|
+
column_registry.each do |label, column|
|
178
206
|
column.width = wrapped_width.call(column.header)
|
179
207
|
end
|
180
208
|
|
181
209
|
@sources.each do |source|
|
182
|
-
|
210
|
+
column_registry.each do |label, column|
|
183
211
|
width = wrapped_width.call(column.formatted_cell_content(source))
|
184
212
|
column.width = width if width > column.width
|
185
213
|
end
|
186
214
|
end
|
187
215
|
|
188
216
|
if max_table_width
|
189
|
-
total_columns_width =
|
190
|
-
|
191
|
-
|
217
|
+
total_columns_width = column_registry.inject(0) do |sum, label_with_column|
|
218
|
+
_label, column = label_with_column
|
219
|
+
sum + column.width
|
220
|
+
end
|
221
|
+
total_padding = column_registry.count * 2
|
222
|
+
total_borders = column_registry.count + 1
|
192
223
|
unadjusted_table_width = total_columns_width + total_padding + total_borders
|
193
224
|
|
194
225
|
# Ensure max table width is at least wide enough to accommodate table borders and padding
|
195
226
|
# and one character of content.
|
196
|
-
min_table_width = total_padding + total_borders +
|
227
|
+
min_table_width = total_padding + total_borders + column_registry.count
|
197
228
|
max_table_width = min_table_width if min_table_width > max_table_width
|
198
229
|
|
199
230
|
required_reduction = [unadjusted_table_width - max_table_width, 0].max
|
200
231
|
|
201
232
|
required_reduction.times do
|
202
|
-
|
233
|
+
_first_label, first_column = column_registry.first
|
234
|
+
widest_column = column_registry.inject(first_column) do |widest, label_with_column|
|
235
|
+
_label, column = label_with_column
|
203
236
|
column.width >= widest.width ? column : widest
|
204
237
|
end
|
205
238
|
|
@@ -212,7 +245,7 @@ module Tabulo
|
|
212
245
|
|
213
246
|
# @!visibility private
|
214
247
|
def formatted_body_row(source, with_header: false)
|
215
|
-
cells =
|
248
|
+
cells = column_registry.map { |label, column| column.body_subcells(source) }
|
216
249
|
inner = format_row(cells, @wrap_body_cells_to)
|
217
250
|
if with_header
|
218
251
|
join_lines([horizontal_rule, formatted_header, horizontal_rule, inner])
|
@@ -245,7 +278,7 @@ module Tabulo
|
|
245
278
|
row_height = ([wrap_cells_to, cells.map(&:size).max].compact.min || 1)
|
246
279
|
|
247
280
|
subrows = (0...row_height).map do |subrow_index|
|
248
|
-
subrow_components = cells.map
|
281
|
+
subrow_components = cells.zip(column_registry.values).map do |cell, column|
|
249
282
|
num_subcells = cell.size
|
250
283
|
cell_truncated = (num_subcells > row_height)
|
251
284
|
append_truncator = (cell_truncated && subrow_index + 1 == row_height)
|
@@ -257,8 +290,7 @@ module Tabulo
|
|
257
290
|
if subrow_index < num_subcells
|
258
291
|
cell[subrow_index]
|
259
292
|
else
|
260
|
-
|
261
|
-
PADDING_CHARACTER * column_width
|
293
|
+
PADDING_CHARACTER * column.width
|
262
294
|
end
|
263
295
|
|
264
296
|
"#{lpad}#{inner}#{rpad}"
|
data/lib/tabulo/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tabulo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Harvey
|
@@ -159,6 +159,7 @@ files:
|
|
159
159
|
- bin/setup
|
160
160
|
- lib/tabulo.rb
|
161
161
|
- lib/tabulo/column.rb
|
162
|
+
- lib/tabulo/exceptions.rb
|
162
163
|
- lib/tabulo/row.rb
|
163
164
|
- lib/tabulo/table.rb
|
164
165
|
- lib/tabulo/version.rb
|