markdown_exec 1.8.1 → 1.8.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.
@@ -145,10 +145,6 @@ module MarkdownExec
145
145
 
146
146
  private
147
147
 
148
- def should_add_back_option?
149
- @delegate_object[:menu_with_back] && history_env_state_exist?
150
- end
151
-
152
148
  def add_back_option(menu_blocks)
153
149
  append_chrome_block(menu_blocks, MenuState::BACK)
154
150
  end
@@ -216,42 +212,16 @@ module MarkdownExec
216
212
 
217
213
  # private
218
214
 
219
- def divider_formatting_present?(position)
220
- divider_key = position == :initial ? :menu_initial_divider : :menu_final_divider
221
- @delegate_object[:menu_divider_format].present? && @delegate_object[divider_key].present?
222
- end
223
-
224
- def create_divider(position)
225
- divider_key = position == :initial ? :menu_initial_divider : :menu_final_divider
226
- oname = format(@delegate_object[:menu_divider_format],
227
- safeval(@delegate_object[divider_key]))
228
-
229
- FCB.new(
230
- chrome: true,
231
- disabled: '',
232
- dname: string_send_color(oname, :menu_divider_color),
233
- oname: oname
234
- )
235
- end
236
-
237
- # Execute a code block after approval and provide user interaction options.
238
- #
239
- # This method displays required code blocks, asks for user approval, and
240
- # executes the code block if approved. It also allows users to copy the
241
- # code to the clipboard or save it to a file.
242
- #
243
- # @param opts [Hash] Options hash containing configuration settings.
244
- # @param mdoc [YourMDocClass] An instance of the MDoc class.
215
+ # Applies shell color options to the given string if applicable.
245
216
  #
246
- def approve_and_execute_block(selected, mdoc)
247
- if selected.fetch(:shell, '') == BlockType::LINK
248
- handle_link_block(selected.fetch(:body, ''), mdoc, selected)
249
- elsif @menu_user_clicked_back_link
250
- handle_back_link
251
- elsif selected[:shell] == BlockType::OPTS
252
- handle_opts_block(selected, @menu_base_options)
217
+ # @param name [String] The name to potentially colorize.
218
+ # @param shell_color_option [Symbol, nil] The shell color option to apply.
219
+ # @return [String] The colorized or original name string.
220
+ def apply_shell_color_option(name, shell_color_option)
221
+ if shell_color_option && @delegate_object[shell_color_option].present?
222
+ string_send_color(name, shell_color_option)
253
223
  else
254
- handle_generic_block(mdoc, selected)
224
+ name
255
225
  end
256
226
  end
257
227
 
@@ -286,22 +256,6 @@ module MarkdownExec
286
256
 
287
257
  # private
288
258
 
289
- def process_block_based_on_type(blocks, btype, fcb)
290
- case btype
291
- when :blocks
292
- blocks.push(get_block_summary(fcb))
293
- when :filter
294
- %i[blocks line]
295
- when :line
296
- unless @delegate_object[:no_chrome]
297
- create_and_add_chrome_blocks(blocks,
298
- fcb)
299
- end
300
- end
301
- end
302
-
303
- # private
304
-
305
259
  def cfile
306
260
  @cfile ||= CachedNestedFileReader.new(
307
261
  import_pattern: @delegate_object.fetch(:import_pattern) #, "^ *@import +(?<name>.+?) *$")
@@ -322,25 +276,6 @@ module MarkdownExec
322
276
  true
323
277
  end
324
278
 
325
- def runtime_exception(exception_sym, name, items)
326
- if @delegate_object[exception_sym] != 0
327
- data = { name: name, detail: items.join(', ') }
328
- warn(
329
- format(
330
- @delegate_object.fetch(:exception_format_name, "\n%{name}"),
331
- data
332
- ).send(@delegate_object.fetch(:exception_color_name, :red)) +
333
- format(
334
- @delegate_object.fetch(:exception_format_detail, " - %{detail}\n"),
335
- data
336
- ).send(@delegate_object.fetch(:exception_color_detail, :yellow))
337
- )
338
- end
339
- return unless (@delegate_object[exception_sym]).positive?
340
-
341
- exit @delegate_object[exception_sym]
342
- end
343
-
344
279
  # Collects required code lines based on the selected block and the delegate object's configuration.
345
280
  # If the block type is VARS, it also sets environment variables based on the block's content.
346
281
  #
@@ -348,7 +283,7 @@ module MarkdownExec
348
283
  # @param selected [Hash] The selected block.
349
284
  # @return [Array<String>] Required code blocks as an array of lines.
350
285
  def collect_required_code_lines(mdoc, selected)
351
- set_environment_variables(selected) if selected[:shell] == BlockType::VARS
286
+ set_environment_variables_for_block(selected) if selected[:shell] == BlockType::VARS
352
287
 
353
288
  required = mdoc.collect_recursively_required_code(
354
289
  @delegate_object[:block_name],
@@ -367,24 +302,10 @@ module MarkdownExec
367
302
  read_required_blocks_from_temp_file + required[:code]
368
303
  end
369
304
 
370
- # private
371
-
372
- def set_environment_variables(selected)
373
- YAML.load(selected[:body].join("\n")).each do |key, value|
374
- ENV[key] = value.to_s
375
- next unless @delegate_object[:menu_vars_set_format].present?
376
-
377
- formatted_string = format(@delegate_object[:menu_vars_set_format],
378
- { key: key, value: value })
379
- print string_send_color(formatted_string, :menu_vars_set_color)
380
- end
381
- end
382
-
383
305
  def command_execute(command, args: [])
384
306
  @run_state.files = Hash.new([])
385
307
  @run_state.options = @delegate_object
386
308
  @run_state.started_at = Time.now.utc
387
- # rbp
388
309
 
389
310
  Open3.popen3(@delegate_object[:shell],
390
311
  '-c', command,
@@ -442,6 +363,23 @@ module MarkdownExec
442
363
  error_handler('command_or_user_selected_block')
443
364
  end
444
365
 
366
+ # This method is responsible for handling the execution of generic blocks in a markdown document.
367
+ # It collects the required code lines from the document and, depending on the configuration,
368
+ # may display the code for user approval before execution. It then executes the approved block.
369
+ #
370
+ # @param mdoc [Object] The markdown document object containing code blocks.
371
+ # @param selected [Hash] The selected item from the menu to be executed.
372
+ # @return [LoadFileNextBlock] An object indicating whether to load the next block or reuse the current one.
373
+ def compile_execute_bash_and_special_blocks_and_trigger_reuse(mdoc, selected)
374
+ required_lines = collect_required_code_lines(mdoc, selected)
375
+ output_or_approval = @delegate_object[:output_script] || @delegate_object[:user_must_approve]
376
+ display_required_code(required_lines) if output_or_approval
377
+ allow_execution = @delegate_object[:user_must_approve] ? prompt_for_user_approval(required_lines) : true
378
+ execute_approved_block(required_lines, selected) if allow_execution
379
+
380
+ LoadFileNextBlock.new(LoadFile::Reuse, '')
381
+ end
382
+
445
383
  def copy_to_clipboard(required_lines)
446
384
  text = required_lines.flatten.join($INPUT_RECORD_SEPARATOR)
447
385
  Clipboard.copy(text)
@@ -535,12 +473,17 @@ module MarkdownExec
535
473
  FileUtils.mkdir_p(File.dirname(file_path))
536
474
  end
537
475
 
538
- def write_file_content(file_path, content)
539
- File.write(file_path, content)
540
- end
476
+ def create_divider(position)
477
+ divider_key = position == :initial ? :menu_initial_divider : :menu_final_divider
478
+ oname = format(@delegate_object[:menu_divider_format],
479
+ safeval(@delegate_object[divider_key]))
541
480
 
542
- def set_file_permissions(file_path, chmod_value)
543
- File.chmod(chmod_value, file_path)
481
+ FCB.new(
482
+ chrome: true,
483
+ disabled: '',
484
+ dname: string_send_color(oname, :menu_divider_color),
485
+ oname: oname
486
+ )
544
487
  end
545
488
 
546
489
  # Creates a temporary file, writes the provided code blocks into it,
@@ -558,14 +501,6 @@ module MarkdownExec
558
501
  Dir::Tmpname.create(self.class.to_s) { |path| path }
559
502
  end
560
503
 
561
- def write_to_file(path, content)
562
- File.write(path, content)
563
- end
564
-
565
- def set_environment_variable(path)
566
- ENV['MDE_LINK_REQUIRED_FILE'] = path
567
- end
568
-
569
504
  # Updates the title of an FCB object from its body content if the title is nil or empty.
570
505
  def default_block_title_from_body(fcb)
571
506
  return unless fcb.title.nil? || fcb.title.empty?
@@ -595,16 +530,6 @@ module MarkdownExec
595
530
  error_handler('delete_required_temp_file')
596
531
  end
597
532
 
598
- # private
599
-
600
- def fetch_temp_blocks_file_path
601
- ENV.fetch('MDE_LINK_REQUIRED_FILE', nil)
602
- end
603
-
604
- def safely_remove_file(path)
605
- FileUtils.rm_f(path)
606
- end
607
-
608
533
  # Determines the state of a selected block in the menu based on the selected option.
609
534
  # It categorizes the selected option into either EXIT, BACK, or CONTINUE state.
610
535
  #
@@ -636,12 +561,9 @@ module MarkdownExec
636
561
  :script_preview_frame_color)
637
562
  end
638
563
 
639
- # private
640
-
641
- def output_color_formatted(data_sym, color_sym)
642
- formatted_string = string_send_color(@delegate_object[data_sym],
643
- color_sym)
644
- @fout.fout formatted_string
564
+ def divider_formatting_present?(position)
565
+ divider_key = position == :initial ? :menu_initial_divider : :menu_final_divider
566
+ @delegate_object[:menu_divider_format].present? && @delegate_object[divider_key].present?
645
567
  end
646
568
 
647
569
  def error_handler(name = '', opts = {})
@@ -666,29 +588,25 @@ module MarkdownExec
666
588
  post_execution_process
667
589
  end
668
590
 
669
- # private
670
-
671
- def set_script_block_name(selected)
672
- @run_state.script_block_name = selected[:oname]
673
- end
674
-
675
- def write_command_file_if_needed(lines)
676
- write_command_file(lines) if @delegate_object[:save_executed_script]
677
- end
678
-
679
- def format_and_execute_command(lines)
680
- formatted_command = lines.flatten.join("\n")
681
- @fout.fout fetch_color(data_sym: :script_execution_head,
682
- color_sym: :script_execution_frame_color)
683
- command_execute(formatted_command, args: @pass_args)
684
- @fout.fout fetch_color(data_sym: :script_execution_tail,
685
- color_sym: :script_execution_frame_color)
686
- end
687
-
688
- def post_execution_process
689
- initialize_and_save_execution_output
690
- output_execution_summary
691
- output_execution_result
591
+ # Execute a code block after approval and provide user interaction options.
592
+ #
593
+ # This method displays required code blocks, asks for user approval, and
594
+ # executes the code block if approved. It also allows users to copy the
595
+ # code to the clipboard or save it to a file.
596
+ #
597
+ # @param opts [Hash] Options hash containing configuration settings.
598
+ # @param mdoc [YourMDocClass] An instance of the MDoc class.
599
+ #
600
+ def execute_bash_and_special_blocks(selected, mdoc)
601
+ if selected.fetch(:shell, '') == BlockType::LINK
602
+ push_link_history_and_trigger_load(selected.fetch(:body, ''), mdoc, selected)
603
+ elsif @menu_user_clicked_back_link
604
+ pop_link_history_and_trigger_load
605
+ elsif selected[:shell] == BlockType::OPTS
606
+ update_options_and_trigger_reuse(selected, @menu_base_options)
607
+ else
608
+ compile_execute_bash_and_special_blocks_and_trigger_reuse(mdoc, selected)
609
+ end
692
610
  end
693
611
 
694
612
  # Retrieves a specific data symbol from the delegate object, converts it to a string,
@@ -705,6 +623,31 @@ module MarkdownExec
705
623
  string_send_color(data_string, color_sym)
706
624
  end
707
625
 
626
+ def fetch_temp_blocks_file_path
627
+ ENV.fetch('MDE_LINK_REQUIRED_FILE', nil)
628
+ end
629
+
630
+ # DebugHelper.d ["HDmm method_name: #{method_name}", "#{first_n_caller_items 1}"]
631
+ def first_n_caller_items(n)
632
+ # Get the call stack
633
+ call_stack = caller
634
+ base_path = File.realpath('.')
635
+
636
+ # Modify the call stack to remove the base path and keep only the first n items
637
+ call_stack.take(n + 1)[1..].map do |line|
638
+ " . #{line.sub(/^#{Regexp.escape(base_path)}\//, '')}"
639
+ end.join("\n")
640
+ end
641
+
642
+ def format_and_execute_command(lines)
643
+ formatted_command = lines.flatten.join("\n")
644
+ @fout.fout fetch_color(data_sym: :script_execution_head,
645
+ color_sym: :script_execution_frame_color)
646
+ command_execute(formatted_command, args: @pass_args)
647
+ @fout.fout fetch_color(data_sym: :script_execution_tail,
648
+ color_sym: :script_execution_frame_color)
649
+ end
650
+
708
651
  # Formats a string based on a given context and applies color styling to it.
709
652
  # It retrieves format and color information from the delegate object and processes accordingly.
710
653
  #
@@ -745,133 +688,29 @@ module MarkdownExec
745
688
  @delegate_object[:block_name_match])
746
689
 
747
690
  fcb.stdin = extract_named_captures_from_option(titlexcall,
748
- @delegate_object[:block_stdin_scan])
749
- fcb.stdout = extract_named_captures_from_option(titlexcall,
750
- @delegate_object[:block_stdout_scan])
751
-
752
- shell_color_option = SHELL_COLOR_OPTIONS[fcb[:shell]]
753
- fcb.title = fcb.oname = bm && bm[1] ? bm[:title] : titlexcall
754
- fcb.dname = apply_shell_color_option(fcb.oname, shell_color_option)
755
-
756
- fcb
757
- end
758
-
759
- # private
760
-
761
- # Applies shell color options to the given string if applicable.
762
- #
763
- # @param name [String] The name to potentially colorize.
764
- # @param shell_color_option [Symbol, nil] The shell color option to apply.
765
- # @return [String] The colorized or original name string.
766
- def apply_shell_color_option(name, shell_color_option)
767
- if shell_color_option && @delegate_object[shell_color_option].present?
768
- string_send_color(name, shell_color_option)
769
- else
770
- name
771
- end
772
- end
773
-
774
- # This method handles the back-link operation in the Markdown execution context.
775
- # It updates the history state and prepares to load the next block.
776
- #
777
- # @return [LoadFileNextBlock] An object indicating the action to load the next block.
778
- def handle_back_link
779
- history_state_pop
780
- LoadFileNextBlock.new(LoadFile::Load, '')
781
- end
782
-
783
- # private
784
- # Updates the delegate object's state based on the provided block state.
785
- # It sets the block name and determines if the user clicked the back link in the menu.
786
- #
787
- # @param block_state [Object] An object representing the state of a block in the menu.
788
- def handle_block_state(block_state)
789
- unless [MenuState::BACK,
790
- MenuState::CONTINUE].include?(block_state.state)
791
- return
792
- end
793
-
794
- @delegate_object[:block_name] = block_state.block[:oname]
795
- # rbp
796
- @menu_user_clicked_back_link = block_state.state == MenuState::BACK
797
- end
798
-
799
- # This method is responsible for handling the execution of generic blocks in a markdown document.
800
- # It collects the required code lines from the document and, depending on the configuration,
801
- # may display the code for user approval before execution. It then executes the approved block.
802
- #
803
- # @param mdoc [Object] The markdown document object containing code blocks.
804
- # @param selected [Hash] The selected item from the menu to be executed.
805
- # @return [LoadFileNextBlock] An object indicating whether to load the next block or reuse the current one.
806
- def handle_generic_block(mdoc, selected)
807
- # rbp
808
- required_lines = collect_required_code_lines(mdoc, selected)
809
- output_or_approval = @delegate_object[:output_script] || @delegate_object[:user_must_approve]
810
- display_required_code(required_lines) if output_or_approval
811
- allow_execution = @delegate_object[:user_must_approve] ? prompt_for_user_approval(required_lines) : true
812
- execute_approved_block(required_lines, selected) if allow_execution
813
-
814
- LoadFileNextBlock.new(LoadFile::Reuse, '')
815
- end
816
-
817
- # Handles the processing of a link block in Markdown Execution.
818
- # It loads YAML data from the body content, pushes the state to history,
819
- # sets environment variables, and decides on the next block to load.
820
- #
821
- # @param body [Array<String>] The body content as an array of strings.
822
- # @param mdoc [Object] Markdown document object.
823
- # @param selected [Boolean] Selected state.
824
- # @return [LoadFileNextBlock] Object indicating the next action for file loading.
825
- def handle_link_block(body, mdoc, selected)
826
- data = parse_yaml_data_from_body(body)
827
- data_file = data['file']
828
- return LoadFileNextBlock.new(LoadFile::Reuse, '') unless data_file
829
-
830
- history_state_push(mdoc, data_file, selected)
831
- set_environment_variables(data['vars'])
832
-
833
- LoadFileNextBlock.new(LoadFile::Load, data['block'] || '')
834
- end
835
-
836
- # private
837
-
838
- def parse_yaml_data_from_body(body)
839
- body.any? ? YAML.load(body.join("\n")) : {}
840
- end
841
-
842
- def set_environment_variables(vars)
843
- vars ||= []
844
- vars.each { |key, value| ENV[key] = value.to_s }
845
- end
846
-
847
- # Processes YAML data from the selected menu item, updating delegate objects and optionally printing formatted output.
848
- # @param selected [Hash] Selected item from the menu containing a YAML body.
849
- # @param tgt2 [Hash, nil] An optional target hash to update with YAML data.
850
- # @return [LoadFileNextBlock] An instance indicating the next action for loading files.
851
- def handle_opts_block(selected, tgt2 = nil)
852
- data = YAML.load(selected[:body].join("\n"))
853
- (data || []).each do |key, value|
854
- update_delegate_and_target(key, value, tgt2)
855
- if @delegate_object[:menu_opts_set_format].present?
856
- print_formatted_option(key,
857
- value)
858
- end
859
- end
860
- LoadFileNextBlock.new(LoadFile::Reuse, '')
861
- end
691
+ @delegate_object[:block_stdin_scan])
692
+ fcb.stdout = extract_named_captures_from_option(titlexcall,
693
+ @delegate_object[:block_stdout_scan])
862
694
 
863
- # private
695
+ shell_color_option = SHELL_COLOR_OPTIONS[fcb[:shell]]
696
+ fcb.title = fcb.oname = bm && bm[1] ? bm[:title] : titlexcall
697
+ fcb.dname = apply_shell_color_option(fcb.oname, shell_color_option)
864
698
 
865
- def update_delegate_and_target(key, value, tgt2)
866
- sym_key = key.to_sym
867
- @delegate_object[sym_key] = value
868
- tgt2[sym_key] = value if tgt2
699
+ fcb
869
700
  end
870
701
 
871
- def print_formatted_option(key, value)
872
- formatted_str = format(@delegate_object[:menu_opts_set_format],
873
- { key: key, value: value })
874
- print string_send_color(formatted_str, :menu_opts_set_color)
702
+ # Updates the delegate object's state based on the provided block state.
703
+ # It sets the block name and determines if the user clicked the back link in the menu.
704
+ #
705
+ # @param block_state [Object] An object representing the state of a block in the menu.
706
+ def handle_block_state(block_state)
707
+ unless [MenuState::BACK,
708
+ MenuState::CONTINUE].include?(block_state.state)
709
+ return
710
+ end
711
+
712
+ @delegate_object[:block_name] = block_state.block[:oname]
713
+ @menu_user_clicked_back_link = block_state.state == MenuState::BACK
875
714
  end
876
715
 
877
716
  def handle_stream(stream, file_type, swap: false)
@@ -896,10 +735,10 @@ module MarkdownExec
896
735
  end
897
736
  end
898
737
 
899
- def wait_for_stream_processing
900
- @process_mutex.synchronize do
901
- @process_cv.wait(@process_mutex)
902
- end
738
+ # Checks if a history environment variable is set and returns its value if present.
739
+ # @return [String, nil] The value of the history environment variable or nil if not present.
740
+ def history_env_state_exist?
741
+ ENV.fetch(MDE_HISTORY_ENV_NAME, '').present?
903
742
  end
904
743
 
905
744
  # Partitions the history state from the environment variable based on the document separator.
@@ -952,10 +791,6 @@ module MarkdownExec
952
791
  body.lines.map { |line| indent + line.chomp }.join("\n")
953
792
  end
954
793
 
955
- def initialize_fcb_names(fcb)
956
- fcb.oname = fcb.dname = fcb.title || ''
957
- end
958
-
959
794
  # Initializes variables for regex and other states
960
795
  def initial_state
961
796
  {
@@ -989,6 +824,10 @@ module MarkdownExec
989
824
  write_execution_output_to_file
990
825
  end
991
826
 
827
+ def initialize_fcb_names(fcb)
828
+ fcb.oname = fcb.dname = fcb.title || ''
829
+ end
830
+
992
831
  # Iterates through blocks in a file, applying the provided block to each line.
993
832
  # The iteration only occurs if the file exists.
994
833
  # @yield [Symbol] :filter Yields to obtain selected messages for processing.
@@ -1019,46 +858,11 @@ module MarkdownExec
1019
858
  block = block_find(all_blocks, :oname, block_name)
1020
859
  return unless block
1021
860
 
1022
- handle_opts_block(block, @delegate_object)
861
+ update_options_and_trigger_reuse(block, @delegate_object)
1023
862
  @most_recent_loaded_filename = @delegate_object[:filename]
1024
863
  true
1025
864
  end
1026
865
 
1027
- # DebugHelper.d ["HDmm method_name: #{method_name}", "#{first_n_caller_items 1}"]
1028
- def first_n_caller_items(n)
1029
- # Get the call stack
1030
- call_stack = caller
1031
- base_path = File.realpath('.')
1032
-
1033
- # Modify the call stack to remove the base path and keep only the first n items
1034
- call_stack.take(n + 1)[1..].map do |line|
1035
- " . #{line.sub(/^#{Regexp.escape(base_path)}\//, '')}"
1036
- end.join("\n")
1037
- end
1038
-
1039
- # Checks if a history environment variable is set and returns its value if present.
1040
- # @return [String, nil] The value of the history environment variable or nil if not present.
1041
- def history_env_state_exist?
1042
- ENV.fetch(MDE_HISTORY_ENV_NAME, '').present?
1043
- end
1044
-
1045
- # def history_env_state_exist?
1046
- # history = ENV.fetch(MDE_HISTORY_ENV_NAME, '')
1047
- # history.present? ? history : nil
1048
- # end
1049
-
1050
- # If a method is missing, treat it as a key for the @delegate_object.
1051
- def method_missing(method_name, *args, &block)
1052
- if @delegate_object.respond_to?(method_name)
1053
- @delegate_object.send(method_name, *args, &block)
1054
- elsif method_name.to_s.end_with?('=') && args.size == 1
1055
- @delegate_object[method_name.to_s.chop.to_sym] = args.first
1056
- else
1057
- @delegate_object[method_name]
1058
- # super
1059
- end
1060
- end
1061
-
1062
866
  def mdoc_and_blocks_from_nested_files
1063
867
  menu_blocks = blocks_from_nested_files
1064
868
  mdoc = MDoc.new(menu_blocks) do |nopts|
@@ -1074,9 +878,7 @@ module MarkdownExec
1074
878
 
1075
879
  # recreate menu with new options
1076
880
  #
1077
- if load_auto_blocks(all_blocks)
1078
- all_blocks, mdoc = mdoc_and_blocks_from_nested_files
1079
- end
881
+ all_blocks, mdoc = mdoc_and_blocks_from_nested_files if load_auto_blocks(all_blocks)
1080
882
 
1081
883
  menu_blocks = mdoc.fcbs_per_options(@delegate_object)
1082
884
  add_menu_chrome_blocks!(menu_blocks)
@@ -1108,14 +910,33 @@ module MarkdownExec
1108
910
  end
1109
911
  end
1110
912
 
913
+ # If a method is missing, treat it as a key for the @delegate_object.
914
+ def method_missing(method_name, *args, &block)
915
+ if @delegate_object.respond_to?(method_name)
916
+ @delegate_object.send(method_name, *args, &block)
917
+ elsif method_name.to_s.end_with?('=') && args.size == 1
918
+ @delegate_object[method_name.to_s.chop.to_sym] = args.first
919
+ else
920
+ @delegate_object[method_name]
921
+ # super
922
+ end
923
+ end
924
+
1111
925
  def next_block_name_from_command_line_arguments
1112
926
  return MenuControl::Repeat unless @delegate_object[:input_cli_rest].present?
1113
927
 
1114
- # rbp
1115
928
  @delegate_object[:block_name] = @delegate_object[:input_cli_rest].pop
1116
929
  MenuControl::Fresh
1117
930
  end
1118
931
 
932
+ # private
933
+
934
+ def output_color_formatted(data_sym, color_sym)
935
+ formatted_string = string_send_color(@delegate_object[data_sym],
936
+ color_sym)
937
+ @fout.fout formatted_string
938
+ end
939
+
1119
940
  def output_execution_result
1120
941
  @fout.fout fetch_color(data_sym: :execution_report_preview_head,
1121
942
  color_sym: :execution_report_preview_frame_color)
@@ -1162,6 +983,27 @@ module MarkdownExec
1162
983
  ), level: level
1163
984
  end
1164
985
 
986
+ # private
987
+
988
+ def parse_yaml_data_from_body(body)
989
+ body.any? ? YAML.load(body.join("\n")) : {}
990
+ end
991
+
992
+ # This method handles the back-link operation in the Markdown execution context.
993
+ # It updates the history state and prepares to load the next block.
994
+ #
995
+ # @return [LoadFileNextBlock] An object indicating the action to load the next block.
996
+ def pop_link_history_and_trigger_load
997
+ history_state_pop
998
+ LoadFileNextBlock.new(LoadFile::Load, '')
999
+ end
1000
+
1001
+ def post_execution_process
1002
+ initialize_and_save_execution_output
1003
+ output_execution_summary
1004
+ output_execution_result
1005
+ end
1006
+
1165
1007
  # Prepare the blocks menu by adding labels and other necessary details.
1166
1008
  #
1167
1009
  # @param all_blocks [Array<Hash>] The list of blocks from the file.
@@ -1189,6 +1031,28 @@ module MarkdownExec
1189
1031
  end.compact
1190
1032
  end
1191
1033
 
1034
+ def print_formatted_option(key, value)
1035
+ formatted_str = format(@delegate_object[:menu_opts_set_format],
1036
+ { key: key, value: value })
1037
+ print string_send_color(formatted_str, :menu_opts_set_color)
1038
+ end
1039
+
1040
+ # private
1041
+
1042
+ def process_block_based_on_type(blocks, btype, fcb)
1043
+ case btype
1044
+ when :blocks
1045
+ blocks.push(get_block_summary(fcb))
1046
+ when :filter
1047
+ %i[blocks line]
1048
+ when :line
1049
+ unless @delegate_object[:no_chrome]
1050
+ create_and_add_chrome_blocks(blocks,
1051
+ fcb)
1052
+ end
1053
+ end
1054
+ end
1055
+
1192
1056
  ##
1193
1057
  # Presents a menu to the user for approving an action and performs additional tasks based on the selection.
1194
1058
  # The function provides options for approval, rejection, copying data to clipboard, or saving data to a file.
@@ -1247,13 +1111,27 @@ module MarkdownExec
1247
1111
  exit 1
1248
1112
  end
1249
1113
 
1250
- def save_to_file(required_lines)
1251
- write_command_file(required_lines)
1252
- @fout.fout "File saved: #{@run_state.saved_filespec}"
1253
- end
1254
-
1255
1114
  # public
1256
1115
 
1116
+ # Handles the processing of a link block in Markdown Execution.
1117
+ # It loads YAML data from the body content, pushes the state to history,
1118
+ # sets environment variables, and decides on the next block to load.
1119
+ #
1120
+ # @param body [Array<String>] The body content as an array of strings.
1121
+ # @param mdoc [Object] Markdown document object.
1122
+ # @param selected [Boolean] Selected state.
1123
+ # @return [LoadFileNextBlock] Object indicating the next action for file loading.
1124
+ def push_link_history_and_trigger_load(body, mdoc, selected)
1125
+ data = parse_yaml_data_from_body(body)
1126
+ data_file = data['file']
1127
+ return LoadFileNextBlock.new(LoadFile::Reuse, '') unless data_file
1128
+
1129
+ history_state_push(mdoc, data_file, selected)
1130
+ set_environment_variables_per_array(data['vars'])
1131
+
1132
+ LoadFileNextBlock.new(LoadFile::Load, data['block'] || '')
1133
+ end
1134
+
1257
1135
  # Reads required code blocks from a temporary file specified by an environment variable.
1258
1136
  # @return [Array<String>] Lines read from the temporary file, or an empty array if file is not found or path is empty.
1259
1137
  def read_required_blocks_from_temp_file
@@ -1269,6 +1147,29 @@ module MarkdownExec
1269
1147
  end
1270
1148
  end
1271
1149
 
1150
+ def runtime_exception(exception_sym, name, items)
1151
+ if @delegate_object[exception_sym] != 0
1152
+ data = { name: name, detail: items.join(', ') }
1153
+ warn(
1154
+ format(
1155
+ @delegate_object.fetch(:exception_format_name, "\n%{name}"),
1156
+ data
1157
+ ).send(@delegate_object.fetch(:exception_color_name, :red)) +
1158
+ format(
1159
+ @delegate_object.fetch(:exception_format_detail, " - %{detail}\n"),
1160
+ data
1161
+ ).send(@delegate_object.fetch(:exception_color_detail, :yellow))
1162
+ )
1163
+ end
1164
+ return unless (@delegate_object[exception_sym]).positive?
1165
+
1166
+ exit @delegate_object[exception_sym]
1167
+ end
1168
+
1169
+ def safely_remove_file(path)
1170
+ FileUtils.rm_f(path)
1171
+ end
1172
+
1272
1173
  # Evaluates the given string as Ruby code and rescues any StandardErrors.
1273
1174
  # If an error occurs, it calls the error_handler method with 'safeval'.
1274
1175
  # @param str [String] The string to be evaluated.
@@ -1285,17 +1186,22 @@ module MarkdownExec
1285
1186
  # error_handler('safeval')
1286
1187
  # end
1287
1188
 
1189
+ def save_to_file(required_lines)
1190
+ write_command_file(required_lines)
1191
+ @fout.fout "File saved: #{@run_state.saved_filespec}"
1192
+ end
1193
+
1288
1194
  # Select and execute a code block from a Markdown document.
1289
1195
  #
1290
1196
  # This method allows the user to interactively select a code block from a
1291
1197
  # Markdown document, obtain approval, and execute the chosen block of code.
1292
1198
  #
1293
1199
  # @return [Nil] Returns nil if no code block is selected or an error occurs.
1294
- def select_approve_and_execute_block(_execute: true)
1200
+ def select_execute_bash_and_special_blocks(_execute: true)
1295
1201
  @menu_base_options = @delegate_object
1296
1202
  repeat_menu = @menu_base_options[:block_name].present? ? MenuControl::Fresh : MenuControl::Repeat
1297
1203
  load_file_next_block = LoadFileNextBlock.new(LoadFile::Reuse)
1298
- default = nil
1204
+ menu_default_dname = nil
1299
1205
 
1300
1206
  @menu_state_filename = @menu_base_options[:filename]
1301
1207
  @menu_state_block_name = @menu_base_options[:block_name]
@@ -1304,7 +1210,6 @@ module MarkdownExec
1304
1210
  loop do
1305
1211
  @delegate_object = @menu_base_options.dup
1306
1212
  @menu_base_options[:filename] = @menu_state_filename
1307
- # rbp
1308
1213
  @menu_base_options[:block_name] = @menu_state_block_name
1309
1214
  @menu_state_filename = nil
1310
1215
  @menu_state_block_name = nil
@@ -1324,11 +1229,11 @@ module MarkdownExec
1324
1229
  )
1325
1230
  end
1326
1231
  block_state = command_or_user_selected_block(blocks_in_file,
1327
- menu_blocks, default)
1232
+ menu_blocks, menu_default_dname)
1328
1233
  return if block_state.state == MenuState::EXIT
1329
1234
 
1330
1235
  if block_state.block.nil?
1331
- warn_format('select_approve_and_execute_block', "Block not found -- #{@delegate_object[:block_name]}",
1236
+ warn_format('select_execute_bash_and_special_blocks', "Block not found -- #{@delegate_object[:block_name]}",
1332
1237
  { abort: true })
1333
1238
  # error_handler("Block not found -- #{opts[:block_name]}", { abort: true })
1334
1239
  end
@@ -1337,11 +1242,13 @@ module MarkdownExec
1337
1242
  warn block_state.block.to_yaml.sub(/^(?:---\n)?/, "Block:\n")
1338
1243
  end
1339
1244
 
1340
- # rbp
1341
- load_file_next_block = approve_and_execute_block(block_state.block,
1342
- mdoc)
1343
- default = load_file_next_block.load_file == LoadFile::Load ? nil : @delegate_object[:block_name]
1344
- # rbp
1245
+ load_file_next_block = execute_bash_and_special_blocks(block_state.block,
1246
+ mdoc)
1247
+ # if the same menu is being displayed,
1248
+ # collect the display name of the selected menu item
1249
+ # for use as the default item
1250
+ menu_default_dname = load_file_next_block.load_file == LoadFile::Load ? nil : block_state.block[:dname]
1251
+
1345
1252
  @menu_base_options[:block_name] =
1346
1253
  @delegate_object[:block_name] = load_file_next_block.next_block
1347
1254
  @menu_base_options[:filename] = @delegate_object[:filename]
@@ -1369,7 +1276,7 @@ module MarkdownExec
1369
1276
  @menu_state_block_name = @menu_base_options[:block_name]
1370
1277
  end
1371
1278
  rescue StandardError
1372
- error_handler('select_approve_and_execute_block',
1279
+ error_handler('select_execute_bash_and_special_blocks',
1373
1280
  { abort: true })
1374
1281
  end
1375
1282
 
@@ -1400,6 +1307,38 @@ module MarkdownExec
1400
1307
  error_handler('select_option_with_metadata')
1401
1308
  end
1402
1309
 
1310
+ def set_environment_variable(path)
1311
+ ENV['MDE_LINK_REQUIRED_FILE'] = path
1312
+ end
1313
+
1314
+ def set_environment_variables_for_block(selected)
1315
+ YAML.load(selected[:body].join("\n")).each do |key, value|
1316
+ ENV[key] = value.to_s
1317
+ next unless @delegate_object[:menu_vars_set_format].present?
1318
+
1319
+ formatted_string = format(@delegate_object[:menu_vars_set_format],
1320
+ { key: key, value: value })
1321
+ print string_send_color(formatted_string, :menu_vars_set_color)
1322
+ end
1323
+ end
1324
+
1325
+ def set_environment_variables_per_array(vars)
1326
+ vars ||= []
1327
+ vars.each { |key, value| ENV[key] = value.to_s }
1328
+ end
1329
+
1330
+ def set_file_permissions(file_path, chmod_value)
1331
+ File.chmod(chmod_value, file_path)
1332
+ end
1333
+
1334
+ def set_script_block_name(selected)
1335
+ @run_state.script_block_name = selected[:oname]
1336
+ end
1337
+
1338
+ def should_add_back_option?
1339
+ @delegate_object[:menu_with_back] && history_env_state_exist?
1340
+ end
1341
+
1403
1342
  # Initializes a new fenced code block (FCB) object based on the provided line and heading information.
1404
1343
  # @param line [String] The line initiating the fenced block.
1405
1344
  # @param headings [Array<String>] Current headings hierarchy.
@@ -1458,6 +1397,13 @@ module MarkdownExec
1458
1397
  symbols: { cross: ' ' }
1459
1398
  )
1460
1399
  end
1400
+ # private
1401
+
1402
+ def update_delegate_and_target(key, value, tgt2)
1403
+ sym_key = key.to_sym
1404
+ @delegate_object[sym_key] = value
1405
+ tgt2[sym_key] = value if tgt2
1406
+ end
1461
1407
 
1462
1408
  # Updates the hierarchy of document headings based on the given line.
1463
1409
  # Utilizes regular expressions to identify heading levels.
@@ -1555,6 +1501,25 @@ module MarkdownExec
1555
1501
  yield_to_block_if_applicable(fcb, selected_messages, &block)
1556
1502
  end
1557
1503
 
1504
+ # Processes YAML data from the selected menu item, updating delegate objects and optionally printing formatted output.
1505
+ # @param selected [Hash] Selected item from the menu containing a YAML body.
1506
+ # @param tgt2 [Hash, nil] An optional target hash to update with YAML data.
1507
+ # @return [LoadFileNextBlock] An instance indicating the next action for loading files.
1508
+ def update_options_and_trigger_reuse(selected, tgt2 = nil)
1509
+ data = YAML.load(selected[:body].join("\n"))
1510
+ (data || []).each do |key, value|
1511
+ update_delegate_and_target(key, value, tgt2)
1512
+ print_formatted_option(key, value) if @delegate_object[:menu_opts_set_format].present?
1513
+ end
1514
+ LoadFileNextBlock.new(LoadFile::Reuse, '')
1515
+ end
1516
+
1517
+ def wait_for_stream_processing
1518
+ @process_mutex.synchronize do
1519
+ @process_cv.wait(@process_mutex)
1520
+ end
1521
+ end
1522
+
1558
1523
  def wait_for_user_selected_block(all_blocks, menu_blocks, default)
1559
1524
  block_state = wait_for_user_selection(all_blocks, menu_blocks, default)
1560
1525
  handle_block_state(block_state)
@@ -1590,7 +1555,6 @@ module MarkdownExec
1590
1555
  def write_command_file(required_lines)
1591
1556
  return unless @delegate_object[:save_executed_script]
1592
1557
 
1593
- # rbp
1594
1558
  time_now = Time.now.utc
1595
1559
  @run_state.saved_script_filename =
1596
1560
  SavedAsset.script_name(blockname: @delegate_object[:block_name],
@@ -1622,6 +1586,10 @@ module MarkdownExec
1622
1586
  error_handler('write_command_file')
1623
1587
  end
1624
1588
 
1589
+ def write_command_file_if_needed(lines)
1590
+ write_command_file(lines) if @delegate_object[:save_executed_script]
1591
+ end
1592
+
1625
1593
  def write_execution_output_to_file
1626
1594
  FileUtils.mkdir_p File.dirname(@delegate_object[:logged_stdout_filespec])
1627
1595
 
@@ -1637,6 +1605,10 @@ module MarkdownExec
1637
1605
  )
1638
1606
  end
1639
1607
 
1608
+ def write_file_content(file_path, content)
1609
+ File.write(file_path, content)
1610
+ end
1611
+
1640
1612
  # Writes required code blocks to a temporary file and sets an environment variable with its path.
1641
1613
  #
1642
1614
  # @param mdoc [Object] The Markdown document object.
@@ -1658,6 +1630,10 @@ module MarkdownExec
1658
1630
  create_temp_file_with_code(code_blocks)
1659
1631
  end
1660
1632
 
1633
+ def write_to_file(path, content)
1634
+ File.write(path, content)
1635
+ end
1636
+
1661
1637
  # Yields a line as a new block if the selected message type includes :line.
1662
1638
  # @param [String] line The line to be processed.
1663
1639
  # @param [Array<Symbol>] selected_messages A list of message types to check.
@@ -1720,26 +1696,26 @@ if $PROGRAM_NAME == __FILE__
1720
1696
  end
1721
1697
 
1722
1698
  # Test case for empty body
1723
- def test_handle_link_block_with_empty_body
1699
+ def test_push_link_history_and_trigger_load_with_empty_body
1724
1700
  assert_equal LoadFileNextBlock.new(LoadFile::Reuse, ''),
1725
- @hd.handle_link_block([], nil, false)
1701
+ @hd.push_link_history_and_trigger_load([], nil, false)
1726
1702
  end
1727
1703
 
1728
1704
  # Test case for non-empty body without 'file' key
1729
- def test_handle_link_block_without_file_key
1705
+ def test_push_link_history_and_trigger_load_without_file_key
1730
1706
  body = ["vars:\n KEY: VALUE"]
1731
1707
  assert_equal LoadFileNextBlock.new(LoadFile::Reuse, ''),
1732
- @hd.handle_link_block(body, nil, false)
1708
+ @hd.push_link_history_and_trigger_load(body, nil, false)
1733
1709
  end
1734
1710
 
1735
1711
  # Test case for non-empty body with 'file' key
1736
- def test_handle_link_block_with_file_key
1712
+ def test_push_link_history_and_trigger_load_with_file_key
1737
1713
  body = ["file: sample_file\nblock: sample_block\nvars:\n KEY: VALUE"]
1738
1714
  expected_result = LoadFileNextBlock.new(LoadFile::Load,
1739
1715
  'sample_block')
1740
1716
  # mdoc = MDoc.new()
1741
1717
  assert_equal expected_result,
1742
- @hd.handle_link_block(body, nil, FCB.new)
1718
+ @hd.push_link_history_and_trigger_load(body, nil, FCB.new)
1743
1719
  end
1744
1720
 
1745
1721
  def test_history_env_state_exist_with_value
@@ -2261,11 +2237,11 @@ if $PROGRAM_NAME == __FILE__
2261
2237
  @hd.stubs(:history_state_pop)
2262
2238
  end
2263
2239
 
2264
- def test_handle_back_link
2240
+ def test_pop_link_history_and_trigger_load
2265
2241
  # Verifying that history_state_pop is called
2266
2242
  @hd.expects(:history_state_pop).once
2267
2243
 
2268
- result = @hd.handle_back_link
2244
+ result = @hd.pop_link_history_and_trigger_load
2269
2245
 
2270
2246
  # Asserting the result is an instance of LoadFileNextBlock
2271
2247
  assert_instance_of LoadFileNextBlock, result
@@ -2320,7 +2296,7 @@ if $PROGRAM_NAME == __FILE__
2320
2296
  @selected_item = mock('FCB')
2321
2297
  end
2322
2298
 
2323
- def test_handle_generic_block_without_user_approval
2299
+ def test_compile_execute_bash_and_special_blocks_and_trigger_reuse_without_user_approval
2324
2300
  # Mock the delegate object configuration
2325
2301
  @hd.instance_variable_set(:@delegate_object,
2326
2302
  { output_script: false,
@@ -2330,7 +2306,7 @@ if $PROGRAM_NAME == __FILE__
2330
2306
  # Expectations and assertions go here
2331
2307
  end
2332
2308
 
2333
- def test_handle_generic_block_with_user_approval
2309
+ def test_compile_execute_bash_and_special_blocks_and_trigger_reuse_with_user_approval
2334
2310
  # Mock the delegate object configuration
2335
2311
  @hd.instance_variable_set(:@delegate_object,
2336
2312
  { output_script: false,
@@ -2340,7 +2316,7 @@ if $PROGRAM_NAME == __FILE__
2340
2316
  # Expectations and assertions go here
2341
2317
  end
2342
2318
 
2343
- def test_handle_generic_block_with_output_script
2319
+ def test_compile_execute_bash_and_special_blocks_and_trigger_reuse_with_output_script
2344
2320
  # Mock the delegate object configuration
2345
2321
  @hd.instance_variable_set(:@delegate_object,
2346
2322
  { output_script: true,
@@ -2361,11 +2337,11 @@ if $PROGRAM_NAME == __FILE__
2361
2337
  @hd.stubs(:print)
2362
2338
  end
2363
2339
 
2364
- def test_handle_opts_block
2340
+ def test_update_options_and_trigger_reuse
2365
2341
  selected = { body: ['option1: value1'] }
2366
2342
  tgt2 = {}
2367
2343
 
2368
- result = @hd.handle_opts_block(selected, tgt2)
2344
+ result = @hd.update_options_and_trigger_reuse(selected, tgt2)
2369
2345
 
2370
2346
  assert_instance_of LoadFileNextBlock, result
2371
2347
  assert_equal 'value1',
@@ -2373,11 +2349,11 @@ if $PROGRAM_NAME == __FILE__
2373
2349
  assert_equal 'value1', tgt2[:option1]
2374
2350
  end
2375
2351
 
2376
- def test_handle_opts_block_without_format
2352
+ def test_update_options_and_trigger_reuse_without_format
2377
2353
  selected = { body: ['option2: value2'] }
2378
2354
  @hd.instance_variable_set(:@delegate_object, {})
2379
2355
 
2380
- result = @hd.handle_opts_block(selected)
2356
+ result = @hd.update_options_and_trigger_reuse(selected)
2381
2357
 
2382
2358
  assert_instance_of LoadFileNextBlock, result
2383
2359
  assert_equal 'value2',
@@ -2544,7 +2520,7 @@ if $PROGRAM_NAME == __FILE__
2544
2520
  def setup
2545
2521
  @hd = HashDelegator.new
2546
2522
  @hd.stubs(:block_find).returns({})
2547
- @hd.stubs(:handle_opts_block)
2523
+ @hd.stubs(:update_options_and_trigger_reuse)
2548
2524
  end
2549
2525
 
2550
2526
  def test_load_auto_blocks_with_new_filename