markdown_exec 1.2.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.reek +6 -1
- data/.rubocop.yml +21 -0
- data/CHANGELOG.md +87 -3
- data/Gemfile +5 -0
- data/Gemfile.lock +15 -13
- data/README.md +81 -4
- data/Rakefile +571 -18
- data/bin/tab_completion.sh +29 -39
- data/bin/tab_completion.sh.erb +23 -0
- data/lib/cli.rb +19 -0
- data/lib/environment_opt_parse.rb +200 -0
- data/lib/globfiles.rb +40 -0
- data/lib/markdown_exec/version.rb +3 -1
- data/lib/markdown_exec.rb +427 -618
- data/lib/menu.yml +364 -0
- data/lib/object_present.rb +40 -0
- data/lib/shared.rb +20 -10
- data/lib/tap.rb +78 -17
- metadata +7 -2
data/bin/tab_completion.sh
CHANGED
@@ -13,7 +13,7 @@ __filedirs_all()
|
|
13
13
|
}
|
14
14
|
|
15
15
|
_mde_echo_version() {
|
16
|
-
echo "1.
|
16
|
+
echo "1.3.1"
|
17
17
|
}
|
18
18
|
|
19
19
|
_mde() {
|
@@ -34,17 +34,23 @@ _mde() {
|
|
34
34
|
|
35
35
|
--debug) COMPREPLY="0"; return 0 ;;
|
36
36
|
|
37
|
-
|
37
|
+
-d) COMPREPLY="0"; return 0 ;;
|
38
38
|
|
39
39
|
--filename) COMPREPLY="."; return 0 ;;
|
40
40
|
|
41
|
-
|
41
|
+
-f) COMPREPLY="."; return 0 ;;
|
42
|
+
|
43
|
+
--path) COMPREPLY="."; return 0 ;;
|
44
|
+
|
45
|
+
-p) COMPREPLY="."; return 0 ;;
|
42
46
|
|
43
|
-
--
|
47
|
+
--user-must-approve) COMPREPLY="1"; return 0 ;;
|
44
48
|
|
45
|
-
|
49
|
+
-q) COMPREPLY="1"; return 0 ;;
|
46
50
|
|
47
|
-
--
|
51
|
+
--display-level) COMPREPLY="1"; return 0 ;;
|
52
|
+
|
53
|
+
--list-count) COMPREPLY="32"; return 0 ;;
|
48
54
|
|
49
55
|
--output-execution-summary) COMPREPLY="0"; return 0 ;;
|
50
56
|
|
@@ -52,26 +58,14 @@ _mde() {
|
|
52
58
|
|
53
59
|
--output-stdout) COMPREPLY="1"; return 0 ;;
|
54
60
|
|
55
|
-
--path) COMPREPLY="."; return 0 ;;
|
56
|
-
|
57
61
|
--save-executed-script) COMPREPLY="0"; return 0 ;;
|
58
62
|
|
59
63
|
--save-execution-output) COMPREPLY="0"; return 0 ;;
|
60
64
|
|
61
|
-
--saved-script-chmod) COMPREPLY="493"; return 0 ;;
|
62
|
-
|
63
|
-
--saved-script-filename-prefix) COMPREPLY="mde"; return 0 ;;
|
64
|
-
|
65
65
|
--saved-script-folder) COMPREPLY="logs"; return 0 ;;
|
66
66
|
|
67
|
-
--saved-script-glob) COMPREPLY="mde_\*.sh"; return 0 ;;
|
68
|
-
|
69
67
|
--saved-stdout-folder) COMPREPLY="logs"; return 0 ;;
|
70
68
|
|
71
|
-
--saved-stdout-glob) COMPREPLY="mde_\*.out.txt"; return 0 ;;
|
72
|
-
|
73
|
-
--user-must-approve) COMPREPLY="1"; return 0 ;;
|
74
|
-
|
75
69
|
esac
|
76
70
|
fi
|
77
71
|
fi
|
@@ -80,7 +74,7 @@ _mde() {
|
|
80
74
|
# present matching option names
|
81
75
|
#
|
82
76
|
if [[ ${cur} == -* ]] ; then
|
83
|
-
opts=("--
|
77
|
+
opts=("--block-name" "--config" "--debug" "--filename" "--help" "--path" "--user-must-approve" "--version" "--exit" "--list-blocks" "--list-default-env" "--list-default-yaml" "--list-docs" "--list-recent-output" "--list-recent-scripts" "--select-recent-output" "--select-recent-script" "--tab-completions" "--run-last-script" "--pwd" "--display-level" "--list-count" "--output-execution-summary" "--output-script" "--output-stdout" "--save-executed-script" "--save-execution-output" "--saved-script-folder" "--saved-stdout-folder")
|
84
78
|
COMPREPLY=( $(compgen -W "$(printf "'%s' " "${opts[@]}")" -- "${cur}") )
|
85
79
|
|
86
80
|
return 0
|
@@ -93,23 +87,31 @@ _mde() {
|
|
93
87
|
if [[ -z ${cur} ]] ; then
|
94
88
|
case $prev in
|
95
89
|
|
90
|
+
--block-name) COMPREPLY=".NAME."; return 0 ;;
|
91
|
+
|
92
|
+
-b) COMPREPLY=".NAME."; return 0 ;;
|
93
|
+
|
96
94
|
--config) COMPREPLY=".PATH."; return 0 ;;
|
97
95
|
|
98
96
|
--debug) COMPREPLY=".BOOL."; return 0 ;;
|
99
97
|
|
100
|
-
|
101
|
-
|
102
|
-
--block-name) COMPREPLY=".NAME."; return 0 ;;
|
98
|
+
-d) COMPREPLY=".BOOL."; return 0 ;;
|
103
99
|
|
104
100
|
--filename) COMPREPLY=".RELATIVE_PATH."; return 0 ;;
|
105
101
|
|
106
|
-
|
102
|
+
-f) COMPREPLY=".RELATIVE_PATH."; return 0 ;;
|
103
|
+
|
104
|
+
--path) COMPREPLY=".RELATIVE_PATH."; return 0 ;;
|
105
|
+
|
106
|
+
-p) COMPREPLY=".RELATIVE_PATH."; return 0 ;;
|
107
|
+
|
108
|
+
--user-must-approve) COMPREPLY=".BOOL."; return 0 ;;
|
107
109
|
|
108
|
-
|
110
|
+
-q) COMPREPLY=".BOOL."; return 0 ;;
|
109
111
|
|
110
|
-
--
|
112
|
+
--display-level) COMPREPLY=".INT.0-3."; return 0 ;;
|
111
113
|
|
112
|
-
--
|
114
|
+
--list-count) COMPREPLY=".INT.1-."; return 0 ;;
|
113
115
|
|
114
116
|
--output-execution-summary) COMPREPLY=".BOOL."; return 0 ;;
|
115
117
|
|
@@ -117,26 +119,14 @@ _mde() {
|
|
117
119
|
|
118
120
|
--output-stdout) COMPREPLY=".BOOL."; return 0 ;;
|
119
121
|
|
120
|
-
--path) COMPREPLY=".RELATIVE_PATH."; return 0 ;;
|
121
|
-
|
122
122
|
--save-executed-script) COMPREPLY=".BOOL."; return 0 ;;
|
123
123
|
|
124
124
|
--save-execution-output) COMPREPLY=".BOOL."; return 0 ;;
|
125
125
|
|
126
|
-
--saved-script-chmod) COMPREPLY=".INT."; return 0 ;;
|
127
|
-
|
128
|
-
--saved-script-filename-prefix) COMPREPLY=".PREFIX."; return 0 ;;
|
129
|
-
|
130
126
|
--saved-script-folder) COMPREPLY=".RELATIVE_PATH."; return 0 ;;
|
131
127
|
|
132
|
-
--saved-script-glob) COMPREPLY=".GLOB."; return 0 ;;
|
133
|
-
|
134
128
|
--saved-stdout-folder) COMPREPLY=".RELATIVE_PATH."; return 0 ;;
|
135
129
|
|
136
|
-
--saved-stdout-glob) COMPREPLY=".GLOB."; return 0 ;;
|
137
|
-
|
138
|
-
--user-must-approve) COMPREPLY=".BOOL."; return 0 ;;
|
139
|
-
|
140
130
|
esac
|
141
131
|
fi
|
142
132
|
|
@@ -148,4 +138,4 @@ _mde() {
|
|
148
138
|
|
149
139
|
complete -o filenames -o nospace -F _mde mde
|
150
140
|
# _mde_echo_version
|
151
|
-
# echo "Updated: 2022-
|
141
|
+
# echo "Updated: 2022-10-29 23:24:15 UTC"
|
data/bin/tab_completion.sh.erb
CHANGED
@@ -30,6 +30,7 @@ _mde() {
|
|
30
30
|
if [[ ${prev} == -* ]] ; then
|
31
31
|
case $prev in
|
32
32
|
<% svhs.each do |svh|
|
33
|
+
|
33
34
|
svn = svh[:long_name]
|
34
35
|
if svn && svh[:arg_name]
|
35
36
|
svn = '--' + svh[:long_name]
|
@@ -43,6 +44,21 @@ _mde() {
|
|
43
44
|
<%= svn + ') COMPREPLY="' + svh[:compreply] + '"; return 0 ;;' %>
|
44
45
|
<% end
|
45
46
|
end
|
47
|
+
|
48
|
+
svn = svh[:short_name]
|
49
|
+
if svn && svh[:arg_name]
|
50
|
+
svn = '-' + svh[:short_name]
|
51
|
+
if svh[:compreply] == false
|
52
|
+
# nothing
|
53
|
+
elsif svh[:compreply].nil? %>
|
54
|
+
<%= svn + ') __filedirs_all; return 0 ;;' %>
|
55
|
+
<% elsif svh[:compreply].empty?
|
56
|
+
# nothing
|
57
|
+
else %>
|
58
|
+
<%= svn + ') COMPREPLY="' + svh[:compreply] + '"; return 0 ;;' %>
|
59
|
+
<% end
|
60
|
+
end
|
61
|
+
|
46
62
|
end %>
|
47
63
|
esac
|
48
64
|
fi
|
@@ -65,11 +81,18 @@ _mde() {
|
|
65
81
|
if [[ -z ${cur} ]] ; then
|
66
82
|
case $prev in
|
67
83
|
<% svhs.each do |svh|
|
84
|
+
|
68
85
|
svn = svh[:long_name]
|
69
86
|
if svn && svh[:arg_name]
|
70
87
|
svn = '--' + svh[:long_name] %>
|
71
88
|
<%= svn + ') COMPREPLY=".' + svh[:arg_name] + '."; return 0 ;;' %>
|
72
89
|
<% end
|
90
|
+
|
91
|
+
svn = svh[:short_name]
|
92
|
+
if svn && svh[:arg_name]
|
93
|
+
svn = '-' + svh[:short_name] %>
|
94
|
+
<%= svn + ') COMPREPLY=".' + svh[:arg_name] + '."; return 0 ;;' %>
|
95
|
+
<% end
|
73
96
|
end %>
|
74
97
|
esac
|
75
98
|
fi
|
data/lib/cli.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# encoding=utf-8
|
4
|
+
|
5
|
+
# utility functions to provide CLI
|
6
|
+
#
|
7
|
+
module CLI
|
8
|
+
# skip :reek:UtilityFunction
|
9
|
+
def value_for_cli(value)
|
10
|
+
case value.class.to_s
|
11
|
+
when 'String'
|
12
|
+
Shellwords.escape value
|
13
|
+
when 'FalseClass', 'TrueClass'
|
14
|
+
value ? '1' : '0'
|
15
|
+
else
|
16
|
+
Shellwords.escape value.to_s
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# encoding=utf-8
|
4
|
+
|
5
|
+
require 'optparse'
|
6
|
+
require 'yaml'
|
7
|
+
|
8
|
+
require_relative 'object_present'
|
9
|
+
|
10
|
+
class Hash
|
11
|
+
unless defined?(sym_keys)
|
12
|
+
def sym_keys
|
13
|
+
transform_keys(&:to_sym)
|
14
|
+
# map do |key, value|
|
15
|
+
# [key.to_sym, value]
|
16
|
+
# end.to_h
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class EnvironmentOptParse
|
22
|
+
attr_reader :options, :remainder
|
23
|
+
|
24
|
+
# utility functions to create menu
|
25
|
+
#
|
26
|
+
module Menu
|
27
|
+
def menu_all(menu_data, lambdas, config)
|
28
|
+
config.tap_yaml 'config'
|
29
|
+
input_option_values, remainder, = menu_parse(add_proc(menu_data, lambdas))
|
30
|
+
# options = menu_default_option_values(menu_data).merge input_option_values
|
31
|
+
# options = (menu_default_option_values(menu_data)).merge(input_option_values).tap_yaml 'options1'
|
32
|
+
options = menu_default_option_values(menu_data).merge(config).merge(input_option_values) #.tap_yaml 'options2'
|
33
|
+
# options = menu_data.map do |menu_item|
|
34
|
+
# menu_item.tap_inspect 'menu_item'
|
35
|
+
# mion = menu_item[:opt_name]&.to_sym.tap_inspect 'mion'
|
36
|
+
# omion = config[mion].tap_inspect 'omion'
|
37
|
+
# unless omion.nil?
|
38
|
+
# menu_item[:default] = omion
|
39
|
+
# end
|
40
|
+
# menu_item
|
41
|
+
# end,
|
42
|
+
|
43
|
+
[options, remainder]
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_proc(menu_data, lambdas)
|
47
|
+
menu_data.each do |menu_item|
|
48
|
+
menu_item.tap_yaml 'menu_item'
|
49
|
+
procname = menu_item[:procname]
|
50
|
+
next if procname.nil?
|
51
|
+
|
52
|
+
menu_item[:proccode] = lambdas.fetch(procname.to_sym, menu_item[:procname])
|
53
|
+
end.tap_yaml
|
54
|
+
end
|
55
|
+
|
56
|
+
def menu_default_option_values(menu_data)
|
57
|
+
menu_data.map do |item|
|
58
|
+
item_default = item[:default]
|
59
|
+
next if item_default.nil?
|
60
|
+
next unless item[:opt_name].present?
|
61
|
+
|
62
|
+
[item[:opt_name].to_sym, item_default]
|
63
|
+
end.compact.to_h
|
64
|
+
end
|
65
|
+
|
66
|
+
def menu_help(menu_data)
|
67
|
+
options = {}
|
68
|
+
option_parser = OptionParser.new do |opts|
|
69
|
+
opts.banner = [
|
70
|
+
"#{APP_NAME} - #{APP_DESC} (#{VERSION})",
|
71
|
+
"Usage: #{File.basename($PROGRAM_NAME)} [options]"
|
72
|
+
].join("\n")
|
73
|
+
|
74
|
+
menu_data.each do |item|
|
75
|
+
menu_option_append opts, options, item
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
option_parser.help
|
80
|
+
end
|
81
|
+
|
82
|
+
def menu_option_append(opts, options, item)
|
83
|
+
return unless item[:long_name].present? || item[:short_name].present?
|
84
|
+
|
85
|
+
mmoo = [
|
86
|
+
# long name
|
87
|
+
if item[:long_name].present?
|
88
|
+
"--#{item[:long_name]}#{item[:arg_name].present? ? " #{item[:arg_name]}" : ''}"
|
89
|
+
end,
|
90
|
+
|
91
|
+
# short name
|
92
|
+
item[:short_name].present? ? "-#{item[:short_name]}" : nil,
|
93
|
+
|
94
|
+
# description and default
|
95
|
+
[item[:description],
|
96
|
+
item[:default].present? ? "[#{value_for_menu item[:default]}]" : nil].compact.join(' '),
|
97
|
+
|
98
|
+
# apply proccode, if present, to value
|
99
|
+
# save value to options hash if option is named
|
100
|
+
#
|
101
|
+
lambda { |value|
|
102
|
+
(item[:proccode] ? item[:proccode].call(value) : value).tap do |converted|
|
103
|
+
opt_name = item[:opt_name]
|
104
|
+
next if opt_name.nil?
|
105
|
+
|
106
|
+
options[opt_name.to_sym] = converted if item[:opt_name]
|
107
|
+
end
|
108
|
+
}
|
109
|
+
].compact
|
110
|
+
opts.on(*mmoo)
|
111
|
+
end
|
112
|
+
|
113
|
+
def menu_parse(menu_options)
|
114
|
+
options = {}
|
115
|
+
option_parser = OptionParser.new do |opts|
|
116
|
+
menu_options.each do |item|
|
117
|
+
item[:opt_name] = item[:opt_name]&.to_sym
|
118
|
+
menu_option_append opts, options, item
|
119
|
+
end
|
120
|
+
end
|
121
|
+
option_parser.load # filename defaults to basename of the program without suffix in a directory ~/.options
|
122
|
+
option_parser.environment # env defaults to the basename of the program.
|
123
|
+
remainder = option_parser.parse!
|
124
|
+
|
125
|
+
[options, remainder, option_parser.help]
|
126
|
+
end
|
127
|
+
|
128
|
+
# skip :reek:UtilityFunction
|
129
|
+
def value_for_menu(value)
|
130
|
+
case value.class.to_s
|
131
|
+
when 'String'
|
132
|
+
value
|
133
|
+
when 'FalseClass', 'TrueClass'
|
134
|
+
value ? '1' : '0'
|
135
|
+
else
|
136
|
+
value.to_s
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
include Menu
|
142
|
+
|
143
|
+
def initialize(menu: {}, lambdas: nil, options: nil, version: nil)
|
144
|
+
@menu = if menu.class.to_s == 'String'
|
145
|
+
filetext = File.read(menu).tap_yaml 'filetext'
|
146
|
+
fileyaml = YAML.load(filetext)
|
147
|
+
fileyaml.map(&:sym_keys)
|
148
|
+
else
|
149
|
+
menu
|
150
|
+
end.tap_yaml '@menu'
|
151
|
+
@lambdas = lambdas
|
152
|
+
@version = version || '0.1'
|
153
|
+
# @options = {}
|
154
|
+
@options = if options.class.to_s == 'String'
|
155
|
+
YAML.safe_load(File.read(options)).sym_keys.tap_yaml '@options'
|
156
|
+
else
|
157
|
+
{}
|
158
|
+
end #.tap_yaml '@options'
|
159
|
+
|
160
|
+
parse!
|
161
|
+
end
|
162
|
+
|
163
|
+
def parse!
|
164
|
+
@options, @remainder = menu_all(
|
165
|
+
@menu,
|
166
|
+
# @menu.map do |menu_item|
|
167
|
+
# menu_item.tap_inspect 'menu_item'
|
168
|
+
# mion = menu_item[:opt_name]&.to_sym.tap_inspect 'mion'
|
169
|
+
# omion = @options[mion].tap_inspect 'omion'
|
170
|
+
# unless omion.nil?
|
171
|
+
# @options[menu_item[:default]] = omion
|
172
|
+
# end
|
173
|
+
# menu_item
|
174
|
+
# end,
|
175
|
+
{
|
176
|
+
debug: ->(value) { tap_config value: value },
|
177
|
+
|
178
|
+
# stdout_configuration: lambda { |_| self.options.tap_puts 'eop' },
|
179
|
+
# stdout_configuration: (lambda { |options|
|
180
|
+
# lambda { |v| options.tap_puts 'eop_l' }
|
181
|
+
# }).call(@options),
|
182
|
+
|
183
|
+
stdout_defaults: ->(_) { menu_default_option_values(@menu).to_yaml.tap_puts },
|
184
|
+
stdout_help: lambda { |_|
|
185
|
+
menu_help(@menu).tap_puts
|
186
|
+
exit
|
187
|
+
},
|
188
|
+
val_as_bool: ->(value) { value.class.to_s == 'String' ? (value.chomp != '0') : value },
|
189
|
+
val_as_int: ->(value) { value.to_i },
|
190
|
+
val_as_str: ->(value) { value.to_s },
|
191
|
+
version: lambda { |_|
|
192
|
+
@version.tap_puts
|
193
|
+
exit
|
194
|
+
}
|
195
|
+
}.merge(@lambdas || {}),
|
196
|
+
@options
|
197
|
+
)
|
198
|
+
@options #.tap_yaml '@options'
|
199
|
+
end
|
200
|
+
end
|
data/lib/globfiles.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# encoding=utf-8
|
4
|
+
|
5
|
+
# require 'json'
|
6
|
+
# require 'yaml'
|
7
|
+
|
8
|
+
# require_relative 'env'
|
9
|
+
# include Env
|
10
|
+
|
11
|
+
## directory listing of saved files matching glob
|
12
|
+
#
|
13
|
+
class Globfiles
|
14
|
+
def initialize(folder, glob)
|
15
|
+
@folder = folder
|
16
|
+
@glob = glob
|
17
|
+
end
|
18
|
+
|
19
|
+
def list_all
|
20
|
+
Dir.glob(File.join(@folder, @glob)).tap_inspect
|
21
|
+
end
|
22
|
+
|
23
|
+
## single most recent item
|
24
|
+
#
|
25
|
+
def most_recent(arr = nil)
|
26
|
+
arr = list_all if arr.nil?
|
27
|
+
return if arr.count < 1
|
28
|
+
|
29
|
+
arr.max.tap_inspect
|
30
|
+
end
|
31
|
+
|
32
|
+
## multiple recent items
|
33
|
+
#
|
34
|
+
def most_recent_list(list_count, arr = nil)
|
35
|
+
arr = list_all if arr.nil?
|
36
|
+
return if (ac = arr.count) < 1
|
37
|
+
|
38
|
+
arr.sort[-[ac, list_count].min..].reverse.tap_inspect
|
39
|
+
end
|
40
|
+
end # class Globfiles
|