ghostwriter 1.0.1 → 1.1.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: 2868e207e695355f8e9f40521b38d1f96b72b45c9ab73207396a87e5b4d535cd
4
- data.tar.gz: cf111d734daa4bf94e4d9c2924dbd6c7d12b38b2b26a55db79110730f306fccc
3
+ metadata.gz: b87477fb4de8fc196eda6ea049eb3f1355ae583f0622c11b98543a67cfe71c8b
4
+ data.tar.gz: fa4e66701a3326ccceef69b3eb8f8591f8eea337f6f4f37c5b18a820bd120d42
5
5
  SHA512:
6
- metadata.gz: 8d71989a44d8d2da33496172c600ec38063100c53642850982a9a93ccefbea37e40847e93edfde09d3b0f0dad98f457296f01c070387d86b11cc06e2ee9e04c1
7
- data.tar.gz: 0767f0d24a895477aee922bd960380608185b741c0d87975bba65eaa03270539e2af2776bbcef2f689b604b76fde7882dbe9322e522b190858c2699359ce8a3b
6
+ metadata.gz: b5c545e83c7073b3c5923efc3ffcb6ecfd78650c472a482b5ec16fd7ef5533859ce6fb24d32378aafd20c83e35acd558c136856b46f0c7e0373beb7e3191be34
7
+ data.tar.gz: 8b259323e6653c112885ddf6fe4ee99b31c01e5625ee2fcf22d0259330dc090df11a1b7ee2dc1507c222b5f79c8322717aba9455b9bdbdca04334c0becc5959a
data/README.md CHANGED
@@ -172,9 +172,9 @@ This meal we enjoy together would be improved by one.
172
172
 
173
173
  ```
174
174
 
175
- ### Headers
175
+ ### Headings
176
176
 
177
- For now, headers are all treated the same and given a simple marker:
177
+ Headings are wrapped with a marker per heading level:
178
178
 
179
179
  ```html
180
180
  <h1>Dog Maintenance and Repair</h1>
@@ -186,10 +186,12 @@ Becomes:
186
186
 
187
187
  ```
188
188
  -- Dog Maintenance and Repair --
189
- -- Food Input Port --
190
- -- Exhaust Port Considerations --
189
+ ---- Food Input Port ----
190
+ ------ Exhaust Port Considerations ------
191
191
  ```
192
192
 
193
+ The `<header>` tag is treated like an `<h1>` tag.
194
+
193
195
  ### Lists
194
196
 
195
197
  Lists are converted, too. They are padded with newlines and are given simple markers:
@@ -211,7 +213,6 @@ Lists are converted, too. They are padded with newlines and are given simple mar
211
213
  Becomes:
212
214
 
213
215
  ```
214
-
215
216
  - Planes
216
217
  - Trains
217
218
  - Automobiles
@@ -219,7 +220,6 @@ Becomes:
219
220
  1. I get knocked down
220
221
  2. I get up again
221
222
  3. Never gonna keep me down
222
-
223
223
  ```
224
224
 
225
225
  ### Tables
@@ -265,10 +265,85 @@ Becomes:
265
265
  | Planet Express Ship | Turanga Leela |
266
266
  ```
267
267
 
268
- ### Presentation ARIA Role
268
+ ### Customizing Output
269
+
270
+ Ghostwriter has some constructor options to customize output.
271
+
272
+ You can set heading markers.
273
+
274
+ ```ruby
275
+ html = <<~HTML
276
+ <h1>Emergency Cat Procedures</h1>
277
+ HTML
278
+
279
+ writer = Ghostwriter::Writer.new(heading_marker: '#')
280
+
281
+ puts writer.textify(html)
282
+ ```
283
+
284
+ Produces:
285
+
286
+ ```
287
+ # Emergency Cat Procedures #
288
+ ```
289
+
290
+ You can also set list item markers. Ordered markers can be anything that responds to `#next` (eg. any `Enumerator`)
291
+
292
+ ```ruby
293
+ html = <<~HTML
294
+ <ol><li>Mercury</li><li>Venus</li><li>Mars</li></ol>
295
+ <ul><li>Teapot</li><li>Kettle</li></ul>
296
+ HTML
297
+
298
+ writer = Ghostwriter::Writer.new(ul_marker: '*', ol_marker: 'a')
299
+
300
+ puts writer.textify(html)
301
+ ```
302
+
303
+ Produces:
304
+
305
+ ```
306
+ a. Mercury
307
+ b. Venus
308
+ c. Mars
309
+
310
+ * Teapot
311
+ * Kettle
312
+ ```
313
+
314
+ And tables can be customized:
315
+
316
+ ```ruby
317
+ writer = Ghostwriter::Writer.new(table_row: '.',
318
+ table_column: '#',
319
+ table_corner: '+')
320
+
321
+ puts writer.textify <<~HTML
322
+ <table>
323
+ <thead>
324
+ <tr><th>Moon</th><th>Portfolio</th></tr>
325
+ </thead>
326
+ <tbody>
327
+ <tr><td>Phobos</td><td>Fear & Panic</td></tr>
328
+ <tr><td>Deimos</td><td>Dread and Terror</td></tr>
329
+ </tbody>
330
+ </table>
331
+ HTML
332
+ ```
333
+
334
+ Produces:
335
+
336
+ ```
337
+ # Moon # Portfolio #
338
+ +........+..................+
339
+ # Phobos # Fear & Panic #
340
+ # Deimos # Dread and Terror #
341
+
342
+ ```
343
+
344
+ #### Presentation ARIA Role
269
345
 
270
- Lists and tables with `role="presentation"` will be treated as a simple container and the normal behaviour will be
271
- suppressed.
346
+ Tags with `role="presentation"` will be treated as a simple container and the normal behaviour will be suppressed.
272
347
 
273
348
  ```html
274
349
 
data/RELEASE_NOTES.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Release Notes
2
2
 
3
+ ## 1.1.0 (2021-03-23)
4
+
5
+ ### Major
6
+
7
+ * none
8
+
9
+ ### Minor
10
+
11
+ * Added customization for headings
12
+ * Headings now marked more for higher order headings
13
+ * Added customization for list markers
14
+ * Added customization for table markers
15
+ * Writer is now immutable
16
+
17
+ ### Bugfixes
18
+
19
+ * none
20
+
3
21
  ## 1.0.1 (2021-03-22)
4
22
 
5
23
  ### Major
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ghostwriter
4
- VERSION = '1.0.1'
4
+ VERSION = '1.1.0'
5
5
  end
@@ -3,12 +3,22 @@
3
3
  module Ghostwriter
4
4
  # Main Ghostwriter converter object.
5
5
  class Writer
6
+ attr_reader :link_base, :heading_marker, :ul_marker, :ol_marker, :table_row, :table_column, :table_corner
7
+
6
8
  # Creates a new ghostwriter
7
9
  #
8
10
  # @param [String] link_base the url to prefix relative links with
9
- def initialize(link_base: '')
10
- @link_base = link_base
11
- @list_marker = '-'
11
+ def initialize(link_base: '', heading_marker: '--', ul_marker: '-', ol_marker: '1',
12
+ table_column: '|', table_row: '-', table_corner: '|')
13
+ @link_base = link_base
14
+ @heading_marker = heading_marker
15
+ @ul_marker = ul_marker
16
+ @ol_marker = ol_marker
17
+ @table_column = table_column
18
+ @table_row = table_row
19
+ @table_corner = table_corner
20
+
21
+ freeze
12
22
  end
13
23
 
14
24
  # Strips HTML down to plain text.
@@ -34,13 +44,13 @@ module Ghostwriter
34
44
  simple_replace(doc, 'br', "\n")
35
45
  simple_replace(doc, 'p', "\n\n")
36
46
 
37
- doc.text.strip.split("\n").collect(&:strip).join("\n").concat("\n")
47
+ normalize_lines(doc)
38
48
  end
39
49
 
40
50
  private
41
51
 
42
- def normalize_whitespace(html)
43
- html.gsub(/\s/, ' ').squeeze(' ')
52
+ def normalize_lines(doc)
53
+ doc.text.strip.split("\n").collect(&:strip).join("\n").concat("\n")
44
54
  end
45
55
 
46
56
  def replace_anchors(doc)
@@ -78,8 +88,16 @@ module Ghostwriter
78
88
  end
79
89
 
80
90
  def replace_headers(doc)
81
- doc.search('header, h1, h2, h3, h4, h5, h6').each do |node|
82
- node.inner_html = "-- #{ node.inner_html } --\n".squeeze(' ')
91
+ doc.search('header, h1').each do |node|
92
+ node.replace("#{ @heading_marker } #{ node.inner_html } #{ @heading_marker }\n"
93
+ .squeeze(' '))
94
+ end
95
+
96
+ (2..6).each do |n|
97
+ doc.search("h#{ n }").each do |node|
98
+ node.replace("#{ @heading_marker * n } #{ node.inner_html } #{ @heading_marker * n }\n"
99
+ .squeeze(' '))
100
+ end
83
101
  end
84
102
  end
85
103
 
@@ -97,41 +115,50 @@ module Ghostwriter
97
115
  end
98
116
 
99
117
  def replace_lists(doc)
100
- doc.search('ul, ol').each do |list_node|
101
- list_node.search('./li').each_with_index do |list_item, i|
102
- marker = if list_node.node_name == 'ol'
103
- "#{ i + 1 }."
104
- else
105
- @list_marker
106
- end
107
-
108
- list_item.inner_html = "#{ marker } #{ list_item.inner_html }\n".squeeze(' ')
109
- end
118
+ doc.search('ol').each do |list_node|
119
+ replace_list_items(list_node, @ol_marker, after_marker: '.', increment: true)
120
+ end
110
121
 
122
+ doc.search('ul').each do |list_node|
123
+ replace_list_items(list_node, @ul_marker)
124
+ end
125
+
126
+ doc.search('ul, ol').each do |list_node|
111
127
  list_node.replace("#{ list_node.inner_html }\n")
112
128
  end
113
129
  end
114
130
 
131
+ def replace_list_items(list_node, marker, after_marker: '', increment: false)
132
+ list_node.search('./li').each do |list_item|
133
+ list_item.replace("#{ marker }#{ after_marker } #{ list_item.inner_html }\n")
134
+
135
+ marker = marker.next if increment
136
+ end
137
+ end
138
+
115
139
  def replace_tables(doc)
116
140
  doc.css('table').each do |table|
141
+ # remove whitespace between nodes
142
+ table.search('//text()[normalize-space()=""]').remove
143
+
117
144
  column_sizes = calculate_column_sizes(table)
118
145
 
119
146
  table.search('./thead/tr', './tbody/tr', './tr').each do |row|
120
147
  replace_table_nodes(row, column_sizes)
121
148
 
122
- row.inner_html = "#{ row.inner_html }|\n"
149
+ row.replace("#{ row.inner_html }#{ @table_column }\n")
123
150
  end
124
151
 
125
152
  add_table_header_underline(table, column_sizes)
126
153
 
127
- table.inner_html = "#{ table.inner_html }\n"
154
+ table.replace("\n#{ table.inner_html }\n")
128
155
  end
129
156
  end
130
157
 
131
158
  def calculate_column_sizes(table)
132
159
  column_sizes = table.search('tr').collect do |row|
133
160
  row.search('th', 'td').collect do |node|
134
- node.inner_html.length
161
+ node.text.length
135
162
  end
136
163
  end
137
164
 
@@ -139,19 +166,19 @@ module Ghostwriter
139
166
  end
140
167
 
141
168
  def add_table_header_underline(table, column_sizes)
142
- table.search('./thead').each do |row|
143
- header_bottom = "|#{ column_sizes.collect { |len| ('-' * (len + 2)) }.join('|') }|"
169
+ table.search('./thead').each do |thead|
170
+ lines = column_sizes.collect { |len| @table_row * (len + 2) }
171
+ underline_row = "#{ table_corner }#{ lines.join(@table_corner) }#{ @table_corner }"
144
172
 
145
- row.inner_html = "#{ row.inner_html }#{ header_bottom }\n"
173
+ thead.replace("#{ thead.inner_html }#{ underline_row }\n")
146
174
  end
147
175
  end
148
176
 
149
177
  def replace_table_nodes(row, column_sizes)
150
178
  row.search('th', 'td').each_with_index do |node, i|
151
- new_content = "| #{ node.inner_html }".squeeze(' ')
179
+ new_content = node.text.ljust(column_sizes[i] + 1)
152
180
 
153
- # +2 for the extra spacing between text and pipe
154
- node.inner_html = new_content.ljust(column_sizes[i] + 2)
181
+ node.replace("#{ @table_column } #{ new_content }")
155
182
  end
156
183
  end
157
184
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ghostwriter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robin Miller