Class: MarkdownExec::MDoc
Overview
MDoc represents an imported markdown document.
It provides methods to extract and manipulate specific sections of the document, such as code blocks. It also supports recursion to fetch related or dependent blocks.
Instance Attribute Summary collapse
-
#table ⇒ Object
readonly
Returns the value of attribute table.
Instance Method Summary collapse
- #collect_block_code_cann(fcb) ⇒ Object
- #collect_block_code_shell(fcb) ⇒ Object
- #collect_block_code_stdout(fcb) ⇒ Object
-
#collect_block_dependencies(anyname:) ⇒ Array<Hash>
Retrieves code blocks that are required by a specified code block.
-
#collect_dependencies(source, memo = {}) ⇒ Hash
Recursively collects dependencies of a given source.
-
#collect_recursively_required_code(anyname:, block_source:, label_body: true, label_format_above: nil, label_format_below: nil) ⇒ Array<String>
Collects recursively required code blocks and returns them as an array of strings.
- #collect_unique_names(hash) ⇒ Object
-
#collect_wrapped_blocks(blocks) ⇒ Array<Hash>
Retrieves code blocks that are wrapped wraps are applied from left to right e.g.
- #error_handler(name = '', opts = {}) ⇒ Object
-
#fcbs_per_options(opts = {}) ⇒ Array<Hash>
Retrieves code blocks based on the provided options.
-
#get_block_by_anyname(name, default = {}) ⇒ Hash
Retrieves a code block by its name.
-
#hide_menu_block_on_name(opts, block) ⇒ Boolean
Checks if a code block should be hidden based on the given options.
-
#initialize(table = []) ⇒ MDoc
constructor
Initializes an instance of MDoc with the given table of markdown sections.
-
#recursively_required(reqs) ⇒ Array<String>
Recursively fetches required code blocks for a given list of requirements.
-
#recursively_required_hash(source, memo = Hash.new([])) ⇒ Hash
Recursively fetches required code blocks for a given list of requirements.
- #select_elements_with_neighbor_conditions(array, last_selected_placeholder = nil, next_selected_placeholder = nil) ⇒ Object
Constructor Details
#initialize(table = []) ⇒ MDoc
Initializes an instance of MDoc with the given table of markdown sections.
24 25 26 27 |
# File 'lib/mdoc.rb', line 24 def initialize(table = []) @table = table # &bc '@table.count:',@table.count end |
Instance Attribute Details
#table ⇒ Object (readonly)
Returns the value of attribute table.
18 19 20 |
# File 'lib/mdoc.rb', line 18 def table @table end |
Instance Method Details
#collect_block_code_cann(fcb) ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/mdoc.rb', line 29 def collect_block_code_cann(fcb) body = fcb[:body].join("\n") xcall = fcb[:cann][1..-2] mstdin = xcall.match(/<(?<type>\$)?(?<name>[A-Za-z_\-.\w]+)/) mstdout = xcall.match(/>(?<type>\$)?(?<name>[A-Za-z_\-.\w]+)/) yqcmd = if mstdin[:type] "echo \"$#{mstdin[:name]}\" | yq '#{body}'" else "yq e '#{body}' '#{mstdin[:name]}'" end if mstdout[:type] "export #{mstdout[:name]}=$(#{yqcmd})" else "#{yqcmd} > '#{mstdout[:name]}'" end end |
#collect_block_code_shell(fcb) ⇒ Object
47 48 49 50 51 52 53 54 |
# File 'lib/mdoc.rb', line 47 def collect_block_code_shell(fcb) # write named variables to block at top of script # fcb[:body].join(' ').split.compact.map do |key| ### format(opts[:block_type_port_set_format], { key: key, value: ENV.fetch(key, nil) }) "key: #{key}, value: #{ENV.fetch(key, nil)}" end end |
#collect_block_code_stdout(fcb) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/mdoc.rb', line 56 def collect_block_code_stdout(fcb) stdout = fcb[:stdout] body = fcb[:body].join("\n") if stdout[:type] %(export #{stdout[:name]}=$(cat <<"EOF"\n#{body}\nEOF\n)) else "cat > '#{stdout[:name]}' <<\"EOF\"\n" \ "#{body}\n" \ "EOF\n" end end |
#collect_block_dependencies(anyname:) ⇒ Array<Hash>
Retrieves code blocks that are required by a specified code block.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/mdoc.rb', line 73 def collect_block_dependencies(anyname:) name_block = get_block_by_anyname(anyname) if name_block.nil? || name_block.keys.empty? raise "Named code block `#{anyname}` not found. (@#{__LINE__})" end nickname = name_block[:nickname] || name_block[:oname] dependencies = collect_dependencies(nickname) # &bc 'dependencies.count:',dependencies.count all_dependency_names = collect_unique_names(dependencies).push(nickname).uniq # &bc 'all_dependency_names.count:',all_dependency_names.count # select non-chrome blocks in order of appearance in source documents # blocks = @table.select do |fcb| !fcb.fetch(:chrome, false) && all_dependency_names.include?(fcb.fetch(:nickname, nil) || fcb.fetch(:oname)) end # &bc 'blocks.count:',blocks.count ## add cann key to blocks, calc unmet_dependencies # unmet_dependencies = all_dependency_names.dup blocks = blocks.map do |fcb| unmet_dependencies.delete(fcb[:nickname] || fcb[:oname]) # may not exist if block name is duplicated if (call = fcb[:call]) [get_block_by_anyname("[#{call.match(/^%\((\S+) |\)/)[1]}]") .merge({ cann: call })] else [] end + [fcb] end.flatten(1) # &bc 'unmet_dependencies.count:',unmet_dependencies.count { all_dependency_names: all_dependency_names, blocks: blocks, dependencies: dependencies, unmet_dependencies: unmet_dependencies } end |
#collect_dependencies(source, memo = {}) ⇒ Hash
Recursively collects dependencies of a given source.
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/mdoc.rb', line 309 def collect_dependencies(source, memo = {}) return memo unless source if (block = get_block_by_anyname(source)).nil? || block.keys.empty? return memo if true raise "Named code block `#{source}` not found. (@#{__LINE__})" end return memo unless block[:reqs] memo[source] = block[:reqs] block[:reqs].each { |req| collect_dependencies(req, memo) unless memo.key?(req) } memo end |
#collect_recursively_required_code(anyname:, block_source:, label_body: true, label_format_above: nil, label_format_below: nil) ⇒ Array<String>
Collects recursively required code blocks and returns them as an array of strings.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/mdoc.rb', line 119 def collect_recursively_required_code(anyname:, block_source:, label_body: true, label_format_above: nil, label_format_below: nil) block_search = collect_block_dependencies(anyname: anyname) if block_search[:blocks] blocks = collect_wrapped_blocks(block_search[:blocks]) # &bc 'blocks.count:',blocks.count block_search.merge( { block_names: blocks.map { |block| block[:nickname] || block[:oname] }, code: blocks.map do |fcb| if fcb[:cann] collect_block_code_cann(fcb) elsif fcb[:stdout] collect_block_code_stdout(fcb) elsif [BlockType::LINK, BlockType::OPTS, BlockType::VARS].include? fcb[:shell] nil elsif fcb[:shell] == BlockType::PORT collect_block_code_shell(fcb) elsif label_body block_name_for_bash_comment = (fcb[:nickname] || fcb[:oname]).gsub(/\s+/, '_') [label_format_above && format(label_format_above, block_source.merge({ block_name: block_name_for_bash_comment }))] + fcb[:body] + [label_format_below && format(label_format_below, block_source.merge({ block_name: block_name_for_bash_comment }))] else # raw body fcb[:body] end end.compact.flatten(1).compact } ) else block_search.merge({ block_names: [], code: [] }) end rescue StandardError error_handler('collect_recursively_required_code') end |
#collect_unique_names(hash) ⇒ Object
157 158 159 |
# File 'lib/mdoc.rb', line 157 def collect_unique_names(hash) hash.values.flatten.uniq end |
#collect_wrapped_blocks(blocks) ⇒ Array<Hash>
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
167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/mdoc.rb', line 167 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.oname } end.flatten(1) + [block] + (block[:wraps] || []).reverse.map do |wrap| wrap_after = wrap.sub('}', '-after}') ### hardcoded wrap name @table.select { |fcb| fcb.oname == wrap_after } end.flatten(1) end.flatten(1).compact end |
#error_handler(name = '', opts = {}) ⇒ Object
181 182 183 184 185 186 |
# File 'lib/mdoc.rb', line 181 def error_handler(name = '', opts = {}) Exceptions.error_handler( "MDoc.#{name} -- #{$!}", opts ) end |
#fcbs_per_options(opts = {}) ⇒ Array<Hash>
Retrieves code blocks based on the provided options.
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/mdoc.rb', line 193 def (opts = {}) = opts.merge(block_name_hidden_match: nil) selrows = @table.select do |fcb_title_groups| Filter.fcb_select? , fcb_title_groups end ### hide rows correctly if ![:menu_include_imported_blocks] selrows = selrows.reject do |block| block.fetch(:depth, 0).positive? end end if opts[:hide_blocks_by_name] selrows = selrows.reject do |block| opts, block end end # remove # . empty chrome between code; edges are same as blanks # select_elements_with_neighbor_conditions(selrows) do |prev_element, current, next_element| !(current[:chrome] && !current[:oname].present?) || !(!prev_element.nil? && prev_element[:shell].present? && !next_element.nil? && next_element[:shell].present?) end end |
#get_block_by_anyname(name, default = {}) ⇒ Hash
Retrieves a code block by its name.
227 228 229 230 231 232 |
# File 'lib/mdoc.rb', line 227 def get_block_by_anyname(name, default = {}) @table.select do |fcb| fcb.fetch(:nickname, '') == name || fcb.fetch(:dname, '') == name || fcb.fetch(:oname, '') == name end.fetch(0, default) end |
#hide_menu_block_on_name(opts, block) ⇒ Boolean
Checks if a code block should be hidden based on the given options.
:reek:UtilityFunction
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/mdoc.rb', line 241 def (opts, block) if block.fetch(:chrome, false) false else (opts[:hide_blocks_by_name] && ((opts[:block_name_hidden_match]&.present? && block.oname&.match(Regexp.new(opts[:block_name_hidden_match]))) || (opts[:block_name_include_match]&.present? && block.oname&.match(Regexp.new(opts[:block_name_include_match]))) || (opts[:block_name_wrapper_match]&.present? && block.oname&.match(Regexp.new(opts[:block_name_wrapper_match])))) && (block.oname&.present? || block[:label]&.present?) ) end end |
#recursively_required(reqs) ⇒ Array<String>
Recursively fetches required code blocks for a given list of requirements.
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/mdoc.rb', line 262 def recursively_required(reqs) return [] unless reqs rem = reqs memo = [] while rem && rem.count.positive? rem = rem.map do |req| next if memo.include? req memo += [req] get_block_by_anyname(req).fetch(:reqs, []) end .compact .flatten(1) end memo end |
#recursively_required_hash(source, memo = Hash.new([])) ⇒ Hash
Recursively fetches required code blocks for a given list of requirements.
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/mdoc.rb', line 285 def recursively_required_hash(source, memo = Hash.new([])) return memo unless source return memo if memo.keys.include? source block = get_block_by_anyname(source) if block.nil? || block.keys.empty? raise "Named code block `#{source}` not found. (@#{__LINE__})" end memo[source] = block[:reqs] return memo unless memo[source]&.count&.positive? memo[source].each do |req| next if memo.keys.include? req recursively_required_hash(req, memo) end memo end |
#select_elements_with_neighbor_conditions(array, last_selected_placeholder = nil, next_selected_placeholder = nil) ⇒ Object
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'lib/mdoc.rb', line 327 def select_elements_with_neighbor_conditions(array, last_selected_placeholder = nil, next_selected_placeholder = nil) selected_elements = [] last_selected = last_selected_placeholder array.each_with_index do |current, index| next_element = if index < array.size - 1 array[index + 1] else next_selected_placeholder end if yield(last_selected, current, next_element) selected_elements << current last_selected = current end end selected_elements end |