markdown_exec 0.2.6 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +77 -11
- data/Gemfile +7 -5
- data/Gemfile.lock +16 -1
- data/README.md +38 -12
- data/Rakefile +35 -1
- data/assets/approve_code.png +0 -0
- data/assets/output_of_execution.png +0 -0
- data/assets/select_a_block.png +0 -0
- data/assets/select_a_file.png +0 -0
- data/bin/tab_completion.sh +143 -0
- data/bin/tab_completion.sh.erb +85 -0
- data/lib/markdown_exec/version.rb +3 -2
- data/lib/markdown_exec.rb +809 -262
- data/lib/shared.rb +121 -0
- metadata +43 -11
- data/fixtures/bash1.md +0 -12
- data/fixtures/bash2.md +0 -15
- data/fixtures/exclude1.md +0 -6
- data/fixtures/exclude2.md +0 -9
- data/fixtures/exec1.md +0 -8
- data/fixtures/heading1.md +0 -19
- data/fixtures/sample1.md +0 -9
- data/fixtures/title1.md +0 -6
data/lib/markdown_exec.rb
CHANGED
@@ -3,38 +3,15 @@
|
|
3
3
|
|
4
4
|
# encoding=utf-8
|
5
5
|
|
6
|
+
require 'English'
|
7
|
+
require 'clipboard'
|
8
|
+
require 'mrdialog'
|
6
9
|
require 'open3'
|
7
10
|
require 'optparse'
|
8
11
|
require 'tty-prompt'
|
9
12
|
require 'yaml'
|
10
13
|
|
11
|
-
|
12
|
-
# default if nil
|
13
|
-
# false if empty or '0'
|
14
|
-
# else true
|
15
|
-
|
16
|
-
def env_bool(name, default: false)
|
17
|
-
return default if name.nil? || (val = ENV[name]).nil?
|
18
|
-
return false if val.empty? || val == '0'
|
19
|
-
|
20
|
-
true
|
21
|
-
end
|
22
|
-
|
23
|
-
def env_int(name, default: 0)
|
24
|
-
return default if name.nil? || (val = ENV[name]).nil?
|
25
|
-
return default if val.empty?
|
26
|
-
|
27
|
-
val.to_i
|
28
|
-
end
|
29
|
-
|
30
|
-
def env_str(name, default: '')
|
31
|
-
return default if name.nil? || (val = ENV[name]).nil?
|
32
|
-
|
33
|
-
val || default
|
34
|
-
end
|
35
|
-
|
36
|
-
$pdebug = env_bool 'MDE_DEBUG'
|
37
|
-
|
14
|
+
require_relative 'shared'
|
38
15
|
require_relative 'markdown_exec/version'
|
39
16
|
|
40
17
|
$stderr.sync = true
|
@@ -62,27 +39,17 @@ end
|
|
62
39
|
|
63
40
|
public
|
64
41
|
|
65
|
-
#
|
66
|
-
#
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
:to_yaml
|
77
|
-
else
|
78
|
-
:inspect
|
79
|
-
end
|
80
|
-
|
81
|
-
puts "-> #{caller[0].scan(/in `?(\S+)'$/)[0][0]}()" \
|
82
|
-
" #{name}: #{method(fn).call}"
|
83
|
-
|
84
|
-
self
|
85
|
-
end
|
42
|
+
# display_level values
|
43
|
+
DISPLAY_LEVEL_BASE = 0 # required output
|
44
|
+
DISPLAY_LEVEL_ADMIN = 1
|
45
|
+
DISPLAY_LEVEL_DEBUG = 2
|
46
|
+
DISPLAY_LEVEL_DEFAULT = DISPLAY_LEVEL_ADMIN
|
47
|
+
DISPLAY_LEVEL_MAX = DISPLAY_LEVEL_DEBUG
|
48
|
+
|
49
|
+
# @execute_files[ind] = @execute_files[ind] + [block]
|
50
|
+
EF_STDOUT = 0
|
51
|
+
EF_STDERR = 1
|
52
|
+
EF_STDIN = 2
|
86
53
|
|
87
54
|
module MarkdownExec
|
88
55
|
class Error < StandardError; end
|
@@ -101,14 +68,22 @@ module MarkdownExec
|
|
101
68
|
# options necessary to start, parse input, defaults for cli options
|
102
69
|
|
103
70
|
def base_options
|
104
|
-
|
105
|
-
.
|
106
|
-
next unless opt_name.present?
|
107
|
-
|
108
|
-
|
71
|
+
menu_iter do |item|
|
72
|
+
item.tap_inspect name: :item, format: :yaml
|
73
|
+
next unless item[:opt_name].present?
|
74
|
+
|
75
|
+
item_default = item[:default]
|
76
|
+
item_default.tap_inspect name: :item_default
|
77
|
+
value = if item_default.nil?
|
78
|
+
item_default
|
79
|
+
else
|
80
|
+
env_str(item[:env_var], default: value_for_hash(item_default))
|
81
|
+
end
|
82
|
+
[item[:opt_name], item[:proc1] ? item[:proc1].call(value) : value]
|
109
83
|
end.compact.to_h.merge(
|
110
84
|
{
|
111
85
|
mdheadings: true, # use headings (levels 1,2,3) in block lable
|
86
|
+
menu_exit_at_top: true,
|
112
87
|
menu_with_exit: true
|
113
88
|
}
|
114
89
|
).tap_inspect format: :yaml
|
@@ -123,6 +98,7 @@ module MarkdownExec
|
|
123
98
|
prompt_approve_block: 'Process?',
|
124
99
|
prompt_select_block: 'Choose a block:',
|
125
100
|
prompt_select_md: 'Choose a file:',
|
101
|
+
prompt_select_output: 'Choose a file:',
|
126
102
|
saved_script_filename: nil, # calculated
|
127
103
|
struct: true # allow get_block_summary()
|
128
104
|
}
|
@@ -139,13 +115,43 @@ module MarkdownExec
|
|
139
115
|
display_command(opts, required_blocks) if opts[:output_script] || opts[:user_must_approve]
|
140
116
|
|
141
117
|
allow = true
|
142
|
-
|
143
|
-
|
118
|
+
if opts[:user_must_approve]
|
119
|
+
loop do
|
120
|
+
# (sel = @prompt.select(opts[:prompt_approve_block], %w(Yes No Copy_script_to_clipboard Save_script), cycle: true)).tap_inspect name: :sel
|
121
|
+
(sel = @prompt.select(opts[:prompt_approve_block], filter: true) do |menu|
|
122
|
+
menu.default 1
|
123
|
+
# menu.enum '.'
|
124
|
+
# menu.filter true
|
125
|
+
|
126
|
+
menu.choice 'Yes', 1
|
127
|
+
menu.choice 'No', 2
|
128
|
+
menu.choice 'Copy script to clipboard', 3
|
129
|
+
menu.choice 'Save script', 4
|
130
|
+
end).tap_inspect name: :sel
|
131
|
+
allow = (sel == 1)
|
132
|
+
if sel == 3
|
133
|
+
text = required_blocks.flatten.join($INPUT_RECORD_SEPARATOR)
|
134
|
+
Clipboard.copy(text)
|
135
|
+
fout "Clipboard updated: #{required_blocks.count} blocks, #{required_blocks.flatten.count} lines, #{text.length} characters"
|
136
|
+
end
|
137
|
+
if sel == 4
|
138
|
+
# opts[:saved_script_filename] = saved_name_make(opts)
|
139
|
+
write_command_file(opts.merge(save_executed_script: true), required_blocks)
|
140
|
+
fout "File saved: #{@options[:saved_filespec]}"
|
141
|
+
end
|
142
|
+
break if [1, 2].include? sel
|
143
|
+
end
|
144
|
+
end
|
145
|
+
(opts[:ir_approve] = allow).tap_inspect name: :allow
|
146
|
+
|
144
147
|
selected = get_block_by_name blocks_in_file, opts[:block_name]
|
145
148
|
|
146
149
|
if opts[:ir_approve]
|
147
|
-
write_command_file
|
150
|
+
write_command_file opts, required_blocks
|
148
151
|
command_execute opts, required_blocks.flatten.join("\n")
|
152
|
+
save_execution_output
|
153
|
+
output_execution_summary
|
154
|
+
output_execution_result
|
149
155
|
end
|
150
156
|
|
151
157
|
selected[:name]
|
@@ -164,40 +170,49 @@ module MarkdownExec
|
|
164
170
|
@execute_files = Hash.new([])
|
165
171
|
@execute_options = opts
|
166
172
|
@execute_started_at = Time.now.utc
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
until
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
# do nothing at EOF
|
187
|
-
end
|
188
|
-
end
|
173
|
+
|
174
|
+
Open3.popen3(@options[:shell], '-c', cmd2) do |stdin, stdout, stderr, exec_thr|
|
175
|
+
# pid = exec_thr.pid # pid of the started process
|
176
|
+
|
177
|
+
t1 = Thread.new do
|
178
|
+
until (line = stdout.gets).nil?
|
179
|
+
@execute_files[EF_STDOUT] = @execute_files[EF_STDOUT] + [line]
|
180
|
+
print line if opts[:output_stdout]
|
181
|
+
yield nil, line, nil, exec_thr if block_given?
|
182
|
+
end
|
183
|
+
rescue IOError => e
|
184
|
+
# thread killed, do nothing
|
185
|
+
end
|
186
|
+
|
187
|
+
t2 = Thread.new do
|
188
|
+
until (line = stderr.gets).nil?
|
189
|
+
@execute_files[EF_STDERR] = @execute_files[EF_STDERR] + [line]
|
190
|
+
print line if opts[:output_stdout]
|
191
|
+
yield nil, nil, line, exec_thr if block_given?
|
189
192
|
end
|
190
193
|
rescue IOError => e
|
191
|
-
|
194
|
+
# thread killed, do nothing
|
192
195
|
end
|
193
|
-
|
196
|
+
|
197
|
+
in_thr = Thread.new do
|
198
|
+
while exec_thr.alive? # reading input until the child process ends
|
199
|
+
stdin.puts(line = $stdin.gets)
|
200
|
+
@execute_files[EF_STDIN] = @execute_files[EF_STDIN] + [line]
|
201
|
+
yield line, nil, nil, exec_thr if block_given?
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
exec_thr.join
|
206
|
+
in_thr.kill
|
207
|
+
# @return_code = exec_thr.value
|
194
208
|
end
|
209
|
+
@execute_completed_at = Time.now.utc
|
195
210
|
rescue Errno::ENOENT => e
|
196
211
|
# error triggered by missing command in script
|
197
212
|
@execute_aborted_at = Time.now.utc
|
198
213
|
@execute_error_message = e.message
|
199
214
|
@execute_error = e
|
200
|
-
@execute_files[
|
215
|
+
@execute_files[EF_STDERR] += [e.message]
|
201
216
|
fout "Error ENOENT: #{e.inspect}"
|
202
217
|
end
|
203
218
|
|
@@ -211,7 +226,9 @@ module MarkdownExec
|
|
211
226
|
end
|
212
227
|
|
213
228
|
def display_command(_opts, required_blocks)
|
229
|
+
fout ' #=#=#'.yellow
|
214
230
|
required_blocks.each { |cb| fout cb }
|
231
|
+
fout ' #=#=#'.yellow
|
215
232
|
end
|
216
233
|
|
217
234
|
def exec_block(options, _block_name = '')
|
@@ -221,41 +238,143 @@ module MarkdownExec
|
|
221
238
|
# document and block reports
|
222
239
|
#
|
223
240
|
files = list_files_per_options(options)
|
224
|
-
if @options[:list_blocks]
|
225
|
-
fout_list (files.map do |file|
|
226
|
-
make_block_labels(filename: file, struct: true)
|
227
|
-
end).flatten(1)
|
228
|
-
return
|
229
|
-
end
|
230
|
-
|
231
|
-
if @options[:list_default_yaml]
|
232
|
-
fout_list list_default_yaml
|
233
|
-
return
|
234
|
-
end
|
235
|
-
|
236
|
-
if @options[:list_docs]
|
237
|
-
fout_list files
|
238
|
-
return
|
239
|
-
end
|
240
|
-
|
241
|
-
if @options[:list_default_env]
|
242
|
-
fout_list list_default_env
|
243
|
-
return
|
244
|
-
end
|
245
|
-
|
246
|
-
if @options[:list_recent_scripts]
|
247
|
-
fout_list list_recent_scripts
|
248
|
-
return
|
249
|
-
end
|
250
|
-
|
251
|
-
if @options[:run_last_script]
|
252
|
-
run_last_script
|
253
|
-
return
|
254
|
-
end
|
255
241
|
|
256
|
-
|
257
|
-
|
258
|
-
|
242
|
+
simple_commands = {
|
243
|
+
doc_glob: -> { fout options[:md_filename_glob] },
|
244
|
+
list_blocks: lambda do
|
245
|
+
fout_list (files.map do |file|
|
246
|
+
make_block_labels(filename: file, struct: true)
|
247
|
+
end).flatten(1)
|
248
|
+
end,
|
249
|
+
list_default_yaml: -> { fout_list list_default_yaml },
|
250
|
+
list_docs: -> { fout_list files },
|
251
|
+
list_default_env: -> { fout_list list_default_env },
|
252
|
+
list_recent_output: -> { fout_list list_recent_output },
|
253
|
+
list_recent_scripts: -> { fout_list list_recent_scripts },
|
254
|
+
pwd: -> { fout File.expand_path('..', __dir__) },
|
255
|
+
pwd3: lambda {
|
256
|
+
text = 'A B C'
|
257
|
+
items = []
|
258
|
+
Struct.new('BuildListData', :tag, :item, :status)
|
259
|
+
data = Struct::BuildListData.new
|
260
|
+
|
261
|
+
data.tag = '1'
|
262
|
+
data.item = 'Item number 1'
|
263
|
+
data.status = true
|
264
|
+
items.push(data.to_a)
|
265
|
+
|
266
|
+
data = Struct::BuildListData.new
|
267
|
+
data.tag = '2'
|
268
|
+
data.item = 'Item number 2'
|
269
|
+
data.status = false
|
270
|
+
items.push(data.to_a)
|
271
|
+
|
272
|
+
data = Struct::BuildListData.new
|
273
|
+
data.tag = '3'
|
274
|
+
data.item = 'Item number 3'
|
275
|
+
data.status = false
|
276
|
+
items.push(data.to_a)
|
277
|
+
|
278
|
+
data = Struct::BuildListData.new
|
279
|
+
data.tag = '4'
|
280
|
+
data.item = 'Item number 4'
|
281
|
+
data.status = true
|
282
|
+
items.push(data.to_a)
|
283
|
+
|
284
|
+
data = Struct::BuildListData.new
|
285
|
+
data.tag = '5'
|
286
|
+
data.item = 'Item number 5'
|
287
|
+
data.status = false
|
288
|
+
items.push(data.to_a)
|
289
|
+
|
290
|
+
data = Struct::BuildListData.new
|
291
|
+
data.tag = '6'
|
292
|
+
data.item = 'Item number 6'
|
293
|
+
data.status = true
|
294
|
+
items.push(data.to_a)
|
295
|
+
|
296
|
+
dialog = MRDialog.new
|
297
|
+
dialog.clear = true
|
298
|
+
dialog.shadow = false
|
299
|
+
dialog.title = 'BUILDLIST'
|
300
|
+
# dialog.logger = Logger.new(ENV["HOME"] + "/dialog_" + ME + ".log")
|
301
|
+
|
302
|
+
height = 0
|
303
|
+
width = 0
|
304
|
+
listheight = 0
|
305
|
+
|
306
|
+
selected_items = dialog.buildlist(text, items, height, width, listheight)
|
307
|
+
exit_code = dialog.exit_code
|
308
|
+
puts "Exit code: #{exit_code}"
|
309
|
+
puts 'Selecetd tags:'
|
310
|
+
selected_items.each do |item|
|
311
|
+
puts " '#{item}'"
|
312
|
+
end
|
313
|
+
},
|
314
|
+
pwd1: lambda {
|
315
|
+
dialog = MRDialog.new
|
316
|
+
dialog.clear = true
|
317
|
+
dialog.title = 'YES/NO BOX'
|
318
|
+
puts "yesno: #{dialog.yesno('ABC', 0, 0)}"
|
319
|
+
},
|
320
|
+
pwd2: lambda {
|
321
|
+
dialog = MRDialog.new
|
322
|
+
# dialog.logger = Logger.new(ENV["HOME"] + "/dialog_" + ME + ".log")
|
323
|
+
dialog.clear = true
|
324
|
+
dialog.title = 'MENU BOX'
|
325
|
+
text = 'textextst'
|
326
|
+
items = []
|
327
|
+
menu_data = Struct.new(:tag, :item)
|
328
|
+
data = menu_data.new
|
329
|
+
data.tag = 'Linux'
|
330
|
+
data.item = 'The Great Unix Clone for 386/486'
|
331
|
+
items.push(data.to_a)
|
332
|
+
|
333
|
+
data = menu_data.new
|
334
|
+
data.tag = 'NetBSD'
|
335
|
+
data.item = 'Another free Unix Clone for 386/486'
|
336
|
+
items.push(data.to_a)
|
337
|
+
|
338
|
+
data = menu_data.new
|
339
|
+
data.tag = 'OS/2'
|
340
|
+
data.item = 'IBM OS/2'
|
341
|
+
items.push(data.to_a)
|
342
|
+
|
343
|
+
data = menu_data.new
|
344
|
+
data.tag = 'WIN NT'
|
345
|
+
data.item = 'Microsoft Windows NT'
|
346
|
+
items.push(data.to_a)
|
347
|
+
|
348
|
+
data = menu_data.new
|
349
|
+
data.tag = 'PCDOS'
|
350
|
+
data.item = 'IBM PC DOS'
|
351
|
+
items.push(data.to_a)
|
352
|
+
|
353
|
+
data = menu_data.new
|
354
|
+
data.tag = 'MSDOS'
|
355
|
+
data.item = 'Microsoft DOS'
|
356
|
+
items.push(data.to_a)
|
357
|
+
|
358
|
+
height = 0
|
359
|
+
width = 0
|
360
|
+
menu_height = 4
|
361
|
+
|
362
|
+
selected_item = dialog.menu(text, items, height, width, menu_height)
|
363
|
+
|
364
|
+
puts "Selected item: #{selected_item}"
|
365
|
+
},
|
366
|
+
# pwd: -> { fout `dialog --yesno "ABC" 99 99` },
|
367
|
+
run_last_script: -> { run_last_script },
|
368
|
+
select_recent_output: -> { select_recent_output },
|
369
|
+
select_recent_script: -> { select_recent_script },
|
370
|
+
tab_completions: -> { fout tab_completions },
|
371
|
+
menu_export: -> { fout menu_export }
|
372
|
+
}
|
373
|
+
simple_commands.each_key do |key|
|
374
|
+
if @options[key]
|
375
|
+
simple_commands[key].call
|
376
|
+
return # rubocop:disable Lint/NonLocalExitFromIterator
|
377
|
+
end
|
259
378
|
end
|
260
379
|
|
261
380
|
# process
|
@@ -266,8 +385,6 @@ module MarkdownExec
|
|
266
385
|
struct: true
|
267
386
|
)
|
268
387
|
fout "saved_filespec: #{@execute_script_filespec}" if @options[:output_saved_script_filename]
|
269
|
-
save_execution_output
|
270
|
-
output_execution_summary
|
271
388
|
end
|
272
389
|
|
273
390
|
# standard output; not for debug
|
@@ -304,6 +421,19 @@ module MarkdownExec
|
|
304
421
|
end
|
305
422
|
end
|
306
423
|
|
424
|
+
def approved_fout?(level)
|
425
|
+
level <= @options[:display_level]
|
426
|
+
end
|
427
|
+
|
428
|
+
# display output at level or lower than filter (DISPLAY_LEVEL_DEFAULT)
|
429
|
+
#
|
430
|
+
def lout(str, level: DISPLAY_LEVEL_BASE)
|
431
|
+
return unless approved_fout? level
|
432
|
+
|
433
|
+
# fout level == DISPLAY_LEVEL_BASE ? str : DISPLAY_LEVEL_XBASE_PREFIX + str
|
434
|
+
fout level == DISPLAY_LEVEL_BASE ? str : @options[:display_level_xbase_prefix] + str
|
435
|
+
end
|
436
|
+
|
307
437
|
def list_blocks_in_file(call_options = {}, &options_block)
|
308
438
|
opts = optsmerge call_options, options_block
|
309
439
|
|
@@ -312,6 +442,11 @@ module MarkdownExec
|
|
312
442
|
exit 1
|
313
443
|
end
|
314
444
|
|
445
|
+
unless File.exist? opts[:filename]
|
446
|
+
fout 'Document is missing.'
|
447
|
+
exit 1
|
448
|
+
end
|
449
|
+
|
315
450
|
fenced_start_and_end_match = Regexp.new opts[:fenced_start_and_end_match]
|
316
451
|
fenced_start_ex = Regexp.new opts[:fenced_start_ex_match]
|
317
452
|
block_title = ''
|
@@ -367,25 +502,23 @@ module MarkdownExec
|
|
367
502
|
end
|
368
503
|
|
369
504
|
def list_default_env
|
370
|
-
|
371
|
-
|
372
|
-
next unless env_var.present?
|
505
|
+
menu_iter do |item|
|
506
|
+
next unless item[:env_var].present?
|
373
507
|
|
374
508
|
[
|
375
|
-
"#{env_var}=#{value_for_cli default}",
|
376
|
-
description.present? ? description : nil
|
509
|
+
"#{item[:env_var]}=#{value_for_cli item[:default]}",
|
510
|
+
item[:description].present? ? item[:description] : nil
|
377
511
|
].compact.join(' # ')
|
378
512
|
end.compact.sort
|
379
513
|
end
|
380
514
|
|
381
515
|
def list_default_yaml
|
382
|
-
|
383
|
-
|
384
|
-
next unless opt_name.present? && default.present?
|
516
|
+
menu_iter do |item|
|
517
|
+
next unless item[:opt_name].present? && item[:default].present?
|
385
518
|
|
386
519
|
[
|
387
|
-
"#{opt_name}: #{value_for_yaml default}",
|
388
|
-
description.present? ? description : nil
|
520
|
+
"#{item[:opt_name]}: #{value_for_yaml item[:default]}",
|
521
|
+
item[:description].present? ? item[:description] : nil
|
389
522
|
].compact.join(' # ')
|
390
523
|
end.compact.sort
|
391
524
|
end
|
@@ -451,9 +584,28 @@ module MarkdownExec
|
|
451
584
|
.tap_inspect
|
452
585
|
end
|
453
586
|
|
587
|
+
def most_recent(arr)
|
588
|
+
return unless arr
|
589
|
+
return if arr.count < 1
|
590
|
+
|
591
|
+
arr.max.tap_inspect
|
592
|
+
end
|
593
|
+
|
594
|
+
def most_recent_list(arr)
|
595
|
+
return unless arr
|
596
|
+
return if (ac = arr.count) < 1
|
597
|
+
|
598
|
+
arr.sort[-[ac, options[:list_count]].min..].reverse.tap_inspect
|
599
|
+
end
|
600
|
+
|
601
|
+
def list_recent_output
|
602
|
+
most_recent_list(Dir.glob(File.join(@options[:saved_stdout_folder],
|
603
|
+
@options[:saved_stdout_glob]))).tap_inspect
|
604
|
+
end
|
605
|
+
|
454
606
|
def list_recent_scripts
|
455
|
-
Dir.glob(File.join(@options[:saved_script_folder],
|
456
|
-
|
607
|
+
most_recent_list(Dir.glob(File.join(@options[:saved_script_folder],
|
608
|
+
@options[:saved_script_glob]))).tap_inspect
|
457
609
|
end
|
458
610
|
|
459
611
|
def make_block_label(block, call_options = {})
|
@@ -475,76 +627,400 @@ module MarkdownExec
|
|
475
627
|
end.compact.tap_inspect
|
476
628
|
end
|
477
629
|
|
478
|
-
def
|
479
|
-
val_as_bool = ->(value) { value.
|
630
|
+
def menu_data1
|
631
|
+
val_as_bool = ->(value) { value.class.to_s == 'String' ? (value.chomp != '0') : value }
|
480
632
|
val_as_int = ->(value) { value.to_i }
|
481
633
|
val_as_str = ->(value) { value.to_s }
|
482
|
-
val_true = ->(
|
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
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
634
|
+
# val_true = ->(_value) { true } # for commands, sets option to true
|
635
|
+
set1 = [
|
636
|
+
{
|
637
|
+
arg_name: 'PATH',
|
638
|
+
default: '.',
|
639
|
+
description: 'Read configuration file',
|
640
|
+
long_name: 'config',
|
641
|
+
proc1: lambda { |value|
|
642
|
+
read_configuration_file! options, value
|
643
|
+
}
|
644
|
+
},
|
645
|
+
{
|
646
|
+
arg_name: 'BOOL',
|
647
|
+
default: false,
|
648
|
+
description: 'Debug output',
|
649
|
+
env_var: 'MDE_DEBUG',
|
650
|
+
long_name: 'debug',
|
651
|
+
short_name: 'd',
|
652
|
+
proc1: lambda { |value|
|
653
|
+
$pdebug = value.to_i != 0
|
654
|
+
}
|
655
|
+
},
|
656
|
+
{
|
657
|
+
arg_name: "INT.#{DISPLAY_LEVEL_BASE}-#{DISPLAY_LEVEL_MAX}",
|
658
|
+
default: DISPLAY_LEVEL_DEFAULT,
|
659
|
+
description: "Output display level (#{DISPLAY_LEVEL_BASE} to #{DISPLAY_LEVEL_MAX})",
|
660
|
+
env_var: 'MDE_DISPLAY_LEVEL',
|
661
|
+
long_name: 'display-level',
|
662
|
+
opt_name: :display_level,
|
663
|
+
proc1: val_as_int
|
664
|
+
},
|
665
|
+
{
|
666
|
+
arg_name: 'NAME',
|
667
|
+
compreply: false,
|
668
|
+
description: 'Name of block',
|
669
|
+
env_var: 'MDE_BLOCK_NAME',
|
670
|
+
long_name: 'block-name',
|
671
|
+
opt_name: :block_name,
|
672
|
+
short_name: 'f',
|
673
|
+
proc1: val_as_str
|
674
|
+
},
|
675
|
+
{
|
676
|
+
arg_name: 'RELATIVE_PATH',
|
677
|
+
compreply: '.',
|
678
|
+
description: 'Name of document',
|
679
|
+
env_var: 'MDE_FILENAME',
|
680
|
+
long_name: 'filename',
|
681
|
+
opt_name: :filename,
|
682
|
+
short_name: 'f',
|
683
|
+
proc1: val_as_str
|
684
|
+
},
|
685
|
+
{
|
686
|
+
description: 'List blocks',
|
687
|
+
long_name: 'list-blocks',
|
688
|
+
opt_name: :list_blocks,
|
689
|
+
proc1: val_as_bool
|
690
|
+
},
|
691
|
+
{
|
692
|
+
arg_name: 'INT.1-',
|
693
|
+
default: 32,
|
694
|
+
description: 'Max. items to return in list',
|
695
|
+
env_var: 'MDE_LIST_COUNT',
|
696
|
+
long_name: 'list-count',
|
697
|
+
opt_name: :list_count,
|
698
|
+
proc1: val_as_int
|
699
|
+
},
|
700
|
+
{
|
701
|
+
description: 'List default configuration as environment variables',
|
702
|
+
long_name: 'list-default-env',
|
703
|
+
opt_name: :list_default_env
|
704
|
+
},
|
705
|
+
{
|
706
|
+
description: 'List default configuration as YAML',
|
707
|
+
long_name: 'list-default-yaml',
|
708
|
+
opt_name: :list_default_yaml
|
709
|
+
},
|
710
|
+
{
|
711
|
+
description: 'List docs in current folder',
|
712
|
+
long_name: 'list-docs',
|
713
|
+
opt_name: :list_docs,
|
714
|
+
proc1: val_as_bool
|
715
|
+
},
|
716
|
+
{
|
717
|
+
description: 'List recent saved output',
|
718
|
+
long_name: 'list-recent-output',
|
719
|
+
opt_name: :list_recent_output,
|
720
|
+
proc1: val_as_bool
|
721
|
+
},
|
722
|
+
{
|
723
|
+
description: 'List recent saved scripts',
|
724
|
+
long_name: 'list-recent-scripts',
|
725
|
+
opt_name: :list_recent_scripts,
|
726
|
+
proc1: val_as_bool
|
727
|
+
},
|
728
|
+
{
|
729
|
+
arg_name: 'PREFIX',
|
730
|
+
default: MarkdownExec::BIN_NAME,
|
731
|
+
description: 'Name prefix for stdout files',
|
732
|
+
env_var: 'MDE_LOGGED_STDOUT_FILENAME_PREFIX',
|
733
|
+
long_name: 'logged-stdout-filename-prefix',
|
734
|
+
opt_name: :logged_stdout_filename_prefix,
|
735
|
+
proc1: val_as_str
|
736
|
+
},
|
737
|
+
{
|
738
|
+
arg_name: 'BOOL',
|
739
|
+
default: false,
|
740
|
+
description: 'Display summary for execution',
|
741
|
+
env_var: 'MDE_OUTPUT_EXECUTION_SUMMARY',
|
742
|
+
long_name: 'output-execution-summary',
|
743
|
+
opt_name: :output_execution_summary,
|
744
|
+
proc1: val_as_bool
|
745
|
+
},
|
746
|
+
{
|
747
|
+
arg_name: 'BOOL',
|
748
|
+
default: false,
|
749
|
+
description: 'Display script prior to execution',
|
750
|
+
env_var: 'MDE_OUTPUT_SCRIPT',
|
751
|
+
long_name: 'output-script',
|
752
|
+
opt_name: :output_script,
|
753
|
+
proc1: val_as_bool
|
754
|
+
},
|
755
|
+
{
|
756
|
+
arg_name: 'BOOL',
|
757
|
+
default: true,
|
758
|
+
description: 'Display standard output from execution',
|
759
|
+
env_var: 'MDE_OUTPUT_STDOUT',
|
760
|
+
long_name: 'output-stdout',
|
761
|
+
opt_name: :output_stdout,
|
762
|
+
proc1: val_as_bool
|
763
|
+
},
|
764
|
+
{
|
765
|
+
arg_name: 'RELATIVE_PATH',
|
766
|
+
default: '.',
|
767
|
+
description: 'Path to documents',
|
768
|
+
env_var: 'MDE_PATH',
|
769
|
+
long_name: 'path',
|
770
|
+
opt_name: :path,
|
771
|
+
short_name: 'p',
|
772
|
+
proc1: val_as_str
|
773
|
+
},
|
774
|
+
{
|
775
|
+
description: 'Gem home folder',
|
776
|
+
long_name: 'pwd',
|
777
|
+
opt_name: :pwd,
|
778
|
+
proc1: val_as_bool
|
779
|
+
},
|
780
|
+
{
|
781
|
+
description: 'Run most recently saved script',
|
782
|
+
long_name: 'run-last-script',
|
783
|
+
opt_name: :run_last_script,
|
784
|
+
proc1: val_as_bool
|
785
|
+
},
|
786
|
+
{
|
787
|
+
arg_name: 'BOOL',
|
788
|
+
default: false,
|
789
|
+
description: 'Save executed script',
|
790
|
+
env_var: 'MDE_SAVE_EXECUTED_SCRIPT',
|
791
|
+
long_name: 'save-executed-script',
|
792
|
+
opt_name: :save_executed_script,
|
793
|
+
proc1: val_as_bool
|
794
|
+
},
|
795
|
+
{
|
796
|
+
arg_name: 'BOOL',
|
797
|
+
default: false,
|
798
|
+
description: 'Save standard output of the executed script',
|
799
|
+
env_var: 'MDE_SAVE_EXECUTION_OUTPUT',
|
800
|
+
long_name: 'save-execution-output',
|
801
|
+
opt_name: :save_execution_output,
|
802
|
+
proc1: val_as_bool
|
803
|
+
},
|
804
|
+
{
|
805
|
+
arg_name: 'INT',
|
806
|
+
default: 0o755,
|
807
|
+
description: 'chmod for saved scripts',
|
808
|
+
env_var: 'MDE_SAVED_SCRIPT_CHMOD',
|
809
|
+
long_name: 'saved-script-chmod',
|
810
|
+
opt_name: :saved_script_chmod,
|
811
|
+
proc1: val_as_int
|
812
|
+
},
|
813
|
+
{
|
814
|
+
arg_name: 'PREFIX',
|
815
|
+
default: MarkdownExec::BIN_NAME,
|
816
|
+
description: 'Name prefix for saved scripts',
|
817
|
+
env_var: 'MDE_SAVED_SCRIPT_FILENAME_PREFIX',
|
818
|
+
long_name: 'saved-script-filename-prefix',
|
819
|
+
opt_name: :saved_script_filename_prefix,
|
820
|
+
proc1: val_as_str
|
821
|
+
},
|
822
|
+
{
|
823
|
+
arg_name: 'RELATIVE_PATH',
|
824
|
+
default: 'logs',
|
825
|
+
description: 'Saved script folder',
|
826
|
+
env_var: 'MDE_SAVED_SCRIPT_FOLDER',
|
827
|
+
long_name: 'saved-script-folder',
|
828
|
+
opt_name: :saved_script_folder,
|
829
|
+
proc1: val_as_str
|
830
|
+
},
|
831
|
+
{
|
832
|
+
arg_name: 'GLOB',
|
833
|
+
default: 'mde_*.sh',
|
834
|
+
description: 'Glob matching saved scripts',
|
835
|
+
env_var: 'MDE_SAVED_SCRIPT_GLOB',
|
836
|
+
long_name: 'saved-script-glob',
|
837
|
+
opt_name: :saved_script_glob,
|
838
|
+
proc1: val_as_str
|
839
|
+
},
|
840
|
+
{
|
841
|
+
arg_name: 'RELATIVE_PATH',
|
842
|
+
default: 'logs',
|
843
|
+
description: 'Saved stdout folder',
|
844
|
+
env_var: 'MDE_SAVED_STDOUT_FOLDER',
|
845
|
+
long_name: 'saved-stdout-folder',
|
846
|
+
opt_name: :saved_stdout_folder,
|
847
|
+
proc1: val_as_str
|
848
|
+
},
|
849
|
+
{
|
850
|
+
arg_name: 'GLOB',
|
851
|
+
default: 'mde_*.out.txt',
|
852
|
+
description: 'Glob matching saved outputs',
|
853
|
+
env_var: 'MDE_SAVED_STDOUT_GLOB',
|
854
|
+
long_name: 'saved-stdout-glob',
|
855
|
+
opt_name: :saved_stdout_glob,
|
856
|
+
proc1: val_as_str
|
857
|
+
},
|
858
|
+
{
|
859
|
+
description: 'Select and execute a recently saved output',
|
860
|
+
long_name: 'select-recent-output',
|
861
|
+
opt_name: :select_recent_output,
|
862
|
+
proc1: val_as_bool
|
863
|
+
},
|
864
|
+
{
|
865
|
+
description: 'Select and execute a recently saved script',
|
866
|
+
long_name: 'select-recent-script',
|
867
|
+
opt_name: :select_recent_script,
|
868
|
+
proc1: val_as_bool
|
869
|
+
},
|
870
|
+
{
|
871
|
+
description: 'YAML export of menu',
|
872
|
+
long_name: 'menu-export',
|
873
|
+
opt_name: :menu_export,
|
874
|
+
proc1: val_as_bool
|
875
|
+
},
|
876
|
+
{
|
877
|
+
description: 'List tab completions',
|
878
|
+
long_name: 'tab-completions',
|
879
|
+
opt_name: :tab_completions,
|
880
|
+
proc1: val_as_bool
|
881
|
+
},
|
882
|
+
{
|
883
|
+
arg_name: 'BOOL',
|
884
|
+
default: true,
|
885
|
+
description: 'Pause for user to approve script',
|
886
|
+
env_var: 'MDE_USER_MUST_APPROVE',
|
887
|
+
long_name: 'user-must-approve',
|
888
|
+
opt_name: :user_must_approve,
|
889
|
+
proc1: val_as_bool
|
890
|
+
},
|
891
|
+
{
|
892
|
+
description: 'Show current configuration values',
|
893
|
+
short_name: '0',
|
894
|
+
proc1: lambda { |_|
|
895
|
+
options_finalize options
|
896
|
+
fout sorted_keys(options).to_yaml
|
897
|
+
}
|
898
|
+
},
|
899
|
+
{
|
900
|
+
description: 'App help',
|
901
|
+
long_name: 'help',
|
902
|
+
short_name: 'h',
|
903
|
+
proc1: lambda { |_|
|
904
|
+
fout menu_help
|
905
|
+
exit
|
906
|
+
}
|
907
|
+
},
|
908
|
+
{
|
909
|
+
description: "Print the gem's version",
|
910
|
+
long_name: 'version',
|
911
|
+
short_name: 'v',
|
912
|
+
proc1: lambda { |_|
|
913
|
+
fout MarkdownExec::VERSION
|
914
|
+
exit
|
915
|
+
}
|
916
|
+
},
|
917
|
+
{
|
918
|
+
description: 'Exit app',
|
919
|
+
long_name: 'exit',
|
920
|
+
short_name: 'x',
|
921
|
+
proc1: ->(_) { exit }
|
922
|
+
},
|
923
|
+
{
|
924
|
+
default: '^\(.*\)$',
|
925
|
+
description: 'Pattern for blocks to hide from user-selection',
|
926
|
+
env_var: 'MDE_BLOCK_NAME_EXCLUDED_MATCH',
|
927
|
+
opt_name: :block_name_excluded_match,
|
928
|
+
proc1: val_as_str
|
929
|
+
},
|
930
|
+
{
|
931
|
+
default: ':(?<title>\S+)( |$)',
|
932
|
+
env_var: 'MDE_BLOCK_NAME_MATCH',
|
933
|
+
opt_name: :block_name_match,
|
934
|
+
proc1: val_as_str
|
935
|
+
},
|
936
|
+
{
|
937
|
+
default: '\+\S+',
|
938
|
+
env_var: 'MDE_BLOCK_REQUIRED_SCAN',
|
939
|
+
opt_name: :block_required_scan,
|
940
|
+
proc1: val_as_str
|
941
|
+
},
|
942
|
+
{
|
943
|
+
default: '> ',
|
944
|
+
env_var: 'MDE_DISPLAY_LEVEL_XBASE_PREFIX',
|
945
|
+
opt_name: :display_level_xbase_prefix,
|
946
|
+
proc1: val_as_str
|
947
|
+
},
|
948
|
+
{
|
949
|
+
default: '^`{3,}',
|
950
|
+
env_var: 'MDE_FENCED_START_AND_END_MATCH',
|
951
|
+
opt_name: :fenced_start_and_end_match,
|
952
|
+
proc1: val_as_str
|
953
|
+
},
|
954
|
+
{
|
955
|
+
default: '^`{3,}(?<shell>[^`\s]*) *(?<name>.*)$',
|
956
|
+
env_var: 'MDE_FENCED_START_EX_MATCH',
|
957
|
+
opt_name: :fenced_start_ex_match,
|
958
|
+
proc1: val_as_str
|
959
|
+
},
|
960
|
+
{
|
961
|
+
default: '^# *(?<name>[^#]*?) *$',
|
962
|
+
env_var: 'MDE_HEADING1_MATCH',
|
963
|
+
opt_name: :heading1_match,
|
964
|
+
proc1: val_as_str
|
965
|
+
},
|
966
|
+
{
|
967
|
+
default: '^## *(?<name>[^#]*?) *$',
|
968
|
+
env_var: 'MDE_HEADING2_MATCH',
|
969
|
+
opt_name: :heading2_match,
|
970
|
+
proc1: val_as_str
|
971
|
+
},
|
972
|
+
{
|
973
|
+
default: '^### *(?<name>.+?) *$',
|
974
|
+
env_var: 'MDE_HEADING3_MATCH',
|
975
|
+
opt_name: :heading3_match,
|
976
|
+
proc1: val_as_str
|
977
|
+
},
|
978
|
+
{
|
979
|
+
default: '*.[Mm][Dd]',
|
980
|
+
env_var: 'MDE_MD_FILENAME_GLOB',
|
981
|
+
opt_name: :md_filename_glob,
|
982
|
+
proc1: val_as_str
|
983
|
+
},
|
984
|
+
{
|
985
|
+
default: '.+\\.md',
|
986
|
+
env_var: 'MDE_MD_FILENAME_MATCH',
|
987
|
+
opt_name: :md_filename_match,
|
988
|
+
proc1: val_as_str
|
989
|
+
},
|
990
|
+
{
|
991
|
+
description: 'Options for viewing saved output file',
|
992
|
+
env_var: 'MDE_OUTPUT_VIEWER_OPTIONS',
|
993
|
+
opt_name: :output_viewer_options,
|
994
|
+
proc1: val_as_str
|
995
|
+
},
|
996
|
+
{
|
997
|
+
default: 24,
|
998
|
+
description: 'Maximum # of rows in select list',
|
999
|
+
env_var: 'MDE_SELECT_PAGE_HEIGHT',
|
1000
|
+
opt_name: :select_page_height,
|
1001
|
+
proc1: val_as_int
|
1002
|
+
},
|
1003
|
+
{
|
1004
|
+
default: '#!/usr/bin/env',
|
1005
|
+
description: 'Shebang for saved scripts',
|
1006
|
+
env_var: 'MDE_SHEBANG',
|
1007
|
+
opt_name: :shebang,
|
1008
|
+
proc1: val_as_str
|
1009
|
+
},
|
1010
|
+
{
|
1011
|
+
default: 'bash',
|
1012
|
+
description: 'Shell for launched scripts',
|
1013
|
+
env_var: 'MDE_SHELL',
|
1014
|
+
opt_name: :shell,
|
1015
|
+
proc1: val_as_str
|
1016
|
+
}
|
545
1017
|
]
|
1018
|
+
# commands first, options second
|
1019
|
+
(set1.reject { |v1| v1[:arg_name] }) + (set1.select { |v1| v1[:arg_name] })
|
1020
|
+
end
|
546
1021
|
|
547
|
-
|
1022
|
+
def menu_iter(data = menu_data1, &block)
|
1023
|
+
data.map(&block)
|
548
1024
|
end
|
549
1025
|
|
550
1026
|
def menu_help
|
@@ -577,7 +1053,8 @@ module MarkdownExec
|
|
577
1053
|
|
578
1054
|
## position 1: block name (optional)
|
579
1055
|
#
|
580
|
-
|
1056
|
+
block_name = rest.fetch(1, nil)
|
1057
|
+
@options[:block_name] = block_name if block_name.present?
|
581
1058
|
end
|
582
1059
|
|
583
1060
|
def optsmerge(call_options = {}, options_block = nil)
|
@@ -589,6 +1066,24 @@ module MarkdownExec
|
|
589
1066
|
end.tap_inspect
|
590
1067
|
end
|
591
1068
|
|
1069
|
+
def output_execution_result
|
1070
|
+
oq = [['Block', @options[:block_name], DISPLAY_LEVEL_ADMIN],
|
1071
|
+
['Command',
|
1072
|
+
[MarkdownExec::BIN_NAME,
|
1073
|
+
@options[:filename],
|
1074
|
+
@options[:block_name]].join(' '),
|
1075
|
+
DISPLAY_LEVEL_ADMIN]]
|
1076
|
+
|
1077
|
+
[['Script', :saved_filespec],
|
1078
|
+
['StdOut', :logged_stdout_filespec]].each do |label, name|
|
1079
|
+
oq << [label, @options[name], DISPLAY_LEVEL_ADMIN] if @options[name]
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
oq.map do |label, value, level|
|
1083
|
+
lout ["#{label}:".yellow, value.to_s].join(' '), level: level
|
1084
|
+
end
|
1085
|
+
end
|
1086
|
+
|
592
1087
|
def output_execution_summary
|
593
1088
|
return unless @options[:output_execution_summary]
|
594
1089
|
|
@@ -606,9 +1101,12 @@ module MarkdownExec
|
|
606
1101
|
|
607
1102
|
def prompt_with_quit(prompt_text, items, opts = {})
|
608
1103
|
exit_option = '* Exit'
|
609
|
-
|
610
|
-
|
611
|
-
|
1104
|
+
all_items = if @options[:menu_exit_at_top]
|
1105
|
+
(@options[:menu_with_exit] ? [exit_option] : []) + items
|
1106
|
+
else
|
1107
|
+
items + (@options[:menu_with_exit] ? [exit_option] : [])
|
1108
|
+
end
|
1109
|
+
sel = @prompt.select(prompt_text, all_items, opts.merge(filter: true))
|
612
1110
|
sel == exit_option ? nil : sel
|
613
1111
|
end
|
614
1112
|
|
@@ -652,22 +1150,22 @@ module MarkdownExec
|
|
652
1150
|
opts.banner = [
|
653
1151
|
"#{MarkdownExec::APP_NAME}" \
|
654
1152
|
" - #{MarkdownExec::APP_DESC} (#{MarkdownExec::VERSION})",
|
655
|
-
"Usage: #{executable_name} [path
|
1153
|
+
"Usage: #{executable_name} [(path | filename [block_name])] [options]"
|
656
1154
|
].join("\n")
|
657
1155
|
|
658
|
-
|
659
|
-
|
660
|
-
next unless long_name.present? || short_name.present?
|
1156
|
+
menu_iter do |item|
|
1157
|
+
next unless item[:long_name].present? || item[:short_name].present?
|
661
1158
|
|
662
|
-
opts.on(*[if long_name.present?
|
663
|
-
"--#{long_name}#{arg_name.present? ? " #{arg_name}" : ''}"
|
1159
|
+
opts.on(*[if item[:long_name].present?
|
1160
|
+
"--#{item[:long_name]}#{item[:arg_name].present? ? " #{item[:arg_name]}" : ''}"
|
664
1161
|
end,
|
665
|
-
short_name.present? ? "-#{short_name}" : nil,
|
666
|
-
[description,
|
667
|
-
default.present? ? "[#{value_for_cli default}]" : nil].compact.join(' '),
|
1162
|
+
item[:short_name].present? ? "-#{item[:short_name]}" : nil,
|
1163
|
+
[item[:description],
|
1164
|
+
item[:default].present? ? "[#{value_for_cli item[:default]}]" : nil].compact.join(' '),
|
668
1165
|
lambda { |value|
|
669
|
-
ret = proc1.call(value)
|
670
|
-
|
1166
|
+
# ret = item[:proc1].call(value)
|
1167
|
+
ret = item[:proc1] ? item[:proc1].call(value) : value
|
1168
|
+
options[item[:opt_name]] = ret if item[:opt_name]
|
671
1169
|
ret
|
672
1170
|
}].compact)
|
673
1171
|
end
|
@@ -681,24 +1179,39 @@ module MarkdownExec
|
|
681
1179
|
exec_block options, options[:block_name]
|
682
1180
|
end
|
683
1181
|
|
1182
|
+
FNR11 = '/'
|
1183
|
+
FNR12 = ',~'
|
1184
|
+
|
1185
|
+
def saved_name_make(opts)
|
1186
|
+
fne = opts[:filename].gsub(FNR11, FNR12)
|
1187
|
+
"#{[opts[:saved_script_filename_prefix], Time.now.utc.strftime('%F-%H-%M-%S'), fne,
|
1188
|
+
',', opts[:block_name]].join('_')}.sh"
|
1189
|
+
end
|
1190
|
+
|
1191
|
+
def saved_name_split(name)
|
1192
|
+
mf = name.match(/#{@options[:saved_script_filename_prefix]}_(?<time>[0-9\-]+)_(?<file>.+)_,_(?<block>.+)\.sh/)
|
1193
|
+
return unless mf
|
1194
|
+
|
1195
|
+
@options[:block_name] = mf[:block].tap_inspect name: :options_block_name
|
1196
|
+
@options[:filename] = mf[:file].gsub(FNR12, FNR11).tap_inspect name: :options_filename
|
1197
|
+
end
|
1198
|
+
|
684
1199
|
def run_last_script
|
685
|
-
filename = Dir.glob(File.join(@options[:saved_script_folder],
|
686
|
-
|
687
|
-
|
688
|
-
mf = filename.match(/#{@options[:saved_script_filename_prefix]}_(?<time>[0-9\-]+)_(?<file>.+)_(?<block>.+)\.sh/)
|
1200
|
+
filename = most_recent Dir.glob(File.join(@options[:saved_script_folder],
|
1201
|
+
@options[:saved_script_glob]))
|
1202
|
+
return unless filename
|
689
1203
|
|
690
|
-
|
691
|
-
|
1204
|
+
filename.tap_inspect name: filename
|
1205
|
+
saved_name_split filename
|
692
1206
|
@options[:save_executed_script] = false
|
693
1207
|
select_and_approve_block
|
694
|
-
save_execution_output
|
695
|
-
output_execution_summary
|
696
1208
|
end
|
697
1209
|
|
698
1210
|
def save_execution_output
|
699
1211
|
return unless @options[:save_execution_output]
|
700
1212
|
|
701
1213
|
fne = File.basename(@options[:filename], '.*')
|
1214
|
+
|
702
1215
|
@options[:logged_stdout_filename] =
|
703
1216
|
"#{[@options[:logged_stdout_filename_prefix], Time.now.utc.strftime('%F-%H-%M-%S'), fne,
|
704
1217
|
@options[:block_name]].join('_')}.out.txt"
|
@@ -706,34 +1219,51 @@ module MarkdownExec
|
|
706
1219
|
@logged_stdout_filespec = @options[:logged_stdout_filespec]
|
707
1220
|
dirname = File.dirname(@options[:logged_stdout_filespec])
|
708
1221
|
Dir.mkdir dirname unless File.exist?(dirname)
|
709
|
-
|
710
|
-
# @options[:
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
1222
|
+
|
1223
|
+
# File.write(@options[:logged_stdout_filespec], @execute_files&.fetch(EF_STDOUT, ''))
|
1224
|
+
ol = ["-STDOUT-\n"]
|
1225
|
+
ol += @execute_files&.fetch(EF_STDOUT, [])
|
1226
|
+
ol += ["-STDERR-\n"].tap_inspect name: :ol3
|
1227
|
+
ol += @execute_files&.fetch(EF_STDERR, [])
|
1228
|
+
ol += ["-STDIN-\n"]
|
1229
|
+
ol += @execute_files&.fetch(EF_STDIN, [])
|
1230
|
+
File.write(@options[:logged_stdout_filespec], ol.join)
|
716
1231
|
end
|
717
1232
|
|
718
1233
|
def select_and_approve_block(call_options = {}, &options_block)
|
719
1234
|
opts = optsmerge call_options, options_block
|
720
1235
|
blocks_in_file = list_blocks_in_file(opts.merge(struct: true))
|
721
1236
|
|
722
|
-
|
723
|
-
pt = (opts[:prompt_select_block]).to_s
|
724
|
-
blocks_in_file.each { |block| block.merge! label: make_block_label(block, opts) }
|
725
|
-
block_labels = option_exclude_blocks(opts, blocks_in_file).map { |block| block[:label] }
|
1237
|
+
loop1 = true && !opts[:block_name].present?
|
726
1238
|
|
727
|
-
|
1239
|
+
loop do
|
1240
|
+
unless opts[:block_name].present?
|
1241
|
+
pt = (opts[:prompt_select_block]).to_s
|
1242
|
+
blocks_in_file.each { |block| block.merge! label: make_block_label(block, opts) }
|
1243
|
+
block_labels = option_exclude_blocks(opts, blocks_in_file).map { |block| block[:label] }
|
728
1244
|
|
729
|
-
|
730
|
-
return nil if sel.nil?
|
1245
|
+
return nil if block_labels.count.zero?
|
731
1246
|
|
732
|
-
|
733
|
-
|
734
|
-
end
|
1247
|
+
sel = prompt_with_quit pt, block_labels, per_page: opts[:select_page_height]
|
1248
|
+
return nil if sel.nil?
|
735
1249
|
|
736
|
-
|
1250
|
+
# if sel.nil?
|
1251
|
+
# loop1 = false
|
1252
|
+
# break
|
1253
|
+
# end
|
1254
|
+
|
1255
|
+
label_block = blocks_in_file.select { |block| block[:label] == sel }.fetch(0, nil)
|
1256
|
+
opts[:block_name] = @options[:block_name] = label_block[:name]
|
1257
|
+
|
1258
|
+
end
|
1259
|
+
# if loop1
|
1260
|
+
approve_block opts, blocks_in_file
|
1261
|
+
# end
|
1262
|
+
|
1263
|
+
break unless loop1
|
1264
|
+
|
1265
|
+
opts[:block_name] = ''
|
1266
|
+
end
|
737
1267
|
end
|
738
1268
|
|
739
1269
|
def select_md_file(files_ = nil)
|
@@ -746,22 +1276,25 @@ module MarkdownExec
|
|
746
1276
|
end
|
747
1277
|
end
|
748
1278
|
|
1279
|
+
def select_recent_output
|
1280
|
+
filename = prompt_with_quit @options[:prompt_select_output].to_s, list_recent_output,
|
1281
|
+
per_page: @options[:select_page_height]
|
1282
|
+
return unless filename.present?
|
1283
|
+
|
1284
|
+
`open #{filename} #{options[:output_viewer_options]}`
|
1285
|
+
end
|
1286
|
+
|
749
1287
|
def select_recent_script
|
750
1288
|
filename = prompt_with_quit @options[:prompt_select_md].to_s, list_recent_scripts,
|
751
1289
|
per_page: @options[:select_page_height]
|
752
1290
|
return if filename.nil?
|
753
1291
|
|
754
|
-
|
755
|
-
|
756
|
-
@options[:block_name] = mf[:block]
|
757
|
-
@options[:filename] = "#{mf[:file]}.md" ### other extensions
|
1292
|
+
saved_name_split filename
|
758
1293
|
select_and_approve_block(
|
759
1294
|
bash: true,
|
760
1295
|
save_executed_script: false,
|
761
1296
|
struct: true
|
762
1297
|
)
|
763
|
-
save_execution_output
|
764
|
-
output_execution_summary
|
765
1298
|
end
|
766
1299
|
|
767
1300
|
def sorted_keys(hash1)
|
@@ -772,6 +1305,19 @@ module MarkdownExec
|
|
772
1305
|
{ headings: headings, name: title, title: title }
|
773
1306
|
end
|
774
1307
|
|
1308
|
+
def menu_export(data = menu_data1)
|
1309
|
+
data.map do |item|
|
1310
|
+
item.delete(:proc1)
|
1311
|
+
item
|
1312
|
+
end.to_yaml
|
1313
|
+
end
|
1314
|
+
|
1315
|
+
def tab_completions(data = menu_data1)
|
1316
|
+
data.map do |item|
|
1317
|
+
"--#{item[:long_name]}" if item[:long_name]
|
1318
|
+
end.compact
|
1319
|
+
end
|
1320
|
+
|
775
1321
|
def update_options(opts = {}, over: true)
|
776
1322
|
if over
|
777
1323
|
@options = @options.merge opts
|
@@ -781,19 +1327,6 @@ module MarkdownExec
|
|
781
1327
|
@options.tap_inspect format: :yaml
|
782
1328
|
end
|
783
1329
|
|
784
|
-
def value_for_cli(value)
|
785
|
-
case value.class.to_s
|
786
|
-
when 'String'
|
787
|
-
"'#{value}'"
|
788
|
-
when 'FalseClass', 'TrueClass'
|
789
|
-
value ? '1' : '0'
|
790
|
-
when 'Integer'
|
791
|
-
value
|
792
|
-
else
|
793
|
-
value.to_s
|
794
|
-
end
|
795
|
-
end
|
796
|
-
|
797
1330
|
def value_for_hash(value, default = nil)
|
798
1331
|
return default if value.nil?
|
799
1332
|
|
@@ -825,19 +1358,33 @@ module MarkdownExec
|
|
825
1358
|
end
|
826
1359
|
|
827
1360
|
def write_command_file(opts, required_blocks)
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
1361
|
+
return unless opts[:save_executed_script]
|
1362
|
+
|
1363
|
+
opts[:saved_script_filename] = saved_name_make(opts)
|
1364
|
+
@execute_script_filespec =
|
1365
|
+
@options[:saved_filespec] =
|
1366
|
+
File.join opts[:saved_script_folder], opts[:saved_script_filename]
|
1367
|
+
|
834
1368
|
dirname = File.dirname(@options[:saved_filespec])
|
835
1369
|
Dir.mkdir dirname unless File.exist?(dirname)
|
836
|
-
|
1370
|
+
(shebang = if @options[:shebang]&.present?
|
1371
|
+
"#{@options[:shebang]} #{@options[:shell]}\n"
|
1372
|
+
else
|
1373
|
+
''
|
1374
|
+
end
|
1375
|
+
).tap_inspect name: :shebang
|
1376
|
+
File.write(@options[:saved_filespec], shebang +
|
837
1377
|
"# file_name: #{opts[:filename]}\n" \
|
838
1378
|
"# block_name: #{opts[:block_name]}\n" \
|
839
1379
|
"# time: #{Time.now.utc}\n" \
|
840
1380
|
"#{required_blocks.flatten.join("\n")}\n")
|
1381
|
+
|
1382
|
+
@options[:saved_script_chmod].tap_inspect name: :@options_saved_script_chmod
|
1383
|
+
return if @options[:saved_script_chmod].zero?
|
1384
|
+
|
1385
|
+
@options[:saved_script_chmod].tap_inspect name: :@options_saved_script_chmod
|
1386
|
+
File.chmod @options[:saved_script_chmod], @options[:saved_filespec]
|
1387
|
+
@options[:saved_script_chmod].tap_inspect name: :@options_saved_script_chmod
|
841
1388
|
end
|
842
1389
|
end
|
843
1390
|
end
|