markdown-run 0.1.7 → 0.1.9
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/.tool-versions +1 -0
- data/CHANGELOG.md +8 -0
- data/README.md +34 -4
- data/Rakefile +22 -0
- data/exe/markdown-run +6 -409
- data/lib/code_block_parser.rb +60 -0
- data/lib/code_executor.rb +137 -0
- data/lib/enum_helper.rb +17 -0
- data/lib/execution_decider.rb +97 -0
- data/lib/frontmatter_parser.rb +72 -0
- data/lib/language_configs.rb +42 -7
- data/lib/markdown/run/version.rb +1 -1
- data/lib/markdown_file_writer.rb +25 -0
- data/lib/markdown_processor.rb +262 -0
- data/lib/markdown_run.rb +20 -0
- metadata +49 -11
- data/test_markdown_exec.rb +0 -204
@@ -0,0 +1,262 @@
|
|
1
|
+
require_relative "language_configs"
|
2
|
+
require_relative "frontmatter_parser"
|
3
|
+
require_relative "code_block_parser"
|
4
|
+
require_relative "code_executor"
|
5
|
+
require_relative "execution_decider"
|
6
|
+
require_relative "enum_helper"
|
7
|
+
|
8
|
+
class MarkdownProcessor
|
9
|
+
include EnumHelper
|
10
|
+
def initialize(temp_dir, input_file_path = nil)
|
11
|
+
@temp_dir = temp_dir
|
12
|
+
@input_file_path = input_file_path
|
13
|
+
@output_lines = []
|
14
|
+
@state = :outside_code_block
|
15
|
+
@current_block_lang = ""
|
16
|
+
@current_code_content = ""
|
17
|
+
@current_block_rerun = false
|
18
|
+
@current_block_run = true
|
19
|
+
@frontmatter_parser = FrontmatterParser.new
|
20
|
+
@code_block_parser = CodeBlockParser.new(@frontmatter_parser)
|
21
|
+
end
|
22
|
+
|
23
|
+
def process_file(file_enum)
|
24
|
+
@frontmatter_parser.parse_frontmatter(file_enum, @output_lines)
|
25
|
+
|
26
|
+
loop do
|
27
|
+
current_line = get_next_line(file_enum)
|
28
|
+
break unless current_line
|
29
|
+
|
30
|
+
handle_line(current_line, file_enum)
|
31
|
+
end
|
32
|
+
@output_lines
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def resolve_language(lang)
|
38
|
+
@frontmatter_parser.resolve_language(lang)
|
39
|
+
end
|
40
|
+
|
41
|
+
def ruby_style_result?(lang)
|
42
|
+
lang_config = SUPPORTED_LANGUAGES[lang]
|
43
|
+
lang_config && lang_config[:result_block_type] == "ruby"
|
44
|
+
end
|
45
|
+
|
46
|
+
def mermaid_style_result?(lang)
|
47
|
+
lang_config = SUPPORTED_LANGUAGES[lang]
|
48
|
+
lang_config && lang_config[:result_handling] == :mermaid_svg
|
49
|
+
end
|
50
|
+
|
51
|
+
def result_block_header(lang)
|
52
|
+
ruby_style_result?(lang) ? "```ruby RESULT\n" : "```RESULT\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
def result_block_regex(lang)
|
56
|
+
if mermaid_style_result?(lang)
|
57
|
+
# For mermaid, look for existing image tags with .svg extension
|
58
|
+
/^!\[.*\]\(.*\.svg\)$/i
|
59
|
+
elsif ruby_style_result?(lang)
|
60
|
+
/^```ruby\s+RESULT$/i
|
61
|
+
else
|
62
|
+
/^```RESULT$/i
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def is_block_end?(line)
|
67
|
+
@code_block_parser.is_block_end?(line)
|
68
|
+
end
|
69
|
+
|
70
|
+
def has_content?(content)
|
71
|
+
!content.strip.empty?
|
72
|
+
end
|
73
|
+
|
74
|
+
def add_result_block(result_output, blank_line_before_new_result)
|
75
|
+
if mermaid_style_result?(@current_block_lang)
|
76
|
+
# For mermaid, add the image tag directly without a result block
|
77
|
+
@output_lines << "\n" if blank_line_before_new_result.nil?
|
78
|
+
@output_lines << result_output
|
79
|
+
@output_lines << "\n" unless result_output.empty? || result_output.end_with?("\n")
|
80
|
+
@output_lines << "\n"
|
81
|
+
else
|
82
|
+
@output_lines << "\n" if blank_line_before_new_result.nil?
|
83
|
+
@output_lines << result_block_header(@current_block_lang)
|
84
|
+
@output_lines << result_output
|
85
|
+
@output_lines << "\n" unless result_output.empty? || result_output.end_with?("\n")
|
86
|
+
@output_lines << "```\n\n"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def line_matches_pattern?(line, pattern)
|
91
|
+
line && line.match?(pattern)
|
92
|
+
end
|
93
|
+
|
94
|
+
def is_blank_line?(line)
|
95
|
+
line && line.strip == ""
|
96
|
+
end
|
97
|
+
|
98
|
+
def parse_rerun_option(options_string)
|
99
|
+
@code_block_parser.parse_rerun_option(options_string)
|
100
|
+
end
|
101
|
+
|
102
|
+
def parse_run_option(options_string)
|
103
|
+
@code_block_parser.parse_run_option(options_string)
|
104
|
+
end
|
105
|
+
|
106
|
+
def handle_line(current_line, file_enum)
|
107
|
+
case @state
|
108
|
+
when :outside_code_block
|
109
|
+
handle_outside_code_block(current_line, file_enum)
|
110
|
+
when :inside_code_block
|
111
|
+
handle_inside_code_block(current_line, file_enum)
|
112
|
+
when :inside_result_block
|
113
|
+
handle_inside_result_block(current_line, file_enum)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def handle_outside_code_block(current_line, file_enum)
|
118
|
+
if @code_block_parser.is_ruby_result_block?(current_line)
|
119
|
+
handle_existing_ruby_result_block(current_line, file_enum)
|
120
|
+
else
|
121
|
+
parsed_header = @code_block_parser.parse_code_block_header(current_line)
|
122
|
+
if parsed_header && parsed_header[:is_supported]
|
123
|
+
start_code_block(current_line, parsed_header[:original_lang], parsed_header[:options_string])
|
124
|
+
else
|
125
|
+
@output_lines << current_line
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def handle_inside_code_block(current_line, file_enum)
|
131
|
+
if is_block_end?(current_line)
|
132
|
+
end_code_block(current_line, file_enum)
|
133
|
+
else
|
134
|
+
accumulate_code_content(current_line)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def handle_inside_result_block(current_line, file_enum)
|
139
|
+
@output_lines << current_line
|
140
|
+
if is_block_end?(current_line)
|
141
|
+
@state = :outside_code_block
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def handle_existing_ruby_result_block(current_line, file_enum)
|
146
|
+
warn "Found existing '```ruby RESULT' block, passing through."
|
147
|
+
@output_lines << current_line
|
148
|
+
@state = :inside_result_block
|
149
|
+
end
|
150
|
+
|
151
|
+
def start_code_block(current_line, lang, options_string = nil)
|
152
|
+
@output_lines << current_line
|
153
|
+
@current_block_lang = resolve_language(lang)
|
154
|
+
@current_block_rerun = parse_rerun_option(options_string)
|
155
|
+
@current_block_run = parse_run_option(options_string)
|
156
|
+
@state = :inside_code_block
|
157
|
+
@current_code_content = ""
|
158
|
+
end
|
159
|
+
|
160
|
+
def accumulate_code_content(current_line)
|
161
|
+
@current_code_content += current_line
|
162
|
+
@output_lines << current_line
|
163
|
+
end
|
164
|
+
|
165
|
+
def end_code_block(current_line, file_enum)
|
166
|
+
@output_lines << current_line
|
167
|
+
|
168
|
+
decision = decide_execution(file_enum)
|
169
|
+
|
170
|
+
if decision[:execute]
|
171
|
+
# If we consumed lines for rerun, don't add them to output (they'll be replaced)
|
172
|
+
execute_and_add_result(decision[:blank_line])
|
173
|
+
else
|
174
|
+
skip_and_pass_through_result(decision[:lines_to_pass_through], file_enum)
|
175
|
+
end
|
176
|
+
|
177
|
+
reset_code_block_state
|
178
|
+
end
|
179
|
+
|
180
|
+
def decide_execution(file_enum)
|
181
|
+
decider = ExecutionDecider.new(@current_block_run, @current_block_rerun, @current_block_lang)
|
182
|
+
decision = decider.decide(file_enum, method(:result_block_regex))
|
183
|
+
|
184
|
+
# Handle the consume_existing flag for rerun scenarios
|
185
|
+
if decision[:consume_existing]
|
186
|
+
consume_existing_result_block(file_enum, decision[:consumed_lines])
|
187
|
+
end
|
188
|
+
|
189
|
+
decision
|
190
|
+
end
|
191
|
+
|
192
|
+
def execute_and_add_result(blank_line_before_new_result)
|
193
|
+
@output_lines << blank_line_before_new_result if blank_line_before_new_result
|
194
|
+
|
195
|
+
if has_content?(@current_code_content)
|
196
|
+
result_output = CodeExecutor.execute(@current_code_content, @current_block_lang, @temp_dir, @input_file_path)
|
197
|
+
add_result_block(result_output, blank_line_before_new_result)
|
198
|
+
else
|
199
|
+
warn "Skipping empty code block for language '#{@current_block_lang}'."
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def skip_and_pass_through_result(lines_to_pass_through, file_enum)
|
204
|
+
# Handle run=false case where there are no lines to pass through
|
205
|
+
if lines_to_pass_through.empty?
|
206
|
+
warn "Skipping execution due to run=false option."
|
207
|
+
return
|
208
|
+
end
|
209
|
+
|
210
|
+
if mermaid_style_result?(@current_block_lang)
|
211
|
+
warn "Found existing mermaid SVG image for current #{@current_block_lang} block, skipping execution."
|
212
|
+
@output_lines.concat(lines_to_pass_through)
|
213
|
+
# For mermaid, no additional consumption needed since it's just an image line
|
214
|
+
else
|
215
|
+
lang_specific_result_type = ruby_style_result?(@current_block_lang) ? "```ruby RESULT" : "```RESULT"
|
216
|
+
warn "Found existing '#{lang_specific_result_type}' block for current #{@current_block_lang} block, skipping execution."
|
217
|
+
@output_lines.concat(lines_to_pass_through)
|
218
|
+
consume_result_block_content(file_enum)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def consume_result_block_content(file_enum)
|
223
|
+
consume_block_lines(file_enum) do |line|
|
224
|
+
@output_lines << line
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def consume_existing_result_block(file_enum, consumed_lines)
|
229
|
+
if mermaid_style_result?(@current_block_lang)
|
230
|
+
# For mermaid, there's no result block to consume, just the image line
|
231
|
+
# The image line should already be in consumed_lines from ExecutionDecider
|
232
|
+
return
|
233
|
+
end
|
234
|
+
|
235
|
+
consume_block_lines(file_enum) do |line|
|
236
|
+
consumed_lines << line
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def consume_block_lines(file_enum)
|
241
|
+
begin
|
242
|
+
loop do
|
243
|
+
result_block_line = file_enum.next
|
244
|
+
yield result_block_line
|
245
|
+
break if is_block_end?(result_block_line)
|
246
|
+
end
|
247
|
+
rescue StopIteration
|
248
|
+
warn "Warning: End of file reached while consuming result block."
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def reset_code_block_state
|
253
|
+
@state = :outside_code_block
|
254
|
+
@current_code_content = ""
|
255
|
+
@current_block_rerun = false
|
256
|
+
@current_block_run = true
|
257
|
+
end
|
258
|
+
|
259
|
+
def stderr_has_content?(stderr_output)
|
260
|
+
stderr_output && !stderr_output.strip.empty?
|
261
|
+
end
|
262
|
+
end
|
data/lib/markdown_run.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative "language_configs"
|
2
|
+
require_relative "markdown_processor"
|
3
|
+
require_relative "markdown_file_writer"
|
4
|
+
|
5
|
+
module MarkdownRun
|
6
|
+
def self.run_code_blocks(input_file_path)
|
7
|
+
unless File.exist?(input_file_path) && File.readable?(input_file_path)
|
8
|
+
abort "Error: Input file '#{input_file_path}' not found or not readable."
|
9
|
+
end
|
10
|
+
|
11
|
+
temp_dir = File.dirname(File.expand_path(input_file_path))
|
12
|
+
file_enum = File.foreach(input_file_path, chomp: false).to_enum
|
13
|
+
|
14
|
+
processor = MarkdownProcessor.new(temp_dir, input_file_path)
|
15
|
+
output_lines = processor.process_file(file_enum)
|
16
|
+
|
17
|
+
# Write the modified content back to the input file
|
18
|
+
MarkdownFileWriter.write_output_to_file(output_lines, input_file_path)
|
19
|
+
end
|
20
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: markdown-run
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aurélien Bottazini
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-06-
|
11
|
+
date: 2025-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rcodetools
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.8.5
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.8.5
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -31,15 +31,44 @@ dependencies:
|
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 5.25.5
|
34
|
-
type: :
|
34
|
+
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 5.25.5
|
41
|
-
|
42
|
-
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: flog
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Run code blocks in Markdown files for Ruby, JavaScript, sqlite, psql,
|
70
|
+
bash, zsh, and mermaid. Insert execution results next to the original code blocks.
|
71
|
+
Generate SVG diagrams from mermaid blocks.
|
43
72
|
email:
|
44
73
|
- 32635+aurelienbottazini@users.noreply.github.com
|
45
74
|
executables:
|
@@ -48,15 +77,24 @@ extensions: []
|
|
48
77
|
extra_rdoc_files: []
|
49
78
|
files:
|
50
79
|
- ".rubocop.yml"
|
80
|
+
- ".tool-versions"
|
51
81
|
- CHANGELOG.md
|
52
82
|
- CODE_OF_CONDUCT.md
|
53
83
|
- LICENSE.txt
|
54
84
|
- README.md
|
85
|
+
- Rakefile
|
55
86
|
- exe/markdown-run
|
87
|
+
- lib/code_block_parser.rb
|
88
|
+
- lib/code_executor.rb
|
89
|
+
- lib/enum_helper.rb
|
90
|
+
- lib/execution_decider.rb
|
91
|
+
- lib/frontmatter_parser.rb
|
56
92
|
- lib/language_configs.rb
|
57
93
|
- lib/markdown/run/version.rb
|
94
|
+
- lib/markdown_file_writer.rb
|
95
|
+
- lib/markdown_processor.rb
|
96
|
+
- lib/markdown_run.rb
|
58
97
|
- markdown-run-sample.md
|
59
|
-
- test_markdown_exec.rb
|
60
98
|
homepage: https://github.com/aurelienbottazini/markdown-run
|
61
99
|
licenses:
|
62
100
|
- MIT
|
@@ -79,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
79
117
|
- !ruby/object:Gem::Version
|
80
118
|
version: '0'
|
81
119
|
requirements: []
|
82
|
-
rubygems_version: 3.
|
120
|
+
rubygems_version: 3.5.16
|
83
121
|
signing_key:
|
84
122
|
specification_version: 4
|
85
123
|
summary: Run code blocks in Markdown files
|
data/test_markdown_exec.rb
DELETED
@@ -1,204 +0,0 @@
|
|
1
|
-
require "bundler/inline"
|
2
|
-
gemfile(true) do
|
3
|
-
source "https://rubygems.org"
|
4
|
-
gem "minitest", "5.25.5" # Specify the required version
|
5
|
-
gem "rcodetools"
|
6
|
-
end
|
7
|
-
|
8
|
-
require "minitest/test"
|
9
|
-
require "minitest/autorun"
|
10
|
-
require "fileutils"
|
11
|
-
require "tmpdir"
|
12
|
-
|
13
|
-
# --- Minitest Test Class Definition ---
|
14
|
-
class TestMarkdownExec < Minitest::Test
|
15
|
-
def setup
|
16
|
-
@temp_dir = Dir.mktmpdir("markdown_exec_tests")
|
17
|
-
@test_md_file_path = File.join(@temp_dir, "test.md")
|
18
|
-
end
|
19
|
-
|
20
|
-
def teardown
|
21
|
-
FileUtils.remove_entry @temp_dir if @temp_dir && Dir.exist?(@temp_dir)
|
22
|
-
end
|
23
|
-
|
24
|
-
def create_md_file(content)
|
25
|
-
File.write(@test_md_file_path, content)
|
26
|
-
@test_md_file_path
|
27
|
-
end
|
28
|
-
|
29
|
-
def read_md_file
|
30
|
-
File.read(@test_md_file_path)
|
31
|
-
end
|
32
|
-
|
33
|
-
def test_script_runs_without_error_on_empty_file
|
34
|
-
create_md_file("")
|
35
|
-
assert process_markdown_file_main(@test_md_file_path), "Processing empty file should succeed"
|
36
|
-
assert_equal "", read_md_file.strip, "Empty file should remain empty after processing"
|
37
|
-
end
|
38
|
-
|
39
|
-
def test_psql_block_execution
|
40
|
-
skip "Skipping test_psql_block_execution on GitHub CI" if ENV['CI']
|
41
|
-
|
42
|
-
md_content = <<~MARKDOWN
|
43
|
-
```psql
|
44
|
-
SELECT 'hello psql test';
|
45
|
-
```
|
46
|
-
MARKDOWN
|
47
|
-
create_md_file(md_content)
|
48
|
-
process_markdown_file_main(@test_md_file_path)
|
49
|
-
|
50
|
-
expected_output = <<~MARKDOWN.strip
|
51
|
-
```psql
|
52
|
-
SELECT 'hello psql test';
|
53
|
-
```
|
54
|
-
|
55
|
-
```RESULT
|
56
|
-
hello psql test
|
57
|
-
```
|
58
|
-
MARKDOWN
|
59
|
-
assert_equal expected_output, read_md_file.strip
|
60
|
-
end
|
61
|
-
|
62
|
-
def test_ruby_block_execution_and_result_generation
|
63
|
-
md_content = <<~MARKDOWN
|
64
|
-
```ruby
|
65
|
-
puts "Hello from Ruby"
|
66
|
-
p 1 + 2
|
67
|
-
```
|
68
|
-
MARKDOWN
|
69
|
-
create_md_file(md_content)
|
70
|
-
process_markdown_file_main(@test_md_file_path)
|
71
|
-
|
72
|
-
file_content = read_md_file
|
73
|
-
assert file_content.include?("```ruby\nputs \"Hello from Ruby\""), "Original Ruby code should be present"
|
74
|
-
assert file_content.include?("```ruby RESULT\n"), "Ruby RESULT block should be created"
|
75
|
-
assert file_content.include?("3"), "Output from p 1 + 2 should be in the result"
|
76
|
-
end
|
77
|
-
|
78
|
-
def test_skip_execution_if_result_block_exists
|
79
|
-
original_content = <<~MARKDOWN
|
80
|
-
```psql
|
81
|
-
SELECT 'this should not run';
|
82
|
-
```
|
83
|
-
|
84
|
-
```RESULT
|
85
|
-
pre-existing result
|
86
|
-
```
|
87
|
-
MARKDOWN
|
88
|
-
create_md_file(original_content)
|
89
|
-
process_markdown_file_main(@test_md_file_path)
|
90
|
-
|
91
|
-
assert_equal original_content.strip, read_md_file.strip, "Should not execute if RESULT block exists"
|
92
|
-
end
|
93
|
-
|
94
|
-
def test_skip_execution_if_ruby_result_block_exists
|
95
|
-
original_content = <<~MARKDOWN
|
96
|
-
```ruby
|
97
|
-
puts "this should not run either"
|
98
|
-
```
|
99
|
-
|
100
|
-
```ruby RESULT
|
101
|
-
this is a pre-existing ruby result
|
102
|
-
```
|
103
|
-
MARKDOWN
|
104
|
-
create_md_file(original_content)
|
105
|
-
process_markdown_file_main(@test_md_file_path)
|
106
|
-
|
107
|
-
assert_equal original_content.strip, read_md_file.strip, "Should not execute if ```ruby RESULT block exists"
|
108
|
-
end
|
109
|
-
|
110
|
-
def test_frontmatter_alias_functionality
|
111
|
-
skip "Skipping test_frontmatter_alias_functionality on GitHub CI" if ENV['CI']
|
112
|
-
|
113
|
-
md_content = <<~MARKDOWN
|
114
|
-
---
|
115
|
-
markdown-run:
|
116
|
-
alias:
|
117
|
-
- sql: psql
|
118
|
-
---
|
119
|
-
|
120
|
-
# Test Document
|
121
|
-
|
122
|
-
```sql
|
123
|
-
SELECT 'aliased to psql' as test;
|
124
|
-
```
|
125
|
-
MARKDOWN
|
126
|
-
create_md_file(md_content)
|
127
|
-
process_markdown_file_main(@test_md_file_path)
|
128
|
-
|
129
|
-
file_content = read_md_file
|
130
|
-
assert file_content.include?("```sql\nSELECT 'aliased to psql' as test;"), "Original SQL code should be present"
|
131
|
-
assert file_content.include?("```RESULT\n"), "RESULT block should be created for aliased language"
|
132
|
-
assert file_content.include?("aliased to psql"), "Output should contain the expected result"
|
133
|
-
end
|
134
|
-
|
135
|
-
def test_rerun_functionality
|
136
|
-
# Test 1: Default behavior (no rerun option) should skip existing result
|
137
|
-
md_content_with_result = <<~MARKDOWN
|
138
|
-
```ruby
|
139
|
-
puts "Should not change: \#{Time.now.to_i}"
|
140
|
-
```
|
141
|
-
|
142
|
-
```ruby RESULT
|
143
|
-
Should not change: 999999999
|
144
|
-
```
|
145
|
-
MARKDOWN
|
146
|
-
create_md_file(md_content_with_result)
|
147
|
-
process_markdown_file_main(@test_md_file_path)
|
148
|
-
|
149
|
-
file_content = read_md_file
|
150
|
-
assert file_content.include?("Should not change: 999999999"), "Default behavior should preserve existing result"
|
151
|
-
refute file_content.match?(/Should not change: (?!999999999)\d+/), "Default behavior should not generate new timestamp"
|
152
|
-
|
153
|
-
# Test 2: rerun=false should skip existing result
|
154
|
-
md_content_rerun_false = <<~MARKDOWN
|
155
|
-
```ruby rerun=false
|
156
|
-
puts "Should not change either: \#{Time.now.to_i}"
|
157
|
-
```
|
158
|
-
|
159
|
-
```ruby RESULT
|
160
|
-
Should not change either: 888888888
|
161
|
-
```
|
162
|
-
MARKDOWN
|
163
|
-
create_md_file(md_content_rerun_false)
|
164
|
-
process_markdown_file_main(@test_md_file_path)
|
165
|
-
|
166
|
-
file_content = read_md_file
|
167
|
-
assert file_content.include?("Should not change either: 888888888"), "rerun=false should preserve existing result"
|
168
|
-
refute file_content.match?(/Should not change either: (?!888888888)\d+/), "rerun=false should not generate new timestamp"
|
169
|
-
|
170
|
-
# Test 3: rerun=true should replace existing result
|
171
|
-
md_content_rerun_true = <<~MARKDOWN
|
172
|
-
```ruby rerun=true
|
173
|
-
puts "Should change: \#{Time.now.to_i}"
|
174
|
-
```
|
175
|
-
|
176
|
-
```ruby RESULT
|
177
|
-
Should change: 777777777
|
178
|
-
```
|
179
|
-
MARKDOWN
|
180
|
-
create_md_file(md_content_rerun_true)
|
181
|
-
process_markdown_file_main(@test_md_file_path)
|
182
|
-
|
183
|
-
file_content = read_md_file
|
184
|
-
refute file_content.include?("Should change: 777777777"), "rerun=true should replace existing result"
|
185
|
-
assert file_content.match?(/Should change: \d+/), "rerun=true should generate new result with actual timestamp"
|
186
|
-
|
187
|
-
# Test 4: rerun=true with blank line before result block
|
188
|
-
md_content_rerun_true_blank = <<~MARKDOWN
|
189
|
-
```ruby rerun=true
|
190
|
-
puts "Should also change: \#{Time.now.to_i}"
|
191
|
-
```
|
192
|
-
|
193
|
-
```ruby RESULT
|
194
|
-
Should also change: 666666666
|
195
|
-
```
|
196
|
-
MARKDOWN
|
197
|
-
create_md_file(md_content_rerun_true_blank)
|
198
|
-
process_markdown_file_main(@test_md_file_path)
|
199
|
-
|
200
|
-
file_content = read_md_file
|
201
|
-
refute file_content.include?("Should also change: 666666666"), "rerun=true with blank line should replace existing result"
|
202
|
-
assert file_content.match?(/Should also change: \d+/), "rerun=true with blank line should generate new result"
|
203
|
-
end
|
204
|
-
end
|