lib/hash_delegator.rb in markdown_exec-2.4.0 vs lib/hash_delegator.rb in markdown_exec-2.5.0
- old
+ new
@@ -18,15 +18,16 @@
require 'yaml'
require_relative 'ansi_string'
require_relative 'array'
require_relative 'array_util'
-require_relative 'block_label'
+# require_relative 'block_label'
require_relative 'block_types'
require_relative 'cached_nested_file_reader'
require_relative 'constants'
require_relative 'directory_searcher'
+require_relative 'evaluate_shell_expressions'
require_relative 'exceptions'
require_relative 'fcb'
require_relative 'filter'
require_relative 'format_table'
require_relative 'fout'
@@ -40,12 +41,10 @@
require_relative 'streams_out'
require_relative 'string_util'
require_relative 'table_extractor'
require_relative 'text_analyzer'
-require_relative 'argument_processor'
-
$pd = false unless defined?($pd)
class String
# Checks if the string is not empty.
# @return [Boolean] Returns true if the string is not empty, false otherwise.
@@ -59,55 +58,38 @@
# The method retrieves the color method from the provided hash. If the
# color key is not present in the hash, it uses a default color method.
# @param string [String] The string to be colored.
# @param color_methods [Hash] A hash where keys are color names
# (String/Symbol) and values are color methods.
- # @param color_key [String, Symbol] The key representing the desired
+ # @param color_key [String, Symbol] The key representing
+ # the desired
# color method in the color_methods hash.
# @param default_method [String] (optional) Default color method to
# use if color_key is not found in color_methods. Defaults to 'plain'.
# @return [String] The colored string.
def apply_color_from_hash(string, color_methods, color_key,
default_method: 'plain')
color_method = color_methods.fetch(color_key, default_method).to_sym
AnsiString.new(string.to_s).send(color_method)
end
- # # Enhanced `apply_color_from_hash` method to support dynamic color transformations
- # # @param string [String] The string to be colored.
- # # @param color_transformations [Hash] A hash mapping color names to lambdas that apply color transformations.
- # # @param color_key [String, Symbol] The key representing the desired color transformation in the color_transformations hash.
- # # @param default_transformation [Proc] Default color transformation to use if color_key is not found in color_transformations.
- # # @return [String] The colored string.
- # def apply_color_from_hash(string, color_transformations, color_key, default_transformation: ->(str) { str })
- # transformation = color_transformations.fetch(color_key.to_sym, default_transformation)
- # transformation.call(string)
- # end
- # color_transformations = {
- # red: ->(str) { "\e[31m#{str}\e[0m" }, # ANSI color code for red
- # green: ->(str) { "\e[32m#{str}\e[0m" }, # ANSI color code for green
- # # Add more color transformations as needed
- # }
- # string = "Hello, World!"
- # colored_string = apply_color_from_hash(string, color_transformations, :red)
- # puts colored_string # This will print the string in red
-
# Searches for the first element in a collection where the specified
# message sent to an element matches a given value.
# This method is particularly useful for finding a specific hash-like
# object within an enumerable collection.
# If no match is found, it returns a specified default value.
#
- # @param blocks [Enumerable] The collection of hash-like objects to search.
+ # @param blocks [Enumerable] The collection of hash-like
+ # objects to search.
# @param msg [Symbol, String] The message to send to each element of
- # the collection.
+ # the collection.
# @param value [Object] The value to match against the result of the message
- # sent to each element.
+ # sent to each element.
# @param default [Object, nil] The default value to return if no match is
- # found (optional).
+ # found (optional).
# @return [Object, nil] The first matching element or the default value if
- # no match is found.
+ # no match is found.
def block_find(blocks, msg, value, default = nil)
blocks.find { |item| item.send(msg) == value } || default
end
def code_merge(*bodies)
@@ -120,18 +102,19 @@
def create_directory_for_file(file_path)
FileUtils.mkdir_p(File.dirname(file_path))
end
- # Creates a file at the specified path, writes the given content to it,
- # and sets file permissions if required. Handles any errors encountered
- # during the process.
+ # Creates a file at the specified path, writes the given
+ # content to it, 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 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.
+ # @param chmod_value [Integer] The file permission value
+ # to set; skips if zero.
def create_file_and_write_string_with_permissions(file_path, content,
chmod_value)
create_directory_for_file(file_path)
File.write(file_path, content)
set_file_permissions(file_path, chmod_value) unless chmod_value.zero?
@@ -144,56 +127,37 @@
# end
# Updates the title of an FCB object from its body content if the title
# is nil or empty.
def default_block_title_from_body(fcb)
- return unless fcb.title.nil? || fcb.title.empty?
+ return fcb.title unless fcb.title.nil? || fcb.title.empty?
fcb.derive_title_from_body
end
# delete the current line if it is empty and the previous is also empty
def delete_consecutive_blank_lines!(blocks_menu)
- blocks_menu.process_and_conditionally_delete! do |prev_item, current_item, _next_item|
+ blocks_menu.process_and_conditionally_delete! do
+ |prev_item, current_item, _next_item|
prev_item&.fetch(:chrome, nil) &&
!(prev_item && prev_item.oname.present?) &&
current_item&.fetch(:chrome, nil) &&
!(current_item && current_item.oname.present?)
end
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)
- # 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 = {}, error: $!)
Exceptions.error_handler(
"HashDelegator.#{name} -- #{error}",
opts
)
end
- # # DebugHelper.d ["HDmm method_name: #{method_name}", "#{first_n_caller_items 1}"]
- # def first_n_caller_items(n)
- # 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|
- # " . #{line.sub(/^#{Regexp.escape(base_path)}\//, '')}"
- # end.join("\n")
- # 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).
+ # (default is an empty string).
# @return [String] A single string with each line indented as specified.
def indent_all_lines(body, indent = nil)
return body unless indent&.non_empty?
body.lines.map { |line| indent + line.chomp }.join("\n")
@@ -206,38 +170,48 @@
def join_code_lines(lines)
((lines || []) + ['']).join("\n")
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
+ # 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
- def next_link_state(block_name_from_cli:, was_using_cli:, block_state:,
- block_name: nil)
+ def next_link_state(
+ block_name_from_cli:, was_using_cli:, block_state:, block_name: nil
+ )
# Set block_name based on block_name_from_cli
block_name = @cli_block_name if block_name_from_cli
# Determine the state of breaker based on was_using_cli and the block type
- # true only when block_name is nil, block_name_from_cli is false, was_using_cli is true, and the block_state.block.shell equals BlockType::BASH. In all other scenarios, breaker is false.
- breaker = !block_name && !block_name_from_cli && was_using_cli && block_state.block.shell == BlockType::BASH
+ # true only when block_name is nil, block_name_from_cli is false,
+ # was_using_cli is true, and the block_state.block.shell equals
+ # BlockType::BASH. In all other scenarios, breaker is false.
+ breaker = !block_name &&
+ !block_name_from_cli &&
+ was_using_cli &&
+ block_state.block.type == BlockType::SHELL
# Reset block_name_from_cli if the conditions are not met
block_name_from_cli ||= false
[block_name, block_name_from_cli, breaker]
end
def parse_yaml_data_from_body(body)
- body.any? ? YAML.load(body.join("\n")) : {}
+ body&.any? ? YAML.load(body.join("\n")) : {}
rescue StandardError
- error_handler('parse_yaml_data_from_body', { abort: true })
+ error_handler("parse_yaml_data_from_body for body: #{body}",
+ { abort: true })
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.
+ # 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)
return [] if temp_blocks_file_path.to_s.empty?
if File.exist?(temp_blocks_file_path)
File.readlines(
@@ -262,11 +236,12 @@
# error_handler('safeval') # 'Invalid expression'
# return
# end
# # Whitelisting allowed operations
- # allowed_methods = %w[+ - * / == != < > <= >= && || % & |]
+ # allowed_methods = %w[+ - * / == != < > <= >= && || % &
+ # |]
# unless allowed_methods.any? { |op| str.include?(op) }
# error_handler('safeval', 'Operation not allowed')
# return
# end
@@ -291,62 +266,72 @@
# find tables in multiple lines and format horizontally
def tables_into_columns!(blocks_menu, delegate_object)
return unless delegate_object[:tables_into_columns]
lines = blocks_menu.map(&:oname)
- text_tables = TableExtractor.extract_tables(lines)
+ text_tables = TableExtractor.extract_tables(
+ lines,
+ regexp: delegate_object[:table_parse_regexp]
+ )
return unless text_tables.count.positive?
- text_tables.each do |match|
- range = match[:start_index]..(match[:start_index] + match[:rows] - 1)
- lines = blocks_menu[range].map(&:oname)
+ text_tables.each do |table|
+ next unless table[:columns].positive?
+
+ range = table[:start_index]..(table[:start_index] + table[:rows] - 1)
+ lines = blocks_menu[range].map(&:dname)
formatted = MarkdownTableFormatter.format_table(
- lines,
- match[:columns],
+ column_count: table[:columns],
decorate: {
border: delegate_object[:table_border_color],
header_row: delegate_object[:table_header_row_color],
row: delegate_object[:table_row_color],
separator_line: delegate_object[:table_separator_line_color]
- }
+ },
+ lines: lines
)
- if formatted.count == range.size
- # read indentation from first line
- indent = blocks_menu[range.first].oname.split('|', 2).first
-
- # replace text in each block
- range.each.with_index do |block_ind, ind|
- ### format oname to dname
- blocks_menu[block_ind].dname = indent + formatted[ind]
- end
- else
- warn [__LINE__, range, lines, formatted].inspect
+ unless formatted.count == range.size
+ # warn [__LINE__, range, lines, formatted].inspect
raise 'Invalid result from MarkdownTableFormatter.format_table()'
end
+
+ # read indentation from first line
+ indent = blocks_menu[range.first].oname.split('|', 2).first
+
+ # replace text in each block
+ range.each.with_index do |block_ind, ind|
+ ### format oname to dname
+ blocks_menu[block_ind].dname = indent + formatted[ind]
+ end
end
end
- # Creates a TTY prompt with custom settings. Specifically, it disables the default 'cross' symbol and
+ # Creates a TTY prompt with custom settings. Specifically,
+ # it disables the default 'cross' symbol and
# defines a lambda function to handle interrupts.
- # @return [TTY::Prompt] A new TTY::Prompt instance with specified configurations.
+ # @return [TTY::Prompt] A new TTY::Prompt instance
+ # with specified configurations.
def tty_prompt_without_disabled_symbol
TTY::Prompt.new(
interrupt: lambda {
puts # next line in case not at start
raise TTY::Reader::InputInterrupt
},
symbols: { cross: ' ' }
)
end
- # Updates the attributes of the given fcb object and conditionally yields to a block.
+ # Updates the attributes of the given fcb object and
+ # conditionally yields to a block.
# It initializes fcb names and sets the default block title from fcb's body.
- # If the fcb has a body and meets certain conditions, it yields to the given block.
+ # If the fcb has a body and meets certain conditions,
+ # it yields to the given block.
#
# @param fcb [Object] The fcb object whose attributes are to be updated.
- # @param selected_types [Array<Symbol>] A list of message types to determine if yielding is applicable.
+ # @param selected_types [Array<Symbol>] A list of message types to
+ # determine if yielding is applicable.
# @param block [Block] An optional block to yield to if conditions are met.
def update_menu_attrib_yield_selected(fcb:, messages:, configuration: {},
&block)
initialize_fcb_names(fcb)
return unless fcb.body
@@ -367,11 +352,12 @@
end
end
# This module provides methods for compacting and converting data structures.
module CompactionHelpers
- # Converts an array of key-value pairs into a hash, applying compaction to the values.
+ # Converts an array of key-value pairs into a hash,
+ # applying compaction to the values.
# Each value is processed by `compact_hash` to remove ineligible elements.
#
# @param array [Array] The array of key-value pairs to be converted.
# @return [Hash] A hash with keys from the array and compacted values.
def compact_and_convert_array_to_hash(array)
@@ -379,11 +365,12 @@
compact_hash(value)
end
end
# Compacts a hash by removing ineligible elements.
- # It filters out nil, empty arrays, empty hashes, and empty strings from its values.
+ # It filters out nil, empty arrays, empty hashes,
+ # and empty strings from its values.
# It also removes entries with :random as the key.
#
# @param hash [Hash] The hash to be compacted.
# @return [Hash] A compacted version of the input hash.
def compact_hash(hash)
@@ -392,12 +379,14 @@
[key, value]
end.compact.to_h
end
- # Converts a hash into another hash with indexed keys, applying compaction to the values.
- # The keys are indexed, and the values are compacted using `compact_and_convert_array_to_hash`.
+ # Converts a hash into another hash with indexed keys,
+ # applying compaction to the values.
+ # The keys are indexed, and the values are
+ # compacted using `compact_and_convert_array_to_hash`.
#
# @param hash [Hash] The hash to be converted and compacted.
# @return [Hash] A hash with indexed keys and the compacted original values.
def compact_and_index_hash(hash)
compact_and_convert_array_to_hash(hash.map.with_index do |value, index|
@@ -405,36 +394,42 @@
end.to_h)
end
private
- # Determines if a value is ineligible for inclusion in a compacted hash.
- # Ineligible values are nil, empty arrays, empty hashes, and empty strings.
+ # Determines if a value is ineligible for inclusion in a
+ # compacted hash.
+ # Ineligible values are nil, empty arrays, empty hashes,
+ # and empty strings.
#
# @param value [Object] The value to be checked.
# @return [Boolean] True if the value is ineligible, false otherwise.
def value_ineligible?(value)
[nil, [], {}, ''].include?(value)
end
end
module PathUtils
- # Determines if a given path is absolute or substitutes a placeholder in an expression with the path.
+ # Determines if a given path is absolute or substitutes a
+ # placeholder in an expression with the path.
# @param path [String] The input path to check or fill in.
- # @param expression [String] The expression where a wildcard '*' is replaced by the path if it's not absolute.
- # @return [String] The absolute path or the expression with the wildcard replaced by the path.
+ # @param expression [String] The expression where a wildcard
+ # '*' is replaced by the path if it's not absolute.
+ # @return [String] The absolute path or the expression with
+ # the wildcard replaced by the path.
def self.resolve_path_or_substitute(path, expression)
if path.start_with?('/')
path
else
expression.gsub('*', path)
end
end
end
class BashCommentFormatter
- # Formats a multi-line string into a format safe for use in Bash comments.
+ # Formats a multi-line string into a format safe for use
+ # in Bash comments.
def self.format_comment(input_string)
return '# ' if input_string.nil?
return '# ' if input_string.empty?
formatted = input_string.split("\n").map do |line|
@@ -453,12 +448,14 @@
# Initializes the StringWrapper with the given options.
#
# @param width [Integer] the maximum width of each line
# @param left_margin [Integer] the number of spaces for the left margin
- # @param right_margin [Integer] the number of spaces for the right margin
- # @param indent [Integer] the number of spaces to indent all but the first line
+ # @param right_margin [Integer] the number of spaces for
+ # the right margin
+ # @param indent [Integer] the number of
+ # spaces to indent all but the first line
# @param fill_margin [Boolean] whether to fill the left margin with spaces
def initialize(
width:,
fill_margin: false,
first_indent: '',
@@ -616,12 +613,72 @@
append_divider(menu_blocks: menu_blocks, position: :initial)
append_divider(menu_blocks: menu_blocks, position: :final)
end
+ def variable_expansions!(
+ echo_command_form: 'echo "$%s"',
+ link_state:,
+ menu_blocks:,
+ regexp: Regexp.new(@delegate_object[:variable_expression_regexp])
+ )
+ # !!v link_state.inherited_lines_block
+ # collect variables in menu_blocks
+ #
+ variables_count = Hash.new(0)
+ menu_blocks.each do |fcb|
+ next if fcb.type == BlockType::SHELL
+
+ fcb.oname.scan(regexp) do |(expression, variable)|
+ expression.match(regexp)
+ variables_count[$LAST_MATCH_INFO[:variable]] += 1
+ end
+ end
+ # !!v variables_count
+
+ # commands to echo variables
+ #
+ commands = {}
+ variables_count.each do |variable, count|
+ command = format(echo_command_form, variable)
+ commands[variable] = command
+ end
+ # !!v commands
+
+ # replacement dictionary from evaluated commands
+ #
+ replacement_dictionary = evaluate_shell_expressions(
+ link_state.inherited_lines_block, commands,
+ key_format: "${%s}" # no need to escape variable name for regexp
+ ) # !!t
+ return if replacement_dictionary.nil?
+
+ # update blocks
+ #
+ Regexp.union(replacement_dictionary.keys).tap do |pattern|
+ menu_blocks.each do |fcb|
+ next if fcb.type == BlockType::SHELL
+
+ fcb.variable_expansion!(pattern, replacement_dictionary)
+ end
+ end
+ end
+
private
+ def replace_keys_in_lines(replacement_dictionary, lines)
+ # Create a regex pattern that matches any key in the replacement dictionary
+ pattern = Regexp.union(replacement_dictionary.keys.map { |key|
+ "%<#{key}>"
+ })
+
+ # Iterate over each line and apply gsub with the replacement hash
+ lines.map do |line|
+ line.gsub(pattern) { |match| replacement_dictionary[match] }
+ end
+ end
+
def add_back_option(menu_blocks:)
append_chrome_block(menu_blocks: menu_blocks, menu_state: MenuState::BACK)
end
def add_exit_option(menu_blocks:)
@@ -737,26 +794,30 @@
# private
# Applies shell color options to the given string if applicable.
#
# @param name [String] The name to potentially colorize.
- # @param shell_color_option [Symbol, nil] The shell color option to apply.
+ # @param block_type_color_option [Symbol, nil] The shell color option to apply.
# @return [String] The colorized or original name string.
- def apply_shell_color_option(name, shell_color_option)
- if shell_color_option && @delegate_object[shell_color_option].present?
- string_send_color(name, shell_color_option)
+ def apply_block_type_color_option(name, block_type_color_option)
+ if block_type_color_option && @delegate_object[block_type_color_option].present?
+ string_send_color(name, block_type_color_option)
else
name
end
end
def apply_tree_decorations(text, color_method, decor_patterns)
tree = HierarchyString.new([{ text: text, color: color_method }])
- decor_patterns.each do |pc|
- analyzed_hierarchy = TextAnalyzer.analyze_hierarchy(tree.substrings, pc[:pattern],
- color_method, pc[:color_method])
- tree = HierarchyString.new(analyzed_hierarchy)
+ if color_method
+ decor_patterns.each do |pc|
+ analyzed_hierarchy = TextAnalyzer.analyze_hierarchy(
+ tree.substrings, pc[:pattern],
+ color_method, pc[:color_method]
+ )
+ tree = HierarchyString.new(analyzed_hierarchy)
+ end
end
tree.decorate
end
def assign_key_value_in_bash(key, value)
@@ -768,23 +829,42 @@
end
end
# private
- # Iterates through nested files to collect various types of blocks, including dividers, tasks, and others.
+ # Iterates through nested files to collect various types
+ # of blocks, including dividers, tasks, and others.
# The method categorizes blocks based on their type and processes them accordingly.
#
# @return [Array<FCB>] An array of FCB objects representing the blocks.
def blocks_from_nested_files
register_console_attributes(@delegate_object)
@decor_patterns_from_delegate_object_for_block_create = collect_line_decor_patterns(@delegate_object)
blocks = []
iter_blocks_from_nested_files do |btype, fcb|
- process_block_based_on_type(blocks, btype, fcb)
+ case btype
+ when :blocks
+ if @delegate_object[:bash]
+ fcb.for_menu!(
+ block_calls_scan: @delegate_object[:block_calls_scan],
+ block_name_match: @delegate_object[:block_name_match],
+ block_name_nick_match: @delegate_object[:block_name_nick_match],
+ ) do |oname, color|
+ apply_block_type_color_option(oname, color)
+ end
+ end
+ blocks << fcb
+ when :filter # types accepted
+ %i[blocks line]
+ when :line
+ unless @delegate_object[:no_chrome]
+ create_and_add_chrome_blocks(blocks, fcb)
+ end
+ end
end
- # &bt blocks.count
+ # !!t blocks.count
blocks
rescue StandardError
HashDelegator.error_handler('blocks_from_nested_files')
end
@@ -822,11 +902,15 @@
blockname: block_name,
filename: @delegate_object[:filename],
prefix: @delegate_object[:logged_stdout_filename_prefix],
time: Time.now.utc,
exts: '.out.txt',
- saved_asset_format: shell_escape_asset_format(@dml_link_state)
+ saved_asset_format:
+ shell_escape_asset_format(
+ code_lines: @dml_link_state.inherited_lines,
+ shell: ShellType::BASH
+ )
).generate_name
@logged_stdout_filespec =
@delegate_object[:logged_stdout_filespec] =
File.join @delegate_object[:saved_stdout_folder],
@@ -851,10 +935,27 @@
return false
end
true
end
+ def code_from_vars_block_to_set_environment_variables(selected)
+ code_lines = []
+ YAML.load(selected.body.join("\n"))&.each do |key, value|
+ ENV[key] = value.to_s
+
+ require 'shellwords'
+ code_lines.push "#{key}=\"#{Shellwords.escape(value)}\""
+
+ next unless @delegate_object[:menu_vars_set_format].present?
+
+ formatted_string = format(@delegate_object[:menu_vars_set_format],
+ { key: key, value: value })
+ print string_send_color(formatted_string, :menu_vars_set_color)
+ end
+ code_lines
+ end
+
def collect_line_decor_patterns(delegate_object)
extract_patterns = lambda do |key|
return [] unless delegate_object[key].present?
HashDelegator.safeval(delegate_object[key]).map do |pc|
@@ -868,125 +969,106 @@
%i[line_decor_pre line_decor_main line_decor_post].flat_map do |key|
extract_patterns.call(key)
end
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:, block_source:,
- link_state: LinkState.new)
- required = mdoc.collect_recursively_required_code(
- anyname: selected.pub_name,
- label_format_above: @delegate_object[:shell_code_label_format_above],
- label_format_below: @delegate_object[:shell_code_label_format_below],
- block_source: block_source
- ) # &bt 'required'
- dependencies = (link_state&.inherited_dependencies || {}).merge(required[:dependencies] || {})
- 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(dependencies,
- highlight: required[:unmet_dependencies])
- runtime_exception(:runtime_exception_error_level,
- 'unmet_dependencies, flag: runtime_exception_error_level',
- required[:unmet_dependencies])
- elsif @delegate_object[:dump_dependencies]
- warn format_and_highlight_dependencies(dependencies,
- highlight: [@delegate_object[:block_name]])
- end
-
- if selected[:shell] == BlockType::OPTS
- # body of blocks is returned as a list of lines to be read an YAML
- HashDelegator.code_merge(required[:blocks].map(&:body).flatten(1))
- else
- code_lines = selected.shell == BlockType::VARS ? set_environment_variables_for_block(selected) : []
- HashDelegator.code_merge(link_state&.inherited_lines,
- required[:code] + code_lines)
- end
- end
-
- def command_execute(command, args: [])
+ def command_execute(command, shell:, args: [])
@run_state.files = StreamsOut.new
@run_state.options = @delegate_object
@run_state.started_at = Time.now.utc
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],
- command_execute_in_own_window_format_arguments(rest: args ? args.join(' ') : '')
- )
+ command_execute_in_own_window(
+ args: args,
+ script: @delegate_object[:execute_command_format]
)
+
else
@run_state.in_own_window = false
- execute_command_with_streams(
- [@delegate_object[:shell], '-c', command,
- @delegate_object[:filename], *args]
+ command_execute_in_process(
+ args: args, command: command,
+ filename: @delegate_object[:filename], shell: shell
)
end
@run_state.completed_at = Time.now.utc
- rescue Errno::ENOENT => err
- # Handle ENOENT error
- @run_state.aborted_at = Time.now.utc
- @run_state.error_message = err.message
- @run_state.error = err
- @run_state.files.append_stream_line(ExecutionStreams::STD_ERR,
- @run_state.error_message)
- @fout.fout "Error ENOENT: #{err.inspect}"
+ rescue Errno::ENOENT
+ report_error($ERROR_INFO)
rescue SignalException => err
# Handle SignalException
@run_state.aborted_at = Time.now.utc
@run_state.error_message = 'SIGTERM'
@run_state.error = err
@run_state.files.append_stream_line(ExecutionStreams::STD_ERR,
@run_state.error_message)
@fout.fout "Error ENOENT: #{err.inspect}"
end
+ def command_execute_in_own_window(args:, script:)
+ system(
+ format(
+ script,
+ command_execute_in_own_window_format_arguments(
+ rest: args ? args.join(' ') : ''
+ )
+ )
+ )
+ end
+
def command_execute_in_own_window_format_arguments(home: Dir.pwd, rest: '')
{
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: home,
- output_filename: File.basename(@delegate_object[:logged_stdout_filespec]),
+ output_filename: File.basename(
+ @delegate_object[:logged_stdout_filespec]
+ ),
output_filespec: @delegate_object[:logged_stdout_filespec],
rest: rest,
script_filename: @run_state.saved_filespec,
script_filespec: File.join(home, @run_state.saved_filespec),
started_at: @run_state.started_at.strftime(
@delegate_object[:execute_command_title_time_format]
)
}
end
- # This method is responsible for handling the execution of generic blocks in a markdown document.
- # It collects the required code lines from the document and, depending on the configuration,
- # may display the code for user approval before execution. It then executes the approved block.
+ def command_execute_in_process(args:, command:, filename:, shell:)
+ execute_command_with_streams(
+ [shell, '-c', command,
+ @delegate_object[:filename], *args]
+ )
+ end
+
+ # This method is responsible for handling the execution of
+ # generic blocks in a markdown document.
+ # It collects the required code lines from the document and,
+ # depending on the configuration, may display the code for
+ # user approval before execution. It then executes the approved block.
#
- # @param mdoc [Object] The markdown document object containing code blocks.
- # @param selected [Hash] The selected item from the menu to be executed.
- # @return [LoadFileLinkState] An object indicating whether to load the next block or reuse the current one.
- def compile_execute_and_trigger_reuse(mdoc:, selected:, block_source:,
- link_state:)
- required_lines = collect_required_code_lines(
+ # @param mdoc [Object] The markdown document object
+ # containing code blocks.
+ # @param selected [Hash] The selected item from the menu
+ # to be executed.
+ # @return [LoadFileLinkState] An object indicating whether to load
+ # the next block or reuse the current one.
+ def compile_execute_and_trigger_reuse(
+ mdoc:, selected:, block_source:, link_state:
+ )
+ required_lines = execute_block_type_port_code_lines(
mdoc: mdoc, selected: selected,
link_state: link_state, block_source: block_source
)
- output_or_approval = @delegate_object[:output_script] || @delegate_object[:user_must_approve]
+ output_or_approval = @delegate_object[:output_script] ||
+ @delegate_object[:user_must_approve]
if output_or_approval
display_required_code(required_lines: required_lines)
end
allow_execution = if @delegate_object[:user_must_approve]
prompt_for_user_approval(
@@ -997,11 +1079,12 @@
true
end
if allow_execution
execute_required_lines(required_lines: required_lines,
- selected: selected)
+ selected: selected,
+ shell: selected.shell)
end
link_state.block_name = nil
end
@@ -1017,35 +1100,43 @@
" #{required_lines.flatten.count} lines," \
" #{text.length} characters"
end
# Counts the number of fenced code blocks in a file.
- # It reads lines from a file and counts occurrences of lines matching the fenced block regex.
- # Assumes that every fenced block starts and ends with a distinct line (hence divided by 2).
+ # It reads lines from a file and counts occurrences of lines
+ # matching the fenced block regex.
+ # 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],
- import_paths: @delegate_object[:import_paths]&.split(':'))
+ 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.
+ # Creates and adds a formatted block to the blocks array
+ # based on the provided match and format options.
# @param blocks [Array] The array of blocks to add the new block to.
- # @param match_data [MatchData] The match data containing named captures for formatting.
- # @param format_option [String] The format string to be used for the new block.
- # @param color_method [Symbol] The color method to apply to the block's display name.
+ # @param match_data [MatchData] The match data containing named captures
+ # for formatting.
+ # @param format_option [String] The format string to be used
+ # for the new block.
+ # @param color_method [Symbol] The color method to apply
+ # to the block's display name.
# return number of lines added
def create_and_add_chrome_block(blocks:, match_data:,
format_option:, color_method:,
case_conversion: nil,
center: nil,
decor_patterns: [],
wrap: nil)
- line_cap = match_data.named_captures.transform_keys(&:to_sym)
+ line_cap = NamedCaptureExtractor::extract_named_group2(match_data)
# replace tabs in indent
line_cap[:indent] ||= ''
line_cap[:indent] = line_cap[:indent].dup if line_cap[:indent].frozen?
line_cap[:indent].gsub!("\t", ' ')
@@ -1057,11 +1148,13 @@
line_cap[:line] ||= ''
accepted_width = @delegate_object[:console_width] - 2
line_caps = if wrap
if line_cap[:text].length > accepted_width
- wrapper = StringWrapper.new(width: accepted_width - line_cap[:indent].length)
+ wrapper = StringWrapper.new(
+ width: accepted_width - line_cap[:indent].length
+ )
wrapper.wrap(line_cap[:text]).map do |line|
line_cap.dup.merge(text: line)
end
else
[line_cap]
@@ -1069,15 +1162,16 @@
else
[line_cap]
end
if center
line_caps.each do |line_obj|
- line_obj[:indent] = if line_obj[:text].length < accepted_width
- ' ' * ((accepted_width - line_obj[:text].length) / 2)
- else
- ''
- end
+ line_obj[:indent] =
+ if line_obj[:text].length < accepted_width
+ ' ' * ((accepted_width - line_obj[:text].length) / 2)
+ else
+ ''
+ end
end
end
line_caps.each do |line_obj|
next if line_obj[:text].nil?
@@ -1089,14 +1183,19 @@
line_obj[:text].downcase!
end
# format expects :line to be text only
line_obj[:line] = line_obj[:text]
- oname = format(format_option, line_obj)
+ oname = if format_option
+ format(format_option, line_obj)
+ else
+ line_obj[:line]
+ end
+ decorated = apply_tree_decorations(
+ oname, color_method, decor_patterns
+ )
- decorated = apply_tree_decorations(oname, color_method, decor_patterns)
-
line_obj[:line] = line_obj[:indent] + line_obj[:text]
blocks.push FCB.new(
chrome: true,
disabled: '',
dname: line_obj[:indent] + decorated,
@@ -1105,24 +1204,27 @@
end
line_caps.count
end
##
- # Processes lines within the file and converts them into blocks if they match certain criteria.
+ # Processes lines within the file and converts them into
+ # blocks if they match certain criteria.
# @param blocks [Array] The array to append new blocks to.
# @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.
+ # @param use_chrome [Boolean] Indicates if the chrome styling should
+ # be applied.
def create_and_add_chrome_blocks(blocks, fcb)
# rubocop:disable Layout/LineLength
match_criteria = [
- { color: :menu_heading1_color, format: :menu_heading1_format, match: :heading1_match, center: true, case_conversion: :upcase, wrap: true },
- { color: :menu_heading2_color, format: :menu_heading2_format, match: :heading2_match, center: true, wrap: true },
- { color: :menu_heading3_color, format: :menu_heading3_format, match: :heading3_match, center: true, case_conversion: :downcase, wrap: true },
+ { format: :menu_note_format, match: :menu_table_rows_match },
+ { color: :menu_heading1_color, format: :menu_heading1_format, match: :heading1_match, wrap: true, center: :heading1_center, case_conversion: :upcase },
+ { color: :menu_heading2_color, format: :menu_heading2_format, match: :heading2_match, wrap: true, center: :heading2_center },
+ { color: :menu_heading3_color, format: :menu_heading3_format, match: :heading3_match, wrap: true, center: :heading3_center, case_conversion: :downcase },
{ color: :menu_divider_color, format: :menu_divider_format, match: :menu_divider_match },
- { color: :menu_note_color, format: :menu_note_format, match: :menu_note_match, wrap: true },
- { color: :menu_task_color, format: :menu_task_format, match: :menu_task_match, wrap: true }
+ { color: :menu_note_color, format: :menu_note_format, match: :menu_note_match, wrap: true },
+ { color: :menu_task_color, format: :menu_task_format, match: :menu_task_match, wrap: true }
]
# rubocop:enable Layout/LineLength
# rubocop:enable Style/UnlessElse
match_criteria.each do |criteria|
unless @delegate_object[criteria[:match]].present? &&
@@ -1131,23 +1233,31 @@
end
create_and_add_chrome_block(
blocks: blocks,
case_conversion: criteria[:case_conversion],
- center: criteria[:center],
- color_method: @delegate_object[criteria[:color]].to_sym,
- decor_patterns: @decor_patterns_from_delegate_object_for_block_create,
- format_option: @delegate_object[criteria[:format]],
+ center: criteria[:center] &&
+ @delegate_object[criteria[:center]],
+ color_method: criteria[:color] &&
+ @delegate_object[criteria[:color]].to_sym,
+ decor_patterns:
+ @decor_patterns_from_delegate_object_for_block_create,
+ format_option: criteria[:format] &&
+ @delegate_object[criteria[:format]],
match_data: mbody,
wrap: criteria[:wrap]
)
break
end
end
def create_divider(position)
- divider_key = position == :initial ? :menu_initial_divider : :menu_final_divider
+ divider_key = if position == :initial
+ :menu_initial_divider
+ else
+ :menu_final_divider
+ end
oname = format(@delegate_object[:menu_divider_format],
HashDelegator.safeval(@delegate_object[divider_key]))
FCB.new(
chrome: true,
@@ -1166,11 +1276,12 @@
# filter block if selected in menu
return true if @run_state.source.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
+ return @allowed_execution_block == @prior_execution_block ||
+ prompt_approve_repeat
end
@prior_execution_block = @delegate_object[:block_name]
@allowed_execution_block = nil
true
@@ -1178,16 +1289,21 @@
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.
+ # 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.
+ # @return [SelectedBlockMenuState] An object representing
+ # the state of the selected block.
def determine_block_state(selected_option)
+ return if selected_option.nil?
+
option_name = selected_option[:oname]
if option_name == menu_chrome_formatted_option(:menu_option_exit_name)
return SelectedBlockMenuState.new(nil,
OpenStruct.new,
MenuState::EXIT)
@@ -1201,12 +1317,14 @@
SelectedBlockMenuState.new(selected_option,
OpenStruct.new,
MenuState::CONTINUE)
end
- # Displays the required lines of code with color formatting for the preview section.
- # It wraps the code lines between a formatted header and tail.
+ # Displays the required lines of code with color formatting
+ # for the preview section.
+ # It wraps the code lines between a formatted header and
+ # tail.
#
# @param required_lines [Array<String>] The lines of code to be displayed.
def display_required_code(required_lines:)
output_color_formatted(:script_preview_head,
:script_preview_frame_color)
@@ -1214,12 +1332,17 @@
output_color_formatted(:script_preview_tail,
:script_preview_frame_color)
end
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?
+ divider_key = if position == :initial
+ :menu_initial_divider
+ else
+ :menu_final_divider
+ end
+ @delegate_object[:menu_divider_format].present? &&
+ @delegate_object[divider_key].present?
end
def dml_menu_append_chrome_item(
name, count, type, menu_state: MenuState::LOAD,
always_create: true, always_enable: true
@@ -1250,23 +1373,34 @@
def do_save_execution_output
return unless @delegate_object[:save_execution_output]
return if @run_state.in_own_window
- @run_state.files.write_execution_output_to_file(@delegate_object[:logged_stdout_filespec])
+ @run_state.files.write_execution_output_to_file(
+ @delegate_object[:logged_stdout_filespec]
+ )
end
# remove leading "./"
# replace characters: / : . * (space) with: (underscore)
- def document_name_in_glob_as_file_name(document_filename, glob)
+ def document_name_in_glob_as_file_name(
+ document_filename: @dml_link_state.document_filename,
+ format_glob: @delegate_object[:document_saved_lines_glob],
+ remove_regexp: %r{^\./},
+ subst_regexp: /[\/:\.\* ]/,
+ subst_string: '_'
+ )
if document_filename.nil? || document_filename.empty?
return document_filename
end
- format(glob,
- { document_filename: document_filename.gsub(%r{^\./}, '').gsub(/[\/:\.\* ]/,
- '_') })
+ format(
+ format_glob,
+ { document_filename:
+ document_filename.gsub(remove_regexp, '')
+ .gsub(subst_regexp, subst_string) }
+ )
end
def dump_and_warn_block_state(name:, selected:)
if selected.nil?
Exceptions.warn_format("Block not found -- name: #{name}",
@@ -1278,28 +1412,34 @@
warn selected.to_yaml.sub(/^(?:---\n)?/, "Block:\n")
end
# Outputs warnings based on the delegate object's configuration
#
- # @param delegate_object [Hash] The delegate object containing configuration flags.
- # @param blocks_in_file [Hash] Hash of blocks present in the file.
+ # @param delegate_object [Hash] The delegate object containing
+ # configuration flags.
+ # @param blocks_in_file [Hash] Hash of blocks present in
+ # the file.
# @param menu_blocks [Hash] Hash of menu blocks.
# @param link_state [LinkState] Current state of the link.
def dump_delobj(blocks_in_file, menu_blocks, link_state)
if @delegate_object[:dump_delegate_object]
warn format_and_highlight_hash(@delegate_object,
label: '@delegate_object')
end
if @delegate_object[:dump_blocks_in_file]
- warn format_and_highlight_dependencies(compact_and_index_hash(blocks_in_file),
- label: 'blocks_in_file')
+ warn format_and_highlight_dependencies(
+ compact_and_index_hash(blocks_in_file),
+ label: 'blocks_in_file'
+ )
end
if @delegate_object[:dump_menu_blocks]
- warn format_and_highlight_dependencies(compact_and_index_hash(menu_blocks),
- label: 'menu_blocks')
+ warn format_and_highlight_dependencies(
+ compact_and_index_hash(menu_blocks),
+ label: 'menu_blocks'
+ )
end
if @delegate_object[:dump_inherited_block_names]
warn format_and_highlight_lines(link_state.inherited_block_names,
label: 'inherited_block_names')
@@ -1312,21 +1452,24 @@
warn format_and_highlight_lines(link_state.inherited_lines,
label: 'inherited_lines')
end
- # Opens text in an editor for user modification and returns the modified text.
+ # Opens text in an editor for user modification and
+ # returns the modified text.
#
# This method reads the provided text, opens it in the default editor,
# and allows the user to modify it. If the user makes changes, the
# modified text is returned. If the user exits the editor without
# making changes or the editor is closed abruptly, appropriate messages
# are displayed.
#
# @param [String] initial_text The initial text to be edited.
- # @param [String] temp_name The base name for the temporary file (default: 'edit_text').
- # @return [String, nil] The modified text, or nil if no changes were made or the editor was closed abruptly.
+ # @param [String] temp_name The base name for the temporary file
+ # (default: 'edit_text').
+ # @return [String, nil] The modified text, or nil if no changes
+ # were made or the editor was closed abruptly.
def edit_text(initial_text, temp_name: 'edit_text')
# Create a temporary file to store the initial text
temp_file = Tempfile.new(temp_name)
temp_file.write(initial_text)
temp_file.rewind
@@ -1364,20 +1507,136 @@
temp_file.unlink
result_text
end
- def execute_block_for_state_and_name(selected:, mdoc:, link_state:,
- block_source: {})
+ # Execute a code block after approval and provide user interaction options.
+ #
+ # This method displays required code blocks, asks for user approval, and
+ # executes the code block if approved. It also allows users to copy the
+ # 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_block_by_type_for_lfls(
+ selected:, mdoc:, block_source:, link_state: LinkState.new
+ )
+ # !!v selected
+ # order should not be important other than else clause
+ if selected.type == BlockType::EDIT
+ debounce_reset
+ # !!v link_state.inherited_lines_block
+ vux_edit_inherited
+ return :break if pause_user_exit
+
+ next_state_append_code(selected, link_state, [])
+
+ elsif selected.type == BlockType::HISTORY
+ # !!b
+ debounce_reset
+ return :break if execute_block_type_history_ux(
+ selected: selected,
+ link_state: link_state
+ ) == :no_history
+
+ LoadFileLinkState.new(LoadFile::REUSE, link_state)
+
+ elsif selected.type == BlockType::LINK
+ debounce_reset
+ execute_block_type_link_with_state(link_block_body: selected.body,
+ mdoc: mdoc,
+ selected: selected,
+ link_state: link_state,
+ block_source: block_source)
+
+ elsif selected.type == BlockType::LOAD
+ debounce_reset
+ code_lines = execute_block_type_load_code_lines(selected)
+ next_state_append_code(selected, link_state, code_lines)
+
+ elsif selected.type == BlockType::SAVE
+ debounce_reset
+
+ execute_block_type_save(
+ code_lines: link_state&.inherited_lines,
+ selected: selected
+ )
+
+ LoadFileLinkState.new(LoadFile::REUSE, link_state)
+
+ elsif selected.type == BlockType::VIEW
+ debounce_reset
+ vux_view_inherited(stream: $stderr)
+ return :break if pause_user_exit
+
+ LoadFileLinkState.new(LoadFile::REUSE, link_state)
+
+ # from CLI
+ elsif selected.nickname == @delegate_object[:menu_option_exit_name][:line]
+ debounce_reset
+ LoadFileLinkState.new(LoadFile::EXIT, link_state)
+
+ elsif @menu_user_clicked_back_link
+ debounce_reset
+ LoadFileLinkState.new(
+ LoadFile::LOAD,
+ pop_link_history_new_state
+ )
+
+ elsif selected.type == BlockType::OPTS
+ debounce_reset
+ code_lines = []
+ options_state = read_show_options_and_trigger_reuse(
+ link_state: link_state,
+ mdoc: @dml_mdoc,
+ selected: selected
+ )
+ update_menu_base(options_state.options)
+
+ link_state = LinkState.new
+ next_state_append_code(selected, link_state, code_lines)
+
+ elsif selected.type == BlockType::PORT
+ debounce_reset
+ required_lines = execute_block_type_port_code_lines(
+ mdoc: @dml_mdoc,
+ selected: selected,
+ link_state: link_state,
+ block_source: block_source
+ )
+ next_state_set_code(selected, link_state, required_lines)
+
+ elsif selected.type == BlockType::VARS
+ debounce_reset
+ next_state_append_code(selected, link_state,
+ code_from_vars_block_to_set_environment_variables(selected))
+
+ elsif debounce_allows
+ compile_execute_and_trigger_reuse(mdoc: mdoc,
+ selected: selected,
+ link_state: link_state,
+ block_source: block_source)
+ LoadFileLinkState.new(LoadFile::REUSE, link_state)
+
+ else
+ LoadFileLinkState.new(LoadFile::REUSE, link_state)
+ end
+ end
+
+ def execute_block_for_state_and_name(
+ selected:, mdoc:, link_state:, block_source: {}
+ )
lfls = execute_block_by_type_for_lfls(
selected: selected,
mdoc: mdoc,
link_state: link_state,
block_source: block_source
)
- # if the same menu is being displayed, collect the display name of the selected menu item for use as the default item
+ # if the same menu is being displayed, collect the display name
+ # of the selected menu item for use as the default item
[lfls.link_state,
lfls.load_file == LoadFile::LOAD ? nil : selected.dname,
# 2024-08-22 true to quit
lfls.load_file == LoadFile::EXIT]
end
@@ -1391,24 +1650,279 @@
selected: @dml_block_state.block,
mdoc: @dml_mdoc,
link_state: @dml_link_state,
block_source: {
document_filename: @delegate_object[:filename],
- time_now_date: Time.now.utc.strftime(@delegate_object[:shell_code_label_time_format])
+ time_now_date: Time.now.utc.strftime(
+ @delegate_object[:shell_code_label_time_format]
+ )
}
)
:break if quit
end
- # Executes a given command and processes its input, output, and error streams.
+ def execute_block_type_history_ux(
+ directory: @delegate_object[:document_configurations_directory],
+ filename: '*',
+ form: '%{line}',
+ link_state:,
+ regexp: "^(?<line>.*)$",
+ selected:
+ )
+ block_data = HashDelegator.parse_yaml_data_from_body(selected.body)
+ files_table_rows = read_saved_assets_for_history_table(
+ filename: filename,
+ form: form,
+ path: block_data['directory'] || directory,
+ regexp: regexp
+ )
+ return :no_history unless files_table_rows
+
+ execute_history_select(files_table_rows, stream: $stderr)
+ end
+
+ # Handles the processing of a link block in Markdown Execution.
+ # 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 [Array<String>] command the command to execute along with its arguments.
- # @yield [stdin, stdout, stderr, thread] if a block is provided, it yields input, output, error lines, and the execution thread.
+ # @param link_block_body [Array<String>]
+ # The body content as an array of strings.
+ # @param mdoc [Object] Markdown document object.
+ # @param selected [FCB] Selected code block.
+ # @return [LoadFileLinkState] Object indicating the next
+ # action for file loading.
+ def execute_block_type_link_with_state(
+ link_block_body: [], mdoc: nil, selected: FCB.new,
+ link_state: LinkState.new, block_source: {}
+ )
+ # !!p link_block_body selected
+ link_block_data = HashDelegator.parse_yaml_data_from_body(link_block_body)
+ # !!v link_block_data
+ ## collect blocks specified by block
+ #
+ if mdoc
+ code_info = mdoc.collect_recursively_required_code(
+ anyname: selected.pub_name,
+ label_format_above: @delegate_object[:shell_code_label_format_above],
+ label_format_below: @delegate_object[:shell_code_label_format_below],
+ block_source: block_source
+ )
+ code_lines = code_info[:code]
+ block_names = code_info[:block_names]
+ dependencies = code_info[:dependencies]
+ else
+ block_names = []
+ code_lines = []
+ dependencies = {}
+ end
+
+ # load key and values from link block into current environment
+ #
+ if link_block_data[LinkKeys::VARS]
+ code_lines.push BashCommentFormatter.format_comment(selected.pub_name)
+ (link_block_data[LinkKeys::VARS] || []).each do |(key, value)|
+ ENV[key] = value.to_s
+ code_lines.push(assign_key_value_in_bash(key, value))
+ end
+ end
+
+ ## append blocks loaded
+ #
+ if (load_expr = link_block_data.fetch(LinkKeys::LOAD, '')).present?
+ load_filespec = load_filespec_from_expression(load_expr)
+ if load_filespec
+ begin
+ code_lines += File.readlines(load_filespec,
+ chomp: true)
+ rescue Errno::ENOENT
+ report_error($ERROR_INFO)
+ end
+ end
+ end
+
+ # if an eval link block, evaluate code_lines and return its standard output
+ #
+ if link_block_data.fetch(LinkKeys::EVAL,
+ false) || link_block_data.fetch(LinkKeys::EXEC,
+ false)
+ code_lines = link_block_data_eval(
+ link_state, code_lines, selected, link_block_data,
+ block_source: block_source,
+ shell: @delegate_object[:block_type_default]
+ )
+ end
+
+ # config next state
+ #
+ next_document_filename = write_inherited_lines_to_file(link_state,
+ link_block_data)
+ next_block_name = link_block_data.fetch(
+ LinkKeys::NEXT_BLOCK,
+ nil
+ ) || link_block_data.fetch(LinkKeys::BLOCK, nil) || ''
+
+ if link_block_data[LinkKeys::RETURN]
+ pop_add_current_code_to_head_and_trigger_load(
+ link_state, block_names, code_lines,
+ dependencies, selected, next_block_name: next_block_name
+ )
+
+ else
+ next_keep_code = link_state&.keep_code || link_block_data.fetch('keep', false) #/*LinkKeys::KEEP*/
+ link_history_push_and_next(
+ curr_block_name: selected.pub_name,
+ 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
+ ),
+ keep_code: link_state&.keep_code,
+ next_block_name: next_block_name,
+ next_document_filename: next_document_filename,
+ next_keep_code: next_keep_code,
+ next_load_file: next_document_filename == @delegate_object[:filename] ? LoadFile::REUSE : LoadFile::LOAD
+ )
+ end
+ end
+
+ def execute_block_type_load_code_lines(
+ selected,
+ directory: @delegate_object[:document_configurations_directory],
+ filename_pattern: @delegate_object[:vars_block_filename_pattern],
+ glob: @delegate_object[:document_configurations_glob],
+ view: @delegate_object[:vars_block_filename_view]
+ )
+ # !!p selected
+ block_data = HashDelegator.parse_yaml_data_from_body(selected.body)
+ # !!v block_data
+ if selected_option = select_option_with_metadata(
+ prompt_title,
+ Dir.glob(
+ File.join(
+ Dir.pwd,
+ block_data['directory'] || directory,
+ block_data['glob'] || glob
+ )
+ ).sort.map do |file|
+ { name: format(
+ block_data['view'] || view,
+ NamedCaptureExtractor::extract_named_group2(
+ file.match(
+ Regexp.new(block_data['filename_pattern'] ||
+ filename_pattern)
+ )
+ )
+ ),
+ oname: file }
+ end,
+ simple_menu_options
+ )
+ File.readlines(selected_option.oname, chomp: true)
+ else
+ warn "No matching files found" ###
+ end
+ 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 execute_block_type_port_code_lines(mdoc:, selected:, block_source:,
+ link_state: LinkState.new)
+ required = mdoc.collect_recursively_required_code(
+ anyname: selected.pub_name,
+ label_format_above: @delegate_object[:shell_code_label_format_above],
+ label_format_below: @delegate_object[:shell_code_label_format_below],
+ block_source: block_source
+ ) # !!t 'required'
+ dependencies = (
+ link_state&.inherited_dependencies || {}
+ ).merge(required[:dependencies] || {})
+ 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(
+ dependencies, highlight: required[:unmet_dependencies]
+ )
+ runtime_exception(
+ :runtime_exception_error_level,
+ 'unmet_dependencies, flag: runtime_exception_error_level',
+ required[:unmet_dependencies]
+ )
+ elsif @delegate_object[:dump_dependencies]
+ warn format_and_highlight_dependencies(
+ dependencies,
+ highlight: [@delegate_object[:block_name]]
+ )
+ end
+
+ if selected[:type] == BlockType::OPTS
+ # body of blocks is returned as a list of lines to be read an YAML
+ HashDelegator.code_merge(required[:blocks].map(&:body).flatten(1))
+ else
+ code_lines = if selected.type == BlockType::VARS
+ code_from_vars_block_to_set_environment_variables(selected)
+ else
+ []
+ end
+ HashDelegator.code_merge(link_state&.inherited_lines,
+ required[:code] + code_lines)
+ end
+ end
+
+ def execute_block_type_save(code_lines:, selected:)
+ # !!p code_lines, selected
+ block_data = HashDelegator.parse_yaml_data_from_body(selected.body)
+ # !!v block_data
+ directory_glob = if block_data['directory']
+ # !!b
+ File.join(
+ block_data['directory'],
+ block_data['glob'] ||
+ @delegate_object[:document_saved_lines_glob].split('/').last
+ )
+ else
+ # !!b
+ @delegate_object[:document_saved_lines_glob]
+ end
+ # !!v directory_glob
+
+ save_filespec_from_expression(directory_glob).tap do |save_filespec|
+ if save_filespec
+ begin
+ File.write(save_filespec,
+ HashDelegator.join_code_lines(code_lines))
+ rescue Errno::ENOENT
+ report_error($ERROR_INFO)
+ end
+ end
+ end
+ end
+
+ # Executes a given command and processes its
+ # input, output, and error streams.
+ #
+ # @param [Array<String>] command the command to
+ # execute along with its arguments.
+ # @yield [stdin, stdout, stderr, thread] if a block is provided, it
+ # yields input, output, error lines, and the execution thread.
# @return [Integer] the exit status of the executed command (0 to 255).
#
# @example
- # status = execute_command_with_streams(['ls', '-la']) do |stdin, stdout, stderr, thread|
+ # status = execute_command_with_streams(['ls', '-la']) \
+ # do |stdin, stdout, stderr, thread|
# puts "STDOUT: #{stdout}" if stdout
# puts "STDERR: #{stderr}" if stderr
# end
# puts "Command exited with status: #{status}"
def execute_command_with_streams(command)
@@ -1458,12 +1972,12 @@
# repeat select+display until user exits
pause_now = false
row_attrib = :row
loop do
- if pause_now
- break if prompt_select_continue == MenuState::EXIT
+ if pause_now && (prompt_select_continue == MenuState::EXIT)
+ break
end
# menu with Back and Facet options at top
case (name = prompt_select_code_filename(
[exit_prompt,
@@ -1484,28 +1998,29 @@
"#{info[:size]} bytes"
stream.puts(
File.readlines(file.file,
chomp: false).map.with_index do |line, ind|
format(' %s. %s',
- AnsiString.new(format('% 4d', ind + 1)).send(:violet), line)
+ AnsiString.new(format('% 4d', ind + 1)).send(:violet),
+ line)
end
)
pause_now = pause_refresh
end
end
end
- def execute_inherited_save
- save_filespec = save_filespec_from_expression(
- document_name_in_glob_as_file_name(
- @dml_link_state.document_filename,
- @delegate_object[:document_saved_lines_glob]
- )
+ def execute_inherited_save(
+ code_lines: @dml_link_state.inherited_lines
+ )
+ return unless save_filespec = save_filespec_from_expression(
+ document_name_in_glob_as_file_name
)
- if save_filespec && !write_file_with_directory_creation(
- save_filespec,
- HashDelegator.join_code_lines(@dml_link_state.inherited_lines)
+
+ unless write_file_with_directory_creation(
+ content: HashDelegator.join_code_lines(code_lines),
+ filespec: save_filespec
)
:break
end
end
@@ -1524,106 +2039,42 @@
keep_code: keep_code
}
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
+ # 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: [], selected: FCB.new)
+ def execute_required_lines(
+ required_lines: [], selected: FCB.new, shell:
+ )
if @delegate_object[:save_executed_script]
write_command_file(required_lines: required_lines,
- selected: selected)
+ selected: selected,
+ shell: shell)
end
if @dml_block_state
calc_logged_stdout_filename(block_name: @dml_block_state.block.oname)
end
- format_and_execute_command(code_lines: required_lines)
+ format_and_execute_command(code_lines: required_lines, shell: shell)
post_execution_process
end
- # Execute a code block after approval and provide user interaction options.
+ # 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.
#
- # This method displays required code blocks, asks for user approval, and
- # executes the code block if approved. It also allows users to copy the
- # 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_block_by_type_for_lfls(selected:, mdoc:, block_source:,
- link_state: LinkState.new)
- if selected.shell == BlockType::LINK
- debounce_reset
- push_link_history_and_trigger_load(link_block_body: selected.body,
- mdoc: mdoc,
- selected: selected,
- link_state: link_state,
- block_source: block_source)
-
- # from CLI
- elsif selected.nickname == @delegate_object[:menu_option_exit_name][:line]
- debounce_reset
- LoadFileLinkState.new(LoadFile::EXIT, link_state)
-
- elsif @menu_user_clicked_back_link
- debounce_reset
- # pop_link_history_new_state
- LoadFileLinkState.new(
- LoadFile::LOAD,
- pop_link_history_new_state
- )
-
- elsif selected.shell == BlockType::OPTS
- debounce_reset
- code_lines = []
- options_state = read_show_options_and_trigger_reuse(
- link_state: link_state,
- mdoc: @dml_mdoc,
- selected: selected
- )
- update_menu_base(options_state.options)
-
- ### options_state.load_file_link_state
- link_state = LinkState.new
- next_state_append_code(selected, link_state, code_lines)
-
- elsif selected.shell == BlockType::PORT
- debounce_reset
- required_lines = collect_required_code_lines(
- mdoc: @dml_mdoc,
- selected: selected,
- link_state: link_state,
- block_source: block_source
- )
- next_state_set_code(selected, link_state, required_lines)
-
- elsif selected.shell == BlockType::VARS
- debounce_reset
- next_state_append_code(selected, link_state,
- set_environment_variables_for_block(selected))
-
- elsif debounce_allows
- compile_execute_and_trigger_reuse(mdoc: mdoc,
- selected: selected,
- link_state: link_state,
- block_source: block_source)
- LoadFileLinkState.new(LoadFile::REUSE, link_state)
-
- 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.
- #
- # @param default [String] The default value if the data symbol is not found.
- # @param data_sym [Symbol] The symbol key to fetch data from the delegate object.
- # @param color_sym [Symbol] The symbol key to fetch the color option for styling.
+ # @param default [String] The default value
+ # if the data symbol is not found.
+ # @param data_sym [Symbol] The symbol key to
+ # fetch data from the delegate object.
+ # @param color_sym [Symbol] The symbol key to
+ # fetch the color option for styling.
# @return [String] The color-styled string.
def fetch_color(default: '',
data_sym: :execution_report_preview_head,
color_sym: :execution_report_preview_frame_color)
data_string = @delegate_object.fetch(data_sym, default).to_s
@@ -1643,47 +2094,45 @@
end
{ size: file_size, lines: line_count }
end
- def format_and_execute_command(code_lines:)
+ def format_and_execute_command(code_lines:, shell:)
formatted_command = code_lines.flatten.join("\n")
@fout.fout fetch_color(data_sym: :script_execution_head,
color_sym: :script_execution_frame_color)
- command_execute(formatted_command, args: @pass_args)
+ command_execute(formatted_command, args: @pass_args, shell: shell)
@fout.fout fetch_color(data_sym: :script_execution_tail,
color_sym: :script_execution_frame_color)
end
# Format expression using environment variables and run state
def format_expression(expr)
data = link_load_format_data
- ENV.each { |key, value| data[key] = value }
+ ENV.each { |key, value| data[key.to_sym] = value }
format(expr, data)
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
-
- # Formats a string based on a given context and applies color styling to it.
- # It retrieves format and color information from the delegate object and processes accordingly.
+ # Formats a string based on a given context and
+ # applies color styling to it.
+ # It retrieves format and color information from
+ # the delegate object and processes accordingly.
#
- # @param default [String] The default value if the format symbol is not found (unused in current implementation).
+ # @param default [String] The default value if the format symbol
+ # is not found (unused in current implementation).
# @param context [Hash] Contextual data used for string formatting.
- # @param format_sym [Symbol] Symbol key to fetch the format string from the delegate object.
- # @param color_sym [Symbol] Symbol key to fetch the color option for string styling.
+ # @param format_sym [Symbol] Symbol key to fetch the format string
+ # from the delegate object.
+ # @param color_sym [Symbol] Symbol key to fetch the color option
+ # for string styling.
# @return [String] The formatted and color-styled string.
- def format_references_send_color(default: '', context: {},
- format_sym: :output_execution_label_format,
- color_sym: :execution_report_preview_frame_color)
+ def format_references_send_color(
+ color_sym: :execution_report_preview_frame_color,
+ context: {},
+ default: '',
+ format_sym: :output_execution_label_format
+ )
formatted_string = format(@delegate_object.fetch(format_sym, ''),
context).to_s
string_send_color(formatted_string, color_sym)
end
@@ -1721,44 +2170,16 @@
"#{SecureRandom.urlsafe_base64}#{ext}"
end
File.join(Dir.tmpdir, filename)
end
- # Processes a block to generate its summary, modifying its attributes based on various matching criteria.
- # It handles special formatting for bash blocks, extracting and setting properties like call, stdin, stdout, and dname.
- #
- # @param fcb [Object] An object representing a functional code block.
- # @return [Object] The modified functional code block with updated summary attributes.
- def get_block_summary(fcb)
- return fcb unless @delegate_object[:bash]
-
- fcb.call = fcb.title.match(Regexp.new(@delegate_object[:block_calls_scan]))&.fetch(1, nil)
- titlexcall = fcb.call ? fcb.title.sub("%#{fcb.call}", '') : fcb.title
- bm = extract_named_captures_from_option(titlexcall,
- @delegate_object[:block_name_match])
-
- shell_color_option = SHELL_COLOR_OPTIONS[fcb.shell]
-
- 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.indent
- )
-
- fcb # &br
- 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.
+ # 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.
+ # @param block_state [Object] An object representing the
+ # state of a block in the menu.
def handle_back_or_continue(block_state)
return if block_state.nil?
unless [MenuState::BACK,
MenuState::CONTINUE].include?(block_state.state)
return
@@ -1788,21 +2209,24 @@
@process_cv.signal
end
end
end
- def history_files(link_state, order: :chronological, direction: :reverse)
+ def history_files(
+ link_state,
+ direction: :reverse,
+ filename: nil,
+ home: Dir.pwd,
+ order: :chronological,
+ path: ''
+ )
+ # !!v filename, 'path', path
+ # !!v File.join(home, path, filename)
files = Dir.glob(
- File.join(
- @delegate_object[:saved_script_folder],
- SavedAsset.new(
- filename: @delegate_object[:filename],
- saved_asset_format: shell_escape_asset_format(link_state)
- ).generate_name
- )
+ File.join(home, path, filename)
)
-
+ # !!v files
sorted_files = case order
when :alphabetical
files.sort
when :chronological
files.sort_by { |file| File.mtime(file) }
@@ -1828,35 +2252,60 @@
in_fenced_block: false,
headings: []
}
end
+ public
+
# 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_types = yield :filter
- cfile.readlines(@delegate_object[:filename],
- import_paths: @delegate_object[:import_paths]&.split(':')).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_types,
&block)
end
end
end
+ def iter_source_blocks(source, &block)
+ # !!v source
+ case source
+ when 1
+ blocks_from_nested_files.each(&block)
+ when 2
+ @dml_blocks_in_file.each(&block)
+ when 3
+ @dml_menu_blocks.each(&block)
+ else
+ iter_blocks_from_nested_files do |btype, fcb|
+ case btype
+ when :blocks
+ yield fcb
+ when :filter
+ %i[blocks]
+ end
+ end
+ end
+ end
+
def link_block_data_eval(link_state, code_lines, selected, link_block_data,
- block_source:)
+ block_source:, shell:)
all_code = HashDelegator.code_merge(link_state&.inherited_lines,
code_lines)
output_lines = []
Tempfile.open do |file|
- cmd = "#{@delegate_object[:shell]} #{file.path}"
+ cmd = "#{shell} #{file.path}"
file.write(all_code.join("\n"))
file.rewind
if link_block_data.fetch(LinkKeys::EXEC, false)
@run_state.files = StreamsOut.new
@@ -1942,21 +2391,42 @@
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,
- started_at: Time.now.utc.strftime(@delegate_object[:execute_command_title_time_format])
+ started_at: Time.now.utc.strftime(
+ @delegate_object[:execute_command_title_time_format]
+ )
}
end
- # Loads auto blocks based on delegate object settings and updates if new filename is detected.
+ def list_blocks
+ # !!b
+ message = @delegate_object[:list_blocks_message]
+ block_eval = @delegate_object[:list_blocks_eval]
+ # !!v message block_eval
+
+ list = []
+ iter_source_blocks(@delegate_object[:list_blocks_type]) do |block|
+ # !!v block
+ list << (block_eval.present? ? eval(block_eval) : block.send(message))
+ end
+ list.compact!
+ # !!v list
+
+ @fout.fout_list(list)
+ 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_opts_block(all_blocks, mdoc:)
block_name = @delegate_object[:document_load_opts_block_name]
- unless block_name.present? && @most_recent_loaded_filename != @delegate_object[:filename]
+ unless block_name.present? &&
+ @most_recent_loaded_filename != @delegate_object[:filename]
return
end
block = HashDelegator.block_find(all_blocks, :oname, block_name)
return unless block
@@ -1971,28 +2441,32 @@
true
end
def load_cli_or_user_selected_block(all_blocks: [], menu_blocks: [],
default: nil)
+ # !!b
if @delegate_object[:block_name].present?
block = all_blocks.find do |item|
item.pub_name == @delegate_object[:block_name]
end
source = OpenStruct.new(block_name_from_ui: false)
else
block_state = wait_for_user_selected_block(all_blocks, menu_blocks,
default)
+ return if block_state.nil?
+
block = block_state.block
source = OpenStruct.new(block_name_from_ui: true)
state = block_state.state
end
SelectedBlockMenuState.new(block, source, state)
end
# format + glob + select for file in load block
- # name has references to ENV vars and doc and batch vars incl. timestamp
+ # name has references to ENV vars and doc and batch vars
+ # incl. timestamp
def load_filespec_from_expression(expression)
# Process expression with embedded formatting
expanded_expression = formatted_expression(expression)
# Handle wildcards or direct file specification
@@ -2027,12 +2501,13 @@
end
end
def manage_cli_selection_state(block_name_from_cli:, now_using_cli:,
link_state:)
- if block_name_from_cli && @cli_block_name == @menu_base_options[:menu_persist_block_name]
- # &bsp 'pause cli control, allow user to select block'
+ if block_name_from_cli &&
+ @cli_block_name == @menu_base_options[:menu_persist_block_name]
+ # !!b 'pause cli control, allow user to select block'
block_name_from_cli = false
now_using_cli = false
@menu_base_options[:block_name] =
@delegate_object[:block_name] = \
link_state.block_name =
@@ -2050,45 +2525,48 @@
@delegate_object.merge!(nopts)
end
[menu_blocks, mdoc]
end
- ## Handles the file loading and returns the blocks in the file and MDoc instance
+ ## Handles the file loading and returns the blocks
+ # in the file and MDoc instance
#
def mdoc_menu_and_blocks_from_nested_files(link_state)
all_blocks, mdoc = mdoc_and_blocks_from_nested_files
# recreate menu with new options
#
- all_blocks, mdoc = mdoc_and_blocks_from_nested_files if load_auto_opts_block(
- all_blocks, mdoc: mdoc
- )
+ if load_auto_opts_block(all_blocks, mdoc: mdoc)
+ all_blocks, mdoc = mdoc_and_blocks_from_nested_files
+ end
menu_blocks = mdoc.fcbs_per_options(@delegate_object)
+
+ variable_expansions!(menu_blocks: menu_blocks, link_state: link_state)
add_menu_chrome_blocks!(menu_blocks: menu_blocks, link_state: link_state)
+
### compress empty lines
HashDelegator.delete_consecutive_blank_lines!(menu_blocks)
HashDelegator.tables_into_columns!(menu_blocks, @delegate_object)
- [all_blocks, menu_blocks, mdoc] # &br
+ [all_blocks, menu_blocks, mdoc] # !!r
end
- def menu_add_disabled_option(name)
- raise unless name.present?
+ def menu_add_disabled_option(document_glob)
+ raise unless document_glob.present?
raise if @dml_menu_blocks.nil?
- block = @dml_menu_blocks.find { |item| item.oname == name }
+ block = @dml_menu_blocks.find { |item| item.oname == document_glob }
# create menu item when it is needed (count > 0)
#
return unless block.nil?
- # append_chrome_block(menu_blocks: @dml_menu_blocks, menu_state: MenuState::LOAD)
chrome_block = FCB.new(
chrome: true,
disabled: '',
dname: HashDelegator.new(@delegate_object).string_send_color(
- name, :menu_inherited_lines_color
+ document_glob, :menu_inherited_lines_color
),
oname: formatted_name
)
if insert_at_top
@@ -2096,23 +2574,27 @@
else
@dml_menu_blocks.push(chrome_block)
end
end
- # Formats and optionally colors a menu option based on delegate object's configuration.
- # @param option_symbol [Symbol] The symbol key for the menu option in the delegate object.
- # @return [String] The formatted and possibly colored value of the menu option.
+ # Formats and optionally colors a menu option based on delegate
+ # object's configuration.
+ # @param option_symbol [Symbol] The symbol key for the menu option
+ # in the delegate object.
+ # @return [String] The formatted and possibly colored value
+ # of the menu option.
def menu_chrome_colored_option(option_symbol = :menu_option_back_name)
formatted_option = menu_chrome_formatted_option(option_symbol)
return formatted_option unless @delegate_object[:menu_chrome_color]
string_send_color(formatted_option, :menu_chrome_color)
end
# Formats a menu option based on the delegate object's configuration.
# It safely evaluates the value of the option and optionally formats it.
- # @param option_symbol [Symbol] The symbol key for the menu option in the delegate object.
+ # @param option_symbol [Symbol] The symbol key for the menu option in
+ # the delegate object.
# @return [String] The formatted or original value of the menu option.
def menu_chrome_formatted_option(option_symbol = :menu_option_back_name)
option_value = HashDelegator.safeval(@delegate_object.fetch(
option_symbol, ''
))
@@ -2135,23 +2617,27 @@
# super
end
end
def next_state_append_code(selected, link_state, code_lines)
- next_state_set_code(selected, link_state, HashDelegator.code_merge(
- link_state&.inherited_lines, code_lines
- ))
+ next_state_set_code(
+ selected,
+ link_state,
+ HashDelegator.code_merge(link_state&.inherited_lines, code_lines)
+ )
end
def next_state_set_code(selected, link_state, code_lines)
block_names = []
dependencies = {}
link_history_push_and_next(
curr_block_name: selected.pub_name,
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_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(code_lines),
keep_code: link_state&.keep_code,
next_block_name: '',
next_document_filename: @delegate_object[:filename],
next_keep_code: false,
@@ -2181,24 +2667,28 @@
}
end
def output_labeled_value(label, value, level)
@fout.lout format_references_send_color(
- context: { name: string_send_color(label, :output_execution_label_name_color),
- value: string_send_color(value.to_s,
- :output_execution_label_value_color) },
+ context: {
+ name: string_send_color(label, :output_execution_label_name_color),
+ value: string_send_color(value.to_s,
+ :output_execution_label_value_color)
+ },
format_sym: :output_execution_label_format
), level: level
end
def pause_user_exit
@delegate_object[:pause_after_script_execution] &&
prompt_select_continue == MenuState::EXIT
end
- def pop_add_current_code_to_head_and_trigger_load(link_state, block_names, code_lines,
- dependencies, selected, next_block_name: nil)
+ def pop_add_current_code_to_head_and_trigger_load(
+ link_state, block_names, code_lines,
+ dependencies, selected, next_block_name: nil
+ )
pop = @link_history.pop # updatable
if pop.document_filename
next_state = LinkState.new(
block_name: pop.block_name,
document_filename: pop.document_filename,
@@ -2212,11 +2702,12 @@
@link_history.push(next_state)
next_state.block_name = nil
LoadFileLinkState.new(LoadFile::LOAD, next_state)
else
- # no history exists; must have been called independently => retain script
+ # no history exists; must have been called independently
+ # => retain script
link_history_push_and_next(
curr_block_name: selected.pub_name,
curr_document_filename: @delegate_object[:filename],
inherited_block_names:
((link_state&.inherited_block_names || []) + block_names).sort.uniq,
@@ -2235,11 +2726,12 @@
end
# This method handles the back-link operation in the Markdown execution context.
# It updates the history state for the next block.
#
- # @return [LinkState] An object indicating the state for the next block.
+ # @return [LinkState] An object indicating the state for
+ # the next block.
def pop_link_history_new_state
pop = @link_history.pop
peek = @link_history.peek
LinkState.new(
document_filename: pop.document_filename,
@@ -2254,10 +2746,11 @@
output_execution_summary
fout_execution_report if @delegate_object[:output_execution_report]
end
# Prepare the blocks menu by adding labels and other necessary details.
+ # Remove filtered blocks.
#
# @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)
@@ -2267,19 +2760,19 @@
fcb,
%i[block_name_include_match block_name_wrapper_match]
)
fcb.name = fcb.dname
- fcb.label = BlockLabel.make(
- body: fcb.body,
- filename: @delegate_object[:filename],
- headings: fcb.headings,
- menu_blocks_with_docname: @delegate_object[:menu_blocks_with_docname],
- menu_blocks_with_headings: @delegate_object[:menu_blocks_with_headings],
- text: fcb.text,
- title: fcb.title
- )
+ # fcb.label = BlockLabel.make(
+ # body: fcb.body,
+ # filename: @delegate_object[:filename],
+ # headings: fcb.headings,
+ # menu_blocks_with_docname: @delegate_object[:menu_blocks_with_docname],
+ # menu_blocks_with_headings: @delegate_object[:menu_blocks_with_headings],
+ # text: fcb.text,
+ # title: fcb.title
+ # )
fcb.to_h
end.compact
end
def print_formatted_option(key, value)
@@ -2288,23 +2781,10 @@
print string_send_color(formatted_str, :menu_opts_set_color)
end
# private
- def process_block_based_on_type(blocks, btype, fcb)
- case btype
- when :blocks
- blocks.push(get_block_summary(fcb))
- when :filter
- %i[blocks line]
- when :line
- unless @delegate_object[:no_chrome]
- create_and_add_chrome_blocks(blocks, fcb)
- end
- end
- end
-
def process_string_array(arr, begin_pattern: nil, end_pattern: nil, scan1: nil,
format1: nil)
in_block = !begin_pattern.present?
collected_lines = []
@@ -2312,11 +2792,11 @@
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)
+ caps = NamedCaptureExtractor::extract_named_groups(line, scan1)
if caps
formatted = format(format1, caps)
collected_lines << formatted
end
else
@@ -2362,52 +2842,64 @@
gets.chomp
rescue Interrupt
nil
end
- # Prompts the user to enter a path or name to substitute into the wildcard expression.
- # If interrupted by the user (e.g., pressing Ctrl-C), it returns nil.
+ # Prompts the user to enter a path or name to substitute
+ # into the wildcard expression.
+ # If interrupted by the user (e.g., pressing Ctrl-C), it
+ # returns nil.
#
- # @param filespec [String] the wildcard expression to be substituted
- # @return [String, nil] the resolved path or substituted expression, or nil if interrupted
+ # @param filespec [String] the wildcard expression to be
+ # substituted
+ # @return [String, nil] the resolved path or substituted
+ # expression, or nil if interrupted
def prompt_for_filespec_with_wildcard(filespec)
puts format(@delegate_object[:prompt_show_expr_format],
{ expr: filespec })
puts @delegate_object[:prompt_enter_filespec]
begin
- input = gets.chomp
+ input = $stdin.gets.chomp
PathUtils.resolve_path_or_substitute(input, filespec)
rescue Interrupt
puts "\nOperation interrupted. Returning nil."
nil
end
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.
+ # 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.
- # @param required_lines [Array<String>] Lines of text or code that are subject to user approval.
+ # @param required_lines [Array<String>] Lines of text or
+ # code that are subject to user approval.
#
- # @option opts [String] :prompt_approve_block Prompt text for the approval menu.
- # @option opts [String] :prompt_yes Text for the 'Yes' choice in the menu.
- # @option opts [String] :prompt_no Text for the 'No' choice in the menu.
- # @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.
+ # @option opts [String] :prompt_approve_block
+ # Prompt text for the approval menu.
+ # @option opts [String] :prompt_yes
+ # Text for the 'Yes' choice in the menu.
+ # @option opts [String] :prompt_no
+ # Text for the 'No' choice in the menu.
+ # @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.
+ # @return [Boolean] Returns true if the user approves (selects 'Yes'),
+ # false otherwise.
##
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
) do |menu|
- # sel = @prompt.select(@delegate_object[:prompt_approve_block], filter: true) do |menu|
menu.default MenuOptions::YES
menu.choice @delegate_object[:prompt_yes], MenuOptions::YES
menu.choice @delegate_object[:prompt_no], MenuOptions::NO
menu.choice @delegate_object[:prompt_script_to_clipboard],
MenuOptions::SCRIPT_TO_CLIPBOARD
@@ -2416,11 +2908,14 @@
end
if sel == MenuOptions::SCRIPT_TO_CLIPBOARD
copy_to_clipboard(required_lines)
elsif sel == MenuOptions::SAVE_SCRIPT
- save_to_file(required_lines: required_lines, selected: selected)
+ save_to_file(
+ required_lines: required_lines, selected: selected,
+ shell: selected.shell
+ )
end
sel == MenuOptions::YES
end
@@ -2466,100 +2961,38 @@
end
# user prompt to exit if the menu will be displayed again
#
def prompt_user_exit(block_name_from_cli:, selected:)
- selected.shell == BlockType::BASH &&
+ selected.type == BlockType::SHELL &&
@delegate_object[:pause_after_script_execution] &&
prompt_select_continue == MenuState::EXIT
end
- # Handles the processing of a link block in Markdown Execution.
- # 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 link_block_body [Array<String>] The body content as an array of strings.
- # @param mdoc [Object] Markdown document object.
- # @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: nil, selected: FCB.new,
- link_state: LinkState.new, block_source: {})
- link_block_data = HashDelegator.parse_yaml_data_from_body(link_block_body)
+ def publish_for_external_automation(message:)
+ return if @delegate_object[:publish_document_file_name].empty?
- ## collect blocks specified by block
- #
- if mdoc
- code_info = mdoc.collect_recursively_required_code(
- anyname: selected.pub_name,
- label_format_above: @delegate_object[:shell_code_label_format_above],
- label_format_below: @delegate_object[:shell_code_label_format_below],
- block_source: block_source
- )
- code_lines = code_info[:code]
- block_names = code_info[:block_names]
- dependencies = code_info[:dependencies]
- else
- block_names = []
- code_lines = []
- dependencies = {}
- end
+ pipe_path = absolute_path(@delegate_object[:publish_document_file_name])
- # load key and values from link block into current environment
- #
- if link_block_data[LinkKeys::VARS]
- code_lines.push BashCommentFormatter.format_comment(selected.pub_name)
- (link_block_data[LinkKeys::VARS] || []).each do |(key, value)|
- ENV[key] = value.to_s
- code_lines.push(assign_key_value_in_bash(key, value))
+ case @delegate_object[:publish_document_file_mode]
+ when 'append'
+ File.write(pipe_path, message + "\n", mode: 'a')
+ when 'fifo'
+ unless @vux_pipe_open
+ unless File.exist?(pipe_path)
+ FileUtils.mkfifo(pipe_path)
+ @vux_pipe_created = pipe_path
+ end
+ @vux_pipe_open = File.open(pipe_path, 'w')
end
- end
-
- ## append blocks loaded
- #
- if (load_expr = link_block_data.fetch(LinkKeys::LOAD, '')).present?
- load_filespec = load_filespec_from_expression(load_expr)
- if load_filespec
- code_lines += File.readlines(load_filespec,
- chomp: true)
- end
- end
-
- # if an eval link block, evaluate code_lines and return its standard output
- #
- if link_block_data.fetch(LinkKeys::EVAL,
- false) || link_block_data.fetch(LinkKeys::EXEC,
- false)
- code_lines = link_block_data_eval(link_state, code_lines, selected, link_block_data,
- block_source: block_source)
- end
-
- next_document_filename = write_inherited_lines_to_file(link_state,
- link_block_data)
- next_block_name = link_block_data.fetch(LinkKeys::NEXT_BLOCK,
- nil) || link_block_data.fetch(LinkKeys::BLOCK,
- nil) || ''
-
- if link_block_data[LinkKeys::RETURN]
- pop_add_current_code_to_head_and_trigger_load(link_state, block_names, code_lines,
- dependencies, selected, next_block_name: next_block_name)
-
+ @vux_pipe_open.puts(message + "\n")
+ @vux_pipe_open.flush
+ when 'write'
+ File.write(pipe_path, message)
else
- next_keep_code = link_state&.keep_code || link_block_data.fetch('keep', false) #/*LinkKeys::KEEP*/
- link_history_push_and_next(
- curr_block_name: selected.pub_name,
- 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
- ),
- keep_code: link_state&.keep_code,
- next_block_name: next_block_name,
- next_document_filename: next_document_filename,
- next_keep_code: next_keep_code,
- next_load_file: next_document_filename == @delegate_object[:filename] ? LoadFile::REUSE : LoadFile::LOAD
- )
+ raise 'Invalid publish_document_file_mode:' \
+ " #{@delegate_object[:publish_document_file_mode]}"
end
end
# Handle expression with wildcard characters
# allow user to select or enter
@@ -2568,55 +3001,79 @@
{ expr: filespec })
puts @delegate_object[:prompt_enter_filespec]
gets.chomp
end
- def read_saved_assets_for_history_table
- files = history_files(@dml_link_state).sort
- files.map do |file|
- if Regexp.new(@delegate_object[:saved_asset_match]) =~ file
- begin
- OpenStruct.new(
- file: file,
- row: format(
- @delegate_object[:saved_history_format],
- # create with default '*' so unknown parameters are given a wildcard
- $~.names.each_with_object(Hash.new('*')) do |name, hash|
- hash[name.to_sym] = $~[name]
- end
- )
- )
- rescue KeyError
- # pp $!, $@
- warn "Cannot format with: #{@delegate_object[:saved_history_format]}"
- error_handler('saved_history_format')
- return nil
- end
- else
+ def read_saved_assets_for_history_table(
+ asset: nil,
+ filename: nil,
+ form: @delegate_object[:saved_history_format],
+ path: @delegate_object[:saved_script_folder],
+ regexp: @delegate_object[:saved_asset_match]
+ )
+ history_files(
+ @dml_link_state,
+ filename:
+ asset.present? ? saved_asset_filename(asset,
+ @dml_link_state) : filename,
+ path: path
+ )&.map do |file|
+ unless Regexp.new(regexp) =~ file
warn "Cannot parse name: #{file}"
next
end
+
+ saved_asset = saved_asset_for_history(
+ file: file, form: form,
+ match_info: $LAST_MATCH_INFO
+ )
+ saved_asset == :break ? nil : saved_asset
end&.compact
end
- # Processes YAML data from the selected menu item, updating delegate objects and optionally printing formatted output.
+ def saved_asset_for_history(
+ file:, form:, match_info:
+ )
+ begin
+ OpenStruct.new(
+ file: file[(Dir.pwd.length + 1)..-1],
+ full: file,
+ row: format(
+ form,
+ # default '*' so unknown parameters are given a wildcard
+ match_info.names.each_with_object(Hash.new('*')) do |name, hash|
+ hash[name.to_sym] = match_info[name]
+ end
+ )
+ )
+ rescue KeyError
+ # pp $!, $@
+ warn "Cannot format with: #{@delegate_object[:saved_history_format]}"
+ error_handler('saved_history_format')
+ return :break
+ end
+ 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 [LoadFileLinkState] An instance indicating the next action for loading files.
+ # @return [LoadFileLinkState] An instance indicating the
+ # next action for loading files.
def read_show_options_and_trigger_reuse(selected:,
mdoc:, link_state: LinkState.new)
obj = {}
# concatenated body of all required blocks loaded a YAML
data = (YAML.load(
- collect_required_code_lines(
+ execute_block_type_port_code_lines(
mdoc: mdoc, selected: selected,
link_state: link_state, block_source: {}
).join("\n")
) || {}).transform_keys(&:to_sym)
- if selected.shell == BlockType::OPTS
+ if selected.type == BlockType::OPTS
obj = data
else
(data || []).each do |key, value|
sym_key = key.to_sym
obj[sym_key] = value
@@ -2636,23 +3093,37 @@
# Registers console attributes by modifying the options hash.
# This method handles terminal resizing and adjusts the console dimensions
# and pagination settings based on the current terminal size.
#
- # @param opts [Hash] a hash containing various options for the console settings.
- # - :console_width [Integer, nil] The width of the console. If not provided or if the terminal is resized, it will be set to the current console width.
- # - :console_height [Integer, nil] The height of the console. If not provided or if the terminal is resized, it will be set to the current console height.
- # - :console_winsize [Array<Integer>, nil] The dimensions of the console [height, width]. If not provided or if the terminal is resized, it will be set to the current console dimensions.
- # - :select_page_height [Integer, nil] The height of the page for selection. If not provided or if not positive, it will be set to the maximum of (console height - 3) or 4.
- # - :per_page [Integer, nil] The number of items per page. If :select_page_height is not provided or if not positive, it will be set to the maximum of (console height - 3) or 4.
+ # @param opts [Hash] a hash containing various options
+ # for the console settings.
+ # - :console_width [Integer, nil] The width of the console. If not
+ # provided or if the terminal is resized, it will be set to the
+ # current console width.
+ # - :console_height [Integer, nil] The height of the console.
+ # If not provided or if the terminal is resized, it will be set
+ # to the current console height.
+ # - :console_winsize [Array<Integer>, nil] The dimensions of the
+ # console [height, width]. If not provided or if the terminal
+ # is resized, it will be set to the current console dimensions.
+ # - :select_page_height [Integer, nil] The height of the page for
+ # selection. If not provided or if not positive, it will be set
+ # to the maximum of (console height - 3) or 4.
+ # - :per_page [Integer, nil] The number of items per page. If
+ # :select_page_height is not provided or if not positive, it
+ # will be set to the maximum of (console height - 3) or 4.
#
- # @raise [StandardError] If an error occurs during the process, it will be caught and handled by calling HashDelegator.error_handler with 'register_console_attributes' and { abort: true }.
+ # @raise [StandardError] If an error occurs during the process, it
+ # will be caught and handled by calling HashDelegator.error_handler
+ # with 'register_console_attributes' and { abort: true }.
#
# @example
# opts = { console_width: nil, console_height: nil, select_page_height: nil }
# register_console_attributes(opts)
- # # opts will be updated with the current console dimensions and pagination settings.
+ # # opts will be updated with the current console dimensions
+ # # and pagination settings.
def register_console_attributes(opts)
if (resized = @delegate_object[:menu_resize_terminal])
resize_terminal
end
@@ -2669,44 +3140,56 @@
rescue StandardError
HashDelegator.error_handler('register_console_attributes',
{ abort: true })
end
+ def report_error(err)
+ # Handle ENOENT error
+ @run_state.aborted_at = Time.now.utc
+ @run_state.error_message = err.message
+ @run_state.error = err
+ @run_state.files.append_stream_line(ExecutionStreams::STD_ERR,
+ @run_state.error_message)
+ @fout.fout err.inspect
+ end
+
# Check if the delegate object responds to a given method.
# @param method_name [Symbol] The name of the method to check.
- # @param include_private [Boolean] Whether to include private methods in the check.
- # @return [Boolean] true if the delegate object responds to the method, false otherwise.
+ # @param include_private [Boolean]
+ # Whether to include private methods in the check.
+ # @return [Boolean] true if the delegate object responds
+ # to the method, false otherwise.
def respond_to?(method_name, include_private = false)
if super
true
elsif @delegate_object.respond_to?(method_name, include_private)
true
- elsif method_name.to_s.end_with?('=') && @delegate_object.respond_to?(:[]=,
- include_private)
+ elsif method_name.to_s.end_with?('=') &&
+ @delegate_object.respond_to?(:[]=, include_private)
true
else
@delegate_object.respond_to?(method_name, include_private)
end
end
def runtime_exception(exception_sym, name, items)
if @delegate_object[exception_sym] != 0
data = { name: name, detail: items.join(', ') }
warn(
- AnsiString.new(format(
- @delegate_object.fetch(:exception_format_name,
- "\n%{name}"),
- data
- )).send(@delegate_object.fetch(:exception_color_name,
- :red)) +
- AnsiString.new(format(
- @delegate_object.fetch(:exception_format_detail,
- " - %{detail}\n"),
- data
- )).send(@delegate_object.fetch(
- :exception_color_detail, :yellow
- ))
+ AnsiString.new(
+ format(
+ @delegate_object.fetch(:exception_format_name, "\n%{name}"),
+ data
+ )
+ ).send(@delegate_object.fetch(:exception_color_name, :red)) +
+ AnsiString.new(
+ format(
+ @delegate_object.fetch(:exception_format_detail,
+ " - %{detail}\n"),
+ data
+ )
+ ).send(@delegate_object.fetch(:exception_color_detail, :yellow))
)
end
return unless (@delegate_object[exception_sym]).positive?
exit @delegate_object[exception_sym]
@@ -2750,15 +3233,28 @@
name
end
end
end
- def save_to_file(required_lines:, selected:)
- write_command_file(required_lines: required_lines, selected: selected)
+ def save_to_file(required_lines:, selected:, shell:)
+ write_command_file(
+ required_lines: required_lines, selected: selected, shell: shell
+ )
@fout.fout "File saved: #{@run_state.saved_filespec}"
end
+ def saved_asset_filename(filename, link_state = LinkState.new)
+ SavedAsset.new(
+ filename: filename,
+ saved_asset_format:
+ shell_escape_asset_format(
+ code_lines: link_state&.inherited_lines,
+ shell: shell
+ )
+ ).generate_name
+ end
+
def select_document_if_multiple(options, files, prompt:)
# binding.irb
return files if files.class == String ###
return files[0] if (count = files.count) == 1
@@ -2773,20 +3269,28 @@
files,
opts.merge(per_page: opts[:select_page_height])
)
end
- # Presents a TTY prompt to select an option or exit, returns metadata including option and selected
+ # Presents a TTY prompt to select an option or exit,
+ # returns metadata including option and selected
def select_option_with_metadata(prompt_text, menu_items, opts = {})
+ # !!v prompt_text menu_items
## configure to environment
#
register_console_attributes(opts)
# crashes if all menu options are disabled
- selection = @prompt.select(prompt_text,
- menu_items,
- opts.merge(filter: true))
+ begin
+ selection = @prompt.select(prompt_text,
+ menu_items,
+ opts.merge(filter: true))
+ # !!v selection
+ rescue NoMethodError
+ # no enabled options in page
+ return
+ end
selected = menu_items.find do |item|
if item.instance_of?(Hash)
(item[:name] || item[:dname]) == selection
elsif item.instance_of?(MarkdownExec::FCB)
@@ -2806,140 +3310,184 @@
exit 1
end
if selection == menu_chrome_colored_option(:menu_option_back_name)
selected.option = selection
- selected.shell = BlockType::LINK
+ selected.type = BlockType::LINK
elsif selection == menu_chrome_colored_option(:menu_option_exit_name)
selected.option = selection
else
selected.selected = selection
end
selected
end
- def set_environment_variables_for_block(selected)
- code_lines = []
- YAML.load(selected.body.join("\n"))&.each do |key, value|
- ENV[key] = value.to_s
+ def shell
+ @delegate_object[:shell]
+ end
- require 'shellwords'
- code_lines.push "#{key}=\"#{Shellwords.escape(value)}\""
-
- next unless @delegate_object[:menu_vars_set_format].present?
-
- formatted_string = format(@delegate_object[:menu_vars_set_format],
- { key: key, value: value })
- print string_send_color(formatted_string, :menu_vars_set_color)
- end
- code_lines
+ def shell=(value)
+ @delegate_object[:shell] = value
end
- def shell_escape_asset_format(link_state)
- raw = @delegate_object[:saved_asset_format]
+ def shell_escape_asset_format(
+ code_lines:,
+ enable: @delegate_object[:shell_parameter_expansion],
+ raw: @delegate_object[:saved_asset_format],
+ shell:
+ )
+ return raw unless enable
- return raw unless @delegate_object[:shell_parameter_expansion]
-
# unchanged if no parameter expansion takes place
return raw unless /$/ =~ raw
filespec = generate_temp_filename
- cmd = [@delegate_object[:shell], '-c', filespec].join(' ')
+ cmd = [shell, '-c', filespec].join(' ')
marker = Random.new.rand.to_s
- code = (link_state&.inherited_lines || []) + ["echo -n \"#{marker}#{raw}\""]
- # &bt code
+ code = (code_lines || []) + ["echo -n \"#{marker}#{raw}\""]
+ # !!t code
File.write filespec, HashDelegator.join_code_lines(code)
File.chmod 0o755, filespec
out = `#{cmd}`.sub(/.*?#{marker}/m, '')
File.delete filespec
- out # &br
+ out # !!r
end
- def should_add_back_option?
- @delegate_object[:menu_with_back] && @link_history.prior_state_exist?
+ def should_add_back_option?(
+ menu_with_back: @delegate_object[:menu_with_back]
+ )
+ 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.
+ def simple_menu_options(
+ per_page: @delegate_object[:select_page_height]
+ )
+ { cycle: true,
+ per_page: per_page }
+ 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.
- # @param fenced_start_extended_regex [Regexp] Regular expression to identify fenced block start.
+ # @param fenced_start_extended_regex [Regexp]
+ # Regular expression to identify fenced block start.
# @return [MarkdownExec::FCB] A new FCB instance with the parsed attributes.
def start_fenced_block(line, headings, fenced_start_extended_regex)
- fcb_title_groups = line.match(fenced_start_extended_regex).named_captures.sym_keys
+ fcb_title_groups = NamedCaptureExtractor::extract_named_groups(
+ line, fenced_start_extended_regex
+ )
+
rest = fcb_title_groups.fetch(:rest, '')
reqs, wraps =
ArrayUtil.partition_by_predicate(rest.scan(/\+[^\s]+/).map do |req|
req[1..-1]
end) do |name|
!name.match(Regexp.new(@delegate_object[:block_name_wrapper_match]))
end
+ # adjust captured type
+ if fcb_title_groups[:type].present?
+ case fcb_title_groups[:type]
+ when *ShellType::ALL
+ # convert type to shell
+ fcb_title_groups[:shell] = fcb_title_groups[:type]
+ fcb_title_groups[:type] = BlockType::SHELL
+ end
+ else
+ # treat as the default shell
+ fcb_title_groups[:shell] = @delegate_object[:block_type_default]
+ fcb_title_groups[:type] = BlockType::SHELL
+ end
+
dname = oname = title = ''
nickname = nil
- if @delegate_object[:block_name_nick_match].present? && oname =~ Regexp.new(@delegate_object[:block_name_nick_match])
+ 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
# disable fcb for data blocks
- disabled = fcb_title_groups.fetch(:shell, '') == 'yaml' ? '' : nil
+ disabled = if fcb_title_groups.fetch(:type, '') == BlockType::YAML
+ TtyMenu::DISABLE
+ else
+ nil
+ end
MarkdownExec::FCB.new(
body: [],
- call: rest.match(Regexp.new(@delegate_object[:block_calls_scan]))&.to_a&.first,
+ call: rest.match(
+ Regexp.new(@delegate_object[:block_calls_scan])
+ )&.to_a&.first,
disabled: disabled,
dname: dname,
headings: headings,
indent: fcb_title_groups.fetch(:indent, ''),
nickname: nickname,
oname: oname,
reqs: reqs,
shell: fcb_title_groups.fetch(:shell, ''),
+ start_line: line,
stdin: if (tn = rest.match(/<(?<type>\$)?(?<name>[A-Za-z_-]\S+)/))
- tn.named_captures.sym_keys
+ NamedCaptureExtractor::extract_named_group2(tn)
end,
stdout: if (tn = rest.match(/>(?<type>\$)?(?<name>[\w.\-]+)/))
- tn.named_captures.sym_keys
+ NamedCaptureExtractor::extract_named_group2(tn)
end,
title: title,
+ type: fcb_title_groups.fetch(:type, ''),
wraps: wraps
)
end
# Applies a color method to a string based on the provided color symbol.
# The color method is fetched from @delegate_object and applied to the string.
# @param string [String] The string to which the color will be applied.
# @param color_sym [Symbol] The symbol representing the color method.
- # @param default [String] Default color method to use if color_sym is not found in @delegate_object.
+ # @param default [String] Default color method to use if
+ # color_sym is not found in @delegate_object.
# @return [String] The string with the applied color method.
def string_send_color(string, color_sym)
HashDelegator.apply_color_from_hash(string, @delegate_object, color_sym)
end
##
- # Processes an individual line within a loop, updating headings and handling fenced code blocks.
- # This function is designed to be called within a loop that iterates through each line of a document.
+ # Processes an individual line within a loop, updating headings
+ # and handling fenced code blocks.
+ # This function is designed to be called within a loop that iterates
+ # through each line of a document.
#
# @param line [String] The current line being processed.
- # @param state [Hash] The current state of the parser, including flags and data related to the processing.
- # @param opts [Hash] A hash containing various options for line and block processing.
- # @param selected_types [Array<String>] Accumulator for lines or messages that are subject to further processing.
- # @param block [Proc] An optional block for further processing or transformation of lines.
+ # @param state [Hash] The current state of the parser, including flags
+ # and data related to the processing.
+ # @param opts [Hash] A hash containing various options for line
+ # and block processing.
+ # @param selected_types [Array<String>] Accumulator for lines
+ # or messages that are subject to further processing.
+ # @param block [Proc] An optional block for further processing
+ # or transformation of lines.
#
- # @option state [Array<String>] :headings Current headings to be updated based on the line.
- # @option state [Regexp] :fenced_start_and_end_regex Regular expression to match the start and end of a fenced block.
- # @option state [Boolean] :in_fenced_block Flag indicating whether the current line is inside a fenced block.
- # @option state [Object] :fcb An object representing the current fenced code block being processed.
+ # @option state [Array<String>] :headings Current headings
+ # to be updated based on the line.
+ # @option state [Regexp] :fenced_start_and_end_regex Regular expression
+ # to match the start and end of a fenced block.
+ # @option state [Boolean] :in_fenced_block Flag indicating whether
+ # the current line is inside a fenced block.
+ # @option state [Object] :fcb An object representing
+ # the current fenced code block being processed.
#
- # @option opts [Boolean] :menu_blocks_with_headings Flag indicating whether to update headings while processing.
+ # @option opts [Boolean] :menu_blocks_with_headings Flag
+ # indicating whether to update headings while processing.
#
- # @return [Void] The function modifies the `state` and `selected_types` arguments in place.
+ # @return [Void] The function modifies the `state`
+ # and `selected_types` arguments in place.
##
def update_line_and_block_state(nested_line, state, selected_types,
&block)
line = nested_line.to_s
if line.match(@delegate_object[:fenced_start_and_end_regex])
@@ -2954,30 +3502,33 @@
)
state[:in_fenced_block] = false
else
## start of code block
#
- state[:fcb] =
- start_fenced_block(line, state[:headings],
- @delegate_object[:fenced_start_extended_regex])
+ state[:fcb] = start_fenced_block(
+ line, state[:headings],
+ @delegate_object[:fenced_start_extended_regex]
+ )
state[:fcb][:depth] = nested_line[:depth]
+ state[:fcb][:indention] = nested_line[:indention]
state[:in_fenced_block] = true
end
elsif state[:in_fenced_block] && state[:fcb].body
## add line to fenced code block
# remove fcb indent if possible
#
state[:fcb].body += [
line.chomp.sub(/^#{state[:fcb].indent}/, '')
]
- elsif nested_line[:depth].zero? || @delegate_object[:menu_include_imported_notes]
+ elsif nested_line[:depth].zero? ||
+ @delegate_object[:menu_include_imported_notes]
# add line if it is depth 0 or option allows it
#
HashDelegator.yield_line_if_selected(line, selected_types, &block)
else
- # &bsp 'line is not recognized for block state'
+ # !!b 'line is not recognized for block state'
end
end
## apply options to current state
@@ -2987,18 +3538,22 @@
@menu_base_options&.merge!(options)
@delegate_object.merge!(options)
end
def vux_await_user_selection
- @dml_block_state = load_cli_or_user_selected_block(all_blocks: @dml_blocks_in_file,
- menu_blocks: @dml_menu_blocks,
- default: @dml_menu_default_dname)
- # &bsp '@run_state.source.block_name_from_cli:',@run_state.source.block_name_from_cli
+ @dml_block_state = load_cli_or_user_selected_block(
+ all_blocks: @dml_blocks_in_file,
+ menu_blocks: @dml_menu_blocks,
+ default: @dml_menu_default_dname
+ )
+ # !!b '@run_state.source.block_name_from_cli:',@run_state.source.block_name_from_cli
if !@dml_block_state
- HashDelegator.error_handler('block_state missing', { abort: true })
+ # HashDelegator.error_handler('block_state missing', { abort: true })
+ # document has no enabled items
+ :break
elsif @dml_block_state.state == MenuState::EXIT
- # &bsp 'load_cli_or_user_selected_block -> break'
+ # !!b 'load_cli_or_user_selected_block -> break'
:break
end
end
def vux_clear_menu_state
@@ -3011,11 +3566,12 @@
@dml_link_state.inherited_lines = edited.split("\n") if edited
end
def vux_execute_and_prompt(block_name)
@dml_block_state = block_state_for_name_from_cli(block_name)
- if @dml_block_state.block && @dml_block_state.block.shell == BlockType::OPTS
+ if @dml_block_state.block &&
+ @dml_block_state.block.type == BlockType::OPTS
debounce_reset
link_state = LinkState.new
options_state = read_show_options_and_trigger_reuse(
link_state: link_state,
mdoc: @dml_mdoc,
@@ -3027,26 +3583,30 @@
return
end
return :break if execute_block_in_state(block_name) == :break
- if prompt_user_exit(block_name_from_cli: @run_state.source.block_name_from_cli,
- selected: @dml_block_state.block)
+ if prompt_user_exit(
+ block_name_from_cli: @run_state.source.block_name_from_cli,
+ selected: @dml_block_state.block
+ )
return :break
end
- ## order of block name processing: link block, cli, from user
+ ## order of block name processing: link block, cli, from
+ # user
#
- @dml_link_state.block_name, @run_state.source.block_name_from_cli, cli_break =
+ @dml_link_state.block_name,
+ @run_state.source.block_name_from_cli, cli_break =
HashDelegator.next_link_state(
block_name: @dml_link_state.block_name,
block_name_from_cli: @dml_now_using_cli,
block_state: @dml_block_state,
was_using_cli: @dml_now_using_cli
)
- # &bsp '!block_name_from_ui + cli_break -> break'
+ # !!b '!block_name_from_ui + cli_break -> break'
!@dml_block_state.source.block_name_from_ui && cli_break && :break
end
def vux_execute_block_per_type(block_name, formatted_choice_ostructs)
case block_name
@@ -3061,12 +3621,11 @@
InputSequencer.next_link_state(prior_block_was_link: true)
when formatted_choice_ostructs[:history].pub_name
debounce_reset
- files_table_rows = read_saved_assets_for_history_table
- return :break unless files_table_rows
+ return :break unless files_table_rows = vux_history_files_table_rows
execute_history_select(files_table_rows, stream: $stderr)
return :break if pause_user_exit
InputSequencer.next_link_state(prior_block_was_link: true)
@@ -3084,11 +3643,11 @@
InputSequencer.next_link_state(prior_block_was_link: true)
when formatted_choice_ostructs[:shell].pub_name
debounce_reset
- vux_input_and_execute_shell_commands(stream: $stderr)
+ vux_input_and_execute_shell_commands(stream: $stderr, shell: shell)
return :break if pause_user_exit
InputSequencer.next_link_state(prior_block_was_link: true)
when formatted_choice_ostructs[:view].pub_name
@@ -3101,11 +3660,11 @@
else
return :break if vux_execute_and_prompt(block_name) == :break
InputSequencer.next_link_state(
block_name: @dml_link_state.block_name,
- prior_block_was_link: @dml_block_state.block.shell != BlockType::BASH
+ prior_block_was_link: @dml_block_state.block.type != BlockType::SHELL
)
end
end
def vux_formatted_names_for_state_chrome_blocks(
@@ -3122,17 +3681,25 @@
pub_name: dname.pub_name
)
end
end
+ def vux_history_files_table_rows
+ read_saved_assets_for_history_table(
+ asset: @delegate_object[:filename],
+ form: @delegate_object[:saved_history_format]
+ )
+ end
+
def vux_init
@menu_base_options = @delegate_object
@dml_link_state = LinkState.new(
block_name: @delegate_object[:block_name],
document_filename: @delegate_object[:filename]
)
- @run_state.source.block_name_from_cli = @dml_link_state.block_name.present?
+ @run_state.source.block_name_from_cli =
+ @dml_link_state.block_name.present?
@cli_block_name = @dml_link_state.block_name
@dml_now_using_cli = @run_state.source.block_name_from_cli
@dml_menu_default_dname = nil
@dml_block_state = SelectedBlockMenuState.new
@doc_saved_lines_files = []
@@ -3141,23 +3708,25 @@
@run_state.batch_index = 0
@run_state.files = StreamsOut.new
end
- def vux_input_and_execute_shell_commands(stream:)
+ def vux_input_and_execute_shell_commands(stream:, shell:)
loop do
- command = prompt_for_command(AnsiString.new(":MDE #{Time.now.strftime('%FT%TZ')}> ").send(:bgreen))
+ command = prompt_for_command(
+ AnsiString.new(":MDE #{Time.now.strftime('%FT%TZ')}> ").send(:bgreen)
+ )
break if !command.present? || command == 'exit'
exit_status = execute_command_with_streams(
- [@delegate_object[:shell], '-c', command]
+ [shell, '-c', command]
)
case exit_status
when 0
- stream.puts "#{'OK'.green} #{exit_status}"
+ stream.puts "#{AnsiString.new('OK').green} #{exit_status}"
else
- stream.puts "#{'ERR'.bred} #{exit_status}"
+ stream.puts "#{AnsiString.new('ERR').bred} #{exit_status}"
end
end
end
## load file with code lines per options
@@ -3178,32 +3747,34 @@
code_lines, inherited_dependencies, selected
)
end
def vux_load_inherited
- sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename,
- @delegate_object[:document_saved_lines_glob])
- load_filespec = load_filespec_from_expression(sf)
- return unless load_filespec
+ return unless filespec = load_filespec_from_expression(
+ document_name_in_glob_as_file_name
+ )
@dml_link_state.inherited_lines_append(
- File.readlines(load_filespec, chomp: true)
+ File.readlines(filespec, chomp: true)
)
end
# Select and execute a code block from a Markdown document.
#
# This method allows the user to interactively select a code block from a
# Markdown document, obtain approval, and execute the chosen block of code.
#
- # @return [Nil] Returns nil if no code block is selected or an error occurs.
+ # @return [Nil] Returns nil if no code block is selected
+ # or an error occurs.
def vux_main_loop
vux_init
vux_load_code_files_into_state
formatted_choice_ostructs = vux_formatted_names_for_state_chrome_blocks
- block_list = [@delegate_object[:block_name]].select(&:present?).compact + @delegate_object[:input_cli_rest]
+ block_list = [@delegate_object[:block_name]].select(&:present?).compact +
+ @delegate_object[:input_cli_rest]
+
@delegate_object[:block_name] = nil
process_commands(
arguments: @p_all_arguments,
named_procs: yield(:command_names, @delegate_object),
@@ -3237,20 +3808,31 @@
InputSequencer.new(
@delegate_object[:filename],
block_list
).run do |msg, data|
- # &bt msg
+ # !!v msg data
+ # !!t msg
case msg
when :parse_document # once for each menu
vux_parse_document
vux_menu_append_history_files(formatted_choice_ostructs)
vux_publish_document_file_name_for_external_automation
when :display_menu
vux_clear_menu_state
+ when :end_of_cli
+ # !!b
+ # yield :end_of_cli, @delegate_object
+
+ if @delegate_object[:list_blocks]
+ # !!b
+ list_blocks
+ :exit
+ end
+
when :user_choice
vux_user_selected_block_name
when :execute_block
ret = vux_execute_block_per_type(data, formatted_choice_ostructs)
@@ -3280,55 +3862,69 @@
end
end
def vux_menu_append_history_files(formatted_choice_ostructs)
if @delegate_object[:menu_for_history]
- history_files(@dml_link_state).tap do |files|
+ history_files(
+ @dml_link_state,
+ filename: saved_asset_filename(@delegate_object[:filename],
+ @dml_link_state),
+ path: @delegate_object[:saved_script_folder]
+ ).tap do |files|
if files.count.positive?
dml_menu_append_chrome_item(
formatted_choice_ostructs[:history].oname, files.count,
'files', menu_state: MenuState::HISTORY
)
end
end
end
- return unless @delegate_object[:menu_for_saved_lines] && @delegate_object[:document_saved_lines_glob].present?
+ return unless @delegate_object[:menu_for_saved_lines] &&
+ @delegate_object[:document_saved_lines_glob].present?
- sf = document_name_in_glob_as_file_name(
- @dml_link_state.document_filename,
- @delegate_object[:document_saved_lines_glob]
- )
- files = sf ? Dir.glob(sf) : []
+ document_glob = document_name_in_glob_as_file_name
+ files = document_glob ? Dir.glob(document_glob) : []
@doc_saved_lines_files = files.count.positive? ? files : []
lines_count = @dml_link_state.inherited_lines_count
# add menu items (glob, load, save) and enable selectively
if files.count.positive? || lines_count.positive?
- menu_add_disabled_option(sf)
+ menu_add_disabled_option(document_glob)
end
if files.count.positive?
- dml_menu_append_chrome_item(formatted_choice_ostructs[:load].dname, files.count, 'files',
- menu_state: MenuState::LOAD)
+ dml_menu_append_chrome_item(
+ formatted_choice_ostructs[:load].dname, files.count, 'files',
+ menu_state: MenuState::LOAD
+ )
end
- if @delegate_object[:menu_inherited_lines_edit_always] || lines_count.positive?
- dml_menu_append_chrome_item(formatted_choice_ostructs[:edit].dname, lines_count, 'lines',
- menu_state: MenuState::EDIT)
+ if @delegate_object[:menu_inherited_lines_edit_always] ||
+ lines_count.positive?
+ dml_menu_append_chrome_item(
+ formatted_choice_ostructs[:edit].dname, lines_count, 'lines',
+ menu_state: MenuState::EDIT
+ )
end
if lines_count.positive?
- dml_menu_append_chrome_item(formatted_choice_ostructs[:save].dname, 1, '',
- menu_state: MenuState::SAVE)
+ dml_menu_append_chrome_item(
+ formatted_choice_ostructs[:save].dname, 1, '',
+ menu_state: MenuState::SAVE
+ )
end
if lines_count.positive?
- dml_menu_append_chrome_item(formatted_choice_ostructs[:view].dname, 1, '',
- menu_state: MenuState::VIEW)
+ dml_menu_append_chrome_item(
+ formatted_choice_ostructs[:view].dname, 1, '',
+ menu_state: MenuState::VIEW
+ )
end
# rubocop:disable Style/GuardClause
if @delegate_object[:menu_with_shell]
- dml_menu_append_chrome_item(formatted_choice_ostructs[:shell].dname, 1, '',
- menu_state: MenuState::SHELL)
+ dml_menu_append_chrome_item(
+ formatted_choice_ostructs[:shell].dname, 1, '',
+ menu_state: MenuState::SHELL
+ )
end
# rubocop:enable Style/GuardClause
# # reflect new menu items
# @dml_mdoc = MDoc.new(@dml_menu_blocks)
@@ -3354,48 +3950,24 @@
link_state: @dml_link_state
)
@delegate_object[:filename] = @dml_link_state.document_filename
@dml_link_state.block_name = @delegate_object[:block_name] =
- @run_state.source.block_name_from_cli ?
- @cli_block_name :
- @dml_link_state.block_name
+ if @run_state.source.block_name_from_cli
+ @cli_block_name
+ else
+ @dml_link_state.block_name
+ end
# update @delegate_object and @menu_base_options in auto_load
#
@dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc =
mdoc_menu_and_blocks_from_nested_files(@dml_link_state)
dump_delobj(@dml_blocks_in_file, @dml_menu_blocks, @dml_link_state)
- # &bsp 'loop', @run_state.source.block_name_from_cli, @cli_block_name
+ # !!b 'loop', @run_state.source.block_name_from_cli, @cli_block_name
end
- def publish_for_external_automation(message:)
- return if @delegate_object[:publish_document_file_name].empty?
-
- pipe_path = absolute_path(@delegate_object[:publish_document_file_name])
-
- case @delegate_object[:publish_document_file_mode]
- when 'append'
- File.write(pipe_path, message + "\n", mode: 'a')
- when 'fifo'
- unless @vux_pipe_open
- unless File.exist?(pipe_path)
- FileUtils.mkfifo(pipe_path)
- @vux_pipe_created = pipe_path
- end
- @vux_pipe_open = File.open(pipe_path, 'w')
- end
- @vux_pipe_open.puts(message + "\n")
- @vux_pipe_open.flush
- when 'write'
- File.write(pipe_path, message)
- else
- raise 'Invalid publish_document_file_mode:' \
- " #{@delegate_object[:publish_document_file_mode]}"
- end
- end
-
def vux_publish_block_name_for_external_automation(block_name)
publish_for_external_automation(
message: format(
@delegate_object[:publish_block_name_format],
{ block: block_name,
@@ -3421,18 +3993,22 @@
)
end
# return :break to break from loop
def vux_user_selected_block_name
+ # !!b
if @dml_link_state.block_name.present?
# @prior_block_was_link = true
- @dml_block_state.block = blocks_find_by_block_name(@dml_blocks_in_file,
- @dml_link_state.block_name)
+ @dml_block_state.block = blocks_find_by_block_name(
+ @dml_blocks_in_file,
+ @dml_link_state.block_name
+ )
@dml_link_state.block_name = nil
else
- # puts "? - Select a block to execute (or type #{$texit} to exit):"
- return :break if vux_await_user_selection == :break # into @dml_block_state
+ # puts "? - Select a block to execute (or type #{$texit}
+ # to exit):"
+ return :break if vux_await_user_selection == :break
return :break if @dml_block_state.block.nil? # no block matched
end
# puts "! - Executing block: #{data}"
@dml_block_state.block&.pub_name
end
@@ -3448,63 +4024,80 @@
rescue Interrupt
# user interrupts process
end
def wait_for_user_selected_block(all_blocks, menu_blocks, default)
+ # !!b
block_state = wait_for_user_selection(all_blocks, menu_blocks, default)
handle_back_or_continue(block_state)
block_state
end
def wait_for_user_selection(_all_blocks, menu_blocks, default)
+ # !!b
if @delegate_object[:clear_screen_for_select_block]
printf("\e[1;1H\e[2J")
end
+ # !!b
prompt_title = string_send_color(
- @delegate_object[:prompt_select_block].to_s, :prompt_color_after_script_execution
+ @delegate_object[:prompt_select_block].to_s,
+ :prompt_color_after_script_execution
)
+ # !!b
menu_items = prepare_blocks_menu(menu_blocks)
if menu_items.empty?
return SelectedBlockMenuState.new(nil, OpenStruct.new,
MenuState::EXIT)
end
- # default value may not match if color is different from originating menu (opts changed while processing)
+ # !!b
+ # default value may not match if color is different from
+ # originating menu (opts changed while processing)
selection_opts = if default && menu_blocks.map(&:dname).include?(default)
@delegate_object.merge(default: default)
else
@delegate_object
end
- sph = @delegate_object[:select_page_height]
- selection_opts.merge!(per_page: sph)
-
+ # !!b
+ selection_opts.merge!(
+ { cycle: @delegate_object[:select_page_cycle],
+ per_page: @delegate_object[:select_page_height] }
+ )
selected_option = select_option_with_metadata(prompt_title, menu_items,
selection_opts)
+ # !!b
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:, selected:)
+ # Handles the core logic for generating the command
+ # file's metadata and content.
+ def write_command_file(required_lines:, selected:, shell: nil)
return unless @delegate_object[:save_executed_script]
time_now = Time.now.utc
@run_state.saved_script_filename =
- SavedAsset.new(blockname: selected.pub_name,
- exts: '.sh',
- filename: @delegate_object[:filename],
- prefix: @delegate_object[:saved_script_filename_prefix],
- saved_asset_format: shell_escape_asset_format(@dml_link_state),
- time: time_now).generate_name
+ SavedAsset.new(
+ blockname: selected.pub_name,
+ exts: '.sh',
+ filename: @delegate_object[:filename],
+ prefix: @delegate_object[:saved_script_filename_prefix],
+ saved_asset_format:
+ shell_escape_asset_format(
+ code_lines: @dml_link_state.inherited_lines,
+ shell: shell
+ ),
+ time: time_now
+ ).generate_name
@run_state.saved_filespec =
File.join(@delegate_object[:saved_script_folder],
@run_state.saved_script_filename)
shebang = if @delegate_object[:shebang]&.present?
- "#{@delegate_object[:shebang]} #{@delegate_object[:shell]}\n"
+ "#{@delegate_object[:shebang]} #{shell}\n"
else
''
end
content = shebang +
@@ -3521,24 +4114,26 @@
rescue StandardError
HashDelegator.error_handler('write_command_file')
end
# Ensure the directory exists before writing the file
- def write_file_with_directory_creation(save_filespec, content)
- directory = File.dirname(save_filespec)
+ def write_file_with_directory_creation(content:, filespec:)
+ directory = File.dirname(filespec)
begin
FileUtils.mkdir_p(directory)
- File.write(save_filespec, content)
+ File.write(filespec, content)
rescue Errno::EACCES
- warn "Permission denied: Unable to write to file '#{save_filespec}'"
+ warn "Permission denied: Unable to write to file '#{filespec}'"
nil
rescue Errno::EROFS
- warn "Read-only file system: Unable to write to file '#{save_filespec}'"
+ warn 'Read-only file system: Unable to write to file ' \
+ "'#{filespec}'"
nil
rescue StandardError => err
- warn "An error occurred while writing to file '#{save_filespec}': #{err.message}"
+ warn 'An error occurred while writing to file ' \
+ "'#{filespec}': #{err.message}"
nil
end
end
# return next document file name
@@ -3570,11 +4165,11 @@
else
value
end
end
- # Recursively cleans the given object (hash or struct) from unwanted values.
+ # Recursively cleans the given hash or struct from unwanted values.
def self.clean_hash_recursively(obj)
obj.each do |key, value|
cleaned_value = clean_value(value) # Clean and possibly convert value
obj[key] = cleaned_value if value.is_a?(Hash) || value.is_a?(Struct)
end
@@ -3645,78 +4240,87 @@
@hd = HashDelegator.new
end
# Test case for empty body
def test_next_link_state
- @hd.next_link_state(block_name_from_cli: nil, was_using_cli: nil, block_state: nil,
- block_name: nil)
+ @hd.next_link_state(
+ block_name_from_cli: nil, was_using_cli: nil,
+ block_state: nil, block_name: nil
+ )
end
end
class TestHashDelegator < Minitest::Test
def setup
@hd = HashDelegator.new
@mdoc = mock('MarkdownDocument')
end
- def test_calling_execute_required_lines_calls_command_execute_with_argument_args_value
+ def test_execute_required_lines_with_argument_args_value
+ # 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'
}
c = MarkdownExec::HashDelegator.new(obj)
c.pass_args = pigeon
- # Expect that method opts_command_execute is called with argument args having value pigeon
+ # Expect that method opts_command_execute is
+ # called with argument args having value pigeon
c.expects(:command_execute).with(
'',
- args: pigeon
+ args: pigeon,
+ shell: ShellType::BASH
)
# Call method opts_execute_required_lines
- c.execute_required_lines
+ c.execute_required_lines(shell: ShellType::BASH)
end
# Test case for empty body
- def test_push_link_history_and_trigger_load_with_empty_body
+ def test_execute_block_type_link_with_state_with_empty_body
assert_equal LoadFile::REUSE,
- @hd.push_link_history_and_trigger_load.load_file
+ @hd.execute_block_type_link_with_state.load_file
end
# Test case for non-empty body without 'file' key
- def test_push_link_history_and_trigger_load_without_file_key
+ def test_execute_block_type_link_with_state_without_file_key
body = ["vars:\n KEY: VALUE"]
assert_equal LoadFile::REUSE,
- @hd.push_link_history_and_trigger_load(link_block_body: body).load_file
+ @hd.execute_block_type_link_with_state(
+ link_block_body: body
+ ).load_file
end
# Test case for non-empty body with 'file' key
- def test_push_link_history_and_trigger_load_with_file_key
+ def test_execute_block_type_link_with_state_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: ['# ', 'KEY="VALUE"'])
)
assert_equal expected_result,
- @hd.push_link_history_and_trigger_load(
+ @hd.execute_block_type_link_with_state(
link_block_body: body,
selected: FCB.new(block_name: 'sample_block',
filename: 'sample_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)
+ assert_equal expected_result,
+ HashDelegator.indent_all_lines(body, indent)
end
def test_indent_all_lines_without_indent
body = "Line 1\nLine 2"
indent = nil
@@ -3740,11 +4344,12 @@
HashDelegator.safeval('invalid_code_raises_exception')
end
end
def test_set_fcb_title
- # sample input and output data for testing default_block_title_from_body method
+ # 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!'"
@@ -3764,657 +4369,717 @@
# 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
+ title = HashDelegator.default_block_title_from_body(input)
+ assert_equal output, title
end
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: menu_blocks, position: :initial)
+ def test_append_divider_initial
+ menu_blocks = []
+ @hd.append_divider(menu_blocks: menu_blocks, position: :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: menu_blocks, position: :final)
+ def test_append_divider_final
+ menu_blocks = []
+ @hd.append_divider(menu_blocks: menu_blocks, position: :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: menu_blocks, position: :initial)
+ def test_append_divider_without_format
+ @hd.instance_variable_set(:@delegate_object, {})
+ menu_blocks = []
+ @hd.append_divider(menu_blocks: menu_blocks, position: :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 = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
- result = HashDelegator.block_find(blocks, :text, 'value1')
- assert_equal('value1', result.text)
- end
+ def test_block_find_with_match
+ blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
+ result = HashDelegator.block_find(blocks, :text, 'value1')
+ assert_equal('value1', result.text)
+ end
- def test_block_find_without_match
- blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
- result = HashDelegator.block_find(blocks, :text, 'missing_value')
- assert_nil result
- end
+ def test_block_find_without_match
+ blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
+ result = HashDelegator.block_find(blocks, :text, 'missing_value')
+ assert_nil result
+ end
- def test_block_find_with_default
- blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
- result = HashDelegator.block_find(blocks, :text, 'missing_value',
- 'default')
- assert_equal 'default', result
- end
+ def test_block_find_with_default
+ blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
+ result = HashDelegator.block_find(blocks, :text, 'missing_value',
+ '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(: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
- assert_kind_of Array, result
- assert_kind_of FCB, result.first
- end
+ def test_blocks_from_nested_files
+ result = @hd.blocks_from_nested_files
+ 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 = FCB.new(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 = FCB.new(
+ body: ['key: value'],
+ type: BlockType::VARS
+ )
+ 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: @mdoc, selected: @selected,
- block_source: {})
+ def test_execute_block_type_port_code_lines_with_vars
+ YAML.stubs(:load).returns({ 'key' => 'value' })
+ @mdoc.stubs(:collect_recursively_required_code)
+ .returns({ code: ['code line'] })
+ result = @hd.execute_block_type_port_code_lines(
+ mdoc: @mdoc, selected: @selected, block_source: {}
+ )
- assert_equal ['code line', 'key="value"'], result
- end
+ assert_equal ['code line', 'key="value"'], 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: all_blocks)
+ result = @hd.load_cli_or_user_selected_block(all_blocks: all_blocks)
- assert_equal all_blocks.first,
- result.block
- assert_equal OpenStruct.new(block_name_from_ui: false),
- result.source
- assert_nil result.state
- end
+ assert_equal all_blocks.first,
+ result.block
+ assert_equal OpenStruct.new(block_name_from_ui: false),
+ result.source
+ assert_nil result.state
+ end
- def test_user_selected_block
- block_state = SelectedBlockMenuState.new({ oname: 'block2' }, OpenStruct.new,
- :some_state)
- @hd.stubs(:wait_for_user_selected_block).returns(block_state)
+ def test_user_selected_block
+ block_state = SelectedBlockMenuState.new(
+ { oname: 'block2' }, OpenStruct.new, :some_state
+ )
+ @hd.stubs(:wait_for_user_selected_block).returns(block_state)
- result = @hd.load_cli_or_user_selected_block
+ result = @hd.load_cli_or_user_selected_block
- assert_equal block_state.block,
- result.block
- assert_equal OpenStruct.new(block_name_from_ui: true),
- result.source
- assert_equal :some_state, result.state
- end
+ assert_equal block_state.block,
+ result.block
+ assert_equal OpenStruct.new(block_name_from_ui: true),
+ result.source
+ 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',
- import_paths: nil).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',
- import_paths: nil).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 = FCB.new(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 = FCB.new(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 = FCB.new(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 = FCB.new(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 = FCB.new(oname: 'Other Option')
+ def test_determine_block_state_continue
+ selected_option = FCB.new(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: required_lines)
+ @hd.display_required_code(required_lines: 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_stream_with_valid_key
- # result = HashDelegator.format_execution_stream(
- # { stdout: %w[output1 output2] },
- # ExecutionStreams::STD_OUT
- # )
+ # def test_format_execution_stream_with_valid_key
+ # result = HashDelegator.format_execution_stream(
+ # { stdout: %w[output1 output2] },
+ # ExecutionStreams::STD_OUT
+ # )
- # assert_equal "output1\noutput2", result
- # end
+ # assert_equal "output1\noutput2", result
+ # end
- # def test_format_execution_stream_with_empty_key
- # @hd.instance_variable_get(:@run_state).stubs(:files).returns({})
+ # def test_format_execution_stream_with_empty_key
+ # @hd.instance_variable_get(:@run_state).stubs(:files).returns({})
- # result = HashDelegator.format_execution_stream(nil,
- # ExecutionStreams::STD_ERR)
+ # result = HashDelegator.format_execution_stream(
+ # nil, ExecutionStreams::STD_ERR)
- # assert_equal '', result
- # end
+ # assert_equal '', result
+ # end
- # def test_format_execution_stream_with_nil_files
- # @hd.instance_variable_get(:@run_state).stubs(:files).returns(nil)
+ # def test_format_execution_stream_with_nil_files
+ # @hd.instance_variable_get(:@run_state).stubs(:files).returns(nil)
- # result = HashDelegator.format_execution_stream(nil, :stdin)
+ # result = HashDelegator.format_execution_stream(nil, :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_new_state
+ # Verifying that history_state_pop is called
+ # @hd.expects(:history_state_pop).once
- def test_pop_link_history_new_state
- # Verifying that history_state_pop is called
- # @hd.expects(:history_state_pop).once
+ result = @hd.pop_link_history_new_state
- result = @hd.pop_link_history_new_state
+ # Asserting the result is an instance of LinkState
+ assert_nil result.block_name
+ end
+ end
- # Asserting the result is an instance of LinkState
- assert_nil result.block_name
- end
+ class TestHashDelegatorBlockType < Minitest::Test
+ def setup
+ @hd = HashDelegator.new
end
- class TestHashDelegatorHandleBlockState < Minitest::Test
- def setup
- @hd = HashDelegator.new
- @mock_block_state = mock('block_state')
- end
+ # def execute_block_type_history_ux(
+ # directory: @delegate_object[:document_configurations_directory],
+ # filename: '*',
+ # form: '%{line}',
+ # link_state:,
+ # regexp: "^(?<line>.*)$",
+ # selected:
+ # )
- 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 read_saved_assets_for_history_table(
+ # asset: nil,
+ # filename: nil,
+ # form: @delegate_object[:saved_history_format],
+ # path: @delegate_object[:saved_script_folder],
+ # regexp: @delegate_object[:saved_asset_match]
+ # )
- @hd.handle_back_or_continue(@mock_block_state)
+ # def history_files(
+ # link_state,
+ # direction: :reverse,
+ # filename: nil,
+ # home: Dir.pwd,
+ # order: :chronological,
+ # path: ''
+ # )
- 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_call
+ @hd.expects(:history_files).with(nil, filename: '*', path: nil).once
+ @hd.execute_block_type_history_ux(filename: '*', link_state: LinkState.new,
+ selected: FCB.new(body: []))
+ end
+ 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' })
+ class TestHashDelegatorHandleBlockState < Minitest::Test
+ def setup
+ @hd = HashDelegator.new
+ @mock_block_state = mock('block_state')
+ end
- @hd.handle_back_or_continue(@mock_block_state)
+ 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' })
- assert_equal 'another_block',
- @hd.instance_variable_get(:@delegate_object)[:block_name]
- refute @hd.instance_variable_get(:@menu_user_clicked_back_link)
- end
+ @hd.handle_back_or_continue(@mock_block_state)
- 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)
-
- assert_nil @hd.instance_variable_get(:@delegate_object)[:block_name]
- assert_nil @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
- class TestHashDelegatorHandleGenericBlock < Minitest::Test
- def setup
- @hd = HashDelegator.new
- @mock_document = mock('MarkdownDocument')
- @selected_item = mock('FCB')
- 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_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 })
+ @hd.handle_back_or_continue(@mock_block_state)
- # Test the method without user approval
- # Expectations and assertions go here
- 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_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_handle_back_or_continue_with_other
+ @mock_block_state.stubs(:state).returns(nil) # MenuState::OTHER
+ @mock_block_state.stubs(:block).returns({ oname: 'other_block' })
- # Test the method with user approval
- # Expectations and assertions go here
- end
+ @hd.handle_back_or_continue(@mock_block_state)
- 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
+ assert_nil @hd.instance_variable_get(:@delegate_object)[:block_name]
+ assert_nil @hd.instance_variable_get(:@menu_user_clicked_back_link)
end
+ end
- # require 'stringio'
+ # class TestHashDelegatorHandleGenericBlock < Minitest::Test
+ # def setup
+ # @hd = HashDelegator.new
+ # @mock_document = mock('MarkdownDocument')
+ # @selected_item = mock('FCB')
+ # end
- class TestHashDelegatorHandleStream < Minitest::Test
- def setup
- @hd = HashDelegator.new
- @hd.instance_variable_set(:@run_state,
- OpenStruct.new(files: StreamsOut.new))
- @hd.instance_variable_set(:@delegate_object,
- { output_stdout: true })
- 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_handle_stream
- stream = StringIO.new("line 1\nline 2\n")
- file_type = ExecutionStreams::STD_OUT
+ # # Test the method without user approval
+ # # Expectations and assertions go here
+ # end
- Thread.new { @hd.handle_stream(stream: stream, file_type: file_type) }
+ # 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 })
- @hd.wait_for_stream_processing
- assert_equal ['line 1', 'line 2'],
- @hd.instance_variable_get(:@run_state).files.stream_lines(ExecutionStreams::STD_OUT)
- end
+ # # Test the method with user approval
+ # # Expectations and assertions go here
+ # end
- def test_handle_stream_with_io_error
- stream = StringIO.new("line 1\nline 2\n")
- file_type = ExecutionStreams::STD_OUT
- stream.stubs(:each_line).raises(IOError)
+ # 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 })
- Thread.new { @hd.handle_stream(stream: stream, file_type: file_type) }
+ # # Test the method with output script option
+ # # Expectations and assertions go here
+ # end
+ # end
- @hd.wait_for_stream_processing
+ # require 'stringio'
- assert_equal [],
- @hd.instance_variable_get(:@run_state).files.stream_lines(ExecutionStreams::STD_OUT)
- end
+ class TestHashDelegatorHandleStream < Minitest::Test
+ def setup
+ @hd = HashDelegator.new
+ @hd.instance_variable_set(:@run_state,
+ OpenStruct.new(files: StreamsOut.new))
+ @hd.instance_variable_set(:@delegate_object,
+ { output_stdout: true })
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_handle_stream
+ stream = StringIO.new("line 1\nline 2\n")
+ file_type = ExecutionStreams::STD_OUT
- def test_iter_blocks_from_nested_files
- @hd.cfile.expect(:readlines, ['line 1', 'line 2'], ['test.md'],
- import_paths: nil)
- selected_types = ['filtered message']
+ Thread.new { @hd.handle_stream(stream: stream, file_type: file_type) }
- result = @hd.iter_blocks_from_nested_files { selected_types }
- assert_equal ['line 1', 'line 2'], result
+ @hd.wait_for_stream_processing
+ assert_equal ['line 1', 'line 2'],
+ @hd.instance_variable_get(:@run_state)
+ .files.stream_lines(ExecutionStreams::STD_OUT)
+ end
- @hd.cfile.verify
- end
+ def test_handle_stream_with_io_error
+ stream = StringIO.new("line 1\nline 2\n")
+ file_type = ExecutionStreams::STD_OUT
+ stream.stubs(:each_line).raises(IOError)
- def test_iter_blocks_from_nested_files_with_no_file
- @hd.stubs(:check_file_existence).with('test.md').returns(false)
+ Thread.new { @hd.handle_stream(stream: stream, file_type: file_type) }
- assert_nil(@hd.iter_blocks_from_nested_files do
- ['filtered message']
- end)
- end
+ @hd.wait_for_stream_processing
+
+ assert_equal [],
+ @hd.instance_variable_get(:@run_state)
+ .files.stream_lines(ExecutionStreams::STD_OUT)
end
+ 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(AnsiString.new('-- Back --').red)
- 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_menu_chrome_colored_option_with_color
- assert_equal AnsiString.new('-- Back --').red,
- @hd.menu_chrome_colored_option(:menu_option_back_name)
- end
+ def test_iter_blocks_from_nested_files
+ @hd.cfile.expect(:readlines, ['line 1', 'line 2'], ['test.md'],
+ import_paths: nil)
+ selected_types = ['filtered message']
- 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
+ result = @hd.iter_blocks_from_nested_files { selected_types }
+ assert_equal ['line 1', 'line 2'], result
+
+ @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
+ assert_nil(@hd.iter_blocks_from_nested_files do
+ ['filtered message']
+ end)
+ end
+ 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
+ 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(AnsiString.new('-- Back --').red)
end
- class TestHashDelegatorStartFencedBlock < Minitest::Test
- def setup
- @hd = HashDelegator.new({
- block_name_wrapper_match: 'WRAPPER_REGEX',
- block_calls_scan: 'CALLS_REGEX'
+ def test_menu_chrome_colored_option_with_color
+ assert_equal AnsiString.new('-- Back --').red,
+ @hd.menu_chrome_colored_option(:menu_option_back_name)
+ 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
+
+ class TestHashDelegatorMenuChromeOption < Minitest::Test
+ def setup
+ @hd = HashDelegator.new
+ @hd.instance_variable_set(:@delegate_object, {
+ menu_option_back_name: "'Back'",
+ menu_chrome_format: '-- %s --'
})
- end
+ HashDelegator.stubs(:safeval).with("'Back'").returns('Back')
+ end
- def test_start_fenced_block
- line = '```fenced'
- headings = ['Heading 1']
- regex = /```(?<name>\w+)(?<rest>.*)/
+ def test_menu_chrome_formatted_option_with_format
+ assert_equal '-- Back --',
+ @hd.menu_chrome_formatted_option(:menu_option_back_name)
+ end
- fcb = @hd.start_fenced_block(line, headings, regex)
+ 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
- assert_instance_of MarkdownExec::FCB, fcb
- assert_equal headings, fcb.headings
- assert_equal 'fenced', fcb.dname
- end
+ class TestHashDelegatorStartFencedBlock < Minitest::Test
+ def setup
+ @hd = HashDelegator.new({
+ block_name_wrapper_match: 'WRAPPER_REGEX',
+ block_calls_scan: 'CALLS_REGEX'
+ })
end
- class TestHashDelegatorStringSendColor < Minitest::Test
- def setup
- @hd = HashDelegator.new
- @hd.instance_variable_set(:@delegate_object,
- { red: 'red', green: 'green' })
- end
+ def test_start_fenced_block
+ line = '```fenced'
+ headings = ['Heading 1']
+ regex = /```(?<name>\w+)(?<rest>.*)/
- def test_string_send_color
- assert_equal AnsiString.new('Hello').red,
- @hd.string_send_color('Hello', :red)
- assert_equal AnsiString.new('World').green,
- @hd.string_send_color('World', :green)
- assert_equal AnsiString.new('Default').plain,
- @hd.string_send_color('Default', :blue)
- end
+ 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
+ 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 TestHashDelegatorStringSendColor < Minitest::Test
+ def setup
+ @hd = HashDelegator.new
+ @hd.instance_variable_set(:@delegate_object,
+ { red: 'red', green: 'green' })
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_string_send_color
+ assert_equal AnsiString.new('Hello').red,
+ @hd.string_send_color('Hello', :red)
+ assert_equal AnsiString.new('World').green,
+ @hd.string_send_color('World', :green)
+ assert_equal AnsiString.new('Default').plain,
+ @hd.string_send_color('Default', :blue)
end
+ end
- def test_yield_line_if_selected_without_block
- result = HashDelegator.yield_line_if_selected('Test line', [:line])
- assert_nil result
+ 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
+ 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_yield_line_if_selected_without_block
+ result = HashDelegator.yield_line_if_selected('Test line', [:line])
+ assert_nil result
+ end
+ # end
+
class TestHashDelegatorUpdateMenuAttribYieldSelectedWithBody < Minitest::Test
def setup
@hd = HashDelegator.new
@fcb = mock('Fcb')
@fcb.stubs(:body).returns(true)
@@ -4446,11 +5111,11 @@
@hd = HashDelegator.new
HashDelegator.stubs(:error_handler)
end
def test_wait_for_user_selected_block_with_back_state
- mock_block_state = Struct.new(:state, :block).new(MenuState::BACK,
- { oname: 'back_block' })
+ 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)