tabulo 0.5.1 → 0.6.0

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: a8546e761344f764d8d14a7cbfc8fd9d84912da5
4
- data.tar.gz: 6371e46be06a8891f4a80306a434b16f33c6aa44
3
+ metadata.gz: 84dadcb8679194ba436e2e213defc55800771628
4
+ data.tar.gz: aa8d2182569ba3a439bce0b82ef8ceeeb36a4b81
5
5
  SHA512:
6
- metadata.gz: 4a0a18378e96fa1d64019f2d4179fad9829e7ce65dfc60390eab46c7674c97bb655eeec144ebf72b93c5aed064183155811dad5ac74e4cb093e4628d33dbc3ac
7
- data.tar.gz: d4426bf5cb78e655ba8cc60b71f577cb9a47f7e7408879fb9fe51136b409e5f8b04e42d4bea44ac2342cac36b8d9ed9d7126fab383e96dd112dc4b3656ae09de
6
+ metadata.gz: 91d583322688d1db9f7c0dd21111f40d039da50fc767c78eecda2d640b75e42bae6478bc73a88b2c0810e1ce20fe35b61c7ad550a00b5c8a261fb2bf9e7c4b34
7
+ data.tar.gz: e0cee253d0513f05158b84bacd7c3e40ad0c546e5765b28c726ecab9936d7c67fa5893d032925bb43c28a96bc2002533c218105bf1ac0fdf43de07559b1106ad
data/CHANGELOG.md CHANGED
@@ -1,16 +1,21 @@
1
1
  # Changelog
2
2
 
3
- ## v0.5.1
3
+ ### v0.6.0
4
4
 
5
- * Fix Rubydocs Markdown parsing.
5
+ * Correctly handle newlines in cell content.
6
+ * Use keyword arguments instead of option hashes.
7
+ * Write remaining pending specs.
6
8
 
7
- ## v0.5.0
9
+ ### v0.5.1
10
+
11
+ * Unsuccessful attempt to fix broken appearance of http://www.rubydoc.info/gems/tabulo/0.5.1
12
+
13
+ ### v0.5.0
8
14
 
9
15
  * Add Table#shrinkwrap! method to automate column widths so they "just fit".
10
16
  * Improve documentation.
11
- * Minor tidy-ups.
12
17
 
13
- ## v0.4.2
18
+ ### v0.4.2
14
19
 
15
20
  * Improve README.
16
21
  * Fix error when printing a Table, or a Row thereof, when the Table doesn't
@@ -18,44 +23,43 @@
18
23
  * Remove unused development dependency on yard-tomdoc.
19
24
  * Write more specs.
20
25
 
21
- ## v0.4.1
26
+ ### v0.4.1
22
27
 
23
28
  * Update README to reflect default column width of 12.
24
29
 
25
- ## v0.4.0
30
+ ### v0.4.0
26
31
 
27
32
  * Increase default column width from 8 to 12
28
33
  * Allow default column width to be configured when initializing a Table
29
34
  * Minor code tidy-ups, including removal of undocumented ability for
30
35
  Table#add_column to accept a Column instance directly.
31
36
 
32
- ## v0.3.1
37
+ ### v0.3.1
33
38
 
34
39
  * Fix width and other options ignored by Table#add_column.
35
40
 
36
- ## v0.3.0
41
+ ### v0.3.0
37
42
 
38
43
  * Rename Table#header_row to Table#formatted_header
39
44
  * Improve documentation, and use Yardoc instead of Tomdoc
40
45
  * Remove Tabulo::Column from the publicly documented API.
41
46
 
42
- ## v0.2.2
47
+ ### v0.2.2
43
48
 
44
49
  * Write documentation
45
- * Create a TODO file
46
50
 
47
- ## v0.2.1
51
+ ### v0.2.1
48
52
 
49
53
  * Code tidy-ups
50
54
  * Tidy-ups and improvements to README, including adding badges for test coverage etc..
51
55
 
52
- ## v0.2.0
56
+ ### v0.2.0
53
57
 
54
58
  * Allow columns to be initialized with `columns` option in `Table` initializer
55
59
  * Removed redundant `truncate` option.
56
60
  * Rename `wrap_cells_to` to `wrap_body_cells_to`.
57
61
  * Improve README.
58
62
 
59
- ## v0.1.0
63
+ ### v0.1.0
60
64
 
61
65
  Initial release.
data/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![Build Status][BS img]][Build Status]
5
5
  [![Dependency Status][DS img]][Dependency Status]
6
6
  [![Coverage Status][CS img]][Coverage Status]
7
+ [![Code Climate][CC img]][Code Climate]
7
8
 
8
9
  ## Overview
9
10
 
@@ -26,28 +27,30 @@ end
26
27
  | 5000000 | 10000000 |
27
28
  ```
28
29
 
29
- Tabulo is flexible:
30
+ ## Features
30
31
 
31
- * Fix individual column widths, then either [wrap](#width-wrapping-truncation) or
32
- [truncate](#width-wrapping-truncation) the overflow as you prefer.
32
+ * [Fix](#fixed-column-widths) individual column widths, then either [wrap](#overflow-handling) or
33
+ [truncate](#overflow-handling) the overflow as you prefer.
33
34
  * Alternatively, [shrinkwrap](#shrinkwrap) the table so that each column is just wide enough for
34
35
  its contents.
35
36
  * You can cap total table width when shrinkwrapping, to [stop it overflowing your terminal](#max-table-width)
36
37
  horizontally and becoming an unreadable mess.
37
38
  * Cell content alignment is [configurable](#cell-alignment), but with useful defaults, with numbers
38
39
  aligned right and strings left.
39
- * Headers can be [repeated](#repeating-headers) as desired.
40
+ * Headers are [repeatable](#repeating-headers)
41
+ * Newlines within cell content are correctly handled.
40
42
  * A `Tabulo::Table` is an `Enumerable`, so you can [step through it](#enumerator) one row at a time,
41
43
  without having to wait for the entire underlying collection to load.
42
- * Each `Tabulo::Row` is also an `Enumerable`.
44
+ * Each `Tabulo::Row` is also an `Enumerable`:
43
45
 
44
- ```ruby
45
- table.each do |row|
46
- row.each do |cell|
47
- # cell => 1, 2 ... 2, 4 ... etc.
48
- end
49
- end
50
- ```
46
+ >
47
+ ```ruby
48
+ table.each do |row|
49
+ row.each do |cell|
50
+ # cell => 1, 2 ... 2, 4 ... etc.
51
+ end
52
+ end
53
+ ```
51
54
 
52
55
  ## Installation
53
56
 
@@ -139,9 +142,11 @@ the `align_header` or `align_body` options of `add_column`, e.g.:
139
142
  table.add_column("Doubled", align_header: :left, align_body: :left) { |n| n * 2 }
140
143
  ```
141
144
 
142
- <a name="width-wrapping-truncation"></a>
143
145
  ### Column width, wrapping and truncation
144
146
 
147
+ <a name="fixed-column-widths"></a>
148
+ #### Configuring fixed widths
149
+
145
150
  By default, column width is fixed at 12 characters, plus 1 character of padding on either side.
146
151
  This can be adjusted on a column-by-column basis using the `width` option of `add_column`:
147
152
 
@@ -180,7 +185,7 @@ table = Tabulo::Table.new([1, 2], columns: %i(itself even?), column_width: 6)
180
185
  Widths set for individual columns always override the default column width for the table.
181
186
 
182
187
  <a name="shrinkwrap"></a>
183
- ### Automating column widths
188
+ #### Automating column widths
184
189
 
185
190
  Instead of setting column widths "manually", you can tell the table to sort out the widths
186
191
  itself, so that each column is just wide enough for its header and contents (plus a character
@@ -222,7 +227,7 @@ puts Tabulo::Table.new([1, 2], columns: %i(itself even?)).shrinkwrap!(max_table_
222
227
  | 2 | true |
223
228
  ```
224
229
 
225
- If the table cannot be fit within `max_column_width`, column widths are reduced as required, with
230
+ If the table cannot be fit within `max_table_width`, column widths are reduced as required, with
226
231
  wrapping or truncation then occuring as necessary (see [Overflow handling](#overflow-handling)).
227
232
  Under the hood, a character of width is deducted column by column&mdash;the widest column being
228
233
  targetted each time&mdash;until the table will fit. This is very useful when you want to ensure the
@@ -233,7 +238,7 @@ the maximum cell width needs to be calculated for each column. You may not want
233
238
  if the collection is very large.
234
239
 
235
240
  <a name="overflow-handling"></a>
236
- ### Overflow handling
241
+ #### Overflow handling
237
242
 
238
243
  By default, if cell contents exceed their column width, they are wrapped for as many rows as
239
244
  required:
@@ -394,8 +399,10 @@ License](http://opensource.org/licenses/MIT).
394
399
  [Build Status]: https://travis-ci.org/matt-harvey/tabulo
395
400
  [Dependency Status]: https://gemnasium.com/matt-harvey/tabulo
396
401
  [Coverage Status]: https://coveralls.io/r/matt-harvey/tabulo
402
+ [Code Climate]: https://codeclimate.com/github/matt-harvey/tabulo
397
403
 
398
404
  [GV img]: https://img.shields.io/gem/v/tabulo.svg
399
405
  [BS img]: https://img.shields.io/travis/matt-harvey/tabulo.svg
400
406
  [DS img]: https://img.shields.io/gemnasium/matt-harvey/tabulo.svg
401
407
  [CS img]: https://img.shields.io/coveralls/matt-harvey/tabulo.svg
408
+ [CC img]: https://img.shields.io/codeclimate/github/matt-harvey/tabulo.svg
data/TODO.md CHANGED
@@ -1,9 +1,3 @@
1
1
  # TODO
2
2
 
3
- * Consider incorporating a linter / static analysis tool into the build.
4
- * Raise an ArgumentError for disallowed arguments and options (this is
5
- a library!)
6
- * Column#initialize should have the same signature as Table#add_column.
7
- * Handle multiline cell content (i.e. when the calculated cell value
8
- itself contains a newline).
9
- * Pending specs.
3
+ * Fix broken appearance of http://www.rubydoc.info/gems/tabulo/<version> .
data/lib/tabulo/column.rb CHANGED
@@ -7,31 +7,34 @@ module Tabulo
7
7
  attr_reader :header, :label
8
8
 
9
9
  # @!visibility private
10
- def initialize(options)
11
- @label, @header = options[:label], options[:header]
12
- @align_header = options[:align_header] || :center
13
- @align_body = options[:align_body] || nil
14
- @extractor = options[:extractor] || @label.to_proc
15
- @formatter = options[:formatter] || :to_s.to_proc
16
- @width = options[:width]
10
+ def initialize(label:, header:, width:, align_header:, align_body:,
11
+ formatter:, extractor:)
12
+
13
+ @label = label
14
+ @header = header
15
+ @width = width
16
+ @align_header = align_header
17
+ @align_body = align_body
18
+ @formatter = formatter
19
+ @extractor = extractor
17
20
  end
18
21
 
19
22
  # @!visibility private
20
- def header_cell
21
- align_cell_content(@header, @align_header)
23
+ def header_subcells
24
+ infilled_subcells(@header, @align_header)
22
25
  end
23
26
 
24
27
  # @!visibility private
25
28
  def horizontal_rule
26
- Table::HORIZONTAL_RULE_CHARACTER * @width
29
+ Table::HORIZONTAL_RULE_CHARACTER * width
27
30
  end
28
31
 
29
32
  # @!visibility private
30
- def body_cell(source)
33
+ def body_subcells(source)
31
34
  cell_datum = body_cell_value(source)
32
35
  formatted_content = @formatter.call(cell_datum)
33
36
  real_alignment = (@align_body || infer_alignment(cell_datum))
34
- align_cell_content(formatted_content, real_alignment)
37
+ infilled_subcells(formatted_content, real_alignment)
35
38
  end
36
39
 
37
40
  # @!visibility private
@@ -46,6 +49,16 @@ module Tabulo
46
49
 
47
50
  private
48
51
 
52
+ # @!visibility private
53
+ def infilled_subcells(str, real_alignment)
54
+ str.split($/, -1).flat_map do |substr|
55
+ num_subsubcells = [1, (substr.length.to_f / width).ceil].max
56
+ (0...num_subsubcells).map do |i|
57
+ align_cell_content(substr.slice(i * width, width), real_alignment)
58
+ end
59
+ end
60
+ end
61
+
49
62
  # @!visibility private
50
63
  def align_cell_content(content, real_alignment)
51
64
  padding = [@width - content.length, 0].max
data/lib/tabulo/row.rb CHANGED
@@ -4,10 +4,10 @@ module Tabulo
4
4
  include Enumerable
5
5
 
6
6
  # @!visibility private
7
- def initialize(table, source, options = { with_header: true })
7
+ def initialize(table, source, with_header: true)
8
8
  @table = table
9
9
  @source = source
10
- @with_header = options[:with_header]
10
+ @with_header = with_header
11
11
  end
12
12
 
13
13
  # Calls the given block once for each cell in the {Row}, passing that cell as parameter.
data/lib/tabulo/table.rb CHANGED
@@ -15,73 +15,67 @@ module Tabulo
15
15
  attr_reader :columns
16
16
 
17
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.
18
+ # @param [Array[Symbol]] columns Specifies the initial columns.
20
19
  # Each element of the Array will be used to create a column whose content is
21
20
  # created by calling the corresponding method on each element of sources. Note
22
21
  # the {#add_column} method is a much more flexible way to set up columns on the table.
23
- # @option options [Fixnum, nil] :column_width (nil) The default column width for columns in this
22
+ # @param [Fixnum, nil] column_width The default column width for columns in this
24
23
  # table, not excluding padding. If nil, then DEFAULT_COLUMN_WIDTH will be used.
25
- # @option options [:start, nil, Fixnum] :header_frequency (:start) Controls the display of column headers.
24
+ # @param [:start, nil, Fixnum] header_frequency Controls the display of column headers.
26
25
  # If passed <tt>:start</tt>, headers will be shown at the top of the table only. If passed <tt>nil</tt>,
27
26
  # headers will not be shown. If passed a Fixnum N (> 0), headers will be shown at the top of the table,
28
27
  # then repeated every N rows.
29
- # @option options [nil, Fixnum] :wrap_header_cells_to (nil) Controls wrapping behaviour for header
28
+ # @param [nil, Fixnum] wrap_header_cells_to Controls wrapping behaviour for header
30
29
  # cells if the content thereof is longer than the column's fixed width. If passed <tt>nil</tt> (default),
31
30
  # content will be wrapped for as many rows as required to accommodate it. If passed a Fixnum N (> 0),
32
31
  # content will be wrapped up to N rows and then truncated thereafter.
33
- # @option options [nil, Fixnum] :wrap_body_cells_to (nil) Controls wrapping behaviour for table cells (excluding
32
+ # @param [nil, Fixnum] wrap_body_cells_to Controls wrapping behaviour for table cells (excluding
34
33
  # headers), if their content is longer than the column's fixed width. If passed <tt>nil</tt>, content will
35
34
  # be wrapped for as many rows as required to accommodate it. If passed a Fixnum N (> 0), content will be
36
35
  # wrapped up to N rows and then truncated thereafter.
37
36
  #
38
37
  # @return [Table] a new Table
39
- def initialize(sources, options = { })
40
- opts = {
41
- columns: [],
42
- column_width: DEFAULT_COLUMN_WIDTH,
43
- header_frequency: :start,
38
+ def initialize(sources, columns: [], column_width: nil, header_frequency: :start,
39
+ wrap_header_cells_to: nil, wrap_body_cells_to: nil)
44
40
 
45
- # nil to wrap to no max, 1 to wrap to 1 row then truncate, etc..
46
- wrap_header_cells_to: nil,
47
- wrap_body_cells_to: nil
41
+ @sources = sources
42
+ @header_frequency = header_frequency
43
+ @wrap_header_cells_to = wrap_header_cells_to
44
+ @wrap_body_cells_to = wrap_body_cells_to
48
45
 
49
- }.merge(options)
46
+ @default_column_width = (column_width || DEFAULT_COLUMN_WIDTH)
50
47
 
51
- @header_frequency = opts[:header_frequency]
52
- @wrap_header_cells_to = opts[:wrap_header_cells_to]
53
- @wrap_body_cells_to = opts[:wrap_body_cells_to]
54
- @sources = sources
55
48
  @joiner = "|"
56
49
  @truncation_indicator = "~"
57
50
  @padding_character = " "
58
- @default_column_width = opts[:column_width] || DEFAULT_COLUMN_WIDTH
59
- @columns = opts[:columns].map { |item| make_column(item) }
51
+
52
+ @columns = []
53
+ columns.each { |item| add_column(item) }
54
+
60
55
  yield self if block_given?
61
56
  end
62
57
 
63
58
  # Adds a column to the Table.
64
59
  #
65
60
  # @param [Symbol, String] label A unique identifier for this column, which by default will
66
- # also be used as the column header text (see also the header option). If the
61
+ # also be used as the column header text (see also the header param). If the
67
62
  # extractor argument is not also provided, then the label argument should correspond to
68
63
  # a method to be called on each item in the table sources to provide the content
69
64
  # for this column.
70
65
  #
71
- # @param [Hash] options
72
- # @option options [String] :header Text to be displayed in the column header. By default the column
73
- # label will also be used as its header text.
74
- # @option options [:left, :center, :right] :align_header (:center) Specifies how the header text
66
+ # @param [nil, #to_s] header (nil) Text to be displayed in the column header. If passed nil,
67
+ # the column's label will also be used as its header text.
68
+ # @param [:left, :center, :right] align_header (:center) Specifies how the header text
75
69
  # should be aligned.
76
- # @option options [:left, :center, :right, nil] :align_body (nil) Specifies how the cell body contents
70
+ # @param [:left, :center, :right, nil] align_body (nil) Specifies how the cell body contents
77
71
  # should be aligned. Possible If <tt>nil</tt> is passed, then the alignment is determined
78
72
  # by the type of the cell value, with numbers aligned right, booleans center-aligned, and
79
73
  # other values left-aligned. Note header text alignment is configured separately using the
80
- # :align_header option.
81
- # @option options [Fixnum] :width (nil) Specifies the width of the column, excluding padding. If
82
- # nil, then the column will take the width provided by the `column_width` option
74
+ # :align_header param.
75
+ # @param [Fixnum] width (nil) Specifies the width of the column, excluding padding. If
76
+ # nil, then the column will take the width provided by the `column_width` param
83
77
  # with which the Table was initialized.
84
- # @option options [#to_proc] :formatter (:to_s.to_proc) A lambda or other callable object that
78
+ # @param [#to_proc] formatter (:to_s.to_proc) A lambda or other callable object that
85
79
  # will be passed the calculated value of each cell to determine how it should be displayed. This
86
80
  # is distinct from the extractor (see below). For example, if the extractor for this column
87
81
  # generates a Date, then the formatter might format that Date in a particular way.
@@ -92,8 +86,18 @@ module Tabulo
92
86
  # column. If this is not provided, then the column label will be treated as a method to be
93
87
  # called on each source item to determine each cell's value.
94
88
  #
95
- def add_column(label, options = { }, &extractor)
96
- @columns << make_column(label, options.merge(extractor: extractor))
89
+ def add_column(label, header: nil, align_header: :center, align_body: nil,
90
+ width: nil, formatter: :to_s.to_proc, &extractor)
91
+
92
+ @columns << Column.new(
93
+ label: label.to_sym,
94
+ header: (header || label).to_s,
95
+ align_header: align_header,
96
+ align_body: align_body,
97
+ width: (width || @default_column_width),
98
+ formatter: formatter,
99
+ extractor: (extractor || label.to_proc)
100
+ )
97
101
  end
98
102
 
99
103
  # @return [String] a graphical "ASCII" representation of the Table, suitable for
@@ -132,7 +136,7 @@ module Tabulo
132
136
 
133
137
  # @return [String] an "ASCII" graphical representation of the Table column headers.
134
138
  def formatted_header
135
- format_row(true, &:header_cell)
139
+ format_row(@wrap_header_cells_to, &:header_subcells)
136
140
  end
137
141
 
138
142
  # @return [String] an "ASCII" graphical representation of a horizontal
@@ -145,7 +149,8 @@ module Tabulo
145
149
  # end
146
150
  #
147
151
  def horizontal_rule
148
- format_row(false, HORIZONTAL_RULE_CHARACTER, CORNER_CHARACTER, &:horizontal_rule)
152
+ inner = @columns.map { |column| surround(column.horizontal_rule, HORIZONTAL_RULE_CHARACTER) }
153
+ surround_join(inner, CORNER_CHARACTER)
149
154
  end
150
155
 
151
156
  # Reset all the column widths so that each column is *just* wide enough to accommodate
@@ -157,23 +162,28 @@ module Tabulo
157
162
  # be traversed and all the column extractors and formatters to be applied in order
158
163
  # to calculate the required widths.
159
164
  #
160
- # @param [Hash] options
161
- # @option options [String] :max_table_width (nil) If provided, stops the total table
165
+ # @param [nil, Numeric] max_table_width (nil) If provided, stops the total table
162
166
  # width (including padding and borders) from expanding beyond this number of characters.
163
167
  # Width is deducted from columns if required to achieve this, with one character progressively
164
168
  # deducted from the width of the widest column until the target is reached. When the
165
169
  # table is printed, wrapping or truncation will then occur in these columns as required
166
- # (depending on how they were configured).
170
+ # (depending on how they were configured). Note that regardless of the value passed to
171
+ # max_table_width, the table will always be left wide enough to accommodate at least
172
+ # 1 character's width of content, 1 character of left padding and 1 character of right padding
173
+ # in each column, together with border characters (1 on each side of the table and 1 between
174
+ # adjacent columns). I.e. there is a certain width below width the Table will refuse to
175
+ # shrink itself.
167
176
  #
168
177
  # @return [Table] the Table itself
169
- def shrinkwrap!(options = { })
178
+ def shrinkwrap!(max_table_width: nil)
170
179
  return self if columns.none?
171
- max_table_width = options[:max_table_width]
172
180
 
173
181
  header_widths = columns.map { |c| c.header.length }
174
182
 
175
183
  column_widths = @sources.inject(header_widths) do |widths, source|
176
- columns.map { |c| c.formatted_cell_content(source).length }.zip(widths).map(&:max)
184
+ columns.map do |c|
185
+ c.formatted_cell_content(source).split($/, -1).map(&:length).max || 1
186
+ end.zip(widths).map(&:max)
177
187
  end
178
188
 
179
189
  columns.zip(column_widths).each do |column, width|
@@ -188,7 +198,6 @@ module Tabulo
188
198
 
189
199
  # Ensure max table width is at least wide enough to accommodate table borders and padding
190
200
  # and one character of content.
191
- # TODO Document this behaviour.
192
201
  min_table_width = total_padding + total_borders + columns.count
193
202
  max_table_width = min_table_width if min_table_width > max_table_width
194
203
 
@@ -207,9 +216,9 @@ module Tabulo
207
216
  end
208
217
 
209
218
  # @!visibility private
210
- def formatted_body_row(source, options = { with_header: false })
211
- inner = format_row { |column| column.body_cell(source) }
212
- if options[:with_header]
219
+ def formatted_body_row(source, with_header: false)
220
+ inner = format_row(@wrap_body_cells_to) { |column| column.body_subcells(source) }
221
+ if with_header
213
222
  join_lines([horizontal_rule, formatted_header, horizontal_rule, inner])
214
223
  else
215
224
  inner
@@ -219,56 +228,70 @@ module Tabulo
219
228
  private
220
229
 
221
230
  # @!visibility private
222
- def body_row(source, options = { with_header: false })
223
- Row.new(self, source, options)
231
+ def body_row(source, with_header: false)
232
+ Row.new(self, source, with_header: with_header)
224
233
  end
225
234
 
226
235
  # @!visibility private
227
- def format_row(header = false, padder = @padding_character, joiner = @joiner)
228
- # TODO Tidy this up -- or at least comment it.
236
+ #
237
+ # Yields each column to passed block, then wraps and joins the results to form
238
+ # a formatted row.
239
+ def format_row(wrap_cells_to)
240
+
241
+ # Create an array of "cell stacks", each of which is an array of strings that
242
+ # will be stacked on top of each other to form a wrapped cell.
229
243
  cell_stacks = @columns.map do |column|
230
- raw = yield column
231
- wrap = (header ? @wrap_header_cells_to : @wrap_body_cells_to)
232
244
  column_width = column.width
233
- cell_body_length = (wrap ? column_width * wrap : raw.length)
234
- truncated = (cell_body_length < raw.length)
235
- cell_body = raw[0...cell_body_length]
236
- num_subcells = (cell_body_length.to_f / column_width).ceil
237
- (0...num_subcells).map do |i|
238
- s = cell_body.slice(i * column_width, column_width)
239
- right_padder = ((truncated && i == num_subcells - 1) ? @truncation_indicator : padder)
240
- "#{padder}#{s}#{padder * (column_width - s.length)}#{right_padder}"
245
+
246
+ # Get the raw, non-wrapped, non-truncated content of the cell.
247
+ raw_subcells = yield column
248
+
249
+ truncated = (wrap_cells_to && (raw_subcells.size > wrap_cells_to))
250
+ subcells = (wrap_cells_to ? raw_subcells[0...wrap_cells_to] : raw_subcells)
251
+
252
+ subcells.map.with_index do |subcell, i|
253
+ lpad = @padding_character
254
+ rpad = (truncated && (i == subcells.size - 1) ? @truncation_indicator : @padding_character)
255
+ "#{lpad}#{subcell}#{rpad}"
241
256
  end
242
257
  end
243
258
 
244
- subrows = (0...cell_stacks.map(&:size).max || 1).map do |subrow_index|
259
+ max_cell_stack_height = cell_stacks.map(&:size).max || 1
260
+
261
+ # A subrow is a string representing a single horizontal slice of this row that's
262
+ # strictly one character high.
263
+ subrows = (0...max_cell_stack_height).map do |subrow_index|
245
264
  cell_stacks.map.with_index do |cell_stack, column_index|
246
265
  if subrow_index < cell_stack.size
266
+ # This cell stack is at least as "deep" as the subrow we're on. So just
267
+ # grab the subcell for this subrow from this cell stack.
247
268
  cell_stack[subrow_index]
248
269
  else
249
- "#{padder}#{' ' * @columns[column_index].width}#{padder}"
270
+ # This cell stack is not "deep" enough. So we make an empty subcell to
271
+ # add to this subrow for this column
272
+ surround(' ' * @columns[column_index].width, @padding_character)
250
273
  end
251
274
  end
252
275
  end
253
276
 
254
- join_lines(subrows.map { |subrow| "#{joiner}#{subrow.join(joiner)}#{joiner}" })
277
+ # Join each subrow with border characters, then join these with newlines, to form
278
+ # the wrapped, formatted row as a single string.
279
+ join_lines(subrows.map { |subrow| surround_join(subrow, @joiner) })
255
280
  end
256
281
 
257
282
  # @!visibility private
258
- def join_lines(lines)
259
- lines.join($/) # join strings with cross-platform newline
283
+ def surround(str, ch0)
284
+ "#{ch0}#{str}#{ch0}"
285
+ end
286
+
287
+ # @!visibility private
288
+ def surround_join(arr, ch)
289
+ surround(arr.join(ch), ch)
260
290
  end
261
291
 
262
292
  # @!visibility private
263
- def make_column(item, options = { })
264
- Column.new({
265
- label: item.to_sym,
266
- header: item.to_s,
267
- align_header: :center,
268
- width: @default_column_width,
269
- formatter: :to_s.to_proc
270
-
271
- }.merge(options))
293
+ def join_lines(lines)
294
+ lines.join($/) # join strings with cross-platform newline
272
295
  end
273
296
  end
274
297
  end
@@ -1,3 +1,3 @@
1
1
  module Tabulo
2
- VERSION = "0.5.1"
2
+ VERSION = "0.6.0"
3
3
  end
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.5.1
4
+ version: 0.6.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-28 00:00:00.000000000 Z
11
+ date: 2017-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler