tabulo 0.2.2 → 0.3.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 +6 -0
- data/TODO.md +0 -5
- data/lib/tabulo/column.rb +8 -45
- data/lib/tabulo/row.rb +11 -13
- data/lib/tabulo/table.rb +67 -98
- data/lib/tabulo/version.rb +1 -1
- data/tabulo.gemspec +2 -0
- metadata +29 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b63d694608052e887a021c0c0e0eb4c9e77512a
|
4
|
+
data.tar.gz: e2781dc44f766676c25fa2148a867e14cf49795f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33b5af849750ab1ba36c4212b835193d50592f524f7055c3236043f81dcce5b1bffb56c0770651fd9ca5e6c89df935073e4018cbe3029ac1215f7efd6e805e4f
|
7
|
+
data.tar.gz: f9563b27c2a150ea1544e3705c5fbac6c77163e2f23d9187f50310c71ac66a13b2066d04223907707cef698fa3d024112a981edc25245e6f372d2dfc29ab8c2f
|
data/CHANGELOG.md
CHANGED
data/TODO.md
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
# TODO
|
2
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
3
|
* Consider incorporating a linter / static analysis tool into the build.
|
7
4
|
* Raise an ArgumentError for disallowed arguments and options (this is
|
8
5
|
a library!)
|
9
6
|
* Document :formatter option in README.
|
10
7
|
* 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
8
|
* Column#initialize should have the same signature as Table#add_column.
|
data/lib/tabulo/column.rb
CHANGED
@@ -1,48 +1,11 @@
|
|
1
1
|
module Tabulo
|
2
2
|
|
3
|
+
# @!visibility private
|
3
4
|
class Column
|
4
5
|
|
5
6
|
attr_reader :label, :width
|
6
7
|
|
7
|
-
#
|
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.
|
8
|
+
# @!visibility private
|
46
9
|
def initialize(options)
|
47
10
|
@label, @header = options[:label], options[:header]
|
48
11
|
@align_header = options[:align_header] || :center
|
@@ -52,17 +15,17 @@ module Tabulo
|
|
52
15
|
@width = options[:width] || Table::DEFAULT_COLUMN_WIDTH
|
53
16
|
end
|
54
17
|
|
55
|
-
#
|
18
|
+
# @!visibility private
|
56
19
|
def header_cell
|
57
20
|
align_cell_content(@header, @align_header)
|
58
21
|
end
|
59
22
|
|
60
|
-
#
|
23
|
+
# @!visibility private
|
61
24
|
def horizontal_rule
|
62
25
|
Table::HORIZONTAL_RULE_CHARACTER * @width
|
63
26
|
end
|
64
27
|
|
65
|
-
#
|
28
|
+
# @!visibility private
|
66
29
|
def body_cell(source)
|
67
30
|
cell_datum = body_cell_value(source)
|
68
31
|
formatted_cell_content = @formatter.call(cell_datum)
|
@@ -70,14 +33,14 @@ module Tabulo
|
|
70
33
|
align_cell_content(formatted_cell_content, real_alignment)
|
71
34
|
end
|
72
35
|
|
73
|
-
#
|
36
|
+
# @!visibility private
|
74
37
|
def body_cell_value(source)
|
75
38
|
@extractor.call(source)
|
76
39
|
end
|
77
40
|
|
78
41
|
private
|
79
42
|
|
80
|
-
#
|
43
|
+
# @!visibility private
|
81
44
|
def align_cell_content(content, real_alignment)
|
82
45
|
padding = [@width - content.length, 0].max
|
83
46
|
left_padding, right_padding =
|
@@ -94,7 +57,7 @@ module Tabulo
|
|
94
57
|
"#{' ' * left_padding}#{content}#{' ' * right_padding}"
|
95
58
|
end
|
96
59
|
|
97
|
-
#
|
60
|
+
# @!visibility private
|
98
61
|
def infer_alignment(cell_datum)
|
99
62
|
case cell_datum
|
100
63
|
when Numeric
|
data/lib/tabulo/row.rb
CHANGED
@@ -3,19 +3,18 @@ module Tabulo
|
|
3
3
|
class Row
|
4
4
|
include Enumerable
|
5
5
|
|
6
|
-
#
|
6
|
+
# @!visibility private
|
7
7
|
def initialize(table, source, options = { with_header: true })
|
8
8
|
@table = table
|
9
9
|
@source = source
|
10
10
|
@with_header = options[:with_header]
|
11
11
|
end
|
12
12
|
|
13
|
-
#
|
14
|
-
# Each "cell" is just the calculated value for its column (pre-formatting) for this Row's
|
13
|
+
# 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
15
|
# source item.
|
16
16
|
#
|
17
|
-
#
|
18
|
-
#
|
17
|
+
# @example
|
19
18
|
# table = Tabulo::Table.new([1, 10], columns: %i(itself even?))
|
20
19
|
# row = table.first
|
21
20
|
# row.each do |cell|
|
@@ -28,21 +27,20 @@ module Tabulo
|
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
30
|
+
# @return a String being an "ASCII" graphical representation of the {Row}, including
|
31
|
+
# any column headers that appear just above it in the {Table} (depending on where this Row is
|
32
|
+
# in the {Table} and how the {Table} was configured with respect to header frequency).
|
34
33
|
def to_s
|
35
34
|
@table.formatted_body_row(@source, with_header: @with_header)
|
36
35
|
end
|
37
36
|
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# Examples
|
37
|
+
# @return a Hash representation of the {Row}, with column labels acting
|
38
|
+
# as keys and the calculated cell values (before formatting) providing the values.
|
42
39
|
#
|
40
|
+
# @example
|
43
41
|
# table = Tabulo::Table.new([1, 10], columns: %i(itself even?))
|
44
42
|
# row = table.first
|
45
|
-
# row.to_h #
|
43
|
+
# row.to_h # => { :itself => 1, :even? => false }
|
46
44
|
#
|
47
45
|
def to_h
|
48
46
|
@table.columns.map(&:label).zip(to_a).to_h
|
data/lib/tabulo/table.rb
CHANGED
@@ -2,7 +2,7 @@ module Tabulo
|
|
2
2
|
|
3
3
|
# Represents a table primarily intended for "pretty-printing" in a fixed-width font.
|
4
4
|
#
|
5
|
-
# A Table is also an Enumerable, of which each element is a
|
5
|
+
# A Table is also an Enumerable, of which each element is a {Row}.
|
6
6
|
class Table
|
7
7
|
include Enumerable
|
8
8
|
|
@@ -11,45 +11,29 @@ module Tabulo
|
|
11
11
|
HORIZONTAL_RULE_CHARACTER = "-"
|
12
12
|
CORNER_CHARACTER = "+"
|
13
13
|
|
14
|
+
# @!visibility private
|
14
15
|
attr_reader :columns
|
15
16
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
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
|
17
|
+
# @param [Enumerable] sources the underlying Enumerable from which the table will derive its data
|
18
|
+
# @param [Hash] options
|
19
|
+
# @option options [Array[Symbol]] :columns ([]) Specifies the initial columns.
|
20
|
+
# Each element of the Array will be used to create a column whose content is
|
21
|
+
# created by calling the corresponding method on each element of sources. Note
|
22
|
+
# the {#add_column} method is a much more flexible way to set up columns on the table.
|
23
|
+
# @option options [:start, nil, Fixnum] :header_frequency (:start) Controls the display of column headers.
|
24
|
+
# If passed <tt>:start</tt>, headers will be shown at the top of the table only. If passed <tt>nil</tt>,
|
25
|
+
# headers will not be shown. If passed a Fixnum N (> 0), headers will be shown at the top of the table,
|
26
|
+
# then repeated every N rows.
|
27
|
+
# @option options [nil, Fixnum] :wrap_header_cells_to (nil) Controls wrapping behaviour for header
|
28
|
+
# cells if the content thereof is longer than the column's fixed width. If passed <tt>nil</tt> (default),
|
29
|
+
# content will be wrapped for as many rows as required to accommodate it. If passed a Fixnum N (> 0),
|
30
|
+
# content will be wrapped up to N rows and then truncated thereafter.
|
31
|
+
# @option options [nil, Fixnum] :wrap_body_cells_to (nil) Controls wrapping behaviour for table cells (excluding
|
32
|
+
# headers), if their content is longer than the column's fixed width. If passed <tt>nil</tt>, content will
|
33
|
+
# be wrapped for as many rows as required to accommodate it. If passed a Fixnum N (> 0), content will be
|
34
|
+
# wrapped up to N rows and then truncated thereafter.
|
35
|
+
#
|
36
|
+
# @return [Table] a new Table
|
53
37
|
def initialize(sources, options = { })
|
54
38
|
opts = {
|
55
39
|
columns: [],
|
@@ -73,62 +57,50 @@ module Tabulo
|
|
73
57
|
yield self if block_given?
|
74
58
|
end
|
75
59
|
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
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.
|
60
|
+
# Adds a column to the Table.
|
61
|
+
#
|
62
|
+
# @param [Symbol, String] label A unique identifier for this column, which by default will
|
63
|
+
# also be used as the column header text (see also the header option). If the
|
64
|
+
# extractor argument is not also provided, then the label argument should correspond to
|
65
|
+
# a method to be called on each item in the table sources to provide the content
|
66
|
+
# for this column.
|
67
|
+
#
|
68
|
+
# @param [Hash] options
|
69
|
+
# @option options [String] :header Text to be displayed in the column header. By default the column
|
70
|
+
# label will also be used as its header text.
|
71
|
+
# @option options [:left, :center, :right] :align_header (:center) Specifies how the header text
|
72
|
+
# should be aligned.
|
73
|
+
# @option options [:left, :center, :right, nil] :align_body (nil) Specifies how the cell body contents
|
74
|
+
# should be aligned. Possible If <tt>nil</tt> is passed, then the alignment is determined
|
75
|
+
# by the type of the cell value, with numbers aligned right, booleans center-aligned, and
|
76
|
+
# other values left-aligned. Note header text alignment is configured separately using the
|
77
|
+
# :align_header option.
|
78
|
+
# @option options [Fixnum] :width (8) Specifies the width of the
|
79
|
+
# column, excluding padding.
|
80
|
+
# @option options [#to_proc] :formatter (:to_s.to_proc) A lambda or other callable object that
|
81
|
+
# will be passed the calculated value of each cell to determine how it should be displayed. This
|
82
|
+
# is distinct from the extractor (see below). For example, if the extractor for this column
|
83
|
+
# generates a Date, then the formatter might format that Date in a particular way.
|
84
|
+
# If no formatter is provided, then <tt>.to_s</tt> will be called on
|
85
|
+
# the extracted value of each cell to determine its displayed content.
|
86
|
+
# @param [#to_proc] extractor A block or other callable
|
87
|
+
# that will be passed each of the Table sources to determine the value in each cell of this
|
88
|
+
# column. If this is not provided, then the column label will be treated as a method to be
|
89
|
+
# called on each source item to determine each cell's value.
|
117
90
|
#
|
118
91
|
def add_column(label, options = { }, &extractor)
|
119
92
|
@columns << make_column(label, extractor: extractor)
|
120
93
|
end
|
121
94
|
|
122
|
-
#
|
123
|
-
#
|
95
|
+
# @return [String] a graphical "ASCII" representation of the Table, suitable for
|
96
|
+
# display in a fixed-width font.
|
124
97
|
def to_s
|
125
98
|
join_lines(map(&:to_s))
|
126
99
|
end
|
127
100
|
|
128
|
-
#
|
129
|
-
#
|
130
|
-
# Examples
|
101
|
+
# Calls the given block once for each {Row} in the Table, passing that {Row} as parameter.
|
131
102
|
#
|
103
|
+
# @example
|
132
104
|
# table.each do |row|
|
133
105
|
# puts row
|
134
106
|
# end
|
@@ -150,18 +122,15 @@ module Tabulo
|
|
150
122
|
end
|
151
123
|
end
|
152
124
|
|
153
|
-
#
|
154
|
-
|
155
|
-
def header_row
|
125
|
+
# @return [String] an "ASCII" graphical representation of the Table column headers.
|
126
|
+
def formatted_header
|
156
127
|
format_row(true, &:header_cell)
|
157
128
|
end
|
158
129
|
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
# Examples
|
130
|
+
# @return [String] an "ASCII" graphical representation of a horizontal
|
131
|
+
# dividing line suitable for printing at any point in the table.
|
163
132
|
#
|
164
|
-
#
|
133
|
+
# @example Print a horizontal divider after every row:
|
165
134
|
# table.each do |row|
|
166
135
|
# puts row
|
167
136
|
# puts table.horizontal_rule
|
@@ -171,11 +140,11 @@ module Tabulo
|
|
171
140
|
format_row(false, HORIZONTAL_RULE_CHARACTER, CORNER_CHARACTER, &:horizontal_rule)
|
172
141
|
end
|
173
142
|
|
174
|
-
#
|
143
|
+
# @!visibility private
|
175
144
|
def formatted_body_row(source, options = { with_header: false })
|
176
145
|
inner = format_row { |column| column.body_cell(source) }
|
177
146
|
if options[:with_header]
|
178
|
-
join_lines([horizontal_rule,
|
147
|
+
join_lines([horizontal_rule, formatted_header, horizontal_rule, inner])
|
179
148
|
else
|
180
149
|
inner
|
181
150
|
end
|
@@ -183,12 +152,12 @@ module Tabulo
|
|
183
152
|
|
184
153
|
private
|
185
154
|
|
186
|
-
#
|
155
|
+
# @!visibility private
|
187
156
|
def body_row(source, options = { with_header: false })
|
188
157
|
Row.new(self, source, options)
|
189
158
|
end
|
190
159
|
|
191
|
-
#
|
160
|
+
# @!visibility private
|
192
161
|
def format_row(header = false, padder = @padding_character, joiner = @joiner)
|
193
162
|
# TODO Tidy this up -- or at least comment it.
|
194
163
|
cell_stacks = @columns.map do |column|
|
@@ -219,12 +188,12 @@ module Tabulo
|
|
219
188
|
join_lines(subrows.map { |subrow| "#{joiner}#{subrow.join(joiner)}#{joiner}" })
|
220
189
|
end
|
221
190
|
|
222
|
-
#
|
191
|
+
# @!visibility private
|
223
192
|
def join_lines(lines)
|
224
193
|
lines.join($/) # join strings with cross-platform newline
|
225
194
|
end
|
226
195
|
|
227
|
-
#
|
196
|
+
# @!visibility private
|
228
197
|
def make_column(item, options = { })
|
229
198
|
case item
|
230
199
|
when Column
|
data/lib/tabulo/version.rb
CHANGED
data/tabulo.gemspec
CHANGED
@@ -28,4 +28,6 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency "rspec", "~> 3.0"
|
29
29
|
spec.add_development_dependency "simplecov"
|
30
30
|
spec.add_development_dependency "coveralls"
|
31
|
+
spec.add_development_dependency "yard"
|
32
|
+
spec.add_development_dependency "yard-tomdoc"
|
31
33
|
end
|
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.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Harvey
|
@@ -80,6 +80,34 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: yard
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard-tomdoc
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
83
111
|
description: Enumerable ASCII table
|
84
112
|
email:
|
85
113
|
- software@matthewharvey.net
|