tabulo 1.3.0 → 1.4.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
  SHA256:
3
- metadata.gz: 6ca950eccef1e0d5bcd333ce42584fd4a95020316e80d2f684b721f0cb6ec00a
4
- data.tar.gz: ea4c7f96ebce149b131db590fdeb75a54152576717007024556047a36004469f
3
+ metadata.gz: 53f13b68dff441391dc7250a607d3d449f52cf2735ba2259bf01ac60d996014e
4
+ data.tar.gz: 2a3b0f1cd2225cf8ef4d143f115de50a24f23253888770d04c9329b76f5e15e5
5
5
  SHA512:
6
- metadata.gz: 2f83f8889ad5f72954bc0a16a743c6ecdb00b732fdac29d179a43874eba68b021f180be981bbce82773bf4f8f0e968a92c03400cf9af74cf8a6bb23ed585cf77
7
- data.tar.gz: f9a47229fe11cfc79ea265f4280ca84666e7e09c7d325db4e4b4feb9c2a854f76a9c1647fdcbd2eff3a5073fd327044be75758af839f227750ab1fd24aa2eccc
6
+ metadata.gz: 48358f0322bbd3bf6c85305e885d1a6afa6028a0688d8d9442eb1c67bd46870866c120fcb1aed4c33ce176e9cb72f60f61d657044a71a21f1c2960689225ed51
7
+ data.tar.gz: 55f578f021f9b7516fef26ef0b7f4c583d0400c7bc8a1d069cdee609752a734698c73719294608a16bbb1709d9461657b8b354e8622233ba34b341653814ac95
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ### v1.4.0
4
+
5
+ * New `#transpose` function to produced a new Table in which the rows and
6
+ columns are transposed relative to the original one.
7
+ * Properly handle multibyte characters when calculating widths, wrapping etc..
8
+
3
9
  ### v1.3.0
4
10
 
5
11
  * More ergonomic Table initializer, allowing you to specify columns directly as varargs rather
data/README.md CHANGED
@@ -5,10 +5,14 @@
5
5
  [![Coverage Status][CS img]][Coverage Status]
6
6
  [![Build Status][BS img]][Build Status]
7
7
  [![Code Climate][CC img]][Code Climate]
8
+ [![Awesome][AR img]][Awesome Ruby]
8
9
 
9
- ## Overview
10
+ Tabulo is a terminal table generator for Ruby.
11
+
12
+ It offers a DRY, "column-centric" interface, and is designed to make it very easy it produce highly
13
+ readable tables, even from large and unwieldy data sets and streams.
10
14
 
11
- Tabulo is a Ruby library for generating ASCII tables.
15
+ ## Overview
12
16
 
13
17
  *Quick API:*
14
18
 
@@ -44,21 +48,25 @@ end
44
48
 
45
49
  ## Features
46
50
 
47
- * Lets you set [fixed column widths](#fixed-column-widths), then either [wrap](#overflow-handling) or
48
- [truncate](#overflow-handling) the overflow.
49
- * Alternatively, ["pack"](#pack) the table so that each column is automatically just wide enough for its
50
- contents, but [without overflowing the terminal horizontally](#max-table-width).
51
- * Alignment of cell content is [configurable](#cell-alignment), but has helpful content-based defaults
52
- (numbers right, strings left).
53
- * Headers are [repeatable](#repeating-headers).
54
- * Newlines within cell content are correctly handled.
55
- * A `Tabulo::Table` is an `Enumerable`, so you can [step through it](#enumerator) a row at a time,
56
- printing as you go, without waiting for the entire underlying collection to load.
57
- * Each `Tabulo::Row` is also an `Enumerable`, [providing access](#accessing-cell-values) to the underlying cell values.
51
+ * Lets you set [fixed column widths](#fixed-column-widths), then either [wrap](#overflow-handling)
52
+ or [truncate](#overflow-handling) the overflow.
53
+ * Alternatively, ["pack"](#pack) the table so that each column is automatically just wide enough for
54
+ its contents, but [without overflowing the terminal horizontally](#max-table-width).
55
+ * Alignment of cell content is [configurable](#cell-alignment), but has helpful content-based
56
+ defaults (numbers right, strings left).
58
57
  * Tabulate any `Enumerable`: the underlying collection need not be an array.
58
+ * Since a `Tabulo::Table` is itself also an `Enumerable`, you can [step through it](#enumerator) a
59
+ row at a time, printing as you go, without waiting for the entire underlying collection to load.
60
+ In other words, you get a [streaming interface](#enumerator) for free.
61
+ * Each `Tabulo::Row` is also an `Enumerable`, [providing access](#accessing-cell-values) to the
62
+ underlying cell values.
63
+ * The header row can be [repeated](#repeating-headers) at arbitrary intervals.
64
+ * Newlines within cell content are correctly handled.
65
+ * Multibyte characters are correctly handled.
66
+ * Easily [transpose](#transposition) the table, so that rows are swapped with columns.
59
67
  * [Customize](#additional-configuration-options) border and divider characters.
60
- * Use a [DRY interface](#configuring-columns): by being "column based", it is designed to spare the
61
- developer the burden of syncing the ordering within the header row with that of the body rows.
68
+ * Use a [DRY initialization interface](#configuring-columns): by being "column based", it is
69
+ designed to spare the developer the burden of syncing the ordering within the header row with that of the body rows.
62
70
 
63
71
  Tabulo has also been ported to Crystal (with some modifications): see [Tablo](https://github.com/hutou/tablo).
64
72
 
@@ -82,6 +90,7 @@ Tabulo has also been ported to Crystal (with some modifications): see [Tablo](ht
82
90
  * [Using a Table Enumerator](#using-a-table-enumerator)
83
91
  * [Accessing cell values](#accessing-cell-values)
84
92
  * [Accessing the underlying enumerable](#accessing-sources)
93
+ * [Transposing rows and columns](#transposition)
85
94
  * [Additional configuration options](#additional-configuration-options)
86
95
  * [Motivation](#motivation)
87
96
  * [Contributing](#contributing)
@@ -180,7 +189,7 @@ table = Tabulo::Table.new([1, 2], :itself, :even?, align_header: :left, align_bo
180
189
  ```
181
190
 
182
191
  The table-level alignment settings can be overridden for individual columns by
183
- passing using similarly-named options passed to `add_column`, e.g.:
192
+ passing similarly-named options to `add_column`, e.g.:
184
193
 
185
194
  ```ruby
186
195
  table.add_column("Doubled", align_header: :right, align_body: :left) { |n| n * 2 }
@@ -278,7 +287,7 @@ puts Tabulo::Table.new([1, 2], :itself, :even?).pack(max_table_width: 17)
278
287
  | 2 | true |
279
288
  ```
280
289
 
281
- Or if you simply call `pack` with no parameters (or if you explicitly call
290
+ Or if you simply call `pack` with no arguments (or if you explicitly call
282
291
  `pack(max_table_width: :auto)`), the table width will automatically be capped at the
283
292
  width of your terminal.
284
293
 
@@ -458,11 +467,11 @@ table.each do |row|
458
467
  end
459
468
  ```
460
469
 
461
- The first argument to `add_column`, considered as a `Symbol`, always provides the key
462
- for the purpose of accessing the `Hash` form of a `Tabulo::Row`. This key serves as
463
- a sort of "logical label" for the column; and it need not be the same as the column
464
- header. If we want the header to be different to the label, we can achieve this
465
- using the `header` option to `add_column`:
470
+ The first argument to `add_column` always provides the key for the purpose of accessing the `Hash`
471
+ form of a `Tabulo::Row`. (If the provided argument was a `String`, it will be converted to a
472
+ `Symbol` for purposes of accessing this `Hash`.) This key serves as a sort of "logical label" for
473
+ the column; and it need not be the same as the column header. If we want the header to be different
474
+ to the label, we can achieve this using the `header` option to `add_column`:
466
475
 
467
476
  ```ruby
468
477
  table = Tabulo::Table.new(1..5) do |t|
@@ -510,9 +519,38 @@ row can be accessed by calling the `source` method on that row:
510
519
  table.each do |row|
511
520
  puts row.source # 50...60...
512
521
  end
522
+ ```
523
+
524
+ <a name="transposition"></a>
525
+ ### Transposing rows and columns
513
526
 
527
+ By default, Tabulo generates a table in which each row corresponds to a _record_, i.e. an element of
528
+ the underlying enumerable, and each column to a _field_. However, there are times when one instead
529
+ wants each row to represent a field, and each column a record. This is generally the case when there
530
+ are a small number or records but a large number of fields. To produce such a table, we can first
531
+ initialize an ordinary table, specifying fields as columns, and then call `transpose`, which returns
532
+ a new table in which the rows and columns are swapped:
533
+
534
+ ```ruby
535
+ > puts Tabulo::Table.new(-1..1, :even?, :odd?, :zero?, :pred, :succ, :abs).transpose
536
+ ```
537
+ ```
538
+ +-------+--------------+--------------+--------------+
539
+ | | -1 | 0 | 1 |
540
+ +-------+--------------+--------------+--------------+
541
+ | even? | false | true | false |
542
+ | odd? | true | false | true |
543
+ | zero? | false | true | false |
544
+ | pred | -2 | -1 | 0 |
545
+ | succ | 0 | 1 | 2 |
546
+ | abs | 1 | 0 | 1 |
514
547
  ```
515
548
 
549
+ By default, a header row is added to the new table, showing the string value of the element
550
+ represented in that column. This can be configured, however, along with other aspects of
551
+ `transpose`'s behaviour. For details, see the documentation. (TODO Link to
552
+ documentation for #transpose, once available at rubydoc site.)
553
+
516
554
  <a name="additional-configuration-options"></a>
517
555
  ### Additional configuration options
518
556
 
@@ -556,19 +594,19 @@ table = Tabulo::Table.new(1..3, :itself, :even?)
556
594
 
557
595
  There are other terminal table generators for Ruby. Popular among these are:
558
596
 
559
- * [tty-table](https://github.com/piotrmurach/tty-table)
560
597
  * [terminal-table](https://github.com/tj/terminal-table)
598
+ * [tty-table](https://github.com/piotrmurach/tty-table)
561
599
  * [table\_print](https://github.com/arches/table_print)
562
600
 
563
- *DISCLAIMER: My comments regarding these other libraries are based only on my own,
564
- possibly-flawed, reading of the documentation for, and experimentation with, these libraries at the
565
- time of my writing this. Their APIs, features or documentation may well change between when I write this, and
566
- when you read it&mdash;possibly in ways that address the issues I describe here!*
601
+ *DISCLAIMER: My comments regarding these other libraries are based only on my own, possibly flawed
602
+ reading of the documentation for, and experimentation with, these libraries at the time of my
603
+ writing this. Their APIs, features or documentation may well change between when I write this, and
604
+ when you read it. Please consult the libraries' own documentation for yourself, rather than relying
605
+ on these comments.*
567
606
 
568
- These are excellent libraries, and each has its strengths. However, I personally found that for the
569
- common use case of printing a table on the basis of some underlying collection (such as an
570
- ActiveRecord query result), using these libraries in practice was more cumbersome than it needed to
571
- be.
607
+ While these libraries have their strengths, I personally found that for the common use case of
608
+ printing a table on the basis of some underlying enumerable collection (such as an ActiveRecord
609
+ query result), using these libraries felt more cumbersome than it needed to be.
572
610
 
573
611
  For example, suppose we have called `User.all` from the Rails console, and want to print
574
612
  a table showing the email, first name, last name and ID of each user,
@@ -586,17 +624,17 @@ puts table
586
624
 
587
625
  The problem here is that there is no single source of knowledge about which columns
588
626
  appear, and in which order. If we want to add another column to the left of "email",
589
- we need to amend the rows map, and the headings map, and the index passed to `align_column`.
627
+ we need to amend the rows array, and the headings array, and the index passed to `align_column`.
590
628
  We bear the burden of keeping these three in sync. This is not be a big deal for small one-off
591
629
  tables, but for tables that have many columns, or that are constructed
592
630
  dynamically based on user input or other runtime factors determining the columns
593
631
  to be included, this can be a hassle and a source of brittleness.
594
632
 
595
633
  `tty-table` has a somewhat different API to `terminal-table`. It offers both a
596
- "row-based" and a "column-based" method of initializing a table. We won't cover
597
- the row-based method here, but it is similar to `terminal-table`'s in that it
598
- burdens the developer with syncing the column ordering across multiple code
599
- locations. The "column-based" API for `tty-table`, on the other hand, looks like this:
634
+ "row-based" and a "column-based" method of initializing a table. The row-based method
635
+ is similar to `terminal-table`'s in that it burdens the developer with syncing the
636
+ column ordering across multiple code locations. The "column-based" API for `tty-table`, on
637
+ the other hand, seems to avoid this problem. One way of using it is like this:
600
638
 
601
639
  ```ruby
602
640
  users = User.all
@@ -652,13 +690,15 @@ The gem is available as open source under the terms of the [MIT
652
690
  License](http://opensource.org/licenses/MIT).
653
691
 
654
692
  [Gem Version]: https://rubygems.org/gems/tabulo
655
- [Documentation]: http://www.rubydoc.info/gems/tabulo/1.3.0
693
+ [Documentation]: http://www.rubydoc.info/gems/tabulo/1.4.0
656
694
  [Build Status]: https://travis-ci.org/matt-harvey/tabulo
657
695
  [Coverage Status]: https://coveralls.io/r/matt-harvey/tabulo
658
696
  [Code Climate]: https://codeclimate.com/github/matt-harvey/tabulo
697
+ [Awesome Ruby]: https://github.com/markets/awesome-ruby#cli-utilities
659
698
 
660
699
  [GV img]: https://img.shields.io/gem/v/tabulo.svg
661
- [DC img]: https://img.shields.io/badge/documentation-v1.3.0-blue.svg
700
+ [DC img]: https://img.shields.io/badge/documentation-v1.4.0
662
701
  [BS img]: https://img.shields.io/travis/matt-harvey/tabulo.svg
663
702
  [CS img]: https://img.shields.io/coveralls/matt-harvey/tabulo.svg
664
703
  [CC img]: https://codeclimate.com/github/matt-harvey/tabulo/badges/gpa.svg
704
+ [AR img]: https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0
1
+ 1.4.0
@@ -1 +1 @@
1
- theme: jekyll-theme-cayman
1
+ theme: jekyll-theme-dinky
@@ -1,5 +1,6 @@
1
1
  require "tabulo/deprecation"
2
2
  require "tabulo/exceptions"
3
+ require "tabulo/util"
3
4
  require "tabulo/table"
4
5
  require "tabulo/version"
5
6
  require "tabulo/row"
@@ -1,3 +1,5 @@
1
+ require "unicode/display_width"
2
+
1
3
  module Tabulo
2
4
 
3
5
  # @!visibility private
@@ -38,15 +40,28 @@ module Tabulo
38
40
 
39
41
  def infilled_subcells(str, real_alignment)
40
42
  str.split($/, -1).flat_map do |substr|
41
- num_subsubcells = [1, (substr.length.to_f / width).ceil].max
42
- (0...num_subsubcells).map do |i|
43
- align_cell_content(substr.slice(i * width, width), real_alignment)
43
+ substr_grapheme_clusters = substr.scan(/\X/)
44
+ subsubcells = []
45
+ current_subsubcell_grapheme_clusters = []
46
+ current_subsubcell_display_width = 0
47
+ substr_grapheme_clusters.each do |sgc|
48
+ sgc_display_width = Unicode::DisplayWidth.of(sgc)
49
+ if sgc_display_width + current_subsubcell_display_width > width
50
+ subsubcells << current_subsubcell_grapheme_clusters.join("")
51
+ current_subsubcell_grapheme_clusters.clear
52
+ current_subsubcell_display_width = 0
53
+ end
54
+
55
+ current_subsubcell_grapheme_clusters << sgc
56
+ current_subsubcell_display_width += sgc_display_width
44
57
  end
58
+ subsubcells << current_subsubcell_grapheme_clusters.join("")
59
+ subsubcells.map { |s| align_cell_content(s, real_alignment) }
45
60
  end
46
61
  end
47
62
 
48
63
  def align_cell_content(content, real_alignment)
49
- padding = [@width - content.length, 0].max
64
+ padding = [@width - Unicode::DisplayWidth.of(content), 0].max
50
65
  left_padding, right_padding =
51
66
  case real_alignment
52
67
  when :center
@@ -1,5 +1,6 @@
1
1
  module Tabulo
2
2
 
3
+ # @!visibility private
3
4
  module Deprecation
4
5
 
5
6
  # @!visibility private
@@ -43,7 +43,7 @@ module Tabulo
43
43
  # @return a Hash representation of the {Row}, with column labels acting
44
44
  # as keys and the calculated cell values (before formatting) providing the values.
45
45
  # @example
46
- # table = Tabulo::Table.new([1, 10], columns: %i(itself even?))
46
+ # table = Tabulo::Table.new([1, 10], columns: %i[itself even?])
47
47
  # row = table.first
48
48
  # row.to_h # => { :itself => 1, :even? => false }
49
49
  def to_h
@@ -1,4 +1,5 @@
1
1
  require "tty-screen"
2
+ require "unicode/display_width"
2
3
 
3
4
  module Tabulo
4
5
 
@@ -40,7 +41,7 @@ module Tabulo
40
41
  # be unique. Each element of the Array will be used to create a column whose content is
41
42
  # created by calling the corresponding method on each element of sources. Note
42
43
  # the {#add_column} method is a much more flexible way to set up columns on the table.
43
- # @param [Array[Symbol]] columns <b>DEPRECATED</b> Use {cols} instead.
44
+ # @param [Array[Symbol]] columns <b>DEPRECATED</b> Use <tt>cols</tt> instead.
44
45
  # @param [Integer, nil] column_width The default column width for columns in this
45
46
  # table, not excluding padding. If <tt>nil</tt>, then {DEFAULT_COLUMN_WIDTH} will be used.
46
47
  # @param [:start, nil, Integer] header_frequency Controls the display of column headers.
@@ -125,11 +126,12 @@ module Tabulo
125
126
 
126
127
  # Adds a column to the Table.
127
128
  #
128
- # @param [Symbol, String] label A unique identifier for this column, which by default will
129
- # also be used as the column header text (see also the header param). If the
129
+ # @param [Symbol, String, Integer] label A unique identifier for this column, which by
130
+ # default will also be used as the column header text (see also the header param). If the
130
131
  # extractor argument is not also provided, then the label argument should correspond to
131
132
  # a method to be called on each item in the table sources to provide the content
132
- # for this column.
133
+ # for this column. If a String is passed as the label, then it will be converted to
134
+ # a Symbol for the purpose of serving as this label.
133
135
  # @param [nil, #to_s] header (nil) Text to be displayed in the column header. If passed nil,
134
136
  # the column's label will also be used as its header text.
135
137
  # @param [:left, :center, :right, nil] align_header (nil) Specifies how the header text
@@ -163,7 +165,13 @@ module Tabulo
163
165
  def add_column(label, header: nil, align_header: nil, align_body: nil,
164
166
  width: nil, formatter: :to_s.to_proc, &extractor)
165
167
 
166
- column_label = label.to_sym
168
+ column_label =
169
+ case label
170
+ when Integer, Symbol
171
+ label
172
+ when String
173
+ label.to_sym
174
+ end
167
175
 
168
176
  if column_registry.include?(column_label)
169
177
  raise InvalidColumnLabelError, "Column label already used in this table."
@@ -284,6 +292,83 @@ module Tabulo
284
292
  self
285
293
  end
286
294
 
295
+ # Creates a new {Table} from the current Table, transposed, that is rotated 90 degrees,
296
+ # relative to the current Table, so that the header names of the current Table form the
297
+ # content of left-most column of the new Table, and each column thereafter corresponds to one of the
298
+ # elements of the current Table's <tt>sources</tt>, with the header of that column being the String
299
+ # value of that element.
300
+ #
301
+ # @example
302
+ # puts Tabulo::Table.new(-1..1, :even?, :odd?, :abs).transpose
303
+ # # => +-------+--------------+--------------+--------------+
304
+ # # | | -1 | 0 | 1 |
305
+ # # +-------+--------------+--------------+--------------+
306
+ # # | even? | false | true | false |
307
+ # # | odd? | true | false | true |
308
+ # # | abs | 1 | 0 | 1 |
309
+ #
310
+ # @param [Hash] opts Options for configuring the new, transposed {Table}.
311
+ # The following options are the same as the keyword params for the {#initialize} method for
312
+ # {Table}: <tt>column_width</tt>, <tt>column_padding</tt>, <tt>header_frequency</tt>,
313
+ # <tt>wrap_header_cells_to</tt>, <tt>wrap_body_cells_to</tt>, <tt>horizontal_rule_character</tt>,
314
+ # <tt>vertical_rule_character</tt>, <tt>intersection_character</tt>, <tt>truncation_indicator</tt>,
315
+ # <tt>align_header</tt>, <tt>align_body</tt>.
316
+ # These are applied in the same way as documented for {#initialize}, when creating the
317
+ # new, transposed Table. Any options not specified explicitly in the call to {#transpose}
318
+ # will inherit their values from the original {Table} (with the exception of settings
319
+ # for the left-most column, containing the field names, which are determined as described
320
+ # below). In addition, the following options also apply to {#transpose}:
321
+ # @option opts [nil, Integer] :field_names_width Determines the width of the left-most column of the
322
+ # new Table, which contains the names of "fields" (corresponding to the original Table's
323
+ # column headings). If this is not provided, then by default this column will be made just
324
+ # wide enough to accommodate its contents.
325
+ # @option opts [String] :field_names_header ("") By default the left-most column will have a
326
+ # blank header; but this can be overridden by passing a String to this option.
327
+ # @option opts [:left, :center, :right] :field_names_header_alignment (:right) Specifies how the
328
+ # header text of the left-most column (if it has header text) should be aligned.
329
+ # @option opts [:left, :center, :right] :field_names_body_alignment (:right) Specifies how the
330
+ # body text of the left-most column should be aligned.
331
+ # @option opts [#to_proc] :headers (:to_s.to_proc) A lambda or other callable object that
332
+ # will be passed in turn each of the elements of the current Table's <tt>sources</tt>
333
+ # Enumerable, to determine the text to be displayed in the header of each column of the
334
+ # new Table (other than the left-most column's header, which is determined as described
335
+ # above).
336
+ # @return [Table] a new {Table}
337
+ # @raise [InvalidHorizontalRuleCharacterError] if invalid argument passed to horizontal_rule_character.
338
+ # @raise [InvalidVerticalRuleCharacterError] if invalid argument passed to vertical_rule_character.
339
+ def transpose(opts = {})
340
+ default_opts = [:column_width, :column_padding, :header_frequency, :wrap_header_cells_to,
341
+ :wrap_body_cells_to, :horizontal_rule_character, :vertical_rule_character,
342
+ :intersection_character, :truncation_indicator, :align_header, :align_body].map do |sym|
343
+ [sym, instance_variable_get("@#{sym}")]
344
+ end.to_h
345
+
346
+ initializer_opts = default_opts.merge(Util.slice_hash(opts, *default_opts.keys))
347
+ default_extra_opts = { field_names_width: nil, field_names_header: "",
348
+ field_names_body_alignment: :right, field_names_header_alignment: :right, headers: :to_s.to_proc }
349
+ extra_opts = default_extra_opts.merge(Util.slice_hash(opts, *default_extra_opts.keys))
350
+
351
+ # The underlying enumerable for the new table, is the columns of the original table.
352
+ fields = column_registry.values
353
+
354
+ Table.new(fields, **initializer_opts) do |t|
355
+
356
+ # Left hand column of new table, containing field names
357
+ width_opt = extra_opts[:field_names_width]
358
+ field_names_width = (width_opt.nil? ? fields.map { |f| f.header.length }.max : width_opt)
359
+
360
+ t.add_column(:dummy, header: extra_opts[:field_names_header], width: field_names_width, align_header:
361
+ extra_opts[:field_names_header_alignment], align_body: extra_opts[:field_names_body_alignment], &:header)
362
+
363
+ # Add a column to the new table for each of the original table's sources
364
+ sources.each_with_index do |source, i|
365
+ t.add_column(i, header: extra_opts[:headers].call(source)) do |original_column|
366
+ original_column.body_cell_value(source)
367
+ end
368
+ end
369
+ end
370
+ end
371
+
287
372
  # @deprecated Use {#pack} instead.
288
373
  #
289
374
  # Reset all the column widths so that each column is *just* wide enough to accommodate
@@ -427,7 +512,7 @@ module Tabulo
427
512
  when nil
428
513
  ; # do nothing
429
514
  when String
430
- if c.length != 1
515
+ if Unicode::DisplayWidth.of(c) != 1
431
516
  raise exception_class, "#{message_fragment} is neither nil nor a single-character String"
432
517
  end
433
518
  else
@@ -440,8 +525,9 @@ module Tabulo
440
525
  # @return [Integer] the length of the longest segment of str when split by newlines
441
526
  def wrapped_width(str)
442
527
  segments = str.split($/)
443
- segments.inject(1) do |length, segment|
444
- length > segment.length ? length : segment.length
528
+ segments.inject(1) do |longest_length_so_far, segment|
529
+ length = Unicode::DisplayWidth.of(segment)
530
+ longest_length_so_far > length ? longest_length_so_far : length
445
531
  end
446
532
  end
447
533
  end
@@ -0,0 +1,15 @@
1
+ module Tabulo
2
+
3
+ # @!visibility private
4
+ module Util
5
+
6
+ # @!visibility private
7
+ def self.slice_hash(hash, *keys)
8
+ new_hash = {}
9
+ keys.each { |k| new_hash[k] = hash[k] if hash.include?(k) }
10
+ new_hash
11
+ end
12
+
13
+ end
14
+ end
15
+
@@ -1,3 +1,3 @@
1
1
  module Tabulo
2
- VERSION = "1.3.0"
2
+ VERSION = "1.4.0"
3
3
  end
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
29
29
  }
30
30
 
31
31
  spec.add_runtime_dependency "tty-screen", "0.6.5"
32
+ spec.add_runtime_dependency "unicode-display_width", "1.5.0"
32
33
 
33
34
  spec.add_development_dependency "bundler"
34
35
  spec.add_development_dependency "rake", "~> 11.0"
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: 1.3.0
4
+ version: 1.4.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: 2019-04-22 00:00:00.000000000 Z
11
+ date: 2019-05-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tty-screen
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.6.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: unicode-display_width
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.5.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.5.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -178,6 +192,7 @@ files:
178
192
  - lib/tabulo/exceptions.rb
179
193
  - lib/tabulo/row.rb
180
194
  - lib/tabulo/table.rb
195
+ - lib/tabulo/util.rb
181
196
  - lib/tabulo/version.rb
182
197
  - tabulo.gemspec
183
198
  homepage: https://github.com/matt-harvey/tabulo