tabulo 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5a2f88a15024f1728f244d9f72a14b6f88a893fc
4
- data.tar.gz: c6f23f4a1568575083122f501d2e61492057b2b7
3
+ metadata.gz: 6756f8ae92c219bb324358e9046e86ec756d3923
4
+ data.tar.gz: de3d6bedfcbea40b81d607d00ca07be75e4277a5
5
5
  SHA512:
6
- metadata.gz: 703ef06a5ea3c9c0dbf1b01b1572b02f0eca2e9ef6dc64508cb7a93459211c1c420f32af29c95a22581dd31c6a02ed3308ee7e913dfa37be65cf592633650b14
7
- data.tar.gz: f17355d77642115e8bca70c5601164dfe16e186d1b5c79e3577d1e292eda46942a1fd9f0f9ccd730bbedaf2c4ce2a526832425715ae0c6d13e11a470ff018a2c
6
+ metadata.gz: b5f10a9d672d15083bf96cb7d6f5e8a21030e98318fc13cd98d82ec9c22f5d83ea25d65cb792273c5840db056b7b6a0a961d71676a10bac430f5f874517521b6
7
+ data.tar.gz: c46bcc143a0447af136aab26c7dc960b1c4f53afdf72e27a57522428dab7d936ccc95cd8b2a4bbe19726100fd28de4547f978ed1280be9a491bc88bc9a18547d
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ### v0.6.3
4
+
5
+ * Throw an exception if column labels are not unique.
6
+
3
7
  ### v0.6.2
4
8
 
5
9
  * Explicitly support only Ruby >= 2.1.10.
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. (The cost of supporting this behaviour is that Tabulo requires us to set
372
- column widths up front, rather than adapting to the width of the widest value.)
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/gems/tabulo/0.6.2
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-v0.6.2-blue.svg?style=plastic
407
+ [DC img]: https://img.shields.io/badge/docs-latest-blue.svg?style=plastic
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.2
1
+ 0.6.3
@@ -1,4 +1,5 @@
1
1
  require "tabulo/version"
2
+ require "tabulo/exceptions"
2
3
  require "tabulo/table"
3
4
  require "tabulo/row"
4
5
  require "tabulo/column"
@@ -4,13 +4,10 @@ module Tabulo
4
4
  class Column
5
5
 
6
6
  attr_accessor :width
7
- attr_reader :header, :label
7
+ attr_reader :header
8
8
 
9
- # @!visibility private
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
@@ -0,0 +1,5 @@
1
+ module Tabulo
2
+
3
+ # Error indicating that the label of a column is invalid.
4
+ class InvalidColumnLabelError < StandardError; end
5
+ end
@@ -22,7 +22,7 @@ module Tabulo
22
22
  # cell.class # => Fixnum, => FalseClass
23
23
  # end
24
24
  def each
25
- @table.columns.each do |column|
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.columns.any?
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.columns.map(&:label).zip(to_a).to_h
48
+ @table.column_registry.map { |label, column| [label, column.body_cell_value(@source)] }.to_h
49
49
  end
50
50
  end
51
51
  end
@@ -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 :columns
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
- @columns = []
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
- @columns << Column.new(
88
- label: label.to_sym,
89
- header: (header || label).to_s,
90
- align_header: align_header,
91
- align_body: align_body,
92
- width: (width || @default_column_width),
93
- formatter: formatter,
94
- extractor: (extractor || label.to_proc)
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 @columns.any?
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 = @columns.map(&:header_subcells)
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 = @columns.map { |column| surround(column.horizontal_rule, HORIZONTAL_RULE_CHARACTER) }
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 columns.none?
201
+ return self if column_registry.none?
174
202
 
175
203
  wrapped_width = -> (str) { str.split($/).map(&:length).max || 1 }
176
204
 
177
- columns.each do |column|
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
- columns.each do |column|
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 = columns.inject(0) { |sum, column| sum + column.width }
190
- total_padding = columns.count * 2
191
- total_borders = columns.count + 1
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 + columns.count
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
- widest_column = columns.inject(columns.first) do |widest, column|
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 = @columns.map { |column| column.body_subcells(source) }
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.with_index do |cell, column_index|
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
- column_width = @columns[column_index].width
261
- PADDING_CHARACTER * column_width
293
+ PADDING_CHARACTER * column.width
262
294
  end
263
295
 
264
296
  "#{lpad}#{inner}#{rpad}"
@@ -1,3 +1,3 @@
1
1
  module Tabulo
2
- VERSION = "0.6.2"
2
+ VERSION = "0.6.3"
3
3
  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.6.2
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