tabulo 0.2.1 → 0.2.2
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/.rdoc_options +23 -0
- data/CHANGELOG.md +4 -0
- data/README.md +1 -3
- data/TODO.md +13 -0
- data/lib/tabulo.rb +0 -5
- data/lib/tabulo/column.rb +45 -2
- data/lib/tabulo/row.rb +25 -0
- data/lib/tabulo/table.rb +115 -1
- data/lib/tabulo/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 758e3d9a0c11ce15ef23bcfc38c6e1e5dcf1e818
|
4
|
+
data.tar.gz: 5ffb67d92e38efec2b4eec90beea8ea1a047fb3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6ea3bb9c620c39e784949174afe15d028892af72a148159a6cfc1282f07aaf858c265d70fa41ee516c04f55fe1b34e3b21f23b684196adcb488dcf02a6f4335
|
7
|
+
data.tar.gz: f68ca83f4647a7d8f21976ef838248bbbcbd6b4aba21ec6c81087c521e07dc3b8d9f0c4aee59c45c5935107ff4740d72c6c3f70287e94d27d57c3ecb94008305
|
data/.rdoc_options
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
--- !ruby/object:RDoc::Options
|
2
|
+
encoding: UTF-8
|
3
|
+
static_path: []
|
4
|
+
rdoc_include:
|
5
|
+
- "."
|
6
|
+
- "/Users/matthew/Workbench/versioned/linode/tabulo"
|
7
|
+
charset: UTF-8
|
8
|
+
exclude:
|
9
|
+
hyperlink_all: false
|
10
|
+
line_numbers: false
|
11
|
+
locale:
|
12
|
+
locale_dir: locale
|
13
|
+
locale_name:
|
14
|
+
main_page:
|
15
|
+
markup: tomdoc
|
16
|
+
output_decoration: true
|
17
|
+
page_dir:
|
18
|
+
show_hash: false
|
19
|
+
tab_width: 8
|
20
|
+
template_stylesheets: []
|
21
|
+
title:
|
22
|
+
visibility: :protected
|
23
|
+
webcvs:
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -217,8 +217,6 @@ table = Tabulo::Table.new(1..10, columns: %i(itself even?), header_frequency: 5)
|
|
217
217
|
| 10 | true |
|
218
218
|
```
|
219
219
|
|
220
|
-
TODO: Write rdocs, and link to them here "for more".
|
221
|
-
|
222
220
|
### Using a Table Enumerator
|
223
221
|
|
224
222
|
Because it's an `Enumerable`, a `Tabulo::Table` can also give you an `Enumerator`,
|
@@ -242,7 +240,7 @@ end.to_enum # <-- make an Enumerator
|
|
242
240
|
=> nil
|
243
241
|
```
|
244
242
|
|
245
|
-
Note the
|
243
|
+
Note the use of `.find_each`: we can start printing the table without having to load the entire
|
246
244
|
underlying collection. (The cost of supporting this behaviour is that Tabulo requires us to set
|
247
245
|
column widths up front, rather than adapting to the width of the widest value.)
|
248
246
|
|
data/TODO.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# TODO
|
2
|
+
|
3
|
+
* Finish Tomdoc documentation. Link to it at rubydocs from the README. In particular,
|
4
|
+
make it clear which methods are part of the public API and which are internal.
|
5
|
+
* Allow the option of a horizontal rule between each row?
|
6
|
+
* Consider incorporating a linter / static analysis tool into the build.
|
7
|
+
* Raise an ArgumentError for disallowed arguments and options (this is
|
8
|
+
a library!)
|
9
|
+
* Document :formatter option in README.
|
10
|
+
* Allow default column width to be configured at level of Table.
|
11
|
+
* Rename Table#header_row to Table#header to avoid confusion as it does not
|
12
|
+
return a Row.
|
13
|
+
* Column#initialize should have the same signature as Table#add_column.
|
data/lib/tabulo.rb
CHANGED
data/lib/tabulo/column.rb
CHANGED
@@ -4,25 +4,65 @@ module Tabulo
|
|
4
4
|
|
5
5
|
attr_reader :label, :width
|
6
6
|
|
7
|
+
# Public: Initializes and returns a new Column.
|
8
|
+
#
|
9
|
+
# options - A Hash of options:
|
10
|
+
#
|
11
|
+
# :label - A Symbol or String being a unique identifier for this Column.
|
12
|
+
# If the :extractor option is not also provided, then the :label option
|
13
|
+
# should correspond to a method to be called on each item in the table sources
|
14
|
+
# to provide the content for this column.
|
15
|
+
#
|
16
|
+
# :header - The text to be displayed in the header for this Column.
|
17
|
+
#
|
18
|
+
# :align_header - Specifies the alignment of the header text. Possible values are
|
19
|
+
# <tt>:left</tt>, <tt>:center</tt> (the default) and <tt>right</tt>
|
20
|
+
#
|
21
|
+
# :align_body - Specifies how the cell body contents will be aligned. Possible
|
22
|
+
# values are <tt>:left</tt>, <tt>:center</tt>, <tt>:right</tt>
|
23
|
+
# and <tt>nil</tt>. If <tt>nil</tt> is passed (the default), then
|
24
|
+
# the alignment is determined by the type of the cell value,
|
25
|
+
# with numbers aligned right, booleans center-aligned, and
|
26
|
+
# other values left-aligned. Note header text alignment is configured
|
27
|
+
# separately using the :align_header option.
|
28
|
+
#
|
29
|
+
# :extractor - A callable, e.g. a block or lambda, that will be passed each of the
|
30
|
+
# Table sources to determine the value in each cell of this Column. If
|
31
|
+
# this is not provided, then the Column label will be treated as a
|
32
|
+
# method to be called on each source item to determine each cell's
|
33
|
+
# value.
|
34
|
+
#
|
35
|
+
# :formatter - A callable (e.g. a lambda) that will be passed the calculated
|
36
|
+
# value of each cell to determine how it should be displayed. This
|
37
|
+
# is distinct from the extractor (see below). For
|
38
|
+
# example, if the extractor for this column generates a Date, then
|
39
|
+
# the formatter might format that Date in a particular way.
|
40
|
+
# If no formatter is provided, then <tt>.to_s</tt> will be called on
|
41
|
+
# the extracted value of each cell to determine its displayed
|
42
|
+
# content.
|
43
|
+
#
|
44
|
+
# :width - Specifies the width of the column, not including the single character
|
45
|
+
# of padding. The default is given by Table::DEFAULT_COLUMN_WIDTH.
|
7
46
|
def initialize(options)
|
8
47
|
@label, @header = options[:label], options[:header]
|
9
48
|
@align_header = options[:align_header] || :center
|
10
49
|
@align_body = options[:align_body] || nil
|
11
50
|
@extractor = options[:extractor] || @label.to_proc
|
12
51
|
@formatter = options[:formatter] || :to_s.to_proc
|
13
|
-
|
14
|
-
# TODO Should be able to set these default on a Table-by-Table basis.
|
15
52
|
@width = options[:width] || Table::DEFAULT_COLUMN_WIDTH
|
16
53
|
end
|
17
54
|
|
55
|
+
# Internal
|
18
56
|
def header_cell
|
19
57
|
align_cell_content(@header, @align_header)
|
20
58
|
end
|
21
59
|
|
60
|
+
# Internal
|
22
61
|
def horizontal_rule
|
23
62
|
Table::HORIZONTAL_RULE_CHARACTER * @width
|
24
63
|
end
|
25
64
|
|
65
|
+
# Internal
|
26
66
|
def body_cell(source)
|
27
67
|
cell_datum = body_cell_value(source)
|
28
68
|
formatted_cell_content = @formatter.call(cell_datum)
|
@@ -30,12 +70,14 @@ module Tabulo
|
|
30
70
|
align_cell_content(formatted_cell_content, real_alignment)
|
31
71
|
end
|
32
72
|
|
73
|
+
# Internal
|
33
74
|
def body_cell_value(source)
|
34
75
|
@extractor.call(source)
|
35
76
|
end
|
36
77
|
|
37
78
|
private
|
38
79
|
|
80
|
+
# Internal
|
39
81
|
def align_cell_content(content, real_alignment)
|
40
82
|
padding = [@width - content.length, 0].max
|
41
83
|
left_padding, right_padding =
|
@@ -52,6 +94,7 @@ module Tabulo
|
|
52
94
|
"#{' ' * left_padding}#{content}#{' ' * right_padding}"
|
53
95
|
end
|
54
96
|
|
97
|
+
# Internal
|
55
98
|
def infer_alignment(cell_datum)
|
56
99
|
case cell_datum
|
57
100
|
when Numeric
|
data/lib/tabulo/row.rb
CHANGED
@@ -3,22 +3,47 @@ module Tabulo
|
|
3
3
|
class Row
|
4
4
|
include Enumerable
|
5
5
|
|
6
|
+
# Internal
|
6
7
|
def initialize(table, source, options = { with_header: true })
|
7
8
|
@table = table
|
8
9
|
@source = source
|
9
10
|
@with_header = options[:with_header]
|
10
11
|
end
|
11
12
|
|
13
|
+
# Public: Calls the given block once for each cell in the Row, passing that cell as parameter.
|
14
|
+
# Each "cell" is just the calculated value for its column (pre-formatting) for this Row's
|
15
|
+
# source item.
|
16
|
+
#
|
17
|
+
# Examples
|
18
|
+
#
|
19
|
+
# table = Tabulo::Table.new([1, 10], columns: %i(itself even?))
|
20
|
+
# row = table.first
|
21
|
+
# row.each do |cell|
|
22
|
+
# cell # => 1, => false
|
23
|
+
# cell.class # => Fixnum, => FalseClass
|
24
|
+
# end
|
12
25
|
def each
|
13
26
|
@table.columns.each do |column|
|
14
27
|
yield column.body_cell_value(@source)
|
15
28
|
end
|
16
29
|
end
|
17
30
|
|
31
|
+
# Public: Returns a String being an "ASCII" graphical representation of the Row, including
|
32
|
+
# any column headers that appear just above it in the Table (depending on where this Row is
|
33
|
+
# in the Table and how the Table was configured with respect to header frequency).
|
18
34
|
def to_s
|
19
35
|
@table.formatted_body_row(@source, with_header: @with_header)
|
20
36
|
end
|
21
37
|
|
38
|
+
# Public: Returns a Hash representation of the Row, with Column labels acting
|
39
|
+
# as keys and the calculated cell values (before formatting) providing the values.
|
40
|
+
#
|
41
|
+
# Examples
|
42
|
+
#
|
43
|
+
# table = Tabulo::Table.new([1, 10], columns: %i(itself even?))
|
44
|
+
# row = table.first
|
45
|
+
# row.to_h # -> { :itself => 1, :even? => false }
|
46
|
+
#
|
22
47
|
def to_h
|
23
48
|
@table.columns.map(&:label).zip(to_a).to_h
|
24
49
|
end
|
data/lib/tabulo/table.rb
CHANGED
@@ -1,14 +1,55 @@
|
|
1
1
|
module Tabulo
|
2
2
|
|
3
|
+
# Represents a table primarily intended for "pretty-printing" in a fixed-width font.
|
4
|
+
#
|
5
|
+
# A Table is also an Enumerable, of which each element is a Tabulo::Row.
|
3
6
|
class Table
|
4
7
|
include Enumerable
|
5
8
|
|
6
9
|
DEFAULT_COLUMN_WIDTH = 8
|
10
|
+
|
7
11
|
HORIZONTAL_RULE_CHARACTER = "-"
|
8
12
|
CORNER_CHARACTER = "+"
|
9
13
|
|
10
14
|
attr_reader :columns
|
11
15
|
|
16
|
+
# Public: Initializes and returns a new Table.
|
17
|
+
#
|
18
|
+
# sources - the underlying Enumerable from which the table will derive its data
|
19
|
+
# options - a Hash of options providing for customization of the Table:
|
20
|
+
#
|
21
|
+
# :columns - An Array (default: []) specifying the initial columns (note more can be
|
22
|
+
# added later using #add_column). Each element of the Array
|
23
|
+
# should be either a Symbol or a Column. If it's a Symbol,
|
24
|
+
# it will be used to initialize a Column whose content is
|
25
|
+
# created by calling the corresponding method on each
|
26
|
+
# element of sources.
|
27
|
+
#
|
28
|
+
# :header_frequency - Controls the display of Column headers. Possible values:
|
29
|
+
#
|
30
|
+
# <tt>:start</tt> (default) - show column headers at top of table only
|
31
|
+
#
|
32
|
+
# <tt>nil</tt> - do not show column headers.
|
33
|
+
#
|
34
|
+
# N (a Fixnum > 0) - show column headers at start, then repeated
|
35
|
+
# every N rows.
|
36
|
+
#
|
37
|
+
# :wrap_header_cells_to - Controls wrapping behaviour for header cells if the content
|
38
|
+
# thereof is longer than the Column's fixed width. Possible
|
39
|
+
# values:
|
40
|
+
#
|
41
|
+
# <tt>nil</tt> - wrap content for as many rows as necessary
|
42
|
+
#
|
43
|
+
# N (a Fixnum > 0) - wrap content for up to N rows and
|
44
|
+
# truncate thereafter
|
45
|
+
#
|
46
|
+
# :wrap_body_cells_to - Controls wrapping behaviour for table cells (excluding
|
47
|
+
# headers). Possible values:
|
48
|
+
#
|
49
|
+
# <tt>nil</tt> - wrap content for as many rows as necessary
|
50
|
+
#
|
51
|
+
# N (a `Fixnum` > 0) - wrap content for up to N rows and
|
52
|
+
# truncate thereafter
|
12
53
|
def initialize(sources, options = { })
|
13
54
|
opts = {
|
14
55
|
columns: [],
|
@@ -32,14 +73,68 @@ module Tabulo
|
|
32
73
|
yield self if block_given?
|
33
74
|
end
|
34
75
|
|
35
|
-
|
76
|
+
# Public: Initializes a Column and adds it to the Table.
|
77
|
+
#
|
78
|
+
# label - A Symbol or String being a unique identifier for this column, which by default will
|
79
|
+
# also be used as the column header text (see also the header option). If the
|
80
|
+
# extractor argument is not also provided, then the label argument should correspond to
|
81
|
+
# a method to be called on each item in the table sources to provide the content
|
82
|
+
# for this column.
|
83
|
+
#
|
84
|
+
# options - A Hash of options providing for customization of the column:
|
85
|
+
#
|
86
|
+
# :header - Text to be displayed in the column header. By default the column
|
87
|
+
# label will also be used as its header text.
|
88
|
+
#
|
89
|
+
# :align_header - Specifies how the header text should be aligned. Possible values
|
90
|
+
# are <tt>:left</tt>, <tt>:center</tt> and <tt>:right</tt>.
|
91
|
+
#
|
92
|
+
# :align_body - Specifies how the cell body contents will be aligned. Possible
|
93
|
+
# values are <tt>:left</tt>, <tt>:center</tt>, <tt>:right</tt>
|
94
|
+
# and <tt>nil</tt>. If <tt>nil</tt> is passed (the default), then
|
95
|
+
# the alignment is determined by the type of the cell value,
|
96
|
+
# with numbers aligned right, booleans center-aligned, and
|
97
|
+
# other values left-aligned. Note header text alignment is
|
98
|
+
# configured separately using the :align_header option.
|
99
|
+
#
|
100
|
+
# :width - Specifies the width of the column, not including the single
|
101
|
+
# character of padding. The default is given by
|
102
|
+
# Table::DEFAULT_COLUMN_WIDTH.
|
103
|
+
#
|
104
|
+
# :formatter - A callable (e.g. a lambda) that will be passed the calculated
|
105
|
+
# value of each cell to determine how it should be displayed. This
|
106
|
+
# is distinct from the extractor (see below). For
|
107
|
+
# example, if the extractor for this column generates a Date, then
|
108
|
+
# the formatter might format that Date in a particular way.
|
109
|
+
# If no formatter is provided, then <tt>.to_s</tt> will be called on
|
110
|
+
# the extracted value of each cell to determine its displayed
|
111
|
+
# content.
|
112
|
+
#
|
113
|
+
# extractor - A callable, e.g. a block or lambda, that will be passed each of the Table
|
114
|
+
# sources to determine the value in each cell of this column. If this is not
|
115
|
+
# provided, then the column label will be treated as a method to be called on each
|
116
|
+
# source item to determine each cell's value.
|
117
|
+
#
|
118
|
+
def add_column(label, options = { }, &extractor)
|
36
119
|
@columns << make_column(label, extractor: extractor)
|
37
120
|
end
|
38
121
|
|
122
|
+
# Public: Returns a String being a graphical "ASCII" representation of the Table, suitable for
|
123
|
+
# display in a fixed-width font.
|
39
124
|
def to_s
|
40
125
|
join_lines(map(&:to_s))
|
41
126
|
end
|
42
127
|
|
128
|
+
# Public: Calls the given block once for each Row in the Table, passing that Row as parameter.
|
129
|
+
#
|
130
|
+
# Examples
|
131
|
+
#
|
132
|
+
# table.each do |row|
|
133
|
+
# puts row
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# Note that when printed, the first row will visually include the headers (assuming these
|
137
|
+
# were not disabled when the Table was initialized).
|
43
138
|
def each
|
44
139
|
@sources.each_with_index do |source, index|
|
45
140
|
include_header =
|
@@ -55,14 +150,28 @@ module Tabulo
|
|
55
150
|
end
|
56
151
|
end
|
57
152
|
|
153
|
+
# Public: Returns a String being an "ASCII" graphical representation of the Table column
|
154
|
+
# headers.
|
58
155
|
def header_row
|
59
156
|
format_row(true, &:header_cell)
|
60
157
|
end
|
61
158
|
|
159
|
+
# Public: Returns a String being an "ASCII" graphical representation of a horizontal
|
160
|
+
# dividing line suitable for printing at any point in the table.
|
161
|
+
#
|
162
|
+
# Examples
|
163
|
+
#
|
164
|
+
# # To print a horizontal divider after every row:
|
165
|
+
# table.each do |row|
|
166
|
+
# puts row
|
167
|
+
# puts table.horizontal_rule
|
168
|
+
# end
|
169
|
+
#
|
62
170
|
def horizontal_rule
|
63
171
|
format_row(false, HORIZONTAL_RULE_CHARACTER, CORNER_CHARACTER, &:horizontal_rule)
|
64
172
|
end
|
65
173
|
|
174
|
+
# Internal
|
66
175
|
def formatted_body_row(source, options = { with_header: false })
|
67
176
|
inner = format_row { |column| column.body_cell(source) }
|
68
177
|
if options[:with_header]
|
@@ -74,11 +183,14 @@ module Tabulo
|
|
74
183
|
|
75
184
|
private
|
76
185
|
|
186
|
+
# Internal
|
77
187
|
def body_row(source, options = { with_header: false })
|
78
188
|
Row.new(self, source, options)
|
79
189
|
end
|
80
190
|
|
191
|
+
# Internal
|
81
192
|
def format_row(header = false, padder = @padding_character, joiner = @joiner)
|
193
|
+
# TODO Tidy this up -- or at least comment it.
|
82
194
|
cell_stacks = @columns.map do |column|
|
83
195
|
raw = yield column
|
84
196
|
wrap = (header ? @wrap_header_cells_to : @wrap_body_cells_to)
|
@@ -107,10 +219,12 @@ module Tabulo
|
|
107
219
|
join_lines(subrows.map { |subrow| "#{joiner}#{subrow.join(joiner)}#{joiner}" })
|
108
220
|
end
|
109
221
|
|
222
|
+
# Internal
|
110
223
|
def join_lines(lines)
|
111
224
|
lines.join($/) # join strings with cross-platform newline
|
112
225
|
end
|
113
226
|
|
227
|
+
# Internal
|
114
228
|
def make_column(item, options = { })
|
115
229
|
case item
|
116
230
|
when Column
|
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.2.
|
4
|
+
version: 0.2.2
|
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-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -88,6 +88,7 @@ extensions: []
|
|
88
88
|
extra_rdoc_files: []
|
89
89
|
files:
|
90
90
|
- ".gitignore"
|
91
|
+
- ".rdoc_options"
|
91
92
|
- ".rspec"
|
92
93
|
- ".travis.yml"
|
93
94
|
- CHANGELOG.md
|
@@ -95,6 +96,7 @@ files:
|
|
95
96
|
- LICENSE.txt
|
96
97
|
- README.md
|
97
98
|
- Rakefile
|
99
|
+
- TODO.md
|
98
100
|
- bin/console
|
99
101
|
- bin/setup
|
100
102
|
- lib/tabulo.rb
|