markdown_helper 2.3.0 → 2.4.0
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +24 -17
- data/bin/_run_irb +46 -0
- data/lib/markdown_helper.rb +3 -384
- data/lib/markdown_helper/markdown_helper.rb +79 -0
- data/lib/markdown_helper/markdown_includer.rb +315 -0
- data/lib/markdown_helper/markdown_irb_runner.rb +65 -0
- data/lib/markdown_helper/version.rb +1 -1
- data/markdown/readme/README.template.md +21 -16
- data/markdown/use_cases/Rakefile +9 -2
- data/markdown/use_cases/include/diagnose_circular_includes/diagnose_circular_includes.err +26 -0
- data/markdown/use_cases/{include_files → include}/diagnose_circular_includes/includer.md +0 -0
- data/markdown/use_cases/{include_files → include}/diagnose_circular_includes/includer_0.md +0 -0
- data/markdown/use_cases/{include_files → include}/diagnose_circular_includes/includer_1.md +0 -0
- data/markdown/use_cases/{include_files → include}/diagnose_circular_includes/includer_2.md +0 -0
- data/markdown/use_cases/{include_files → include}/diagnose_circular_includes/use_case.md +8 -8
- data/markdown/use_cases/{include_files → include}/diagnose_circular_includes/use_case_template.md +0 -0
- data/markdown/use_cases/include/diagnose_missing_includee/diagnose_missing_includee.err +26 -0
- data/markdown/use_cases/{include_files → include}/diagnose_missing_includee/includer.md +0 -0
- data/markdown/use_cases/{include_files → include}/diagnose_missing_includee/includer_0.md +0 -0
- data/markdown/use_cases/{include_files → include}/diagnose_missing_includee/includer_1.md +0 -0
- data/markdown/use_cases/{include_files → include}/diagnose_missing_includee/includer_2.md +0 -0
- data/markdown/use_cases/{include_files → include}/diagnose_missing_includee/use_case.md +8 -8
- data/markdown/use_cases/{include_files → include}/diagnose_missing_includee/use_case_template.md +0 -0
- data/markdown/use_cases/{include_files → include}/include.rb +0 -0
- data/markdown/use_cases/{include_files → include}/include_code_block/hello.rb +0 -0
- data/markdown/use_cases/{include_files → include}/include_code_block/included.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_code_block/includer.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_code_block/use_case.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_code_block/use_case_template.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_generated_text/use_case.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_generated_text/use_case_template.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_highlighted_code/hello.rb +0 -0
- data/markdown/use_cases/{include_files → include}/include_highlighted_code/included.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_highlighted_code/includer.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_highlighted_code/use_case.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_highlighted_code/use_case_template.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_markdown/included.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_markdown/includer.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_markdown/markdown.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_markdown/use_case.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_markdown/use_case_template.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_page_toc/included.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_page_toc/includer.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_page_toc/markdown_0.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_page_toc/markdown_1.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_page_toc/use_case.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_page_toc/use_case_template.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_comment/hello.rb +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_comment/included.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_comment/includer.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_comment/use_case.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_comment/use_case_template.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_details/details.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_details/included.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_details/includer.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_details/use_case.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_details/use_case_template.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_pre/included.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_pre/includer.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_pre/triple_backtick.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_pre/use_case.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_text_as_pre/use_case_template.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_with_added_comments/included.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_with_added_comments/includee.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_with_added_comments/includer.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_with_added_comments/use_case.md +0 -0
- data/markdown/use_cases/{include_files → include}/include_with_added_comments/use_case_template.md +0 -0
- data/markdown/use_cases/{include_files → include}/interface.md +0 -0
- data/markdown/use_cases/{include_files → include}/nest_inclusions/included.md +0 -0
- data/markdown/use_cases/{include_files → include}/nest_inclusions/includee.md +0 -0
- data/markdown/use_cases/{include_files → include}/nest_inclusions/includer.md +0 -0
- data/markdown/use_cases/{include_files → include}/nest_inclusions/nested_includee.md +0 -0
- data/markdown/use_cases/{include_files → include}/nest_inclusions/use_case.md +0 -0
- data/markdown/use_cases/{include_files → include}/nest_inclusions/use_case_template.md +0 -0
- data/markdown/use_cases/{include_files → include}/reuse_text/included.md +0 -0
- data/markdown/use_cases/{include_files → include}/reuse_text/includee.md +0 -0
- data/markdown/use_cases/{include_files → include}/reuse_text/includer.md +0 -0
- data/markdown/use_cases/{include_files → include}/reuse_text/use_case.md +0 -0
- data/markdown/use_cases/{include_files → include}/reuse_text/use_case_template.md +0 -0
- data/markdown/use_cases/run_irb/interface.md +16 -0
- data/markdown/use_cases/run_irb/run_irb.rb +5 -0
- data/markdown/use_cases/run_irb/run_irb/markdown.md +43 -0
- data/markdown/use_cases/run_irb/run_irb/template.md +37 -0
- data/markdown/use_cases/run_irb/run_irb/use_case.md +178 -0
- data/markdown/use_cases/run_irb/run_irb/use_case_template.md +33 -0
- data/markdown/use_cases/use_cases.md +17 -14
- metadata +82 -72
- data/markdown/use_cases/include_files/diagnose_circular_includes/diagnose_circular_includes.err +0 -26
- 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
|