lib/hash_delegator.rb in markdown_exec-1.8.5 vs lib/hash_delegator.rb in markdown_exec-1.8.6

- old
+ new

@@ -357,11 +357,11 @@ @run_state.error = err @run_state.files[ExecutionStreams::StdErr] += [@run_state.error_message] @fout.fout "Error ENOENT: #{err.inspect}" end - def command_or_user_selected_block(all_blocks, menu_blocks, default) + def load_cli_or_user_selected_block(all_blocks, menu_blocks, default) if @delegate_object[:block_name].present? block = all_blocks.find do |item| item[:oname] == @delegate_object[:block_name] end else @@ -371,22 +371,21 @@ state = block_state.state end SelectedBlockMenuState.new(block, state) rescue StandardError - error_handler('command_or_user_selected_block') + error_handler('load_cli_or_user_selected_block') end # This method is responsible for handling the execution of generic blocks in a markdown document. # It collects the required code lines from the document and, depending on the configuration, # may display the code for user approval before execution. It then executes the approved block. # # @param mdoc [Object] The markdown document object containing code blocks. # @param selected [Hash] The selected item from the menu to be executed. # @return [LoadFileLinkState] An object indicating whether to load the next block or reuse the current one. - def compile_execute_bash_and_special_blocks_and_trigger_reuse(mdoc, selected, - link_state = nil, block_source:) + def compile_execute_and_trigger_reuse(mdoc, selected, link_state = nil, block_source:) required_lines = collect_required_code_lines(mdoc, selected, link_state, block_source: block_source) output_or_approval = @delegate_object[:output_script] || @delegate_object[:user_must_approve] display_required_code(required_lines) if output_or_approval allow_execution = @delegate_object[:user_must_approve] ? prompt_for_user_approval(required_lines) : true @@ -600,12 +599,12 @@ # code to the clipboard or save it to a file. # # @param opts [Hash] Options hash containing configuration settings. # @param mdoc [YourMDocClass] An instance of the MDoc class. # - def execute_bash_and_special_blocks(selected, mdoc, link_state = LinkState.new, - block_source:) + def execute_shell_type(selected, mdoc, link_state = LinkState.new, + block_source:) if selected.fetch(:shell, '') == BlockType::LINK push_link_history_and_trigger_load(selected.fetch(:body, ''), mdoc, selected, link_state) elsif @menu_user_clicked_back_link @@ -616,12 +615,12 @@ @menu_base_options.merge!(options_state.options) @delegate_object.merge!(options_state.options) options_state.load_file_link_state else - compile_execute_bash_and_special_blocks_and_trigger_reuse(mdoc, selected, link_state, - block_source: block_source) + compile_execute_and_trigger_reuse(mdoc, selected, link_state, + block_source: block_source) end end # Retrieves a specific data symbol from the delegate object, converts it to a string, # and applies a color style based on the specified color symbol. @@ -920,14 +919,16 @@ @delegate_object[method_name] # super end end - def pop_cli_argument! - return false unless @delegate_object[:input_cli_rest].present? + def shift_cli_argument! + return false unless @menu_base_options[:input_cli_rest].present? - @cli_block_name = @delegate_object[:input_cli_rest].pop + @cli_block_name = @menu_base_options[:input_cli_rest].shift + # @delegate_object[:input_cli_rest].shift + # p [__LINE__, @cli_block_name, @menu_base_options[:input_cli_rest]] true end # private @@ -1270,106 +1271,212 @@ # # This method allows the user to interactively select a code block from a # Markdown document, obtain approval, and execute the chosen block of code. # # @return [Nil] Returns nil if no code block is selected or an error occurs. - def select_execute_bash_and_special_blocks(_execute: true) + def document_menu_loop @menu_base_options = @delegate_object + link_state, block_name_from_cli, now_using_cli = initialize_selection_states + menu_default_dname = nil + + loop do + # @bsp 'loop',block_name_from_cli,@cli_block_name + block_name_from_cli, now_using_cli, blocks_in_file, menu_blocks, mdoc = \ + set_delobj_menu_loop_vars(block_name_from_cli, now_using_cli, link_state) + + # cli or user selection + # + block_state = load_cli_or_user_selected_block(blocks_in_file, menu_blocks, + menu_default_dname) + if block_state.state == MenuState::EXIT + # @bsp 'MenuState::EXIT -> break' + break + end + + dump_and_warn_block_state(block_state.block) + link_state, menu_default_dname = exec_bash_next_state(block_state.block, mdoc, + link_state) + if prompt_user_exit(block_name_from_cli, block_state.block) + # @bsp 'prompt_user_exit -> break' + break + end + + link_state.block_name, block_name_from_cli, cli_break = \ + next_state_from_cli(now_using_cli, block_state) + + if cli_break + # @bsp 'read_block_name_from_cli + next_link_state -> break' + break + end + end + rescue StandardError + error_handler('document_menu_loop', + { abort: true }) + end + + def next_state_from_cli(now_using_cli, block_state) + was_using_cli = now_using_cli + block_name_from_cli, = read_block_name_from_cli(now_using_cli) + block_name, block_name_from_cli, cli_break = \ + next_link_state(block_name_from_cli, was_using_cli, block_state) + + [block_name, block_name_from_cli, cli_break] + end + + def exec_bash_next_state(block_state_block, mdoc, link_state) + lfls = execute_shell_type( + block_state_block, + mdoc, + link_state, + block_source: { document_filename: @delegate_object[:filename] } + ) + + # if the same menu is being displayed, collect the display name of the selected menu item for use as the default item + [lfls.link_state, + lfls.load_file == LoadFile::Load ? nil : block_state_block[:dname]] + end + + def set_delobj_menu_loop_vars(block_name_from_cli, now_using_cli, link_state) + block_name_from_cli, now_using_cli = \ + manage_cli_selection_state(block_name_from_cli, now_using_cli, link_state) + set_delob_filename_block_name(link_state, block_name_from_cli) + + # update @delegate_object and @menu_base_options in auto_load + # + blocks_in_file, menu_blocks, mdoc = mdoc_menu_and_blocks_from_nested_files + dump_delobj(blocks_in_file, menu_blocks, link_state) + + [block_name_from_cli, now_using_cli, blocks_in_file, menu_blocks, mdoc] + end + + # user prompt to exit if the menu will be displayed again + # + def prompt_user_exit(block_name_from_cli, block_state_block) + !block_name_from_cli && + block_state_block[:shell] == BlockType::BASH && + @delegate_object[:pause_after_script_execution] && + prompt_select_continue == MenuState::EXIT + end + + def manage_cli_selection_state(block_name_from_cli, now_using_cli, link_state) + if block_name_from_cli && @cli_block_name == '.' + # @bsp 'pause cli control, allow user to select block' + block_name_from_cli = false + now_using_cli = false + @menu_base_options[:block_name] = \ + @delegate_object[:block_name] = \ + link_state.block_name = \ + @cli_block_name = nil + end + + @delegate_object = @menu_base_options.dup + @menu_user_clicked_back_link = false + [block_name_from_cli, now_using_cli] + end + + def next_link_state(block_name_from_cli, was_using_cli, block_state) + # @bsp 'next_link_state',block_name_from_cli, was_using_cli, block_state + # Set block_name based on block_name_from_cli + block_name = block_name_from_cli ? @cli_block_name : nil + + # Determine the state of breaker based on was_using_cli and the block type + breaker = !block_name_from_cli && was_using_cli && block_state.block[:shell] == BlockType::BASH + + # Reset block_name_from_cli if the conditions are not met + block_name_from_cli ||= false + + [block_name, block_name_from_cli, breaker] + end + + # Initialize the selection states for the execution loop. + def initialize_selection_states link_state = LinkState.new( block_name: @delegate_object[:block_name], document_filename: @delegate_object[:filename] ) - block_name_from_cli = link_state.block_name.present? - @cli_block_name = link_state.block_name - load_file = nil - menu_default_dname = nil + block_name_from_cli, now_using_cli = handle_cli_block_name(link_state) + [link_state, block_name_from_cli, now_using_cli] + end - loop do - loop do - @delegate_object = @menu_base_options.dup - @menu_user_clicked_back_link = false - @delegate_object[:filename] = link_state.document_filename - link_state.block_name = @delegate_object[:block_name] = - block_name_from_cli ? @cli_block_name : link_state.block_name + # Update the state related to CLI block name. + # + # This method updates the flags indicating whether a CLI block name is being used + # and if it was being used previously. + # + # @param block_name_from_cli [Boolean] Indicates if the block name is from CLI. + # @return [Array] Returns the updated state of block name from CLI and its usage. + def read_block_name_from_cli(block_name_from_cli) + # was_using_cli = block_name_from_cli + block_name_from_cli = shift_cli_argument! + now_using_cli = block_name_from_cli - # update @delegate_object and @menu_base_options in auto_load - # - blocks_in_file, menu_blocks, mdoc = mdoc_menu_and_blocks_from_nested_files + [block_name_from_cli, now_using_cli] + end - if @delegate_object[:dump_delegate_object] - warn format_and_highlight_hash( - @delegate_object, - label: '@delegate_object' - ) - end + # Update the block name in the link state and delegate object. + # + # This method updates the block name based on whether it was specified + # through the CLI or derived from the link state. + # + # @param link_state [LinkState] The current link state object. + # @param block_name_from_cli [Boolean] Indicates if the block name is from CLI. + def set_delob_filename_block_name(link_state, block_name_from_cli) + @delegate_object[:filename] = link_state.document_filename + link_state.block_name = @delegate_object[:block_name] = + block_name_from_cli ? @cli_block_name : link_state.block_name + end - if @delegate_object[:dump_blocks_in_file] - warn format_and_highlight_dependencies( - compact_and_index_hash(blocks_in_file), - label: 'blocks_in_file' - ) - end - if @delegate_object[:dump_menu_blocks] - warn format_and_highlight_dependencies( - compact_and_index_hash(menu_blocks), - label: 'menu_blocks' - ) - end - if @delegate_object[:dump_inherited_lines] - warn format_and_highlight_lines( - link_state.inherited_lines, - label: 'inherited_lines' - ) - end + # Handle CLI block name and determine the current CLI usage state. + # + # This method processes the CLI block name from the link state and sets + # the initial state for CLI usage. + # + # @param link_state [LinkState] The current link state object. + # @return [Array] Returns the state of block name from CLI and current usage of CLI. + def handle_cli_block_name(link_state) + block_name_from_cli = link_state.block_name.present? + @cli_block_name = link_state.block_name + now_using_cli = block_name_from_cli - block_state = command_or_user_selected_block(blocks_in_file, - menu_blocks, menu_default_dname) - return if block_state.state == MenuState::EXIT + [block_name_from_cli, now_using_cli] + end - if block_state.block.nil? - warn_format('select_execute_bash_and_special_blocks', "Block not found -- #{@delegate_object[:block_name]}", - { abort: true }) - # error_handler("Block not found -- #{opts[:block_name]}", { abort: true }) - end + # Outputs warnings based on the delegate object's configuration + # + # @param delegate_object [Hash] The delegate object containing configuration flags. + # @param blocks_in_file [Hash] Hash of blocks present in the file. + # @param menu_blocks [Hash] Hash of menu blocks. + # @param link_state [LinkState] Current state of the link. + def dump_delobj(blocks_in_file, menu_blocks, link_state) + if @delegate_object[:dump_delegate_object] + warn format_and_highlight_hash(@delegate_object, label: '@delegate_object') + end - if @delegate_object[:dump_selected_block] - warn block_state.block.to_yaml.sub(/^(?:---\n)?/, "Block:\n") - end + if @delegate_object[:dump_blocks_in_file] + warn format_and_highlight_dependencies(compact_and_index_hash(blocks_in_file), + label: 'blocks_in_file') + end - load_file_link_state = execute_bash_and_special_blocks( - block_state.block, - mdoc, - link_state, - block_source: { document_filename: @delegate_object[:filename] } - ) - load_file = load_file_link_state.load_file - link_state = load_file_link_state.link_state + if @delegate_object[:dump_menu_blocks] + warn format_and_highlight_dependencies(compact_and_index_hash(menu_blocks), + label: 'menu_blocks') + end - # if the same menu is being displayed, collect the display name of the selected menu item for use as the default item - menu_default_dname = load_file == LoadFile::Load ? nil : block_state.block[:dname] + return unless @delegate_object[:dump_inherited_lines] - # user prompt to exit if the menu will be displayed again - # - if !block_name_from_cli && - block_state.block[:shell] == BlockType::BASH && - @delegate_object[:pause_after_script_execution] && - prompt_select_continue == MenuState::EXIT - return - end + warn format_and_highlight_lines(link_state.inherited_lines, label: 'inherited_lines') + end - # exit current document/menu if loading next document or single block_name was specified - # - break if block_state.state == MenuState::CONTINUE && load_file == LoadFile::Load - break if block_name_from_cli - end - break if load_file == LoadFile::Reuse - - block_name_from_cli = pop_cli_argument! + def dump_and_warn_block_state(block_state_block) + if block_state_block.nil? + Exceptions.warn_format("Block not found -- name: #{@delegate_object[:block_name]}", + { abort: true }) end - rescue StandardError - error_handler('select_execute_bash_and_special_blocks', - { abort: true }) + + return unless @delegate_object[:dump_selected_block] + + warn block_state_block.to_yaml.sub(/^(?:---\n)?/, "Block:\n") end # Presents a TTY prompt to select an option or exit, returns metadata including option and selected def select_option_with_metadata(prompt_text, names, opts = {}) selection = @prompt.select(prompt_text, @@ -1559,11 +1666,11 @@ # add line if it is depth 0 or option allows it # yield_line_if_selected(line, selected_messages, &block) else - # 'rejected' + # @bsp 'line is not recognized for block state' end end # Updates the attributes of the given fcb object and conditionally yields to a block. @@ -1986,22 +2093,22 @@ def test_command_selected_block all_blocks = [{ oname: 'block1' }, { oname: 'block2' }] @hd.instance_variable_set(:@delegate_object, { block_name: 'block1' }) - result = @hd.command_or_user_selected_block(all_blocks, [], nil) + result = @hd.load_cli_or_user_selected_block(all_blocks, [], nil) assert_equal all_blocks.first, result.block assert_nil result.state end def test_user_selected_block block_state = SelectedBlockMenuState.new({ oname: 'block2' }, :some_state) @hd.stubs(:wait_for_user_selected_block).returns(block_state) - result = @hd.command_or_user_selected_block([], [], nil) + result = @hd.load_cli_or_user_selected_block([], [], nil) assert_equal block_state.block, result.block assert_equal :some_state, result.state end end @@ -2289,30 +2396,30 @@ @hd = HashDelegator.new @mock_document = mock('MarkdownDocument') @selected_item = mock('FCB') end - def test_compile_execute_bash_and_special_blocks_and_trigger_reuse_without_user_approval + def test_compile_execute_and_trigger_reuse_without_user_approval # Mock the delegate object configuration @hd.instance_variable_set(:@delegate_object, { output_script: false, user_must_approve: false }) # Test the method without user approval # Expectations and assertions go here end - def test_compile_execute_bash_and_special_blocks_and_trigger_reuse_with_user_approval + def test_compile_execute_and_trigger_reuse_with_user_approval # Mock the delegate object configuration @hd.instance_variable_set(:@delegate_object, { output_script: false, user_must_approve: true }) # Test the method with user approval # Expectations and assertions go here end - def test_compile_execute_bash_and_special_blocks_and_trigger_reuse_with_output_script + def test_compile_execute_and_trigger_reuse_with_output_script # Mock the delegate object configuration @hd.instance_variable_set(:@delegate_object, { output_script: true, user_must_approve: false })