markdown_exec 1.8.1 → 1.8.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -24,6 +24,7 @@ require_relative 'fcb'
24
24
  require_relative 'filter'
25
25
  require_relative 'fout'
26
26
  require_relative 'hash'
27
+ require_relative 'link_history'
27
28
  require_relative 'mdoc'
28
29
  require_relative 'string_util'
29
30
 
@@ -115,6 +116,7 @@ module MarkdownExec
115
116
  @run_state = OpenStruct.new(
116
117
  link_history: []
117
118
  )
119
+ @link_history = LinkHistory.new
118
120
  @fout = FOut.new(@delegate_object) ### slice only relevant keys
119
121
 
120
122
  @process_mutex = Mutex.new
@@ -145,10 +147,6 @@ module MarkdownExec
145
147
 
146
148
  private
147
149
 
148
- def should_add_back_option?
149
- @delegate_object[:menu_with_back] && history_env_state_exist?
150
- end
151
-
152
150
  def add_back_option(menu_blocks)
153
151
  append_chrome_block(menu_blocks, MenuState::BACK)
154
152
  end
@@ -171,9 +169,7 @@ module MarkdownExec
171
169
  def append_chrome_block(menu_blocks, type)
172
170
  case type
173
171
  when MenuState::BACK
174
- state = history_state_partition
175
- @hs_curr = state[:unit]
176
- @hs_rest = state[:rest]
172
+ history_state_partition
177
173
  option_name = @delegate_object[:menu_option_back_name]
178
174
  insert_at_top = @delegate_object[:menu_back_at_top]
179
175
  when MenuState::EXIT
@@ -185,13 +181,9 @@ module MarkdownExec
185
181
  safeval(option_name))
186
182
  chrome_block = FCB.new(
187
183
  chrome: true,
188
-
189
- # dname: formatted_name.send(@delegate_object[:menu_link_color].to_sym),
190
184
  dname: HashDelegator.new(@delegate_object).string_send_color(
191
185
  formatted_name, :menu_link_color
192
186
  ),
193
- #dname: @delegate_object.string_send_color(formatted_name, :menu_link_color),
194
-
195
187
  oname: formatted_name
196
188
  )
197
189
 
@@ -216,42 +208,16 @@ module MarkdownExec
216
208
 
217
209
  # private
218
210
 
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.
211
+ # Applies shell color options to the given string if applicable.
245
212
  #
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)
213
+ # @param name [String] The name to potentially colorize.
214
+ # @param shell_color_option [Symbol, nil] The shell color option to apply.
215
+ # @return [String] The colorized or original name string.
216
+ def apply_shell_color_option(name, shell_color_option)
217
+ if shell_color_option && @delegate_object[shell_color_option].present?
218
+ string_send_color(name, shell_color_option)
253
219
  else
254
- handle_generic_block(mdoc, selected)
220
+ name
255
221
  end
256
222
  end
257
223
 
@@ -286,22 +252,6 @@ module MarkdownExec
286
252
 
287
253
  # private
288
254
 
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
255
  def cfile
306
256
  @cfile ||= CachedNestedFileReader.new(
307
257
  import_pattern: @delegate_object.fetch(:import_pattern) #, "^ *@import +(?<name>.+?) *$")
@@ -322,23 +272,13 @@ module MarkdownExec
322
272
  true
323
273
  end
324
274
 
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?
275
+ def code_join(*bodies)
276
+ bc = bodies&.compact
277
+ bc.count.positive? ? bc.join("\n") : nil
278
+ end
340
279
 
341
- exit @delegate_object[exception_sym]
280
+ def code_merge(*bodies)
281
+ merge_lists(*bodies)
342
282
  end
343
283
 
344
284
  # Collects required code lines based on the selected block and the delegate object's configuration.
@@ -347,15 +287,20 @@ module MarkdownExec
347
287
  # @param mdoc [YourMDocClass] An instance of the MDoc class.
348
288
  # @param selected [Hash] The selected block.
349
289
  # @return [Array<String>] Required code blocks as an array of lines.
350
- def collect_required_code_lines(mdoc, selected)
351
- set_environment_variables(selected) if selected[:shell] == BlockType::VARS
290
+ def collect_required_code_lines(mdoc, selected, link_state = LinkState.new, block_source:)
291
+ set_environment_variables_for_block(selected) if selected[:shell] == BlockType::VARS
352
292
 
353
293
  required = mdoc.collect_recursively_required_code(
354
294
  @delegate_object[:block_name],
355
295
  label_format_above: @delegate_object[:shell_code_label_format_above],
356
- label_format_below: @delegate_object[:shell_code_label_format_below]
296
+ label_format_below: @delegate_object[:shell_code_label_format_below],
297
+ block_source: block_source
357
298
  )
299
+ required[:unmet_dependencies] =
300
+ (required[:unmet_dependencies] || []) - (link_state&.inherited_block_names || [])
358
301
  if required[:unmet_dependencies].present?
302
+ ### filter against link_state.inherited_block_names
303
+
359
304
  warn format_and_highlight_dependencies(required[:dependencies],
360
305
  highlight: required[:unmet_dependencies])
361
306
  runtime_exception(:runtime_exception_error_level,
@@ -364,27 +309,14 @@ module MarkdownExec
364
309
  warn format_and_highlight_dependencies(required[:dependencies],
365
310
  highlight: [@delegate_object[:block_name]])
366
311
  end
367
- read_required_blocks_from_temp_file + required[:code]
368
- end
369
-
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
312
 
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
313
+ code_merge link_state&.inherited_lines, required[:code]
381
314
  end
382
315
 
383
316
  def command_execute(command, args: [])
384
317
  @run_state.files = Hash.new([])
385
318
  @run_state.options = @delegate_object
386
319
  @run_state.started_at = Time.now.utc
387
- # rbp
388
320
 
389
321
  Open3.popen3(@delegate_object[:shell],
390
322
  '-c', command,
@@ -442,6 +374,26 @@ module MarkdownExec
442
374
  error_handler('command_or_user_selected_block')
443
375
  end
444
376
 
377
+ # This method is responsible for handling the execution of generic blocks in a markdown document.
378
+ # It collects the required code lines from the document and, depending on the configuration,
379
+ # may display the code for user approval before execution. It then executes the approved block.
380
+ #
381
+ # @param mdoc [Object] The markdown document object containing code blocks.
382
+ # @param selected [Hash] The selected item from the menu to be executed.
383
+ # @return [LoadFileLinkState] An object indicating whether to load the next block or reuse the current one.
384
+ def compile_execute_bash_and_special_blocks_and_trigger_reuse(mdoc, selected,
385
+ link_state = nil, block_source:)
386
+ required_lines = collect_required_code_lines(mdoc, selected, link_state,
387
+ block_source: block_source)
388
+ output_or_approval = @delegate_object[:output_script] || @delegate_object[:user_must_approve]
389
+ display_required_code(required_lines) if output_or_approval
390
+ allow_execution = @delegate_object[:user_must_approve] ? prompt_for_user_approval(required_lines) : true
391
+ execute_required_lines(required_lines) if allow_execution
392
+
393
+ link_state.block_name = nil
394
+ LoadFileLinkState.new(LoadFile::Reuse, link_state)
395
+ end
396
+
445
397
  def copy_to_clipboard(required_lines)
446
398
  text = required_lines.flatten.join($INPUT_RECORD_SEPARATOR)
447
399
  Clipboard.copy(text)
@@ -520,13 +472,13 @@ module MarkdownExec
520
472
  # @param file_path [String] The path where the file will be created.
521
473
  # @param content [String] The content to write into the file.
522
474
  # @param chmod_value [Integer] The file permission value to set; skips if zero.
523
- def create_and_write_file_with_permissions(file_path, content,
524
- chmod_value)
475
+ def create_file_and_write_string_with_permissions(file_path, content,
476
+ chmod_value)
525
477
  create_directory_for_file(file_path)
526
- write_file_content(file_path, content)
478
+ File.write(file_path, content)
527
479
  set_file_permissions(file_path, chmod_value) unless chmod_value.zero?
528
480
  rescue StandardError
529
- error_handler('create_and_write_file_with_permissions')
481
+ error_handler('create_file_and_write_string_with_permissions')
530
482
  end
531
483
 
532
484
  # private
@@ -535,21 +487,17 @@ module MarkdownExec
535
487
  FileUtils.mkdir_p(File.dirname(file_path))
536
488
  end
537
489
 
538
- def write_file_content(file_path, content)
539
- File.write(file_path, content)
540
- end
541
-
542
- def set_file_permissions(file_path, chmod_value)
543
- File.chmod(chmod_value, file_path)
544
- end
490
+ def create_divider(position)
491
+ divider_key = position == :initial ? :menu_initial_divider : :menu_final_divider
492
+ oname = format(@delegate_object[:menu_divider_format],
493
+ safeval(@delegate_object[divider_key]))
545
494
 
546
- # Creates a temporary file, writes the provided code blocks into it,
547
- # and sets an environment variable with the file path.
548
- # @param code_blocks [String] Code blocks to write into the file.
549
- def create_temp_file_with_code(code_blocks)
550
- temp_file_path = create_temp_file
551
- write_to_file(temp_file_path, code_blocks)
552
- set_environment_variable(temp_file_path)
495
+ FCB.new(
496
+ chrome: true,
497
+ disabled: '',
498
+ dname: string_send_color(oname, :menu_divider_color),
499
+ oname: oname
500
+ )
553
501
  end
554
502
 
555
503
  # private
@@ -558,14 +506,6 @@ module MarkdownExec
558
506
  Dir::Tmpname.create(self.class.to_s) { |path| path }
559
507
  end
560
508
 
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
509
  # Updates the title of an FCB object from its body content if the title is nil or empty.
570
510
  def default_block_title_from_body(fcb)
571
511
  return unless fcb.title.nil? || fcb.title.empty?
@@ -584,27 +524,14 @@ module MarkdownExec
584
524
  # Deletes a temporary file specified by an environment variable.
585
525
  # Checks if the file exists before attempting to delete it and clears the environment variable afterward.
586
526
  # Any errors encountered during deletion are handled gracefully.
587
- def delete_required_temp_file
588
- temp_blocks_file_path = fetch_temp_blocks_file_path
589
-
527
+ def delete_required_temp_file(temp_blocks_file_path)
590
528
  return if temp_blocks_file_path.nil? || temp_blocks_file_path.empty?
591
529
 
592
530
  safely_remove_file(temp_blocks_file_path)
593
- clear_required_file
594
531
  rescue StandardError
595
532
  error_handler('delete_required_temp_file')
596
533
  end
597
534
 
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
535
  # Determines the state of a selected block in the menu based on the selected option.
609
536
  # It categorizes the selected option into either EXIT, BACK, or CONTINUE state.
610
537
  #
@@ -636,12 +563,9 @@ module MarkdownExec
636
563
  :script_preview_frame_color)
637
564
  end
638
565
 
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
566
+ def divider_formatting_present?(position)
567
+ divider_key = position == :initial ? :menu_initial_divider : :menu_final_divider
568
+ @delegate_object[:menu_divider_format].present? && @delegate_object[divider_key].present?
645
569
  end
646
570
 
647
571
  def error_handler(name = '', opts = {})
@@ -659,36 +583,38 @@ module MarkdownExec
659
583
  #
660
584
  # @param required_lines [Array<String>] The lines of code to be executed.
661
585
  # @param selected [FCB] The selected functional code block object.
662
- def execute_approved_block(required_lines = [], _selected = FCB.new)
586
+ def execute_required_lines(required_lines = [])
663
587
  # set_script_block_name(selected)
664
- write_command_file_if_needed(required_lines)
588
+ save_executed_script_if_specified(required_lines)
665
589
  format_and_execute_command(required_lines)
666
590
  post_execution_process
667
591
  end
668
592
 
669
- # private
670
-
671
- def set_script_block_name(selected)
672
- @run_state.script_block_name = selected[:oname]
673
- end
593
+ # Execute a code block after approval and provide user interaction options.
594
+ #
595
+ # This method displays required code blocks, asks for user approval, and
596
+ # executes the code block if approved. It also allows users to copy the
597
+ # code to the clipboard or save it to a file.
598
+ #
599
+ # @param opts [Hash] Options hash containing configuration settings.
600
+ # @param mdoc [YourMDocClass] An instance of the MDoc class.
601
+ #
602
+ def execute_bash_and_special_blocks(selected, mdoc, link_state = LinkState.new,
603
+ block_source:)
604
+ if selected.fetch(:shell, '') == BlockType::LINK
605
+ push_link_history_and_trigger_load(selected.fetch(:body, ''), mdoc, selected,
606
+ link_state)
674
607
 
675
- def write_command_file_if_needed(lines)
676
- write_command_file(lines) if @delegate_object[:save_executed_script]
677
- end
608
+ elsif @menu_user_clicked_back_link
609
+ pop_link_history_and_trigger_load
678
610
 
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
611
+ elsif selected[:shell] == BlockType::OPTS
612
+ update_options_and_trigger_reuse(selected, @menu_base_options, link_state)
687
613
 
688
- def post_execution_process
689
- initialize_and_save_execution_output
690
- output_execution_summary
691
- output_execution_result
614
+ else
615
+ compile_execute_bash_and_special_blocks_and_trigger_reuse(mdoc, selected, link_state,
616
+ block_source: block_source)
617
+ end
692
618
  end
693
619
 
694
620
  # Retrieves a specific data symbol from the delegate object, converts it to a string,
@@ -705,6 +631,26 @@ module MarkdownExec
705
631
  string_send_color(data_string, color_sym)
706
632
  end
707
633
 
634
+ # DebugHelper.d ["HDmm method_name: #{method_name}", "#{first_n_caller_items 1}"]
635
+ def first_n_caller_items(n)
636
+ call_stack = caller
637
+ base_path = File.realpath('.')
638
+
639
+ # Modify the call stack to remove the base path and keep only the first n items
640
+ call_stack.take(n + 1)[1..].map do |line|
641
+ " . #{line.sub(/^#{Regexp.escape(base_path)}\//, '')}"
642
+ end.join("\n")
643
+ end
644
+
645
+ def format_and_execute_command(lines)
646
+ formatted_command = lines.flatten.join("\n")
647
+ @fout.fout fetch_color(data_sym: :script_execution_head,
648
+ color_sym: :script_execution_frame_color)
649
+ command_execute(formatted_command, args: @pass_args)
650
+ @fout.fout fetch_color(data_sym: :script_execution_tail,
651
+ color_sym: :script_execution_frame_color)
652
+ end
653
+
708
654
  # Formats a string based on a given context and applies color styling to it.
709
655
  # It retrieves format and color information from the delegate object and processes accordingly.
710
656
  #
@@ -756,124 +702,21 @@ module MarkdownExec
756
702
  fcb
757
703
  end
758
704
 
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
705
  # Updates the delegate object's state based on the provided block state.
785
706
  # It sets the block name and determines if the user clicked the back link in the menu.
786
707
  #
787
708
  # @param block_state [Object] An object representing the state of a block in the menu.
788
709
  def handle_block_state(block_state)
710
+ return if block_state.nil?
789
711
  unless [MenuState::BACK,
790
712
  MenuState::CONTINUE].include?(block_state.state)
791
713
  return
792
714
  end
793
715
 
794
716
  @delegate_object[:block_name] = block_state.block[:oname]
795
- # rbp
796
717
  @menu_user_clicked_back_link = block_state.state == MenuState::BACK
797
718
  end
798
719
 
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
862
-
863
- # private
864
-
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
869
- end
870
-
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)
875
- end
876
-
877
720
  def handle_stream(stream, file_type, swap: false)
878
721
  @process_mutex.synchronize do
879
722
  Thread.new do
@@ -896,52 +739,6 @@ module MarkdownExec
896
739
  end
897
740
  end
898
741
 
899
- def wait_for_stream_processing
900
- @process_mutex.synchronize do
901
- @process_cv.wait(@process_mutex)
902
- end
903
- end
904
-
905
- # Partitions the history state from the environment variable based on the document separator.
906
- # @return [Hash] A hash containing two parts: :unit (first part) and :rest (remaining part).
907
- def history_state_partition
908
- history_env_value = ENV.fetch(MDE_HISTORY_ENV_NAME, '')
909
- separator = @delegate_object[:history_document_separator]
910
-
911
- unit, rest = StringUtil.partition_at_first(history_env_value, separator)
912
- { unit: unit, rest: rest }
913
- end
914
-
915
- # Pops the last entry from the history state, updating the delegate object and environment variable.
916
- # It also deletes the required temporary file and updates the run state link history.
917
- def history_state_pop
918
- state = history_state_partition
919
- @delegate_object[:filename] = state[:unit]
920
- ENV[MDE_HISTORY_ENV_NAME] = state[:rest]
921
- delete_required_temp_file
922
- @run_state.link_history.pop
923
- end
924
-
925
- # Updates the history state by pushing a new entry and managing environment variables.
926
- # @param mdoc [Object] The Markdown document object.
927
- # @param data_file [String] The data file to be processed.
928
- # @param selected [Hash] Hash containing the selected block's name.
929
- def history_state_push(mdoc, data_file, selected)
930
- # Construct new history string
931
- new_history = [@delegate_object[:filename],
932
- @delegate_object[:history_document_separator],
933
- ENV.fetch(MDE_HISTORY_ENV_NAME, '')].join
934
-
935
- # Update delegate object and environment variable
936
- @delegate_object[:filename] = data_file
937
- ENV[MDE_HISTORY_ENV_NAME] = new_history
938
-
939
- # Write required blocks to temp file and update run state
940
- write_required_blocks_to_temp_file(mdoc, @delegate_object[:block_name])
941
- @run_state.link_history.push(block_name: selected[:oname],
942
- filename: data_file)
943
- end
944
-
945
742
  # Indents all lines in a given string with a specified indentation string.
946
743
  # @param body [String] A multi-line string to be indented.
947
744
  # @param indent [String] The string used for indentation (default is an empty string).
@@ -952,10 +749,6 @@ module MarkdownExec
952
749
  body.lines.map { |line| indent + line.chomp }.join("\n")
953
750
  end
954
751
 
955
- def initialize_fcb_names(fcb)
956
- fcb.oname = fcb.dname = fcb.title || ''
957
- end
958
-
959
752
  # Initializes variables for regex and other states
960
753
  def initial_state
961
754
  {
@@ -989,6 +782,10 @@ module MarkdownExec
989
782
  write_execution_output_to_file
990
783
  end
991
784
 
785
+ def initialize_fcb_names(fcb)
786
+ fcb.oname = fcb.dname = fcb.title || ''
787
+ end
788
+
992
789
  # Iterates through blocks in a file, applying the provided block to each line.
993
790
  # The iteration only occurs if the file exists.
994
791
  # @yield [Symbol] :filter Yields to obtain selected messages for processing.
@@ -1006,6 +803,31 @@ module MarkdownExec
1006
803
  end
1007
804
  end
1008
805
 
806
+ def link_history_push_and_next(
807
+ curr_block_name:, curr_document_filename:,
808
+ inherited_block_names:, inherited_lines:,
809
+ next_block_name:, next_document_filename:,
810
+ next_load_file:
811
+ )
812
+ @link_history.push(
813
+ LinkState.new(
814
+ block_name: curr_block_name,
815
+ document_filename: curr_document_filename,
816
+ inherited_block_names: inherited_block_names,
817
+ inherited_lines: inherited_lines
818
+ )
819
+ )
820
+ LoadFileLinkState.new(
821
+ next_load_file,
822
+ LinkState.new(
823
+ block_name: next_block_name,
824
+ document_filename: next_document_filename,
825
+ inherited_block_names: inherited_block_names,
826
+ inherited_lines: inherited_lines
827
+ )
828
+ )
829
+ end
830
+
1009
831
  # Loads auto blocks based on delegate object settings and updates if new filename is detected.
1010
832
  # Executes a specified block once per filename.
1011
833
  # @param all_blocks [Array] Array of all block elements.
@@ -1019,46 +841,11 @@ module MarkdownExec
1019
841
  block = block_find(all_blocks, :oname, block_name)
1020
842
  return unless block
1021
843
 
1022
- handle_opts_block(block, @delegate_object)
844
+ update_options_and_trigger_reuse(block, @delegate_object)
1023
845
  @most_recent_loaded_filename = @delegate_object[:filename]
1024
846
  true
1025
847
  end
1026
848
 
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
849
  def mdoc_and_blocks_from_nested_files
1063
850
  menu_blocks = blocks_from_nested_files
1064
851
  mdoc = MDoc.new(menu_blocks) do |nopts|
@@ -1074,9 +861,7 @@ module MarkdownExec
1074
861
 
1075
862
  # recreate menu with new options
1076
863
  #
1077
- if load_auto_blocks(all_blocks)
1078
- all_blocks, mdoc = mdoc_and_blocks_from_nested_files
1079
- end
864
+ all_blocks, mdoc = mdoc_and_blocks_from_nested_files if load_auto_blocks(all_blocks)
1080
865
 
1081
866
  menu_blocks = mdoc.fcbs_per_options(@delegate_object)
1082
867
  add_menu_chrome_blocks!(menu_blocks)
@@ -1104,16 +889,41 @@ module MarkdownExec
1104
889
  if @delegate_object[:menu_chrome_format]
1105
890
  format(@delegate_object[:menu_chrome_format], option_value)
1106
891
  else
1107
- option_value
892
+ option_value
893
+ end
894
+ end
895
+
896
+ def merge_lists(*args)
897
+ # Filters out nil values, flattens the arrays, and ensures an empty list is returned if no valid lists are provided
898
+ merged = args.compact.flatten
899
+ merged.empty? ? [] : merged
900
+ end
901
+
902
+ # If a method is missing, treat it as a key for the @delegate_object.
903
+ def method_missing(method_name, *args, &block)
904
+ if @delegate_object.respond_to?(method_name)
905
+ @delegate_object.send(method_name, *args, &block)
906
+ elsif method_name.to_s.end_with?('=') && args.size == 1
907
+ @delegate_object[method_name.to_s.chop.to_sym] = args.first
908
+ else
909
+ @delegate_object[method_name]
910
+ # super
1108
911
  end
1109
912
  end
1110
913
 
1111
- def next_block_name_from_command_line_arguments
1112
- return MenuControl::Repeat unless @delegate_object[:input_cli_rest].present?
914
+ def pop_cli_argument!
915
+ return false unless @delegate_object[:input_cli_rest].present?
916
+
917
+ @cli_block_name = @delegate_object[:input_cli_rest].pop
918
+ true
919
+ end
920
+
921
+ # private
1113
922
 
1114
- # rbp
1115
- @delegate_object[:block_name] = @delegate_object[:input_cli_rest].pop
1116
- MenuControl::Fresh
923
+ def output_color_formatted(data_sym, color_sym)
924
+ formatted_string = string_send_color(@delegate_object[data_sym],
925
+ color_sym)
926
+ @fout.fout formatted_string
1117
927
  end
1118
928
 
1119
929
  def output_execution_result
@@ -1162,13 +972,38 @@ module MarkdownExec
1162
972
  ), level: level
1163
973
  end
1164
974
 
975
+ # private
976
+
977
+ def parse_yaml_data_from_body(body)
978
+ body.any? ? YAML.load(body.join("\n")) : {}
979
+ end
980
+
981
+ # This method handles the back-link operation in the Markdown execution context.
982
+ # It updates the history state and prepares to load the next block.
983
+ #
984
+ # @return [LoadFileLinkState] An object indicating the action to load the next block.
985
+ def pop_link_history_and_trigger_load
986
+ pop = @link_history.pop
987
+ peek = @link_history.peek
988
+ LoadFileLinkState.new(LoadFile::Load, LinkState.new(
989
+ document_filename: pop.document_filename,
990
+ inherited_block_names: peek.inherited_block_names,
991
+ inherited_lines: peek.inherited_lines
992
+ ))
993
+ end
994
+
995
+ def post_execution_process
996
+ initialize_and_save_execution_output
997
+ output_execution_summary
998
+ output_execution_result
999
+ end
1000
+
1165
1001
  # Prepare the blocks menu by adding labels and other necessary details.
1166
1002
  #
1167
1003
  # @param all_blocks [Array<Hash>] The list of blocks from the file.
1168
1004
  # @param opts [Hash] The options hash.
1169
1005
  # @return [Array<Hash>] The updated blocks menu.
1170
1006
  def prepare_blocks_menu(menu_blocks)
1171
- ### replace_consecutive_blanks(menu_blocks).map do |fcb|
1172
1007
  menu_blocks.map do |fcb|
1173
1008
  next if Filter.prepared_not_in_menu?(@delegate_object, fcb,
1174
1009
  %i[block_name_include_match block_name_wrapper_match])
@@ -1189,6 +1024,28 @@ module MarkdownExec
1189
1024
  end.compact
1190
1025
  end
1191
1026
 
1027
+ def print_formatted_option(key, value)
1028
+ formatted_str = format(@delegate_object[:menu_opts_set_format],
1029
+ { key: key, value: value })
1030
+ print string_send_color(formatted_str, :menu_opts_set_color)
1031
+ end
1032
+
1033
+ # private
1034
+
1035
+ def process_block_based_on_type(blocks, btype, fcb)
1036
+ case btype
1037
+ when :blocks
1038
+ blocks.push(get_block_summary(fcb))
1039
+ when :filter
1040
+ %i[blocks line]
1041
+ when :line
1042
+ unless @delegate_object[:no_chrome]
1043
+ create_and_add_chrome_blocks(blocks,
1044
+ fcb)
1045
+ end
1046
+ end
1047
+ end
1048
+
1192
1049
  ##
1193
1050
  # Presents a menu to the user for approving an action and performs additional tasks based on the selection.
1194
1051
  # The function provides options for approval, rejection, copying data to clipboard, or saving data to a file.
@@ -1247,17 +1104,57 @@ module MarkdownExec
1247
1104
  exit 1
1248
1105
  end
1249
1106
 
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
1107
  # public
1256
1108
 
1109
+ # Handles the processing of a link block in Markdown Execution.
1110
+ # It loads YAML data from the link_block_body content, pushes the state to history,
1111
+ # sets environment variables, and decides on the next block to load.
1112
+ #
1113
+ # @param link_block_body [Array<String>] The body content as an array of strings.
1114
+ # @param mdoc [Object] Markdown document object.
1115
+ # @param selected [FCB] Selected code block.
1116
+ # @return [LoadFileLinkState] Object indicating the next action for file loading.
1117
+ def push_link_history_and_trigger_load(link_block_body, mdoc, selected,
1118
+ link_state = LinkState.new)
1119
+ link_block_data = parse_yaml_data_from_body(link_block_body)
1120
+
1121
+ # load key and values from link block into current environment
1122
+ #
1123
+ (link_block_data['vars'] || []).each do |(key, value)|
1124
+ ENV[key] = value.to_s
1125
+ end
1126
+
1127
+ ## collect blocks specified by block
1128
+ #
1129
+ if mdoc
1130
+ code_info = mdoc.collect_recursively_required_code(
1131
+ selected[:oname],
1132
+ label_format_above: @delegate_object[:shell_code_label_format_above],
1133
+ label_format_below: @delegate_object[:shell_code_label_format_below],
1134
+ block_source: { document_filename: link_state.document_filename }
1135
+ )
1136
+ code_lines = code_info[:code]
1137
+ block_names = code_info[:block_names]
1138
+ else
1139
+ block_names = []
1140
+ code_lines = []
1141
+ end
1142
+
1143
+ next_document_filename = link_block_data['file'] || @delegate_object[:filename]
1144
+ link_history_push_and_next(
1145
+ curr_block_name: selected[:oname],
1146
+ curr_document_filename: @delegate_object[:filename],
1147
+ inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
1148
+ inherited_lines: code_merge(link_state&.inherited_lines, code_lines),
1149
+ next_block_name: link_block_data['block'] || '',
1150
+ next_document_filename: next_document_filename,
1151
+ next_load_file: next_document_filename == @delegate_object[:filename] ? LoadFile::Reuse : LoadFile::Load
1152
+ )
1153
+ end
1154
+
1257
1155
  # Reads required code blocks from a temporary file specified by an environment variable.
1258
1156
  # @return [Array<String>] Lines read from the temporary file, or an empty array if file is not found or path is empty.
1259
- def read_required_blocks_from_temp_file
1260
- temp_blocks_file_path = ENV.fetch('MDE_LINK_REQUIRED_FILE', nil)
1157
+ def read_required_blocks_from_temp_file(temp_blocks_file_path)
1261
1158
  return [] if temp_blocks_file_path.to_s.empty?
1262
1159
 
1263
1160
  if File.exist?(temp_blocks_file_path)
@@ -1269,6 +1166,29 @@ module MarkdownExec
1269
1166
  end
1270
1167
  end
1271
1168
 
1169
+ def runtime_exception(exception_sym, name, items)
1170
+ if @delegate_object[exception_sym] != 0
1171
+ data = { name: name, detail: items.join(', ') }
1172
+ warn(
1173
+ format(
1174
+ @delegate_object.fetch(:exception_format_name, "\n%{name}"),
1175
+ data
1176
+ ).send(@delegate_object.fetch(:exception_color_name, :red)) +
1177
+ format(
1178
+ @delegate_object.fetch(:exception_format_detail, " - %{detail}\n"),
1179
+ data
1180
+ ).send(@delegate_object.fetch(:exception_color_detail, :yellow))
1181
+ )
1182
+ end
1183
+ return unless (@delegate_object[exception_sym]).positive?
1184
+
1185
+ exit @delegate_object[exception_sym]
1186
+ end
1187
+
1188
+ def safely_remove_file(path)
1189
+ FileUtils.rm_f(path)
1190
+ end
1191
+
1272
1192
  # Evaluates the given string as Ruby code and rescues any StandardErrors.
1273
1193
  # If an error occurs, it calls the error_handler method with 'safeval'.
1274
1194
  # @param str [String] The string to be evaluated.
@@ -1279,11 +1199,10 @@ module MarkdownExec
1279
1199
  error_handler('safeval')
1280
1200
  end
1281
1201
 
1282
- # def safeval(str)
1283
- # eval(str)
1284
- # rescue StandardError
1285
- # error_handler('safeval')
1286
- # end
1202
+ def save_to_file(required_lines)
1203
+ write_command_file(required_lines)
1204
+ @fout.fout "File saved: #{@run_state.saved_filespec}"
1205
+ end
1287
1206
 
1288
1207
  # Select and execute a code block from a Markdown document.
1289
1208
  #
@@ -1291,26 +1210,27 @@ module MarkdownExec
1291
1210
  # Markdown document, obtain approval, and execute the chosen block of code.
1292
1211
  #
1293
1212
  # @return [Nil] Returns nil if no code block is selected or an error occurs.
1294
- def select_approve_and_execute_block(_execute: true)
1213
+ def select_execute_bash_and_special_blocks(_execute: true)
1295
1214
  @menu_base_options = @delegate_object
1296
- repeat_menu = @menu_base_options[:block_name].present? ? MenuControl::Fresh : MenuControl::Repeat
1297
- load_file_next_block = LoadFileNextBlock.new(LoadFile::Reuse)
1298
- default = nil
1299
-
1300
- @menu_state_filename = @menu_base_options[:filename]
1301
- @menu_state_block_name = @menu_base_options[:block_name]
1215
+ link_state = LinkState.new(
1216
+ block_name: @delegate_object[:block_name],
1217
+ document_filename: @delegate_object[:filename]
1218
+ )
1219
+ block_name_from_cli = link_state.block_name.present?
1220
+ @cli_block_name = link_state.block_name
1221
+ load_file = nil
1222
+ menu_default_dname = nil
1302
1223
 
1303
1224
  loop do
1304
1225
  loop do
1305
1226
  @delegate_object = @menu_base_options.dup
1306
- @menu_base_options[:filename] = @menu_state_filename
1307
- # rbp
1308
- @menu_base_options[:block_name] = @menu_state_block_name
1309
- @menu_state_filename = nil
1310
- @menu_state_block_name = nil
1311
1227
  @menu_user_clicked_back_link = false
1228
+ @delegate_object[:filename] = link_state.document_filename
1229
+ link_state.block_name = @delegate_object[:block_name] =
1230
+ block_name_from_cli ? @cli_block_name : link_state.block_name
1312
1231
 
1313
1232
  blocks_in_file, menu_blocks, mdoc = mdoc_menu_and_blocks_from_nested_files
1233
+
1314
1234
  if @delegate_object[:dump_blocks_in_file]
1315
1235
  warn format_and_highlight_dependencies(
1316
1236
  compact_and_index_hash(blocks_in_file),
@@ -1323,12 +1243,13 @@ module MarkdownExec
1323
1243
  label: 'menu_blocks'
1324
1244
  )
1325
1245
  end
1246
+
1326
1247
  block_state = command_or_user_selected_block(blocks_in_file,
1327
- menu_blocks, default)
1248
+ menu_blocks, menu_default_dname)
1328
1249
  return if block_state.state == MenuState::EXIT
1329
1250
 
1330
1251
  if block_state.block.nil?
1331
- warn_format('select_approve_and_execute_block', "Block not found -- #{@delegate_object[:block_name]}",
1252
+ warn_format('select_execute_bash_and_special_blocks', "Block not found -- #{@delegate_object[:block_name]}",
1332
1253
  { abort: true })
1333
1254
  # error_handler("Block not found -- #{opts[:block_name]}", { abort: true })
1334
1255
  end
@@ -1337,18 +1258,20 @@ module MarkdownExec
1337
1258
  warn block_state.block.to_yaml.sub(/^(?:---\n)?/, "Block:\n")
1338
1259
  end
1339
1260
 
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
1345
- @menu_base_options[:block_name] =
1346
- @delegate_object[:block_name] = load_file_next_block.next_block
1347
- @menu_base_options[:filename] = @delegate_object[:filename]
1261
+ load_file_link_state = execute_bash_and_special_blocks(
1262
+ block_state.block,
1263
+ mdoc,
1264
+ link_state,
1265
+ block_source: { document_filename: @delegate_object[:filename] }
1266
+ )
1267
+ load_file = load_file_link_state.load_file
1268
+ link_state = load_file_link_state.link_state
1269
+ # if the same menu is being displayed, collect the display name of the selected menu item for use as the default item
1270
+ menu_default_dname = load_file == LoadFile::Load ? nil : block_state.block[:dname]
1348
1271
 
1349
1272
  # user prompt to exit if the menu will be displayed again
1350
1273
  #
1351
- if repeat_menu == MenuControl::Repeat &&
1274
+ if !block_name_from_cli &&
1352
1275
  block_state.block[:shell] == BlockType::BASH &&
1353
1276
  @delegate_object[:pause_after_script_execution] &&
1354
1277
  prompt_select_continue == MenuState::EXIT
@@ -1357,19 +1280,15 @@ module MarkdownExec
1357
1280
 
1358
1281
  # exit current document/menu if loading next document or single block_name was specified
1359
1282
  #
1360
- if block_state.state == MenuState::CONTINUE && load_file_next_block.load_file == LoadFile::Load
1361
- break
1362
- end
1363
- break if repeat_menu == MenuControl::Fresh
1283
+ break if block_state.state == MenuState::CONTINUE && load_file == LoadFile::Load
1284
+ break if block_name_from_cli
1364
1285
  end
1365
- break if load_file_next_block.load_file == LoadFile::Reuse
1286
+ break if load_file == LoadFile::Reuse
1366
1287
 
1367
- repeat_menu = next_block_name_from_command_line_arguments
1368
- @menu_state_filename = @menu_base_options[:filename]
1369
- @menu_state_block_name = @menu_base_options[:block_name]
1288
+ block_name_from_cli = pop_cli_argument!
1370
1289
  end
1371
1290
  rescue StandardError
1372
- error_handler('select_approve_and_execute_block',
1291
+ error_handler('select_execute_bash_and_special_blocks',
1373
1292
  { abort: true })
1374
1293
  end
1375
1294
 
@@ -1386,8 +1305,7 @@ module MarkdownExec
1386
1305
 
1387
1306
  item.merge(
1388
1307
  if selection == menu_chrome_colored_option(:menu_option_back_name)
1389
- { option: selection, curr: @hs_curr, rest: @hs_rest,
1390
- shell: BlockType::LINK }
1308
+ { option: selection, shell: BlockType::LINK }
1391
1309
  elsif selection == menu_chrome_colored_option(:menu_option_exit_name)
1392
1310
  { option: selection }
1393
1311
  else
@@ -1400,6 +1318,35 @@ module MarkdownExec
1400
1318
  error_handler('select_option_with_metadata')
1401
1319
  end
1402
1320
 
1321
+ def set_environment_variables_for_block(selected)
1322
+ YAML.load(selected[:body].join("\n")).each do |key, value|
1323
+ ENV[key] = value.to_s
1324
+ next unless @delegate_object[:menu_vars_set_format].present?
1325
+
1326
+ formatted_string = format(@delegate_object[:menu_vars_set_format],
1327
+ { key: key, value: value })
1328
+ print string_send_color(formatted_string, :menu_vars_set_color)
1329
+ end
1330
+ end
1331
+
1332
+ def set_environment_variables_per_array(vars)
1333
+ vars ||= []
1334
+ vars.each { |key, value| ENV[key] = value.to_s }
1335
+ end
1336
+
1337
+ def set_file_permissions(file_path, chmod_value)
1338
+ File.chmod(chmod_value, file_path)
1339
+ end
1340
+
1341
+ def set_script_block_name(selected)
1342
+ @run_state.script_block_name = selected[:oname]
1343
+ end
1344
+
1345
+ def should_add_back_option?
1346
+ @delegate_object[:menu_with_back] && @link_history.prior_state_exist?
1347
+ # @delegate_object[:menu_with_back] && link_history_prior_state_exist?
1348
+ end
1349
+
1403
1350
  # Initializes a new fenced code block (FCB) object based on the provided line and heading information.
1404
1351
  # @param line [String] The line initiating the fenced block.
1405
1352
  # @param headings [Array<String>] Current headings hierarchy.
@@ -1459,6 +1406,12 @@ module MarkdownExec
1459
1406
  )
1460
1407
  end
1461
1408
 
1409
+ def update_delegate_and_target(key, value, tgt2)
1410
+ sym_key = key.to_sym
1411
+ @delegate_object[sym_key] = value
1412
+ tgt2[sym_key] = value if tgt2
1413
+ end
1414
+
1462
1415
  # Updates the hierarchy of document headings based on the given line.
1463
1416
  # Utilizes regular expressions to identify heading levels.
1464
1417
  # @param line [String] The line of text to check for headings.
@@ -1527,12 +1480,9 @@ module MarkdownExec
1527
1480
  ## add line to fenced code block
1528
1481
  # remove fcb indent if possible
1529
1482
  #
1530
- # if nested_line[:depth].zero? || opts[:menu_include_imported_blocks]
1531
- # if add_import_block?(nested_line)
1532
1483
  state[:fcb].body += [
1533
1484
  line.chomp.sub(/^#{state[:fcb].indent}/, '')
1534
1485
  ]
1535
- # end
1536
1486
  elsif nested_line[:depth].zero? || @delegate_object[:menu_include_imported_notes]
1537
1487
  # add line if it is depth 0 or option allows it
1538
1488
  #
@@ -1555,10 +1505,29 @@ module MarkdownExec
1555
1505
  yield_to_block_if_applicable(fcb, selected_messages, &block)
1556
1506
  end
1557
1507
 
1508
+ # Processes YAML data from the selected menu item, updating delegate objects and optionally printing formatted output.
1509
+ # @param selected [Hash] Selected item from the menu containing a YAML body.
1510
+ # @param tgt2 [Hash, nil] An optional target hash to update with YAML data.
1511
+ # @return [LoadFileLinkState] An instance indicating the next action for loading files.
1512
+ def update_options_and_trigger_reuse(selected, tgt2 = nil, link_state = LinkState.new)
1513
+ data = YAML.load(selected[:body].join("\n"))
1514
+ (data || []).each do |key, value|
1515
+ update_delegate_and_target(key, value, tgt2)
1516
+ print_formatted_option(key, value) if @delegate_object[:menu_opts_set_format].present?
1517
+ end
1518
+ link_state.block_name = nil
1519
+ LoadFileLinkState.new(LoadFile::Reuse, link_state)
1520
+ end
1521
+
1522
+ def wait_for_stream_processing
1523
+ @process_mutex.synchronize do
1524
+ @process_cv.wait(@process_mutex)
1525
+ end
1526
+ end
1527
+
1558
1528
  def wait_for_user_selected_block(all_blocks, menu_blocks, default)
1559
1529
  block_state = wait_for_user_selection(all_blocks, menu_blocks, default)
1560
1530
  handle_block_state(block_state)
1561
-
1562
1531
  block_state
1563
1532
  rescue StandardError
1564
1533
  error_handler('wait_for_user_selected_block')
@@ -1590,7 +1559,6 @@ module MarkdownExec
1590
1559
  def write_command_file(required_lines)
1591
1560
  return unless @delegate_object[:save_executed_script]
1592
1561
 
1593
- # rbp
1594
1562
  time_now = Time.now.utc
1595
1563
  @run_state.saved_script_filename =
1596
1564
  SavedAsset.script_name(blockname: @delegate_object[:block_name],
@@ -1613,7 +1581,7 @@ module MarkdownExec
1613
1581
  "# time: #{time_now}\n" \
1614
1582
  "#{required_lines.flatten.join("\n")}\n"
1615
1583
 
1616
- create_and_write_file_with_permissions(
1584
+ create_file_and_write_string_with_permissions(
1617
1585
  @run_state.saved_filespec,
1618
1586
  content,
1619
1587
  @delegate_object[:saved_script_chmod]
@@ -1622,6 +1590,10 @@ module MarkdownExec
1622
1590
  error_handler('write_command_file')
1623
1591
  end
1624
1592
 
1593
+ def save_executed_script_if_specified(lines)
1594
+ write_command_file(lines) if @delegate_object[:save_executed_script]
1595
+ end
1596
+
1625
1597
  def write_execution_output_to_file
1626
1598
  FileUtils.mkdir_p File.dirname(@delegate_object[:logged_stdout_filespec])
1627
1599
 
@@ -1641,7 +1613,7 @@ module MarkdownExec
1641
1613
  #
1642
1614
  # @param mdoc [Object] The Markdown document object.
1643
1615
  # @param block_name [String] The name of the block to collect code for.
1644
- def write_required_blocks_to_temp_file(mdoc, block_name)
1616
+ def write_required_blocks_to_file(mdoc, block_name, temp_file_path, import_filename: nil)
1645
1617
  c1 = if mdoc
1646
1618
  mdoc.collect_recursively_required_code(
1647
1619
  block_name,
@@ -1652,10 +1624,16 @@ module MarkdownExec
1652
1624
  []
1653
1625
  end
1654
1626
 
1655
- code_blocks = (read_required_blocks_from_temp_file +
1627
+ code_blocks = (read_required_blocks_from_temp_file(import_filename) +
1656
1628
  c1).join("\n")
1657
1629
 
1658
- create_temp_file_with_code(code_blocks)
1630
+ write_code_to_file(code_blocks, temp_file_path)
1631
+ end
1632
+
1633
+ # Writes the provided code blocks to a file.
1634
+ # @param code_blocks [String] Code blocks to write into the file.
1635
+ def write_code_to_file(content, path)
1636
+ File.write(path, content)
1659
1637
  end
1660
1638
 
1661
1639
  # Yields a line as a new block if the selected message type includes :line.
@@ -1698,7 +1676,7 @@ if $PROGRAM_NAME == __FILE__
1698
1676
  @mdoc = mock('MarkdownDocument')
1699
1677
  end
1700
1678
 
1701
- def test_calling_execute_approved_block_calls_command_execute_with_argument_args_value
1679
+ def test_calling_execute_required_lines_calls_command_execute_with_argument_args_value
1702
1680
  pigeon = 'E'
1703
1681
  obj = {
1704
1682
  output_execution_label_format: '',
@@ -1715,46 +1693,33 @@ if $PROGRAM_NAME == __FILE__
1715
1693
  args: pigeon
1716
1694
  )
1717
1695
 
1718
- # Call method opts_execute_approved_block
1719
- c.execute_approved_block([], MarkdownExec::FCB.new)
1696
+ # Call method opts_execute_required_lines
1697
+ c.execute_required_lines([])
1720
1698
  end
1721
1699
 
1722
1700
  # Test case for empty body
1723
- def test_handle_link_block_with_empty_body
1724
- assert_equal LoadFileNextBlock.new(LoadFile::Reuse, ''),
1725
- @hd.handle_link_block([], nil, false)
1701
+ def test_push_link_history_and_trigger_load_with_empty_body
1702
+ assert_equal LoadFile::Reuse,
1703
+ @hd.push_link_history_and_trigger_load([], nil, FCB.new).load_file
1726
1704
  end
1727
1705
 
1728
1706
  # Test case for non-empty body without 'file' key
1729
- def test_handle_link_block_without_file_key
1707
+ def test_push_link_history_and_trigger_load_without_file_key
1730
1708
  body = ["vars:\n KEY: VALUE"]
1731
- assert_equal LoadFileNextBlock.new(LoadFile::Reuse, ''),
1732
- @hd.handle_link_block(body, nil, false)
1709
+ assert_equal LoadFile::Reuse,
1710
+ @hd.push_link_history_and_trigger_load(body, nil, FCB.new).load_file
1733
1711
  end
1734
1712
 
1735
1713
  # Test case for non-empty body with 'file' key
1736
- def test_handle_link_block_with_file_key
1714
+ def test_push_link_history_and_trigger_load_with_file_key
1737
1715
  body = ["file: sample_file\nblock: sample_block\nvars:\n KEY: VALUE"]
1738
- expected_result = LoadFileNextBlock.new(LoadFile::Load,
1739
- 'sample_block')
1740
- # mdoc = MDoc.new()
1716
+ expected_result = LoadFileLinkState.new(LoadFile::Load,
1717
+ LinkState.new(block_name: 'sample_block',
1718
+ document_filename: 'sample_file',
1719
+ inherited_lines: []))
1741
1720
  assert_equal expected_result,
1742
- @hd.handle_link_block(body, nil, FCB.new)
1743
- end
1744
-
1745
- def test_history_env_state_exist_with_value
1746
- ENV[MDE_HISTORY_ENV_NAME] = 'history_value'
1747
- assert @hd.history_env_state_exist?
1748
- end
1749
-
1750
- def test_history_env_state_exist_without_value
1751
- ENV[MDE_HISTORY_ENV_NAME] = ''
1752
- refute @hd.history_env_state_exist?
1753
- end
1754
-
1755
- def test_history_env_state_exist_not_set
1756
- ENV.delete(MDE_HISTORY_ENV_NAME)
1757
- refute @hd.history_env_state_exist?
1721
+ @hd.push_link_history_and_trigger_load(body, nil, FCB.new(block_name: 'sample_block',
1722
+ filename: 'sample_file'))
1758
1723
  end
1759
1724
 
1760
1725
  def test_indent_all_lines_with_indent
@@ -1778,22 +1743,6 @@ if $PROGRAM_NAME == __FILE__
1778
1743
  assert_equal body, @hd.indent_all_lines(body, indent)
1779
1744
  end
1780
1745
 
1781
- def test_read_required_blocks_from_temp_file
1782
- Tempfile.create do |file|
1783
- file.write("Line 1\nLine 2")
1784
- file.rewind
1785
- ENV['MDE_LINK_REQUIRED_FILE'] = file.path
1786
-
1787
- result = @hd.read_required_blocks_from_temp_file
1788
- assert_equal ['Line 1', 'Line 2'], result
1789
- end
1790
- end
1791
-
1792
- def test_read_required_blocks_from_temp_file_no_file
1793
- ENV['MDE_LINK_REQUIRED_FILE'] = nil
1794
- assert_empty @hd.read_required_blocks_from_temp_file
1795
- end
1796
-
1797
1746
  def test_safeval_successful_evaluation
1798
1747
  assert_equal 4, @hd.safeval('2 + 2')
1799
1748
  end
@@ -1823,8 +1772,6 @@ if $PROGRAM_NAME == __FILE__
1823
1772
  }
1824
1773
  ]
1825
1774
 
1826
- # hd = HashDelegator.new
1827
-
1828
1775
  # iterate over the input and output data and
1829
1776
  # assert that the method sets the title as expected
1830
1777
  input_output_data.each do |data|
@@ -1871,7 +1818,7 @@ if $PROGRAM_NAME == __FILE__
1871
1818
 
1872
1819
  assert_empty menu_blocks
1873
1820
  end
1874
- end # class TestHashDelegator
1821
+ end
1875
1822
 
1876
1823
  class TestHashDelegatorBlockFind < Minitest::Test
1877
1824
  def setup
@@ -1895,7 +1842,7 @@ if $PROGRAM_NAME == __FILE__
1895
1842
  result = @hd.block_find(blocks, :key, 'value3', 'default')
1896
1843
  assert_equal 'default', result
1897
1844
  end
1898
- end # class TestHashDelegator
1845
+ end
1899
1846
 
1900
1847
  class TestHashDelegatorBlocksFromNestedFiles < Minitest::Test
1901
1848
  def setup
@@ -1922,7 +1869,7 @@ if $PROGRAM_NAME == __FILE__
1922
1869
 
1923
1870
  assert_kind_of Array, result
1924
1871
  end
1925
- end # class TestHashDelegator
1872
+ end
1926
1873
 
1927
1874
  class TestHashDelegatorCollectRequiredCodeLines < Minitest::Test
1928
1875
  def setup
@@ -1938,13 +1885,11 @@ if $PROGRAM_NAME == __FILE__
1938
1885
  def test_collect_required_code_lines_with_vars
1939
1886
  YAML.stubs(:load).returns({ 'key' => 'value' })
1940
1887
  @mdoc.stubs(:collect_recursively_required_code).returns({ code: ['code line'] })
1941
- ENV.stubs(:[]=)
1942
-
1943
- result = @hd.collect_required_code_lines(@mdoc, @selected)
1888
+ result = @hd.collect_required_code_lines(@mdoc, @selected, block_source: {})
1944
1889
 
1945
1890
  assert_equal ['code line'], result
1946
1891
  end
1947
- end # class TestHashDelegator
1892
+ end
1948
1893
 
1949
1894
  class TestHashDelegatorCommandOrUserSelectedBlock < Minitest::Test
1950
1895
  def setup
@@ -1975,7 +1920,7 @@ if $PROGRAM_NAME == __FILE__
1975
1920
  assert_equal block_state.block, result.block
1976
1921
  assert_equal :some_state, result.state
1977
1922
  end
1978
- end # class TestHashDelegator
1923
+ end
1979
1924
 
1980
1925
  class TestHashDelegatorCountBlockInFilename < Minitest::Test
1981
1926
  def setup
@@ -2004,7 +1949,7 @@ if $PROGRAM_NAME == __FILE__
2004
1949
 
2005
1950
  assert_equal 0, count
2006
1951
  end
2007
- end # class TestHashDelegator
1952
+ end
2008
1953
 
2009
1954
  class TestHashDelegatorCreateAndWriteFile < Minitest::Test
2010
1955
  def setup
@@ -2015,7 +1960,7 @@ if $PROGRAM_NAME == __FILE__
2015
1960
  File.stubs(:chmod)
2016
1961
  end
2017
1962
 
2018
- def test_create_and_write_file_with_permissions
1963
+ def test_create_file_and_write_string_with_permissions
2019
1964
  file_path = '/path/to/file'
2020
1965
  content = 'sample content'
2021
1966
  chmod_value = 0o644
@@ -2024,8 +1969,8 @@ if $PROGRAM_NAME == __FILE__
2024
1969
  File.expects(:write).with(file_path, content).once
2025
1970
  File.expects(:chmod).with(chmod_value, file_path).once
2026
1971
 
2027
- @hd.create_and_write_file_with_permissions(file_path, content,
2028
- chmod_value)
1972
+ @hd.create_file_and_write_string_with_permissions(file_path, content,
1973
+ chmod_value)
2029
1974
 
2030
1975
  assert true # Placeholder for actual test assertions
2031
1976
  end
@@ -2039,70 +1984,12 @@ if $PROGRAM_NAME == __FILE__
2039
1984
  File.expects(:write).with(file_path, content).once
2040
1985
  File.expects(:chmod).never
2041
1986
 
2042
- @hd.create_and_write_file_with_permissions(file_path, content,
2043
- chmod_value)
2044
-
2045
- assert true # Placeholder for actual test assertions
2046
- end
2047
- end # class TestHashDelegator
2048
-
2049
- class TestHashDelegatorCreateTempFile < Minitest::Test
2050
- def setup
2051
- @hd = HashDelegator.new
2052
- @temp_file_path = '/tmp/tempfile'
2053
- end
2054
-
2055
- def test_create_temp_file_with_code
2056
- Dir::Tmpname.stubs(:create).returns(@temp_file_path)
2057
- File.stubs(:write).with(@temp_file_path, 'code_blocks')
2058
- # ENV.expects(:[]=).with('MDE_LINK_REQUIRED_FILE', @temp_file_path)
2059
-
2060
- @hd.create_temp_file_with_code('code_blocks')
2061
-
2062
- assert true # Placeholder for actual test assertions
2063
- end
2064
- end # class TestHashDelegator
2065
-
2066
- class TestHashDelegatorDeleteRequiredTempFile < Minitest::Test
2067
- def setup
2068
- @hd = HashDelegator.new
2069
- @hd.stubs(:error_handler)
2070
- @hd.stubs(:clear_required_file)
2071
- FileUtils.stubs(:rm_f)
2072
- end
2073
-
2074
- def test_delete_required_temp_file_with_existing_file
2075
- ENV.stubs(:fetch).with('MDE_LINK_REQUIRED_FILE',
2076
- nil).returns('/path/to/temp_file')
2077
- FileUtils.expects(:rm_f).with('/path/to/temp_file').once
2078
- @hd.expects(:clear_required_file).once
2079
-
2080
- @hd.delete_required_temp_file
2081
-
2082
- assert true # Placeholder for actual test assertions
2083
- end
2084
-
2085
- def test_delete_required_temp_file_with_no_file
2086
- ENV.stubs(:fetch).with('MDE_LINK_REQUIRED_FILE', nil).returns(nil)
2087
- FileUtils.expects(:rm_f).never
2088
- @hd.expects(:clear_required_file).never
2089
-
2090
- @hd.delete_required_temp_file
2091
-
2092
- assert true # Placeholder for actual test assertions
2093
- end
2094
-
2095
- def test_delete_required_temp_file_with_error
2096
- ENV.stubs(:fetch).with('MDE_LINK_REQUIRED_FILE',
2097
- nil).returns('/path/to/temp_file')
2098
- FileUtils.stubs(:rm_f).raises(StandardError)
2099
- @hd.expects(:error_handler).with('delete_required_temp_file').once
2100
-
2101
- @hd.delete_required_temp_file
1987
+ @hd.create_file_and_write_string_with_permissions(file_path, content,
1988
+ chmod_value)
2102
1989
 
2103
1990
  assert true # Placeholder for actual test assertions
2104
1991
  end
2105
- end # class TestHashDelegator
1992
+ end
2106
1993
 
2107
1994
  class TestHashDelegatorDetermineBlockState < Minitest::Test
2108
1995
  def setup
@@ -2137,7 +2024,7 @@ if $PROGRAM_NAME == __FILE__
2137
2024
  assert_equal MenuState::CONTINUE, result.state
2138
2025
  assert_equal selected_option, result.block
2139
2026
  end
2140
- end # class TestHashDelegator
2027
+ end
2141
2028
 
2142
2029
  class TestHashDelegatorDisplayRequiredCode < Minitest::Test
2143
2030
  def setup
@@ -2158,7 +2045,7 @@ if $PROGRAM_NAME == __FILE__
2158
2045
  # Verifying that fout is called for each line and for header & footer
2159
2046
  assert true # Placeholder for actual test assertions
2160
2047
  end
2161
- end # class TestHashDelegator
2048
+ end
2162
2049
 
2163
2050
  class TestHashDelegatorFetchColor < Minitest::Test
2164
2051
  def setup
@@ -2189,7 +2076,7 @@ if $PROGRAM_NAME == __FILE__
2189
2076
 
2190
2077
  assert_equal 'Default Colored String', result
2191
2078
  end
2192
- end # class TestHashDelegator
2079
+ end
2193
2080
 
2194
2081
  class TestHashDelegatorFormatReferencesSendColor < Minitest::Test
2195
2082
  def setup
@@ -2220,7 +2107,7 @@ if $PROGRAM_NAME == __FILE__
2220
2107
 
2221
2108
  assert_equal 'Default Colored String', result
2222
2109
  end
2223
- end # class TestHashDelegator
2110
+ end
2224
2111
 
2225
2112
  class TestHashDelegatorFormatExecutionStreams < Minitest::Test
2226
2113
  def setup
@@ -2253,7 +2140,7 @@ if $PROGRAM_NAME == __FILE__
2253
2140
 
2254
2141
  assert_equal '', result
2255
2142
  end
2256
- end # class TestHashDelegator
2143
+ end
2257
2144
 
2258
2145
  class TestHashDelegatorHandleBackLink < Minitest::Test
2259
2146
  def setup
@@ -2261,18 +2148,18 @@ if $PROGRAM_NAME == __FILE__
2261
2148
  @hd.stubs(:history_state_pop)
2262
2149
  end
2263
2150
 
2264
- def test_handle_back_link
2151
+ def test_pop_link_history_and_trigger_load
2265
2152
  # Verifying that history_state_pop is called
2266
- @hd.expects(:history_state_pop).once
2153
+ # @hd.expects(:history_state_pop).once
2267
2154
 
2268
- result = @hd.handle_back_link
2155
+ result = @hd.pop_link_history_and_trigger_load
2269
2156
 
2270
- # Asserting the result is an instance of LoadFileNextBlock
2271
- assert_instance_of LoadFileNextBlock, result
2157
+ # Asserting the result is an instance of LoadFileLinkState
2158
+ assert_instance_of LoadFileLinkState, result
2272
2159
  assert_equal LoadFile::Load, result.load_file
2273
- assert_equal '', result.next_block
2160
+ assert_nil result.link_state.block_name
2274
2161
  end
2275
- end # class TestHashDelegator
2162
+ end
2276
2163
 
2277
2164
  class TestHashDelegatorHandleBlockState < Minitest::Test
2278
2165
  def setup
@@ -2311,7 +2198,7 @@ if $PROGRAM_NAME == __FILE__
2311
2198
  assert_nil @hd.instance_variable_get(:@delegate_object)[:block_name]
2312
2199
  assert_nil @hd.instance_variable_get(:@menu_user_clicked_back_link)
2313
2200
  end
2314
- end # class TestHashDelegator
2201
+ end
2315
2202
 
2316
2203
  class TestHashDelegatorHandleGenericBlock < Minitest::Test
2317
2204
  def setup
@@ -2320,7 +2207,7 @@ if $PROGRAM_NAME == __FILE__
2320
2207
  @selected_item = mock('FCB')
2321
2208
  end
2322
2209
 
2323
- def test_handle_generic_block_without_user_approval
2210
+ def test_compile_execute_bash_and_special_blocks_and_trigger_reuse_without_user_approval
2324
2211
  # Mock the delegate object configuration
2325
2212
  @hd.instance_variable_set(:@delegate_object,
2326
2213
  { output_script: false,
@@ -2330,7 +2217,7 @@ if $PROGRAM_NAME == __FILE__
2330
2217
  # Expectations and assertions go here
2331
2218
  end
2332
2219
 
2333
- def test_handle_generic_block_with_user_approval
2220
+ def test_compile_execute_bash_and_special_blocks_and_trigger_reuse_with_user_approval
2334
2221
  # Mock the delegate object configuration
2335
2222
  @hd.instance_variable_set(:@delegate_object,
2336
2223
  { output_script: false,
@@ -2340,7 +2227,7 @@ if $PROGRAM_NAME == __FILE__
2340
2227
  # Expectations and assertions go here
2341
2228
  end
2342
2229
 
2343
- def test_handle_generic_block_with_output_script
2230
+ def test_compile_execute_bash_and_special_blocks_and_trigger_reuse_with_output_script
2344
2231
  # Mock the delegate object configuration
2345
2232
  @hd.instance_variable_set(:@delegate_object,
2346
2233
  { output_script: true,
@@ -2361,25 +2248,25 @@ if $PROGRAM_NAME == __FILE__
2361
2248
  @hd.stubs(:print)
2362
2249
  end
2363
2250
 
2364
- def test_handle_opts_block
2251
+ def test_update_options_and_trigger_reuse
2365
2252
  selected = { body: ['option1: value1'] }
2366
2253
  tgt2 = {}
2367
2254
 
2368
- result = @hd.handle_opts_block(selected, tgt2)
2255
+ result = @hd.update_options_and_trigger_reuse(selected, tgt2)
2369
2256
 
2370
- assert_instance_of LoadFileNextBlock, result
2257
+ assert_instance_of LoadFileLinkState, result
2371
2258
  assert_equal 'value1',
2372
2259
  @hd.instance_variable_get(:@delegate_object)[:option1]
2373
2260
  assert_equal 'value1', tgt2[:option1]
2374
2261
  end
2375
2262
 
2376
- def test_handle_opts_block_without_format
2263
+ def test_update_options_and_trigger_reuse_without_format
2377
2264
  selected = { body: ['option2: value2'] }
2378
2265
  @hd.instance_variable_set(:@delegate_object, {})
2379
2266
 
2380
- result = @hd.handle_opts_block(selected)
2267
+ result = @hd.update_options_and_trigger_reuse(selected)
2381
2268
 
2382
- assert_instance_of LoadFileNextBlock, result
2269
+ assert_instance_of LoadFileLinkState, result
2383
2270
  assert_equal 'value2',
2384
2271
  @hd.instance_variable_get(:@delegate_object)[:option2]
2385
2272
  end
@@ -2422,94 +2309,6 @@ if $PROGRAM_NAME == __FILE__
2422
2309
  end
2423
2310
  end
2424
2311
 
2425
- class TestHashDelegatorHistoryStatePartition < Minitest::Test
2426
- def setup
2427
- @hd = HashDelegator.new
2428
- @hd.instance_variable_set(:@delegate_object, {
2429
- history_document_separator: '|'
2430
- })
2431
- end
2432
-
2433
- def test_history_state_partition_with_value
2434
- ENV[MDE_HISTORY_ENV_NAME] = 'part1|part2'
2435
-
2436
- result = @hd.history_state_partition
2437
- assert_equal({ unit: 'part1', rest: 'part2' }, result)
2438
- end
2439
-
2440
- def test_history_state_partition_with_no_separator
2441
- ENV[MDE_HISTORY_ENV_NAME] = 'onlypart'
2442
-
2443
- result = @hd.history_state_partition
2444
- assert_equal({ unit: 'onlypart', rest: '' }, result)
2445
- end
2446
-
2447
- def test_history_state_partition_with_empty_env
2448
- ENV[MDE_HISTORY_ENV_NAME] = ''
2449
-
2450
- result = @hd.history_state_partition
2451
- assert_equal({ unit: '', rest: '' }, result)
2452
- end
2453
- end
2454
-
2455
- class TestHashDelegatorHistoryStatePop < Minitest::Test
2456
- def setup
2457
- @hd = HashDelegator.new
2458
- @hd.instance_variable_set(:@delegate_object,
2459
- { filename: 'initial.md' })
2460
- @hd.instance_variable_set(:@run_state,
2461
- OpenStruct.new(link_history: [{ block_name: 'block1',
2462
- filename: 'file1.md' }]))
2463
- @hd.stubs(:history_state_partition).returns({ unit: 'file2.md',
2464
- rest: 'history_data' })
2465
- @hd.stubs(:delete_required_temp_file)
2466
- end
2467
-
2468
- def test_history_state_pop
2469
- ENV[MDE_HISTORY_ENV_NAME] = 'some_history'
2470
-
2471
- @hd.history_state_pop
2472
-
2473
- assert_equal 'file2.md',
2474
- @hd.instance_variable_get(:@delegate_object)[:filename]
2475
- assert_equal 'history_data',
2476
- ENV.fetch(MDE_HISTORY_ENV_NAME, nil)
2477
- assert_empty @hd.instance_variable_get(:@run_state).link_history
2478
- end
2479
- end
2480
-
2481
- class TestHashDelegatorHistoryStatePush < Minitest::Test
2482
- def setup
2483
- @hd = HashDelegator.new
2484
- @hd.instance_variable_set(:@delegate_object, {
2485
- filename: 'test.md',
2486
- block_name: 'test_block',
2487
- history_document_separator: '||'
2488
- })
2489
- @hd.instance_variable_set(:@run_state,
2490
- OpenStruct.new(link_history: []))
2491
- @hd.stubs(:write_required_blocks_to_temp_file)
2492
- end
2493
-
2494
- def test_history_state_push
2495
- mdoc = 'markdown content'
2496
- data_file = 'data.md'
2497
- selected = { oname: 'selected_block' }
2498
-
2499
- ENV[MDE_HISTORY_ENV_NAME] = 'existing_history'
2500
-
2501
- @hd.history_state_push(mdoc, data_file, selected)
2502
-
2503
- assert_equal 'data.md',
2504
- @hd.instance_variable_get(:@delegate_object)[:filename]
2505
- assert_equal 'test.md||existing_history',
2506
- ENV.fetch(MDE_HISTORY_ENV_NAME, nil)
2507
- assert_includes @hd.instance_variable_get(:@run_state).link_history,
2508
- { block_name: 'selected_block',
2509
- filename: 'data.md' }
2510
- end
2511
- end
2512
-
2513
2312
  class TestHashDelegatorIterBlocksFromNestedFiles < Minitest::Test
2514
2313
  def setup
2515
2314
  @hd = HashDelegator.new
@@ -2544,7 +2343,7 @@ if $PROGRAM_NAME == __FILE__
2544
2343
  def setup
2545
2344
  @hd = HashDelegator.new
2546
2345
  @hd.stubs(:block_find).returns({})
2547
- @hd.stubs(:handle_opts_block)
2346
+ @hd.stubs(:update_options_and_trigger_reuse)
2548
2347
  end
2549
2348
 
2550
2349
  def test_load_auto_blocks_with_new_filename
@@ -2681,7 +2480,7 @@ if $PROGRAM_NAME == __FILE__
2681
2480
  result = @hd.yield_line_if_selected('Test line', [:line])
2682
2481
  assert_nil result
2683
2482
  end
2684
- end # class TestHashDelegator
2483
+ end
2685
2484
 
2686
2485
  class TestHashDelegator < Minitest::Test
2687
2486
  def setup