Class: MarkdownExec::FCB
Overview
Fenced Code Block (FCB)
This class represents a fenced code block in a markdown document. It allows for setting and getting attributes related to the code block, such as body, call, headings, and more.
Class Method Summary collapse
- .act_source(export) ⇒ Object
-
.format_multiline_body_as_title(body_lines) ⇒ String
Formats multiline body content as a title string.
- .init_source(export) ⇒ Object
- .is_allow?(export) ⇒ Boolean
- .is_echo?(export) ⇒ Boolean
- .is_edit?(export) ⇒ Boolean
- .is_exec?(export) ⇒ Boolean
- .pub_name(attrs, **kwargs) ⇒ Object
Instance Method Summary collapse
- #append_block_line(line) ⇒ Object
- #code_name_exp?(regexp) ⇒ Boolean
- #code_name_included?(*names) ⇒ Boolean
- #delete_key(key) ⇒ Object
-
#delete_matching_name!(dependencies) ⇒ Object
Removes and returns the first matching name from dependencies collection Checks nickname, oname, pub_name and s2title 2024-08-04 match oname for long block names 2024-08-04 match nickname may not exist if block name is duplicated.
-
#derive_title_from_body ⇒ String
Derives a title from the body of an FCB object.
-
#expand_variables_in_attributes!(pattern, replacements) ⇒ void
Expand variables in attributes.
-
#for_menu!(appopts:, block_calls_scan:, block_name_match:, block_name_nick_match:, id: '', menu_format:, prompt:, table_center:) ⇒ Object
Processes a block to generate its summary, modifying its attributes based on various matching criteria.
-
#initialize(options = {}) ⇒ FCB
constructor
A new instance of FCB.
- #is_allow? ⇒ Boolean
-
#is_dependency_of?(dependency_names) ⇒ Boolean
:reek:ManualDispatch 2024-08-04 match nickname.
- #is_disabled? ⇒ Boolean
- #is_echo? ⇒ Boolean
- #is_edit? ⇒ Boolean
- #is_enabled? ⇒ Boolean
- #is_exec? ⇒ Boolean
- #is_named?(name) ⇒ Boolean
-
#is_split? ⇒ Boolean
true if this is a line split block.
-
#is_split_displayed?(opts) ⇒ Boolean
true if this block displays its split body names and nicknames are displayed instead of the body ux blocks display a single line for the named variable split blocks are: opts, shell, vars.
-
#is_split_first? ⇒ Boolean
true if this is the first line in a split block.
-
#is_split_rest? ⇒ Boolean
true if this is the second or later line in a split block.
-
#method_missing(method, *args, &block) ⇒ Object
:reek:ManualDispatch.
- #name_in_menu!(indented_multi_line) ⇒ Object
-
#option_to_decorate_ux_block ⇒ Object
calc the decoration sybol for the current block.
- #option_to_format_ux_block(export) ⇒ Object
- #pub_name(**kwargs) ⇒ Object
- #respond_to_missing?(method_name, include_private = false) ⇒ Boolean
- #shell ⇒ Object
- #shell=(value) ⇒ Object
- #to_h ⇒ Object
- #to_yaml ⇒ Object
- #type ⇒ Object
- #type=(value) ⇒ Object
Constructor Details
#initialize(options = {}) ⇒ FCB
Returns a new instance of FCB.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/fcb.rb', line 56 def initialize( = {}) @attrs = { block: nil, body: nil, call: nil, dname: nil, headings: [], id: object_id, indent: '', name: nil, nickname: nil, oname: nil, random: Random.new.rand, reqs: [], shell: '', start_line: nil, text: nil, # displayable in menu title: '', type: '' }.merge() end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
:reek:ManualDispatch
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
# File 'lib/fcb.rb', line 374 def method_missing(method, *args, &block) method_name = method.to_s if @attrs.respond_to?(method_name) @attrs.send(method_name, *args, &block) elsif method_name[-1] == '=' @attrs[method_name.chop.to_sym] = args[0] else @attrs[method_name.to_sym] end rescue StandardError => err warn("ERROR ** FCB.method_missing(method: #{method_name}," \ " *args: #{args.inspect}, &block)") warn err.inspect warn(caller[0..4]) raise err end |
Class Method Details
.act_source(export) ⇒ Object
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/fcb.rb', line 256 def self.act_source(export) # If `false`, the UX block is not activated. # If one of `:allow`, `:echo`, `:edit`, or `:exec` is specified, # the value is calculated or the user is prompted. # If not present, the default value is `:edit`. if export.act.nil? export.act = if export.init.to_s == 'false' # if export.allow.present? if FCB.is_allow?(export) UxActSource::ALLOW # elsif export.echo.present? elsif FCB.is_echo?(export) UxActSource::ECHO # elsif export.edit.present? elsif FCB.is_edit?(export) UxActSource::EDIT # elsif export.exec.present? elsif FCB.is_exec?(export) UxActSource::EXEC else UxActSource::EDIT end elsif FCB.is_allow?(export) UxActSource::ALLOW else UxActSource::EDIT end end export.act end |
.format_multiline_body_as_title(body_lines) ⇒ String
Formats multiline body content as a title string. indents all but first line with two spaces so it displays correctly in menu.
218 219 220 221 222 |
# File 'lib/fcb.rb', line 218 def self.format_multiline_body_as_title(body_lines) body_lines.map.with_index do |line, index| index.zero? ? line : " #{line}" end.join("\n") end |
.init_source(export) ⇒ Object
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
# File 'lib/fcb.rb', line 288 def self.init_source(export) # If `false`, there is no initial value set. # If a string, it is the initial value of the object variable. # Otherwise, if one of `:allow`, `:echo`, or `:exec` is specified, # the value is the output of the `echo` or `exec` evaluation # or the first allowed value. # If not present, the default value is whichever of # `:allow`, `:default`, `:echo`, or `:exec` is present. if export.init.nil? export.init = case when FCB.is_allow?(export) UxActSource::ALLOW when export.default.present? UxActSource::DEFAULT # when export.echo.present? when FCB.is_echo?(export) UxActSource::ECHO # when export.exec.present? when FCB.is_exec?(export) UxActSource::EXEC else UxActSource::FALSE end end export.init end |
.is_allow?(export) ⇒ Boolean
224 225 226 |
# File 'lib/fcb.rb', line 224 def self.is_allow?(export) export&.allow&.present? end |
.is_echo?(export) ⇒ Boolean
232 233 234 |
# File 'lib/fcb.rb', line 232 def self.is_echo?(export) export&.echo&.present? end |
.is_edit?(export) ⇒ Boolean
240 241 242 |
# File 'lib/fcb.rb', line 240 def self.is_edit?(export) export&.edit&.present? end |
.is_exec?(export) ⇒ Boolean
248 249 250 |
# File 'lib/fcb.rb', line 248 def self.is_exec?(export) export&.exec&.present? end |
.pub_name(attrs, **kwargs) ⇒ Object
86 87 88 89 |
# File 'lib/fcb.rb', line 86 def self.pub_name(attrs, **kwargs) full = attrs.fetch(:nickname, nil) || attrs.fetch(:oname, nil) full&.to_s&.pub_name(**kwargs) end |
Instance Method Details
#append_block_line(line) ⇒ Object
78 79 80 |
# File 'lib/fcb.rb', line 78 def append_block_line(line) @attrs[:block].push line end |
#code_name_exp?(regexp) ⇒ Boolean
95 96 97 |
# File 'lib/fcb.rb', line 95 def code_name_exp?(regexp) Regexp.new(regexp) =~ @attrs[:oname] end |
#code_name_included?(*names) ⇒ Boolean
91 92 93 |
# File 'lib/fcb.rb', line 91 def code_name_included?(*names) names.include?(@attrs[:oname]) end |
#delete_key(key) ⇒ Object
99 100 101 |
# File 'lib/fcb.rb', line 99 def delete_key(key) @attrs.delete(key) end |
#delete_matching_name!(dependencies) ⇒ Object
Removes and returns the first matching name from dependencies collection Checks nickname, oname, pub_name and s2title 2024-08-04 match oname for long block names 2024-08-04 match nickname may not exist if block name is duplicated
108 109 110 111 112 113 114 115 |
# File 'lib/fcb.rb', line 108 def delete_matching_name!(dependencies) dependencies.delete(@attrs[:id]) || dependencies.delete(@attrs[:dname]) || dependencies.delete(@attrs[:nickname]) || dependencies.delete(@attrs[:oname]) || dependencies.delete(@attrs.pub_name) || dependencies.delete(@attrs[:s2title]) end |
#derive_title_from_body ⇒ String
Derives a title from the body of an FCB object.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/fcb.rb', line 120 def derive_title_from_body unless (body_content = @attrs[:body]) # empty body -> empty title @attrs[:title] = '' return end # body -> title @attrs[:title] = if body_content.count == 1 body_content.first else FCB.format_multiline_body_as_title(body_content) end end |
#expand_variables_in_attributes!(pattern, replacements) ⇒ void
This method returns an undefined value.
Expand variables in attributes
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 |
# File 'lib/fcb.rb', line 480 def (pattern, replacements) @attrs[:raw_dname] ||= @attrs[:dname] @attrs[:dname] = @attrs[:dname]&.gsub(pattern) do |match| replacements[match] end @attrs[:raw_s0printable] ||= @attrs[:s0printable] @attrs[:s0printable] = @attrs[:s0printable]&.gsub(pattern) do |match| replacements[match] end @attrs[:raw_s1decorated] ||= @attrs[:s1decorated] @attrs[:s1decorated] = @attrs[:s1decorated]&.gsub(pattern) do |match| replacements[match] end # Replace variables in each line of `body` if `body` is present return unless @attrs[:body] # save body for YAML and re-interpretation @attrs[:raw_body] ||= @attrs[:body] @attrs[:body] = @attrs[:body]&.map do |line| if line.empty? line else line.gsub(pattern) { |match| replacements[match] } end end end |
#for_menu!(appopts:, block_calls_scan:, block_name_match:, block_name_nick_match:, id: '', menu_format:, prompt:, table_center:) ⇒ Object
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.
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/fcb.rb', line 143 def ( appopts:, block_calls_scan:, block_name_match:, block_name_nick_match:, id: '', menu_format:, prompt:, table_center: ) call = @attrs[:call] = @attrs[:start_line]&.match( Regexp.new(block_calls_scan) )&.fetch(1, nil) titlexcall = call ? @attrs[:title].sub("%#{call}", '') : @attrs[:title] oname = if is_split? @attrs[:text] elsif block_name_nick_match.present? && @attrs[:oname] =~ Regexp.new(block_name_nick_match) @attrs[:nickname] = $~[0] derive_title_from_body else bm = NamedCaptureExtractor.extract_named_groups( titlexcall, block_name_match ) bm && bm[1] ? bm[:title] : titlexcall end @attrs[:title] = @attrs[:oname] = oname @attrs[:id] = id if @attrs[:type] == BlockType::UX begin case data = YAML.load(@attrs[:body].join("\n")) when Hash export = parse_yaml_of_ux_block( data, prompt: prompt ) if !export. || export..empty? format_symbol = option_to_format_ux_block(export) export. = appopts[format_symbol] if !export. || export..empty? export. = appopts[:menu_ux_row_format] end end @attrs[:oname] = oname = format(export., export.to_h) @attrs[:center] = table_center @attrs[:readonly] = export.readonly else # triggered by an empty or non-YAML block return NullResult.new(message: 'Invalid YAML', data: data) end rescue StandardError wwe 'Error processing block for menu', 'body:', @attrs[:body], 'data', data, 'export', export end end @attrs[:dname] = HashDelegator.indent_all_lines( # yield the text and option name for the color (yield oname, option_to_decorate_ux_block), @attrs[:indent] ) SuccessResult.instance end |
#is_allow? ⇒ Boolean
228 229 230 |
# File 'lib/fcb.rb', line 228 def is_allow? FCB.is_allow?(export) end |
#is_dependency_of?(dependency_names) ⇒ Boolean
:reek:ManualDispatch 2024-08-04 match nickname
318 319 320 321 322 323 324 325 |
# File 'lib/fcb.rb', line 318 def is_dependency_of?(dependency_names) dependency_names.include?(@attrs[:id]) || dependency_names.include?(@attrs[:dname]) || dependency_names.include?(@attrs[:nickname]) || dependency_names.include?(@attrs[:oname]) || dependency_names.include?(@attrs.pub_name) || dependency_names.include?(@attrs[:s2title]) end |
#is_disabled? ⇒ Boolean
327 328 329 |
# File 'lib/fcb.rb', line 327 def is_disabled? @attrs[:disabled] == TtyMenu::DISABLE end |
#is_echo? ⇒ Boolean
236 237 238 |
# File 'lib/fcb.rb', line 236 def is_echo? FCB.is_echo?(export) end |
#is_edit? ⇒ Boolean
244 245 246 |
# File 'lib/fcb.rb', line 244 def is_edit? FCB.is_edit?(export) end |
#is_enabled? ⇒ Boolean
331 332 333 |
# File 'lib/fcb.rb', line 331 def is_enabled? !is_disabled? end |
#is_exec? ⇒ Boolean
252 253 254 |
# File 'lib/fcb.rb', line 252 def is_exec? FCB.is_exec?(export) end |
#is_named?(name) ⇒ Boolean
335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'lib/fcb.rb', line 335 def is_named?(name) if /^ItrBlk/.match(name) @attrs[:id] == name else @attrs[:id] == name || @attrs[:dname] == name || @attrs[:nickname] == name || @attrs[:oname] == name || @attrs.pub_name == name || @attrs[:s2title] == name end end |
#is_split? ⇒ Boolean
true if this is a line split block
349 350 351 |
# File 'lib/fcb.rb', line 349 def is_split? is_split_first? || is_split_rest? end |
#is_split_displayed?(opts) ⇒ Boolean
true if this block displays its split body names and nicknames are displayed instead of the body ux blocks display a single line for the named variable split blocks are: opts, shell, vars
357 358 359 360 361 |
# File 'lib/fcb.rb', line 357 def is_split_displayed?(opts) @attrs[:type] != BlockType::UX && !(@attrs[:start_line] =~ Regexp.new(opts[:block_name_nick_match]) || @attrs[:start_line] =~ Regexp.new(opts[:block_name_match])) end |
#is_split_first? ⇒ Boolean
true if this is the first line in a split block
364 365 366 |
# File 'lib/fcb.rb', line 364 def is_split_first? @attrs.fetch(:is_split_first, false) end |
#is_split_rest? ⇒ Boolean
true if this is the second or later line in a split block
369 370 371 |
# File 'lib/fcb.rb', line 369 def is_split_rest? @attrs.fetch(:is_split_rest, false) end |
#name_in_menu!(indented_multi_line) ⇒ Object
391 392 393 394 395 396 397 398 399 400 |
# File 'lib/fcb.rb', line 391 def (indented_multi_line) # Indent has been extracted from the first line, # remove indent from the remaining lines. @attrs[:dname] = if @attrs[:indent].empty? indented_multi_line else indented_multi_line.gsub("\n#{@attrs[:indent]}", "\n") end end |
#option_to_decorate_ux_block ⇒ Object
calc the decoration sybol for the current block
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 |
# File 'lib/fcb.rb', line 403 def option_to_decorate_ux_block symbol_or_hash = BLOCK_TYPE_COLOR_OPTIONS[@attrs[:type]] || BLOCK_TYPE_COLOR_OPTIONS[true] if @attrs[:type] == BlockType::UX # only UX blocks accept a symbol or a hash if symbol_or_hash.is_a? Hash # default to the first symbol symbol = symbol_or_hash.first.last symbol_or_hash.each_key do |key| if key == true symbol = symbol_or_hash[key] break elsif symbol_or_hash[key].present? && send(key) symbol = symbol_or_hash[key] break end end symbol else # only symbol symbol_or_hash end else # only symbol symbol_or_hash end end |
#option_to_format_ux_block(export) ⇒ Object
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'lib/fcb.rb', line 430 def option_to_format_ux_block(export) if export.readonly :menu_ux_row_format_readonly else case FCB.act_source(export) when UxActSource::ALLOW :menu_ux_row_format_allow when UxActSource::ECHO :menu_ux_row_format_echo when UxActSource::EDIT :menu_ux_row_format_edit when UxActSource::EXEC :menu_ux_row_format_exec else # this UX block does not have a format, treat as editable :menu_ux_row_format_edit end end end |
#pub_name(**kwargs) ⇒ Object
82 83 84 |
# File 'lib/fcb.rb', line 82 def pub_name(**kwargs) self.class.pub_name(@attrs, **kwargs) end |
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
450 451 452 |
# File 'lib/fcb.rb', line 450 def respond_to_missing?(method_name, include_private = false) @attrs.key?(method_name.to_sym) || super end |
#shell ⇒ Object
454 455 456 |
# File 'lib/fcb.rb', line 454 def shell @attrs[:shell] end |
#shell=(value) ⇒ Object
458 459 460 |
# File 'lib/fcb.rb', line 458 def shell=(value) @attrs[:shell] = value end |
#to_h ⇒ Object
470 471 472 |
# File 'lib/fcb.rb', line 470 def to_h @attrs.to_h end |
#to_yaml ⇒ Object
474 475 476 |
# File 'lib/fcb.rb', line 474 def to_yaml @attrs.to_yaml end |
#type ⇒ Object
462 463 464 |
# File 'lib/fcb.rb', line 462 def type @attrs[:type] end |
#type=(value) ⇒ Object
466 467 468 |
# File 'lib/fcb.rb', line 466 def type=(value) @attrs[:type] = value end |