lib/hash_delegator.rb in markdown_exec-1.8.2 vs lib/hash_delegator.rb in markdown_exec-1.8.4
- old
+ new
@@ -22,10 +22,11 @@
require_relative 'exceptions'
require_relative 'fcb'
require_relative 'filter'
require_relative 'fout'
require_relative 'hash'
+require_relative 'link_history'
require_relative 'mdoc'
require_relative 'string_util'
class String
# Checks if the string is not empty.
@@ -113,10 +114,11 @@
@most_recent_loaded_filename = nil
@pass_args = []
@run_state = OpenStruct.new(
link_history: []
)
+ @link_history = LinkHistory.new
@fout = FOut.new(@delegate_object) ### slice only relevant keys
@process_mutex = Mutex.new
@process_cv = ConditionVariable.new
end
@@ -165,13 +167,11 @@
# @param all_blocks [Array] The current blocks in the menu
# @param type [Symbol] The type of chrome block to add (:back or :exit)
def append_chrome_block(menu_blocks, type)
case type
when MenuState::BACK
- state = history_state_partition
- @hs_curr = state[:unit]
- @hs_rest = state[:rest]
+ history_state_partition
option_name = @delegate_object[:menu_option_back_name]
insert_at_top = @delegate_object[:menu_back_at_top]
when MenuState::EXIT
option_name = @delegate_object[:menu_option_exit_name]
insert_at_top = @delegate_object[:menu_exit_at_top]
@@ -179,17 +179,13 @@
formatted_name = format(@delegate_object[:menu_link_format],
safeval(option_name))
chrome_block = FCB.new(
chrome: true,
-
- # dname: formatted_name.send(@delegate_object[:menu_link_color].to_sym),
dname: HashDelegator.new(@delegate_object).string_send_color(
formatted_name, :menu_link_color
),
- #dname: @delegate_object.string_send_color(formatted_name, :menu_link_color),
-
oname: formatted_name
)
if insert_at_top
menu_blocks.unshift(chrome_block)
@@ -274,34 +270,49 @@
return false
end
true
end
+ def code_join(*bodies)
+ bc = bodies&.compact
+ bc.count.positive? ? bc.join("\n") : nil
+ end
+
+ def code_merge(*bodies)
+ merge_lists(*bodies)
+ end
+
# Collects required code lines based on the selected block and the delegate object's configuration.
# If the block type is VARS, it also sets environment variables based on the block's content.
#
# @param mdoc [YourMDocClass] An instance of the MDoc class.
# @param selected [Hash] The selected block.
# @return [Array<String>] Required code blocks as an array of lines.
- def collect_required_code_lines(mdoc, selected)
+ 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],
label_format_above: @delegate_object[:shell_code_label_format_above],
- label_format_below: @delegate_object[:shell_code_label_format_below]
+ label_format_below: @delegate_object[:shell_code_label_format_below],
+ block_source: block_source
)
+ required[:unmet_dependencies] =
+ (required[:unmet_dependencies] || []) - (link_state&.inherited_block_names || [])
if required[:unmet_dependencies].present?
+ ### filter against link_state.inherited_block_names
+
warn format_and_highlight_dependencies(required[:dependencies],
highlight: required[:unmet_dependencies])
runtime_exception(:runtime_exception_error_level,
'unmet_dependencies, flag: runtime_exception_error_level', required[:unmet_dependencies])
elsif true
warn format_and_highlight_dependencies(required[:dependencies],
highlight: [@delegate_object[:block_name]])
end
- read_required_blocks_from_temp_file + required[:code]
+
+ code_merge link_state&.inherited_lines, required[:code]
end
def command_execute(command, args: [])
@run_state.files = Hash.new([])
@run_state.options = @delegate_object
@@ -367,19 +378,22 @@
# 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 [LoadFileNextBlock] 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)
- required_lines = collect_required_code_lines(mdoc, selected)
+ # @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:)
+ 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
- execute_approved_block(required_lines, selected) if allow_execution
+ execute_required_lines(required_lines) if allow_execution
- LoadFileNextBlock.new(LoadFile::Reuse, '')
+ link_state.block_name = nil
+ LoadFileLinkState.new(LoadFile::Reuse, link_state)
end
def copy_to_clipboard(required_lines)
text = required_lines.flatten.join($INPUT_RECORD_SEPARATOR)
Clipboard.copy(text)
@@ -456,17 +470,17 @@
# and sets file permissions if required. Handles any errors encountered during the process.
#
# @param file_path [String] The path where the file will be created.
# @param content [String] The content to write into the file.
# @param chmod_value [Integer] The file permission value to set; skips if zero.
- def create_and_write_file_with_permissions(file_path, content,
- chmod_value)
+ def create_file_and_write_string_with_permissions(file_path, content,
+ chmod_value)
create_directory_for_file(file_path)
- write_file_content(file_path, content)
+ File.write(file_path, content)
set_file_permissions(file_path, chmod_value) unless chmod_value.zero?
rescue StandardError
- error_handler('create_and_write_file_with_permissions')
+ error_handler('create_file_and_write_string_with_permissions')
end
# private
def create_directory_for_file(file_path)
@@ -484,19 +498,10 @@
dname: string_send_color(oname, :menu_divider_color),
oname: oname
)
end
- # Creates a temporary file, writes the provided code blocks into it,
- # and sets an environment variable with the file path.
- # @param code_blocks [String] Code blocks to write into the file.
- def create_temp_file_with_code(code_blocks)
- temp_file_path = create_temp_file
- write_to_file(temp_file_path, code_blocks)
- set_environment_variable(temp_file_path)
- end
-
# private
def create_temp_file
Dir::Tmpname.create(self.class.to_s) { |path| path }
end
@@ -517,17 +522,14 @@
end
# Deletes a temporary file specified by an environment variable.
# Checks if the file exists before attempting to delete it and clears the environment variable afterward.
# Any errors encountered during deletion are handled gracefully.
- def delete_required_temp_file
- temp_blocks_file_path = fetch_temp_blocks_file_path
-
+ def delete_required_temp_file(temp_blocks_file_path)
return if temp_blocks_file_path.nil? || temp_blocks_file_path.empty?
safely_remove_file(temp_blocks_file_path)
- clear_required_file
rescue StandardError
error_handler('delete_required_temp_file')
end
# Determines the state of a selected block in the menu based on the selected option.
@@ -579,13 +581,13 @@
# 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_approved_block(required_lines = [], _selected = FCB.new)
+ def execute_required_lines(required_lines = [])
# set_script_block_name(selected)
- write_command_file_if_needed(required_lines)
+ save_executed_script_if_specified(required_lines)
format_and_execute_command(required_lines)
post_execution_process
end
# Execute a code block after approval and provide user interaction options.
@@ -595,19 +597,25 @@
# 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)
+ def execute_bash_and_special_blocks(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)
+ push_link_history_and_trigger_load(selected.fetch(:body, ''), mdoc, selected,
+ link_state)
+
elsif @menu_user_clicked_back_link
pop_link_history_and_trigger_load
+
elsif selected[:shell] == BlockType::OPTS
- update_options_and_trigger_reuse(selected, @menu_base_options)
+ update_options_and_trigger_reuse(selected, @menu_base_options, link_state)
+
else
- compile_execute_bash_and_special_blocks_and_trigger_reuse(mdoc, selected)
+ compile_execute_bash_and_special_blocks_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.
@@ -621,17 +629,12 @@
color_sym: :execution_report_preview_frame_color)
data_string = @delegate_object.fetch(data_sym, default).to_s
string_send_color(data_string, color_sym)
end
- def fetch_temp_blocks_file_path
- ENV.fetch('MDE_LINK_REQUIRED_FILE', nil)
- end
-
# DebugHelper.d ["HDmm method_name: #{method_name}", "#{first_n_caller_items 1}"]
def first_n_caller_items(n)
- # Get the call stack
call_stack = caller
base_path = File.realpath('.')
# Modify the call stack to remove the base path and keep only the first n items
call_stack.take(n + 1)[1..].map do |line|
@@ -702,10 +705,11 @@
# 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_block_state(block_state)
+ return if block_state.nil?
unless [MenuState::BACK,
MenuState::CONTINUE].include?(block_state.state)
return
end
@@ -733,56 +737,10 @@
@process_cv.signal
end
end
end
- # Checks if a history environment variable is set and returns its value if present.
- # @return [String, nil] The value of the history environment variable or nil if not present.
- def history_env_state_exist?
- ENV.fetch(MDE_HISTORY_ENV_NAME, '').present?
- end
-
- # Partitions the history state from the environment variable based on the document separator.
- # @return [Hash] A hash containing two parts: :unit (first part) and :rest (remaining part).
- def history_state_partition
- history_env_value = ENV.fetch(MDE_HISTORY_ENV_NAME, '')
- separator = @delegate_object[:history_document_separator]
-
- unit, rest = StringUtil.partition_at_first(history_env_value, separator)
- { unit: unit, rest: rest }
- end
-
- # Pops the last entry from the history state, updating the delegate object and environment variable.
- # It also deletes the required temporary file and updates the run state link history.
- def history_state_pop
- state = history_state_partition
- @delegate_object[:filename] = state[:unit]
- ENV[MDE_HISTORY_ENV_NAME] = state[:rest]
- delete_required_temp_file
- @run_state.link_history.pop
- end
-
- # Updates the history state by pushing a new entry and managing environment variables.
- # @param mdoc [Object] The Markdown document object.
- # @param data_file [String] The data file to be processed.
- # @param selected [Hash] Hash containing the selected block's name.
- def history_state_push(mdoc, data_file, selected)
- # Construct new history string
- new_history = [@delegate_object[:filename],
- @delegate_object[:history_document_separator],
- ENV.fetch(MDE_HISTORY_ENV_NAME, '')].join
-
- # Update delegate object and environment variable
- @delegate_object[:filename] = data_file
- ENV[MDE_HISTORY_ENV_NAME] = new_history
-
- # Write required blocks to temp file and update run state
- write_required_blocks_to_temp_file(mdoc, @delegate_object[:block_name])
- @run_state.link_history.push(block_name: selected[:oname],
- filename: data_file)
- end
-
# Indents all lines in a given string with a specified indentation string.
# @param body [String] A multi-line string to be indented.
# @param indent [String] The string used for indentation (default is an empty string).
# @return [String] A single string with each line indented as specified.
def indent_all_lines(body, indent = nil)
@@ -843,10 +801,35 @@
&block)
end
end
end
+ def link_history_push_and_next(
+ curr_block_name:, curr_document_filename:,
+ inherited_block_names:, inherited_lines:,
+ next_block_name:, next_document_filename:,
+ next_load_file:
+ )
+ @link_history.push(
+ LinkState.new(
+ block_name: curr_block_name,
+ document_filename: curr_document_filename,
+ inherited_block_names: inherited_block_names,
+ inherited_lines: inherited_lines
+ )
+ )
+ LoadFileLinkState.new(
+ next_load_file,
+ LinkState.new(
+ block_name: next_block_name,
+ document_filename: next_document_filename,
+ inherited_block_names: inherited_block_names,
+ inherited_lines: inherited_lines
+ )
+ )
+ end
+
# Loads auto blocks based on delegate object settings and updates if new filename is detected.
# Executes a specified block once per filename.
# @param all_blocks [Array] Array of all block elements.
# @return [Boolean, nil] True if values were modified, nil otherwise.
def load_auto_blocks(all_blocks)
@@ -908,10 +891,16 @@
else
option_value
end
end
+ def merge_lists(*args)
+ # Filters out nil values, flattens the arrays, and ensures an empty list is returned if no valid lists are provided
+ merged = args.compact.flatten
+ merged.empty? ? [] : merged
+ end
+
# If a method is missing, treat it as a key for the @delegate_object.
def method_missing(method_name, *args, &block)
if @delegate_object.respond_to?(method_name)
@delegate_object.send(method_name, *args, &block)
elsif method_name.to_s.end_with?('=') && args.size == 1
@@ -920,15 +909,15 @@
@delegate_object[method_name]
# super
end
end
- def next_block_name_from_command_line_arguments
- return MenuControl::Repeat unless @delegate_object[:input_cli_rest].present?
+ def pop_cli_argument!
+ return false unless @delegate_object[:input_cli_rest].present?
- @delegate_object[:block_name] = @delegate_object[:input_cli_rest].pop
- MenuControl::Fresh
+ @cli_block_name = @delegate_object[:input_cli_rest].pop
+ true
end
# private
def output_color_formatted(data_sym, color_sym)
@@ -990,14 +979,19 @@
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.
#
- # @return [LoadFileNextBlock] An object indicating the action to load the next block.
+ # @return [LoadFileLinkState] An object indicating the action to load the next block.
def pop_link_history_and_trigger_load
- history_state_pop
- LoadFileNextBlock.new(LoadFile::Load, '')
+ pop = @link_history.pop
+ peek = @link_history.peek
+ LoadFileLinkState.new(LoadFile::Load, LinkState.new(
+ document_filename: pop.document_filename,
+ inherited_block_names: peek.inherited_block_names,
+ inherited_lines: peek.inherited_lines
+ ))
end
def post_execution_process
initialize_and_save_execution_output
output_execution_summary
@@ -1008,11 +1002,10 @@
#
# @param all_blocks [Array<Hash>] The list of blocks from the file.
# @param opts [Hash] The options hash.
# @return [Array<Hash>] The updated blocks menu.
def prepare_blocks_menu(menu_blocks)
- ### replace_consecutive_blanks(menu_blocks).map do |fcb|
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!(
@@ -1112,32 +1105,58 @@
end
# public
# Handles the processing of a link block in Markdown Execution.
- # It loads YAML data from the body content, pushes the state to history,
+ # It loads YAML data from the link_block_body content, pushes the state to history,
# sets environment variables, and decides on the next block to load.
#
- # @param body [Array<String>] The body content as an array of strings.
+ # @param link_block_body [Array<String>] The body content as an array of strings.
# @param mdoc [Object] Markdown document object.
- # @param selected [Boolean] Selected state.
- # @return [LoadFileNextBlock] Object indicating the next action for file loading.
- def push_link_history_and_trigger_load(body, mdoc, selected)
- data = parse_yaml_data_from_body(body)
- data_file = data['file']
- return LoadFileNextBlock.new(LoadFile::Reuse, '') unless data_file
+ # @param selected [FCB] Selected code block.
+ # @return [LoadFileLinkState] Object indicating the next action for file loading.
+ def push_link_history_and_trigger_load(link_block_body, mdoc, selected,
+ link_state = LinkState.new)
+ link_block_data = parse_yaml_data_from_body(link_block_body)
- history_state_push(mdoc, data_file, selected)
- set_environment_variables_per_array(data['vars'])
+ # load key and values from link block into current environment
+ #
+ (link_block_data['vars'] || []).each do |(key, value)|
+ ENV[key] = value.to_s
+ end
- LoadFileNextBlock.new(LoadFile::Load, data['block'] || '')
+ ## collect blocks specified by block
+ #
+ if mdoc
+ code_info = mdoc.collect_recursively_required_code(
+ selected[:oname],
+ 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 }
+ )
+ code_lines = code_info[:code]
+ block_names = code_info[:block_names]
+ else
+ block_names = []
+ code_lines = []
+ end
+
+ next_document_filename = link_block_data['file'] || @delegate_object[:filename]
+ 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_lines: code_merge(link_state&.inherited_lines, code_lines),
+ next_block_name: link_block_data['block'] || '',
+ next_document_filename: next_document_filename,
+ next_load_file: next_document_filename == @delegate_object[:filename] ? LoadFile::Reuse : LoadFile::Load
+ )
end
# Reads required code blocks from a temporary file specified by an environment variable.
# @return [Array<String>] Lines read from the temporary file, or an empty array if file is not found or path is empty.
- def read_required_blocks_from_temp_file
- temp_blocks_file_path = ENV.fetch('MDE_LINK_REQUIRED_FILE', nil)
+ def read_required_blocks_from_temp_file(temp_blocks_file_path)
return [] if temp_blocks_file_path.to_s.empty?
if File.exist?(temp_blocks_file_path)
File.readlines(
temp_blocks_file_path, chomp: true
@@ -1178,16 +1197,10 @@
eval(str)
rescue StandardError
error_handler('safeval')
end
- # def safeval(str)
- # eval(str)
- # rescue StandardError
- # error_handler('safeval')
- # end
-
def save_to_file(required_lines)
write_command_file(required_lines)
@fout.fout "File saved: #{@run_state.saved_filespec}"
end
@@ -1197,27 +1210,29 @@
# 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)
@menu_base_options = @delegate_object
- repeat_menu = @menu_base_options[:block_name].present? ? MenuControl::Fresh : MenuControl::Repeat
- load_file_next_block = LoadFileNextBlock.new(LoadFile::Reuse)
+ 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
- @menu_state_filename = @menu_base_options[:filename]
- @menu_state_block_name = @menu_base_options[:block_name]
-
loop do
loop do
@delegate_object = @menu_base_options.dup
- @menu_base_options[:filename] = @menu_state_filename
- @menu_base_options[:block_name] = @menu_state_block_name
- @menu_state_filename = nil
- @menu_state_block_name = nil
@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
blocks_in_file, menu_blocks, mdoc = mdoc_menu_and_blocks_from_nested_files
+
if @delegate_object[:dump_blocks_in_file]
warn format_and_highlight_dependencies(
compact_and_index_hash(blocks_in_file),
label: 'blocks_in_file'
)
@@ -1226,10 +1241,11 @@
warn format_and_highlight_dependencies(
compact_and_index_hash(menu_blocks),
label: 'menu_blocks'
)
end
+
block_state = command_or_user_selected_block(blocks_in_file,
menu_blocks, menu_default_dname)
return if block_state.state == MenuState::EXIT
if block_state.block.nil?
@@ -1240,42 +1256,38 @@
if @delegate_object[:dump_selected_block]
warn block_state.block.to_yaml.sub(/^(?:---\n)?/, "Block:\n")
end
- load_file_next_block = execute_bash_and_special_blocks(block_state.block,
- mdoc)
- # 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_next_block.load_file == LoadFile::Load ? nil : block_state.block[:dname]
+ 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 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]
- @menu_base_options[:block_name] =
- @delegate_object[:block_name] = load_file_next_block.next_block
- @menu_base_options[:filename] = @delegate_object[:filename]
-
# user prompt to exit if the menu will be displayed again
#
- if repeat_menu == MenuControl::Repeat &&
+ if !block_name_from_cli &&
block_state.block[:shell] == BlockType::BASH &&
@delegate_object[:pause_after_script_execution] &&
prompt_select_continue == MenuState::EXIT
return
end
# exit current document/menu if loading next document or single block_name was specified
#
- if block_state.state == MenuState::CONTINUE && load_file_next_block.load_file == LoadFile::Load
- break
- end
- break if repeat_menu == MenuControl::Fresh
+ break if block_state.state == MenuState::CONTINUE && load_file == LoadFile::Load
+ break if block_name_from_cli
end
- break if load_file_next_block.load_file == LoadFile::Reuse
+ break if load_file == LoadFile::Reuse
- repeat_menu = next_block_name_from_command_line_arguments
- @menu_state_filename = @menu_base_options[:filename]
- @menu_state_block_name = @menu_base_options[:block_name]
+ block_name_from_cli = pop_cli_argument!
end
rescue StandardError
error_handler('select_execute_bash_and_special_blocks',
{ abort: true })
end
@@ -1291,12 +1303,11 @@
names.find { |item| item[:dname] == selection }
end
item.merge(
if selection == menu_chrome_colored_option(:menu_option_back_name)
- { option: selection, curr: @hs_curr, rest: @hs_rest,
- shell: BlockType::LINK }
+ { option: selection, shell: BlockType::LINK }
elsif selection == menu_chrome_colored_option(:menu_option_exit_name)
{ option: selection }
else
{ selected: selection }
end
@@ -1305,14 +1316,10 @@
exit 1
rescue StandardError
error_handler('select_option_with_metadata')
end
- def set_environment_variable(path)
- ENV['MDE_LINK_REQUIRED_FILE'] = path
- end
-
def set_environment_variables_for_block(selected)
YAML.load(selected[:body].join("\n")).each do |key, value|
ENV[key] = value.to_s
next unless @delegate_object[:menu_vars_set_format].present?
@@ -1334,11 +1341,12 @@
def set_script_block_name(selected)
@run_state.script_block_name = selected[:oname]
end
def should_add_back_option?
- @delegate_object[:menu_with_back] && history_env_state_exist?
+ @delegate_object[:menu_with_back] && @link_history.prior_state_exist?
+ # @delegate_object[:menu_with_back] && link_history_prior_state_exist?
end
# Initializes a new fenced code block (FCB) object based on the provided line and heading information.
# @param line [String] The line initiating the fenced block.
# @param headings [Array<String>] Current headings hierarchy.
@@ -1395,11 +1403,10 @@
raise TTY::Reader::InputInterrupt
},
symbols: { cross: ' ' }
)
end
- # private
def update_delegate_and_target(key, value, tgt2)
sym_key = key.to_sym
@delegate_object[sym_key] = value
tgt2[sym_key] = value if tgt2
@@ -1471,16 +1478,13 @@
end
elsif state[:in_fenced_block] && state[:fcb].body
## add line to fenced code block
# remove fcb indent if possible
#
- # if nested_line[:depth].zero? || opts[:menu_include_imported_blocks]
- # if add_import_block?(nested_line)
state[:fcb].body += [
line.chomp.sub(/^#{state[:fcb].indent}/, '')
]
- # end
elsif nested_line[:depth].zero? || @delegate_object[:menu_include_imported_notes]
# add line if it is depth 0 or option allows it
#
yield_line_if_selected(line, selected_messages, &block)
end
@@ -1502,18 +1506,19 @@
end
# Processes YAML data from the selected menu item, updating delegate objects and optionally printing formatted output.
# @param selected [Hash] Selected item from the menu containing a YAML body.
# @param tgt2 [Hash, nil] An optional target hash to update with YAML data.
- # @return [LoadFileNextBlock] An instance indicating the next action for loading files.
- def update_options_and_trigger_reuse(selected, tgt2 = nil)
+ # @return [LoadFileLinkState] An instance indicating the next action for loading files.
+ def update_options_and_trigger_reuse(selected, tgt2 = nil, link_state = LinkState.new)
data = YAML.load(selected[:body].join("\n"))
(data || []).each do |key, value|
update_delegate_and_target(key, value, tgt2)
print_formatted_option(key, value) if @delegate_object[:menu_opts_set_format].present?
end
- LoadFileNextBlock.new(LoadFile::Reuse, '')
+ link_state.block_name = nil
+ LoadFileLinkState.new(LoadFile::Reuse, link_state)
end
def wait_for_stream_processing
@process_mutex.synchronize do
@process_cv.wait(@process_mutex)
@@ -1521,11 +1526,10 @@
end
def wait_for_user_selected_block(all_blocks, menu_blocks, default)
block_state = wait_for_user_selection(all_blocks, menu_blocks, default)
handle_block_state(block_state)
-
block_state
rescue StandardError
error_handler('wait_for_user_selected_block')
end
@@ -1575,20 +1579,20 @@
"# file_name: #{@delegate_object[:filename]}\n" \
"# block_name: #{@delegate_object[:block_name]}\n" \
"# time: #{time_now}\n" \
"#{required_lines.flatten.join("\n")}\n"
- create_and_write_file_with_permissions(
+ create_file_and_write_string_with_permissions(
@run_state.saved_filespec,
content,
@delegate_object[:saved_script_chmod]
)
rescue StandardError
error_handler('write_command_file')
end
- def write_command_file_if_needed(lines)
+ def save_executed_script_if_specified(lines)
write_command_file(lines) if @delegate_object[:save_executed_script]
end
def write_execution_output_to_file
FileUtils.mkdir_p File.dirname(@delegate_object[:logged_stdout_filespec])
@@ -1603,36 +1607,34 @@
format_execution_streams(ExecutionStreams::StdIn),
"\n"].join
)
end
- def write_file_content(file_path, content)
- File.write(file_path, content)
- end
-
# Writes required code blocks to a temporary file and sets an environment variable with its path.
#
# @param mdoc [Object] The Markdown document object.
# @param block_name [String] The name of the block to collect code for.
- def write_required_blocks_to_temp_file(mdoc, block_name)
+ def write_required_blocks_to_file(mdoc, block_name, temp_file_path, import_filename: nil)
c1 = if mdoc
mdoc.collect_recursively_required_code(
block_name,
label_format_above: @delegate_object[:shell_code_label_format_above],
label_format_below: @delegate_object[:shell_code_label_format_below]
)[:code]
else
[]
end
- code_blocks = (read_required_blocks_from_temp_file +
+ code_blocks = (read_required_blocks_from_temp_file(import_filename) +
c1).join("\n")
- create_temp_file_with_code(code_blocks)
+ write_code_to_file(code_blocks, temp_file_path)
end
- def write_to_file(path, content)
+ # Writes the provided code blocks to a file.
+ # @param code_blocks [String] Code blocks to write into the file.
+ def write_code_to_file(content, path)
File.write(path, content)
end
# Yields a line as a new block if the selected message type includes :line.
# @param [String] line The line to be processed.
@@ -1672,11 +1674,11 @@
def setup
@hd = HashDelegator.new
@mdoc = mock('MarkdownDocument')
end
- def test_calling_execute_approved_block_calls_command_execute_with_argument_args_value
+ 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'
@@ -1689,52 +1691,39 @@
c.expects(:command_execute).with(
'',
args: pigeon
)
- # Call method opts_execute_approved_block
- c.execute_approved_block([], MarkdownExec::FCB.new)
+ # Call method opts_execute_required_lines
+ c.execute_required_lines([])
end
# Test case for empty body
def test_push_link_history_and_trigger_load_with_empty_body
- assert_equal LoadFileNextBlock.new(LoadFile::Reuse, ''),
- @hd.push_link_history_and_trigger_load([], nil, false)
+ assert_equal LoadFile::Reuse,
+ @hd.push_link_history_and_trigger_load([], nil, FCB.new).load_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 LoadFileNextBlock.new(LoadFile::Reuse, ''),
- @hd.push_link_history_and_trigger_load(body, nil, false)
+ assert_equal LoadFile::Reuse,
+ @hd.push_link_history_and_trigger_load(body, 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 = LoadFileNextBlock.new(LoadFile::Load,
- 'sample_block')
- # mdoc = MDoc.new()
+ expected_result = LoadFileLinkState.new(LoadFile::Load,
+ LinkState.new(block_name: 'sample_block',
+ document_filename: 'sample_file',
+ inherited_lines: []))
assert_equal expected_result,
- @hd.push_link_history_and_trigger_load(body, nil, FCB.new)
+ @hd.push_link_history_and_trigger_load(body, nil, FCB.new(block_name: 'sample_block',
+ filename: 'sample_file'))
end
- def test_history_env_state_exist_with_value
- ENV[MDE_HISTORY_ENV_NAME] = 'history_value'
- assert @hd.history_env_state_exist?
- end
-
- def test_history_env_state_exist_without_value
- ENV[MDE_HISTORY_ENV_NAME] = ''
- refute @hd.history_env_state_exist?
- end
-
- def test_history_env_state_exist_not_set
- ENV.delete(MDE_HISTORY_ENV_NAME)
- refute @hd.history_env_state_exist?
- 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, @hd.indent_all_lines(body, indent)
@@ -1752,26 +1741,10 @@
indent = ''
assert_equal body, @hd.indent_all_lines(body, indent)
end
- def test_read_required_blocks_from_temp_file
- Tempfile.create do |file|
- file.write("Line 1\nLine 2")
- file.rewind
- ENV['MDE_LINK_REQUIRED_FILE'] = file.path
-
- result = @hd.read_required_blocks_from_temp_file
- assert_equal ['Line 1', 'Line 2'], result
- end
- end
-
- def test_read_required_blocks_from_temp_file_no_file
- ENV['MDE_LINK_REQUIRED_FILE'] = nil
- assert_empty @hd.read_required_blocks_from_temp_file
- end
-
def test_safeval_successful_evaluation
assert_equal 4, @hd.safeval('2 + 2')
end
def test_safeval_rescue_from_error
@@ -1797,12 +1770,10 @@
input: MarkdownExec::FCB.new(title: 'foo', body: %w[bar baz]),
output: 'foo' # expect the title to remain unchanged
}
]
- # hd = HashDelegator.new
-
# 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]
@@ -1845,11 +1816,11 @@
menu_blocks = []
@hd.append_divider(menu_blocks, :initial)
assert_empty menu_blocks
end
- end # class TestHashDelegator
+ end
class TestHashDelegatorBlockFind < Minitest::Test
def setup
@hd = HashDelegator.new
end
@@ -1869,11 +1840,11 @@
def test_block_find_with_default
blocks = [{ key: 'value1' }, { key: 'value2' }]
result = @hd.block_find(blocks, :key, 'value3', 'default')
assert_equal 'default', result
end
- end # class TestHashDelegator
+ end
class TestHashDelegatorBlocksFromNestedFiles < Minitest::Test
def setup
@hd = HashDelegator.new
@hd.stubs(:iter_blocks_from_nested_files).yields(:blocks, FCB.new)
@@ -1896,11 +1867,11 @@
result = @hd.blocks_from_nested_files
assert_kind_of Array, result
end
- end # class TestHashDelegator
+ end
class TestHashDelegatorCollectRequiredCodeLines < Minitest::Test
def setup
@hd = HashDelegator.new
@hd.instance_variable_set(:@delegate_object, {})
@@ -1912,17 +1883,15 @@
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'] })
- ENV.stubs(:[]=)
+ result = @hd.collect_required_code_lines(@mdoc, @selected, block_source: {})
- result = @hd.collect_required_code_lines(@mdoc, @selected)
-
assert_equal ['code line'], result
end
- end # class TestHashDelegator
+ end
class TestHashDelegatorCommandOrUserSelectedBlock < Minitest::Test
def setup
@hd = HashDelegator.new
@hd.instance_variable_set(:@delegate_object, {})
@@ -1949,11 +1918,11 @@
result = @hd.command_or_user_selected_block([], [], nil)
assert_equal block_state.block, result.block
assert_equal :some_state, result.state
end
- end # class TestHashDelegator
+ end
class TestHashDelegatorCountBlockInFilename < Minitest::Test
def setup
@hd = HashDelegator.new
@hd.instance_variable_set(:@delegate_object,
@@ -1978,32 +1947,32 @@
count = @hd.count_blocks_in_filename
assert_equal 0, count
end
- end # class TestHashDelegator
+ end
class TestHashDelegatorCreateAndWriteFile < Minitest::Test
def setup
@hd = HashDelegator.new
@hd.stubs(:error_handler)
FileUtils.stubs(:mkdir_p)
File.stubs(:write)
File.stubs(:chmod)
end
- def test_create_and_write_file_with_permissions
+ 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
- @hd.create_and_write_file_with_permissions(file_path, content,
- chmod_value)
+ @hd.create_file_and_write_string_with_permissions(file_path, content,
+ chmod_value)
assert true # Placeholder for actual test assertions
end
def test_create_and_write_file_without_chmod
@@ -2013,75 +1982,17 @@
FileUtils.expects(:mkdir_p).with('/path/to').once
File.expects(:write).with(file_path, content).once
File.expects(:chmod).never
- @hd.create_and_write_file_with_permissions(file_path, content,
- chmod_value)
+ @hd.create_file_and_write_string_with_permissions(file_path, content,
+ chmod_value)
assert true # Placeholder for actual test assertions
end
- end # class TestHashDelegator
+ end
- class TestHashDelegatorCreateTempFile < Minitest::Test
- def setup
- @hd = HashDelegator.new
- @temp_file_path = '/tmp/tempfile'
- end
-
- def test_create_temp_file_with_code
- Dir::Tmpname.stubs(:create).returns(@temp_file_path)
- File.stubs(:write).with(@temp_file_path, 'code_blocks')
- # ENV.expects(:[]=).with('MDE_LINK_REQUIRED_FILE', @temp_file_path)
-
- @hd.create_temp_file_with_code('code_blocks')
-
- assert true # Placeholder for actual test assertions
- end
- end # class TestHashDelegator
-
- class TestHashDelegatorDeleteRequiredTempFile < Minitest::Test
- def setup
- @hd = HashDelegator.new
- @hd.stubs(:error_handler)
- @hd.stubs(:clear_required_file)
- FileUtils.stubs(:rm_f)
- end
-
- def test_delete_required_temp_file_with_existing_file
- ENV.stubs(:fetch).with('MDE_LINK_REQUIRED_FILE',
- nil).returns('/path/to/temp_file')
- FileUtils.expects(:rm_f).with('/path/to/temp_file').once
- @hd.expects(:clear_required_file).once
-
- @hd.delete_required_temp_file
-
- assert true # Placeholder for actual test assertions
- end
-
- def test_delete_required_temp_file_with_no_file
- ENV.stubs(:fetch).with('MDE_LINK_REQUIRED_FILE', nil).returns(nil)
- FileUtils.expects(:rm_f).never
- @hd.expects(:clear_required_file).never
-
- @hd.delete_required_temp_file
-
- assert true # Placeholder for actual test assertions
- end
-
- def test_delete_required_temp_file_with_error
- ENV.stubs(:fetch).with('MDE_LINK_REQUIRED_FILE',
- nil).returns('/path/to/temp_file')
- FileUtils.stubs(:rm_f).raises(StandardError)
- @hd.expects(:error_handler).with('delete_required_temp_file').once
-
- @hd.delete_required_temp_file
-
- assert true # Placeholder for actual test assertions
- end
- end # class TestHashDelegator
-
class TestHashDelegatorDetermineBlockState < Minitest::Test
def setup
@hd = HashDelegator.new
@hd.stubs(:menu_chrome_formatted_option).returns('Formatted Option')
end
@@ -2111,11 +2022,11 @@
result = @hd.determine_block_state(selected_option)
assert_equal MenuState::CONTINUE, result.state
assert_equal selected_option, result.block
end
- end # class TestHashDelegator
+ end
class TestHashDelegatorDisplayRequiredCode < Minitest::Test
def setup
@hd = HashDelegator.new
@hd.instance_variable_set(:@fout, mock('fout'))
@@ -2132,11 +2043,11 @@
@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
- end # class TestHashDelegator
+ end
class TestHashDelegatorFetchColor < Minitest::Test
def setup
@hd = HashDelegator.new
@hd.instance_variable_set(:@delegate_object, {})
@@ -2163,11 +2074,11 @@
result = @hd.fetch_color
assert_equal 'Default Colored String', result
end
- end # class TestHashDelegator
+ end
class TestHashDelegatorFormatReferencesSendColor < Minitest::Test
def setup
@hd = HashDelegator.new
@hd.instance_variable_set(:@delegate_object, {})
@@ -2194,11 +2105,11 @@
result = @hd.format_references_send_color(context: { key: 'value' },
color_sym: :execution_report_preview_frame_color)
assert_equal 'Default Colored String', result
end
- end # class TestHashDelegator
+ end
class TestHashDelegatorFormatExecutionStreams < Minitest::Test
def setup
@hd = HashDelegator.new
@hd.instance_variable_set(:@run_state, mock('run_state'))
@@ -2227,30 +2138,30 @@
result = @hd.format_execution_streams(:stdin)
assert_equal '', result
end
- end # class TestHashDelegator
+ 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
+ # @hd.expects(:history_state_pop).once
result = @hd.pop_link_history_and_trigger_load
- # Asserting the result is an instance of LoadFileNextBlock
- assert_instance_of LoadFileNextBlock, result
+ # Asserting the result is an instance of LoadFileLinkState
+ assert_instance_of LoadFileLinkState, result
assert_equal LoadFile::Load, result.load_file
- assert_equal '', result.next_block
+ assert_nil result.link_state.block_name
end
- end # class TestHashDelegator
+ end
class TestHashDelegatorHandleBlockState < Minitest::Test
def setup
@hd = HashDelegator.new
@mock_block_state = mock('block_state')
@@ -2285,11 +2196,11 @@
@hd.handle_block_state(@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
- end # class TestHashDelegator
+ end
class TestHashDelegatorHandleGenericBlock < Minitest::Test
def setup
@hd = HashDelegator.new
@mock_document = mock('MarkdownDocument')
@@ -2341,11 +2252,11 @@
selected = { body: ['option1: value1'] }
tgt2 = {}
result = @hd.update_options_and_trigger_reuse(selected, tgt2)
- assert_instance_of LoadFileNextBlock, result
+ assert_instance_of LoadFileLinkState, result
assert_equal 'value1',
@hd.instance_variable_get(:@delegate_object)[:option1]
assert_equal 'value1', tgt2[:option1]
end
@@ -2353,11 +2264,11 @@
selected = { body: ['option2: value2'] }
@hd.instance_variable_set(:@delegate_object, {})
result = @hd.update_options_and_trigger_reuse(selected)
- assert_instance_of LoadFileNextBlock, result
+ assert_instance_of LoadFileLinkState, result
assert_equal 'value2',
@hd.instance_variable_get(:@delegate_object)[:option2]
end
end
@@ -2396,98 +2307,10 @@
assert_equal [],
@hd.instance_variable_get(:@run_state).files[:stdout]
end
end
- class TestHashDelegatorHistoryStatePartition < Minitest::Test
- def setup
- @hd = HashDelegator.new
- @hd.instance_variable_set(:@delegate_object, {
- history_document_separator: '|'
- })
- end
-
- def test_history_state_partition_with_value
- ENV[MDE_HISTORY_ENV_NAME] = 'part1|part2'
-
- result = @hd.history_state_partition
- assert_equal({ unit: 'part1', rest: 'part2' }, result)
- end
-
- def test_history_state_partition_with_no_separator
- ENV[MDE_HISTORY_ENV_NAME] = 'onlypart'
-
- result = @hd.history_state_partition
- assert_equal({ unit: 'onlypart', rest: '' }, result)
- end
-
- def test_history_state_partition_with_empty_env
- ENV[MDE_HISTORY_ENV_NAME] = ''
-
- result = @hd.history_state_partition
- assert_equal({ unit: '', rest: '' }, result)
- end
- end
-
- class TestHashDelegatorHistoryStatePop < Minitest::Test
- def setup
- @hd = HashDelegator.new
- @hd.instance_variable_set(:@delegate_object,
- { filename: 'initial.md' })
- @hd.instance_variable_set(:@run_state,
- OpenStruct.new(link_history: [{ block_name: 'block1',
- filename: 'file1.md' }]))
- @hd.stubs(:history_state_partition).returns({ unit: 'file2.md',
- rest: 'history_data' })
- @hd.stubs(:delete_required_temp_file)
- end
-
- def test_history_state_pop
- ENV[MDE_HISTORY_ENV_NAME] = 'some_history'
-
- @hd.history_state_pop
-
- assert_equal 'file2.md',
- @hd.instance_variable_get(:@delegate_object)[:filename]
- assert_equal 'history_data',
- ENV.fetch(MDE_HISTORY_ENV_NAME, nil)
- assert_empty @hd.instance_variable_get(:@run_state).link_history
- end
- end
-
- class TestHashDelegatorHistoryStatePush < Minitest::Test
- def setup
- @hd = HashDelegator.new
- @hd.instance_variable_set(:@delegate_object, {
- filename: 'test.md',
- block_name: 'test_block',
- history_document_separator: '||'
- })
- @hd.instance_variable_set(:@run_state,
- OpenStruct.new(link_history: []))
- @hd.stubs(:write_required_blocks_to_temp_file)
- end
-
- def test_history_state_push
- mdoc = 'markdown content'
- data_file = 'data.md'
- selected = { oname: 'selected_block' }
-
- ENV[MDE_HISTORY_ENV_NAME] = 'existing_history'
-
- @hd.history_state_push(mdoc, data_file, selected)
-
- assert_equal 'data.md',
- @hd.instance_variable_get(:@delegate_object)[:filename]
- assert_equal 'test.md||existing_history',
- ENV.fetch(MDE_HISTORY_ENV_NAME, nil)
- assert_includes @hd.instance_variable_get(:@run_state).link_history,
- { block_name: 'selected_block',
- filename: 'data.md' }
- end
- end
-
class TestHashDelegatorIterBlocksFromNestedFiles < Minitest::Test
def setup
@hd = HashDelegator.new
@hd.instance_variable_set(:@delegate_object,
{ filename: 'test.md' })
@@ -2655,10 +2478,10 @@
def test_yield_line_if_selected_without_block
result = @hd.yield_line_if_selected('Test line', [:line])
assert_nil result
end
- end # class TestHashDelegator
+ end
class TestHashDelegator < Minitest::Test
def setup
@hd = HashDelegator.new
@hd.instance_variable_set(:@delegate_object, {