lib/hash_delegator.rb in markdown_exec-1.8.4 vs lib/hash_delegator.rb in markdown_exec-1.8.5
- old
+ new
@@ -24,10 +24,11 @@
require_relative 'filter'
require_relative 'fout'
require_relative 'hash'
require_relative 'link_history'
require_relative 'mdoc'
+require_relative 'regexp'
require_relative 'string_util'
class String
# Checks if the string is not empty.
# @return [Boolean] Returns true if the string is not empty, false otherwise.
@@ -294,21 +295,22 @@
@delegate_object[:block_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
)
+ 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(required[:dependencies],
+ 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 true
- warn format_and_highlight_dependencies(required[:dependencies],
+ warn format_and_highlight_dependencies(dependencies,
highlight: [@delegate_object[:block_name]])
end
code_merge link_state&.inherited_lines, required[:code]
end
@@ -386,10 +388,11 @@
required_lines = collect_required_code_lines(mdoc, selected, link_state,
block_source: block_source)
output_or_approval = @delegate_object[:output_script] || @delegate_object[:user_must_approve]
display_required_code(required_lines) if output_or_approval
allow_execution = @delegate_object[:user_must_approve] ? prompt_for_user_approval(required_lines) : true
+
execute_required_lines(required_lines) if allow_execution
link_state.block_name = nil
LoadFileLinkState.new(LoadFile::Reuse, link_state)
end
@@ -607,11 +610,14 @@
elsif @menu_user_clicked_back_link
pop_link_history_and_trigger_load
elsif selected[:shell] == BlockType::OPTS
- update_options_and_trigger_reuse(selected, @menu_base_options, link_state)
+ options_state = read_show_options_and_trigger_reuse(selected, link_state)
+ @menu_base_options.merge!(options_state.options)
+ @delegate_object.merge!(options_state.options)
+ options_state.load_file_link_state
else
compile_execute_bash_and_special_blocks_and_trigger_reuse(mdoc, selected, link_state,
block_source: block_source)
end
@@ -803,28 +809,30 @@
end
end
def link_history_push_and_next(
curr_block_name:, curr_document_filename:,
- inherited_block_names:, inherited_lines:,
+ inherited_block_names:, inherited_dependencies:, inherited_lines:,
next_block_name:, next_document_filename:,
next_load_file:
)
@link_history.push(
LinkState.new(
block_name: curr_block_name,
document_filename: curr_document_filename,
inherited_block_names: inherited_block_names,
+ inherited_dependencies: inherited_dependencies,
inherited_lines: inherited_lines
)
)
LoadFileLinkState.new(
next_load_file,
LinkState.new(
block_name: next_block_name,
document_filename: next_document_filename,
inherited_block_names: inherited_block_names,
+ inherited_dependencies: inherited_dependencies,
inherited_lines: inherited_lines
)
)
end
@@ -839,11 +847,14 @@
end
block = block_find(all_blocks, :oname, block_name)
return unless block
- update_options_and_trigger_reuse(block, @delegate_object)
+ options_state = read_show_options_and_trigger_reuse(block)
+ @menu_base_options.merge!(options_state.options)
+ @delegate_object.merge!(options_state.options)
+
@most_recent_loaded_filename = @delegate_object[:filename]
true
end
def mdoc_and_blocks_from_nested_files
@@ -976,22 +987,42 @@
def parse_yaml_data_from_body(body)
body.any? ? YAML.load(body.join("\n")) : {}
end
+ def pop_add_current_code_to_head_and_trigger_load(_link_state, block_names, code_lines,
+ dependencies)
+ pop = @link_history.pop # updatable
+ next_link_state = LinkState.new(
+ block_name: pop.block_name,
+ document_filename: pop.document_filename,
+ inherited_block_names:
+ (pop.inherited_block_names + block_names).sort.uniq,
+ inherited_dependencies:
+ dependencies.merge(pop.inherited_dependencies || {}), ### merge, not replace, key data
+ inherited_lines:
+ code_merge(pop.inherited_lines, code_lines)
+ )
+ @link_history.push(next_link_state)
+
+ next_link_state.block_name = nil
+ LoadFileLinkState.new(LoadFile::Load, next_link_state)
+ end
+
# This method handles the back-link operation in the Markdown execution context.
# It updates the history state and prepares to load the next block.
#
# @return [LoadFileLinkState] An object indicating the action to load the next block.
def pop_link_history_and_trigger_load
pop = @link_history.pop
peek = @link_history.peek
LoadFileLinkState.new(LoadFile::Load, LinkState.new(
- document_filename: pop.document_filename,
- inherited_block_names: peek.inherited_block_names,
- inherited_lines: peek.inherited_lines
- ))
+ document_filename: pop.document_filename,
+ inherited_block_names: peek.inherited_block_names,
+ inherited_dependencies: peek.inherited_dependencies,
+ inherited_lines: peek.inherited_lines
+ ))
end
def post_execution_process
initialize_and_save_execution_output
output_execution_summary
@@ -1133,25 +1164,56 @@
label_format_below: @delegate_object[:shell_code_label_format_below],
block_source: { document_filename: link_state.document_filename }
)
code_lines = code_info[:code]
block_names = code_info[:block_names]
+ dependencies = code_info[:dependencies]
else
block_names = []
code_lines = []
+ dependencies = {}
end
-
next_document_filename = link_block_data['file'] || @delegate_object[:filename]
- link_history_push_and_next(
- curr_block_name: selected[:oname],
- curr_document_filename: @delegate_object[:filename],
- inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
- inherited_lines: code_merge(link_state&.inherited_lines, code_lines),
- next_block_name: link_block_data['block'] || '',
- next_document_filename: next_document_filename,
- next_load_file: next_document_filename == @delegate_object[:filename] ? LoadFile::Reuse : LoadFile::Load
- )
+
+ # if an eval link block, evaluate code_lines and return its standard output
+ #
+ if link_block_data.fetch('eval', false)
+ all_code = code_merge(link_state&.inherited_lines, code_lines)
+ output = `#{all_code.join("\n")}`.split("\n")
+ label_format_above = @delegate_object[:shell_code_label_format_above]
+ label_format_below = @delegate_object[:shell_code_label_format_below]
+ block_source = { document_filename: link_state&.document_filename }
+
+ code_lines = [label_format_above && format(label_format_above,
+ block_source.merge({ block_name: selected[:oname] }))] +
+ output.map do |line|
+ re = Regexp.new(link_block_data.fetch('pattern', '(?<line>.*)'))
+ if re =~ line
+ re.gsub_format(line, link_block_data.fetch('format', '%{line}'))
+ end
+ end.compact +
+ [label_format_below && format(label_format_below,
+ block_source.merge({ block_name: selected[:oname] }))]
+
+ end
+
+ if link_block_data['return']
+ pop_add_current_code_to_head_and_trigger_load(link_state, block_names, code_lines,
+ dependencies)
+
+ else
+ link_history_push_and_next(
+ curr_block_name: selected[:oname],
+ curr_document_filename: @delegate_object[:filename],
+ inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
+ inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
+ inherited_lines: code_merge(link_state&.inherited_lines, code_lines),
+ next_block_name: link_block_data['block'] || '',
+ next_document_filename: next_document_filename,
+ next_load_file: next_document_filename == @delegate_object[:filename] ? LoadFile::Reuse : LoadFile::Load
+ )
+ end
end
# Reads required code blocks from a temporary file specified by an environment variable.
# @return [Array<String>] Lines read from the temporary file, or an empty array if file is not found or path is empty.
def read_required_blocks_from_temp_file(temp_blocks_file_path)
@@ -1227,12 +1289,21 @@
@menu_user_clicked_back_link = false
@delegate_object[:filename] = link_state.document_filename
link_state.block_name = @delegate_object[:block_name] =
block_name_from_cli ? @cli_block_name : link_state.block_name
+ # update @delegate_object and @menu_base_options in auto_load
+ #
blocks_in_file, menu_blocks, mdoc = mdoc_menu_and_blocks_from_nested_files
+ 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'
)
@@ -1241,10 +1312,16 @@
warn format_and_highlight_dependencies(
compact_and_index_hash(menu_blocks),
label: 'menu_blocks'
)
end
+ if @delegate_object[:dump_inherited_lines]
+ warn format_and_highlight_lines(
+ link_state.inherited_lines,
+ label: 'inherited_lines'
+ )
+ end
block_state = command_or_user_selected_block(blocks_in_file,
menu_blocks, menu_default_dname)
return if block_state.state == MenuState::EXIT
@@ -1264,10 +1341,11 @@
link_state,
block_source: { document_filename: @delegate_object[:filename] }
)
load_file = load_file_link_state.load_file
link_state = load_file_link_state.link_state
+
# if the same menu is being displayed, collect the display name of the selected menu item for use as the default item
menu_default_dname = load_file == LoadFile::Load ? nil : block_state.block[:dname]
# user prompt to exit if the menu will be displayed again
#
@@ -1404,16 +1482,10 @@
},
symbols: { cross: ' ' }
)
end
- def update_delegate_and_target(key, value, tgt2)
- sym_key = key.to_sym
- @delegate_object[sym_key] = value
- tgt2[sym_key] = value if tgt2
- end
-
# Updates the hierarchy of document headings based on the given line.
# Utilizes regular expressions to identify heading levels.
# @param line [String] The line of text to check for headings.
# @param headings [Array<String>] Current headings hierarchy.
# @return [Array<String>] Updated headings hierarchy.
@@ -1485,10 +1557,14 @@
]
elsif nested_line[:depth].zero? || @delegate_object[:menu_include_imported_notes]
# add line if it is depth 0 or option allows it
#
yield_line_if_selected(line, selected_messages, &block)
+
+ else
+ # 'rejected'
+
end
end
# 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.
@@ -1507,18 +1583,25 @@
# 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.
- def update_options_and_trigger_reuse(selected, tgt2 = nil, link_state = LinkState.new)
+ def read_show_options_and_trigger_reuse(selected, link_state = LinkState.new)
+ obj = {}
data = YAML.load(selected[:body].join("\n"))
(data || []).each do |key, value|
- update_delegate_and_target(key, value, tgt2)
+ sym_key = key.to_sym
+ obj[sym_key] = value
+
print_formatted_option(key, value) if @delegate_object[:menu_opts_set_format].present?
end
+
link_state.block_name = nil
- LoadFileLinkState.new(LoadFile::Reuse, link_state)
+ OpenStruct.new(options: obj,
+ load_file_link_state: LoadFileLinkState.new(
+ LoadFile::Reuse, link_state
+ ))
end
def wait_for_stream_processing
@process_mutex.synchronize do
@process_cv.wait(@process_mutex)
@@ -1714,10 +1797,11 @@
def test_push_link_history_and_trigger_load_with_file_key
body = ["file: sample_file\nblock: sample_block\nvars:\n KEY: VALUE"]
expected_result = LoadFileLinkState.new(LoadFile::Load,
LinkState.new(block_name: 'sample_block',
document_filename: 'sample_file',
+ inherited_dependencies: {},
inherited_lines: []))
assert_equal expected_result,
@hd.push_link_history_and_trigger_load(body, nil, FCB.new(block_name: 'sample_block',
filename: 'sample_file'))
end
@@ -2236,44 +2320,10 @@
# Test the method with output script option
# Expectations and assertions go here
end
end
- class TestHashDelegatorHandleOptsBlock < Minitest::Test
- def setup
- @hd = HashDelegator.new
- @hd.instance_variable_set(:@delegate_object,
- { menu_opts_set_format: 'Option: %<key>s, Value: %<value>s',
- menu_opts_set_color: :blue })
- @hd.stubs(:string_send_color)
- @hd.stubs(:print)
- end
-
- def test_update_options_and_trigger_reuse
- selected = { body: ['option1: value1'] }
- tgt2 = {}
-
- result = @hd.update_options_and_trigger_reuse(selected, tgt2)
-
- assert_instance_of LoadFileLinkState, result
- assert_equal 'value1',
- @hd.instance_variable_get(:@delegate_object)[:option1]
- assert_equal 'value1', tgt2[:option1]
- end
-
- def test_update_options_and_trigger_reuse_without_format
- selected = { body: ['option2: value2'] }
- @hd.instance_variable_set(:@delegate_object, {})
-
- result = @hd.update_options_and_trigger_reuse(selected)
-
- assert_instance_of LoadFileLinkState, result
- assert_equal 'value2',
- @hd.instance_variable_get(:@delegate_object)[:option2]
- end
- end
-
# require 'stringio'
class TestHashDelegatorHandleStream < Minitest::Test
def setup
@hd = HashDelegator.new
@@ -2337,43 +2387,10 @@
['filtered message']
end)
end
end
- class TestHashDelegatorLoadAutoBlocks < Minitest::Test
- def setup
- @hd = HashDelegator.new
- @hd.stubs(:block_find).returns({})
- @hd.stubs(:update_options_and_trigger_reuse)
- end
-
- def test_load_auto_blocks_with_new_filename
- @hd.instance_variable_set(:@delegate_object, {
- document_load_opts_block_name: 'load_block',
- filename: 'new_file'
- })
- assert @hd.load_auto_blocks([])
- end
-
- def test_load_auto_blocks_with_same_filename
- @hd.instance_variable_set(:@delegate_object, {
- document_load_opts_block_name: 'load_block',
- filename: 'new_file'
- })
- @hd.instance_variable_set(:@most_recent_loaded_filename, 'new_file')
- assert_nil @hd.load_auto_blocks([])
- end
-
- def test_load_auto_blocks_without_block_name
- @hd.instance_variable_set(:@delegate_object, {
- document_load_opts_block_name: nil,
- filename: 'new_file'
- })
- assert_nil @hd.load_auto_blocks([])
- end
- end
-
class TestHashDelegatorMenuChromeColoredOption < Minitest::Test
def setup
@hd = HashDelegator.new
@hd.instance_variable_set(:@delegate_object, {
menu_option_back_name: 'Back',
@@ -2564,10 +2581,9 @@
refute @hd.instance_variable_get(:@menu_user_clicked_back_link)
assert_equal mock_block_state, result
end
end
- ####
class TestHashDelegatorYieldToBlock < Minitest::Test
def setup
@hd = HashDelegator.new
@fcb = mock('Fcb')
MarkdownExec::Filter.stubs(:fcb_select?).returns(true)