markdown_exec 1.7 → 1.8

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.
data/lib/mdoc.rb CHANGED
@@ -21,7 +21,7 @@ module MarkdownExec
21
21
  #
22
22
  # @param table [Array<Hash>] An array of hashes representing markdown sections.
23
23
  #
24
- def initialize(table)
24
+ def initialize(table = [])
25
25
  @table = table
26
26
  end
27
27
 
@@ -47,8 +47,8 @@ module MarkdownExec
47
47
  # write named variables to block at top of script
48
48
  #
49
49
  fcb[:body].join(' ').split.compact.map do |key|
50
- format(opts[:block_type_port_set_format],
51
- { key: key, value: ENV.fetch(key, nil) })
50
+ ### format(opts[:block_type_port_set_format], { key: key, value: ENV.fetch(key, nil) })
51
+ "key: #{key}, value: #{ENV.fetch(key, nil)}"
52
52
  end
53
53
  end
54
54
 
@@ -72,22 +72,30 @@ module MarkdownExec
72
72
  def collect_recursively_required_blocks(name)
73
73
  name_block = get_block_by_anyname(name)
74
74
  if name_block.nil? || name_block.keys.empty?
75
- raise "Named code block `#{name}` not found."
75
+ raise "Named code block `#{name}` not found. (@#{__LINE__})"
76
76
  end
77
77
 
78
- all = [name_block.oname] + recursively_required(name_block[:reqs])
78
+ # all_dependency_names = [name_block.oname] + recursively_required(name_block[:reqs])
79
+ dependencies = collect_dependencies(name_block[:oname])
80
+ all_dependency_names = collect_unique_names(dependencies).push(name_block[:oname]).uniq
81
+ unmet_dependencies = all_dependency_names.dup
79
82
 
80
- # in order of appearance in document
83
+ # in order of appearance in document (@table)
81
84
  # insert function blocks
82
- @table.select { |fcb| all.include? fcb.oname }
83
- .map do |fcb|
85
+ blocks = @table.select { |fcb| all_dependency_names.include? fcb[:oname] }
86
+ .map do |fcb|
87
+ unmet_dependencies.delete(fcb[:oname]) # may not exist if block name is duplicated
84
88
  if (call = fcb[:call])
85
89
  [get_block_by_oname("[#{call.match(/^%\((\S+) |\)/)[1]}]")
86
90
  .merge({ cann: call })]
87
91
  else
88
92
  []
89
93
  end + [fcb]
90
- end.flatten(1)
94
+ end.flatten(1) #.tap { rbp }
95
+ { all_dependency_names: all_dependency_names,
96
+ blocks: blocks,
97
+ dependencies: dependencies,
98
+ unmet_dependencies: unmet_dependencies }
91
99
  end
92
100
 
93
101
  # Collects recursively required code blocks and returns them as an array of strings.
@@ -95,10 +103,9 @@ module MarkdownExec
95
103
  # @param name [String] The name of the code block to start the collection from.
96
104
  # @return [Array<String>] An array of strings containing the collected code blocks.
97
105
  #
98
- def collect_recursively_required_code(name, opts: {})
99
- code = collect_wrapped_blocks(
100
- blocks = collect_recursively_required_blocks(name)
101
- ).map do |fcb|
106
+ def collect_recursively_required_code(name, label_body: true, label_format_above: nil, label_format_below: nil)
107
+ block_search = collect_recursively_required_blocks(name)
108
+ block_search.merge({ code: collect_wrapped_blocks(block_search[:blocks]).map do |fcb|
102
109
  if fcb[:cann]
103
110
  collect_block_code_cann(fcb)
104
111
  elsif fcb[:stdout]
@@ -108,11 +115,18 @@ module MarkdownExec
108
115
  nil
109
116
  elsif fcb[:shell] == BlockType::PORT
110
117
  collect_block_code_shell(fcb)
111
- else
118
+ elsif label_body
119
+ [label_format_above && format(label_format_above, { name: fcb[:oname] })] +
120
+ fcb[:body] +
121
+ [label_format_below && format(label_format_below, { name: fcb[:oname] })]
122
+ else # raw body
112
123
  fcb[:body]
113
124
  end
114
- end.compact.flatten(1)
115
- { blocks: blocks, code: code }
125
+ end.compact.flatten(1).compact })
126
+ end
127
+
128
+ def collect_unique_names(hash)
129
+ hash.values.flatten.uniq
116
130
  end
117
131
 
118
132
  # Retrieves code blocks that are wrapped
@@ -209,6 +223,7 @@ module MarkdownExec
209
223
  end
210
224
  end
211
225
 
226
+ # if tr ue
212
227
  # Recursively fetches required code blocks for a given list of requirements.
213
228
  #
214
229
  # @param reqs [Array<String>] An array of requirements to start the recursion from.
@@ -219,7 +234,7 @@ module MarkdownExec
219
234
 
220
235
  rem = reqs
221
236
  memo = []
222
- while rem.count.positive?
237
+ while rem && rem.count.positive?
223
238
  rem = rem.map do |req|
224
239
  next if memo.include? req
225
240
 
@@ -232,6 +247,57 @@ module MarkdownExec
232
247
  memo
233
248
  end
234
249
 
250
+ # else
251
+ # Recursively fetches required code blocks for a given list of requirements.
252
+ #
253
+ # @param source [String] The name of the code block to start the recursion from.
254
+ # @return [Hash] A list of code blocks required by each source code block.
255
+ #
256
+ def recursively_required_hash(source, memo = Hash.new([]))
257
+ return memo unless source
258
+ return memo if memo.keys.include? source
259
+
260
+ block = get_block_by_anyname(source)
261
+ if block.nil? || block.keys.empty?
262
+ raise "Named code block `#{source}` not found. (@#{__LINE__})"
263
+ end
264
+
265
+ memo[source] = block[:reqs]
266
+ return memo unless memo[source]&.count&.positive?
267
+
268
+ memo[source].each do |req|
269
+ next if memo.keys.include? req
270
+
271
+ recursively_required_hash(req, memo)
272
+ end
273
+ memo
274
+ end
275
+
276
+ # end
277
+
278
+ # Recursively collects dependencies of a given source.
279
+ # @param source [String] The name of the initial source block.
280
+ # @param memo [Hash] A memoization hash to store resolved dependencies.
281
+ # @return [Hash] A hash mapping sources to their respective dependencies.
282
+ def collect_dependencies(source, memo = {})
283
+ return memo unless source
284
+
285
+ if (block = get_block_by_anyname(source)).nil? || block.keys.empty?
286
+ if true
287
+ return memo
288
+ else
289
+ raise "Named code block `#{source}` not found. (@#{__LINE__})"
290
+ end
291
+ end
292
+
293
+ return memo unless block[:reqs]
294
+
295
+ memo[source] = block[:reqs]
296
+
297
+ block[:reqs].each { |req| collect_dependencies(req, memo) unless memo.key?(req) }
298
+ memo
299
+ end
300
+
235
301
  def select_elements_with_neighbor_conditions(array,
236
302
  last_selected_placeholder = nil, next_selected_placeholder = nil)
237
303
  selected_elements = []
@@ -283,16 +349,64 @@ if $PROGRAM_NAME == __FILE__
283
349
  Bundler.require(:default)
284
350
 
285
351
  require 'minitest/autorun'
352
+ require 'mocha/minitest'
286
353
 
287
354
  module MarkdownExec
355
+ class TestMDocCollectDependencies < Minitest::Test
356
+ def setup
357
+ @mdoc = MDoc.new
358
+ end
359
+
360
+ def test_collect_dependencies_with_no_source
361
+ assert_empty @mdoc.collect_dependencies(nil)
362
+ end
363
+
364
+ if false # must raise error
365
+ def test_collect_dependencies_with_nonexistent_source
366
+ assert_raises(RuntimeError) { @mdoc.collect_dependencies('nonexistent') }
367
+ end
368
+ end
369
+
370
+ def test_collect_dependencies_with_valid_source
371
+ @mdoc.stubs(:get_block_by_anyname).with('source1').returns({ reqs: ['source2'] })
372
+ @mdoc.stubs(:get_block_by_anyname).with('source2').returns({ reqs: [] })
373
+
374
+ expected = { 'source1' => ['source2'], 'source2' => [] }
375
+ assert_equal expected, @mdoc.collect_dependencies('source1')
376
+ end
377
+ end
378
+
379
+ class TestCollectUniqueNames < Minitest::Test
380
+ def setup
381
+ @mdoc = MDoc.new
382
+ end
383
+
384
+ def test_empty_hash
385
+ assert_empty @mdoc.collect_unique_names({})
386
+ end
387
+
388
+ def test_single_key
389
+ input = { group1: %w[Alice Bob Charlie] }
390
+ assert_equal %w[Alice Bob Charlie], @mdoc.collect_unique_names(input)
391
+ end
392
+
393
+ def test_multiple_keys
394
+ input = { group1: %w[Alice Bob], group2: %w[Charlie Alice] }
395
+ assert_equal %w[Alice Bob Charlie], @mdoc.collect_unique_names(input)
396
+ end
397
+
398
+ def test_no_unique_names
399
+ input = { group1: ['Alice'], group2: ['Alice'] }
400
+ assert_equal ['Alice'], @mdoc.collect_unique_names(input)
401
+ end
402
+ end
403
+
288
404
  class TestMDoc < Minitest::Test
289
405
  def setup
290
406
  @table = [
291
- { oname: 'block1', body: ['code for block1'],
292
- reqs: ['block2'] },
407
+ { oname: 'block1', body: ['code for block1'], reqs: ['block2'] },
293
408
  { oname: 'block2', body: ['code for block2'], reqs: nil },
294
- { oname: 'block3', body: ['code for block3'],
295
- reqs: ['block1'] }
409
+ { oname: 'block3', body: ['code for block3'], reqs: ['block1'] }
296
410
  ]
297
411
  @doc = MDoc.new(@table)
298
412
  end
@@ -312,15 +426,15 @@ if $PROGRAM_NAME == __FILE__
312
426
  end
313
427
 
314
428
  ### broken test
315
- # def test_collect_recursively_required_blocks
316
- # result = @doc.collect_recursively_required_blocks('block3')
317
- # expected_result = [@table[0], @table[1], @table[2]]
318
- # assert_equal expected_result, result
429
+ def test_collect_recursively_required_blocks
430
+ result = @doc.collect_recursively_required_blocks('block3')[:blocks]
431
+ expected_result = [@table[0], @table[1], @table[2]]
432
+ assert_equal expected_result, result
319
433
 
320
- # assert_raises(RuntimeError) do
321
- # @doc.collect_recursively_required_blocks('missing_block')
322
- # end
323
- # end
434
+ assert_raises(RuntimeError) do
435
+ @doc.collect_recursively_required_blocks('missing_block')
436
+ end
437
+ end
324
438
 
325
439
  def test_hide_menu_block_per_options
326
440
  opts = { hide_blocks_by_name: true,
@@ -338,11 +452,12 @@ if $PROGRAM_NAME == __FILE__
338
452
  # end
339
453
 
340
454
  def test_recursively_required
341
- result = @doc.recursively_required(['block3'])
342
- assert_equal %w[block3 block1 block2], result
455
+ result = @doc.recursively_required_hash('block3')
456
+ assert_equal ({ 'block3' => ['block1'], 'block1' => ['block2'], 'block2' => nil }),
457
+ result
343
458
 
344
- result_no_reqs = @doc.recursively_required(nil)
345
- assert_equal [], result_no_reqs
459
+ result_no_reqs = @doc.recursively_required_hash(nil)
460
+ assert_equal ({}), result_no_reqs
346
461
  end
347
462
  end
348
463
 
data/lib/menu.src.yml CHANGED
@@ -92,6 +92,51 @@
92
92
  :opt_name: document_load_opts_block_name
93
93
  :procname: val_as_str
94
94
 
95
+ - :arg_name: BOOL
96
+ :default: false
97
+ :description: Dump BlocksInFile (stage 1)
98
+ :env_var: MDE_DUMP_BLOCKS_IN_FILE
99
+ :opt_name: dump_blocks_in_file
100
+ :procname: val_as_bool
101
+
102
+ - :arg_name: BOOL
103
+ :default: false
104
+ :description: Dump MenuBlocks (stage 2)
105
+ :env_var: MDE_DUMP_MENU_BLOCKS
106
+ :opt_name: dump_menu_blocks
107
+ :procname: val_as_bool
108
+
109
+ - :arg_name: BOOL
110
+ :default: false
111
+ :description: Dump selected block
112
+ :env_var: MDE_DUMP_SELECTED_BLOCK
113
+ :opt_name: dump_selected_block
114
+ :procname: val_as_bool
115
+
116
+ - :default: fg_rgbh_E0_E0_20 # Y
117
+ :description: Color of exception detail
118
+ :env_var: MDE_EXCEPTION_COLOR_DETAIL
119
+ :opt_name: exception_color_detail
120
+ :procname: val_as_str
121
+
122
+ - :default: " - %{detail}\n"
123
+ :description: Format for exception detail
124
+ :env_var: MDE_EXCEPTION_FORMAT_DETAIL
125
+ :opt_name: exception_format_detail
126
+ :procname: val_as_str
127
+
128
+ - :default: fg_rgbh_E0_20_20 # R
129
+ :description: Color of exception name
130
+ :env_var: MDE_EXCEPTION_COLOR_NAME
131
+ :opt_name: exception_color_name
132
+ :procname: val_as_str
133
+
134
+ - :default: "\n%{name}"
135
+ :description: Format for exception name
136
+ :env_var: MDE_EXCEPTION_FORMAT_NAME
137
+ :opt_name: exception_format_name
138
+ :procname: val_as_str
139
+
95
140
  - :arg_name: REGEX
96
141
  :default:
97
142
  :description: Exclude blocks with name matching
@@ -113,7 +158,7 @@
113
158
  :opt_name: exclude_expect_blocks
114
159
  :procname: val_as_bool
115
160
 
116
- - :default: magenta
161
+ - :default: fg_rgbh_20_80_80
117
162
  :description: execution_report_preview_frame_color
118
163
  :env_var: MDE_EXECUTION_REPORT_PREVIEW_FRAME_COLOR
119
164
  :opt_name: execution_report_preview_frame_color
@@ -157,6 +202,13 @@
157
202
  :procname: val_as_str
158
203
  :short_name: f
159
204
 
205
+ - :arg_name: FIND
206
+ :default: ""
207
+ :description: Find in documents
208
+ :long_name: find
209
+ :procname: find
210
+ :short_name: "?"
211
+
160
212
  - :default: "^# *(?<name>[^#]*?) *$"
161
213
  :env_var: MDE_HEADING1_MATCH
162
214
  :opt_name: heading1_match
@@ -196,6 +248,13 @@
196
248
  :opt_name: history_property_separator
197
249
  :procname: val_as_str
198
250
 
251
+ - :arg_name: HOW
252
+ :default: ""
253
+ :description: Find in YAML configuration options
254
+ :long_name: how
255
+ :procname: how
256
+ :short_name: "?"
257
+
199
258
  - :default: "^ *@import +(?<name>.+?) *$"
200
259
  :env_var: MDE_IMPORT_PATTERN
201
260
  :opt_name: import_pattern
@@ -261,7 +320,7 @@
261
320
  :opt_name: menu_back_at_top
262
321
  :procname: val_as_bool
263
322
 
264
- - :default: plain
323
+ - :default: fg_rgbh_40_c0_f0
265
324
  :description: Color of menu bash
266
325
  :env_var: MDE_MENU_BASH_COLOR
267
326
  :opt_name: menu_bash_color
@@ -281,7 +340,7 @@
281
340
  :opt_name: menu_blocks_with_headings
282
341
  :procname: val_as_bool
283
342
 
284
- - :default: fg_rgbh_aa_bb_aa
343
+ - :default: fg_rgbh_80_80_20
285
344
  :description: Color of menu chrome
286
345
  :env_var: MDE_MENU_CHROME_COLOR
287
346
  :opt_name: menu_chrome_color
@@ -293,7 +352,7 @@
293
352
  :opt_name: menu_chrome_format
294
353
  :procname: val_as_str
295
354
 
296
- - :default: magenta
355
+ - :default: fg_rgbh_20_98_80
297
356
  :description: Color of menu divider
298
357
  :env_var: MDE_MENU_DIVIDER_COLOR
299
358
  :opt_name: menu_divider_color
@@ -358,7 +417,7 @@
358
417
  :opt_name: menu_initial_divider
359
418
  :procname: val_as_str
360
419
 
361
- - :default: red
420
+ - :default: fg_rgbh_20_E0_20 # G
362
421
  :description: Color of menu link
363
422
  :env_var: MDE_MENU_LINK_COLOR
364
423
  :opt_name: menu_link_color
@@ -370,7 +429,7 @@
370
429
  :opt_name: menu_link_format
371
430
  :procname: val_as_str
372
431
 
373
- - :default: fg_rgbh_88_88_88
432
+ - :default: fg_rgbh_40_a0_a0
374
433
  :description: Color of menu note
375
434
  :env_var: MDE_MENU_NOTE_COLOR
376
435
  :opt_name: menu_note_color
@@ -404,13 +463,13 @@
404
463
  :opt_name: menu_option_exit_name
405
464
  :procname: val_as_str
406
465
 
407
- - :default: blue
466
+ - :default: fg_rgbh_E0_60_E0 # R
408
467
  :description: Color of menu opts
409
468
  :env_var: MDE_MENU_OPTS_COLOR
410
469
  :opt_name: menu_opts_color
411
470
  :procname: val_as_str
412
471
 
413
- - :default: cyan
472
+ - :default: fg_rgbh_E0_20_20
414
473
  :description: Color of menu opts
415
474
  :env_var: MDE_MENU_OPTS_SET_COLOR
416
475
  :opt_name: menu_opts_set_color
@@ -422,7 +481,7 @@
422
481
  :opt_name: menu_opts_set_format
423
482
  :procname: val_as_str
424
483
 
425
- - :default: cyan
484
+ - :default: fg_rgbh_A0_20_D0 # V
426
485
  :description: Color of menu task
427
486
  :env_var: MDE_MENU_TASK_COLOR
428
487
  :opt_name: menu_task_color
@@ -446,13 +505,13 @@
446
505
  :opt_name: menu_task_symbol
447
506
  :procname: val_as_str
448
507
 
449
- - :default: blue
508
+ - :default: fg_rgbh_E0_80_20
450
509
  :description: Color of menu vars
451
510
  :env_var: MDE_MENU_VARS_COLOR
452
511
  :opt_name: menu_vars_color
453
512
  :procname: val_as_str
454
513
 
455
- - :default: cyan
514
+ - :default: fg_rgbh_E0_80_20
456
515
  :description: Color of menu vars
457
516
  :env_var: MDE_MENU_VARS_SET_COLOR
458
517
  :opt_name: menu_vars_set_color
@@ -499,13 +558,13 @@
499
558
  :opt_name: output_execution_label_format
500
559
  :procname: val_as_str
501
560
 
502
- - :default: yellow
561
+ - :default: fg_rgbh_20_D8_80
503
562
  :description: Color of output_execution_label_name
504
563
  :env_var: MDE_OUTPUT_EXECUTION_LABEL_NAME_COLOR
505
564
  :opt_name: output_execution_label_name_color
506
565
  :procname: val_as_str
507
566
 
508
- - :default: plain
567
+ - :default: fg_rgbh_20_E0_80
509
568
  :description: Color of output_execution_label_value
510
569
  :env_var: MDE_OUTPUT_EXECUTION_LABEL_VALUE_COLOR
511
570
  :opt_name: output_execution_label_value_color
@@ -567,7 +626,7 @@
567
626
  :opt_name: prompt_approve_block
568
627
  :procname: val_as_str
569
628
 
570
- - :default: cyan
629
+ - :default: fg_rgbh_20_E8_80
571
630
  :description: Color of prompt after script execution
572
631
  :env_var: MDE_PROMPT_COLOR_AFTER_SCRIPT_EXECUTION
573
632
  :opt_name: prompt_color_after_script_execution
@@ -631,6 +690,12 @@
631
690
  :opt_name: run_last_script
632
691
  :procname: val_as_bool
633
692
 
693
+ - :default: 1
694
+ :description: "Runtime exception error level (warn if < 0, ignore if 0, abort if > 0)"
695
+ :env_var: MDE_RUNTIME_EXCEPTION_ERROR_LEVEL
696
+ :opt_name: runtime_exception_error_level
697
+ :procname: val_as_int
698
+
634
699
  - :arg_name: BOOL
635
700
  :default: false
636
701
  :description: Save executed script
@@ -703,7 +768,7 @@
703
768
  :opt_name: saved_stdout_glob
704
769
  :procname: val_as_str
705
770
 
706
- - :default: green
771
+ - :default: fg_rgbh_20_80_80
707
772
  :description: script_execution_frame_color
708
773
  :env_var: MDE_SCRIPT_EXECUTION_FRAME_COLOR
709
774
  :opt_name: script_execution_frame_color
@@ -721,7 +786,7 @@
721
786
  :opt_name: script_execution_tail
722
787
  :procname: val_as_str
723
788
 
724
- - :default: yellow
789
+ - :default: fg_rgbh_20_80_80
725
790
  :description: Color of output divider
726
791
  :env_var: MDE_OUTPUT_DIVIDER_COLOR
727
792
  :opt_name: script_preview_frame_color
@@ -781,6 +846,18 @@
781
846
  :opt_name: shell
782
847
  :procname: val_as_str
783
848
 
849
+ - :default: "# -^- +%{name} -^-"
850
+ :description: shell_code_label_format_above
851
+ :env_var: MDE_SHELL_CODE_LABEL_FORMAT_ABOVE
852
+ :opt_name: shell_code_label_format_above
853
+ :procname: val_as_str
854
+
855
+ - :default: "# -v- +%{name} -v-"
856
+ :description: shell_code_label_format_below
857
+ :env_var: MDE_SHELL_CODE_LABEL_FORMAT_BELOW
858
+ :opt_name: shell_code_label_format_below
859
+ :procname: val_as_str
860
+
784
861
  - :description: List tab completions
785
862
  :long_name: tab-completions
786
863
  :opt_name: tab_completions
@@ -800,7 +877,7 @@
800
877
  :procname: version
801
878
  :short_name: v
802
879
 
803
- - :default: red
880
+ - :default: fg_rgbh_E0_E0_20 # Y
804
881
  :description: Color of warning message
805
882
  :env_var: MDE_WARNING_COLOR
806
883
  :opt_name: warning_color