markdown-run 0.1.8 → 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.
@@ -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
@@ -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.8
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-01 00:00:00.000000000 Z
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: '0'
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: '0'
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: :runtime
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
- description: Run code blocks in Markdown files for Ruby, JavaScript, sqlite and psql.
42
- Insert execution results next to the original code blocks.
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.4.10
120
+ rubygems_version: 3.5.16
83
121
  signing_key:
84
122
  specification_version: 4
85
123
  summary: Run code blocks in Markdown files
@@ -1,297 +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
- # Load the main script to access process_markdown_file_main
14
- load File.expand_path("./exe/markdown-run", __dir__)
15
-
16
- # --- Minitest Test Class Definition ---
17
- class TestMarkdownExec < Minitest::Test
18
- def setup
19
- @temp_dir = Dir.mktmpdir("markdown_exec_tests")
20
- @test_md_file_path = File.join(@temp_dir, "test.md")
21
- end
22
-
23
- def teardown
24
- FileUtils.remove_entry @temp_dir if @temp_dir && Dir.exist?(@temp_dir)
25
- end
26
-
27
- def create_md_file(content)
28
- File.write(@test_md_file_path, content)
29
- @test_md_file_path
30
- end
31
-
32
- def read_md_file
33
- File.read(@test_md_file_path)
34
- end
35
-
36
- def test_script_runs_without_error_on_empty_file
37
- create_md_file("")
38
- assert process_markdown_file_main(@test_md_file_path), "Processing empty file should succeed"
39
- assert_equal "", read_md_file.strip, "Empty file should remain empty after processing"
40
- end
41
-
42
- def test_psql_block_execution
43
- skip "Skipping test_psql_block_execution on GitHub CI" if ENV['CI']
44
-
45
- md_content = <<~MARKDOWN
46
- ```psql
47
- SELECT 'hello psql test';
48
- ```
49
- MARKDOWN
50
- create_md_file(md_content)
51
- process_markdown_file_main(@test_md_file_path)
52
-
53
- expected_output = <<~MARKDOWN.strip
54
- ```psql
55
- SELECT 'hello psql test';
56
- ```
57
-
58
- ```RESULT
59
- hello psql test
60
- ```
61
- MARKDOWN
62
- assert_equal expected_output, read_md_file.strip
63
- end
64
-
65
- def test_ruby_block_execution_and_result_generation
66
- md_content = <<~MARKDOWN
67
- ```ruby
68
- puts "Hello from Ruby"
69
- p 1 + 2
70
- ```
71
- MARKDOWN
72
- create_md_file(md_content)
73
- process_markdown_file_main(@test_md_file_path)
74
-
75
- file_content = read_md_file
76
- assert file_content.include?("```ruby\nputs \"Hello from Ruby\""), "Original Ruby code should be present"
77
- assert file_content.include?("```ruby RESULT\n"), "Ruby RESULT block should be created"
78
- assert file_content.include?("3"), "Output from p 1 + 2 should be in the result"
79
- end
80
-
81
- def test_skip_execution_if_result_block_exists
82
- original_content = <<~MARKDOWN
83
- ```psql
84
- SELECT 'this should not run';
85
- ```
86
-
87
- ```RESULT
88
- pre-existing result
89
- ```
90
- MARKDOWN
91
- create_md_file(original_content)
92
- process_markdown_file_main(@test_md_file_path)
93
-
94
- assert_equal original_content.strip, read_md_file.strip, "Should not execute if RESULT block exists"
95
- end
96
-
97
- def test_skip_execution_if_ruby_result_block_exists
98
- original_content = <<~MARKDOWN
99
- ```ruby
100
- puts "this should not run either"
101
- ```
102
-
103
- ```ruby RESULT
104
- this is a pre-existing ruby result
105
- ```
106
- MARKDOWN
107
- create_md_file(original_content)
108
- process_markdown_file_main(@test_md_file_path)
109
-
110
- assert_equal original_content.strip, read_md_file.strip, "Should not execute if ```ruby RESULT block exists"
111
- end
112
-
113
- def test_frontmatter_alias_functionality
114
- skip "Skipping test_frontmatter_alias_functionality on GitHub CI" if ENV['CI']
115
-
116
- md_content = <<~MARKDOWN
117
- ---
118
- markdown-run:
119
- alias:
120
- - sql: psql
121
- ---
122
-
123
- # Test Document
124
-
125
- ```sql
126
- SELECT 'aliased to psql' as test;
127
- ```
128
- MARKDOWN
129
- create_md_file(md_content)
130
- process_markdown_file_main(@test_md_file_path)
131
-
132
- file_content = read_md_file
133
- assert file_content.include?("```sql\nSELECT 'aliased to psql' as test;"), "Original SQL code should be present"
134
- assert file_content.include?("```RESULT\n"), "RESULT block should be created for aliased language"
135
- assert file_content.include?("aliased to psql"), "Output should contain the expected result"
136
- end
137
-
138
- def test_rerun_functionality
139
- # Test 1: Default behavior (no rerun option) should skip existing result
140
- md_content_with_result = <<~MARKDOWN
141
- ```ruby
142
- puts "Should not change: \#{Time.now.to_i}"
143
- ```
144
-
145
- ```ruby RESULT
146
- Should not change: 999999999
147
- ```
148
- MARKDOWN
149
- create_md_file(md_content_with_result)
150
- process_markdown_file_main(@test_md_file_path)
151
-
152
- file_content = read_md_file
153
- assert file_content.include?("Should not change: 999999999"), "Default behavior should preserve existing result"
154
- refute file_content.match?(/Should not change: (?!999999999)\d+/), "Default behavior should not generate new timestamp"
155
-
156
- # Test 2: rerun=false should skip existing result
157
- md_content_rerun_false = <<~MARKDOWN
158
- ```ruby rerun=false
159
- puts "Should not change either: \#{Time.now.to_i}"
160
- ```
161
-
162
- ```ruby RESULT
163
- Should not change either: 888888888
164
- ```
165
- MARKDOWN
166
- create_md_file(md_content_rerun_false)
167
- process_markdown_file_main(@test_md_file_path)
168
-
169
- file_content = read_md_file
170
- assert file_content.include?("Should not change either: 888888888"), "rerun=false should preserve existing result"
171
- refute file_content.match?(/Should not change either: (?!888888888)\d+/), "rerun=false should not generate new timestamp"
172
-
173
- # Test 3: rerun=true should replace existing result
174
- md_content_rerun_true = <<~MARKDOWN
175
- ```ruby rerun=true
176
- puts "Should change: \#{Time.now.to_i}"
177
- ```
178
-
179
- ```ruby RESULT
180
- Should change: 777777777
181
- ```
182
- MARKDOWN
183
- create_md_file(md_content_rerun_true)
184
- process_markdown_file_main(@test_md_file_path)
185
-
186
- file_content = read_md_file
187
- refute file_content.include?("Should change: 777777777"), "rerun=true should replace existing result"
188
- assert file_content.match?(/Should change: \d+/), "rerun=true should generate new result with actual timestamp"
189
-
190
- # Test 4: rerun=true with blank line before result block
191
- md_content_rerun_true_blank = <<~MARKDOWN
192
- ```ruby rerun=true
193
- puts "Should also change: \#{Time.now.to_i}"
194
- ```
195
-
196
- ```ruby RESULT
197
- Should also change: 666666666
198
- ```
199
- MARKDOWN
200
- create_md_file(md_content_rerun_true_blank)
201
- process_markdown_file_main(@test_md_file_path)
202
-
203
- file_content = read_md_file
204
- refute file_content.include?("Should also change: 666666666"), "rerun=true with blank line should replace existing result"
205
- assert file_content.match?(/Should also change: \d+/), "rerun=true with blank line should generate new result"
206
- end
207
-
208
- def test_run_functionality
209
- # Test 1: Default behavior (run=true implicit) should execute new code block
210
- md_content_default = <<~MARKDOWN
211
- ```ruby
212
- puts "Should execute by default"
213
- ```
214
- MARKDOWN
215
- create_md_file(md_content_default)
216
- process_markdown_file_main(@test_md_file_path)
217
-
218
- file_content = read_md_file
219
- assert file_content.include?("```ruby RESULT"), "Default behavior should create result block"
220
- assert file_content.include?("Should execute by default"), "Default behavior should execute and show output"
221
-
222
- # Test 2: run=true explicit should execute new code block
223
- md_content_run_true = <<~MARKDOWN
224
- ```ruby run=true
225
- puts "Should execute with run=true"
226
- ```
227
- MARKDOWN
228
- create_md_file(md_content_run_true)
229
- process_markdown_file_main(@test_md_file_path)
230
-
231
- file_content = read_md_file
232
- assert file_content.include?("```ruby RESULT"), "run=true should create result block"
233
- assert file_content.include?("Should execute with run=true"), "run=true should execute and show output"
234
-
235
- # Test 3: run=false should not execute at all (no result block created)
236
- md_content_run_false = <<~MARKDOWN
237
- ```ruby run=false
238
- puts "Should not execute"
239
- puts "No result block should be created"
240
- ```
241
- MARKDOWN
242
- create_md_file(md_content_run_false)
243
- process_markdown_file_main(@test_md_file_path)
244
-
245
- file_content = read_md_file
246
- refute file_content.include?("```ruby RESULT"), "run=false should not create result block"
247
- refute file_content.match?(/puts "Should not execute"\n# >>/), "run=false should not execute code (no # >> output)"
248
- assert file_content.include?("puts \"Should not execute\""), "run=false should preserve original code block"
249
-
250
- # Test 4: run=false with existing result block should skip execution but preserve result
251
- md_content_run_false_with_result = <<~MARKDOWN
252
- ```ruby run=false
253
- puts "Should not execute"
254
- ```
255
-
256
- ```ruby RESULT
257
- Old result that should be preserved
258
- ```
259
- MARKDOWN
260
- create_md_file(md_content_run_false_with_result)
261
- process_markdown_file_main(@test_md_file_path)
262
-
263
- file_content = read_md_file
264
- assert file_content.include?("Old result that should be preserved"), "run=false should preserve existing result"
265
- refute file_content.match?(/puts "Should not execute"\n# >>/), "run=false should not create new execution output"
266
-
267
- # Test 5: Combined options - run=false with rerun=true should still not execute
268
- md_content_combined = <<~MARKDOWN
269
- ```ruby run=false rerun=true
270
- puts "Should not execute despite rerun=true"
271
- ```
272
-
273
- ```ruby RESULT
274
- Existing result
275
- ```
276
- MARKDOWN
277
- create_md_file(md_content_combined)
278
- process_markdown_file_main(@test_md_file_path)
279
-
280
- file_content = read_md_file
281
- assert file_content.include?("Existing result"), "run=false should override rerun=true"
282
- refute file_content.match?(/puts "Should not execute despite rerun=true"\n# >>/), "run=false should prevent execution even with rerun=true"
283
-
284
- # Test 6: Combined options - run=true with rerun=false should execute if no result exists
285
- md_content_run_true_rerun_false = <<~MARKDOWN
286
- ```ruby run=true rerun=false
287
- puts "Should execute because no result exists"
288
- ```
289
- MARKDOWN
290
- create_md_file(md_content_run_true_rerun_false)
291
- process_markdown_file_main(@test_md_file_path)
292
-
293
- file_content = read_md_file
294
- assert file_content.include?("```ruby RESULT"), "run=true rerun=false should execute when no result exists"
295
- assert file_content.include?("Should execute because no result exists"), "run=true rerun=false should show output when no result exists"
296
- end
297
- end