markdown_helper 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile.lock +1 -1
  3. data/README.md +74 -208
  4. data/Rakefile +0 -1
  5. data/lib/markdown_helper.rb +115 -248
  6. data/lib/markdown_helper/version.rb +1 -1
  7. data/markdown/readme/README.template.md +43 -101
  8. data/markdown/readme/include.md +4 -0
  9. data/markdown/readme/include_usage.rb +1 -2
  10. data/markdown/use_cases/Rakefile +28 -31
  11. data/markdown/use_cases/include_files/diagnose_circular_includes/diagnose_circular_includes.err +1 -1
  12. data/markdown/use_cases/include_files/diagnose_circular_includes/use_case.md +1 -1
  13. data/markdown/use_cases/include_files/diagnose_circular_includes/{diagnose_circular_includes.rb → use_case_builder.rb} +1 -2
  14. data/markdown/use_cases/include_files/diagnose_missing_includee/diagnose_missing_includee.err +1 -1
  15. data/markdown/use_cases/include_files/diagnose_missing_includee/use_case.md +1 -1
  16. data/markdown/use_cases/include_files/diagnose_missing_includee/{diagnose_missing_includee.rb → use_case_builder.rb} +1 -2
  17. data/markdown/use_cases/include_files/include_code_block/{include_code_block.rb → use_case_builder.rb} +1 -2
  18. data/markdown/use_cases/include_files/include_generated_text/{include_generated_text.rb → use_case_builder.rb} +1 -2
  19. data/markdown/use_cases/include_files/include_highlighted_code/{include_highlighted_code.rb → use_case_builder.rb} +1 -2
  20. data/markdown/use_cases/include_files/include_markdown/{include_markdown.rb → use_case_builder.rb} +1 -2
  21. data/markdown/use_cases/include_files/include_page_toc/included.md +48 -0
  22. data/markdown/use_cases/include_files/include_page_toc/includer.md +16 -0
  23. data/markdown/use_cases/include_files/include_page_toc/markdown_0.md +8 -0
  24. data/markdown/use_cases/include_files/include_page_toc/markdown_1.md +8 -0
  25. data/markdown/use_cases/include_files/include_page_toc/use_case.md +212 -0
  26. data/markdown/use_cases/include_files/include_page_toc/use_case_builder.rb +121 -0
  27. data/markdown/use_cases/include_files/include_page_toc/use_case_template.md +46 -0
  28. data/markdown/use_cases/include_files/include_text_as_comment/{include_text_as_comment.rb → use_case_builder.rb} +1 -2
  29. data/markdown/use_cases/include_files/include_text_as_pre/{include_text_as_pre.rb → use_case_builder.rb} +1 -2
  30. data/markdown/use_cases/include_files/include_use_case.rb +2 -8
  31. data/markdown/use_cases/include_files/include_with_added_comments/{include_with_added_comments.rb → use_case_builder.rb} +2 -3
  32. data/markdown/use_cases/include_files/nest_inclusions/{nest_inclusions.rb → use_case_builder.rb} +1 -2
  33. data/markdown/use_cases/include_files/reuse_text/{reuse_text.rb → use_case_builder.rb} +3 -2
  34. data/markdown/use_cases/structure.md +10 -0
  35. data/markdown/use_cases/tables_of_contents/create_and_include_page_toc/included.md +44 -0
  36. data/markdown/use_cases/tables_of_contents/create_and_include_page_toc/page.md +7 -7
  37. data/markdown/use_cases/tables_of_contents/create_and_include_page_toc/toc.md +7 -7
  38. data/markdown/use_cases/tables_of_contents/create_and_include_page_toc/{create_and_include_page_toc.rb → use_case_builder.rb} +1 -2
  39. data/markdown/use_cases/tables_of_contents/create_page_toc_use_case.rb +1 -1
  40. data/markdown/use_cases/use_case.rb +14 -15
  41. data/markdown/use_cases/use_cases.md +2 -4
  42. data/markdown_helper.gemspec +0 -2
  43. metadata +24 -29
  44. data/bin/_resolve +0 -46
  45. data/bin/resolve +0 -49
  46. data/bin/usage/resolve.txt +0 -14
  47. data/images/html.png +0 -0
  48. data/markdown/readme/resolve_usage.rb +0 -12
  49. data/markdown/use_cases/include_files/diagnose_missing_includee/included.md +0 -1
  50. data/markdown/use_cases/resolve/gemify_images/gemify_images.md +0 -11
  51. data/markdown/use_cases/resolve/gemify_images/gemify_images.rb +0 -68
  52. data/markdown/use_cases/resolve/gemify_images/relative_image.md +0 -1
  53. data/markdown/use_cases/resolve/gemify_images/resolved_image.md +0 -1
  54. data/markdown/use_cases/resolve/gemify_images/template.md +0 -15
  55. data/markdown/use_cases/resolve/resize_images/template.md +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: bd8b799693304f32aad8780f7d4ce734ae46614d
4
- data.tar.gz: 703df7ba48954272c6bfe0e794b9150564f4d3cd
2
+ SHA256:
3
+ metadata.gz: cee806406f109c72b11a7a4f78c8ad7efc59706f278a0a5b654267e3ae38ddf6
4
+ data.tar.gz: 4e49e14011eb429bd5d523a314572aff3693876b07fa50b52d9cfecf693fcd3d
5
5
  SHA512:
6
- metadata.gz: efc921a959dd8f3c0cd39d0da08e13dcb6c51f7b82ab405d37e1faf6f4ff81218ae6264e8963e8aa8de67ebad4521145e9bd044c5b7eb4ced8e9003a6461919a
7
- data.tar.gz: a91f66a74094c2bab6e87bbf98403ed168df5d817a4155f7440b9c7cca8c181171c02bea25d6fc6b882794bc617519f127e3a0da15b576ea3cb6e411bada3194
6
+ metadata.gz: 5d4c4a25171b3ac8df8cfd84766843ae7c8d599c896d70b1d70854353dc7f0cb06741d1832640f5f24fdc500285bf16684f09d7d08450a48c73680d0033cab9f
7
+ data.tar.gz: 5f81f39ad84273fa2367a03e7bc18773190c97398ff7ac3c8834eac464a2a9dbe8bbedbf566fee79489124d2458fc32bfec96534b85f2edd4a7cadbb1e5dd63c
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- markdown_helper (2.0.0)
4
+ markdown_helper (2.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,38 +1,58 @@
1
1
  <!-- >>>>>> BEGIN GENERATED FILE (include): SOURCE README.template.md -->
2
2
  # Markdown Helper
3
3
 
4
- ![Gem Version](https://badge.fury.io/rb/markdown_helper.svg) [Visit gem markdown_helper](https://rubygems.org/gems/markdown_helper)
5
-
6
- ## Deprecated
7
-
8
- - Method ```:resolve```.
9
- - Command ```markdown_helper resolve```.
4
+ [![Gem](https://img.shields.io/gem/v/markdown_helper.svg?style=flat)](http://rubygems.org/gems/markdown_helper "View this project in Rubygems")
10
5
 
11
6
  ## What's New?
12
7
 
13
- Treatments for included file:
14
-
15
- - Support is added for including a file as a comment. See the [use case](markdown/use_cases/include_files/include_text_as_comment/use_case.md#include-text-as-comment).
16
- - Support is added for including a file as pre-formatted. See the [use case](markdown/use_cases/include_files/include_text_as_pre/use_case.md#include-text-as-pre).
17
-
18
- (The new version, 2.0.0, is not a major increment over version 1.9.9. Numbers just ran out.)
8
+ Page TOC (table of contents) is improved:
9
+
10
+ - **Old**: You would first run the markdown helper to generate a page TOC, then run the helper a second time to include the page TOC where you want it.
11
+ - **New**: You specify the site for the page TOC in the page itself, and the page TOC is automatically generated and inserted there. See the [use case](markdown/use_cases/include_files/include_page_toc/use_case.md#include-page-toc)
12
+
13
+ The old way is now deprecated.
14
+
15
+ ## Contents
16
+ - [What's a Markdown Helper?](#whats-a-markdown-helper)
17
+ - [How It Works](#how-it-works)
18
+ - [Restriction: ```git``` Only](#restriction-git-only)
19
+ - [Commented or Pristine?](#commented-or-pristine)
20
+ - [File Inclusion](#file-inclusion)
21
+ - [Re-use Text](#re-use-text)
22
+ - [Include Generated Text](#include-generated-text)
23
+ - [Nest Inclusions](#nest-inclusions)
24
+ - [Merged Text Formats](#merged-text-formats)
25
+ - [Markdown](#markdown)
26
+ - [Highlighted Code Block](#highlighted-code-block)
27
+ - [Plain Code Block](#plain-code-block)
28
+ - [Comment](#comment)
29
+ - [Pre-Formattted Text](#pre-formattted-text)
30
+ - [Usage](#usage)
31
+ - [CLI](#cli)
32
+ - [API](#api)
33
+ - [Include Descriptions](#include-descriptions)
34
+ - [Example Include Descriptions](#example-include-descriptions)
35
+ - [Page TOC](#page-toc)
36
+ - [Diagnostics](#diagnostics)
37
+ - ["Noisy" (Not Pristine)](#noisy-not-pristine)
38
+ - [Missing Includee File](#missing-includee-file)
39
+ - [Circular Inclusion](#circular-inclusion)
40
+ - [What Should Be Next?](#what-should-be-next)
19
41
 
20
42
  ## What's a Markdown Helper?
21
43
 
22
44
  Class <code>MarkdownHelper</code> supports:
23
45
 
24
46
  * [File inclusion](#file-inclusion): to include text from other files, as code-block or markdown.
25
- * [Page TOC](#page-toc): to create the table of contents for a markdown page.
26
- * [Image path resolution](#image-path-resolution): to resolve relative image paths to absolute URL paths (so they work even in gem documentation). [Deprecated]**
27
- * [Image attributes](#image-attributes): image attributes are passed through to an HTML <code>img</code> tag. [Deprecated]**
47
+ * [Page TOC](#page-toc): to create and insert the table of contents for a markdown page.
28
48
 
29
49
  ## How It Works
30
50
 
31
51
  The markdown helper is a preprocessor that reads a markdown document (template) and writes another markdown document.
32
52
 
33
- The template can contain certain instructions that call for file inclusions and image resolutions.
53
+ The template can contain certain instructions that call for file inclusions.
34
54
 
35
- ### Restriction: ```git``` Only
55
+ ### Restriction: ```git``` Only
36
56
 
37
57
  The helper works only in a ```git``` project: the working directory or one of ita parents must be a git directory -- one in which command ```git rev-parse --git-dir``` succeeds.
38
58
 
@@ -42,11 +62,10 @@ By default, the output markdown has added comments that show:
42
62
 
43
63
  * The path to the template file.
44
64
  * The path to each included file.
45
- * The image description (original) for each resolved image file path. [Deprecated]**
46
65
 
47
66
  You can suppress those comments using the <code>pristine</code> option.
48
67
 
49
- ## File Inclusion
68
+ ## File Inclusion
50
69
 
51
70
  <img src="images/include.png" alt="include_icon" width="50">
52
71
 
@@ -54,49 +73,41 @@ This markdown helper enables file inclusion in GitHub markdown.
54
73
 
55
74
  (Actually, this README file itself is built using file inclusion.)
56
75
 
57
- Use the markdown helper to merge external files into a markdown (</code>.md</code>) file.
76
+ See all [use cases](markdown/use_cases/use_cases.md#use-cases).
77
+
78
+ ### Re-use Text
79
+
80
+ Keep your markdown DRY (Don't Repeat Yourself) by re-using text. See the [use case](markdown/use_cases/include_files/reuse_text/use_case.md#reuse-text).
81
+
82
+ ### Include Generated Text
83
+
84
+ In particular, you can include text that's built during your "readme build." See the [use case](markdown/use_cases/include_files/include_generated_text/use_case.md#include-generated-text).
58
85
 
59
- See the [use cases](markdown/use_cases/use_cases.md#use-cases).
86
+ ### Nest Inclusions
87
+
88
+ You can nest inclusions. See the [use case](markdown/use_cases/include_files/nest_inclusions/use_case.md#nest-inclusions).
60
89
 
61
90
  ### Merged Text Formats
62
91
 
92
+ #### Markdown
93
+
94
+ You can include text that is to be treated simply as markdown. See the [use case](markdown/use_cases/include_files/include_markdown/use_case.md#include-markdown).
95
+
63
96
  #### Highlighted Code Block
64
97
 
65
- <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE markdown/readme/include.rb -->
66
- ```include.rb```:
67
- ```ruby
68
- class RubyCode
69
- def initialize
70
- raise RuntimeError.new('I am only an example!')
71
- end
72
- end
73
- ```
74
- <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE markdown/readme/include.rb -->
98
+ You can include a code block that's to be highlighted. See the [use case](markdown/use_cases/include_files/include_highlighted_code/use_case.md#include-highlighted-code).
75
99
 
76
100
  #### Plain Code Block
77
101
 
78
- <!-- >>>>>> BEGIN INCLUDED FILE (code_block): SOURCE markdown/readme/include.rb -->
79
- ```include.rb```:
80
- ```
81
- class RubyCode
82
- def initialize
83
- raise RuntimeError.new('I am only an example!')
84
- end
85
- end
86
- ```
87
- <!-- <<<<<< END INCLUDED FILE (code_block): SOURCE markdown/readme/include.rb -->
88
-
89
- [Note: In the gem documentation, RubyDoc.info chooses to highlight this code block regardless. Go figure.]
102
+ You can also include a code block without highlighting. See the [use case](markdown/use_cases/include_files/include_code_block/use_case.md#include-code-block).
90
103
 
91
104
  #### Comment
92
105
 
93
- Comment text is written into the output between the comment delimiters <code>\<!--</code> and <code>--></code>
94
-
95
- #### Markdown
106
+ You can include text that's to become a comment in the markdown. See the [use case](markdown/use_cases/include_files/include_text_as_comment/use_case.md#include-text-as-comment).
96
107
 
97
- Markdown text is included unadorned, and will be processed on GitHub as markdown.
108
+ ### Pre-Formattted Text
98
109
 
99
- The markdown text is itself scanned for nested includes.
110
+ You can include text that's pre-formatted. See the [use case](markdown/use_cases/include_files/include_text_as_pre/use_case.md#include-text-as-pre).
100
111
 
101
112
  ### Usage
102
113
 
@@ -131,9 +142,8 @@ require 'markdown_helper'
131
142
 
132
143
  template_file_path = 'highlight_ruby_template.md'
133
144
  markdown_file_path = 'highlighted_ruby.md'
134
- markdown_helper = MarkdownHelper.new
135
- markdown_helper.include(template_file_path, markdown_file_path)
136
145
  # Pristine.
146
+ markdown_helper = MarkdownHelper.new
137
147
  markdown_helper.pristine = true
138
148
  markdown_helper.include(template_file_path, markdown_file_path)
139
149
  # Also pristine.
@@ -154,6 +164,8 @@ where:
154
164
  * Highlighting mode such as <code>[ruby]</code>, to include a highlighted code block. This can be any Ace mode mentioned in [GitHub Languages](https://github.com/github/linguist/blob/master/lib/linguist/languages.yml).
155
165
  * <code>[:code_block]</code>, to include a plain code block.
156
166
  * <code>[:markdown]</code>, to include text markdown (to be rendered as markdown).
167
+ * <code>[:comment]</code>, to insert text as a markdown comment.
168
+ * <code>[:pre]</code>, to include pre-formatted text.
157
169
  * *relative_file_path* points to the file to be included.
158
170
 
159
171
  ##### Example Include Descriptions
@@ -166,176 +178,30 @@ where:
166
178
  @[:code_block](my_language.xyzzy)
167
179
 
168
180
  @[:markdown](my_markdown.md)
169
- ```
170
- <!-- <<<<<< END INCLUDED FILE (code_block): SOURCE markdown/readme/include.md -->
171
-
172
- ## Page TOC
173
-
174
- The markdown helper can create the table of contents for a markdown page.
175
- - The TOC is a tree of links to the headers on the page, suitable for inclusion with the page itself.
176
- - See the [use case](markdown/use_cases/tables_of_contents/create_and_include_page_toc/use_case.md#create-and-include-page-toc).
177
-
178
-
179
-
180
- ## Image Path Resolution **[Deprecated]**
181
-
182
- <img src="images/image.png" alt="image_icon" width="50">
183
-
184
- This markdown helper enables image path resolution in GitHub markdown.
185
-
186
- (Actually, this README file itself is built using image path resolution.)
187
-
188
- Use the markdown helper to resolve image relative paths in a markdown (</code>.md</code>) file.
189
-
190
- This matters because when markdown becomes part of a Ruby gem, its images will have been relocated in the documentation at RubyDoc.info, breaking the relative paths. The resolved (absolute) urls, however, will still be valid.
191
-
192
- ### Usage
193
-
194
- #### CLI
195
-
196
- <!-- >>>>>> BEGIN INCLUDED FILE (code_block): SOURCE bin/usage/resolve.txt -->
197
- ```resolve.txt```:
198
- ```
199
-
200
- Usage: markdown_helper resolve [options] template_file_path markdown_file_path
201
- --pristine No comments added
202
- --help Display help
203
-
204
- where
205
-
206
- * template_file_path is the path to an existing file.
207
- * markdown_file_path is the path to a file to be created.
208
-
209
- Typically:
210
-
211
- * Both file types are .md.
212
- * The template file contains image descriptions.
213
- ```
214
- <!-- <<<<<< END INCLUDED FILE (code_block): SOURCE bin/usage/resolve.txt -->
215
-
216
- #### API
217
-
218
- <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE markdown/readme/resolve_usage.rb -->
219
- ```resolve_usage.rb```:
220
- ```ruby
221
- require 'markdown_helper'
222
181
 
223
- template_file_path = 'template.md'
224
- markdown_file_path = 'markdown.md'
225
- markdown_helper = MarkdownHelper.new
226
- markdown_helper.resolve(template_file_path, markdown_file_path)
227
- # Pristine.
228
- markdown_helper.pristine = true
229
- markdown_helper.resolve(template_file_path, markdown_file_path)
230
- # Also pristine.
231
- markdown_helper = MarkdownHelper.new(:pristine => true)
232
- markdown_helper.resolve(template_file_path, markdown_file_path)
233
- ```
234
- <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE markdown/readme/resolve_usage.rb -->
235
-
236
- #### Image Descriptions
237
-
238
- Specify each image at the beginning of a line via an *image description*, which has the form:
239
-
240
- <code>![*alt_text*]\(</code>*relative_file_path* <code>|</code> *attributes*<code>)</code>
241
-
242
- where:
243
-
244
- * *alt_text* is the usual alt text for an HTML image.
245
- * *relative_file_path* points to the file to be included.
246
- * *attributes* specify image attributes. See [Image Attributes](#image-attributes) below.
247
-
248
- ##### Example Image Descriptions
249
-
250
- <!-- >>>>>> BEGIN INCLUDED FILE (code_block): SOURCE markdown/readme/resolve.md -->
251
- ```resolve.md```:
252
- ```code_block
253
- ![my_alt](image/image.png)
254
-
255
- ![my_alt](image/image.png | width=50)
256
-
257
- ![my_alt](image/image.png| width=50 height=50)
258
- ```
259
- <!-- <<<<<< END INCLUDED FILE (code_block): SOURCE markdown/readme/resolve.md -->
260
-
261
- ## Image Attributes
262
-
263
- <img src="images/html.png" alt="html_icon" width="50">
264
-
265
- This markdown helper enables HTML image attributes in GitHub markdown [image descriptions](https://github.github.com/gfm/#image-description).
266
-
267
- (Actually, this README file itself is built using image attributes.)
268
-
269
- Use the markdown helper to add image attributes in a markdown (</code>.md</code>) file.
270
-
271
- ### Usage
272
-
273
- #### CLI
274
-
275
- <!-- >>>>>> BEGIN INCLUDED FILE (code_block): SOURCE bin/usage/resolve.txt -->
276
- ```resolve.txt```:
277
- ```
278
-
279
- Usage: markdown_helper resolve [options] template_file_path markdown_file_path
280
- --pristine No comments added
281
- --help Display help
282
-
283
- where
182
+ @[:comment](my_comment.txt)
284
183
 
285
- * template_file_path is the path to an existing file.
286
- * markdown_file_path is the path to a file to be created.
287
-
288
- Typically:
289
-
290
- * Both file types are .md.
291
- * The template file contains image descriptions.
184
+ @[:pre](my_preformatted.txt)
292
185
  ```
293
- <!-- <<<<<< END INCLUDED FILE (code_block): SOURCE bin/usage/resolve.txt -->
294
-
295
- #### API
296
-
297
- <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE markdown/readme/resolve_usage.rb -->
298
- ```resolve_usage.rb```:
299
- ```ruby
300
- require 'markdown_helper'
301
-
302
- template_file_path = 'template.md'
303
- markdown_file_path = 'markdown.md'
304
- markdown_helper = MarkdownHelper.new
305
- markdown_helper.resolve(template_file_path, markdown_file_path)
306
- # Pristine.
307
- markdown_helper.pristine = true
308
- markdown_helper.resolve(template_file_path, markdown_file_path)
309
- # Also pristine.
310
- markdown_helper = MarkdownHelper.new(:pristine => true)
311
- markdown_helper.resolve(template_file_path, markdown_file_path)
312
- ```
313
- <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE markdown/readme/resolve_usage.rb -->
186
+ <!-- <<<<<< END INCLUDED FILE (code_block): SOURCE markdown/readme/include.md -->
314
187
 
315
- #### Image Descriptions
188
+ #### Page TOC
316
189
 
317
- Specify each image at the beginning of a line via an *image description*, which has the form:
190
+ You can specify the location for an automatically-generated page TOC (table of cotents). See the [use case](markdown/use_cases/include_files/include_page_toc/use_case.md#include-page-toc).
318
191
 
319
- <code>![*alt_text*]\(</code>*relative_file_path* <code>|</code> *attributes*<code>)</code>
192
+ #### Diagnostics
320
193
 
321
- where:
194
+ ##### "Noisy" (Not Pristine)
322
195
 
323
- * *alt_text* is the usual alt text for an HTML image.
324
- * *relative_file_path* points to the file to be included.
325
- * *attributes* are whitespace-separated name-value pairs in the form *name*<code>=</code>*value*. These are passed through to the generated <code>img</code> HTML element.
196
+ By default, the markdown helper inserts comments indicating inclusions. See the [use case](markdown/use_cases/include_files/include_with_added_comments/use_case.md#include-with-added-comments).
326
197
 
327
- ##### Example Image Descriptions
198
+ ##### Missing Includee File
328
199
 
329
- <!-- >>>>>> BEGIN INCLUDED FILE (code_block): SOURCE markdown/readme/resolve.md -->
330
- ```resolve.md```:
331
- ```code_block
332
- ![my_alt](image/image.png)
200
+ A missing includee file causes an exception that shows an inclusion backtrace. See the [use case](markdown/use_cases/include_files/diagnose_missing_includee/use_case.md#diagnose-missing-includee).
333
201
 
334
- ![my_alt](image/image.png | width=50)
202
+ ##### Circular Inclusion
335
203
 
336
- ![my_alt](image/image.png| width=50 height=50)
337
- ```
338
- <!-- <<<<<< END INCLUDED FILE (code_block): SOURCE markdown/readme/resolve.md -->
204
+ A circular inclusion causes an exception that shows an inclusion backtrace. See the [use case](markdown/use_cases/include_files/diagnose_circular_includes/use_case.md#diagnose-circular-includes).
339
205
 
340
206
  ## What Should Be Next?
341
207
 
data/Rakefile CHANGED
@@ -28,7 +28,6 @@ namespace :build do
28
28
  %w/
29
29
  create_page_toc
30
30
  include
31
- resolve
32
31
  /.each do |executable_name|
33
32
  usage_text = `ruby bin/_#{executable_name} --help`
34
33
  usage_file_path = "bin/usage/#{executable_name}.txt"
@@ -1,10 +1,6 @@
1
1
  require 'pathname'
2
2
  require 'markdown_helper/version'
3
3
 
4
- # Helper class for working with GitHub markdown.
5
- # Supports file inclusion.
6
- #
7
- # @author Burdette Lamar
8
4
  class MarkdownHelper
9
5
 
10
6
  class MarkdownHelperError < RuntimeError; end
@@ -13,14 +9,19 @@ class MarkdownHelper
13
9
  class TocHeadingsError < MarkdownHelperError; end
14
10
  class OptionError < MarkdownHelperError; end
15
11
  class EnvironmentError < MarkdownHelperError; end
12
+ class InvalidTocTitleError < MarkdownHelperError; end
13
+ class MisplacedPageTocError < MarkdownHelperError; end
14
+ class MultiplePageTocError < MarkdownHelperError; end
16
15
 
17
- IMAGE_REGEXP = /!\[([^\[]+)\]\(([^)]+)\)/
18
16
  INCLUDE_REGEXP = /^@\[([^\[]+)\]\(([^)]+)\)$/
19
17
 
20
18
  attr_accessor :pristine
21
19
 
22
20
  def initialize(options = {})
23
21
  # Confirm that we're in a git project.
22
+ # This is necessary so that we can prune file paths in the tests,
23
+ # which otherwise would fail because of differing installation directories.
24
+ # It also allows pruned paths to be used in the inserted comments (when not pristine).
24
25
  MarkdownHelper.git_clone_dir_path
25
26
  default_options = {
26
27
  :pristine => false,
@@ -36,19 +37,6 @@ class MarkdownHelper
36
37
  end
37
38
  end
38
39
 
39
- # Merges external files into markdown text.
40
- # @param template_file_path [String] the path to the input template markdown file, usually containing include pragmas.
41
- # @param markdown_file_path [String] the path to the output merged markdown file.
42
- # @return [String] the resulting markdown text.
43
- #
44
- # @example pragma to include text as a highlighted code block.
45
- # @[ruby](foo.rb)
46
- #
47
- # @example pragma to include text as a plain code block.
48
- # @[:code_block](foo.xyz)
49
- #
50
- # @example pragma to include text markdown, to be rendered as markdown.
51
- # @[:markdown](foo.md)
52
40
  def include(template_file_path, markdown_file_path)
53
41
  send(:generate_file, template_file_path, markdown_file_path, __method__) do |input_lines, output_lines|
54
42
  send(:include_files, template_file_path, input_lines, output_lines, Inclusions.new)
@@ -56,41 +44,17 @@ class MarkdownHelper
56
44
  end
57
45
 
58
46
  def create_page_toc(markdown_file_path, toc_file_path)
47
+ message = <<EOT
48
+ Method create_page_toc is deprecated.
49
+ Please use method include with embedded :page_toc treatment.
50
+ See https://github.com/BurdetteLamar/markdown_helper/blob/master/markdown/use_cases/include_files/include_page_toc/use_case.md#include-page-toc.
51
+ EOT
52
+ warn(message)
59
53
  send(:generate_file, markdown_file_path, toc_file_path, __method__) do |input_lines, output_lines|
60
54
  send(:_create_page_toc, input_lines, output_lines)
61
55
  end
62
56
  end
63
57
 
64
- # Resolves relative image paths to absolute urls in markdown text.
65
- # @param template_file_path [String] the path to the input template markdown file, usually containing image pragmas.
66
- # @param markdown_file_path [String] the path to the output resolved markdown file.
67
- # @return [String] the resulting markdown text.
68
- #
69
- # This matters because when markdown becomes part of a Ruby gem,
70
- # its images will have been relocated in the documentation at RubyDoc.info, breaking the paths.
71
- # The resolved (absolute) urls, however, will still be valid.
72
- #
73
- # ENV['REPO_USER'] and ENV['REPO_NAME'] must give the user name and repository name of the relevant GitHub repository.
74
- # must give the repo name of the relevant GitHub repository.
75
- #
76
- # @example pragma for an image:
77
- # ![image_icon](images/image.png)
78
- #
79
- # The path resolves to:
80
- #
81
- # image_path = File.join(
82
- # "https://raw.githubusercontent.com/#{repo_user}/#{repo_name}/master",
83
- # relative_file_path,
84
- # )
85
- def resolve(template_file_path, markdown_file_path)
86
- # Method :generate_file does the first things, yields the block, does the last things.
87
- warn("Method :resolve is deprecated")
88
- send(:generate_file, template_file_path, markdown_file_path, __method__) do |input_lines, output_lines|
89
- send(:resolve_images, template_file_path, input_lines, output_lines)
90
- end
91
- end
92
- alias resolve_image_urls resolve
93
-
94
58
  private
95
59
 
96
60
  class Heading
@@ -115,8 +79,14 @@ class MarkdownHelper
115
79
  self.new(level, title)
116
80
  end
117
81
 
82
+
118
83
  def link
119
- anchor = title.gsub(/\W+/, '-').downcase
84
+ remove_regexp = /[\#\(\)\[\]\{\}\.\?\+\*\`\"\']+/
85
+ to_hyphen_regexp = /\W+/
86
+ anchor = title.
87
+ gsub(remove_regexp, '').
88
+ gsub(to_hyphen_regexp, '-').
89
+ downcase
120
90
  "[#{title}](##{anchor})"
121
91
  end
122
92
 
@@ -126,16 +96,6 @@ class MarkdownHelper
126
96
  "<!--#{text}-->\n"
127
97
  end
128
98
 
129
- def repo_user_and_name
130
- repo_user = ENV['REPO_USER']
131
- repo_name = ENV['REPO_NAME']
132
- unless repo_user and repo_name
133
- message = 'ENV values for both REPO_USER and REPO_NAME must be defined.'
134
- raise EnvironmentError.new(message)
135
- end
136
- [repo_user, repo_name]
137
- end
138
-
139
99
  def generate_file(template_file_path, markdown_file_path, method)
140
100
  unless File.readable?(template_file_path)
141
101
  message = [
@@ -152,44 +112,104 @@ class MarkdownHelper
152
112
  yield input_lines, output_lines
153
113
  output_lines.push(MarkdownHelper.comment(" <<<<<< END GENERATED FILE (#{method.to_s}): SOURCE #{template_path_in_project} ")) unless pristine
154
114
  end
155
- output = output_lines.join('')
156
- File.write(markdown_file_path, output)
157
- output
115
+ File.open(markdown_file_path, 'w') do |file|
116
+ output_lines.each do |line|
117
+ file.write(line)
118
+ end
119
+ end
158
120
  end
159
121
 
160
122
  def _create_page_toc(input_lines, output_lines)
161
- level_one_seen = false
123
+ first_heading_level = nil
162
124
  input_lines.each do |input_line|
163
- input_line.chomp!
164
- heading = Heading.parse(input_line)
125
+ line = input_line.chomp
126
+ heading = Heading.parse(line)
165
127
  next unless heading
166
- unless level_one_seen || heading.level == 1
167
- message = "First heading must be level 1, not '#{input_line}'"
168
- raise TocHeadingsError.new(message)
169
- end
170
- level_one_seen = true
171
- indentation = ' ' * heading.level
128
+ first_heading_level ||= heading.level
129
+ indentation = ' ' * (heading.level - first_heading_level)
172
130
  output_line = "#{indentation}- #{heading.link}"
173
131
  output_lines.push("#{output_line}\n")
174
132
  end
175
133
  end
176
134
 
177
135
  def include_files(includer_file_path, input_lines, output_lines, inclusions)
178
-
136
+ markdown_lines = []
137
+ page_toc_inclusion = nil
179
138
  input_lines.each_with_index do |input_line, line_index|
180
139
  match_data = input_line.match(INCLUDE_REGEXP)
181
140
  unless match_data
182
- output_lines.push(input_line)
141
+ markdown_lines.push(input_line)
183
142
  next
184
143
  end
185
144
  treatment = match_data[1]
186
145
  cited_includee_file_path = match_data[2]
187
- inclusions.include(
146
+ new_inclusion = Inclusion.new(
188
147
  input_line.chomp,
189
148
  includer_file_path,
190
149
  line_index + 1,
191
150
  cited_includee_file_path,
192
- treatment,
151
+ treatment
152
+ )
153
+ case treatment
154
+ when ':markdown'
155
+ inclusions.include(
156
+ new_inclusion,
157
+ markdown_lines,
158
+ self
159
+ )
160
+ when ':page_toc'
161
+ unless inclusions.inclusions.size == 0
162
+ message = 'Page TOC must be in outermost markdown file.'
163
+ raise MisplacedPageTocError.new(message)
164
+ end
165
+ unless page_toc_inclusion.nil?
166
+ message = 'Only one page TOC allowed.'
167
+ raise MultiplePageTocError.new(message)
168
+ end
169
+ page_toc_inclusion = new_inclusion
170
+ toc_title = match_data[2]
171
+ title_regexp = /^\#{1,6}\s/
172
+ unless toc_title.match(title_regexp)
173
+ message = "TOC title must be a valid markdown header, not #{toc_title}"
174
+ raise InvalidTocTitleError.new(message)
175
+ end
176
+ page_toc_inclusion.page_toc_title = toc_title
177
+ page_toc_inclusion.page_toc_line = input_line
178
+ markdown_lines.push(input_line)
179
+ else
180
+ markdown_lines.push(input_line)
181
+ end
182
+ end
183
+ # If needed, create page TOC and insert into markdown_lines.
184
+ unless page_toc_inclusion.nil?
185
+ toc_lines = [
186
+ page_toc_inclusion.page_toc_title + "\n",
187
+ '',
188
+ ]
189
+ page_toc_index = markdown_lines.index(page_toc_inclusion.page_toc_line)
190
+ lines_to_scan = markdown_lines[page_toc_index + 1..-1]
191
+ _create_page_toc(lines_to_scan, toc_lines)
192
+ markdown_lines.delete_at(page_toc_index)
193
+ markdown_lines.insert(page_toc_index, *toc_lines)
194
+ end
195
+ # Now review the markdown and include everything.
196
+ markdown_lines.each_with_index do |markdown_line, line_index|
197
+ match_data = markdown_line.match(INCLUDE_REGEXP)
198
+ unless match_data
199
+ output_lines.push(markdown_line)
200
+ next
201
+ end
202
+ treatment = match_data[1]
203
+ cited_includee_file_path = match_data[2]
204
+ new_inclusion = Inclusion.new(
205
+ markdown_line.chomp,
206
+ includer_file_path,
207
+ line_index + 1,
208
+ cited_includee_file_path,
209
+ treatment
210
+ )
211
+ inclusions.include(
212
+ new_inclusion,
193
213
  output_lines,
194
214
  self
195
215
  )
@@ -197,7 +217,7 @@ class MarkdownHelper
197
217
  end
198
218
 
199
219
  def self.git_clone_dir_path
200
- git_dir = `git rev-parse --git-dir`.chomp
220
+ git_dir = `git rev-parse --show-toplevel`.chomp
201
221
  unless $?.success?
202
222
  message = <<EOT
203
223
 
@@ -206,71 +226,13 @@ That is, the working directory one of its parents must be a .git directory.
206
226
  EOT
207
227
  raise RuntimeError.new(message)
208
228
  end
209
- if git_dir == '.git'
210
- path = `pwd`.chomp
211
- else
212
- path = File.dirname(git_dir).chomp
213
- end
214
- realpath = Pathname.new(path.sub(%r|/c/|, 'C:/')).realpath
215
- realpath.to_s
229
+ git_dir
216
230
  end
217
231
 
218
232
  def self.path_in_project(path)
219
233
  path.sub(MarkdownHelper.git_clone_dir_path + '/', '')
220
234
  end
221
235
 
222
- def resolve_images(template_file_path, input_lines, output_lines)
223
- input_lines.each do |input_line|
224
- scan_data = input_line.scan(IMAGE_REGEXP)
225
- if scan_data.empty?
226
- output_lines.push(input_line)
227
- next
228
- end
229
- output_lines.push(MarkdownHelper.comment(" >>>>>> BEGIN RESOLVED IMAGES: INPUT-LINE '#{input_line}' ")) unless pristine
230
- output_line = input_line
231
- scan_data.each do |alt_text, path_and_attributes|
232
- original_image_file_path, attributes_s = path_and_attributes.split(/\s?\|\s?/, 2)
233
-
234
- # Attributes.
235
- attributes = attributes_s ? attributes_s.split(/\s+/) : []
236
- formatted_attributes = ['']
237
- attributes.each do |attribute|
238
- name, value = attribute.split('=', 2)
239
- formatted_attributes.push(format('%s="%s"', name, value))
240
- end
241
- formatted_attributes_s = formatted_attributes.join(' ')
242
-
243
- if original_image_file_path.start_with?('http')
244
- image_path = original_image_file_path
245
- else
246
- absolute_template_file_path = File.absolute_path(template_file_path)
247
- template_dir_path = File.dirname(absolute_template_file_path)
248
- absolute_file_path = File.join(
249
- template_dir_path,
250
- original_image_file_path,
251
- )
252
- absolute_file_path = Pathname.new(absolute_file_path).cleanpath.to_s
253
- relative_image_file_path = MarkdownHelper.path_in_project(absolute_file_path)
254
- repo_user, repo_name = repo_user_and_name
255
- image_path = File.join(
256
- "https://raw.githubusercontent.com/#{repo_user}/#{repo_name}/master",
257
- relative_image_file_path,
258
- )
259
- end
260
- img_element = format(
261
- '<img src="%s" alt="%s"%s>',
262
- image_path,
263
- alt_text,
264
- formatted_attributes_s,
265
- )
266
- output_line = output_line.sub(IMAGE_REGEXP, img_element)
267
- end
268
- output_lines.push(output_line)
269
- output_lines.push(MarkdownHelper.comment(" <<<<<< END RESOLVED IMAGES: INPUT-LINE '#{input_line}' ")) unless pristine
270
- end
271
-
272
- end
273
-
274
236
  class Inclusions
275
237
 
276
238
  attr_accessor :inclusions
@@ -280,15 +242,11 @@ EOT
280
242
  end
281
243
 
282
244
  def include(
283
- include_description,
284
- includer_file_path,
285
- includer_line_number,
286
- cited_includee_file_path,
287
- treatment,
288
- output_lines,
289
- markdown_helper
245
+ new_inclusion,
246
+ output_lines,
247
+ markdown_helper
290
248
  )
291
- treatment = case treatment
249
+ treatment = case new_inclusion.treatment
292
250
  when ':code_block'
293
251
  :code_block
294
252
  when ':markdown'
@@ -302,18 +260,12 @@ EOT
302
260
  when ':pre'
303
261
  :pre
304
262
  else
305
- treatment
263
+ new_inclusion.treatment
306
264
  end
307
- new_inclusion = Inclusion.new(
308
- include_description,
309
- includer_file_path,
310
- includer_line_number,
311
- cited_includee_file_path
312
- )
313
265
  if treatment == :markdown
314
266
  check_circularity(new_inclusion)
315
267
  end
316
- includee_path_in_project = MarkdownHelper.path_in_project(new_inclusion.absolute_includee_file_path)
268
+ includee_path_in_project = MarkdownHelper.path_in_project(new_inclusion.absolute_includee_file_path)
317
269
  output_lines.push(MarkdownHelper.comment(" >>>>>> BEGIN INCLUDED FILE (#{treatment}): SOURCE #{includee_path_in_project} ")) unless markdown_helper.pristine
318
270
  begin
319
271
  include_lines = File.readlines(new_inclusion.absolute_includee_file_path)
@@ -329,7 +281,7 @@ EOT
329
281
  end
330
282
  last_line = include_lines.last
331
283
  unless last_line && last_line.match("\n")
332
- message = "Warning: Included file has no trailing newline: #{cited_includee_file_path}"
284
+ message = "Warning: Included file has no trailing newline: #{new_inclusion.cited_includee_file_path}"
333
285
  warn(message)
334
286
  end
335
287
  case treatment
@@ -346,7 +298,7 @@ EOT
346
298
  output_lines.push("</pre>\n")
347
299
  else
348
300
  # Use the file name as a label.
349
- file_name_line = format("```%s```:\n", File.basename(cited_includee_file_path))
301
+ file_name_line = format("```%s```:\n", File.basename(new_inclusion.cited_includee_file_path))
350
302
  output_lines.push(file_name_line)
351
303
  # Put into code block.
352
304
  language = treatment == :code_block ? '' : treatment
@@ -389,65 +341,6 @@ EOT
389
341
  lines.join("\n")
390
342
  end
391
343
 
392
- def self.assert_io_exception(test, expected_exception_class, expected_label, expected_file_path, e)
393
- test.assert_kind_of(expected_exception_class, e)
394
- lines = e.message.split("\n")
395
- actual_label = lines.shift
396
- test.assert_equal(expected_label, actual_label)
397
- actual_file_path = lines.shift
398
- test.assert_equal(expected_file_path.inspect, actual_file_path)
399
- end
400
-
401
- def self.assert_inclusion_exception(test, expected_exception_class, exception_label, expected_inclusions, e)
402
- test.assert_kind_of(expected_exception_class, e)
403
- lines = e.message.split("\n")
404
- label_line = lines.shift
405
- test.assert_equal(exception_label, label_line)
406
- backtrace_line = lines.shift
407
- test.assert_equal(BACKTRACE_LABEL, backtrace_line)
408
- level_line_count = 1 + Inclusion::LINE_COUNT
409
- level_count = lines.size / level_line_count
410
- # Backtrace levels are innermost first, opposite of inclusions.
411
- reversed_inclusions = expected_inclusions.inclusions.reverse
412
- (0...level_count).each do |level_index|
413
- level_line = lines.shift
414
- inclusion_lines = lines.shift(Inclusion::LINE_COUNT)
415
- test.assert_equal("#{LEVEL_LABEL} #{level_index}:", level_line)
416
- expected_inclusion = reversed_inclusions[level_index]
417
- expected_inclusion.assert_lines(test, level_index, inclusion_lines)
418
- end
419
- end
420
-
421
- def self.assert_circular_exception(test, expected_inclusions, e)
422
- self.assert_inclusion_exception(
423
- test,
424
- CircularIncludeError,
425
- CIRCULAR_EXCEPTION_LABEL,
426
- expected_inclusions,
427
- e
428
- )
429
- end
430
-
431
- def self.assert_includee_missing_exception(test, expected_inclusions, e)
432
- self.assert_inclusion_exception(
433
- test,
434
- Exception,
435
- MISSING_INCLUDEE_EXCEPTION_LABEL,
436
- expected_inclusions,
437
- e
438
- )
439
- end
440
-
441
- def self.assert_template_exception(test, expected_file_path, e)
442
- self.assert_io_exception(
443
- test,
444
- Exception,
445
- UNREADABLE_INPUT_EXCEPTION_LABEL,
446
- expected_file_path,
447
- e
448
- )
449
- end
450
-
451
344
  end
452
345
 
453
346
  class Inclusion
@@ -460,19 +353,23 @@ EOT
460
353
  :include_description,
461
354
  :absolute_includee_file_path,
462
355
  :cited_includee_file_path,
463
- :include_description
356
+ :treatment,
357
+ :page_toc_title,
358
+ :page_toc_line
464
359
 
465
360
  def initialize(
466
361
  include_description,
467
362
  includer_file_path,
468
363
  includer_line_number,
469
- cited_includee_file_path
364
+ cited_includee_file_path,
365
+ treatment
470
366
  )
471
367
  self.include_description = include_description
472
368
  self.includer_file_path = includer_file_path
473
369
  self.includer_line_number = includer_line_number
474
370
  self.cited_includee_file_path = cited_includee_file_path
475
371
  self.absolute_includee_file_path = absolute_includee_file_path
372
+ self.treatment = treatment
476
373
  self.absolute_includee_file_path = File.absolute_path(File.join(
477
374
  File.dirname(includer_file_path),
478
375
  cited_includee_file_path,
@@ -485,10 +382,11 @@ EOT
485
382
  Pathname.new(absolute_includee_file_path).realpath.to_s
486
383
  end
487
384
 
385
+ def indentation(level)
386
+ ' ' * level
387
+ end
388
+
488
389
  def to_lines(indentation_level)
489
- def indentation(level)
490
- ' ' * level
491
- end
492
390
  relative_inluder_file_path = MarkdownHelper.path_in_project(includer_file_path)
493
391
  relative_inludee_file_path = MarkdownHelper.path_in_project(absolute_includee_file_path)
494
392
  text = <<EOT
@@ -501,37 +399,6 @@ EOT
501
399
  text.split("\n")
502
400
  end
503
401
 
504
- def assert_lines(test, level_index, actual_lines)
505
- level_label = "Level #{level_index}:"
506
- # Includer label.
507
- includee_label = actual_lines.shift
508
- test.assert_match(/^\s*Includer:$/, includee_label, level_label)
509
- # Includer locatioin.
510
- location = actual_lines.shift
511
- message = "#{level_label} includer location"
512
- test.assert_match(/^\s*Location:/, location, message)
513
- includer_realpath = Pathname.new(includer_file_path).realpath.to_s
514
- relative_path = MarkdownHelper.path_in_project(includer_realpath)
515
- r = Regexp.new(Regexp.escape("#{relative_path}:#{includer_line_number}") + '$')
516
- test.assert_match(r, location, message)
517
- # Include description.
518
- description = actual_lines.shift
519
- message = "#{level_label} include description"
520
- test.assert_match(/^\s*Include description:/, description, message)
521
- r = Regexp.new(Regexp.escape("#{include_description}") + '$')
522
- test.assert_match(r, description, message)
523
- # Includee label.
524
- includee_label = actual_lines.shift
525
- test.assert_match(/^\s*Includee:$/, includee_label, level_label)
526
- # Includee file path.
527
- includee_file_path = actual_lines.shift
528
- message = "#{level_label} includee cited file path"
529
- test.assert_match(/^\s*File path:/, includee_file_path, message)
530
- relative_path = MarkdownHelper.path_in_project(absolute_includee_file_path)
531
- r = Regexp.new(Regexp.escape("#{relative_path}") + '$')
532
- test.assert_match(r, includee_file_path, message)
533
- end
534
-
535
402
  end
536
403
 
537
404
  end