#!/usr/bin/env ruby # frozen_string_literal: true # encoding=utf-8 require 'digest' require_relative 'namer' # require_relative 'poly' module MarkdownExec class Error < StandardError; end # 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 FCB def initialize(options = {}) @attrs = { body: nil, call: nil, headings: [], dname: nil, indent: '', name: nil, nickname: nil, oname: nil, reqs: [], shell: '', title: '', random: Random.new.rand, text: nil # displayable in menu }.merge(options) end def title=(value) @attrs[:title] = value end # Derives a title from the body of an FCB object. # @param fcb [Object] The FCB object whose title is to be derived. # @return [String] The derived title. 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 format_multiline_body_as_title(body_content) end end private # 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] 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 # :reek:ManualDispatch 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 StandardError, error raise err # Here, we simply propagate the original error instead of wrapping it in a StandardError. end public def respond_to_missing?(method_name, include_private = false) @attrs.key?(method_name.to_sym) || super end def to_h @attrs.to_h end def to_yaml @attrs.to_yaml end end end if $PROGRAM_NAME == __FILE__ require 'bundler/setup' Bundler.require(:default) require 'minitest/autorun' require 'yaml' class FCBTest < Minitest::Test def setup @fcb_data = { body: 'Sample body', call: 'Sample call', headings: %w[Header1 Header2], dname: 'Sample name', indent: '', nickname: nil, name: 'Sample name', oname: 'Sample name', reqs: %w[req1 req2], shell: 'bash', text: 'Sample Text', title: 'Sample Title' } @fcb = MarkdownExec::FCB.new(@fcb_data) end def test_initialization_with_correct_data assert_equal 'Sample body', @fcb.body assert_equal %w[Header1 Header2], @fcb.headings end def test_to_h_method assert_equal @fcb_data.merge({ random: @fcb.random }), @fcb.to_h end def test_to_yaml_method assert_equal YAML.load(@fcb_data.merge({ random: @fcb.random }).to_yaml), YAML.load(@fcb.to_yaml) end def test_method_missing_getter assert_equal 'Sample Title', @fcb.title end def test_method_missing_setter @fcb.title = 'New Title' assert_equal 'New Title', @fcb.title end # 2023-10-09 does not trigger error; treats as option name # # def test_method_missing_with_unknown_method # assert_raises(NoMethodError) { @fcb.unknown_method } # end end end # #!/usr/bin/env ruby # # frozen_string_literal: true # # encoding=utf-8 # require 'digest' # require_relative 'poly' # # require 'ostruct' # module MarkdownExec # class Error < StandardError; end # # 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 FCB # def initialize(options = {}) # # @attrs = ({ # @attrs = Poly.new({ # body: nil, # call: nil, # headings: [], # dname: nil, # indent: '', # name: nil, # nickname: nil, # oname: nil, # reqs: [], # shell: '', # title: '', # random: Random.new.rand, # text: nil # displayable in menu # }.merge(options)) # end # def is?(name) # (save_name == name).tap{|ret|pp [__LINE__,"is?(#{name})",'->',ret]} # end # # fenced_name: text from code # # save_name: for file system # # logical_name: used for requires, not displayed # # menu_name: displayed in menu # # used for requires, not displayed # def logical_name # (@attrs.fetch(:nickname, nil) || @attrs.fetch(:oname, nil)).tap{|ret|pp [__LINE__,"logical_name()",'->',ret]} # end # # displayed in menu # def menu_name # displayed in menu # @attrs.fetch(:nickname, nil).tap{|ret|pp [__LINE__,"menu_name()",'->',ret]} # end # # for file system # # block name in commands and documents # def save_name(id_len: 4, max_len: 48) # full = @attrs.fetch(:nickname, nil) || @attrs.fetch(:oname, nil) # trimmed = if full && full[max_len] # r = rand((10**(id_len - 1) + 1)..10**id_len).to_s # dig = Digest::MD5.hexdigest(full)[0, id_len] # full[0..max_len - id_len] + dig # else # full # end # trimmed&.to_blockname.tap{|ret|pp [__LINE__,"save_name()",'->',ret]} # end # def title=(value) # @attrs[:title] = value # end # # Derives a title from the body of an FCB object. # # @param fcb [Object] The FCB object whose title is to be derived. # # @return [String] The derived title. # 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 # format_multiline_body_as_title(body_content) # end # end # private # # 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] 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 # # :reek:ManualDispatch # 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 StandardError, error # raise err # Here, we simply propagate the original error instead of wrapping it in a StandardError. # end # public # def respond_to_missing?(method_name, include_private = false) # @attrs.key?(method_name.to_sym) || super # end # def to_h # @attrs.to_h # end # def to_yaml # @attrs.to_yaml # end # end # end # if $PROGRAM_NAME == __FILE__ # require 'bundler/setup' # Bundler.require(:default) # require 'minitest/autorun' # require 'yaml' # class FCBTest < Minitest::Test # def setup # @fcb_data = { # body: 'Sample body', # call: 'Sample call', # headings: %w[Header1 Header2], # dname: 'Sample name', # indent: '', # nickname: nil, # name: 'Sample name', # oname: 'Sample name', # reqs: %w[req1 req2], # shell: 'bash', # text: 'Sample Text', # title: 'Sample Title' # } # @fcb = MarkdownExec::FCB.new(@fcb_data) # end # def test_initialization_with_correct_data # assert_equal 'Sample body', @fcb.body # assert_equal %w[Header1 Header2], @fcb.headings # end # def test_to_h_method # assert_equal @fcb_data.merge({ random: @fcb.random }), @fcb.to_h # end # def test_to_yaml_method # assert_equal YAML.load(@fcb_data.merge({ random: @fcb.random }).to_yaml), # YAML.load(@fcb.to_yaml) # end # def test_method_missing_getter # assert_equal 'Sample Title', @fcb.title # end # def test_method_missing_setter # @fcb.title = 'New Title' # assert_equal 'New Title', @fcb.title # end # # 2023-10-09 does not trigger error; treats as option name # # # # def test_method_missing_with_unknown_method # # assert_raises(NoMethodError) { @fcb.unknown_method } # # end # end # end