lib/mdoc.rb in markdown_exec-1.3.7 vs lib/mdoc.rb in markdown_exec-1.3.8

- old
+ new

@@ -14,19 +14,51 @@ # to fetch related or dependent blocks. # class MDoc attr_reader :table - # convert block name to fcb_parse + # Initializes an instance of MDoc with the given table of markdown sections. # + # @param table [Array<Hash>] An array of hashes representing markdown sections. + # def initialize(table) @table = table end + # Retrieves code blocks that are required by a specified code block. + # + # @param name [String] The name of the code block to start the retrieval from. + # @return [Array<Hash>] An array of code blocks required by the specified code block. + # + def collect_recursively_required_blocks(name) + name_block = get_block_by_name(name) + raise "Named code block `#{name}` not found." if name_block.nil? || name_block.keys.empty? + + all = [name_block.fetch(:name, '')] + recursively_required(name_block[:reqs]) + + # in order of appearance in document + # insert function blocks + @table.select { |fcb| all.include? fcb.fetch(:name, '') } + .map do |fcb| + if (call = fcb[:call]) + [get_block_by_name("[#{call.match(/^%\((\S+) |\)/)[1]}]") + .merge({ cann: call })] + else + [] + end + [fcb] + end.flatten(1) + end + + # Collects recursively required code blocks and returns them as an array of strings. + # + # @param name [String] The name of the code block to start the collection from. + # @return [Array<String>] An array of strings containing the collected code blocks. + # def collect_recursively_required_code(name) - get_required_blocks(name) - .map do |fcb| + collect_wrapped_blocks( + collect_recursively_required_blocks(name) + ).map do |fcb| body = fcb[:body].join("\n") if fcb[:cann] xcall = fcb[:cann][1..-2] mstdin = xcall.match(/<(?<type>\$)?(?<name>[A-Za-z_\-.\w]+)/) @@ -56,47 +88,41 @@ fcb[:body] end end.flatten(1) end - def get_block_by_name(name, default = {}) - @table.select { |fcb| fcb.fetch(:name, '') == name }.fetch(0, default) + # Retrieves code blocks that are wrapped + # wraps are applied from left to right + # e.g. w1 w2 => w1-before w2-before w1 w2 w2-after w1-after + # + # @return [Array<Hash>] An array of code blocks required by the specified code blocks. + # + def collect_wrapped_blocks(blocks) + blocks.map do |block| + (block[:wraps] || []).map do |wrap| + wrap_before = wrap.sub('}', '-before}') ### hardcoded wrap name + @table.select { |fcb| [wrap_before, wrap].include? fcb[:name] } + end.flatten(1) + + [block] + + (block[:wraps] || []).reverse.map do |wrap| + wrap_after = wrap.sub('}', '-after}') ### hardcoded wrap name + @table.select { |fcb| fcb[:name] == wrap_after } + end.flatten(1) + end.flatten(1).compact end - def get_required_blocks(name) - name_block = get_block_by_name(name) - raise "Named code block `#{name}` not found." if name_block.nil? || name_block.keys.empty? - - all = [name_block.fetch(:name, '')] + recursively_required(name_block[:reqs]) - - # in order of appearance in document - # insert function blocks - @table.select { |fcb| all.include? fcb.fetch(:name, '') } - .map do |fcb| - if (call = fcb[:call]) - [get_block_by_name("[#{call.match(/^%\((\S+) |\)/)[1]}]") - .merge({ cann: call })] - else - [] - end + [fcb] - end.flatten(1) - end - - # :reek:UtilityFunction - def hide_menu_block_per_options(opts, block) - (opts[:hide_blocks_by_name] && - block[:name]&.match(Regexp.new(opts[:block_name_hidden_match])) && - (block[:name]&.present? || block[:label]&.present?) - ) - end - + # Retrieves code blocks based on the provided options. + # + # @param opts [Hash] The options used for filtering code blocks. + # @return [Array<Hash>] An array of code blocks that match the options. + # def fcbs_per_options(opts = {}) options = opts.merge(block_name_hidden_match: nil) selrows = @table.select do |fcb_title_groups| Filter.fcb_select? options, fcb_title_groups end - + # pp selrows; binding.pry ### hide rows correctly if opts[:hide_blocks_by_name] selrows.reject { |block| hide_menu_block_per_options opts, block } else @@ -105,10 +131,44 @@ # block[:name] = block[:text] if block[:name].nil? block end end + # Retrieves a code block by its name. + # + # @param name [String] The name of the code block to retrieve. + # @param default [Hash] The default value to return if the code block is not found. + # @return [Hash] The code block as a hash or the default value if not found. + # + def get_block_by_name(name, default = {}) + @table.select { |fcb| fcb.fetch(:name, '') == name }.fetch(0, default) + end + + # Checks if a code block should be hidden based on the given options. + # + # @param opts [Hash] The options used for hiding code blocks. + # @param block [Hash] The code block to check for hiding. + # @return [Boolean] True if the code block should be hidden; false otherwise. + # + # :reek:UtilityFunction + def hide_menu_block_per_options(opts, block) + (opts[:hide_blocks_by_name] && + ((opts[:block_name_hidden_match]&.present? && + block[:name]&.match(Regexp.new(opts[:block_name_hidden_match]))) || + (opts[:block_name_include_match]&.present? && + block[:name]&.match(Regexp.new(opts[:block_name_include_match]))) || + (opts[:block_name_wrapper_match]&.present? && + block[:name]&.match(Regexp.new(opts[:block_name_wrapper_match])))) && + (block[:name]&.present? || block[:label]&.present?) + ) + end + + # Recursively fetches required code blocks for a given list of requirements. + # + # @param reqs [Array<String>] An array of requirements to start the recursion from. + # @return [Array<String>] An array of recursively required code block names. + # def recursively_required(reqs) return [] unless reqs rem = reqs memo = [] @@ -126,18 +186,15 @@ end end end if $PROGRAM_NAME == __FILE__ - # require 'bundler/setup' - # Bundler.require(:default) + require 'bundler/setup' + Bundler.require(:default) require 'minitest/autorun' - require_relative 'tap' - include Tap - module MarkdownExec class TestMDoc < Minitest::Test def setup @table = [ { name: 'block1', body: ['code for block1'], reqs: ['block2'] }, @@ -159,16 +216,18 @@ result_missing = @doc.get_block_by_name('missing_block') assert_equal({}, result_missing) end - def test_get_required_blocks - result = @doc.get_required_blocks('block3') + def test_collect_recursively_required_blocks + result = @doc.collect_recursively_required_blocks('block3') expected_result = [@table[0], @table[1], @table[2]] assert_equal expected_result, result - assert_raises(RuntimeError) { @doc.get_required_blocks('missing_block') } + assert_raises(RuntimeError) do + @doc.collect_recursively_required_blocks('missing_block') + end end def test_hide_menu_block_per_options opts = { hide_blocks_by_name: true, block_name_hidden_match: 'block1' } block = { name: 'block1' } @@ -186,9 +245,62 @@ result = @doc.recursively_required(['block3']) assert_equal %w[block3 block1 block2], result result_no_reqs = @doc.recursively_required(nil) assert_equal [], result_no_reqs + end + end + + class TestMDoc2 < Minitest::Test + # Mocking the @table object for testing + def setup + @table = [ + { name: '{wrap1}' }, + { name: '{wrap2-before}' }, + { name: '{wrap2}' }, + { name: '{wrap2-after}' }, + { name: '{wrap3-before}' }, + { name: '{wrap3}' }, + { name: '{wrap3-after}' } + ] + @mdoc = MDoc.new(@table) + end + + def test_collect_wrapped_blocks + # Test case 1: blocks with wraps + assert_equal(%w[{wrap1} a], + @mdoc.collect_wrapped_blocks( + [{ name: 'a', + wraps: ['{wrap1}'] }] + ).map do |block| + block[:name] + end) + + assert_equal(%w[{wrap2-before} {wrap2} b {wrap2-after}], + @mdoc.collect_wrapped_blocks( + [{ name: 'b', + wraps: ['{wrap2}'] }] + ).map do |block| + block[:name] + end) + + assert_equal(%w[{wrap2-before} {wrap2} {wrap3-before} {wrap3} c {wrap3-after} {wrap2-after}], + @mdoc.collect_wrapped_blocks( + [{ name: 'c', + wraps: %w[{wrap2} {wrap3}] }] + ).map { |block| block[:name] }) + + # Test case 2: blocks with no wraps + blocks = @mdoc.collect_wrapped_blocks([]) + assert_empty blocks + + # Test case 3: blocks with missing wraps + assert_equal( + %w[block4], + @mdoc.collect_wrapped_blocks([{ name: 'block4', wraps: ['wrap4'] }]).map do |block| + block[:name] + end + ) end end end end