markdown-tables 1.0.0 → 1.0.1

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/markdown-tables.rb +85 -94
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 10ffe7f2352e6dce04a280d07a27c968f36a87ea
4
- data.tar.gz: 128eb02c4f699e226b248957eebc260535290228
3
+ metadata.gz: bb85037027b4ed9b6c064ef04ed5ad507ae20859
4
+ data.tar.gz: f1d9d7230c5b4882d050e94d401264b35610c57f
5
5
  SHA512:
6
- metadata.gz: 78e9d514b7afbb39198c30e60ff2b9ccdcaefcda66b7d48096f5dd1e0c99c6315406e931f3f82178b4aa60e8383eb03c44be6d2da397f9f4d6c7e22e14c56157
7
- data.tar.gz: 82f332402510d361145cbef7200a3e709dc94d1c04e3a9243b3fb20a9b7c6c91bea3591cd6052361275b1f121905f896f502ed232d407f4734f549912c4aecd0
6
+ metadata.gz: a3aaa0e871716d174ee1530a5b4a1809bd3b88377f5a6393080e1b7f156594652ec96d69ad478c0e57e5361829a6965b06dc1a157233978f9ad0cbe2ac0f8c1b
7
+ data.tar.gz: c772f17d33eac4696d1e5213d8d81d9bb607a3439757a52b664d6e427d35da6af17c4b19ac56b0c22007249c06f7d3c5bdcd2cf4ac3a3919a255c967924ff077
@@ -6,92 +6,75 @@ class MarkdownTables
6
6
  # Pass align: 'l' for left alignment or 'r' for right alignment. Anything
7
7
  # else will result in cells being centered. Pass an array of align values
8
8
  # to specify alignment per column.
9
- # If is_rows is true, then each sub-array represents a row.
9
+ # If is_rows is true, then each sub-array of data represents a row.
10
10
  # Conversely, if is_rows is false, each sub-array of data represents a column.
11
11
  # Empty cells can be given with nil or an empty string.
12
12
  def self.make_table(labels, data, align: '', is_rows: false)
13
+ validate(labels, data, align, is_rows)
14
+
15
+ # Deep copy the arguments so we don't mutate the originals.
13
16
  labels = Marshal.load(Marshal.dump(labels))
14
17
  data = Marshal.load(Marshal.dump(data))
15
- validate(labels, data, align, is_rows)
16
- sanitize!(labels, data)
17
18
 
19
+ # Remove any breaking Markdown characters.
20
+ labels.map! {|label| sanitize(label)}
21
+ data.map! {|datum| datum.map {|cell| sanitize(cell)}}
22
+
23
+ # Convert align to something that other methods won't need to validate.
24
+ align.class == String && align = [align] * labels.length
25
+ align.map! {|a| a =~ /[lr]/i ? a.downcase : 'c'}
26
+
27
+ # Generate the column labels and alignment line.
18
28
  header_line = labels.join('|')
19
- alignment_line = alignment(align, labels.length)
20
-
21
- if is_rows
22
- rows = data.map {|row| row.join('|')}
23
- else
24
- max_len = data.map(&:size).max
25
- rows = []
26
- max_len.times do |i|
27
- row = []
28
- data.each {|col| row.push(col[i])}
29
- rows.push(row.join('|'))
30
- end
31
- end
29
+ alignment_line = parse_alignment(align, labels.length)
30
+
31
+ # Pad the data arrays so that it can be transposed if necessary.
32
+ max_len = data.map(&:length).max
33
+ data.map! {|datum| fill(datum, max_len)}
34
+
35
+ # Generate the table rows.
36
+ rows = (is_rows ? data : data.transpose).map {|row| row.join('|')}
32
37
 
33
38
  return [header_line, alignment_line, rows.join("\n")].join("\n")
34
39
  end
35
40
 
36
41
  # Convert a Markdown table into human-readable form.
37
42
  def self.plain_text(md_table)
43
+ md_table !~ // && raise('Invalid input')
44
+
45
+ # Split the table into lines to get the labels, rows, and alignments.
38
46
  lines = md_table.split("\n")
39
47
  alignments = lines[1].split('|')
48
+ # labels or rows might have some empty values but alignments
49
+ # is guaranteed to be of the right width.
40
50
  table_width = alignments.length
41
-
42
- # Add back any any missing empty cells.
43
- labels = lines[0].split('|')
44
- labels.length < table_width && labels += [' '] * (table_width - labels.length)
45
- rows = lines[2..-1].map {|line| line.split('|')}
46
- rows.each_index do |i|
47
- rows[i].length < table_width && rows[i] += [' '] * (table_width - rows[i].length)
48
- end
49
-
50
- # Replace non-breaking HTML characters with their plaintext counterparts.
51
- rows.each do |row|
52
- row.each do |cell|
53
- cell.gsub!(/(&nbsp;)|(&#124;)/, '&nbsp;' => ' ', '&#124;' => '|')
54
- end
55
- end
51
+ # '|||'.split('|') == [], so we need to manually add trailing empty cells.
52
+ # Leading empty cells are taken care of automatically.
53
+ labels = fill(lines[0].split('|'), table_width)
54
+ rows = lines[2..-1].map {|line| fill(line.split('|'), table_width)}
56
55
 
57
56
  # Get the width for each column.
58
- widths = labels.map(&:length) # Lengths of each column's longest element.
59
- rows.length.times do |i|
60
- rows[i].length.times do |j|
61
- rows[i][j].length > widths[j] && widths[j] = rows[i][j].length
62
- end
63
- end
64
- widths.map! {|w| w + 2} # Add padding on each side.
65
-
66
- # Align the column labels.
67
- labels.length.times do |i|
68
- label_length = labels[i].length
69
- start = align_cell(label_length, widths[i], alignments[i])
70
-
71
- labels[i].prepend(' ' * start)
72
- labels[i] += ' ' * (widths[i] - start - label_length)
73
- end
74
-
75
- # Align the cells.
76
- rows.each do |row|
77
- row.length.times do |i|
78
- cell_length = row[i].length
79
- start = align_cell(cell_length, widths[i], alignments[i])
80
- row[i].prepend(' ' * start)
81
- row[i] += ' ' * (widths[i] - start - cell_length)
82
- end
83
- end
57
+ cols = rows.transpose
58
+ widths = cols.each_index.map {|i| column_width(cols[i].push(labels[i]))}
59
+
60
+ # Align the labels and cells.
61
+ labels = labels.each_index.map { |i|
62
+ aligned_cell(unsanitize(labels[i]), widths[i], alignments[i])
63
+ }
64
+ rows.map! { |row|
65
+ row.each_index.map { |i|
66
+ aligned_cell(unsanitize(row[i]), widths[i], alignments[i])
67
+ }
68
+ }
84
69
 
85
70
  border = "\n|" + widths.map {|w| '=' * w}.join('|') + "|\n"
86
- separator = border.gsub('=', '-')
87
-
88
- table = border[1..-1] # Don't include the first newline.
89
- table += '|' + labels.join('|') + '|'
90
- table += border
91
- table += rows.map {|row| '|' + row.join('|') + '|'}.join(separator)
92
- table += border
71
+ return (
72
+ border + [
73
+ '|' + labels.join('|') + '|',
74
+ rows.map {|row| '|' + row.join('|') + '|'}.join(border.tr('=', '-'))
75
+ ].join(border) + border
76
+ ).strip
93
77
 
94
- return table.chomp
95
78
  end
96
79
 
97
80
  # Sanity checks for make_table.
@@ -119,43 +102,51 @@ class MarkdownTables
119
102
  end
120
103
  end
121
104
 
122
- # Convert all input to strings and replace any '|' characters with
123
- # non-breaking equivalents,
124
- private_class_method def self.sanitize!(labels, data)
105
+ # Convert some input to a string and replace any '|' characters with
106
+ # a non-breaking equivalent,
107
+ private_class_method def self.sanitize(input)
125
108
  bar = '&#124;' # Non-breaking HTML vertical bar.
126
- labels.map! {|label| label.to_s.gsub('|', bar)}
127
- data.length.times {|i| data[i].map! {|cell| cell.to_s.gsub('|', bar)}}
109
+ return input.to_s.gsub('|', bar)
110
+ end
111
+
112
+ # Replace non-breaking HTML characters with their plaintext counterparts.
113
+ private_class_method def self.unsanitize(input)
114
+ return input.gsub(/(&nbsp;)|(&#124;)/, '&nbsp;' => ' ', '&#124;' => '|')
128
115
  end
129
116
 
130
117
  # Generate the alignment line from a string or array.
131
- # align must be a string or array or strings.
118
+ # align must be a string or array of strings.
132
119
  # n: number of labels in the table to be created.
133
- private_class_method def self.alignment(align, n)
134
- if align.class == String
135
- alignment = align == 'l' ? ':-' : align == 'r' ? '-:' : ':-:'
136
- alignment_line = ([alignment] * n).join('|')
137
- else
138
- alignments = align.map {
139
- |a| a.downcase == 'l' ? ':-' : a.downcase == 'r' ? '-:' : ':-:'
140
- }
141
- if alignments.length < n
142
- alignments += [':-:'] * (n - alignments.length)
143
- end
144
- alignment_line = alignments.join('|')
145
- end
146
- return alignment_line
120
+ private_class_method def self.parse_alignment(align, n)
121
+ align_map = {'l' => ':-', 'c' => ':-:', 'r' => '-:'}
122
+ alignments = align.map {|a| align_map[a]}
123
+ # If not enough values were given, center the remaining columns.
124
+ alignments.length < n && alignments += [':-:'] * (n - alignments.length)
125
+ return alignments.join('|')
147
126
  end
148
127
 
149
- # Get the starting index of a cell's text from the text's length, the cell's
150
- # width, and the alignment.
151
- private_class_method def self.align_cell(length, width, align)
152
- if align =~ /:-+:/
153
- return (width / 2) - (length / 2)
154
- elsif align =~ /-+:/
155
- return width - length - 1
156
- else
157
- return 1
128
+ # Align some text in a cell.
129
+ private_class_method def self.aligned_cell(text, width, align)
130
+ if align =~ /:-+:/ # Center alignment.
131
+ start = (width / 2) - (text.length / 2)
132
+ elsif align =~ /-+:/ # Right alignment.
133
+ start = width - text.length - 1
134
+ else # Left alignment.
135
+ start = 1
158
136
  end
137
+ return ' ' * start + text + ' ' * (width - start - text.length)
138
+ end
139
+
140
+ # Get the width for a column.
141
+ private_class_method def self.column_width(col)
142
+ # Pad each cell on either side and maintain a minimum 3 width of characters.
143
+ return [(!col.empty? ? col.map(&:length).max : 0) + 2, 3].max
144
+ end
145
+
146
+ # Add any missing empty values to a row.
147
+ private_class_method def self.fill(row, n)
148
+ row.length > n && raise('Sanity checks failed for fill')
149
+ return row.length < n ? row + ([''] * (n - row.length)) : row
159
150
  end
160
151
 
161
152
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: markdown-tables
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris de Graaf
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-18 00:00:00.000000000 Z
11
+ date: 2017-04-19 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: chrisadegraaf@gmail.com