lib/hash_delegator.rb in markdown_exec-1.8.7 vs lib/hash_delegator.rb in markdown_exec-1.8.8

- old
+ new

@@ -1,8 +1,10 @@ -# encoding=utf-8 +#!/usr/bin/env bundle exec ruby # frozen_string_literal: true +# encoding=utf-8 + require 'English' require 'clipboard' require 'fileutils' require 'open3' require 'optparse' @@ -140,13 +142,13 @@ # return if temp_blocks_file_path.nil? || temp_blocks_file_path.empty? # HashDelegator.remove_file_without_standard_errors(temp_blocks_file_path) # end - def error_handler(name = '', opts = {}) + def error_handler(name = '', opts = {}, error: $!) Exceptions.error_handler( - "HashDelegator.#{name} -- #{$!}", + "HashDelegator.#{name} -- #{error}", opts ) end # # DebugHelper.d ["HDmm method_name: #{method_name}", "#{first_n_caller_items 1}"] @@ -553,10 +555,26 @@ HashDelegator.error_handler('blocks_from_nested_files') end # private + def calc_logged_stdout_filename + return unless @delegate_object[:saved_stdout_folder] + + @delegate_object[:logged_stdout_filename] = + SavedAsset.stdout_name(blockname: @delegate_object[:block_name], + filename: File.basename(@delegate_object[:filename], + '.*'), + prefix: @delegate_object[:logged_stdout_filename_prefix], + time: Time.now.utc) + + @logged_stdout_filespec = + @delegate_object[:logged_stdout_filespec] = + File.join @delegate_object[:saved_stdout_folder], + @delegate_object[:logged_stdout_filename] + end + def cfile @cfile ||= CachedNestedFileReader.new( import_pattern: @delegate_object.fetch(:import_pattern) #, "^ *@import +(?<name>.+?) *$") ) end @@ -583,11 +601,11 @@ # @return [Array<String>] Required code blocks as an array of lines. def collect_required_code_lines(mdoc, selected, link_state = LinkState.new, block_source:) set_environment_variables_for_block(selected) if selected[:shell] == BlockType::VARS required = mdoc.collect_recursively_required_code( - @delegate_object[:block_name], + selected[:nickname] || selected[:oname], label_format_above: @delegate_object[:shell_code_label_format_above], label_format_below: @delegate_object[:shell_code_label_format_below], block_source: block_source ) dependencies = (link_state&.inherited_dependencies || {}).merge(required[:dependencies] || {}) @@ -597,11 +615,12 @@ ### filter against link_state.inherited_block_names warn format_and_highlight_dependencies(dependencies, highlight: required[:unmet_dependencies]) runtime_exception(:runtime_exception_error_level, - 'unmet_dependencies, flag: runtime_exception_error_level', required[:unmet_dependencies]) + 'unmet_dependencies, flag: runtime_exception_error_level', + required[:unmet_dependencies]) elsif true warn format_and_highlight_dependencies(dependencies, highlight: [@delegate_object[:block_name]]) end @@ -611,30 +630,58 @@ def command_execute(command, args: []) @run_state.files = Hash.new([]) @run_state.options = @delegate_object @run_state.started_at = Time.now.utc - Open3.popen3(@delegate_object[:shell], - '-c', command, - @delegate_object[:filename], - *args) do |stdin, stdout, stderr, exec_thr| - handle_stream(stdout, ExecutionStreams::StdOut) do |line| - yield nil, line, nil, exec_thr if block_given? - end - handle_stream(stderr, ExecutionStreams::StdErr) do |line| - yield nil, nil, line, exec_thr if block_given? - end + if @delegate_object[:execute_in_own_window] && + @delegate_object[:execute_command_format].present? && + @run_state.saved_filespec.present? + @run_state.in_own_window = true + system( + format( + @delegate_object[:execute_command_format], + { + batch_index: @run_state.batch_index, + batch_random: @run_state.batch_random, + block_name: @delegate_object[:block_name], + document_filename: File.basename(@delegate_object[:filename]), + document_filespec: @delegate_object[:filename], + home: Dir.pwd, + output_filename: File.basename(@delegate_object[:logged_stdout_filespec]), + output_filespec: @delegate_object[:logged_stdout_filespec], + script_filename: @run_state.saved_filespec, + script_filespec: File.join(Dir.pwd, @run_state.saved_filespec), + started_at: @run_state.started_at.strftime( + @delegate_object[:execute_command_title_time_format] + ) + } + ) + ) - in_thr = handle_stream($stdin, ExecutionStreams::StdIn) do |line| - stdin.puts(line) - yield line, nil, nil, exec_thr if block_given? - end + else + @run_state.in_own_window = false + Open3.popen3(@delegate_object[:shell], + '-c', command, + @delegate_object[:filename], + *args) do |stdin, stdout, stderr, exec_thr| + handle_stream(stdout, ExecutionStreams::StdOut) do |line| + yield nil, line, nil, exec_thr if block_given? + end + handle_stream(stderr, ExecutionStreams::StdErr) do |line| + yield nil, nil, line, exec_thr if block_given? + end - wait_for_stream_processing - exec_thr.join - sleep 0.1 - in_thr.kill if in_thr&.alive? + in_thr = handle_stream($stdin, ExecutionStreams::StdIn) do |line| + stdin.puts(line) + yield line, nil, nil, exec_thr if block_given? + end + + wait_for_stream_processing + exec_thr.join + sleep 0.1 + in_thr.kill if in_thr&.alive? + end end @run_state.completed_at = Time.now.utc rescue Errno::ENOENT => err # Handle ENOENT error @@ -679,13 +726,17 @@ 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 + allow_execution = if @delegate_object[:user_must_approve] + prompt_for_user_approval(required_lines, selected) + else + true + end - execute_required_lines(required_lines) if allow_execution + execute_required_lines(required_lines, selected) if allow_execution link_state.block_name = nil LoadFileLinkState.new(LoadFile::Reuse, link_state) end @@ -702,11 +753,12 @@ # Assumes that every fenced block starts and ends with a distinct line (hence divided by 2). # # @return [Integer] The count of fenced code blocks in the file. def count_blocks_in_filename regex = Regexp.new(@delegate_object[:fenced_start_and_end_regex]) - lines = cfile.readlines(@delegate_object[:filename]) + lines = cfile.readlines(@delegate_object[:filename], + import_paths: @delegate_object[:import_paths]&.split(':')) HashDelegator.count_matches_in_lines(lines, regex) / 2 end ## # Creates and adds a formatted block to the blocks array based on the provided match and format options. @@ -732,18 +784,18 @@ # @param fcb [FCB] The file control block being processed. # @param opts [Hash] Options containing configuration for line processing. # @param use_chrome [Boolean] Indicates if the chrome styling should be applied. def create_and_add_chrome_blocks(blocks, fcb) match_criteria = [ - { match: :heading1_match, format: :menu_heading1_format, color: :menu_heading1_color }, - { match: :heading2_match, format: :menu_heading2_format, color: :menu_heading2_color }, - { match: :heading3_match, format: :menu_heading3_format, color: :menu_heading3_color }, - { match: :menu_divider_match, format: :menu_divider_format, - color: :menu_divider_color }, - { match: :menu_note_match, format: :menu_note_format, color: :menu_note_color }, - { match: :menu_task_match, format: :menu_task_format, color: :menu_task_color } + { color: :menu_heading1_color, format: :menu_heading1_format, match: :heading1_match }, + { color: :menu_heading2_color, format: :menu_heading2_format, match: :heading2_match }, + { color: :menu_heading3_color, format: :menu_heading3_format, match: :heading3_match }, + { color: :menu_divider_color, format: :menu_divider_format, match: :menu_divider_match }, + { color: :menu_note_color, format: :menu_note_format, match: :menu_note_match }, + { color: :menu_task_color, format: :menu_task_format, match: :menu_task_match } ] + # rubocop:enable Style/UnlessElse match_criteria.each do |criteria| unless @delegate_object[criteria[:match]].present? && (mbody = fcb.body[0].match @delegate_object[criteria[:match]]) next end @@ -765,10 +817,33 @@ dname: string_send_color(oname, :menu_divider_color), oname: oname ) end + # Prompts user if named block is the same as the prior execution. + # + # @return [Boolean] Execute the named block. + def debounce_allows + return true unless @delegate_object[:debounce_execution] + + # filter block if selected in menu + return true if @run_state.block_name_from_cli + + # return false if @prior_execution_block == @delegate_object[:block_name] + if @prior_execution_block == @delegate_object[:block_name] + return @allowed_execution_block == @prior_execution_block || prompt_approve_repeat + end + + @prior_execution_block = @delegate_object[:block_name] + @allowed_execution_block = nil + true + end + + def debounce_reset + @prior_execution_block = nil + end + # Determines the state of a selected block in the menu based on the selected option. # It categorizes the selected option into either EXIT, BACK, or CONTINUE state. # # @param selected_option [Hash] The selected menu option. # @return [SelectedBlockMenuState] An object representing the state of the selected block. @@ -801,19 +876,27 @@ def divider_formatting_present?(position) divider_key = position == :initial ? :menu_initial_divider : :menu_final_divider @delegate_object[:menu_divider_format].present? && @delegate_object[divider_key].present? end + def do_save_execution_output + return unless @delegate_object[:save_execution_output] + return if @run_state.in_own_window + + HashDelegator.write_execution_output_to_file(@run_state.files, + @delegate_object[:logged_stdout_filespec]) + end + # Executes a block of code that has been approved for execution. # It sets the script block name, writes command files if required, and handles the execution # including output formatting and summarization. # # @param required_lines [Array<String>] The lines of code to be executed. # @param selected [FCB] The selected functional code block object. - def execute_required_lines(required_lines = []) - # @run_state.script_block_name = selected[:oname] - write_command_file(required_lines) if @delegate_object[:save_executed_script] + def execute_required_lines(required_lines = [], selected = FCB.new) + write_command_file(required_lines, selected) if @delegate_object[:save_executed_script] + calc_logged_stdout_filename format_and_execute_command(required_lines) post_execution_process end # Execute a code block after approval and provide user interaction options. @@ -826,25 +909,30 @@ # @param mdoc [YourMDocClass] An instance of the MDoc class. # def execute_shell_type(selected, mdoc, link_state = LinkState.new, block_source:) if selected.fetch(:shell, '') == BlockType::LINK + debounce_reset push_link_history_and_trigger_load(selected.fetch(:body, ''), mdoc, selected, link_state) elsif @menu_user_clicked_back_link + debounce_reset pop_link_history_and_trigger_load elsif selected[:shell] == BlockType::OPTS + debounce_reset options_state = read_show_options_and_trigger_reuse(selected, link_state) @menu_base_options.merge!(options_state.options) @delegate_object.merge!(options_state.options) options_state.load_file_link_state - else + elsif debounce_allows compile_execute_and_trigger_reuse(mdoc, selected, link_state, block_source: block_source) + else + LoadFileLinkState.new(LoadFile::Reuse, link_state) 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. @@ -902,16 +990,35 @@ @delegate_object[:block_stdin_scan]) fcb.stdout = extract_named_captures_from_option(titlexcall, @delegate_object[:block_stdout_scan]) shell_color_option = SHELL_COLOR_OPTIONS[fcb[:shell]] - fcb.title = fcb.oname = bm && bm[1] ? bm[:title] : titlexcall - fcb.dname = apply_shell_color_option(fcb.oname, shell_color_option) + if @delegate_object[:block_name_nick_match].present? && fcb.oname =~ Regexp.new(@delegate_object[:block_name_nick_match]) + fcb.nickname = $~[0] + fcb.title = fcb.oname = format_multiline_body_as_title(fcb.body) + else + fcb.title = fcb.oname = bm && bm[1] ? bm[:title] : titlexcall + end + + fcb.dname = HashDelegator.indent_all_lines( + apply_shell_color_option(fcb.oname, shell_color_option), + fcb.fetch(:indent, nil) + ) fcb end + # Formats multiline body content as a title string. + # indents all but first line with two spaces so it displays correctly in menu + # @param body_lines [Array<String>] The lines of body content. + # @return [String] Formatted title. + def format_multiline_body_as_title(body_lines) + body_lines.map.with_index do |line, index| + index.zero? ? line : " #{line}" + end.join("\n") + "\n" + end + # Updates the delegate object's state based on the provided block state. # It sets the block name and determines if the user clicked the back link in the menu. # # @param block_state [Object] An object representing the state of a block in the menu. def handle_back_or_continue(block_state) @@ -960,46 +1067,86 @@ in_fenced_block: false, headings: [] } end - def initialize_and_save_execution_output - return unless @delegate_object[:save_execution_output] - - @delegate_object[:logged_stdout_filename] = - SavedAsset.stdout_name(blockname: @delegate_object[:block_name], - filename: File.basename(@delegate_object[:filename], - '.*'), - prefix: @delegate_object[:logged_stdout_filename_prefix], - time: Time.now.utc) - - @logged_stdout_filespec = - @delegate_object[:logged_stdout_filespec] = - File.join @delegate_object[:saved_stdout_folder], - @delegate_object[:logged_stdout_filename] - @logged_stdout_filespec = @delegate_object[:logged_stdout_filespec] - HashDelegator.write_execution_output_to_file(@run_state.files, - @delegate_object[:logged_stdout_filespec]) - end - # Iterates through blocks in a file, applying the provided block to each line. # The iteration only occurs if the file exists. # @yield [Symbol] :filter Yields to obtain selected messages for processing. def iter_blocks_from_nested_files(&block) return unless check_file_existence(@delegate_object[:filename]) state = initial_state selected_messages = yield :filter - - cfile.readlines(@delegate_object[:filename]).each do |nested_line| + cfile.readlines(@delegate_object[:filename], + import_paths: @delegate_object[:import_paths]&.split(':')).each do |nested_line| if nested_line update_line_and_block_state(nested_line, state, selected_messages, &block) end end end + def link_block_data_eval(link_state, code_lines, selected, link_block_data) + all_code = HashDelegator.code_merge(link_state&.inherited_lines, code_lines) + + if link_block_data.fetch(LinkDataKeys::Exec, false) + @run_state.files = Hash.new([]) + output_lines = [] + + Open3.popen3( + @delegate_object[:shell], + '-c', all_code.join("\n") + ) do |stdin, stdout, stderr, _exec_thr| + handle_stream(stdout, ExecutionStreams::StdOut) do |line| + output_lines.push(line) + end + handle_stream(stderr, ExecutionStreams::StdErr) do |line| + output_lines.push(line) + end + + in_thr = handle_stream($stdin, ExecutionStreams::StdIn) do |line| + stdin.puts(line) + end + + wait_for_stream_processing + sleep 0.1 + in_thr.kill if in_thr&.alive? + end + + ## select output_lines that look like assignment or match other specs + # + output_lines = process_string_array( + output_lines, + begin_pattern: @delegate_object.fetch(:output_assignment_begin, nil), + end_pattern: @delegate_object.fetch(:output_assignment_end, nil), + scan1: @delegate_object.fetch(:output_assignment_match, nil), + format1: @delegate_object.fetch(:output_assignment_format, nil) + ) + + else + output_lines = `#{all_code.join("\n")}`.split("\n") + end + + unless output_lines + HashDelegator.error_handler('all_code eval output_lines is nil', { abort: true }) + end + + label_format_above = @delegate_object[:shell_code_label_format_above] + label_format_below = @delegate_object[:shell_code_label_format_below] + block_source = { document_filename: link_state&.document_filename } + + [label_format_above && format(label_format_above, + block_source.merge({ block_name: selected[:oname] }))] + + output_lines.map do |line| + re = Regexp.new(link_block_data.fetch('pattern', '(?<line>.*)')) + re.gsub_format(line, link_block_data.fetch('format', '%{line}')) if re =~ line + end.compact + + [label_format_below && format(label_format_below, + block_source.merge({ block_name: selected[:oname] }))] + end + def link_history_push_and_next( curr_block_name:, curr_document_filename:, inherited_block_names:, inherited_dependencies:, inherited_lines:, next_block_name:, next_document_filename:, next_load_file: @@ -1163,27 +1310,42 @@ :output_execution_label_value_color) }, format_sym: :output_execution_label_format ), level: level end - def pop_add_current_code_to_head_and_trigger_load(_link_state, block_names, code_lines, - dependencies) + def pop_add_current_code_to_head_and_trigger_load(link_state, block_names, code_lines, + dependencies, selected) pop = @link_history.pop # updatable - next_state = LinkState.new( - block_name: pop.block_name, - document_filename: pop.document_filename, - inherited_block_names: - (pop.inherited_block_names + block_names).sort.uniq, - inherited_dependencies: - dependencies.merge(pop.inherited_dependencies || {}), ### merge, not replace, key data - inherited_lines: - HashDelegator.code_merge(pop.inherited_lines, code_lines) - ) - @link_history.push(next_state) + if pop.document_filename + next_state = LinkState.new( + block_name: pop.block_name, + document_filename: pop.document_filename, + inherited_block_names: + (pop.inherited_block_names + block_names).sort.uniq, + inherited_dependencies: + dependencies.merge(pop.inherited_dependencies || {}), ### merge, not replace, key data + inherited_lines: + HashDelegator.code_merge(pop.inherited_lines, code_lines) + ) + @link_history.push(next_state) - next_state.block_name = nil - LoadFileLinkState.new(LoadFile::Load, next_state) + next_state.block_name = nil + LoadFileLinkState.new(LoadFile::Load, next_state) + else + # no history exists; must have been called independently => retain script + link_history_push_and_next( + curr_block_name: selected[:oname], + curr_document_filename: @delegate_object[:filename], + inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq, + inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data + inherited_lines: HashDelegator.code_merge(link_state&.inherited_lines, code_lines), + next_block_name: '', # not link_block_data['block'] || '' + next_document_filename: @delegate_object[:filename], # not next_document_filename + next_load_file: LoadFile::Reuse # not next_document_filename == @delegate_object[:filename] ? LoadFile::Reuse : LoadFile::Load + ) + # LoadFileLinkState.new(LoadFile::Reuse, link_state) + end end # This method handles the back-link operation in the Markdown execution context. # It updates the history state and prepares to load the next block. # @@ -1198,11 +1360,11 @@ inherited_lines: peek.inherited_lines )) end def post_execution_process - initialize_and_save_execution_output + do_save_execution_output output_execution_summary output_execution_result end # Prepare the blocks menu by adding labels and other necessary details. @@ -1214,11 +1376,11 @@ menu_blocks.map do |fcb| next if Filter.prepared_not_in_menu?(@delegate_object, fcb, %i[block_name_include_match block_name_wrapper_match]) fcb.merge!( - name: HashDelegator.indent_all_lines(fcb.dname, fcb.fetch(:indent, nil)), + name: fcb.dname, label: BlockLabel.make( body: fcb[:body], filename: @delegate_object[:filename], headings: fcb.fetch(:headings, []), menu_blocks_with_docname: @delegate_object[:menu_blocks_with_docname], @@ -1248,10 +1410,65 @@ when :line create_and_add_chrome_blocks(blocks, fcb) unless @delegate_object[:no_chrome] end end + def process_string_array(arr, begin_pattern: nil, end_pattern: nil, scan1: nil, + format1: nil) + in_block = !begin_pattern.present? + collected_lines = [] + + arr.each do |line| + if in_block + if end_pattern.present? && line.match?(end_pattern) + in_block = false + elsif scan1.present? + if format1.present? + caps = extract_named_captures_from_option(line, scan1) + if caps + formatted = format(format1, caps) + collected_lines << formatted + end + else + caps = line.match(scan1) + if caps + formatted = caps[0] + collected_lines << formatted + end + end + else + collected_lines << line + end + elsif begin_pattern.present? && line.match?(begin_pattern) + in_block = true + end + end + + collected_lines + end + + def prompt_approve_repeat + sel = @prompt.select( + string_send_color(@delegate_object[:prompt_debounce], + :prompt_color_after_script_execution), + default: @delegate_object[:prompt_no], + filter: true, + quiet: true + ) do |menu| + menu.choice @delegate_object[:prompt_yes] + menu.choice @delegate_object[:prompt_no] + menu.choice @delegate_object[:prompt_uninterrupted] + end + return false if sel == @delegate_object[:prompt_no] + return true if sel == @delegate_object[:prompt_yes] + + @allowed_execution_block = @prior_execution_block + true + rescue TTY::Reader::InputInterrupt + exit 1 + end + ## # Presents a menu to the user for approving an action and performs additional tasks based on the selection. # The function provides options for approval, rejection, copying data to clipboard, or saving data to a file. # # @param opts [Hash] A hash containing various options for the menu. @@ -1263,11 +1480,11 @@ # @option opts [String] :prompt_script_to_clipboard Text for the 'Copy to Clipboard' choice in the menu. # @option opts [String] :prompt_save_script Text for the 'Save to File' choice in the menu. # # @return [Boolean] Returns true if the user approves (selects 'Yes'), false otherwise. ## - def prompt_for_user_approval(required_lines) + def prompt_for_user_approval(required_lines, selected) # Present a selection menu for user approval. sel = @prompt.select( string_send_color(@delegate_object[:prompt_approve_block], :prompt_color_after_script_execution), filter: true @@ -1283,11 +1500,11 @@ end if sel == MenuOptions::SCRIPT_TO_CLIPBOARD copy_to_clipboard(required_lines) elsif sel == MenuOptions::SAVE_SCRIPT - save_to_file(required_lines) + save_to_file(required_lines, selected) end sel == MenuOptions::YES rescue TTY::Reader::InputInterrupt exit 1 @@ -1324,10 +1541,11 @@ # load key and values from link block into current environment # (link_block_data['vars'] || []).each do |(key, value)| ENV[key] = value.to_s + ### add to inherited_lines end ## collect blocks specified by block # if mdoc @@ -1345,35 +1563,26 @@ code_lines = [] dependencies = {} end next_document_filename = link_block_data['file'] || @delegate_object[:filename] - # if an eval link block, evaluate code_lines and return its standard output + ## append blocks loaded per LinkDataKeys::Load # - if link_block_data.fetch('eval', false) - all_code = HashDelegator.code_merge(link_state&.inherited_lines, code_lines) - output = `#{all_code.join("\n")}`.split("\n") - label_format_above = @delegate_object[:shell_code_label_format_above] - label_format_below = @delegate_object[:shell_code_label_format_below] - block_source = { document_filename: link_state&.document_filename } + if (load_filespec = link_block_data.fetch(LinkDataKeys::Load, '')).present? + code_lines += File.readlines(load_filespec, chomp: true) + end - code_lines = [label_format_above && format(label_format_above, - block_source.merge({ block_name: selected[:oname] }))] + - output.map do |line| - re = Regexp.new(link_block_data.fetch('pattern', '(?<line>.*)')) - if re =~ line - re.gsub_format(line, link_block_data.fetch('format', '%{line}')) - end - end.compact + - [label_format_below && format(label_format_below, - block_source.merge({ block_name: selected[:oname] }))] - + # if an eval link block, evaluate code_lines and return its standard output + # + if link_block_data.fetch(LinkDataKeys::Eval, + false) || link_block_data.fetch(LinkDataKeys::Exec, false) + code_lines = link_block_data_eval(link_state, code_lines, selected, link_block_data) end - if link_block_data['return'] + if link_block_data[LinkDataKeys::Return] pop_add_current_code_to_head_and_trigger_load(link_state, block_names, code_lines, - dependencies) + dependencies, selected) else link_history_push_and_next( curr_block_name: selected[:oname], curr_document_filename: @delegate_object[:filename], @@ -1404,12 +1613,12 @@ return unless (@delegate_object[exception_sym]).positive? exit @delegate_object[exception_sym] end - def save_to_file(required_lines) - write_command_file(required_lines) + def save_to_file(required_lines, selected) + write_command_file(required_lines, selected) @fout.fout "File saved: #{@run_state.saved_filespec}" end # Select and execute a code block from a Markdown document. # @@ -1421,41 +1630,47 @@ @menu_base_options = @delegate_object link_state = LinkState.new( block_name: @delegate_object[:block_name], document_filename: @delegate_object[:filename] ) - block_name_from_cli = link_state.block_name.present? + @run_state.block_name_from_cli = link_state.block_name.present? @cli_block_name = link_state.block_name - now_using_cli = block_name_from_cli + now_using_cli = @run_state.block_name_from_cli menu_default_dname = nil + @run_state.batch_random = Random.new.rand + @run_state.batch_index = 0 + loop do + @run_state.batch_index += 1 + @run_state.in_own_window = false + # &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) + @run_state.block_name_from_cli, now_using_cli, blocks_in_file, menu_blocks, mdoc = \ + set_delobj_menu_loop_vars(@run_state.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) - # &bsp 'block_name_from_cli:',block_name_from_cli + # &bsp '@run_state.block_name_from_cli:',@run_state.block_name_from_cli if !block_state HashDelegator.error_handler('block_state missing', { abort: true }) elsif block_state.state == MenuState::EXIT # &bsp 'load_cli_or_user_selected_block -> 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) + if prompt_user_exit(@run_state.block_name_from_cli, block_state.block) # &bsp 'prompt_user_exit -> break' break end - link_state.block_name, block_name_from_cli, cli_break = \ + link_state.block_name, @run_state.block_name_from_cli, cli_break = \ HashDelegator.next_link_state(!shift_cli_argument, now_using_cli, block_state) if !block_state.block[:block_name_from_ui] && cli_break # &bsp '!block_name_from_ui + cli_break -> break' break @@ -1570,15 +1785,20 @@ # 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, names, opts.merge(filter: true)) + item = if names.first.instance_of?(String) { dname: selection } else names.find { |item| item[:dname] == selection } end + unless item + HashDelegator.error_handler('select_option_with_metadata', error: 'menu item not found') + exit 1 + end item.merge( if selection == menu_chrome_colored_option(:menu_option_back_name) { option: selection, shell: BlockType::LINK } elsif selection == menu_chrome_colored_option(:menu_option_exit_name) @@ -1592,11 +1812,11 @@ rescue StandardError HashDelegator.error_handler('select_option_with_metadata') end def set_environment_variables_for_block(selected) - YAML.load(selected[:body].join("\n")).each do |key, value| + YAML.load(selected[:body].join("\n"))&.each do |key, value| ENV[key] = value.to_s next unless @delegate_object[:menu_vars_set_format].present? formatted_string = format(@delegate_object[:menu_vars_set_format], { key: key, value: value }) @@ -1621,26 +1841,35 @@ req[1..-1] end) do |name| !name.match(Regexp.new(@delegate_object[:block_name_wrapper_match])) end + dname = oname = title = '' + nickname = nil + if @delegate_object[:block_name_nick_match].present? && oname =~ Regexp.new(@delegate_object[:block_name_nick_match]) + nickname = $~[0] + else + dname = oname = title = fcb_title_groups.fetch(:name, '') + end + MarkdownExec::FCB.new( body: [], call: rest.match(Regexp.new(@delegate_object[:block_calls_scan]))&.to_a&.first, - dname: fcb_title_groups.fetch(:name, ''), + dname: dname, headings: headings, indent: fcb_title_groups.fetch(:indent, ''), - oname: fcb_title_groups.fetch(:name, ''), + nickname: nickname, + oname: oname, reqs: reqs, shell: fcb_title_groups.fetch(:shell, ''), stdin: if (tn = rest.match(/<(?<type>\$)?(?<name>[A-Za-z_-]\S+)/)) tn.named_captures.sym_keys end, stdout: if (tn = rest.match(/>(?<type>\$)?(?<name>[A-Za-z_\-.\w]+)/)) tn.named_captures.sym_keys end, - title: fcb_title_groups.fetch(:name, ''), + title: title, wraps: wraps ) end # Applies a color method to a string based on the provided color symbol. @@ -1765,19 +1994,21 @@ selection_opts) determine_block_state(selected_option) end # Handles the core logic for generating the command file's metadata and content. - def write_command_file(required_lines) + def write_command_file(required_lines, selected) return unless @delegate_object[:save_executed_script] time_now = Time.now.utc @run_state.saved_script_filename = - SavedAsset.script_name(blockname: @delegate_object[:block_name], - filename: @delegate_object[:filename], - prefix: @delegate_object[:saved_script_filename_prefix], - time: time_now) + SavedAsset.script_name( + blockname: selected[:nickname] || selected[:oname], + filename: @delegate_object[:filename], + prefix: @delegate_object[:saved_script_filename_prefix], + time: time_now + ) @run_state.saved_filespec = File.join(@delegate_object[:saved_script_folder], @run_state.saved_script_filename) shebang = if @delegate_object[:shebang]&.present? @@ -1822,850 +2053,852 @@ HashDelegator.write_code_to_file(code_blocks, temp_file_path) end end end -if $PROGRAM_NAME == __FILE__ - require 'bundler/setup' - Bundler.require(:default) +return if $PROGRAM_NAME != __FILE__ - require 'minitest/autorun' - require 'mocha/minitest' +require 'bundler/setup' +Bundler.require(:default) - module MarkdownExec - class TestHashDelegator < Minitest::Test - def setup - @hd = HashDelegator.new - @mdoc = mock('MarkdownDocument') - end +require 'minitest/autorun' +require 'mocha/minitest' - def test_calling_execute_required_lines_calls_command_execute_with_argument_args_value - pigeon = 'E' - obj = { - output_execution_label_format: '', - output_execution_label_name_color: 'plain', - output_execution_label_value_color: 'plain' - } +module MarkdownExec + class TestHashDelegator < Minitest::Test + def setup + @hd = HashDelegator.new + @mdoc = mock('MarkdownDocument') + end - c = MarkdownExec::HashDelegator.new(obj) - c.pass_args = pigeon + def test_calling_execute_required_lines_calls_command_execute_with_argument_args_value + pigeon = 'E' + obj = { + output_execution_label_format: '', + output_execution_label_name_color: 'plain', + output_execution_label_value_color: 'plain' + } - # Expect that method opts_command_execute is called with argument args having value pigeon - c.expects(:command_execute).with( - '', - args: pigeon - ) + c = MarkdownExec::HashDelegator.new(obj) + c.pass_args = pigeon - # Call method opts_execute_required_lines - c.execute_required_lines([]) - end + # Expect that method opts_command_execute is called with argument args having value pigeon + c.expects(:command_execute).with( + '', + args: pigeon + ) - # Test case for empty body - def test_push_link_history_and_trigger_load_with_empty_body - assert_equal LoadFile::Reuse, - @hd.push_link_history_and_trigger_load([], nil, FCB.new).load_file - end + # Call method opts_execute_required_lines + c.execute_required_lines + end - # Test case for non-empty body without 'file' key - def test_push_link_history_and_trigger_load_without_file_key - body = ["vars:\n KEY: VALUE"] - assert_equal LoadFile::Reuse, - @hd.push_link_history_and_trigger_load(body, nil, FCB.new).load_file - end + # Test case for empty body + def test_push_link_history_and_trigger_load_with_empty_body + assert_equal LoadFile::Reuse, + @hd.push_link_history_and_trigger_load([], nil, FCB.new).load_file + end - # Test case for non-empty body with 'file' key - def test_push_link_history_and_trigger_load_with_file_key - body = ["file: sample_file\nblock: sample_block\nvars:\n KEY: VALUE"] - expected_result = LoadFileLinkState.new(LoadFile::Load, - LinkState.new(block_name: 'sample_block', - document_filename: 'sample_file', - inherited_dependencies: {}, - inherited_lines: [])) - assert_equal expected_result, - @hd.push_link_history_and_trigger_load(body, nil, FCB.new(block_name: 'sample_block', - filename: 'sample_file')) - end + # Test case for non-empty body without 'file' key + def test_push_link_history_and_trigger_load_without_file_key + body = ["vars:\n KEY: VALUE"] + assert_equal LoadFile::Reuse, + @hd.push_link_history_and_trigger_load(body, nil, FCB.new).load_file + end - def test_indent_all_lines_with_indent - body = "Line 1\nLine 2" - indent = ' ' # Two spaces - expected_result = " Line 1\n Line 2" - assert_equal expected_result, HashDelegator.indent_all_lines(body, indent) - end + # Test case for non-empty body with 'file' key + def test_push_link_history_and_trigger_load_with_file_key + body = ["file: sample_file\nblock: sample_block\nvars:\n KEY: VALUE"] + expected_result = LoadFileLinkState.new(LoadFile::Load, + LinkState.new(block_name: 'sample_block', + document_filename: 'sample_file', + inherited_dependencies: {}, + inherited_lines: [])) + assert_equal expected_result, + @hd.push_link_history_and_trigger_load(body, nil, FCB.new(block_name: 'sample_block', + filename: 'sample_file')) + end - def test_indent_all_lines_without_indent - body = "Line 1\nLine 2" - indent = nil + def test_indent_all_lines_with_indent + body = "Line 1\nLine 2" + indent = ' ' # Two spaces + expected_result = " Line 1\n Line 2" + assert_equal expected_result, HashDelegator.indent_all_lines(body, indent) + end - assert_equal body, HashDelegator.indent_all_lines(body, indent) - end + def test_indent_all_lines_without_indent + body = "Line 1\nLine 2" + indent = nil - def test_indent_all_lines_with_empty_indent - body = "Line 1\nLine 2" - indent = '' + assert_equal body, HashDelegator.indent_all_lines(body, indent) + end - assert_equal body, HashDelegator.indent_all_lines(body, indent) - end + def test_indent_all_lines_with_empty_indent + body = "Line 1\nLine 2" + indent = '' - def test_safeval_successful_evaluation - assert_equal 4, HashDelegator.safeval('2 + 2') - end + assert_equal body, HashDelegator.indent_all_lines(body, indent) + end - def test_safeval_rescue_from_error - HashDelegator.stubs(:error_handler).with('safeval') - assert_nil HashDelegator.safeval('invalid code') - end + def test_safeval_successful_evaluation + assert_equal 4, HashDelegator.safeval('2 + 2') + end - def test_set_fcb_title - # sample input and output data for testing default_block_title_from_body method - input_output_data = [ - { - input: MarkdownExec::FCB.new(title: nil, - body: ["puts 'Hello, world!'"]), - output: "puts 'Hello, world!'" - }, - { - input: MarkdownExec::FCB.new(title: '', - body: ['def add(x, y)', - ' x + y', 'end']), - output: "def add(x, y)\n x + y\n end\n" - }, - { - input: MarkdownExec::FCB.new(title: 'foo', body: %w[bar baz]), - output: 'foo' # expect the title to remain unchanged - } - ] + def test_safeval_rescue_from_error + HashDelegator.stubs(:error_handler).with('safeval') + assert_nil HashDelegator.safeval('invalid code') + end - # iterate over the input and output data and - # assert that the method sets the title as expected - input_output_data.each do |data| - input = data[:input] - output = data[:output] - HashDelegator.default_block_title_from_body(input) - assert_equal output, input.title - end + def test_set_fcb_title + # sample input and output data for testing default_block_title_from_body method + input_output_data = [ + { + input: MarkdownExec::FCB.new(title: nil, + body: ["puts 'Hello, world!'"]), + output: "puts 'Hello, world!'" + }, + { + input: MarkdownExec::FCB.new(title: '', + body: ['def add(x, y)', + ' x + y', 'end']), + output: "def add(x, y)\n x + y\n end\n" + }, + { + input: MarkdownExec::FCB.new(title: 'foo', body: %w[bar baz]), + output: 'foo' # expect the title to remain unchanged + } + ] + + # iterate over the input and output data and + # assert that the method sets the title as expected + input_output_data.each do |data| + input = data[:input] + output = data[:output] + HashDelegator.default_block_title_from_body(input) + assert_equal output, input.title end + end - class TestHashDelegatorAppendDivider < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.instance_variable_set(:@delegate_object, { - menu_divider_format: 'Format', - menu_initial_divider: 'Initial Divider', - menu_final_divider: 'Final Divider', - menu_divider_color: :color - }) - @hd.stubs(:string_send_color).returns('Formatted Divider') - HashDelegator.stubs(:safeval).returns('Safe Value') - end + class TestHashDelegatorAppendDivider < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.instance_variable_set(:@delegate_object, { + menu_divider_format: 'Format', + menu_initial_divider: 'Initial Divider', + menu_final_divider: 'Final Divider', + menu_divider_color: :color + }) + @hd.stubs(:string_send_color).returns('Formatted Divider') + HashDelegator.stubs(:safeval).returns('Safe Value') + end - def test_append_divider_initial - menu_blocks = [] - @hd.append_divider(menu_blocks, :initial) + def test_append_divider_initial + menu_blocks = [] + @hd.append_divider(menu_blocks, :initial) - assert_equal 1, menu_blocks.size - assert_equal 'Formatted Divider', menu_blocks.first.dname - end + assert_equal 1, menu_blocks.size + assert_equal 'Formatted Divider', menu_blocks.first.dname + end - def test_append_divider_final - menu_blocks = [] - @hd.append_divider(menu_blocks, :final) + def test_append_divider_final + menu_blocks = [] + @hd.append_divider(menu_blocks, :final) - assert_equal 1, menu_blocks.size - assert_equal 'Formatted Divider', menu_blocks.last.dname - end + assert_equal 1, menu_blocks.size + assert_equal 'Formatted Divider', menu_blocks.last.dname + end - def test_append_divider_without_format - @hd.instance_variable_set(:@delegate_object, {}) - menu_blocks = [] - @hd.append_divider(menu_blocks, :initial) + def test_append_divider_without_format + @hd.instance_variable_set(:@delegate_object, {}) + menu_blocks = [] + @hd.append_divider(menu_blocks, :initial) - assert_empty menu_blocks - end + assert_empty menu_blocks end + end - class TestHashDelegatorBlockFind < Minitest::Test - def setup - @hd = HashDelegator.new - end + class TestHashDelegatorBlockFind < Minitest::Test + def setup + @hd = HashDelegator.new + end - def test_block_find_with_match - blocks = [{ key: 'value1' }, { key: 'value2' }] - result = HashDelegator.block_find(blocks, :key, 'value1') - assert_equal({ key: 'value1' }, result) - end + def test_block_find_with_match + blocks = [{ key: 'value1' }, { key: 'value2' }] + result = HashDelegator.block_find(blocks, :key, 'value1') + assert_equal({ key: 'value1' }, result) + end - def test_block_find_without_match - blocks = [{ key: 'value1' }, { key: 'value2' }] - result = HashDelegator.block_find(blocks, :key, 'value3') - assert_nil result - end + def test_block_find_without_match + blocks = [{ key: 'value1' }, { key: 'value2' }] + result = HashDelegator.block_find(blocks, :key, 'value3') + assert_nil result + end - def test_block_find_with_default - blocks = [{ key: 'value1' }, { key: 'value2' }] - result = HashDelegator.block_find(blocks, :key, 'value3', 'default') - assert_equal 'default', result - end + def test_block_find_with_default + blocks = [{ key: 'value1' }, { key: 'value2' }] + result = HashDelegator.block_find(blocks, :key, 'value3', 'default') + assert_equal 'default', result end + end - class TestHashDelegatorBlocksFromNestedFiles < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.stubs(:iter_blocks_from_nested_files).yields(:blocks, FCB.new) - @hd.stubs(:get_block_summary).returns(FCB.new) - @hd.stubs(:create_and_add_chrome_blocks) - @hd.instance_variable_set(:@delegate_object, {}) - HashDelegator.stubs(:error_handler) - end + class TestHashDelegatorBlocksFromNestedFiles < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.stubs(:iter_blocks_from_nested_files).yields(:blocks, FCB.new) + @hd.stubs(:get_block_summary).returns(FCB.new) + @hd.stubs(:create_and_add_chrome_blocks) + @hd.instance_variable_set(:@delegate_object, {}) + HashDelegator.stubs(:error_handler) + end - def test_blocks_from_nested_files - result = @hd.blocks_from_nested_files + def test_blocks_from_nested_files + result = @hd.blocks_from_nested_files - assert_kind_of Array, result - assert_kind_of FCB, result.first - end + assert_kind_of Array, result + assert_kind_of FCB, result.first + end - def test_blocks_from_nested_files_with_no_chrome - @hd.instance_variable_set(:@delegate_object, { no_chrome: true }) - @hd.expects(:create_and_add_chrome_blocks).never + def test_blocks_from_nested_files_with_no_chrome + @hd.instance_variable_set(:@delegate_object, { no_chrome: true }) + @hd.expects(:create_and_add_chrome_blocks).never - result = @hd.blocks_from_nested_files + result = @hd.blocks_from_nested_files - assert_kind_of Array, result - end + assert_kind_of Array, result end + end - class TestHashDelegatorCollectRequiredCodeLines < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.instance_variable_set(:@delegate_object, {}) - @mdoc = mock('YourMDocClass') - @selected = { shell: BlockType::VARS, body: ['key: value'] } - HashDelegator.stubs(:read_required_blocks_from_temp_file).returns([]) - @hd.stubs(:string_send_color) - @hd.stubs(:print) - end + class TestHashDelegatorCollectRequiredCodeLines < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.instance_variable_set(:@delegate_object, {}) + @mdoc = mock('YourMDocClass') + @selected = { shell: BlockType::VARS, body: ['key: value'] } + HashDelegator.stubs(:read_required_blocks_from_temp_file).returns([]) + @hd.stubs(:string_send_color) + @hd.stubs(:print) + end - def test_collect_required_code_lines_with_vars - YAML.stubs(:load).returns({ 'key' => 'value' }) - @mdoc.stubs(:collect_recursively_required_code).returns({ code: ['code line'] }) - result = @hd.collect_required_code_lines(@mdoc, @selected, block_source: {}) + def test_collect_required_code_lines_with_vars + YAML.stubs(:load).returns({ 'key' => 'value' }) + @mdoc.stubs(:collect_recursively_required_code).returns({ code: ['code line'] }) + result = @hd.collect_required_code_lines(@mdoc, @selected, block_source: {}) - assert_equal ['code line'], result - end + assert_equal ['code line'], result end + end - class TestHashDelegatorCommandOrUserSelectedBlock < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.instance_variable_set(:@delegate_object, {}) - HashDelegator.stubs(:error_handler) - @hd.stubs(:wait_for_user_selected_block) - end + class TestHashDelegatorCommandOrUserSelectedBlock < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.instance_variable_set(:@delegate_object, {}) + HashDelegator.stubs(:error_handler) + @hd.stubs(:wait_for_user_selected_block) + end - def test_command_selected_block - all_blocks = [{ oname: 'block1' }, { oname: 'block2' }] - @hd.instance_variable_set(:@delegate_object, - { block_name: 'block1' }) + def test_command_selected_block + all_blocks = [{ oname: 'block1' }, { oname: 'block2' }] + @hd.instance_variable_set(:@delegate_object, + { block_name: 'block1' }) - result = @hd.load_cli_or_user_selected_block(all_blocks, [], nil) + result = @hd.load_cli_or_user_selected_block(all_blocks, [], nil) - assert_equal all_blocks.first.merge(block_name_from_ui: false), result.block - assert_nil result.state - end + assert_equal all_blocks.first.merge(block_name_from_ui: false), 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) + 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.load_cli_or_user_selected_block([], [], nil) + result = @hd.load_cli_or_user_selected_block([], [], nil) - assert_equal block_state.block.merge(block_name_from_ui: true), result.block - assert_equal :some_state, result.state - end + assert_equal block_state.block.merge(block_name_from_ui: true), result.block + assert_equal :some_state, result.state end + end - class TestHashDelegatorCountBlockInFilename < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.instance_variable_set(:@delegate_object, - { fenced_start_and_end_regex: '^```', - filename: '/path/to/file' }) - @hd.stubs(:cfile).returns(mock('cfile')) - end + class TestHashDelegatorCountBlockInFilename < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.instance_variable_set(:@delegate_object, + { fenced_start_and_end_regex: '^```', + filename: '/path/to/file' }) + @hd.stubs(:cfile).returns(mock('cfile')) + end - def test_count_blocks_in_filename - file_content = ["```ruby\n", "puts 'Hello'\n", "```\n", - "```python\n", "print('Hello')\n", "```\n"] - @hd.cfile.stubs(:readlines).with('/path/to/file').returns(file_content) + def test_count_blocks_in_filename + file_content = ["```ruby\n", "puts 'Hello'\n", "```\n", + "```python\n", "print('Hello')\n", "```\n"] + @hd.cfile.stubs(:readlines).with('/path/to/file', + import_paths: nil).returns(file_content) - count = @hd.count_blocks_in_filename + count = @hd.count_blocks_in_filename - assert_equal 2, count - end + assert_equal 2, count + end - def test_count_blocks_in_filename_with_no_matches - file_content = ["puts 'Hello'\n", "print('Hello')\n"] - @hd.cfile.stubs(:readlines).with('/path/to/file').returns(file_content) + def test_count_blocks_in_filename_with_no_matches + file_content = ["puts 'Hello'\n", "print('Hello')\n"] + @hd.cfile.stubs(:readlines).with('/path/to/file', + import_paths: nil).returns(file_content) - count = @hd.count_blocks_in_filename + count = @hd.count_blocks_in_filename - assert_equal 0, count - end + assert_equal 0, count end + end - class TestHashDelegatorCreateAndWriteFile < Minitest::Test - def setup - @hd = HashDelegator.new - HashDelegator.stubs(:error_handler) - FileUtils.stubs(:mkdir_p) - File.stubs(:write) - File.stubs(:chmod) - end + class TestHashDelegatorCreateAndWriteFile < Minitest::Test + def setup + @hd = HashDelegator.new + HashDelegator.stubs(:error_handler) + FileUtils.stubs(:mkdir_p) + File.stubs(:write) + File.stubs(:chmod) + end - def test_create_file_and_write_string_with_permissions - file_path = '/path/to/file' - content = 'sample content' - chmod_value = 0o644 + def test_create_file_and_write_string_with_permissions + file_path = '/path/to/file' + content = 'sample content' + chmod_value = 0o644 - FileUtils.expects(:mkdir_p).with('/path/to').once - File.expects(:write).with(file_path, content).once - File.expects(:chmod).with(chmod_value, file_path).once + FileUtils.expects(:mkdir_p).with('/path/to').once + File.expects(:write).with(file_path, content).once + File.expects(:chmod).with(chmod_value, file_path).once - HashDelegator.create_file_and_write_string_with_permissions(file_path, content, - chmod_value) + HashDelegator.create_file_and_write_string_with_permissions(file_path, content, + chmod_value) - assert true # Placeholder for actual test assertions - end + assert true # Placeholder for actual test assertions + end - def test_create_and_write_file_without_chmod - file_path = '/path/to/file' - content = 'sample content' - chmod_value = 0 + def test_create_and_write_file_without_chmod + file_path = '/path/to/file' + content = 'sample content' + chmod_value = 0 - FileUtils.expects(:mkdir_p).with('/path/to').once - File.expects(:write).with(file_path, content).once - File.expects(:chmod).never + FileUtils.expects(:mkdir_p).with('/path/to').once + File.expects(:write).with(file_path, content).once + File.expects(:chmod).never - HashDelegator.create_file_and_write_string_with_permissions(file_path, content, - chmod_value) + HashDelegator.create_file_and_write_string_with_permissions(file_path, content, + chmod_value) - assert true # Placeholder for actual test assertions - end + assert true # Placeholder for actual test assertions end + end - class TestHashDelegatorDetermineBlockState < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.stubs(:menu_chrome_formatted_option).returns('Formatted Option') - end + class TestHashDelegatorDetermineBlockState < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.stubs(:menu_chrome_formatted_option).returns('Formatted Option') + end - def test_determine_block_state_exit - selected_option = { oname: 'Formatted Option' } - @hd.stubs(:menu_chrome_formatted_option).with(:menu_option_exit_name).returns('Formatted Option') + def test_determine_block_state_exit + selected_option = { oname: 'Formatted Option' } + @hd.stubs(:menu_chrome_formatted_option).with(:menu_option_exit_name).returns('Formatted Option') - result = @hd.determine_block_state(selected_option) + result = @hd.determine_block_state(selected_option) - assert_equal MenuState::EXIT, result.state - assert_nil result.block - end + assert_equal MenuState::EXIT, result.state + assert_nil result.block + end - def test_determine_block_state_back - selected_option = { oname: 'Formatted Back Option' } - @hd.stubs(:menu_chrome_formatted_option).with(:menu_option_back_name).returns('Formatted Back Option') - result = @hd.determine_block_state(selected_option) + def test_determine_block_state_back + selected_option = { oname: 'Formatted Back Option' } + @hd.stubs(:menu_chrome_formatted_option).with(:menu_option_back_name).returns('Formatted Back Option') + result = @hd.determine_block_state(selected_option) - assert_equal MenuState::BACK, result.state - assert_equal selected_option, result.block - end + assert_equal MenuState::BACK, result.state + assert_equal selected_option, result.block + end - def test_determine_block_state_continue - selected_option = { oname: 'Other Option' } + def test_determine_block_state_continue + selected_option = { oname: 'Other Option' } - result = @hd.determine_block_state(selected_option) + result = @hd.determine_block_state(selected_option) - assert_equal MenuState::CONTINUE, result.state - assert_equal selected_option, result.block - end + assert_equal MenuState::CONTINUE, result.state + assert_equal selected_option, result.block end + end - class TestHashDelegatorDisplayRequiredCode < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.instance_variable_set(:@fout, mock('fout')) - @hd.instance_variable_set(:@delegate_object, {}) - @hd.stubs(:string_send_color) - end + class TestHashDelegatorDisplayRequiredCode < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.instance_variable_set(:@fout, mock('fout')) + @hd.instance_variable_set(:@delegate_object, {}) + @hd.stubs(:string_send_color) + end - def test_display_required_code - required_lines = %w[line1 line2] - @hd.instance_variable_get(:@delegate_object).stubs(:[]).with(:script_preview_head).returns('Header') - @hd.instance_variable_get(:@delegate_object).stubs(:[]).with(:script_preview_tail).returns('Footer') - @hd.instance_variable_get(:@fout).expects(:fout).times(4) + def test_display_required_code + required_lines = %w[line1 line2] + @hd.instance_variable_get(:@delegate_object).stubs(:[]).with(:script_preview_head).returns('Header') + @hd.instance_variable_get(:@delegate_object).stubs(:[]).with(:script_preview_tail).returns('Footer') + @hd.instance_variable_get(:@fout).expects(:fout).times(4) - @hd.display_required_code(required_lines) + @hd.display_required_code(required_lines) - # Verifying that fout is called for each line and for header & footer - assert true # Placeholder for actual test assertions - end + # Verifying that fout is called for each line and for header & footer + assert true # Placeholder for actual test assertions end + end - class TestHashDelegatorFetchColor < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.instance_variable_set(:@delegate_object, {}) - end + class TestHashDelegatorFetchColor < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.instance_variable_set(:@delegate_object, {}) + end - def test_fetch_color_with_valid_data - @hd.instance_variable_get(:@delegate_object).stubs(:fetch).with( - :execution_report_preview_head, '' - ).returns('Data String') - @hd.stubs(:string_send_color).with('Data String', - :execution_report_preview_frame_color).returns('Colored Data String') + def test_fetch_color_with_valid_data + @hd.instance_variable_get(:@delegate_object).stubs(:fetch).with( + :execution_report_preview_head, '' + ).returns('Data String') + @hd.stubs(:string_send_color).with('Data String', + :execution_report_preview_frame_color).returns('Colored Data String') - result = @hd.fetch_color + result = @hd.fetch_color - assert_equal 'Colored Data String', result - end + assert_equal 'Colored Data String', result + end - def test_fetch_color_with_missing_data - @hd.instance_variable_get(:@delegate_object).stubs(:fetch).with( - :execution_report_preview_head, '' - ).returns('') - @hd.stubs(:string_send_color).with('', - :execution_report_preview_frame_color).returns('Default Colored String') + def test_fetch_color_with_missing_data + @hd.instance_variable_get(:@delegate_object).stubs(:fetch).with( + :execution_report_preview_head, '' + ).returns('') + @hd.stubs(:string_send_color).with('', + :execution_report_preview_frame_color).returns('Default Colored String') - result = @hd.fetch_color + result = @hd.fetch_color - assert_equal 'Default Colored String', result - end + assert_equal 'Default Colored String', result end + end - class TestHashDelegatorFormatReferencesSendColor < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.instance_variable_set(:@delegate_object, {}) - end + class TestHashDelegatorFormatReferencesSendColor < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.instance_variable_set(:@delegate_object, {}) + end - def test_format_references_send_color_with_valid_data - @hd.instance_variable_get(:@delegate_object).stubs(:fetch).with( - :output_execution_label_format, '' - ).returns('Formatted: %{key}') - @hd.stubs(:string_send_color).returns('Colored String') + def test_format_references_send_color_with_valid_data + @hd.instance_variable_get(:@delegate_object).stubs(:fetch).with( + :output_execution_label_format, '' + ).returns('Formatted: %{key}') + @hd.stubs(:string_send_color).returns('Colored String') - result = @hd.format_references_send_color(context: { key: 'value' }, - color_sym: :execution_report_preview_frame_color) + result = @hd.format_references_send_color(context: { key: 'value' }, + color_sym: :execution_report_preview_frame_color) - assert_equal 'Colored String', result - end + assert_equal 'Colored String', result + end - def test_format_references_send_color_with_missing_format - @hd.instance_variable_get(:@delegate_object).stubs(:fetch).with( - :output_execution_label_format, '' - ).returns('') - @hd.stubs(:string_send_color).returns('Default Colored String') + def test_format_references_send_color_with_missing_format + @hd.instance_variable_get(:@delegate_object).stubs(:fetch).with( + :output_execution_label_format, '' + ).returns('') + @hd.stubs(:string_send_color).returns('Default Colored String') - result = @hd.format_references_send_color(context: { key: 'value' }, - color_sym: :execution_report_preview_frame_color) + result = @hd.format_references_send_color(context: { key: 'value' }, + color_sym: :execution_report_preview_frame_color) - assert_equal 'Default Colored String', result - end + assert_equal 'Default Colored String', result end + end - class TestHashDelegatorFormatExecutionStreams < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.instance_variable_set(:@run_state, mock('run_state')) - end + class TestHashDelegatorFormatExecutionStreams < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.instance_variable_set(:@run_state, mock('run_state')) + end - def test_format_execution_streams_with_valid_key - result = HashDelegator.format_execution_streams(:stdout, - { stdout: %w[output1 output2] }) + def test_format_execution_streams_with_valid_key + result = HashDelegator.format_execution_streams(:stdout, + { stdout: %w[output1 output2] }) - assert_equal 'output1output2', result - end + assert_equal 'output1output2', result + end - def test_format_execution_streams_with_empty_key - @hd.instance_variable_get(:@run_state).stubs(:files).returns({}) + def test_format_execution_streams_with_empty_key + @hd.instance_variable_get(:@run_state).stubs(:files).returns({}) - result = HashDelegator.format_execution_streams(:stderr) + result = HashDelegator.format_execution_streams(:stderr) - assert_equal '', result - end + assert_equal '', result + end - def test_format_execution_streams_with_nil_files - @hd.instance_variable_get(:@run_state).stubs(:files).returns(nil) + def test_format_execution_streams_with_nil_files + @hd.instance_variable_get(:@run_state).stubs(:files).returns(nil) - result = HashDelegator.format_execution_streams(:stdin) + result = HashDelegator.format_execution_streams(:stdin) - assert_equal '', result - end + assert_equal '', result end + end - class TestHashDelegatorHandleBackLink < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.stubs(:history_state_pop) - end + class TestHashDelegatorHandleBackLink < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.stubs(:history_state_pop) + end - def test_pop_link_history_and_trigger_load - # Verifying that history_state_pop is called - # @hd.expects(:history_state_pop).once + def test_pop_link_history_and_trigger_load + # Verifying that history_state_pop is called + # @hd.expects(:history_state_pop).once - result = @hd.pop_link_history_and_trigger_load + result = @hd.pop_link_history_and_trigger_load - # Asserting the result is an instance of LoadFileLinkState - assert_instance_of LoadFileLinkState, result - assert_equal LoadFile::Load, result.load_file - assert_nil result.link_state.block_name - end + # Asserting the result is an instance of LoadFileLinkState + assert_instance_of LoadFileLinkState, result + assert_equal LoadFile::Load, result.load_file + assert_nil result.link_state.block_name end + end - class TestHashDelegatorHandleBlockState < Minitest::Test - def setup - @hd = HashDelegator.new - @mock_block_state = mock('block_state') - end + class TestHashDelegatorHandleBlockState < Minitest::Test + def setup + @hd = HashDelegator.new + @mock_block_state = mock('block_state') + end - def test_handle_back_or_continue_with_back - @mock_block_state.stubs(:state).returns(MenuState::BACK) - @mock_block_state.stubs(:block).returns({ oname: 'sample_block' }) + def test_handle_back_or_continue_with_back + @mock_block_state.stubs(:state).returns(MenuState::BACK) + @mock_block_state.stubs(:block).returns({ oname: 'sample_block' }) - @hd.handle_back_or_continue(@mock_block_state) + @hd.handle_back_or_continue(@mock_block_state) - assert_equal 'sample_block', - @hd.instance_variable_get(:@delegate_object)[:block_name] - assert @hd.instance_variable_get(:@menu_user_clicked_back_link) - end + assert_equal 'sample_block', + @hd.instance_variable_get(:@delegate_object)[:block_name] + assert @hd.instance_variable_get(:@menu_user_clicked_back_link) + end - def test_handle_back_or_continue_with_continue - @mock_block_state.stubs(:state).returns(MenuState::CONTINUE) - @mock_block_state.stubs(:block).returns({ oname: 'another_block' }) + def test_handle_back_or_continue_with_continue + @mock_block_state.stubs(:state).returns(MenuState::CONTINUE) + @mock_block_state.stubs(:block).returns({ oname: 'another_block' }) - @hd.handle_back_or_continue(@mock_block_state) + @hd.handle_back_or_continue(@mock_block_state) - assert_equal 'another_block', - @hd.instance_variable_get(:@delegate_object)[:block_name] - refute @hd.instance_variable_get(:@menu_user_clicked_back_link) - end + assert_equal 'another_block', + @hd.instance_variable_get(:@delegate_object)[:block_name] + refute @hd.instance_variable_get(:@menu_user_clicked_back_link) + end - def test_handle_back_or_continue_with_other - @mock_block_state.stubs(:state).returns(nil) # MenuState::OTHER - @mock_block_state.stubs(:block).returns({ oname: 'other_block' }) + def test_handle_back_or_continue_with_other + @mock_block_state.stubs(:state).returns(nil) # MenuState::OTHER + @mock_block_state.stubs(:block).returns({ oname: 'other_block' }) - @hd.handle_back_or_continue(@mock_block_state) + @hd.handle_back_or_continue(@mock_block_state) - assert_nil @hd.instance_variable_get(:@delegate_object)[:block_name] - assert_nil @hd.instance_variable_get(:@menu_user_clicked_back_link) - end + assert_nil @hd.instance_variable_get(:@delegate_object)[:block_name] + assert_nil @hd.instance_variable_get(:@menu_user_clicked_back_link) end + end - class TestHashDelegatorHandleGenericBlock < Minitest::Test - def setup - @hd = HashDelegator.new - @mock_document = mock('MarkdownDocument') - @selected_item = mock('FCB') - end + class TestHashDelegatorHandleGenericBlock < Minitest::Test + def setup + @hd = HashDelegator.new + @mock_document = mock('MarkdownDocument') + @selected_item = mock('FCB') + end - 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 }) + 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 + # Test the method without user approval + # Expectations and assertions go here + end - 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 }) + 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 + # Test the method with user approval + # Expectations and assertions go here + end - 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 }) + 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 }) - # Test the method with output script option - # Expectations and assertions go here - end + # Test the method with output script option + # Expectations and assertions go here end + end - # require 'stringio' + # require 'stringio' - class TestHashDelegatorHandleStream < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.instance_variable_set(:@run_state, - OpenStruct.new(files: { stdout: [] })) - @hd.instance_variable_set(:@delegate_object, - { output_stdout: true }) - end + class TestHashDelegatorHandleStream < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.instance_variable_set(:@run_state, + OpenStruct.new(files: { stdout: [] })) + @hd.instance_variable_set(:@delegate_object, + { output_stdout: true }) + end - def test_handle_stream - stream = StringIO.new("line 1\nline 2\n") - file_type = :stdout + def test_handle_stream + stream = StringIO.new("line 1\nline 2\n") + file_type = :stdout - Thread.new { @hd.handle_stream(stream, file_type) } + Thread.new { @hd.handle_stream(stream, file_type) } - @hd.wait_for_stream_processing + @hd.wait_for_stream_processing - assert_equal ['line 1', 'line 2'], - @hd.instance_variable_get(:@run_state).files[:stdout] - end + assert_equal ['line 1', 'line 2'], + @hd.instance_variable_get(:@run_state).files[:stdout] + end - def test_handle_stream_with_io_error - stream = StringIO.new("line 1\nline 2\n") - file_type = :stdout - stream.stubs(:each_line).raises(IOError) + def test_handle_stream_with_io_error + stream = StringIO.new("line 1\nline 2\n") + file_type = :stdout + stream.stubs(:each_line).raises(IOError) - Thread.new { @hd.handle_stream(stream, file_type) } + Thread.new { @hd.handle_stream(stream, file_type) } - @hd.wait_for_stream_processing + @hd.wait_for_stream_processing - assert_equal [], - @hd.instance_variable_get(:@run_state).files[:stdout] - end + assert_equal [], + @hd.instance_variable_get(:@run_state).files[:stdout] end + end - class TestHashDelegatorIterBlocksFromNestedFiles < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.instance_variable_set(:@delegate_object, - { filename: 'test.md' }) - @hd.stubs(:check_file_existence).with('test.md').returns(true) - @hd.stubs(:initial_state).returns({}) - @hd.stubs(:cfile).returns(Minitest::Mock.new) - @hd.stubs(:update_line_and_block_state) - end - - def test_iter_blocks_from_nested_files - @hd.cfile.expect(:readlines, ['line 1', 'line 2'], ['test.md']) - selected_messages = ['filtered message'] - - result = @hd.iter_blocks_from_nested_files { selected_messages } - assert_equal ['line 1', 'line 2'], result - - @hd.cfile.verify - end - - def test_iter_blocks_from_nested_files_with_no_file - @hd.stubs(:check_file_existence).with('test.md').returns(false) - - assert_nil(@hd.iter_blocks_from_nested_files do - ['filtered message'] - end) - end + class TestHashDelegatorIterBlocksFromNestedFiles < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.instance_variable_set(:@delegate_object, + { filename: 'test.md' }) + @hd.stubs(:check_file_existence).with('test.md').returns(true) + @hd.stubs(:initial_state).returns({}) + @hd.stubs(:cfile).returns(Minitest::Mock.new) + @hd.stubs(:update_line_and_block_state) end - class TestHashDelegatorMenuChromeColoredOption < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.instance_variable_set(:@delegate_object, { - menu_option_back_name: 'Back', - menu_chrome_color: :red, - menu_chrome_format: '-- %s --' - }) - @hd.stubs(:menu_chrome_formatted_option).with(:menu_option_back_name).returns('-- Back --') - @hd.stubs(:string_send_color).with('-- Back --', - :menu_chrome_color).returns('-- Back --'.red) - end + def test_iter_blocks_from_nested_files + @hd.cfile.expect(:readlines, ['line 1', 'line 2'], ['test.md'], import_paths: nil) + selected_messages = ['filtered message'] - def test_menu_chrome_colored_option_with_color - assert_equal '-- Back --'.red, - @hd.menu_chrome_colored_option(:menu_option_back_name) - end + result = @hd.iter_blocks_from_nested_files { selected_messages } + assert_equal ['line 1', 'line 2'], result - def test_menu_chrome_colored_option_without_color - @hd.instance_variable_set(:@delegate_object, - { menu_option_back_name: 'Back' }) - assert_equal '-- Back --', - @hd.menu_chrome_colored_option(:menu_option_back_name) - end + @hd.cfile.verify end - class TestHashDelegatorMenuChromeFormattedOptionWithoutFormat < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.instance_variable_set(:@delegate_object, { - menu_option_back_name: "'Back'", - menu_chrome_format: '-- %s --' - }) - HashDelegator.stubs(:safeval).with("'Back'").returns('Back') - end + def test_iter_blocks_from_nested_files_with_no_file + @hd.stubs(:check_file_existence).with('test.md').returns(false) - def test_menu_chrome_formatted_option_with_format - assert_equal '-- Back --', - @hd.menu_chrome_formatted_option(:menu_option_back_name) - end - - def test_menu_chrome_formatted_option_without_format - @hd.instance_variable_set(:@delegate_object, - { menu_option_back_name: "'Back'" }) - assert_equal 'Back', - @hd.menu_chrome_formatted_option(:menu_option_back_name) - end + assert_nil(@hd.iter_blocks_from_nested_files do + ['filtered message'] + end) end + end - class TestHashDelegatorStartFencedBlock < Minitest::Test - def setup - @hd = HashDelegator.new({ - block_name_wrapper_match: 'WRAPPER_REGEX', - block_calls_scan: 'CALLS_REGEX' + class TestHashDelegatorMenuChromeColoredOption < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.instance_variable_set(:@delegate_object, { + menu_option_back_name: 'Back', + menu_chrome_color: :red, + menu_chrome_format: '-- %s --' }) - end + @hd.stubs(:menu_chrome_formatted_option).with(:menu_option_back_name).returns('-- Back --') + @hd.stubs(:string_send_color).with('-- Back --', + :menu_chrome_color).returns('-- Back --'.red) + end - def test_start_fenced_block - line = '```fenced' - headings = ['Heading 1'] - regex = /```(?<name>\w+)(?<rest>.*)/ - - fcb = @hd.start_fenced_block(line, headings, regex) - - assert_instance_of MarkdownExec::FCB, fcb - assert_equal headings, fcb.headings - assert_equal 'fenced', fcb.dname - end + def test_menu_chrome_colored_option_with_color + assert_equal '-- Back --'.red, + @hd.menu_chrome_colored_option(:menu_option_back_name) end - class TestHashDelegatorStringSendColor < Minitest::Test - def setup - @hd = HashDelegator.new - @hd.instance_variable_set(:@delegate_object, - { red: 'red', green: 'green' }) - end - - def test_string_send_color - assert_equal 'Hello'.red, @hd.string_send_color('Hello', :red) - assert_equal 'World'.green, - @hd.string_send_color('World', :green) - assert_equal 'Default'.plain, - @hd.string_send_color('Default', :blue) - end + def test_menu_chrome_colored_option_without_color + @hd.instance_variable_set(:@delegate_object, + { menu_option_back_name: 'Back' }) + assert_equal '-- Back --', + @hd.menu_chrome_colored_option(:menu_option_back_name) end + end - def test_yield_line_if_selected_with_line - block_called = false - HashDelegator.yield_line_if_selected('Test line', [:line]) do |type, content| - block_called = true - assert_equal :line, type - assert_equal 'Test line', content.body[0] - end - assert block_called + class TestHashDelegatorMenuChromeFormattedOptionWithoutFormat < Minitest::Test + def setup + @hd = HashDelegator.new + @hd.instance_variable_set(:@delegate_object, { + menu_option_back_name: "'Back'", + menu_chrome_format: '-- %s --' + }) + HashDelegator.stubs(:safeval).with("'Back'").returns('Back') end - def test_yield_line_if_selected_without_line - block_called = false - HashDelegator.yield_line_if_selected('Test line', [:other]) do |_| - block_called = true - end - refute block_called + def test_menu_chrome_formatted_option_with_format + assert_equal '-- Back --', + @hd.menu_chrome_formatted_option(:menu_option_back_name) end - def test_yield_line_if_selected_without_block - result = HashDelegator.yield_line_if_selected('Test line', [:line]) - assert_nil result + def test_menu_chrome_formatted_option_without_format + @hd.instance_variable_set(:@delegate_object, + { menu_option_back_name: "'Back'" }) + assert_equal 'Back', + @hd.menu_chrome_formatted_option(:menu_option_back_name) end end - class TestHashDelegatorUpdateMenuAttribYieldSelectedWithBody < Minitest::Test + class TestHashDelegatorStartFencedBlock < Minitest::Test def setup - @hd = HashDelegator.new - @fcb = mock('Fcb') - @fcb.stubs(:body).returns(true) - HashDelegator.stubs(:initialize_fcb_names) - HashDelegator.stubs(:default_block_title_from_body) - Filter.stubs(:yield_to_block_if_applicable) + @hd = HashDelegator.new({ + block_name_wrapper_match: 'WRAPPER_REGEX', + block_calls_scan: 'CALLS_REGEX' + }) end - def test_update_menu_attrib_yield_selected_with_body - HashDelegator.expects(:initialize_fcb_names).with(@fcb) - HashDelegator.expects(:default_block_title_from_body).with(@fcb) - Filter.expects(:yield_to_block_if_applicable).with(@fcb, [:some_message], {}) + def test_start_fenced_block + line = '```fenced' + headings = ['Heading 1'] + regex = /```(?<name>\w+)(?<rest>.*)/ - HashDelegator.update_menu_attrib_yield_selected(@fcb, [:some_message]) - end + fcb = @hd.start_fenced_block(line, headings, regex) - def test_update_menu_attrib_yield_selected_without_body - @fcb.stubs(:body).returns(nil) - HashDelegator.expects(:initialize_fcb_names).with(@fcb) - HashDelegator.update_menu_attrib_yield_selected(@fcb, [:some_message]) + assert_instance_of MarkdownExec::FCB, fcb + assert_equal headings, fcb.headings + assert_equal 'fenced', fcb.dname end end - class TestHashDelegatorWaitForUserSelectedBlock < Minitest::Test + class TestHashDelegatorStringSendColor < Minitest::Test def setup @hd = HashDelegator.new - HashDelegator.stubs(:error_handler) + @hd.instance_variable_set(:@delegate_object, + { red: 'red', green: 'green' }) end - def test_wait_for_user_selected_block_with_back_state - mock_block_state = Struct.new(:state, :block).new(MenuState::BACK, - { oname: 'back_block' }) - @hd.stubs(:wait_for_user_selection).returns(mock_block_state) + def test_string_send_color + assert_equal 'Hello'.red, @hd.string_send_color('Hello', :red) + assert_equal 'World'.green, + @hd.string_send_color('World', :green) + assert_equal 'Default'.plain, + @hd.string_send_color('Default', :blue) + end + end - result = @hd.wait_for_user_selected_block([], ['Block 1', 'Block 2'], - nil) + def test_yield_line_if_selected_with_line + block_called = false + HashDelegator.yield_line_if_selected('Test line', [:line]) do |type, content| + block_called = true + assert_equal :line, type + assert_equal 'Test line', content.body[0] + end + assert block_called + end - assert_equal 'back_block', - @hd.instance_variable_get(:@delegate_object)[:block_name] - assert @hd.instance_variable_get(:@menu_user_clicked_back_link) - assert_equal mock_block_state, result + def test_yield_line_if_selected_without_line + block_called = false + HashDelegator.yield_line_if_selected('Test line', [:other]) do |_| + block_called = true end + refute block_called + end - def test_wait_for_user_selected_block_with_continue_state - mock_block_state = Struct.new(:state, :block).new( - MenuState::CONTINUE, { oname: 'continue_block' } - ) - @hd.stubs(:wait_for_user_selection).returns(mock_block_state) + def test_yield_line_if_selected_without_block + result = HashDelegator.yield_line_if_selected('Test line', [:line]) + assert_nil result + end + end - result = @hd.wait_for_user_selected_block([], ['Block 1', 'Block 2'], - nil) + class TestHashDelegatorUpdateMenuAttribYieldSelectedWithBody < Minitest::Test + def setup + @hd = HashDelegator.new + @fcb = mock('Fcb') + @fcb.stubs(:body).returns(true) + HashDelegator.stubs(:initialize_fcb_names) + HashDelegator.stubs(:default_block_title_from_body) + Filter.stubs(:yield_to_block_if_applicable) + end - assert_equal 'continue_block', - @hd.instance_variable_get(:@delegate_object)[:block_name] - refute @hd.instance_variable_get(:@menu_user_clicked_back_link) - assert_equal mock_block_state, result - end + def test_update_menu_attrib_yield_selected_with_body + HashDelegator.expects(:initialize_fcb_names).with(@fcb) + HashDelegator.expects(:default_block_title_from_body).with(@fcb) + Filter.expects(:yield_to_block_if_applicable).with(@fcb, [:some_message], {}) + + HashDelegator.update_menu_attrib_yield_selected(@fcb, [:some_message]) end - class TestHashDelegatorYieldToBlock < Minitest::Test - def setup - @hd = HashDelegator.new - @fcb = mock('Fcb') - MarkdownExec::Filter.stubs(:fcb_select?).returns(true) - end + def test_update_menu_attrib_yield_selected_without_body + @fcb.stubs(:body).returns(nil) + HashDelegator.expects(:initialize_fcb_names).with(@fcb) + HashDelegator.update_menu_attrib_yield_selected(@fcb, [:some_message]) + end + end - def test_yield_to_block_if_applicable_with_correct_conditions - block_called = false - Filter.yield_to_block_if_applicable(@fcb, [:blocks]) do |type, fcb| - block_called = true - assert_equal :blocks, type - assert_equal @fcb, fcb - end - assert block_called - end + class TestHashDelegatorWaitForUserSelectedBlock < Minitest::Test + def setup + @hd = HashDelegator.new + HashDelegator.stubs(:error_handler) + end - def test_yield_to_block_if_applicable_without_block - result = Filter.yield_to_block_if_applicable(@fcb, [:blocks]) - assert_nil result + def test_wait_for_user_selected_block_with_back_state + mock_block_state = Struct.new(:state, :block).new(MenuState::BACK, + { oname: 'back_block' }) + @hd.stubs(:wait_for_user_selection).returns(mock_block_state) + + result = @hd.wait_for_user_selected_block([], ['Block 1', 'Block 2'], + nil) + + assert_equal 'back_block', + @hd.instance_variable_get(:@delegate_object)[:block_name] + assert @hd.instance_variable_get(:@menu_user_clicked_back_link) + assert_equal mock_block_state, result + end + + def test_wait_for_user_selected_block_with_continue_state + mock_block_state = Struct.new(:state, :block).new( + MenuState::CONTINUE, { oname: 'continue_block' } + ) + @hd.stubs(:wait_for_user_selection).returns(mock_block_state) + + result = @hd.wait_for_user_selected_block([], ['Block 1', 'Block 2'], + nil) + + assert_equal 'continue_block', + @hd.instance_variable_get(:@delegate_object)[:block_name] + refute @hd.instance_variable_get(:@menu_user_clicked_back_link) + assert_equal mock_block_state, result + end + end + + class TestHashDelegatorYieldToBlock < Minitest::Test + def setup + @hd = HashDelegator.new + @fcb = mock('Fcb') + MarkdownExec::Filter.stubs(:fcb_select?).returns(true) + end + + def test_yield_to_block_if_applicable_with_correct_conditions + block_called = false + Filter.yield_to_block_if_applicable(@fcb, [:blocks]) do |type, fcb| + block_called = true + assert_equal :blocks, type + assert_equal @fcb, fcb end + assert block_called + end - def test_yield_to_block_if_applicable_with_incorrect_conditions - block_called = false - MarkdownExec::Filter.stubs(:fcb_select?).returns(false) - Filter.yield_to_block_if_applicable(@fcb, [:non_blocks]) do |_| - block_called = true - end - refute block_called + def test_yield_to_block_if_applicable_without_block + result = Filter.yield_to_block_if_applicable(@fcb, [:blocks]) + assert_nil result + end + + def test_yield_to_block_if_applicable_with_incorrect_conditions + block_called = false + MarkdownExec::Filter.stubs(:fcb_select?).returns(false) + Filter.yield_to_block_if_applicable(@fcb, [:non_blocks]) do |_| + block_called = true end + refute block_called end - end # module MarkdownExec -end + end +end # module MarkdownExec