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 +4 -4
- data/README.md +84 -9
- data/RELEASE_NOTES.md +18 -0
- data/lib/ghostwriter/version.rb +1 -1
- data/lib/ghostwriter/writer.rb +54 -27
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b87477fb4de8fc196eda6ea049eb3f1355ae583f0622c11b98543a67cfe71c8b
|
4
|
+
data.tar.gz: fa4e66701a3326ccceef69b3eb8f8591f8eea337f6f4f37c5b18a820bd120d42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
###
|
175
|
+
### Headings
|
176
176
|
|
177
|
-
|
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
|
-
|
190
|
-
|
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
|
-
###
|
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
|
-
|
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
|
data/lib/ghostwriter/version.rb
CHANGED
data/lib/ghostwriter/writer.rb
CHANGED
@@ -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
|
-
|
11
|
-
@
|
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
|
47
|
+
normalize_lines(doc)
|
38
48
|
end
|
39
49
|
|
40
50
|
private
|
41
51
|
|
42
|
-
def
|
43
|
-
|
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
|
82
|
-
node.
|
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('
|
101
|
-
list_node.
|
102
|
-
|
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.
|
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.
|
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.
|
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 |
|
143
|
-
|
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
|
-
|
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 =
|
179
|
+
new_content = node.text.ljust(column_sizes[i] + 1)
|
152
180
|
|
153
|
-
#
|
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
|
|