markdown_exec 3.5.1 → 3.5.2

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.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.ai-agent-instructions +54 -0
  3. data/.cursorrules +198 -0
  4. data/.rubocop.wide.yml +5 -0
  5. data/.rubocop.yml +7 -2
  6. data/CHANGELOG.md +12 -1
  7. data/Gemfile.lock +1 -1
  8. data/Rakefile +2 -0
  9. data/ai-principles.md +516 -0
  10. data/architecture-decisions.md +190 -0
  11. data/bats/block-hide.bats +1 -1
  12. data/bats/block-type-bash.bats +5 -5
  13. data/bats/block-type-link.bats +1 -1
  14. data/bats/block-type-opts.bats +3 -3
  15. data/bats/block-type-port.bats +2 -2
  16. data/bats/block-type-shell-require-ux.bats +2 -2
  17. data/bats/block-type-ux-allowed.bats +4 -4
  18. data/bats/block-type-ux-auto.bats +1 -1
  19. data/bats/block-type-ux-chained.bats +1 -1
  20. data/bats/block-type-ux-default.bats +1 -1
  21. data/bats/block-type-ux-echo-hash-transform.bats +1 -1
  22. data/bats/block-type-ux-echo-hash.bats +2 -2
  23. data/bats/block-type-ux-echo.bats +3 -3
  24. data/bats/block-type-ux-exec-hash-transform.bats +1 -1
  25. data/bats/block-type-ux-exec-hash.bats +2 -2
  26. data/bats/block-type-ux-exec.bats +1 -1
  27. data/bats/block-type-ux-force.bats +1 -1
  28. data/bats/block-type-ux-formats.bats +1 -1
  29. data/bats/block-type-ux-hidden.bats +1 -1
  30. data/bats/block-type-ux-invalid.bats +1 -1
  31. data/bats/block-type-ux-readonly.bats +1 -1
  32. data/bats/block-type-ux-require-chained.bats +2 -2
  33. data/bats/block-type-ux-require-context.bats +2 -2
  34. data/bats/block-type-ux-require.bats +2 -2
  35. data/bats/block-type-ux-required-variables.bats +1 -1
  36. data/bats/block-type-ux-row-format.bats +1 -1
  37. data/bats/block-type-ux-sources.bats +4 -4
  38. data/bats/block-type-ux-transform.bats +1 -1
  39. data/bats/block-type-vars.bats +3 -3
  40. data/bats/border.bats +1 -1
  41. data/bats/cli.bats +11 -11
  42. data/bats/command-substitution-options.bats +2 -2
  43. data/bats/command-substitution.bats +1 -1
  44. data/bats/document-shell.bats +1 -1
  45. data/bats/history.bats +5 -5
  46. data/bats/import-conflict.bats +1 -1
  47. data/bats/import-directive-line-continuation.bats +1 -1
  48. data/bats/import-directive-parameter-symbols.bats +1 -1
  49. data/bats/import-duplicates.bats +6 -6
  50. data/bats/import-parameter-symbols.bats +1 -1
  51. data/bats/import-with-text-substitution.bats +1 -1
  52. data/bats/import.bats +3 -3
  53. data/bats/indented-block-type-vars.bats +1 -1
  54. data/bats/indented-multi-line-output.bats +1 -1
  55. data/bats/line-decor-dynamic.bats +1 -1
  56. data/bats/line-wrapping.bats +1 -1
  57. data/bats/load-vars-state-demo.bats +4 -4
  58. data/bats/markup.bats +4 -4
  59. data/bats/mde.bats +4 -4
  60. data/bats/option-expansion.bats +1 -1
  61. data/bats/options-collapse.bats +4 -4
  62. data/bats/options.bats +47 -17
  63. data/bats/plain.bats +1 -1
  64. data/bats/publish.bats +2 -2
  65. data/bats/table-column-truncate.bats +1 -1
  66. data/bats/table.bats +2 -2
  67. data/bats/variable-expansion-multiline.bats +1 -1
  68. data/bats/variable-expansion.bats +6 -6
  69. data/conversation-template.md +611 -0
  70. data/docs/block-execution-modes.md +177 -0
  71. data/docs/block-filtering.md +252 -0
  72. data/docs/block-naming-patterns.md +210 -0
  73. data/docs/block-scanning-patterns.md +248 -0
  74. data/docs/cli-reference.md +370 -0
  75. data/docs/dev/block-hide.md +1 -1
  76. data/docs/dev/block-type-ux-transform.md +5 -4
  77. data/docs/dev/print_bytes.md +3 -0
  78. data/docs/dev/shebang.md +6 -0
  79. data/docs/docker-testing.md +5 -0
  80. data/docs/execution-control.md +384 -0
  81. data/docs/getting-started.md +209 -0
  82. data/docs/import-options.md +391 -0
  83. data/docs/tab-completion.md +7 -0
  84. data/docs/ux-blocks.md +376 -0
  85. data/examples/linked1.md +8 -1
  86. data/implementation-decisions.md +212 -0
  87. data/lib/cached_nested_file_reader.rb +138 -1
  88. data/lib/command_result.rb +27 -6
  89. data/lib/executed_shell_command.rb +512 -0
  90. data/lib/filter.rb +7 -7
  91. data/lib/hash_delegator.rb +403 -350
  92. data/lib/link_history.rb +22 -11
  93. data/lib/markdown_exec/version.rb +1 -1
  94. data/lib/mdoc.rb +103 -44
  95. data/lib/menu.src.yml +110 -83
  96. data/lib/menu.yml +149 -83
  97. data/lib/transformed_shell_command.rb +449 -0
  98. data/lib/wl.rb +15 -0
  99. data/lib/ww.rb +16 -5
  100. data/requirements.md +111 -0
  101. data/semantic-tokens.md +132 -0
  102. data/tasks.md +69 -0
  103. metadata +26 -4
  104. data/docs/ux-blocks-examples.md +0 -120
  105. data/docs/ux-blocks-init-act.md +0 -100
@@ -0,0 +1,512 @@
1
+ #!/usr/bin/env -S bundle exec ruby
2
+ # frozen_string_literal: true
3
+
4
+ # encoding=utf-8
5
+
6
+ require 'open3'
7
+
8
+ ##
9
+ # ExecutedShellCommand wraps the execution of a shell command and captures:
10
+ #
11
+ # * The original command string
12
+ # * STDOUT and STDERR
13
+ # * Exit status (Process::Status and numeric exit code)
14
+ # * Success / failure convenience predicates
15
+ # * Start and finish timestamps and derived duration
16
+ # * PID of the spawned process
17
+ # * Optional environment and working directory
18
+ #
19
+ # The command is executed automatically during initialization. The result
20
+ # is memoized and can be accessed immediately. Subsequent calls to `run`
21
+ # will re-execute by default (force: true), unless `force: false` is
22
+ # explicitly passed to use the memoized result.
23
+ #
24
+ # Basic usage:
25
+ #
26
+ # cmd = ExecutedShellCommand.new("ls -l /tmp") # executes immediately
27
+ # result = cmd.result # access memoized result
28
+ # fresh = cmd.run # re-executes (force: true by default)
29
+ # cached = cmd.run(force: false) # returns memoized result
30
+ #
31
+ class ExecutedShellCommand
32
+ ##
33
+ # Immutable value object representing the outcome of a command execution.
34
+ #
35
+ Result = Struct.new(
36
+ :command,
37
+ :stdout,
38
+ :stderr,
39
+ :status,
40
+ :started_at,
41
+ :finished_at,
42
+ :pid,
43
+ :env,
44
+ :chdir,
45
+ keyword_init: true
46
+ ) do
47
+ def success?
48
+ status&.success?
49
+ end
50
+
51
+ def exit_code
52
+ status&.exitstatus
53
+ end
54
+
55
+ def duration
56
+ return nil unless started_at && finished_at
57
+
58
+ finished_at - started_at
59
+ end
60
+
61
+ def signaled?
62
+ status&.signaled?
63
+ end
64
+
65
+ def termsig
66
+ status&.termsig
67
+ end
68
+
69
+ def to_h
70
+ {
71
+ command: command,
72
+ stdout: stdout,
73
+ stderr: stderr,
74
+ exit_code: exit_code,
75
+ success: success?,
76
+ started_at: started_at,
77
+ finished_at: finished_at,
78
+ duration: duration,
79
+ pid: pid,
80
+ env: env,
81
+ chdir: chdir,
82
+ status: status
83
+ }
84
+ end
85
+ end
86
+
87
+ attr_reader :command, :env, :chdir, :result
88
+
89
+ def initialize(command, chdir: nil, env: {})
90
+ @command = command
91
+ @chdir = chdir
92
+ @env = env
93
+ @result = nil
94
+ run # Execute command immediately during initialization
95
+ end
96
+
97
+ ##
98
+ # Execute the command, capture all outputs, and return a Result.
99
+ #
100
+ # By default (force: true), the command is executed again and the
101
+ # memoized Result is replaced.
102
+ #
103
+ # If `force: false` is passed and a result already exists, the
104
+ # memoized Result is returned without re-executing.
105
+ #
106
+ def run(force: true)
107
+ return @result if @result && !force
108
+
109
+ raise ArgumentError, 'command cannot be nil' if command.nil?
110
+
111
+ started_at = Time.now
112
+ stdout_str = +''
113
+ stderr_str = +''
114
+ status = nil
115
+ pid = nil
116
+
117
+ popen_args = env.empty? ? [command] : [env, command]
118
+ popen_opts = chdir ? { chdir: chdir } : {}
119
+
120
+ Open3.popen3(*popen_args, popen_opts) do |stdin, stdout, stderr, wait_thr|
121
+ stdin.close
122
+ stdout_str = stdout.read
123
+ stderr_str = stderr.read
124
+ pid = wait_thr.pid
125
+ status = wait_thr.value
126
+ end
127
+
128
+ finished_at = Time.now
129
+
130
+ @result = Result.new(
131
+ command: command,
132
+ stdout: stdout_str,
133
+ stderr: stderr_str,
134
+ status: status,
135
+ started_at: started_at,
136
+ finished_at: finished_at,
137
+ pid: pid,
138
+ env: env,
139
+ chdir: chdir
140
+ )
141
+ end
142
+
143
+ ##
144
+ # Return the memoized result. Since the command runs at initialization,
145
+ # this will always return the memoized result unless run(force: true)
146
+ # was called to update it.
147
+ #
148
+ def fetch_result
149
+ @result
150
+ end
151
+
152
+ # Convenience delegators to the last / memoized result:
153
+
154
+ def stdout
155
+ fetch_result.stdout
156
+ end
157
+
158
+ def stderr
159
+ fetch_result.stderr
160
+ end
161
+
162
+ def exit_code
163
+ fetch_result.exit_code
164
+ end
165
+
166
+ def success?
167
+ fetch_result.success?
168
+ end
169
+
170
+ def failure?
171
+ !fetch_result.success?
172
+ end
173
+
174
+ def duration
175
+ fetch_result.duration
176
+ end
177
+
178
+ def started_at
179
+ fetch_result.started_at
180
+ end
181
+
182
+ def finished_at
183
+ fetch_result.finished_at
184
+ end
185
+
186
+ def pid
187
+ fetch_result.pid
188
+ end
189
+ end
190
+
191
+ # Test suite when running as a script
192
+ return if $PROGRAM_NAME != __FILE__
193
+
194
+ require 'bundler/setup'
195
+ Bundler.require(:default)
196
+
197
+ require 'minitest/autorun'
198
+ require 'tmpdir'
199
+ require 'fileutils'
200
+
201
+ # [TEST:SHELL_COMMAND] Comprehensive test suite for ExecutedShellCommand class
202
+ class ExecutedShellCommandTest < Minitest::Test
203
+ # [TEST:SHELL_COMMAND] Test successful command execution (runs at initialization)
204
+ def test_successful_command_execution
205
+ cmd = ExecutedShellCommand.new("echo 'success'")
206
+ result = cmd.result # Command already executed at initialization
207
+
208
+ assert result.success?, 'Command should succeed'
209
+ assert_equal 0, result.exit_code
210
+ assert_equal "success\n", result.stdout
211
+ assert_equal '', result.stderr
212
+ assert_kind_of Process::Status, result.status
213
+ assert result.status.success?
214
+ end
215
+
216
+ # [TEST:SHELL_COMMAND] Test failed command execution with exit code (runs at initialization)
217
+ def test_failed_command_execution
218
+ cmd = ExecutedShellCommand.new('exit 3')
219
+ result = cmd.result # Command already executed at initialization
220
+
221
+ refute result.success?, 'Command should fail'
222
+ assert_equal 3, result.exit_code
223
+ assert_equal '', result.stdout
224
+ assert_equal '', result.stderr
225
+ assert_kind_of Process::Status, result.status
226
+ refute result.status.success?
227
+ end
228
+
229
+ # [TEST:SHELL_COMMAND] Test command with both STDOUT and STDERR (runs at initialization)
230
+ def test_command_with_stdout_and_stderr
231
+ cmd = ExecutedShellCommand.new(
232
+ "echo 'hello from shell' && >&2 echo 'oops' && exit 3"
233
+ )
234
+ result = cmd.result # Command already executed at initialization
235
+
236
+ refute result.success?, 'Command should fail'
237
+ assert_equal 3, result.exit_code
238
+ assert_equal "hello from shell\n", result.stdout
239
+ assert_equal "oops\n", result.stderr
240
+ end
241
+
242
+ # [TEST:SHELL_COMMAND] Test result memoization - accessing result returns same object
243
+ def test_result_memoization
244
+ cmd = ExecutedShellCommand.new("echo 'test'")
245
+ first = cmd.result # Access memoized result from initialization
246
+ second = cmd.result # Should return same memoized result
247
+
248
+ assert_equal first.object_id, second.object_id,
249
+ 'Should return same memoized result object'
250
+ assert_equal first.stdout, second.stdout
251
+ assert_equal first.pid, second.pid
252
+ end
253
+
254
+ # [TEST:SHELL_COMMAND] Test run defaults to force: true and creates new result object
255
+ def test_run_defaults_to_force_true
256
+ cmd = ExecutedShellCommand.new("echo 'test'")
257
+ first = cmd.result # Initial execution result
258
+ fresh = cmd.run # Defaults to force: true, so re-executes
259
+
260
+ refute_equal first.object_id, fresh.object_id,
261
+ 'Run should create new result object (force: true by default)'
262
+ assert_equal first.stdout, fresh.stdout, 'Output should be the same'
263
+ # PIDs will be different as it's a new process
264
+ refute_equal first.pid, fresh.pid,
265
+ 'PID should be different for new execution'
266
+ end
267
+
268
+ # [TEST:SHELL_COMMAND] Test run with force: false returns memoized result
269
+ def test_run_with_force_false_returns_memoized
270
+ cmd = ExecutedShellCommand.new("echo 'test'")
271
+ first = cmd.result # Initial execution result
272
+ cached = cmd.run(force: false) # Should return memoized result
273
+
274
+ assert_equal first.object_id, cached.object_id,
275
+ 'Run with force: false should return memoized result'
276
+ assert_equal first.stdout, cached.stdout
277
+ assert_equal first.pid, cached.pid
278
+ end
279
+
280
+ # [TEST:SHELL_COMMAND] Test fetch_result returns memoized result
281
+ def test_fetch_result_returns_memoized
282
+ cmd = ExecutedShellCommand.new("echo 'test'")
283
+ first = cmd.result # Initial execution result
284
+ fetched = cmd.fetch_result
285
+
286
+ assert_equal first.object_id, fetched.object_id,
287
+ 'fetch_result should return memoized result'
288
+ end
289
+
290
+ # [TEST:SHELL_COMMAND] Test convenience delegator methods (command runs at initialization)
291
+ def test_convenience_delegators
292
+ cmd = ExecutedShellCommand.new(
293
+ "echo 'output' && >&2 echo 'error' && exit 5"
294
+ )
295
+ # Command already executed at initialization, delegators access memoized result
296
+
297
+ assert_equal "output\n", cmd.stdout
298
+ assert_equal "error\n", cmd.stderr
299
+ assert_equal 5, cmd.exit_code
300
+ refute cmd.success?
301
+ assert_kind_of Numeric, cmd.duration
302
+ assert_kind_of Time, cmd.started_at
303
+ assert_kind_of Time, cmd.finished_at
304
+ assert_kind_of Integer, cmd.pid
305
+ end
306
+
307
+ # [TEST:SHELL_COMMAND] Test PID capture (command runs at initialization)
308
+ def test_pid_capture
309
+ cmd = ExecutedShellCommand.new("echo 'test'")
310
+ result = cmd.result # Command already executed at initialization
311
+
312
+ assert_kind_of Integer, result.pid
313
+ assert result.pid.positive?, 'PID should be positive'
314
+ end
315
+
316
+ # [TEST:SHELL_COMMAND] Test duration calculation (command runs at initialization)
317
+ def test_duration_calculation
318
+ cmd = ExecutedShellCommand.new('sleep 0.1')
319
+ result = cmd.result # Command already executed at initialization
320
+
321
+ assert_kind_of Numeric, result.duration
322
+ assert result.duration >= 0.1, 'Duration should be at least 0.1 seconds'
323
+ assert result.duration < 1.0, 'Duration should be less than 1 second'
324
+ assert result.started_at < result.finished_at,
325
+ 'Started at should be before finished at'
326
+ end
327
+
328
+ # [TEST:SHELL_COMMAND] Test timestamps (command runs at initialization)
329
+ def test_timestamps
330
+ before = Time.now
331
+ cmd = ExecutedShellCommand.new("echo 'test'")
332
+ after = Time.now
333
+ result = cmd.result # Command already executed at initialization
334
+
335
+ assert result.started_at >= before,
336
+ 'Started at should be after test start'
337
+ assert result.finished_at <= after,
338
+ 'Finished at should be before test end'
339
+ assert result.started_at <= result.finished_at,
340
+ 'Started at should be before finished at'
341
+ end
342
+
343
+ # [TEST:SHELL_COMMAND] Test environment variable passing (command runs at initialization)
344
+ def test_environment_variables
345
+ env = { 'TEST_VAR' => 'test_value', 'ANOTHER_VAR' => 'another_value' }
346
+ cmd = ExecutedShellCommand.new('echo $TEST_VAR && echo $ANOTHER_VAR',
347
+ env: env)
348
+ result = cmd.result # Command already executed at initialization
349
+
350
+ assert result.success?
351
+ assert_includes result.stdout, 'test_value'
352
+ assert_includes result.stdout, 'another_value'
353
+ assert_equal env, result.env
354
+ end
355
+
356
+ # [TEST:SHELL_COMMAND] Test working directory change (command runs at initialization)
357
+ def test_working_directory_change
358
+ Dir.mktmpdir do |tmpdir|
359
+ test_file = File.join(tmpdir, 'test.txt')
360
+ File.write(test_file, 'test content')
361
+
362
+ cmd = ExecutedShellCommand.new('cat test.txt', chdir: tmpdir)
363
+ result = cmd.result # Command already executed at initialization
364
+
365
+ assert result.success?
366
+ assert_equal 'test content', result.stdout
367
+ assert_equal tmpdir, result.chdir
368
+ end
369
+ end
370
+
371
+ # [TEST:SHELL_COMMAND] Test command attribute
372
+ def test_command_attribute
373
+ command_str = "echo 'test'"
374
+ cmd = ExecutedShellCommand.new(command_str)
375
+
376
+ assert_equal command_str, cmd.command
377
+ end
378
+
379
+ # [TEST:SHELL_COMMAND] Test nil command raises ArgumentError at initialization
380
+ def test_nil_command_raises_error
381
+ assert_raises(ArgumentError, 'command cannot be nil') do
382
+ ExecutedShellCommand.new(nil) # Error raised during initialization when run is called
383
+ end
384
+ end
385
+
386
+ # [TEST:SHELL_COMMAND] Test Result#to_h method (command runs at initialization)
387
+ def test_result_to_h
388
+ cmd = ExecutedShellCommand.new("echo 'test'")
389
+ result = cmd.result # Command already executed at initialization
390
+ hash = result.to_h
391
+
392
+ assert_kind_of Hash, hash
393
+ assert_equal cmd.command, hash[:command]
394
+ assert_equal result.stdout, hash[:stdout]
395
+ assert_equal result.stderr, hash[:stderr]
396
+ assert_equal result.exit_code, hash[:exit_code]
397
+ assert_equal result.success?, hash[:success]
398
+ assert_equal result.started_at, hash[:started_at]
399
+ assert_equal result.finished_at, hash[:finished_at]
400
+ assert_equal result.duration, hash[:duration]
401
+ assert_equal result.pid, hash[:pid]
402
+ assert_equal result.env, hash[:env]
403
+ assert_equal result.chdir, hash[:chdir]
404
+ assert_equal result.status, hash[:status]
405
+ end
406
+
407
+ # [TEST:SHELL_COMMAND] Test Result#signaled? method (command runs at initialization)
408
+ def test_result_signaled
409
+ cmd = ExecutedShellCommand.new("echo 'test'")
410
+ result = cmd.result # Command already executed at initialization
411
+
412
+ # Normal exit should not be signaled
413
+ refute result.signaled?
414
+ end
415
+
416
+ # [TEST:SHELL_COMMAND] Test Result#termsig method (command runs at initialization)
417
+ def test_result_termsig
418
+ cmd = ExecutedShellCommand.new("echo 'test'")
419
+ result = cmd.result # Command already executed at initialization
420
+
421
+ # Normal exit should have nil termsig
422
+ assert_nil result.termsig, 'Normal exit should have nil termsig'
423
+ end
424
+
425
+ # [TEST:SHELL_COMMAND] Test empty environment hash (command runs at initialization)
426
+ def test_empty_environment_hash
427
+ cmd = ExecutedShellCommand.new("echo 'test'", env: {})
428
+ result = cmd.result # Command already executed at initialization
429
+
430
+ assert result.success?
431
+ assert_equal({}, result.env)
432
+ end
433
+
434
+ # [TEST:SHELL_COMMAND] Test command with complex shell syntax (command runs at initialization)
435
+ def test_complex_shell_command
436
+ cmd = ExecutedShellCommand.new(
437
+ "echo 'line1' && echo 'line2' && echo 'line3'"
438
+ )
439
+ result = cmd.result # Command already executed at initialization
440
+
441
+ assert result.success?
442
+ assert_includes result.stdout, 'line1'
443
+ assert_includes result.stdout, 'line2'
444
+ assert_includes result.stdout, 'line3'
445
+ end
446
+
447
+ # [TEST:SHELL_COMMAND] Test command with no output (command runs at initialization)
448
+ def test_command_with_no_output
449
+ cmd = ExecutedShellCommand.new('true')
450
+ result = cmd.result # Command already executed at initialization
451
+
452
+ assert result.success?
453
+ assert_equal 0, result.exit_code
454
+ assert_equal '', result.stdout
455
+ assert_equal '', result.stderr
456
+ end
457
+
458
+ # [TEST:SHELL_COMMAND] Test multiple runs (default force: true creates new results)
459
+ def test_multiple_runs_create_new_results
460
+ cmd = ExecutedShellCommand.new("echo 'test'")
461
+ first = cmd.result # Initial execution result
462
+ second = cmd.run # Defaults to force: true, creates new result
463
+ third = cmd.run # Defaults to force: true, creates new result
464
+
465
+ refute_equal first.object_id, second.object_id,
466
+ 'Each run creates new result'
467
+ refute_equal second.object_id, third.object_id,
468
+ 'Each run creates new result'
469
+ # All should have same output
470
+ assert_equal first.stdout, second.stdout
471
+ assert_equal second.stdout, third.stdout
472
+ end
473
+
474
+ # [TEST:SHELL_COMMAND] Test command with newlines in output (command runs at initialization)
475
+ def test_command_with_newlines
476
+ cmd = ExecutedShellCommand.new("echo -e 'line1\nline2\nline3'")
477
+ result = cmd.result # Command already executed at initialization
478
+
479
+ assert result.success?
480
+ assert_includes result.stdout, 'line1'
481
+ assert_includes result.stdout, 'line2'
482
+ assert_includes result.stdout, 'line3'
483
+ end
484
+
485
+ # [TEST:SHELL_COMMAND] Test command attribute is immutable (command runs at initialization)
486
+ def test_command_immutability
487
+ original_command = "echo 'original'"
488
+ cmd = ExecutedShellCommand.new(original_command)
489
+
490
+ # Command should remain unchanged
491
+ assert_equal original_command, cmd.command
492
+ result = cmd.result # Command already executed at initialization
493
+ assert_equal original_command, cmd.command
494
+ assert_equal original_command, result.command
495
+ end
496
+
497
+ # [TEST:SHELL_COMMAND] Test command executes automatically at initialization
498
+ def test_command_executes_at_initialization
499
+ before = Time.now
500
+ cmd = ExecutedShellCommand.new("echo 'auto-executed'")
501
+ after = Time.now
502
+
503
+ # Result should be available immediately without calling run
504
+ refute_nil cmd.result,
505
+ 'Result should be available immediately after initialization'
506
+ assert_equal "auto-executed\n", cmd.result.stdout
507
+ assert cmd.result.started_at >= before,
508
+ 'Command should have started during initialization'
509
+ assert cmd.result.finished_at <= after,
510
+ 'Command should have finished during initialization'
511
+ end
512
+ end
data/lib/filter.rb CHANGED
@@ -135,12 +135,12 @@ module MarkdownExec
135
135
 
136
136
  if options[:hide_blocks_by_name]
137
137
  filters[:hidden_name] =
138
- !!(options[:block_name_hidden_match].present? &&
139
- fcb.code_name_exp?(options[:block_name_hidden_match]))
138
+ !!(options[:block_name_hide_custom_match].present? &&
139
+ fcb.code_name_exp?(options[:block_name_hide_custom_match]))
140
140
  end
141
141
  filters[:include_name] =
142
- !!(options[:block_name_include_match].present? &&
143
- fcb.code_name_exp?(options[:block_name_include_match]))
142
+ !!(options[:block_name_hidden_match].present? &&
143
+ fcb.code_name_exp?(options[:block_name_hidden_match]))
144
144
  filters[:wrap_name] =
145
145
  !!(options[:block_name_wrapper_match].present? &&
146
146
  fcb.code_name_exp?(options[:block_name_wrapper_match]))
@@ -251,7 +251,7 @@ if $PROGRAM_NAME == __FILE__
251
251
 
252
252
  def test_hidden_name_condition
253
253
  @options[:hide_blocks_by_name] = true
254
- @options[:block_name_hidden_match] = 'hidden'
254
+ @options[:block_name_hide_custom_match] = 'hidden'
255
255
  @fcb.oname = 'hidden_block'
256
256
  refute Filter.fcb_select?(@options, @fcb)
257
257
  end
@@ -336,7 +336,7 @@ if $PROGRAM_NAME == __FILE__
336
336
  end
337
337
 
338
338
  def test_hidden_block_by_name
339
- @options[:block_name_hidden_match] = 'hidden_block'
339
+ @options[:block_name_hide_custom_match] = 'hidden_block'
340
340
  @options[:hide_blocks_by_name] = true
341
341
  fcb = FCB.new(oname: 'hidden_block', shell: ShellType::BASH,
342
342
  start_line: '')
@@ -345,7 +345,7 @@ if $PROGRAM_NAME == __FILE__
345
345
  end
346
346
 
347
347
  def test_include_block_by_name
348
- @options[:block_name_include_match] = 'include_block'
348
+ @options[:block_name_hidden_match] = 'include_block'
349
349
  fcb = FCB.new(oname: 'include_block', shell: ShellType::BASH,
350
350
  start_line: '')
351
351
  Filter.apply_other_filters(@options, @filters, fcb)