simple-sql 0.5.15 → 0.5.16

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47d594c256de7bf4157b70be496694616ade42814fb0a1559a67fc893b61cbd2
4
- data.tar.gz: e8c200c5a73ea3b64787ab597ea7eb6b8c4a4e9be06416c968eb86654aaf27d9
3
+ metadata.gz: 10f81f5db33aaa6a5f76d3b22fa5f73cd5327fd6fca0f276b112024db322f852
4
+ data.tar.gz: 7c4b1301bd7fb1de48957cd1cb1d7b402cabd99ee1d3defb7d9971290168159f
5
5
  SHA512:
6
- metadata.gz: b4ea4c955d1957d9e774e7aa27f8bc5508014a6ef75090acbe6ba46319e4f443899ae46bad712a11b45aa19a5240d2f68dba8639fa8f8f811e6fd6e3cc796be1
7
- data.tar.gz: 7f220c8ec126a72dec5369bd9600f361ee1ecd3e552f437b9d136d36067f6564978402184f488a5289c68a8997ce834490b31019480c9fe1fbe816a5a55c2909
6
+ metadata.gz: b4a7f2c661bf5751a2d6c06ddf9e261f0327832f236d80ef19da676826899cff721e0bb21e16d8d8bf64019a3006bc11d1720730d3d512fc991c023c464c61ed
7
+ data.tar.gz: ed8e1227f194bdd687e5329c22dc8406ed076d25d973dc3b7624dca033c0db86ac1c84804cba204b169a20c73d5028d0c3ccd0d2047f321a2b4b966fe9301fb9
@@ -68,7 +68,7 @@ class Simple::SQL::Connection
68
68
  records = all sql, *args, into: Hash
69
69
  end
70
70
 
71
- Simple::SQL::Helpers::Printer.print(records, width: width, io: io)
71
+ ::Simple::SQL::Helpers::Printer.print(records, width: width, io: io)
72
72
  records
73
73
  end
74
74
 
@@ -1,4 +1,4 @@
1
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
1
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
2
2
 
3
3
  # private
4
4
  module Simple::SQL::Helpers::Printer
@@ -6,30 +6,42 @@ module Simple::SQL::Helpers::Printer
6
6
 
7
7
  ROW_SEPARATOR = " | "
8
8
 
9
- def self.print(records, io: STDOUT, width: :auto)
9
+ def print(records, io: STDOUT, width: :auto)
10
10
  # check args
11
11
 
12
12
  return if records.empty?
13
- return if records.first.keys.empty?
14
13
 
15
- if width == :auto && io.isatty
16
- width = `tput cols`.to_i
17
- end
14
+ column_count = records.first.length
15
+ return if column_count == 0
16
+
17
+ # -- determine/adjust total width for output. -----------------------------
18
+
19
+ width = terminal_width(io) if width == :auto
18
20
  width = nil if width && width <= 0
19
21
 
20
- # prepare printing
22
+ # -- prepare printing -----------------------------------------------------
21
23
 
22
24
  rows = materialize_rows(records)
23
- column_widths = calculate_column_widths(rows)
24
- column_widths = optimize_column_widths(column_widths, width, rows.first.length) if width
25
+ column_widths = column_max_lengths(rows)
26
+ if width
27
+ column_widths = distribute_column_widths(column_widths, width, column_count, rows.first)
28
+ end
25
29
 
26
- # print
30
+ # -- print ----------------------------------------------------------------
27
31
 
28
32
  print_records(rows, io, column_widths)
29
33
  end
30
34
 
31
35
  private
32
36
 
37
+ def terminal_width(io)
38
+ return unless io.isatty
39
+
40
+ `tput cols`.to_i
41
+ rescue Errno::ENOENT
42
+ nil
43
+ end
44
+
33
45
  def materialize_rows(records)
34
46
  keys = records.first.keys
35
47
 
@@ -41,31 +53,75 @@ module Simple::SQL::Helpers::Printer
41
53
  rows
42
54
  end
43
55
 
44
- def calculate_column_widths(rows)
56
+ def column_max_lengths(rows)
45
57
  rows.inject([0] * rows.first.length) do |ary, row|
46
58
  ary.zip(row.map(&:length)).map(&:max)
47
59
  end
48
60
  end
49
61
 
50
- def optimize_column_widths(column_widths, width, column_count)
51
- required_width = column_widths.sum + column_count * ROW_SEPARATOR.length
52
- overflow = required_width - width
53
- return column_widths if overflow <= 0
62
+ MIN_COLUMN_WIDTH = 7
63
+
64
+ def distribute_column_widths(column_widths, total_chars, column_count, title_row)
65
+ # caluclate available width: this is the number of characters available in
66
+ # total, reduced by the characters "wasted" for row separators.
67
+ available_chars = total_chars - (column_count - 1) * ROW_SEPARATOR.length
68
+
69
+ return column_widths if available_chars <= 0
70
+
71
+ # [TODO] The algorithm below produces ok-ish results - but usually misses a few characters
72
+ # that could still be assigned a column. To do this we shuld emply D'Hondt or something
73
+ # similar.
74
+
75
+ # -- initial setup --------------------------------------------------------
76
+ #
77
+ # We guarantee each column a minimum number of characters of MIN_COLUMN_WIDTH.
78
+ # If the column does not need that many characters, it will only be allocated
79
+ # the number of characters that are really necessary.
80
+ #
81
+ # If necessary we then extend a column to fit its title.
54
82
 
55
- # TODO: Use d'hondt with a minimum percentage for a fairer distribution
56
- # The following is just a quick hack...
57
- overflow += 40
83
+ result = [MIN_COLUMN_WIDTH] * column_count
84
+ result = result.zip(column_widths).map(&:min)
85
+ result = result.zip(title_row).map { |r, title| [r, title.length].max }
86
+
87
+ # -- return if there are no more characters available ---------------------
88
+
89
+ # This happens if the terminal is **way** to narrow.
90
+ return column_widths if result.sum > available_chars
91
+
92
+ # -- distribute unassigned characters -------------------------------------
93
+
94
+ unassigned_widths = column_widths.zip(result).sum { |cw, r| cw - r }
95
+ if unassigned_widths > 0
96
+ available_space = available_chars - result.sum
97
+ if available_space > 0
98
+
99
+ result = result.zip(column_widths).map do |r, cw|
100
+ r + (cw - r) * available_space / unassigned_widths
101
+ end
102
+ end
103
+ end
104
+
105
+ # [TODO] We can still have available characters at this point.
106
+ # unassigned_chars = available_chars - result.sum
107
+
108
+ result
109
+ end
58
110
 
59
- column_widths.map do |col_width|
60
- (col_width - overflow * col_width * 1.0 / required_width).to_i
111
+ def format_value(value, width)
112
+ if value.length < width
113
+ "%-#{width}s" % value
114
+ elsif value.length == width
115
+ value
116
+ else
117
+ value[0, width - 1] + "…"
61
118
  end
62
119
  end
63
120
 
64
121
  def print_records(rows, io, column_widths)
65
122
  rows.each_with_index do |row, idx|
66
123
  parts = row.zip(column_widths).map do |value, col_width|
67
- s = "%-#{col_width}s " % value
68
- s[0..col_width]
124
+ format_value value, col_width
69
125
  end
70
126
 
71
127
  io.puts parts.join(ROW_SEPARATOR)
@@ -1,5 +1,5 @@
1
1
  module Simple
2
2
  module SQL
3
- VERSION = "0.5.15"
3
+ VERSION = "0.5.16"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple-sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.15
4
+ version: 0.5.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - radiospiel
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-06-28 00:00:00.000000000 Z
12
+ date: 2019-07-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pg_array_parser