lib/markdown_exec.rb in markdown_exec-1.1.1 vs lib/markdown_exec.rb in markdown_exec-1.2.0
- old
+ new
@@ -3,36 +3,53 @@
# encoding=utf-8
require 'English'
require 'clipboard'
-require 'mrdialog'
require 'open3'
require 'optparse'
require 'tty-prompt'
require 'yaml'
+require_relative 'colorize'
+require_relative 'env'
require_relative 'shared'
+require_relative 'tap'
require_relative 'markdown_exec/version'
+include Tap # rubocop:disable Style/MixinUsage
+
$stderr.sync = true
$stdout.sync = true
BLOCK_SIZE = 1024
-class Object # rubocop:disable Style/Documentation
+# hash with keys sorted by name
+#
+class Hash
+ def sort_by_key
+ keys.sort.to_h { |key| [key, self[key]] }
+ end
+end
+
+# is the value a non-empty string or a binary?
+#
+# :reek:ManualDispatch ### temp
+class Object
def present?
case self.class.to_s
when 'FalseClass', 'TrueClass'
true
else
self && (!respond_to?(:blank?) || !blank?)
end
end
end
-class String # rubocop:disable Style/Documentation
+# is value empty?
+#
+class String
BLANK_RE = /\A[[:space:]]*\z/.freeze
def blank?
empty? || BLANK_RE.match?(self)
end
end
@@ -49,42 +66,240 @@
# @execute_files[ind] = @execute_files[ind] + [block]
EF_STDOUT = 0
EF_STDERR = 1
EF_STDIN = 2
+# execute markdown documents
+#
module MarkdownExec
+ # :reek:IrresponsibleModule
class Error < StandardError; end
+ ## an imported markdown document
+ #
+ class MDoc
+ def initialize(table)
+ @table = table
+ end
+
+ def code(block)
+ all = [block[:name]] + recursively_required(block[:reqs])
+ all.reverse.map do |req|
+ get_block_by_name(req).fetch(:body, '')
+ end
+ .flatten(1)
+ .tap_inspect
+ end
+
+ def get_block_by_name(name, default = {})
+ @table.select { |block| block[:name] == name }.fetch(0, default)
+ end
+
+ def list_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[:name]] + recursively_required(name_block[:reqs])
+
+ # in order of appearance in document
+ @table.select { |block| all.include? block[:name] }
+ .map { |block| block.fetch(:body, '') }
+ .flatten(1)
+ .tap_inspect
+ end
+
+ def option_exclude_blocks(opts)
+ block_name_excluded_match = Regexp.new opts[:block_name_excluded_match]
+ if opts[:hide_blocks_by_name]
+ @table.reject { |block| block[:name].match(block_name_excluded_match) }
+ else
+ @table
+ end
+ end
+
+ def recursively_required(reqs)
+ all = []
+ rem = reqs
+ while rem.count.positive?
+ rem = rem.map do |req|
+ next if all.include? req
+
+ all += [req]
+ get_block_by_name(req).fetch(:reqs, [])
+ end
+ .compact
+ .flatten(1)
+ .tap_inspect(name: 'rem')
+ end
+ all.tap_inspect
+ end
+ end
+
+ # format option defaults and values
+ #
+ # :reek:TooManyInstanceVariables
+ class BlockLabel
+ def initialize(filename:, headings:, menu_blocks_with_docname:, menu_blocks_with_headings:, title:)
+ @filename = filename
+ @headings = headings
+ @menu_blocks_with_docname = menu_blocks_with_docname
+ @menu_blocks_with_headings = menu_blocks_with_headings
+ @title = title
+ end
+
+ def make
+ ([@title] +
+ (if @menu_blocks_with_headings
+ [@headings.compact.join(' # ')]
+ else
+ []
+ end) +
+ (
+ if @menu_blocks_with_docname
+ [@filename]
+ else
+ []
+ end
+ )).join(' ')
+ end
+ end
+
+ FNR11 = '/'
+ FNR12 = ',~'
+
+ # format option defaults and values
+ #
+ class SavedAsset
+ def initialize(filename:, prefix:, time:, blockname:)
+ @filename = filename
+ @prefix = prefix
+ @time = time
+ @blockname = blockname
+ end
+
+ def script_name
+ fne = @filename.gsub(FNR11, FNR12)
+ "#{[@prefix, @time.strftime('%F-%H-%M-%S'), fne, ',', @blockname].join('_')}.sh".tap_inspect
+ end
+
+ def stdout_name
+ "#{[@prefix, @time.strftime('%F-%H-%M-%S'), @filename, @blockname].join('_')}.out.txt".tap_inspect
+ end
+ end
+
+ # format option defaults and values
+ #
+ class OptionValue
+ def initialize(value)
+ @value = value
+ end
+
+ # as default value in env_str()
+ #
+ def for_hash(default = nil)
+ return default if @value.nil?
+
+ case @value.class.to_s
+ when 'String', 'Integer'
+ @value
+ when 'FalseClass', 'TrueClass'
+ @value ? true : false
+ when @value.empty?
+ default
+ else
+ @value.to_s
+ end
+ end
+
+ # for output as default value in list_default_yaml()
+ #
+ def for_yaml(default = nil)
+ return default if @value.nil?
+
+ case @value.class.to_s
+ when 'String'
+ "'#{@value}'"
+ when 'Integer'
+ @value
+ when 'FalseClass', 'TrueClass'
+ @value ? true : false
+ when @value.empty?
+ default
+ else
+ @value.to_s
+ end
+ end
+ end
+
+ # a generated list of saved files
+ #
+ class Sfiles
+ def initialize(folder, glob)
+ @folder = folder
+ @glob = glob
+ end
+
+ def list_all
+ Dir.glob(File.join(@folder, @glob)).tap_inspect
+ end
+
+ def most_recent(arr = list_all)
+ return unless arr
+ return if arr.count < 1
+
+ arr.max.tap_inspect
+ end
+
+ def most_recent_list(arr = list_all)
+ return unless arr
+ return if (ac = arr.count) < 1
+
+ arr.sort[-[ac, options[:list_count]].min..].reverse.tap_inspect
+ end
+ end
+
##
#
+ # :reek:DuplicateMethodCall { allow_calls: ['block', 'item', 'lm', 'opts', 'option', '@options', 'required_blocks'] }
+ # :reek:MissingSafeMethod { exclude: [ read_configuration_file! ] }
+ # :reek:TooManyInstanceVariables ### temp
+ # :reek:TooManyMethods ### temp
class MarkParse
- attr_accessor :options
+ attr_reader :options
def initialize(options = {})
@options = options
@prompt = TTY::Prompt.new(interrupt: :exit)
+ @execute_aborted_at = nil
+ @execute_completed_at = nil
+ @execute_error = nil
+ @execute_error_message = nil
+ @execute_files = nil
+ @execute_options = nil
+ @execute_script_filespec = nil
+ @execute_started_at = nil
+ @option_parser = nil
end
##
# options necessary to start, parse input, defaults for cli options
def base_options
menu_iter do |item|
- item.tap_inspect name: :item, format: :yaml
+ # noisy item.tap_inspect name: :item, format: :yaml
next unless item[:opt_name].present?
item_default = item[:default]
- item_default.tap_inspect name: :item_default
+ # noisy item_default.tap_inspect name: :item_default
value = if item_default.nil?
item_default
else
- env_str(item[:env_var], default: value_for_hash(item_default))
+ env_str(item[:env_var], default: OptionValue.new(item_default).for_hash)
end
[item[:opt_name], item[:proc1] ? item[:proc1].call(value) : value]
end.compact.to_h.merge(
{
- mdheadings: true, # use headings (levels 1,2,3) in block lable
menu_exit_at_top: true,
menu_with_exit: true
}
).tap_inspect format: :yaml
end
@@ -102,24 +317,17 @@
saved_script_filename: nil, # calculated
struct: true # allow get_block_summary()
}
end
- # Returns true if all files are EOF
- #
- def all_at_eof(files)
- files.find { |f| !f.eof }.nil?
- end
-
- def approve_block(opts, blocks_in_file)
- required_blocks = list_recursively_required_blocks(blocks_in_file, opts[:block_name])
+ def approve_block(opts, mdoc)
+ required_blocks = mdoc.list_recursively_required_blocks(opts[:block_name])
display_command(opts, required_blocks) if opts[:output_script] || opts[:user_must_approve]
allow = true
if opts[:user_must_approve]
loop do
- # (sel = @prompt.select(opts[:prompt_approve_block], %w(Yes No Copy_script_to_clipboard Save_script), cycle: true)).tap_inspect name: :sel
(sel = @prompt.select(opts[:prompt_approve_block], filter: true) do |menu|
menu.default 1
# menu.enum '.'
# menu.filter true
@@ -130,23 +338,24 @@
end).tap_inspect name: :sel
allow = (sel == 1)
if sel == 3
text = required_blocks.flatten.join($INPUT_RECORD_SEPARATOR)
Clipboard.copy(text)
- fout "Clipboard updated: #{required_blocks.count} blocks, #{required_blocks.flatten.count} lines, #{text.length} characters"
+ fout "Clipboard updated: #{required_blocks.count} blocks," /
+ " #{required_blocks.flatten.count} lines," /
+ " #{text.length} characters"
end
if sel == 4
- # opts[:saved_script_filename] = saved_name_make(opts)
write_command_file(opts.merge(save_executed_script: true), required_blocks)
fout "File saved: #{@options[:saved_filespec]}"
end
break if [1, 2].include? sel
end
end
(opts[:ir_approve] = allow).tap_inspect name: :allow
- selected = get_block_by_name blocks_in_file, opts[:block_name]
+ selected = mdoc.get_block_by_name opts[:block_name]
if opts[:ir_approve]
write_command_file opts, required_blocks
command_execute opts, required_blocks.flatten.join("\n")
save_execution_output
@@ -155,44 +364,38 @@
end
selected[:name]
end
- def code(table, block)
- all = [block[:name]] + recursively_required(table, block[:reqs])
- all.reverse.map do |req|
- get_block_by_name(table, req).fetch(:body, '')
- end
- .flatten(1)
- .tap_inspect
- end
-
- def command_execute(opts, cmd2)
+ # :reek:DuplicateMethodCall
+ # :reek:UncommunicativeVariableName { exclude: [ e ] }
+ # :reek:LongYieldList
+ def command_execute(opts, command)
@execute_files = Hash.new([])
@execute_options = opts
@execute_started_at = Time.now.utc
- Open3.popen3(@options[:shell], '-c', cmd2) do |stdin, stdout, stderr, exec_thr|
+ Open3.popen3(@options[:shell], '-c', command) do |stdin, stdout, stderr, exec_thr|
# pid = exec_thr.pid # pid of the started process
- t1 = Thread.new do
+ Thread.new do
until (line = stdout.gets).nil?
@execute_files[EF_STDOUT] = @execute_files[EF_STDOUT] + [line]
print line if opts[:output_stdout]
yield nil, line, nil, exec_thr if block_given?
end
- rescue IOError => e
+ rescue IOError
# thread killed, do nothing
end
- t2 = Thread.new do
+ Thread.new do
until (line = stderr.gets).nil?
@execute_files[EF_STDERR] = @execute_files[EF_STDERR] + [line]
print line if opts[:output_stdout]
yield nil, nil, line, exec_thr if block_given?
end
- rescue IOError => e
+ rescue IOError
# thread killed, do nothing
end
in_thr = Thread.new do
while exec_thr.alive? # reading input until the child process ends
@@ -210,12 +413,19 @@
rescue Errno::ENOENT => e
# error triggered by missing command in script
@execute_aborted_at = Time.now.utc
@execute_error_message = e.message
@execute_error = e
- @execute_files[EF_STDERR] += [e.message]
+ @execute_files[EF_STDERR] += [@execute_error_message]
fout "Error ENOENT: #{e.inspect}"
+ rescue SignalException => e
+ # SIGTERM triggered by user or system
+ @execute_aborted_at = Time.now.utc
+ @execute_error_message = 'SIGTERM'
+ @execute_error = e
+ @execute_files[EF_STDERR] += [@execute_error_message]
+ fout "Error ENOENT: #{e.inspect}"
end
def count_blocks_in_filename
fenced_start_and_end_match = Regexp.new @options[:fenced_start_and_end_match]
cnt = 0
@@ -223,16 +433,19 @@
cnt += 1 if line.match(fenced_start_and_end_match)
end
cnt / 2
end
+ # :reek:DuplicateMethodCall
def display_command(_opts, required_blocks)
- fout ' #=#=#'.yellow
+ frame = ' #=#=#'.yellow
+ fout frame
required_blocks.each { |cb| fout cb }
- fout ' #=#=#'.yellow
+ fout frame
end
+ # :reek:DuplicateMethodCall
def exec_block(options, _block_name = '')
options = default_options.merge options
update_options options, over: false
# document and block reports
@@ -250,122 +463,10 @@
list_docs: -> { fout_list files },
list_default_env: -> { fout_list list_default_env },
list_recent_output: -> { fout_list list_recent_output },
list_recent_scripts: -> { fout_list list_recent_scripts },
pwd: -> { fout File.expand_path('..', __dir__) },
- pwd3: lambda {
- text = 'A B C'
- items = []
- Struct.new('BuildListData', :tag, :item, :status)
- data = Struct::BuildListData.new
-
- data.tag = '1'
- data.item = 'Item number 1'
- data.status = true
- items.push(data.to_a)
-
- data = Struct::BuildListData.new
- data.tag = '2'
- data.item = 'Item number 2'
- data.status = false
- items.push(data.to_a)
-
- data = Struct::BuildListData.new
- data.tag = '3'
- data.item = 'Item number 3'
- data.status = false
- items.push(data.to_a)
-
- data = Struct::BuildListData.new
- data.tag = '4'
- data.item = 'Item number 4'
- data.status = true
- items.push(data.to_a)
-
- data = Struct::BuildListData.new
- data.tag = '5'
- data.item = 'Item number 5'
- data.status = false
- items.push(data.to_a)
-
- data = Struct::BuildListData.new
- data.tag = '6'
- data.item = 'Item number 6'
- data.status = true
- items.push(data.to_a)
-
- dialog = MRDialog.new
- dialog.clear = true
- dialog.shadow = false
- dialog.title = 'BUILDLIST'
- # dialog.logger = Logger.new(ENV["HOME"] + "/dialog_" + ME + ".log")
-
- height = 0
- width = 0
- listheight = 0
-
- selected_items = dialog.buildlist(text, items, height, width, listheight)
- exit_code = dialog.exit_code
- puts "Exit code: #{exit_code}"
- puts 'Selecetd tags:'
- selected_items.each do |item|
- puts " '#{item}'"
- end
- },
- pwd1: lambda {
- dialog = MRDialog.new
- dialog.clear = true
- dialog.title = 'YES/NO BOX'
- puts "yesno: #{dialog.yesno('ABC', 0, 0)}"
- },
- pwd2: lambda {
- dialog = MRDialog.new
- # dialog.logger = Logger.new(ENV["HOME"] + "/dialog_" + ME + ".log")
- dialog.clear = true
- dialog.title = 'MENU BOX'
- text = 'textextst'
- items = []
- menu_data = Struct.new(:tag, :item)
- data = menu_data.new
- data.tag = 'Linux'
- data.item = 'The Great Unix Clone for 386/486'
- items.push(data.to_a)
-
- data = menu_data.new
- data.tag = 'NetBSD'
- data.item = 'Another free Unix Clone for 386/486'
- items.push(data.to_a)
-
- data = menu_data.new
- data.tag = 'OS/2'
- data.item = 'IBM OS/2'
- items.push(data.to_a)
-
- data = menu_data.new
- data.tag = 'WIN NT'
- data.item = 'Microsoft Windows NT'
- items.push(data.to_a)
-
- data = menu_data.new
- data.tag = 'PCDOS'
- data.item = 'IBM PC DOS'
- items.push(data.to_a)
-
- data = menu_data.new
- data.tag = 'MSDOS'
- data.item = 'Microsoft DOS'
- items.push(data.to_a)
-
- height = 0
- width = 0
- menu_height = 4
-
- selected_item = dialog.menu(text, items, height, width, menu_height)
-
- puts "Selected item: #{selected_item}"
- },
- # pwd: -> { fout `dialog --yesno "ABC" 99 99` },
run_last_script: -> { run_last_script },
select_recent_output: -> { select_recent_output },
select_recent_script: -> { select_recent_script },
tab_completions: -> { fout tab_completions },
menu_export: -> { fout menu_export }
@@ -400,21 +501,19 @@
def fout_section(name, data)
puts "# #{name}"
puts data.to_yaml
end
- def get_block_by_name(table, name, default = {})
- table.select { |block| block[:name] == name }.fetch(0, default)
- end
-
- def get_block_summary(opts, headings, block_title, current)
+ # :reek:LongParameterList
+ def get_block_summary(opts, headings:, block_title:, current:)
return [current] unless opts[:struct]
return [summarize_block(headings, block_title).merge({ body: current })] unless opts[:bash]
bm = block_title.match(Regexp.new(opts[:block_name_match]))
- reqs = block_title.scan(Regexp.new(opts[:block_required_scan])).map { |s| s[1..] }
+ reqs = block_title.scan(Regexp.new(opts[:block_required_scan]))
+ .map { |scanned| scanned[1..] }
if bm && bm[1]
[summarize_block(headings, bm[:title]).merge({ body: current, reqs: reqs })]
else
[summarize_block(headings, block_title).merge({ body: current, reqs: reqs })]
@@ -432,10 +531,11 @@
# fout level == DISPLAY_LEVEL_BASE ? str : DISPLAY_LEVEL_XBASE_PREFIX + str
fout level == DISPLAY_LEVEL_BASE ? str : @options[:display_level_xbase_prefix] + str
end
+ # :reek:DuplicateMethodCall
def list_blocks_in_file(call_options = {}, &options_block)
opts = optsmerge call_options, options_block
unless opts[:filename]&.present?
fout 'No blocks found.'
@@ -455,11 +555,11 @@
headings = []
in_block = false
File.readlines(opts[:filename]).each do |line|
continue unless line
- if opts[:mdheadings]
+ if opts[:menu_blocks_with_headings]
if (lm = line.match(Regexp.new(opts[:heading3_match])))
headings = [headings[0], headings[1], lm[:name]]
elsif (lm = line.match(Regexp.new(opts[:heading2_match])))
headings = [headings[0], lm[:name]]
elsif (lm = line.match(Regexp.new(opts[:heading1_match])))
@@ -469,29 +569,29 @@
if line.match(fenced_start_and_end_match)
if in_block
if current
block_title = current.join(' ').gsub(/ +/, ' ')[0..64] if block_title.nil? || block_title.empty?
- blocks += get_block_summary opts, headings, block_title, current
+ blocks += get_block_summary opts, headings: headings, block_title: block_title, current: current
current = nil
end
in_block = false
block_title = ''
else
# new block
#
lm = line.match(fenced_start_ex)
- do1 = false
+ block_allow = false
if opts[:bash_only]
- do1 = true if lm && (lm[:shell] == 'bash')
+ block_allow = true if lm && (lm[:shell] == 'bash')
else
- do1 = true
- do1 = !(lm && (lm[:shell] == 'expect')) if opts[:exclude_expect_blocks]
+ block_allow = true
+ block_allow = !(lm && (lm[:shell] == 'expect')) if opts[:exclude_expect_blocks]
end
in_block = true
- if do1 && (!opts[:title_match] || (lm && lm[:name] && lm[:name].match(opts[:title_match])))
+ if block_allow && (!opts[:title_match] || (lm && lm[:name] && lm[:name].match(opts[:title_match])))
current = []
block_title = (lm && lm[:name])
end
end
elsif current
@@ -515,26 +615,28 @@
def list_default_yaml
menu_iter do |item|
next unless item[:opt_name].present? && item[:default].present?
[
- "#{item[:opt_name]}: #{value_for_yaml item[:default]}",
+ "#{item[:opt_name]}: #{OptionValue.new(item[:default]).for_yaml}",
item[:description].present? ? item[:description] : nil
].compact.join(' # ')
end.compact.sort
end
def list_files_per_options(options)
list_files_specified(
- options[:filename]&.present? ? options[:filename] : nil,
- options[:path],
- 'README.md',
- '.'
+ specified_filename: options[:filename]&.present? ? options[:filename] : nil,
+ specified_folder: options[:path],
+ default_filename: 'README.md',
+ default_folder: '.'
).tap_inspect
end
- def list_files_specified(specified_filename, specified_folder, default_filename, default_folder, filetree = nil)
+ # :reek:LongParameterList
+ def list_files_specified(specified_filename: nil, specified_folder: nil,
+ default_filename: nil, default_folder: nil, filetree: nil)
fn = File.join(if specified_filename&.present?
if specified_folder&.present?
[specified_folder, specified_filename]
elsif specified_filename.start_with? '/'
[specified_filename]
@@ -569,72 +671,41 @@
block
end.compact.tap_inspect
end
- def list_recursively_required_blocks(table, name)
- name_block = get_block_by_name(table, name)
- raise "Named code block `#{name}` not found." if name_block.nil? || name_block.keys.empty?
-
- all = [name_block[:name]] + recursively_required(table, name_block[:reqs])
-
- # in order of appearance in document
- table.select { |block| all.include? block[:name] }
- .map { |block| block.fetch(:body, '') }
- .flatten(1)
- .tap_inspect
- end
-
- def most_recent(arr)
- return unless arr
- return if arr.count < 1
-
- arr.max.tap_inspect
- end
-
- def most_recent_list(arr)
- return unless arr
- return if (ac = arr.count) < 1
-
- arr.sort[-[ac, options[:list_count]].min..].reverse.tap_inspect
- end
-
def list_recent_output
- most_recent_list(Dir.glob(File.join(@options[:saved_stdout_folder],
- @options[:saved_stdout_glob]))).tap_inspect
+ Sfiles.new(@options[:saved_stdout_folder],
+ @options[:saved_stdout_glob]).most_recent_list
end
def list_recent_scripts
- most_recent_list(Dir.glob(File.join(@options[:saved_script_folder],
- @options[:saved_script_glob]))).tap_inspect
+ Sfiles.new(@options[:saved_script_folder],
+ @options[:saved_script_glob]).most_recent_list
end
- def make_block_label(block, call_options = {})
- opts = options.merge(call_options)
- if opts[:mdheadings]
- heads = block.fetch(:headings, []).compact.join(' # ')
- "#{block[:title]} [#{heads}] (#{opts[:filename]})"
- else
- "#{block[:title]} (#{opts[:filename]})"
- end
- end
-
def make_block_labels(call_options = {})
opts = options.merge(call_options)
list_blocks_in_file(opts).map do |block|
# next if opts[:hide_blocks_by_name] && block[:name].match(%r{^:\(.+\)$})
- make_block_label block, opts
+ BlockLabel.new(filename: opts[:filename],
+ headings: block.fetch(:headings, []),
+ menu_blocks_with_docname: opts[:menu_blocks_with_docname],
+ menu_blocks_with_headings: opts[:menu_blocks_with_headings],
+ title: block[:title]).make
end.compact.tap_inspect
end
+ # :reek:DuplicateMethodCall
+ # :reek:UncommunicativeMethodName ### temp
def menu_data1
val_as_bool = ->(value) { value.class.to_s == 'String' ? (value.chomp != '0') : value }
val_as_int = ->(value) { value.to_i }
val_as_str = ->(value) { value.to_s }
# val_true = ->(_value) { true } # for commands, sets option to true
- set1 = [
+ menu_options = [
{
arg_name: 'PATH',
default: '.',
description: 'Read configuration file',
long_name: 'config',
@@ -648,11 +719,11 @@
description: 'Debug output',
env_var: 'MDE_DEBUG',
long_name: 'debug',
short_name: 'd',
proc1: lambda { |value|
- $pdebug = value.to_i != 0
+ tap_config value.to_i != 0
}
},
{
arg_name: "INT.#{DISPLAY_LEVEL_BASE}-#{DISPLAY_LEVEL_MAX}",
default: DISPLAY_LEVEL_DEFAULT,
@@ -735,10 +806,28 @@
proc1: val_as_str
},
{
arg_name: 'BOOL',
default: false,
+ description: 'Display document name in block selection menu',
+ env_var: 'MDE_MENU_BLOCKS_WITH_DOCNAME',
+ long_name: 'menu-blocks-with-docname',
+ opt_name: :menu_blocks_with_docname,
+ proc1: val_as_bool
+ },
+ {
+ arg_name: 'BOOL',
+ default: false,
+ description: 'Display headings (levels 1,2,3) in block selection menu',
+ env_var: 'MDE_MENU_BLOCKS_WITH_HEADINGS',
+ long_name: 'menu-blocks-with-headings',
+ opt_name: :menu_blocks_with_headings,
+ proc1: val_as_bool
+ },
+ {
+ arg_name: 'BOOL',
+ default: false,
description: 'Display summary for execution',
env_var: 'MDE_OUTPUT_EXECUTION_SUMMARY',
long_name: 'output-execution-summary',
opt_name: :output_execution_summary,
proc1: val_as_bool
@@ -891,11 +980,11 @@
{
description: 'Show current configuration values',
short_name: '0',
proc1: lambda { |_|
options_finalize options
- fout sorted_keys(options).to_yaml
+ fout options.sort_by_key.to_yaml
}
},
{
description: 'App help',
long_name: 'help',
@@ -1014,30 +1103,22 @@
opt_name: :shell,
proc1: val_as_str
}
]
# commands first, options second
- (set1.reject { |v1| v1[:arg_name] }) + (set1.select { |v1| v1[:arg_name] })
+ (menu_options.reject { |option| option[:arg_name] }) +
+ (menu_options.select { |option| option[:arg_name] })
end
def menu_iter(data = menu_data1, &block)
data.map(&block)
end
def menu_help
@option_parser.help
end
- def option_exclude_blocks(opts, blocks)
- block_name_excluded_match = Regexp.new opts[:block_name_excluded_match]
- if opts[:hide_blocks_by_name]
- blocks.reject { |block| block[:name].match(block_name_excluded_match) }
- else
- blocks
- end
- end
-
## post-parse options configuration
#
def options_finalize(rest)
## position 0: file or folder (optional)
#
@@ -1055,10 +1136,11 @@
#
block_name = rest.fetch(1, nil)
@options[:block_name] = block_name if block_name.present?
end
+ # :reek:ControlParameter
def optsmerge(call_options = {}, options_block = nil)
class_call_options = @options.merge(call_options || {})
if options_block
options_block.call class_call_options
else
@@ -1108,36 +1190,21 @@
end
sel = @prompt.select(prompt_text, all_items, opts.merge(filter: true))
sel == exit_option ? nil : sel
end
+ # :reek:UtilityFunction ### temp
def read_configuration_file!(options, configuration_path)
return unless File.exist?(configuration_path)
# rubocop:disable Security/YAMLLoad
options.merge!((YAML.load(File.open(configuration_path)) || {})
.transform_keys(&:to_sym))
# rubocop:enable Security/YAMLLoad
end
- def recursively_required(table, reqs)
- all = []
- rem = reqs
- while rem.count.positive?
- rem = rem.map do |req|
- next if all.include? req
-
- all += [req]
- get_block_by_name(table, req).fetch(:reqs, [])
- end
- .compact
- .flatten(1)
- .tap_inspect(name: 'rem')
- end
- all.tap_inspect
- end
-
+ # :reek:NestedIterators
def run
## default configuration
#
@options = base_options
@@ -1177,103 +1244,104 @@
options_finalize rest
exec_block options, options[:block_name]
end
- FNR11 = '/'
- FNR12 = ',~'
-
- def saved_name_make(opts)
- fne = opts[:filename].gsub(FNR11, FNR12)
- "#{[opts[:saved_script_filename_prefix], Time.now.utc.strftime('%F-%H-%M-%S'), fne,
- ',', opts[:block_name]].join('_')}.sh"
- end
-
def saved_name_split(name)
mf = name.match(/#{@options[:saved_script_filename_prefix]}_(?<time>[0-9\-]+)_(?<file>.+)_,_(?<block>.+)\.sh/)
return unless mf
@options[:block_name] = mf[:block].tap_inspect name: :options_block_name
@options[:filename] = mf[:file].gsub(FNR12, FNR11).tap_inspect name: :options_filename
end
def run_last_script
- filename = most_recent Dir.glob(File.join(@options[:saved_script_folder],
- @options[:saved_script_glob]))
+ filename = Sfiles.new(@options[:saved_script_folder],
+ @options[:saved_script_glob]).most_recent
return unless filename
- filename.tap_inspect name: filename
saved_name_split filename
@options[:save_executed_script] = false
select_and_approve_block
end
def save_execution_output
+ @options.tap_inspect name: :options
return unless @options[:save_execution_output]
- fne = File.basename(@options[:filename], '.*')
-
@options[:logged_stdout_filename] =
- "#{[@options[:logged_stdout_filename_prefix], Time.now.utc.strftime('%F-%H-%M-%S'), fne,
- @options[:block_name]].join('_')}.out.txt"
+ SavedAsset.new(blockname: @options[:block_name],
+ filename: File.basename(@options[:filename], '.*'),
+ prefix: @options[:logged_stdout_filename_prefix],
+ time: Time.now.utc).stdout_name
+
@options[:logged_stdout_filespec] = File.join @options[:saved_stdout_folder], @options[:logged_stdout_filename]
@logged_stdout_filespec = @options[:logged_stdout_filespec]
- dirname = File.dirname(@options[:logged_stdout_filespec])
+ (dirname = File.dirname(@options[:logged_stdout_filespec])).tap_inspect name: :dirname
Dir.mkdir dirname unless File.exist?(dirname)
- # File.write(@options[:logged_stdout_filespec], @execute_files&.fetch(EF_STDOUT, ''))
ol = ["-STDOUT-\n"]
ol += @execute_files&.fetch(EF_STDOUT, [])
- ol += ["-STDERR-\n"].tap_inspect name: :ol3
+ ol += ["\n-STDERR-\n"]
ol += @execute_files&.fetch(EF_STDERR, [])
- ol += ["-STDIN-\n"]
+ ol += ["\n-STDIN-\n"]
ol += @execute_files&.fetch(EF_STDIN, [])
+ ol += ["\n"]
File.write(@options[:logged_stdout_filespec], ol.join)
end
def select_and_approve_block(call_options = {}, &options_block)
opts = optsmerge call_options, options_block
blocks_in_file = list_blocks_in_file(opts.merge(struct: true))
+ mdoc = MDoc.new(blocks_in_file)
- loop1 = true && !opts[:block_name].present?
+ repeat_menu = true && !opts[:block_name].present?
loop do
unless opts[:block_name].present?
pt = (opts[:prompt_select_block]).to_s
- blocks_in_file.each { |block| block.merge! label: make_block_label(block, opts) }
- block_labels = option_exclude_blocks(opts, blocks_in_file).map { |block| block[:label] }
+ blocks_in_file.each do |block|
+ block.merge! label:
+ BlockLabel.new(filename: opts[:filename],
+ headings: block.fetch(:headings, []),
+ menu_blocks_with_docname: opts[:menu_blocks_with_docname],
+ menu_blocks_with_headings: opts[:menu_blocks_with_headings],
+ title: block[:title]).make
+ end
+
+ block_labels = mdoc.option_exclude_blocks(opts).map { |block| block[:label] }
+
return nil if block_labels.count.zero?
sel = prompt_with_quit pt, block_labels, per_page: opts[:select_page_height]
return nil if sel.nil?
# if sel.nil?
- # loop1 = false
+ # repeat_menu = false
# break
# end
label_block = blocks_in_file.select { |block| block[:label] == sel }.fetch(0, nil)
opts[:block_name] = @options[:block_name] = label_block[:name]
end
- # if loop1
- approve_block opts, blocks_in_file
+ # if repeat_menu
+ approve_block opts, mdoc
# end
- break unless loop1
+ break unless repeat_menu
opts[:block_name] = ''
end
end
- def select_md_file(files_ = nil)
+ def select_md_file(files = list_markdown_files_in_path)
opts = options
- files = files_ || list_markdown_files_in_path
- if files.count == 1
+ if (count = files.count) == 1
files[0]
- elsif files.count >= 2
+ elsif count >= 2
prompt_with_quit opts[:prompt_select_md].to_s, files, per_page: opts[:select_page_height]
end
end
def select_recent_output
@@ -1295,14 +1363,10 @@
save_executed_script: false,
struct: true
)
end
- def sorted_keys(hash1)
- hash1.keys.sort.to_h { |k| [k, hash1[k]] }
- end
-
def summarize_block(headings, title)
{ headings: headings, name: title, title: title }
end
def menu_export(data = menu_data1)
@@ -1316,53 +1380,32 @@
data.map do |item|
"--#{item[:long_name]}" if item[:long_name]
end.compact
end
+ # :reek:BooleanParameter
+ # :reek:ControlParameter
def update_options(opts = {}, over: true)
if over
@options = @options.merge opts
else
@options.merge! opts
end
@options.tap_inspect format: :yaml
end
- def value_for_hash(value, default = nil)
- return default if value.nil?
+ def write_command_file(call_options, required_blocks)
+ return unless call_options[:save_executed_script]
- case value.class.to_s
- when 'String', 'Integer', 'FalseClass', 'TrueClass'
- value
- when value.empty?
- default
- else
- value.to_s
- end
- end
+ time_now = Time.now.utc
+ opts = optsmerge call_options
+ opts[:saved_script_filename] =
+ SavedAsset.new(blockname: opts[:block_name],
+ filename: opts[:filename],
+ prefix: opts[:saved_script_filename_prefix],
+ time: time_now).script_name
- def value_for_yaml(value)
- return default if value.nil?
-
- case value.class.to_s
- when 'String'
- "'#{value}'"
- when 'Integer'
- value
- when 'FalseClass', 'TrueClass'
- value ? true : false
- when value.empty?
- default
- else
- value.to_s
- end
- end
-
- def write_command_file(opts, required_blocks)
- return unless opts[:save_executed_script]
-
- opts[:saved_script_filename] = saved_name_make(opts)
@execute_script_filespec =
@options[:saved_filespec] =
File.join opts[:saved_script_folder], opts[:saved_script_filename]
dirname = File.dirname(@options[:saved_filespec])
@@ -1374,17 +1417,13 @@
end
).tap_inspect name: :shebang
File.write(@options[:saved_filespec], shebang +
"# file_name: #{opts[:filename]}\n" \
"# block_name: #{opts[:block_name]}\n" \
- "# time: #{Time.now.utc}\n" \
+ "# time: #{time_now}\n" \
"#{required_blocks.flatten.join("\n")}\n")
-
- @options[:saved_script_chmod].tap_inspect name: :@options_saved_script_chmod
return if @options[:saved_script_chmod].zero?
- @options[:saved_script_chmod].tap_inspect name: :@options_saved_script_chmod
File.chmod @options[:saved_script_chmod], @options[:saved_filespec]
- @options[:saved_script_chmod].tap_inspect name: :@options_saved_script_chmod
end
end
end