markdown_helper 2.3.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +24 -17
  4. data/bin/_run_irb +46 -0
  5. data/lib/markdown_helper.rb +3 -384
  6. data/lib/markdown_helper/markdown_helper.rb +79 -0
  7. data/lib/markdown_helper/markdown_includer.rb +315 -0
  8. data/lib/markdown_helper/markdown_irb_runner.rb +65 -0
  9. data/lib/markdown_helper/version.rb +1 -1
  10. data/markdown/readme/README.template.md +21 -16
  11. data/markdown/use_cases/Rakefile +9 -2
  12. data/markdown/use_cases/include/diagnose_circular_includes/diagnose_circular_includes.err +26 -0
  13. data/markdown/use_cases/{include_files → include}/diagnose_circular_includes/includer.md +0 -0
  14. data/markdown/use_cases/{include_files → include}/diagnose_circular_includes/includer_0.md +0 -0
  15. data/markdown/use_cases/{include_files → include}/diagnose_circular_includes/includer_1.md +0 -0
  16. data/markdown/use_cases/{include_files → include}/diagnose_circular_includes/includer_2.md +0 -0
  17. data/markdown/use_cases/{include_files → include}/diagnose_circular_includes/use_case.md +8 -8
  18. data/markdown/use_cases/{include_files → include}/diagnose_circular_includes/use_case_template.md +0 -0
  19. data/markdown/use_cases/include/diagnose_missing_includee/diagnose_missing_includee.err +26 -0
  20. data/markdown/use_cases/{include_files → include}/diagnose_missing_includee/includer.md +0 -0
  21. data/markdown/use_cases/{include_files → include}/diagnose_missing_includee/includer_0.md +0 -0
  22. data/markdown/use_cases/{include_files → include}/diagnose_missing_includee/includer_1.md +0 -0
  23. data/markdown/use_cases/{include_files → include}/diagnose_missing_includee/includer_2.md +0 -0
  24. data/markdown/use_cases/{include_files → include}/diagnose_missing_includee/use_case.md +8 -8
  25. data/markdown/use_cases/{include_files → include}/diagnose_missing_includee/use_case_template.md +0 -0
  26. data/markdown/use_cases/{include_files → include}/include.rb +0 -0
  27. data/markdown/use_cases/{include_files → include}/include_code_block/hello.rb +0 -0
  28. data/markdown/use_cases/{include_files → include}/include_code_block/included.md +0 -0
  29. data/markdown/use_cases/{include_files → include}/include_code_block/includer.md +0 -0
  30. data/markdown/use_cases/{include_files → include}/include_code_block/use_case.md +0 -0
  31. data/markdown/use_cases/{include_files → include}/include_code_block/use_case_template.md +0 -0
  32. data/markdown/use_cases/{include_files → include}/include_generated_text/use_case.md +0 -0
  33. data/markdown/use_cases/{include_files → include}/include_generated_text/use_case_template.md +0 -0
  34. data/markdown/use_cases/{include_files → include}/include_highlighted_code/hello.rb +0 -0
  35. data/markdown/use_cases/{include_files → include}/include_highlighted_code/included.md +0 -0
  36. data/markdown/use_cases/{include_files → include}/include_highlighted_code/includer.md +0 -0
  37. data/markdown/use_cases/{include_files → include}/include_highlighted_code/use_case.md +0 -0
  38. data/markdown/use_cases/{include_files → include}/include_highlighted_code/use_case_template.md +0 -0
  39. data/markdown/use_cases/{include_files → include}/include_markdown/included.md +0 -0
  40. data/markdown/use_cases/{include_files → include}/include_markdown/includer.md +0 -0
  41. data/markdown/use_cases/{include_files → include}/include_markdown/markdown.md +0 -0
  42. data/markdown/use_cases/{include_files → include}/include_markdown/use_case.md +0 -0
  43. data/markdown/use_cases/{include_files → include}/include_markdown/use_case_template.md +0 -0
  44. data/markdown/use_cases/{include_files → include}/include_page_toc/included.md +0 -0
  45. data/markdown/use_cases/{include_files → include}/include_page_toc/includer.md +0 -0
  46. data/markdown/use_cases/{include_files → include}/include_page_toc/markdown_0.md +0 -0
  47. data/markdown/use_cases/{include_files → include}/include_page_toc/markdown_1.md +0 -0
  48. data/markdown/use_cases/{include_files → include}/include_page_toc/use_case.md +0 -0
  49. data/markdown/use_cases/{include_files → include}/include_page_toc/use_case_template.md +0 -0
  50. data/markdown/use_cases/{include_files → include}/include_text_as_comment/hello.rb +0 -0
  51. data/markdown/use_cases/{include_files → include}/include_text_as_comment/included.md +0 -0
  52. data/markdown/use_cases/{include_files → include}/include_text_as_comment/includer.md +0 -0
  53. data/markdown/use_cases/{include_files → include}/include_text_as_comment/use_case.md +0 -0
  54. data/markdown/use_cases/{include_files → include}/include_text_as_comment/use_case_template.md +0 -0
  55. data/markdown/use_cases/{include_files → include}/include_text_as_details/details.md +0 -0
  56. data/markdown/use_cases/{include_files → include}/include_text_as_details/included.md +0 -0
  57. data/markdown/use_cases/{include_files → include}/include_text_as_details/includer.md +0 -0
  58. data/markdown/use_cases/{include_files → include}/include_text_as_details/use_case.md +0 -0
  59. data/markdown/use_cases/{include_files → include}/include_text_as_details/use_case_template.md +0 -0
  60. data/markdown/use_cases/{include_files → include}/include_text_as_pre/included.md +0 -0
  61. data/markdown/use_cases/{include_files → include}/include_text_as_pre/includer.md +0 -0
  62. data/markdown/use_cases/{include_files → include}/include_text_as_pre/triple_backtick.md +0 -0
  63. data/markdown/use_cases/{include_files → include}/include_text_as_pre/use_case.md +0 -0
  64. data/markdown/use_cases/{include_files → include}/include_text_as_pre/use_case_template.md +0 -0
  65. data/markdown/use_cases/{include_files → include}/include_with_added_comments/included.md +0 -0
  66. data/markdown/use_cases/{include_files → include}/include_with_added_comments/includee.md +0 -0
  67. data/markdown/use_cases/{include_files → include}/include_with_added_comments/includer.md +0 -0
  68. data/markdown/use_cases/{include_files → include}/include_with_added_comments/use_case.md +0 -0
  69. data/markdown/use_cases/{include_files → include}/include_with_added_comments/use_case_template.md +0 -0
  70. data/markdown/use_cases/{include_files → include}/interface.md +0 -0
  71. data/markdown/use_cases/{include_files → include}/nest_inclusions/included.md +0 -0
  72. data/markdown/use_cases/{include_files → include}/nest_inclusions/includee.md +0 -0
  73. data/markdown/use_cases/{include_files → include}/nest_inclusions/includer.md +0 -0
  74. data/markdown/use_cases/{include_files → include}/nest_inclusions/nested_includee.md +0 -0
  75. data/markdown/use_cases/{include_files → include}/nest_inclusions/use_case.md +0 -0
  76. data/markdown/use_cases/{include_files → include}/nest_inclusions/use_case_template.md +0 -0
  77. data/markdown/use_cases/{include_files → include}/reuse_text/included.md +0 -0
  78. data/markdown/use_cases/{include_files → include}/reuse_text/includee.md +0 -0
  79. data/markdown/use_cases/{include_files → include}/reuse_text/includer.md +0 -0
  80. data/markdown/use_cases/{include_files → include}/reuse_text/use_case.md +0 -0
  81. data/markdown/use_cases/{include_files → include}/reuse_text/use_case_template.md +0 -0
  82. data/markdown/use_cases/run_irb/interface.md +16 -0
  83. data/markdown/use_cases/run_irb/run_irb.rb +5 -0
  84. data/markdown/use_cases/run_irb/run_irb/markdown.md +43 -0
  85. data/markdown/use_cases/run_irb/run_irb/template.md +37 -0
  86. data/markdown/use_cases/run_irb/run_irb/use_case.md +178 -0
  87. data/markdown/use_cases/run_irb/run_irb/use_case_template.md +33 -0
  88. data/markdown/use_cases/use_cases.md +17 -14
  89. metadata +82 -72
  90. data/markdown/use_cases/include_files/diagnose_circular_includes/diagnose_circular_includes.err +0 -26
  91. data/markdown/use_cases/include_files/diagnose_missing_includee/diagnose_missing_includee.err +0 -26
@@ -0,0 +1,79 @@
1
+ class MarkdownHelper
2
+
3
+ attr_accessor :pristine
4
+
5
+ class MarkdownHelperError < RuntimeError; end
6
+ class OptionError < MarkdownHelperError; end
7
+ class MultiplePageTocError < MarkdownHelperError; end
8
+ class UnreadableTemplateError < MarkdownHelperError; end
9
+ class UnwritableMarkdownError < MarkdownHelperError; end
10
+ class InvalidTocTitleError < MarkdownHelperError; end
11
+ class CircularIncludeError < MarkdownHelperError; end
12
+ class UnreadableIncludeeError < MarkdownHelperError; end
13
+
14
+ def MarkdownHelper.git_clone_dir_path
15
+ git_dir = `git rev-parse --show-toplevel`.chomp
16
+ unless $?.success?
17
+ message = <<EOT
18
+
19
+ Markdown helper must run inside a .git project.
20
+ That is, the working directory one of its parents must be a .git directory.
21
+ EOT
22
+ raise RuntimeError.new(message)
23
+ end
24
+ git_dir
25
+ end
26
+
27
+ def initialize(options = {})
28
+ # Confirm that we're in a git project.
29
+ # This is necessary so that we can prune file paths in the tests,
30
+ # which otherwise would fail because of differing installation directories.
31
+ # It also allows pruned paths to be used in the inserted comments (when not pristine).
32
+ MarkdownHelper.git_clone_dir_path
33
+ default_options = {
34
+ :pristine => false,
35
+ }
36
+ merged_options = default_options.merge(options)
37
+ merged_options.each_pair do |method, value|
38
+ unless self.respond_to?(method)
39
+ raise OptionError.new("Unknown option: #{method}")
40
+ end
41
+ setter_method = "#{method}="
42
+ send(setter_method, value)
43
+ merged_options.delete(method)
44
+ end
45
+ end
46
+
47
+ def generate_file(method, template_file_path, markdown_file_path)
48
+ template_path_in_project = MarkdownHelper.path_in_project(template_file_path)
49
+ output_lines = []
50
+ yield output_lines
51
+ unless pristine
52
+ output_lines.unshift(MarkdownHelper.comment(" >>>>>> BEGIN GENERATED FILE (#{method}): SOURCE #{template_path_in_project} "))
53
+ output_lines.push(MarkdownHelper.comment(" <<<<<< END GENERATED FILE (#{method}): SOURCE #{template_path_in_project} "))
54
+ end
55
+ output_lines.push('')
56
+ output = output_lines.join("\n")
57
+ File.write(markdown_file_path, output)
58
+ end
59
+
60
+ def run_irb(template_file_path, markdown_file_path)
61
+ irb_runner = MarkdownIrbRunner.new(:pristine => pristine)
62
+ irb_runner.run_irb(template_file_path, markdown_file_path)
63
+ end
64
+
65
+ def include(template_file_path, markdown_file_path)
66
+ includer = MarkdownIncluder.new(:pristine => pristine)
67
+ includer.include(template_file_path, markdown_file_path)
68
+ end
69
+
70
+ def self.comment(text)
71
+ "<!--#{text}-->"
72
+ end
73
+
74
+ def self.path_in_project(file_path)
75
+ abs_path = File.absolute_path(file_path)
76
+ abs_path.sub(MarkdownHelper.git_clone_dir_path + '/', '')
77
+ end
78
+
79
+ end
@@ -0,0 +1,315 @@
1
+ require 'pathname'
2
+
3
+ class MarkdownIncluder < MarkdownHelper
4
+
5
+ INCLUDE_REGEXP = /^@\[([^\[]+)\]\(([^)]+)\)$/
6
+ INCLUDE_MARKDOWN_REGEXP = /^@\[:markdown\]\(([^)]+)\)$/
7
+
8
+ def include(template_file_path, markdown_file_path)
9
+ @inclusions = []
10
+ generate_file(:include, template_file_path, markdown_file_path) do |output_lines|
11
+ Dir.chdir(File.dirname(template_file_path)) do
12
+ markdown_lines = include_markdown(template_file_path)
13
+ markdown_lines = include_page_toc(markdown_lines)
14
+ include_all(template_file_path, markdown_lines, output_lines)
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ def MarkdownIncluder.pre(text)
21
+ "<pre>\n#{text}</pre>"
22
+ end
23
+
24
+ def MarkdownIncluder.details(text)
25
+ "<details>\n#{text}</details>"
26
+ end
27
+
28
+ def include_markdown(template_file_path)
29
+ Dir.chdir(File.dirname(template_file_path)) do
30
+ markdown_lines = []
31
+ unless File.readable?(template_file_path)
32
+ path_in_project = MarkdownHelper.path_in_project(template_file_path )
33
+ message = [
34
+ "Could not read template file: #{path_in_project}",
35
+ MarkdownIncluder.backtrace_inclusions(@inclusions),
36
+ ].join("\n")
37
+ e = UnreadableTemplateError.new(message)
38
+ e.set_backtrace([])
39
+ raise e
40
+ end
41
+ template_lines = File.readlines(template_file_path)
42
+ template_lines.each_with_index do |template_line, i|
43
+ template_line.chomp!
44
+ treatment, includee_file_path = *parse_include(template_line)
45
+ if treatment.nil?
46
+ markdown_lines.push(template_line)
47
+ next
48
+ end
49
+ if treatment == ':page_toc'
50
+ markdown_lines.push(template_line)
51
+ next
52
+ end
53
+ inclusion = Inclusion.new(
54
+ template_file_path,
55
+ template_line,
56
+ i,
57
+ treatment,
58
+ includee_file_path,
59
+ @inclusions
60
+ )
61
+ case treatment
62
+ when ':markdown'
63
+ check_includee(inclusion)
64
+ check_circularity(inclusion)
65
+ @inclusions.push(inclusion)
66
+ includee_lines = include_markdown(File.absolute_path(includee_file_path))
67
+ markdown_lines.concat(includee_lines)
68
+ when ':comment'
69
+ text = File.read(includee_file_path)
70
+ markdown_lines.push(MarkdownHelper.comment(text))
71
+ @inclusions.push(inclusion)
72
+ when ':pre'
73
+ text = File.read(includee_file_path)
74
+ markdown_lines.push(MarkdownIncluder.pre(text))
75
+ @inclusions.push(inclusion)
76
+ when ':details'
77
+ text = File.read(includee_file_path)
78
+ markdown_lines.push(MarkdownIncluder.details(text))
79
+ @inclusions.push(inclusion)
80
+ else
81
+ markdown_lines.push(template_line)
82
+ next
83
+ end
84
+ @inclusions.pop
85
+ treatment.sub!(/^:/, '')
86
+ add_inclusion_comments(treatment, includee_file_path, markdown_lines)
87
+ end
88
+ markdown_lines
89
+ end
90
+ end
91
+
92
+ def include_page_toc(template_lines)
93
+ toc_line_index = nil
94
+ toc_title = nil
95
+ template_lines.each_with_index do |template_line, i|
96
+ match_data = template_line.match(INCLUDE_REGEXP)
97
+ next unless match_data
98
+ treatment = match_data[1]
99
+ next unless treatment == ':page_toc'
100
+ unless toc_line_index.nil?
101
+ message = 'Multiple page TOC not allowed'
102
+ raise MultiplePageTocError.new(message)
103
+ end
104
+ toc_line_index = i
105
+ toc_title = match_data[2]
106
+ title_regexp = /^\#{1,6}\s/
107
+ unless toc_title.match(title_regexp)
108
+ message = "TOC title must be a valid markdown header, not #{toc_title}"
109
+ raise InvalidTocTitleError.new(message)
110
+ end
111
+ end
112
+ return template_lines unless toc_line_index
113
+ toc_lines = [toc_title]
114
+ first_heading_level = nil
115
+ template_lines.each_with_index do |input_line, i|
116
+ next if i < toc_line_index
117
+ line = input_line.chomp
118
+ heading = Heading.parse(line)
119
+ next unless heading
120
+ first_heading_level ||= heading.level
121
+ indentation = ' ' * (heading.level - first_heading_level)
122
+ toc_line = "#{indentation}- #{heading.link}"
123
+ toc_lines.push(toc_line)
124
+ end
125
+ template_lines.delete_at(toc_line_index)
126
+ template_lines.insert(toc_line_index, *toc_lines)
127
+ template_lines
128
+ end
129
+
130
+ def include_all(template_file_path, template_lines, output_lines)
131
+ template_lines.each_with_index do |template_line, i|
132
+ treatment, includee_file_path = *parse_include(template_line)
133
+ if treatment.nil?
134
+ output_lines.push(template_line)
135
+ next
136
+ end
137
+ inclusion = Inclusion.new(
138
+ template_file_path,
139
+ template_line,
140
+ i,
141
+ treatment,
142
+ includee_file_path,
143
+ @inclusions
144
+ )
145
+ check_includee(inclusion)
146
+ @inclusions.push(inclusion)
147
+ file_marker = format('```%s```:', File.basename(includee_file_path))
148
+ begin_backticks = '```'
149
+ end_backticks = '```'
150
+ begin_backticks += treatment unless treatment.start_with?(':')
151
+ includee_lines = File.read(includee_file_path).split("\n")
152
+ includee_lines.unshift(begin_backticks)
153
+ includee_lines.unshift(file_marker)
154
+ includee_lines.push(end_backticks)
155
+ add_inclusion_comments(treatment.sub(':', ''), includee_file_path, includee_lines)
156
+ output_lines.concat(includee_lines)
157
+ end
158
+ end
159
+
160
+ def add_inclusion_comments(treatment, includee_file_path, lines)
161
+ path_in_project = MarkdownHelper.path_in_project(includee_file_path)
162
+ unless pristine
163
+ comment = format(' >>>>>> BEGIN INCLUDED FILE (%s): SOURCE %s ', treatment, path_in_project)
164
+ lines.unshift(MarkdownHelper.comment(comment))
165
+ comment = format(' <<<<<< END INCLUDED FILE (%s): SOURCE %s ', treatment, path_in_project)
166
+ lines.push(MarkdownHelper.comment(comment))
167
+ end
168
+ end
169
+
170
+ def parse_include(line)
171
+ match_data = line.match(INCLUDE_REGEXP)
172
+ return [nil, nil] unless match_data
173
+ treatment = match_data[1]
174
+ includee_file_path = match_data[2]
175
+ [treatment, includee_file_path]
176
+ end
177
+
178
+ class Heading
179
+
180
+ attr_accessor :level, :title
181
+
182
+ def initialize(level, title)
183
+ self.level = level
184
+ self.title = title
185
+ end
186
+
187
+ def self.parse(line)
188
+ # Four leading spaces not allowed (but three are allowed).
189
+ return nil if line.start_with?(' ' * 4)
190
+ stripped_line = line.sub(/^ */, '')
191
+ # Now must begin with hash marks and space.
192
+ return nil unless stripped_line.match(/^#+ /)
193
+ hash_marks, title = stripped_line.split(' ', 2)
194
+ level = hash_marks.size
195
+ # Seventh level heading not allowed.
196
+ return nil if level > 6
197
+ self.new(level, title)
198
+ end
199
+
200
+
201
+ def link
202
+ remove_regexp = /[\#\(\)\[\]\{\}\.\?\+\*\`\"\']+/
203
+ to_hyphen_regexp = /\W+/
204
+ anchor = title.
205
+ gsub(remove_regexp, '').
206
+ gsub(to_hyphen_regexp, '-').
207
+ downcase
208
+ "[#{title}](##{anchor})"
209
+ end
210
+
211
+ end
212
+
213
+ def check_circularity(inclusion)
214
+ included_file_paths = @inclusions.collect { |x| x.includee_real_file_path}
215
+ previously_included = included_file_paths.include?(inclusion.includee_real_file_path)
216
+ if previously_included
217
+ @inclusions.push(inclusion)
218
+ message = [
219
+ 'Includes are circular:',
220
+ MarkdownIncluder.backtrace_inclusions(@inclusions),
221
+ ].join("\n")
222
+ e = CircularIncludeError.new(message)
223
+ e.set_backtrace([])
224
+ raise e
225
+ end
226
+ end
227
+
228
+ def check_includee(inclusion)
229
+ unless File.readable?(inclusion.includee_absolute_file_path)
230
+ @inclusions.push(inclusion)
231
+ message = [
232
+ 'Could not read includee file:',
233
+ MarkdownIncluder.backtrace_inclusions(@inclusions),
234
+ ].join("\n")
235
+ e = UnreadableIncludeeError.new(message)
236
+ e.set_backtrace([])
237
+ raise e
238
+ end
239
+
240
+ end
241
+
242
+ def self.backtrace_inclusions(inclusions)
243
+ lines = [' Backtrace (innermost include first):']
244
+ inclusions.reverse.each_with_index do |inclusion, i|
245
+ lines.push("#{' Level'} #{i}:")
246
+ level_lines = inclusion.to_lines(indentation_level = 3)
247
+ lines.push(*level_lines)
248
+ end
249
+ lines.join("\n")
250
+ end
251
+
252
+ class Inclusion
253
+
254
+ attr_accessor \
255
+ :includer_file_path,
256
+ :includer_absolute_file_path,
257
+ :include_pragma,
258
+ :treatment,
259
+ :includer_line_number,
260
+ :cited_includee_file_path,
261
+ :includee_absolute_file_path
262
+
263
+ def initialize(
264
+ includer_file_path,
265
+ include_pragma,
266
+ includer_line_number,
267
+ treatment,
268
+ cited_includee_file_path,
269
+ inclusions
270
+ )
271
+ self.includer_file_path = includer_file_path
272
+ self.include_pragma = include_pragma
273
+ self.includer_line_number = includer_line_number
274
+ self.treatment = treatment
275
+ self.cited_includee_file_path = cited_includee_file_path
276
+
277
+ self.includer_absolute_file_path = File.absolute_path(includer_file_path)
278
+ unless File.exist?(self.includer_absolute_file_path)
279
+ fail self.includer_absolute_file_path
280
+ end
281
+
282
+ self.includee_absolute_file_path = File.absolute_path(File.join(
283
+ File.dirname(includer_file_path),
284
+ cited_includee_file_path,
285
+ ))
286
+ end
287
+
288
+ def includer_real_file_path
289
+ Pathname.new(includer_absolute_file_path).realpath.to_s
290
+ end
291
+
292
+ def includee_real_file_path
293
+ Pathname.new(includee_absolute_file_path).realpath.to_s
294
+ end
295
+
296
+ def indentation(level)
297
+ ' ' * level
298
+ end
299
+
300
+ def to_lines(indentation_level)
301
+ relative_inluder_file_path = MarkdownHelper.path_in_project(includer_file_path)
302
+ relative_inludee_file_path = MarkdownHelper.path_in_project(includee_absolute_file_path)
303
+ text = <<EOT
304
+ #{indentation(indentation_level)}Includer:
305
+ #{indentation(indentation_level+1)}Location: #{relative_inluder_file_path}:#{includer_line_number}
306
+ #{indentation(indentation_level+1)}Include pragma: #{include_pragma}
307
+ #{indentation(indentation_level)}Includee:
308
+ #{indentation(indentation_level+1)}File path: #{relative_inludee_file_path}
309
+ EOT
310
+ text.split("\n")
311
+ end
312
+
313
+ end
314
+
315
+ end
@@ -0,0 +1,65 @@
1
+ require 'tmpdir'
2
+
3
+ class MarkdownIrbRunner < MarkdownHelper
4
+
5
+ IrbFilterPragma = '```#run_irb'
6
+ BeginTextDirective = "=begin #{IrbFilterPragma}"
7
+ EndTextDirective = "=end #{IrbFilterPragma}"
8
+
9
+ def run_irb(template_file_path, markdown_file_path)
10
+ irb_input = make_irb_input(template_file_path)
11
+ irb_output = make_irb_output(irb_input)
12
+ markdown = make_markdown(irb_output)
13
+ File.write(markdown_file_path, markdown)
14
+ end
15
+
16
+ def make_irb_input(template_file_path)
17
+ irb_lines = []
18
+ irb_lines.push(BeginTextDirective)
19
+ source_lines = File.readlines(template_file_path)
20
+ source_lines.each do |source_line|
21
+ source_line.chomp!
22
+ if source_line == IrbFilterPragma
23
+ irb_lines.push(EndTextDirective)
24
+ elsif source_line == '```'
25
+ irb_lines.push(BeginTextDirective)
26
+ else
27
+ irb_lines.push(source_line)
28
+ end
29
+ end
30
+ irb_lines.push(EndTextDirective)
31
+ irb_lines.push('') unless irb_lines.last.empty?
32
+ irb_lines.join("\n")
33
+ end
34
+
35
+ def make_irb_output(irb_input)
36
+ Dir.mktmpdir do |dir|
37
+ Dir.chdir(dir) do
38
+ File.write('irb_input', irb_input)
39
+ command = 'irb --noecho irb_input | tail +2 | head --lines=-2 > irb_output'
40
+ system(command )
41
+ File.read('irb_output')
42
+ end
43
+ end
44
+ end
45
+
46
+ def make_markdown(irb_output)
47
+ output_lines = []
48
+ irb_lines = irb_output.split("\n")
49
+ irb_lines.each_with_index do |irb_line, i|
50
+ irb_line.chomp!
51
+ if irb_line == BeginTextDirective
52
+ output_lines.push('```') unless i == 0
53
+ next
54
+ end
55
+ if irb_line == EndTextDirective
56
+ output_lines.push('```ruby') unless i == irb_lines.size - 1
57
+ next
58
+ end
59
+ output_lines.push(irb_line)
60
+ end
61
+ output_lines.push('') unless output_lines.last.nil? || output_lines.last.empty?
62
+ output_lines.join("\n")
63
+ end
64
+
65
+ end