class MarkdownExec::MarkParse
:reek:TooManyMethods ### temp
:reek:TooManyInstanceVariables ### temp
:reek:MissingSafeMethod { exclude: [ read_configuration_file! ] }
rubocop:enable Layout/LineLength
:reek:DuplicateMethodCall { allow_calls: [‘block’, ‘item’, ‘lm’, ‘opts’, ‘option’, ‘@options’, ‘required_blocks’] }
#
def arguments_for_mde(argv = ARGV)
arguments after ARGV_SEP are passed to the generatede script
return arguments before ARGV_SEP
def arguments_for_mde(argv = ARGV) case ind = argv.find_index(ARGV_SEP) when nil argv when 0 [] else argv[0..ind - 1] end end
def base_options
options necessary to start, parse input, defaults for cli options
#
def base_options menu_iter do |item| next unless item[:opt_name].present? item_default = item[:default] value = if item_default.nil? item_default else env_str(item[:env_var], default: OptionValue.for_hash(item_default)) end [item[:opt_name], item[:proccode] ? item[:proccode].call(value) : value] end.compact.to_h end
def calculated_options
def calculated_options { bash: true, # bash block parsing in get_block_summary() saved_script_filename: nil # calculated } end
def determine_filename(specified_filename: nil, specified_folder: nil, default_filename: nil,
# Determines the correct filename to use for searching files
def determine_filename(specified_filename: nil, specified_folder: nil, default_filename: nil, default_folder: nil, filetree: nil) if specified_filename&.present? return specified_filename if specified_filename.start_with?('/') File.join(specified_folder || default_folder, specified_filename) elsif specified_folder&.present? File.join(specified_folder, filetree ? @options[:md_filename_match] : @options[:md_filename_glob]) else File.join(default_folder, default_filename) end end
def error_handler(name = '', opts = {})
def error_handler(name = '', opts = {}) Exceptions.error_handler( "CachedNestedFileReader.#{name} -- #{$!}", opts ) end
def execute_block_logic(files)
def execute_block_logic(files) @options[:filename] = select_document_if_multiple(files) @options.document_menu_loop end
def execute_block_with_error_handling
# Executes the block specified in the options
def execute_block_with_error_handling finalize_cli_argument_processing execute_code_block_based_on_options(@options) rescue FileMissingError warn "File missing: #{$!}" rescue StandardError error_handler('execute_block_with_error_handling') end
def execute_code_block_based_on_options(options)
def execute_code_block_based_on_options(options) options = calculated_options.merge(options) update_options(options, over: false) simple_commands = { doc_glob: -> { @fout.fout options[:md_filename_glob] }, # list_blocks: -> { list_blocks }, list_default_env: -> { @fout.fout_list list_default_env }, list_default_yaml: -> { @fout.fout_list list_default_yaml }, list_docs: -> { @fout.fout_list files }, list_recent_output: -> { @fout.fout_list list_recent_output( @options[:saved_stdout_folder], @options[:saved_stdout_glob], @options[:list_count] ) }, list_recent_scripts: -> { @fout.fout_list list_recent_scripts( options[:saved_script_folder], options[:saved_script_glob], options[:list_count] ) }, pwd: -> { @fout.fout File.expand_path('..', __dir__) }, run_last_script: -> { run_last_script }, select_recent_output: -> { select_recent_output }, select_recent_script: -> { select_recent_script }, tab_completions: -> { @fout.fout tab_completions }, menu_export: -> { @fout.fout menu_export } } return if execute_simple_commands(simple_commands) files = opts_prepare_file_list(options) execute_block_logic(files) return unless @options[:output_saved_script_filename] @fout.fout "script_block_name: #{@options.run_state.script_block_name}" @fout.fout "s_save_filespec: #{@options.run_state.saved_filespec}" rescue StandardError error_handler('execute_code_block_based_on_options') end
def execute_simple_commands(simple_commands)
def execute_simple_commands(simple_commands) simple_commands.each_key do |key| if @options[key] simple_commands[key].call return true end end false end
def finalize_cli_argument_processing(rest = @rest)
# post-parse options configuration
def finalize_cli_argument_processing(rest = @rest) ## position 0: file or folder (optional) # if (pos = rest.shift)&.present? if Dir.exist?(pos) @options[:path] = pos elsif File.exist?(pos) @options[:filename] = pos elsif @options[:default_find_select_open] find_value(pos, execute_chosen_found: true) else raise FileMissingError, pos, caller end end ## position 1: block name (optional) # @options[:block_name] = nil @options[:input_cli_rest] = @rest rescue FileMissingError warn_format('finalize_cli_argument_processing', "File missing -- #{$!}", { abort: true }) exit 1 rescue StandardError error_handler('finalize_cli_argument_processing') end
def find_value(value, execute_chosen_found: false)
def find_value(value, execute_chosen_found: false) find_path = @options[:find_path].present? ? @options[:find_path] : @options[:path] @fout.fout 'Searching in: ' \ "#{HashDelegator.new(@options).string_send_color(find_path, :menu_chrome_color)}" searcher = SearchResultsReport.new(value, [find_path]) file_names = searcher.file_names(options, value) file_contents = searcher.file_contents(options, value) directory_names = searcher.directory_names(options, value) ### search in file contents (block names, chrome, or text) [file_contents, directory_names, file_names].each do |data| @fout.fout "In #{data[:section_title]}" if data[:section_title] next unless data[:formatted_text] data[:formatted_text].each do |fi| @fout.fout fi[:header] if fi[:header] @fout.fout fi[:content] if fi[:content] end end return { exit: true } unless execute_chosen_found ## pick a document to open # files = directory_names[:data].map do |dn| find_files('*', [dn], exclude_dirs: true) end.flatten(1) choices = \ [{ disabled: '', name: "in #{file_names[:section_title]}".cyan }] \ + file_names[:data] \ + [{ disabled: '', name: "in #{directory_names[:section_title]}".cyan }] \ + files \ + [{ disabled: '', name: "in #{file_contents[:section_title]}".cyan }] \ + file_contents[:data] @options[:filename] = select_document_if_multiple(choices) { exit: false } end
def initialize(options = {})
def initialize(options = {}) @option_parser = nil @options = HashDelegator.new(options) @fout = FOut.new(@delegate_object) end
def initialize_and_parse_cli_options
# Sets up the options and returns the parsed arguments
def initialize_and_parse_cli_options # @options = base_options @options = HashDelegator.new(base_options) read_configuration_file!(@options, ".#{MarkdownExec::APP_NAME.downcase}.yml") @option_parser = OptionParser.new do |opts| executable_name = File.basename($PROGRAM_NAME) opts.banner = [ "#{MarkdownExec::APP_NAME}" \ " - #{MarkdownExec::APP_DESC} (#{MarkdownExec::VERSION})", "Usage: #{executable_name} [(path | filename [block_name])] [options]" ].join("\n") menu_iter do |item| opts_menu_option_append opts, @options, item end end @option_parser.load @option_parser.environment @rest = rest = @option_parser.parse!(arguments_for_mde) @options.pass_args = ARGV[rest.count + 1..] @options.merge(@options.run_state.to_h) rest end
def lambda_for_procname(procname, options)
-
(Lambda)- The corresponding lambda expression.
Parameters:
-
options(Hash) -- The options hash, necessary for some lambdas to access. -
procname(String) -- The name of the process to generate a lambda for.
def lambda_for_procname(procname, options) case procname when 'debug' ->(value) { tap_config value: value } when 'exit' ->(_) { exit } when 'find', 'open' ->(value) { exit if find_value(value, execute_chosen_found: procname == 'open').fetch(:exit, false) } when 'help' ->(_) { @fout.fout menu_help exit } when 'how' ->(value) { @fout.fout(list_default_yaml.select { |line| line.include? value }) exit } when 'path' ->(value) { read_configuration_file!(options, value) } when 'show_config' ->(_) { finalize_cli_argument_processing(options) @fout.fout options.sort_by_key.to_yaml } when 'val_as_bool' ->(value) { value.instance_of?(::String) ? (value.chomp != '0') : value } when 'val_as_int' ->(value) { value.to_i } when 'val_as_str' ->(value) { value.to_s } when 'version' lambda { |_| @fout.fout MarkdownExec::VERSION exit } else procname end end
def list_default_env
def list_default_env menu_iter do |item| next unless item[:env_var].present? [ "#{item[:env_var]}=#{value_for_cli item[:default]}", item[:description].present? ? item[:description] : nil ].compact.join(' # ') end.compact.sort end
def list_default_yaml
def list_default_yaml menu_iter do |item| next unless item[:opt_name].present? && item[:default].present? [ "#{item[:opt_name]}: #{OptionValue.for_yaml(item[:default])}", item[:description].present? ? item[:description] : nil ].compact.join(' # ') end.compact.sort end
def list_files_specified(fn, filetree = nil)
# Searches for files based on the specified or default filenames and folders
def list_files_specified(fn, filetree = nil) return Dir.glob(fn) unless filetree filetree.select do |filename| filename == fn || filename.match(/^#{fn}$/) || filename.match(%r{^#{fn}/.+$}) end end
def list_markdown_files_in_path
def list_markdown_files_in_path Dir.glob(File.join(@options[:path], @options[:md_filename_glob])) end
def menu_export(data = menu_for_optparse)
def menu_export(data = menu_for_optparse) data.map do |item| item.delete(:procname) item end.to_yaml end
def menu_for_optparse
-
(Array- The array of option hashes for OptionParser.)
def menu_for_optparse menu_from_yaml.map do |menu_item| menu_item.merge( opt_name: menu_item[:opt_name]&.to_sym, proccode: lambda_for_procname(menu_item[:procname], options) ) end end
def menu_help
def menu_help @option_parser.help end
def menu_iter(data = menu_for_optparse, &block)
def menu_iter(data = menu_for_optparse, &block) data.map(&block) end
def opts_menu_option_append(opts, options, item)
def opts_menu_option_append(opts, options, item) return unless item[:long_name].present? || item[:short_name].present? opts.on(*[ # - long name if item[:long_name].present? "--#{item[:long_name]}#{item[:arg_name].present? ? " #{item[:arg_name]}" : ''}" end, # - short name item[:short_name].present? ? "-#{item[:short_name]}" : nil, # - description and default [item[:description], ("[#{value_for_cli item[:default]}]" if item[:default].present?)].compact.join(' '), # apply proccode, if present, to value # save value to options hash if option is named # lambda { |value| (item[:proccode] ? item[:proccode].call(value) : value).tap do |converted| options[item[:opt_name]] = converted if item[:opt_name] end } ].compact) end
def opts_prepare_file_list(options)
def opts_prepare_file_list(options) list_files_specified( determine_filename( specified_filename: options[:filename]&.present? ? options[:filename] : nil, specified_folder: options[:path], default_filename: 'README.md', default_folder: '.' ) ) end
def read_configuration_file!(options, configuration_path)
def read_configuration_file!(options, configuration_path) return unless File.exist?(configuration_path) options.merge!((YAML.load(File.open(configuration_path)) || {}) .transform_keys(&:to_sym)) end
def run
def run initialize_and_parse_cli_options execute_block_with_error_handling rescue StandardError error_handler('run') ensure yield if block_given? end
def run_last_script
def run_last_script filename = SavedFilesMatcher.most_recent(@options[:saved_script_folder], @options[:saved_script_glob]) return unless filename saved_name_split filename @options[:save_executed_script] = false @options.document_menu_loop rescue StandardError error_handler('run_last_script') end
def saved_name_split(name)
def saved_name_split(name) mf = /#{@options[:saved_script_filename_prefix]}_(?<time>[0-9\-]+)_(?<file>.+)_,_(?<block>.+)\.sh/.match(name) return unless mf @options[:block_name] = mf[:block] @options[:filename] = mf[:file].gsub(@options[:saved_filename_pattern], @options[:saved_filename_replacement]) end
def select_document_if_multiple(files = list_markdown_files_in_path)
def select_document_if_multiple(files = list_markdown_files_in_path) return files[0] if (count = files.count) == 1 return unless count >= 2 opts = options.dup select_option_or_exit(HashDelegator.new(@options).string_send_color(opts[:prompt_select_md].to_s, :prompt_color_after_script_execution), files, opts.merge(per_page: opts[:select_page_height])) end
def select_option_or_exit(prompt_text, strings, opts = {})
def select_option_or_exit(prompt_text, strings, opts = {}) result = @options.select_option_with_metadata(prompt_text, strings, opts) ### 2024-04-20 what for? # return unless result.fetch(:option, nil) result[:selected] end
def select_recent_output
def select_recent_output filename = select_option_or_exit( HashDelegator.new(@options).string_send_color(@options[:prompt_select_output].to_s, :prompt_color_after_script_execution), list_recent_output( @options[:saved_stdout_folder], @options[:saved_stdout_glob], @options[:list_count] ), @options.merge({ per_page: @options[:select_page_height] }) ) return unless filename.present? `open #{filename} #{options[:output_viewer_options]}` end
def select_recent_script
def select_recent_script filename = select_option_or_exit( HashDelegator.new(@options).string_send_color(@options[:prompt_select_md].to_s, :prompt_color_after_script_execution), list_recent_scripts( @options[:saved_script_folder], @options[:saved_script_glob], @options[:list_count] ), @options.merge({ per_page: @options[:select_page_height] }) ) return if filename.nil? saved_name_split(filename) @options.document_menu_loop ### ({ save_executed_script: false }) end
def tab_completions(data = menu_for_optparse)
def tab_completions(data = menu_for_optparse) data.map do |item| "--#{item[:long_name]}" if item[:long_name] end.compact end
def update_options(opts = {}, over: true)
:reek:BooleanParameter
def update_options(opts = {}, over: true) if over @options = @options.merge opts else @options.merge! opts end @options end
def warn_format(name, message, opts = {})
def warn_format(name, message, opts = {}) Exceptions.warn_format( "CachedNestedFileReader.#{name} -- #{message}", opts ) end