markdown_exec 2.0.2 → 2.0.3.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 +12 -0
- data/Gemfile +0 -2
- data/Gemfile.lock +1 -1
- data/bin/tab_completion.sh +2 -2
- data/examples/linked.md +5 -0
- data/lib/ansi_formatter.rb +8 -4
- data/lib/directory_searcher.rb +1 -1
- data/lib/hash_delegator.rb +194 -92
- data/lib/input_sequencer.rb +8 -3
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +0 -1
- data/lib/menu.src.yml +32 -38
- data/lib/menu.yml +27 -27
- data/lib/saved_assets.rb +27 -25
- data/lib/std_out_err_logger.rb +121 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8472bfcb1c813c87b1094fa2d9789b48b864e1bb8ab76d0f8ae1001237af3e0
|
4
|
+
data.tar.gz: 2699ed15974d6b89ffc3dea6c172044b4fb427e8a8d5184c25fb0cc7d5ad33ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9fbf04758308b522967bb9587eae0ac1d9bb0517697260a228b5c56bf1e2aac582f7aaa5b94aa021d5112a34327865c9535b3aa62fe62e00cd590c541077a6fd
|
7
|
+
data.tar.gz: 2a200effafe807181c6e9bda44e67dcb3978e02333ea61d06d30cb934a5e8bcf4e9f117fddb258cfd0397f833574c2abbb451ab061ff654f654c934f9dddbf65
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [2.0.3] - 2024-04-15
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- Option to set menu line count to fill console height.
|
8
|
+
- Rescue when named block in link block is missing.
|
9
|
+
|
10
|
+
### Changed
|
11
|
+
|
12
|
+
- Fix handling of variables with spaces in link blocks.
|
13
|
+
- Fix substitution for '/' in block names used in file names.
|
14
|
+
|
3
15
|
## [2.0.2] - 2024-02-09
|
4
16
|
|
5
17
|
### Changed
|
data/Gemfile
CHANGED
@@ -9,7 +9,6 @@ gemspec
|
|
9
9
|
gem 'clipboard'
|
10
10
|
gem 'debug'
|
11
11
|
gem 'erb'
|
12
|
-
# gem 'httparty' # 2024-01-01 for ChatGPI API
|
13
12
|
gem 'irb', '>= 1.8.0'
|
14
13
|
gem 'mocha', require: false
|
15
14
|
gem 'minitest', require: false
|
@@ -24,6 +23,5 @@ gem 'rubocop-minitest', require: false
|
|
24
23
|
gem 'rubocop-rake', require: false
|
25
24
|
gem 'rubocop-rspec', require: false
|
26
25
|
gem 'shellwords'
|
27
|
-
# gem 'tty-file'
|
28
26
|
gem 'uri', '0.12.2'
|
29
27
|
gem 'yaml', '~> 0.2.1'
|
data/Gemfile.lock
CHANGED
data/bin/tab_completion.sh
CHANGED
@@ -13,7 +13,7 @@ __filedirs_all()
|
|
13
13
|
}
|
14
14
|
|
15
15
|
_mde_echo_version() {
|
16
|
-
echo "2.0.
|
16
|
+
echo "2.0.3.1"
|
17
17
|
}
|
18
18
|
|
19
19
|
_mde() {
|
@@ -186,4 +186,4 @@ _mde() {
|
|
186
186
|
|
187
187
|
complete -o filenames -o nospace -F _mde mde
|
188
188
|
# _mde_echo_version
|
189
|
-
# echo "Updated: 2024-
|
189
|
+
# echo "Updated: 2024-04-18 00:17:10 UTC"
|
data/examples/linked.md
CHANGED
data/lib/ansi_formatter.rb
CHANGED
@@ -33,11 +33,15 @@ class AnsiFormatter
|
|
33
33
|
def scan_and_process_multiple_substrings(str, substrings, plain_sym, color_sym)
|
34
34
|
return string_send_color(str, plain_sym) if substrings.empty? || substrings.any?(&:empty?)
|
35
35
|
|
36
|
+
substring_patterns = substrings.map do |value|
|
37
|
+
[value, Regexp.new(value, Regexp::IGNORECASE)]
|
38
|
+
end
|
39
|
+
|
36
40
|
results = []
|
37
41
|
remaining_str = str.dup
|
38
42
|
|
39
43
|
while remaining_str.length.positive?
|
40
|
-
match_indices =
|
44
|
+
match_indices = substring_patterns.map { |_, pattern| remaining_str.index(pattern) }.compact
|
41
45
|
earliest_match = match_indices.min
|
42
46
|
|
43
47
|
if earliest_match
|
@@ -48,12 +52,12 @@ class AnsiFormatter
|
|
48
52
|
end
|
49
53
|
|
50
54
|
# Find which substring has this earliest match
|
51
|
-
matching_substring =
|
52
|
-
remaining_str.index(
|
55
|
+
matching_substring = substring_patterns.find do |_, pattern|
|
56
|
+
remaining_str.index(pattern) == earliest_match
|
53
57
|
end
|
54
58
|
|
55
59
|
if matching_substring
|
56
|
-
matching_segment = remaining_str.slice!(0...matching_substring.length)
|
60
|
+
matching_segment = remaining_str.slice!(0...matching_substring[0].length)
|
57
61
|
results << string_send_color(matching_segment, color_sym)
|
58
62
|
end
|
59
63
|
else
|
data/lib/directory_searcher.rb
CHANGED
@@ -102,7 +102,7 @@ class DirectorySearcher
|
|
102
102
|
# @param include_subdirectories [Boolean] Whether to search in subdirectories.
|
103
103
|
# @param filename_glob [String, nil] Glob pattern for file names.
|
104
104
|
def initialize(pattern, paths, include_subdirectories: true, filename_glob: '*.[Mm][Dd]') #'*.md'
|
105
|
-
@pattern = pattern
|
105
|
+
@pattern = Regexp.new(pattern, Regexp::IGNORECASE)
|
106
106
|
@paths = paths
|
107
107
|
@include_subdirectories = include_subdirectories
|
108
108
|
@filename_glob = filename_glob
|
data/lib/hash_delegator.rb
CHANGED
@@ -8,10 +8,11 @@ require 'clipboard'
|
|
8
8
|
require 'fileutils'
|
9
9
|
require 'open3'
|
10
10
|
require 'optparse'
|
11
|
+
require 'ostruct'
|
11
12
|
require 'set'
|
12
13
|
require 'shellwords'
|
14
|
+
require 'tempfile'
|
13
15
|
require 'tmpdir'
|
14
|
-
# require 'tty-file'
|
15
16
|
require 'tty-prompt'
|
16
17
|
require 'yaml'
|
17
18
|
|
@@ -21,6 +22,7 @@ require_relative 'block_label'
|
|
21
22
|
require_relative 'block_types'
|
22
23
|
require_relative 'cached_nested_file_reader'
|
23
24
|
require_relative 'constants'
|
25
|
+
require_relative 'std_out_err_logger'
|
24
26
|
require_relative 'directory_searcher'
|
25
27
|
require_relative 'exceptions'
|
26
28
|
require_relative 'fcb'
|
@@ -193,18 +195,15 @@ module HashDelegatorSelf
|
|
193
195
|
end
|
194
196
|
|
195
197
|
def next_link_state(block_name_from_cli:, was_using_cli:, block_state:, block_name: nil)
|
196
|
-
# &bsp 'next_link_state', block_name_from_cli, was_using_cli, block_state
|
197
198
|
# Set block_name based on block_name_from_cli
|
198
199
|
block_name = @cli_block_name if block_name_from_cli
|
199
|
-
# &bsp 'block_name:', block_name
|
200
200
|
|
201
201
|
# Determine the state of breaker based on was_using_cli and the block type
|
202
|
+
# true only when block_name is nil, block_name_from_cli is false, was_using_cli is true, and the block_state.block[:shell] equals BlockType::BASH. In all other scenarios, breaker is false.
|
202
203
|
breaker = !block_name && !block_name_from_cli && was_using_cli && block_state.block[:shell] == BlockType::BASH
|
203
|
-
# &bsp 'breaker:', breaker
|
204
204
|
|
205
205
|
# Reset block_name_from_cli if the conditions are not met
|
206
206
|
block_name_from_cli ||= false
|
207
|
-
# &bsp 'block_name_from_cli:', block_name_from_cli
|
208
207
|
|
209
208
|
[block_name, block_name_from_cli, breaker]
|
210
209
|
end
|
@@ -352,6 +351,20 @@ module CompactionHelpers
|
|
352
351
|
end
|
353
352
|
end
|
354
353
|
|
354
|
+
module PathUtils
|
355
|
+
# Determines if a given path is absolute or substitutes a placeholder in an expression with the path.
|
356
|
+
# @param path [String] The input path to check or fill in.
|
357
|
+
# @param expression [String] The expression where a wildcard '*' is replaced by the path if it's not absolute.
|
358
|
+
# @return [String] The absolute path or the expression with the wildcard replaced by the path.
|
359
|
+
def self.resolve_path_or_substitute(path, expression)
|
360
|
+
if path.include?('/')
|
361
|
+
path
|
362
|
+
else
|
363
|
+
expression.gsub('*', path)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
355
368
|
module MarkdownExec
|
356
369
|
class DebugHelper
|
357
370
|
# Class-level variable to store history of printed messages
|
@@ -368,7 +381,7 @@ module MarkdownExec
|
|
368
381
|
end
|
369
382
|
end
|
370
383
|
|
371
|
-
class
|
384
|
+
class HashDelegatorParent
|
372
385
|
attr_accessor :most_recent_loaded_filename, :pass_args, :run_state
|
373
386
|
|
374
387
|
extend HashDelegatorSelf
|
@@ -559,11 +572,11 @@ module MarkdownExec
|
|
559
572
|
|
560
573
|
# private
|
561
574
|
|
562
|
-
def calc_logged_stdout_filename
|
575
|
+
def calc_logged_stdout_filename(block_name:)
|
563
576
|
return unless @delegate_object[:saved_stdout_folder]
|
564
577
|
|
565
578
|
@delegate_object[:logged_stdout_filename] =
|
566
|
-
SavedAsset.stdout_name(blockname:
|
579
|
+
SavedAsset.stdout_name(blockname: block_name,
|
567
580
|
filename: File.basename(@delegate_object[:filename],
|
568
581
|
'.*'),
|
569
582
|
prefix: @delegate_object[:logged_stdout_filename_prefix],
|
@@ -898,7 +911,7 @@ module MarkdownExec
|
|
898
911
|
# @param selected [FCB] The selected functional code block object.
|
899
912
|
def execute_required_lines(required_lines: [], selected: FCB.new)
|
900
913
|
write_command_file(required_lines: required_lines, selected: selected) if @delegate_object[:save_executed_script]
|
901
|
-
calc_logged_stdout_filename
|
914
|
+
calc_logged_stdout_filename(block_name: @dml_block_state.block[:oname]) if @dml_block_state
|
902
915
|
format_and_execute_command(code_lines: required_lines)
|
903
916
|
post_execution_process
|
904
917
|
end
|
@@ -950,7 +963,6 @@ module MarkdownExec
|
|
950
963
|
next_load_file: LoadFile::Reuse
|
951
964
|
)
|
952
965
|
|
953
|
-
|
954
966
|
elsif selected[:shell] == BlockType::VARS
|
955
967
|
debounce_reset
|
956
968
|
block_names = []
|
@@ -972,6 +984,7 @@ module MarkdownExec
|
|
972
984
|
selected: selected,
|
973
985
|
link_state: link_state,
|
974
986
|
block_source: block_source)
|
987
|
+
|
975
988
|
else
|
976
989
|
LoadFileLinkState.new(LoadFile::Reuse, link_state)
|
977
990
|
end
|
@@ -1131,43 +1144,46 @@ module MarkdownExec
|
|
1131
1144
|
|
1132
1145
|
def link_block_data_eval(link_state, code_lines, selected, link_block_data, block_source:)
|
1133
1146
|
all_code = HashDelegator.code_merge(link_state&.inherited_lines, code_lines)
|
1147
|
+
cmd = "#{@delegate_object[:shell]} #{file.path}"
|
1148
|
+
output_lines = []
|
1134
1149
|
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1150
|
+
Tempfile.open do |file|
|
1151
|
+
file.write(all_code.join("\n"))
|
1152
|
+
file.rewind
|
1138
1153
|
|
1139
|
-
|
1140
|
-
@
|
1141
|
-
'-c', all_code.join("\n")
|
1142
|
-
) do |stdin, stdout, stderr, _exec_thr|
|
1143
|
-
handle_stream(stream: stdout, file_type: ExecutionStreams::StdOut) do |line|
|
1144
|
-
output_lines.push(line)
|
1145
|
-
end
|
1146
|
-
handle_stream(stream: stderr, file_type: ExecutionStreams::StdErr) do |line|
|
1147
|
-
output_lines.push(line)
|
1148
|
-
end
|
1154
|
+
if link_block_data.fetch(LinkKeys::Exec, false)
|
1155
|
+
@run_state.files = Hash.new([])
|
1149
1156
|
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1157
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr, _exec_thr|
|
1158
|
+
handle_stream(stream: stdout, file_type: ExecutionStreams::StdOut) do |line|
|
1159
|
+
output_lines.push(line)
|
1160
|
+
end
|
1161
|
+
handle_stream(stream: stderr, file_type: ExecutionStreams::StdErr) do |line|
|
1162
|
+
output_lines.push(line)
|
1163
|
+
end
|
1153
1164
|
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
end
|
1165
|
+
in_thr = handle_stream(stream: $stdin, file_type: ExecutionStreams::StdIn) do |line|
|
1166
|
+
stdin.puts(line)
|
1167
|
+
end
|
1158
1168
|
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
begin_pattern: @delegate_object.fetch(:output_assignment_begin, nil),
|
1164
|
-
end_pattern: @delegate_object.fetch(:output_assignment_end, nil),
|
1165
|
-
scan1: @delegate_object.fetch(:output_assignment_match, nil),
|
1166
|
-
format1: @delegate_object.fetch(:output_assignment_format, nil)
|
1167
|
-
)
|
1169
|
+
wait_for_stream_processing
|
1170
|
+
sleep 0.1
|
1171
|
+
in_thr.kill if in_thr&.alive?
|
1172
|
+
end
|
1168
1173
|
|
1169
|
-
|
1170
|
-
|
1174
|
+
## select output_lines that look like assignment or match other specs
|
1175
|
+
#
|
1176
|
+
output_lines = process_string_array(
|
1177
|
+
output_lines,
|
1178
|
+
begin_pattern: @delegate_object.fetch(:output_assignment_begin, nil),
|
1179
|
+
end_pattern: @delegate_object.fetch(:output_assignment_end, nil),
|
1180
|
+
scan1: @delegate_object.fetch(:output_assignment_match, nil),
|
1181
|
+
format1: @delegate_object.fetch(:output_assignment_format, nil)
|
1182
|
+
)
|
1183
|
+
|
1184
|
+
else
|
1185
|
+
output_lines = `#{cmd}`.split("\n")
|
1186
|
+
end
|
1171
1187
|
end
|
1172
1188
|
|
1173
1189
|
HashDelegator.error_handler('all_code eval output_lines is nil', { abort: true }) unless output_lines
|
@@ -1285,7 +1301,7 @@ module MarkdownExec
|
|
1285
1301
|
puts format(@delegate_object[:prompt_show_expr_format],
|
1286
1302
|
{ expr: filespec })
|
1287
1303
|
puts @delegate_object[:prompt_enter_filespec]
|
1288
|
-
resolve_path_or_substitute(gets.chomp, filespec)
|
1304
|
+
PathUtils.resolve_path_or_substitute(gets.chomp, filespec)
|
1289
1305
|
end
|
1290
1306
|
|
1291
1307
|
# Handle expression with wildcard characters
|
@@ -1320,11 +1336,34 @@ module MarkdownExec
|
|
1320
1336
|
}
|
1321
1337
|
end
|
1322
1338
|
|
1339
|
+
# # Loads auto link block.
|
1340
|
+
# def load_auto_link_block(all_blocks, link_state, mdoc, block_source:)
|
1341
|
+
# block_name = @delegate_object[:document_load_link_block_name]
|
1342
|
+
# return unless block_name.present? && @most_recent_loaded_filename != @delegate_object[:filename]
|
1343
|
+
|
1344
|
+
# block = HashDelegator.block_find(all_blocks, :oname, block_name)
|
1345
|
+
# return unless block
|
1346
|
+
|
1347
|
+
# if block.fetch(:shell, '') != BlockType::LINK
|
1348
|
+
# HashDelegator.error_handler('must be Link block type', { abort: true })
|
1349
|
+
|
1350
|
+
# else
|
1351
|
+
# # debounce_reset
|
1352
|
+
# push_link_history_and_trigger_load(
|
1353
|
+
# link_block_body: block.fetch(:body, ''),
|
1354
|
+
# mdoc: mdoc,
|
1355
|
+
# selected: block,
|
1356
|
+
# link_state: link_state,
|
1357
|
+
# block_source: block_source
|
1358
|
+
# )
|
1359
|
+
# end
|
1360
|
+
# end
|
1361
|
+
|
1323
1362
|
# Loads auto blocks based on delegate object settings and updates if new filename is detected.
|
1324
1363
|
# Executes a specified block once per filename.
|
1325
1364
|
# @param all_blocks [Array] Array of all block elements.
|
1326
1365
|
# @return [Boolean, nil] True if values were modified, nil otherwise.
|
1327
|
-
def
|
1366
|
+
def load_auto_opts_block(all_blocks)
|
1328
1367
|
block_name = @delegate_object[:document_load_opts_block_name]
|
1329
1368
|
return unless block_name.present? && @most_recent_loaded_filename != @delegate_object[:filename]
|
1330
1369
|
|
@@ -1354,7 +1393,8 @@ module MarkdownExec
|
|
1354
1393
|
|
1355
1394
|
# recreate menu with new options
|
1356
1395
|
#
|
1357
|
-
all_blocks, mdoc = mdoc_and_blocks_from_nested_files if
|
1396
|
+
all_blocks, mdoc = mdoc_and_blocks_from_nested_files if load_auto_opts_block(all_blocks)
|
1397
|
+
# load_auto_link_block(all_blocks, link_state, mdoc, block_source: {})
|
1358
1398
|
|
1359
1399
|
menu_blocks = mdoc.fcbs_per_options(@delegate_object)
|
1360
1400
|
add_menu_chrome_blocks!(menu_blocks: menu_blocks, link_state: link_state)
|
@@ -1756,18 +1796,6 @@ module MarkdownExec
|
|
1756
1796
|
end
|
1757
1797
|
end
|
1758
1798
|
|
1759
|
-
# Determines if a given path is absolute or substitutes a placeholder in an expression with the path.
|
1760
|
-
# @param path [String] The input path to check or fill in.
|
1761
|
-
# @param expression [String] The expression where a wildcard '*' is replaced by the path if it's not absolute.
|
1762
|
-
# @return [String] The absolute path or the expression with the wildcard replaced by the path.
|
1763
|
-
def resolve_path_or_substitute(path, expression)
|
1764
|
-
if path.include?('/')
|
1765
|
-
path
|
1766
|
-
else
|
1767
|
-
expression.gsub('*', path)
|
1768
|
-
end
|
1769
|
-
end
|
1770
|
-
|
1771
1799
|
def runtime_exception(exception_sym, name, items)
|
1772
1800
|
if @delegate_object[exception_sym] != 0
|
1773
1801
|
data = { name: name, detail: items.join(', ') }
|
@@ -1841,12 +1869,20 @@ module MarkdownExec
|
|
1841
1869
|
@delegate_object[:block_name] = nil
|
1842
1870
|
|
1843
1871
|
when :user_choice
|
1844
|
-
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1872
|
+
if @dml_link_state.block_name.present?
|
1873
|
+
# @prior_block_was_link = true
|
1874
|
+
@dml_block_state.block = @dml_blocks_in_file.find do |item|
|
1875
|
+
item[:oname] == @dml_link_state.block_name
|
1876
|
+
end
|
1877
|
+
@dml_link_state.block_name = nil
|
1878
|
+
else
|
1879
|
+
# puts "? - Select a block to execute (or type #{$texit} to exit):"
|
1880
|
+
break if ii_user_choice == :break # into @dml_block_state
|
1881
|
+
break if @dml_block_state.block.nil? # no block matched
|
1882
|
+
end
|
1848
1883
|
# puts "! - Executing block: #{data}"
|
1849
|
-
@dml_block_state.block[:oname]
|
1884
|
+
# @dml_block_state.block[:oname]
|
1885
|
+
@dml_block_state.block&.fetch(:oname, nil)
|
1850
1886
|
|
1851
1887
|
when :execute_block
|
1852
1888
|
block_name = data
|
@@ -1888,13 +1924,12 @@ module MarkdownExec
|
|
1888
1924
|
|
1889
1925
|
## order of block name processing: link block, cli, from user
|
1890
1926
|
#
|
1891
|
-
@cli_block_name = block_name
|
1892
1927
|
@dml_link_state.block_name, @run_state.block_name_from_cli, cli_break = \
|
1893
1928
|
HashDelegator.next_link_state(
|
1894
|
-
|
1895
|
-
|
1929
|
+
block_name: @dml_link_state.block_name,
|
1930
|
+
block_name_from_cli: !@dml_link_state.block_name.present?,
|
1896
1931
|
block_state: @dml_block_state,
|
1897
|
-
|
1932
|
+
was_using_cli: @dml_now_using_cli
|
1898
1933
|
)
|
1899
1934
|
|
1900
1935
|
if !@dml_block_state.block[:block_name_from_ui] && cli_break
|
@@ -2282,7 +2317,12 @@ module MarkdownExec
|
|
2282
2317
|
@delegate_object
|
2283
2318
|
end
|
2284
2319
|
|
2285
|
-
|
2320
|
+
sph = @delegate_object[:select_page_height]
|
2321
|
+
unless sph.positive?
|
2322
|
+
require 'io/console'
|
2323
|
+
sph = [IO.console.winsize[0] - 3, 4].max
|
2324
|
+
end
|
2325
|
+
selection_opts.merge!(per_page: sph)
|
2286
2326
|
|
2287
2327
|
selected_option = select_option_with_metadata(prompt_title, block_menu,
|
2288
2328
|
selection_opts)
|
@@ -2331,13 +2371,63 @@ module MarkdownExec
|
|
2331
2371
|
if save_expr.present?
|
2332
2372
|
save_filespec = save_filespec_from_expression(save_expr)
|
2333
2373
|
File.write(save_filespec, HashDelegator.join_code_lines(link_state&.inherited_lines))
|
2334
|
-
# TTY::File.create_file save_filespec, HahDelegator.join_code_lines(link_state&.inherited_lines), force: true
|
2335
2374
|
@delegate_object[:filename]
|
2336
2375
|
else
|
2337
2376
|
link_block_data[LinkKeys::File] || @delegate_object[:filename]
|
2338
2377
|
end
|
2339
2378
|
end
|
2340
2379
|
end
|
2380
|
+
|
2381
|
+
class HashDelegator < HashDelegatorParent
|
2382
|
+
# Cleans a value, handling both Hash and Struct types.
|
2383
|
+
# For Structs, the cleaned version is converted to a hash.
|
2384
|
+
def self.clean_value(value)
|
2385
|
+
case value
|
2386
|
+
when Hash
|
2387
|
+
clean_hash_recursively(value)
|
2388
|
+
when Struct
|
2389
|
+
struct_hash = value.to_h # Convert the Struct to a hash
|
2390
|
+
cleaned_hash = clean_hash_recursively(struct_hash) # Clean the hash
|
2391
|
+
# Return the cleaned hash instead of updating the Struct
|
2392
|
+
return cleaned_hash
|
2393
|
+
else
|
2394
|
+
value
|
2395
|
+
end
|
2396
|
+
end
|
2397
|
+
|
2398
|
+
# Recursively cleans the given object (hash or struct) from unwanted values.
|
2399
|
+
def self.clean_hash_recursively(obj)
|
2400
|
+
obj.each do |key, value|
|
2401
|
+
cleaned_value = clean_value(value) # Clean and possibly convert value
|
2402
|
+
obj[key] = cleaned_value if value.is_a?(Hash) || value.is_a?(Struct)
|
2403
|
+
end
|
2404
|
+
|
2405
|
+
if obj.is_a?(Hash)
|
2406
|
+
obj.select! { |key, value| ![nil, '', [], {}, nil].include?(value) }
|
2407
|
+
end
|
2408
|
+
|
2409
|
+
obj
|
2410
|
+
end
|
2411
|
+
|
2412
|
+
def self.next_link_state(*args, **kwargs, &block)
|
2413
|
+
super
|
2414
|
+
# result = super
|
2415
|
+
|
2416
|
+
# @logger ||= StdOutErrLogger.new
|
2417
|
+
# @logger.unknown(
|
2418
|
+
# HashDelegator.clean_hash_recursively(
|
2419
|
+
# { "HashDelegator.next_link_state":
|
2420
|
+
# { 'args': args,
|
2421
|
+
# 'at': Time.now.strftime('%FT%TZ'),
|
2422
|
+
# 'for': /[^\/]+:\d+/.match(caller.first)[0],
|
2423
|
+
# 'kwargs': kwargs,
|
2424
|
+
# 'return': result } }
|
2425
|
+
# )
|
2426
|
+
# )
|
2427
|
+
|
2428
|
+
# result
|
2429
|
+
end
|
2430
|
+
end
|
2341
2431
|
end
|
2342
2432
|
|
2343
2433
|
return if $PROGRAM_NAME != __FILE__
|
@@ -2348,12 +2438,20 @@ Bundler.require(:default)
|
|
2348
2438
|
require 'minitest/autorun'
|
2349
2439
|
require 'mocha/minitest'
|
2350
2440
|
|
2351
|
-
|
2352
|
-
require_relative 'instance_method_wrapper'
|
2353
|
-
# MarkdownExec::HashDelegator.prepend(InstanceMethodWrapper)
|
2354
|
-
# MarkdownExec::HashDelegator.singleton_class.prepend(ClassMethodWrapper)
|
2441
|
+
require_relative 'std_out_err_logger'
|
2355
2442
|
|
2356
2443
|
module MarkdownExec
|
2444
|
+
class TestHashDelegator0 < Minitest::Test
|
2445
|
+
def setup
|
2446
|
+
@hd = HashDelegator.new
|
2447
|
+
end
|
2448
|
+
|
2449
|
+
# Test case for empty body
|
2450
|
+
def test_next_link_state
|
2451
|
+
@hd.next_link_state(block_name_from_cli: nil, was_using_cli: nil, block_state: nil, block_name: nil)
|
2452
|
+
end
|
2453
|
+
end
|
2454
|
+
|
2357
2455
|
class TestHashDelegator < Minitest::Test
|
2358
2456
|
def setup
|
2359
2457
|
@hd = HashDelegator.new
|
@@ -3194,29 +3292,33 @@ module MarkdownExec
|
|
3194
3292
|
end
|
3195
3293
|
end
|
3196
3294
|
|
3197
|
-
|
3198
|
-
|
3199
|
-
|
3200
|
-
|
3295
|
+
class PathUtilsTest < Minitest::Test
|
3296
|
+
def test_absolute_path_returns_unchanged
|
3297
|
+
absolute_path = "/usr/local/bin"
|
3298
|
+
expression = "path/to/*/directory"
|
3299
|
+
assert_equal absolute_path, PathUtils.resolve_path_or_substitute(absolute_path, expression)
|
3300
|
+
end
|
3201
3301
|
|
3202
|
-
|
3203
|
-
|
3204
|
-
|
3205
|
-
|
3206
|
-
|
3207
|
-
|
3302
|
+
def test_relative_path_gets_substituted
|
3303
|
+
relative_path = "my_folder"
|
3304
|
+
expression = "path/to/*/directory"
|
3305
|
+
expected_output = "path/to/my_folder/directory"
|
3306
|
+
assert_equal expected_output, PathUtils.resolve_path_or_substitute(relative_path, expression)
|
3307
|
+
end
|
3208
3308
|
|
3209
|
-
|
3210
|
-
|
3211
|
-
|
3212
|
-
|
3213
|
-
|
3214
|
-
|
3309
|
+
def test_path_with_no_slash_substitutes_correctly
|
3310
|
+
relative_path = "data"
|
3311
|
+
expression = "path/to/*/directory"
|
3312
|
+
expected_output = "path/to/data/directory"
|
3313
|
+
assert_equal expected_output, PathUtils.resolve_path_or_substitute(relative_path, expression)
|
3314
|
+
end
|
3215
3315
|
|
3216
|
-
|
3217
|
-
|
3218
|
-
|
3219
|
-
|
3220
|
-
|
3316
|
+
def test_empty_path_substitution
|
3317
|
+
empty_path = ""
|
3318
|
+
expression = "path/to/*/directory"
|
3319
|
+
expected_output = "path/to//directory"
|
3320
|
+
assert_equal expected_output, PathUtils.resolve_path_or_substitute(empty_path, expression)
|
3321
|
+
end
|
3221
3322
|
end
|
3323
|
+
|
3222
3324
|
end # module MarkdownExec
|
data/lib/input_sequencer.rb
CHANGED
@@ -34,7 +34,8 @@ class InputSequencer
|
|
34
34
|
prior_block_was_link: next_state.prior_block_was_link.nil? ? current.prior_block_was_link : next_state.prior_block_was_link
|
35
35
|
)
|
36
36
|
rescue
|
37
|
-
|
37
|
+
pp $!, $@
|
38
|
+
exit 1
|
38
39
|
end
|
39
40
|
|
40
41
|
# Generates the next menu state based on provided attributes.
|
@@ -75,11 +76,14 @@ class InputSequencer
|
|
75
76
|
|
76
77
|
if now_menu.display_menu
|
77
78
|
exit_when_bq_empty = false
|
78
|
-
|
79
79
|
run_yield :display_menu, &block
|
80
80
|
|
81
81
|
choice = run_yield :user_choice, &block
|
82
82
|
|
83
|
+
if choice.nil?
|
84
|
+
raise "Block not recognized."
|
85
|
+
break
|
86
|
+
end
|
83
87
|
break if run_yield(:exit?, choice&.downcase, &block) # Exit loop and method to terminate the app
|
84
88
|
|
85
89
|
next_state = run_yield :execute_block, choice, &block
|
@@ -115,7 +119,8 @@ class InputSequencer
|
|
115
119
|
now_menu = InputSequencer.merge_link_state(now_menu, next_menu)
|
116
120
|
end
|
117
121
|
rescue
|
118
|
-
|
122
|
+
pp $!, $@
|
123
|
+
exit 1
|
119
124
|
end
|
120
125
|
end
|
121
126
|
|
data/lib/markdown_exec.rb
CHANGED
data/lib/menu.src.yml
CHANGED
@@ -99,6 +99,12 @@
|
|
99
99
|
:opt_name: display_level_xbase_prefix
|
100
100
|
:procname: val_as_str
|
101
101
|
|
102
|
+
# - :default: "(document_link)"
|
103
|
+
# :description: Name of Link block to load with the document
|
104
|
+
# :env_var: MDE_DOCUMENT_LOAD_LINK_BLOCK_NAME
|
105
|
+
# :opt_name: document_load_link_block_name
|
106
|
+
# :procname: val_as_str
|
107
|
+
|
102
108
|
- :default: "(document_options)"
|
103
109
|
:description: Name of Opts block to load with the document
|
104
110
|
:env_var: MDE_DOCUMENT_LOAD_OPTS_BLOCK_NAME
|
@@ -108,7 +114,6 @@
|
|
108
114
|
- :arg_name: BOOL
|
109
115
|
:default: false
|
110
116
|
:description: Dump @delegate_object
|
111
|
-
:env_var: MDE_DUMP_DELEGATE_OBJECT
|
112
117
|
:long_name: dump-dump-delegate-object
|
113
118
|
:opt_name: dump_delegate_object
|
114
119
|
:procname: val_as_bool
|
@@ -116,7 +121,6 @@
|
|
116
121
|
- :arg_name: BOOL
|
117
122
|
:default: false
|
118
123
|
:description: Dump BlocksInFile (stage 1)
|
119
|
-
:env_var: MDE_DUMP_BLOCKS_IN_FILE
|
120
124
|
:long_name: dump-blocks-in-file
|
121
125
|
:opt_name: dump_blocks_in_file
|
122
126
|
:procname: val_as_bool
|
@@ -124,7 +128,6 @@
|
|
124
128
|
- :arg_name: BOOL
|
125
129
|
:default: false
|
126
130
|
:description: Dump inherited block_names
|
127
|
-
:env_var: MDE_DUMP_INHERITED_BLOCK_NAMES
|
128
131
|
:long_name: dump-dump-inherited-block_names
|
129
132
|
:opt_name: dump_inherited_block_names
|
130
133
|
:procname: val_as_bool
|
@@ -132,7 +135,6 @@
|
|
132
135
|
- :arg_name: BOOL
|
133
136
|
:default: false
|
134
137
|
:description: Dump inherited dependencies
|
135
|
-
:env_var: MDE_DUMP_INHERITED_DEPENDENCIES
|
136
138
|
:long_name: dump-dump-inherited-dependencies
|
137
139
|
:opt_name: dump_inherited_dependencies
|
138
140
|
:procname: val_as_bool
|
@@ -140,7 +142,6 @@
|
|
140
142
|
- :arg_name: BOOL
|
141
143
|
:default: false
|
142
144
|
:description: Dump inherited lines
|
143
|
-
:env_var: MDE_DUMP_INHERITED_LINES
|
144
145
|
:long_name: dump-dump-inherited-lines
|
145
146
|
:opt_name: dump_inherited_lines
|
146
147
|
:procname: val_as_bool
|
@@ -148,7 +149,6 @@
|
|
148
149
|
- :arg_name: BOOL
|
149
150
|
:default: false
|
150
151
|
:description: Dump MenuBlocks (stage 2)
|
151
|
-
:env_var: MDE_DUMP_MENU_BLOCKS
|
152
152
|
:long_name: dump-menu-blocks
|
153
153
|
:opt_name: dump_menu_blocks
|
154
154
|
:procname: val_as_bool
|
@@ -156,7 +156,6 @@
|
|
156
156
|
- :arg_name: BOOL
|
157
157
|
:default: false
|
158
158
|
:description: Dump selected block
|
159
|
-
:env_var: MDE_DUMP_SELECTED_BLOCK
|
160
159
|
:long_name: dump-selected-block
|
161
160
|
:opt_name: dump_selected_block
|
162
161
|
:procname: val_as_bool
|
@@ -206,35 +205,30 @@
|
|
206
205
|
:opt_name: exclude_expect_blocks
|
207
206
|
:procname: val_as_bool
|
208
207
|
|
208
|
+
# - :default: >
|
209
|
+
# osascript scripts/applescript/mde.applescript "%{batch_index}" "%{home}" " %{started_at} - %{document_filename} - %{block_name} " "%{script_filespec}" "%{output_filespec}"
|
209
210
|
- :default: >
|
210
211
|
osascript -e '
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
write text "o=\"%{output_filespec}\""
|
232
|
-
write text "cd \"%{home}\""
|
233
|
-
write text "echo -ne \"\\033]; %{started_at} - %{document_filename} - %{block_name} \\007\""
|
234
|
-
write text "\"$s\" | tee -a \"$o\""
|
235
|
-
end tell
|
236
|
-
end tell
|
237
|
-
end tell'
|
212
|
+
on run argv
|
213
|
+
set batch_index to "%{batch_index}"
|
214
|
+
set home to "%{home}"
|
215
|
+
set output_filespec to "%{output_filespec}"
|
216
|
+
set script_filespec to "%{script_filespec}"
|
217
|
+
set title to " %{started_at} - %{document_filename} - %{block_name} "
|
218
|
+
|
219
|
+
tell application "iTerm"
|
220
|
+
create window with default profile
|
221
|
+
tell the first window
|
222
|
+
tell the current session
|
223
|
+
write text "s=" & quoted form of script_filespec & ""
|
224
|
+
write text "o=" & quoted form of output_filespec & ""
|
225
|
+
write text "cd " & quoted form of home & ""
|
226
|
+
write text "echo -ne \"\\033]; " & title & " \\007\""
|
227
|
+
write text "\"$s\" 2>&1 | tee -a \"$o\""
|
228
|
+
end tell
|
229
|
+
end tell
|
230
|
+
end tell
|
231
|
+
end run'
|
238
232
|
:description: execute_command_format
|
239
233
|
:env_var: MDE_EXECUTE_COMMAND_FORMAT
|
240
234
|
:opt_name: execute_command_format
|
@@ -247,7 +241,7 @@
|
|
247
241
|
:procname: val_as_str
|
248
242
|
|
249
243
|
- :arg_name: BOOL
|
250
|
-
:default:
|
244
|
+
:default: false
|
251
245
|
:description: Execute script in own window
|
252
246
|
:env_var: MDE_EXECUTE_IN_OWN_WINDOW
|
253
247
|
:opt_name: execute_in_own_window
|
@@ -301,7 +295,7 @@
|
|
301
295
|
|
302
296
|
- :arg_name: FIND
|
303
297
|
:default: ''
|
304
|
-
:description: Find in documents
|
298
|
+
:description: Find argument in documents
|
305
299
|
:long_name: find
|
306
300
|
:procname: find
|
307
301
|
:short_name: "?"
|
@@ -1056,8 +1050,8 @@
|
|
1056
1050
|
:opt_name: select_by_shell_regex
|
1057
1051
|
:procname: val_as_str
|
1058
1052
|
|
1059
|
-
- :default:
|
1060
|
-
:description: 'Maximum # of rows in select list'
|
1053
|
+
- :default: 0
|
1054
|
+
:description: 'Maximum # of rows in select list. Detects current limit if not specified.'
|
1061
1055
|
:env_var: MDE_SELECT_PAGE_HEIGHT
|
1062
1056
|
:opt_name: select_page_height
|
1063
1057
|
:procname: val_as_int
|
data/lib/menu.yml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# MDE - Markdown Executor (2.0.
|
1
|
+
# MDE - Markdown Executor (2.0.3.1)
|
2
2
|
---
|
3
3
|
- :description: Show current configuration values
|
4
4
|
:procname: show_config
|
@@ -91,49 +91,42 @@
|
|
91
91
|
- :arg_name: BOOL
|
92
92
|
:default: false
|
93
93
|
:description: Dump @delegate_object
|
94
|
-
:env_var: MDE_DUMP_DELEGATE_OBJECT
|
95
94
|
:long_name: dump-dump-delegate-object
|
96
95
|
:opt_name: dump_delegate_object
|
97
96
|
:procname: val_as_bool
|
98
97
|
- :arg_name: BOOL
|
99
98
|
:default: false
|
100
99
|
:description: Dump BlocksInFile (stage 1)
|
101
|
-
:env_var: MDE_DUMP_BLOCKS_IN_FILE
|
102
100
|
:long_name: dump-blocks-in-file
|
103
101
|
:opt_name: dump_blocks_in_file
|
104
102
|
:procname: val_as_bool
|
105
103
|
- :arg_name: BOOL
|
106
104
|
:default: false
|
107
105
|
:description: Dump inherited block_names
|
108
|
-
:env_var: MDE_DUMP_INHERITED_BLOCK_NAMES
|
109
106
|
:long_name: dump-dump-inherited-block_names
|
110
107
|
:opt_name: dump_inherited_block_names
|
111
108
|
:procname: val_as_bool
|
112
109
|
- :arg_name: BOOL
|
113
110
|
:default: false
|
114
111
|
:description: Dump inherited dependencies
|
115
|
-
:env_var: MDE_DUMP_INHERITED_DEPENDENCIES
|
116
112
|
:long_name: dump-dump-inherited-dependencies
|
117
113
|
:opt_name: dump_inherited_dependencies
|
118
114
|
:procname: val_as_bool
|
119
115
|
- :arg_name: BOOL
|
120
116
|
:default: false
|
121
117
|
:description: Dump inherited lines
|
122
|
-
:env_var: MDE_DUMP_INHERITED_LINES
|
123
118
|
:long_name: dump-dump-inherited-lines
|
124
119
|
:opt_name: dump_inherited_lines
|
125
120
|
:procname: val_as_bool
|
126
121
|
- :arg_name: BOOL
|
127
122
|
:default: false
|
128
123
|
:description: Dump MenuBlocks (stage 2)
|
129
|
-
:env_var: MDE_DUMP_MENU_BLOCKS
|
130
124
|
:long_name: dump-menu-blocks
|
131
125
|
:opt_name: dump_menu_blocks
|
132
126
|
:procname: val_as_bool
|
133
127
|
- :arg_name: BOOL
|
134
128
|
:default: false
|
135
129
|
:description: Dump selected block
|
136
|
-
:env_var: MDE_DUMP_SELECTED_BLOCK
|
137
130
|
:long_name: dump-selected-block
|
138
131
|
:opt_name: dump_selected_block
|
139
132
|
:procname: val_as_bool
|
@@ -177,21 +170,28 @@
|
|
177
170
|
:env_var: MDE_EXCLUDE_EXPECT_BLOCKS
|
178
171
|
:opt_name: exclude_expect_blocks
|
179
172
|
:procname: val_as_bool
|
180
|
-
- :default:
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
173
|
+
- :default: |
|
174
|
+
osascript -e '
|
175
|
+
on run argv
|
176
|
+
set batch_index to "%{batch_index}"
|
177
|
+
set home to "%{home}"
|
178
|
+
set output_filespec to "%{output_filespec}"
|
179
|
+
set script_filespec to "%{script_filespec}"
|
180
|
+
set title to " %{started_at} - %{document_filename} - %{block_name} "
|
181
|
+
|
182
|
+
tell application "iTerm"
|
183
|
+
create window with default profile
|
184
|
+
tell the first window
|
185
|
+
tell the current session
|
186
|
+
write text "s=" & quoted form of script_filespec & ""
|
187
|
+
write text "o=" & quoted form of output_filespec & ""
|
188
|
+
write text "cd " & quoted form of home & ""
|
189
|
+
write text "echo -ne \"\\033]; " & title & " \\007\""
|
190
|
+
write text "\"$s\" 2>&1 | tee -a \"$o\""
|
191
|
+
end tell
|
192
|
+
end tell
|
193
|
+
end tell
|
194
|
+
end run'
|
195
195
|
:description: execute_command_format
|
196
196
|
:env_var: MDE_EXECUTE_COMMAND_FORMAT
|
197
197
|
:opt_name: execute_command_format
|
@@ -202,7 +202,7 @@
|
|
202
202
|
:opt_name: execute_command_title_time_format
|
203
203
|
:procname: val_as_str
|
204
204
|
- :arg_name: BOOL
|
205
|
-
:default:
|
205
|
+
:default: false
|
206
206
|
:description: Execute script in own window
|
207
207
|
:env_var: MDE_EXECUTE_IN_OWN_WINDOW
|
208
208
|
:opt_name: execute_in_own_window
|
@@ -249,7 +249,7 @@
|
|
249
249
|
:short_name: f
|
250
250
|
- :arg_name: FIND
|
251
251
|
:default: ''
|
252
|
-
:description: Find in documents
|
252
|
+
:description: Find argument in documents
|
253
253
|
:long_name: find
|
254
254
|
:procname: find
|
255
255
|
:short_name: "?"
|
@@ -901,8 +901,8 @@
|
|
901
901
|
:env_var: MDE_SELECT_BY_SHELL_REGEX
|
902
902
|
:opt_name: select_by_shell_regex
|
903
903
|
:procname: val_as_str
|
904
|
-
- :default:
|
905
|
-
:description: 'Maximum # of rows in select list'
|
904
|
+
- :default: 0
|
905
|
+
:description: 'Maximum # of rows in select list. Detects current limit if not specified.'
|
906
906
|
:env_var: MDE_SELECT_PAGE_HEIGHT
|
907
907
|
:opt_name: select_page_height
|
908
908
|
:procname: val_as_int
|
data/lib/saved_assets.rb
CHANGED
@@ -19,43 +19,45 @@ module MarkdownExec
|
|
19
19
|
# Generates a formatted script name based on the provided parameters.
|
20
20
|
def self.script_name(filename:, prefix:, time:, blockname:, ftime: DEFAULT_FTIME, join_str: '_', pattern: FNR11, replace: FNR12, exts: '.sh')
|
21
21
|
fne = filename.gsub(pattern, replace)
|
22
|
-
|
22
|
+
bne = blockname.gsub(pattern, replace)
|
23
|
+
"#{[prefix, time.strftime(ftime), fne, ',', bne].join(join_str)}#{exts}"
|
23
24
|
end
|
24
25
|
|
25
26
|
# Generates a formatted stdout name based on the provided parameters.
|
26
27
|
def self.stdout_name(filename:, prefix:, time:, blockname:, ftime: DEFAULT_FTIME, join_str: '_', pattern: FNR11, replace: FNR12, exts: '.out.txt')
|
27
28
|
fne = filename.gsub(pattern, replace)
|
28
|
-
|
29
|
+
bne = blockname.gsub(pattern, replace)
|
30
|
+
"#{[prefix, time.strftime(ftime), fne, ',', bne].join(join_str)}#{exts}"
|
29
31
|
end
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
33
|
-
if $PROGRAM_NAME
|
34
|
-
require 'minitest/autorun'
|
35
|
+
return if $PROGRAM_NAME != __FILE__
|
35
36
|
|
36
|
-
|
37
|
-
def test_script_name
|
38
|
-
filename = 'sample.txt'
|
39
|
-
prefix = 'test'
|
40
|
-
time = Time.new(2023, 1, 1, 12, 0, 0) # Sample date-time for consistency in testing
|
41
|
-
blockname = 'block1'
|
37
|
+
require 'minitest/autorun'
|
42
38
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
39
|
+
class SavedAssetTest < Minitest::Test
|
40
|
+
def test_script_name_with_special_characters_in_blockname
|
41
|
+
filename = 'sample.txt'
|
42
|
+
prefix = 'test'
|
43
|
+
time = Time.new(2023, 1, 1, 12, 0, 0)
|
44
|
+
blockname = 'block/1:2'
|
45
|
+
|
46
|
+
expected_name = 'test_2023-01-01-12-00-00_sample.txt_,_block_1_2.sh'
|
47
|
+
assert_equal expected_name, MarkdownExec::SavedAsset.script_name(
|
48
|
+
filename: filename, prefix: prefix, time: time, blockname: blockname
|
49
|
+
)
|
50
|
+
end
|
48
51
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
52
|
+
def test_stdout_name_with_special_characters_in_blockname
|
53
|
+
filename = 'sample.txt'
|
54
|
+
prefix = 'test'
|
55
|
+
time = Time.new(2023, 1, 1, 12, 0, 0)
|
56
|
+
blockname = 'block/1:2'
|
54
57
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
58
|
+
expected_name = 'test_2023-01-01-12-00-00_sample.txt_,_block_1_2.out.txt'
|
59
|
+
assert_equal expected_name, MarkdownExec::SavedAsset.stdout_name(
|
60
|
+
filename: filename, prefix: prefix, time: time, blockname: blockname
|
61
|
+
)
|
60
62
|
end
|
61
63
|
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
#!/usr/bin/env -S bundle exec ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# encoding=utf-8
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
# Logger::LogDevice is used by Logger, the parent class of StdOutErrLogger
|
8
|
+
class Logger::LogDevice
|
9
|
+
# remove header
|
10
|
+
def add_log_header(file); end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Custom logger to direct info to stdout and warn and above to stderr
|
14
|
+
#
|
15
|
+
class StdOutErrLogger < Logger
|
16
|
+
attr_reader :file
|
17
|
+
|
18
|
+
# def initialize(file = nil)
|
19
|
+
def initialize(file = "#{__dir__}/../tmp/hash_delegator_next_link_state.yaml")
|
20
|
+
@file = file
|
21
|
+
super(file || STDOUT)
|
22
|
+
self.formatter = proc do |severity, datetime, progname, msg|
|
23
|
+
"#{msg}\n"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def add(severity, message = nil, progname = nil, &block)
|
28
|
+
message = (message || block&.call || progname) if message.nil?
|
29
|
+
message = "- #{message.to_json}\n"
|
30
|
+
### message = message.join("\n") if message.is_a? Array
|
31
|
+
out = format_message(format_severity(severity), Time.now, progname, message)
|
32
|
+
if severity == Logger::UNKNOWN # does not follow spec, outputs to stderr for IO
|
33
|
+
# $stderr.puts(out)
|
34
|
+
super
|
35
|
+
elsif severity >= Logger::WARN
|
36
|
+
if @file
|
37
|
+
super
|
38
|
+
else
|
39
|
+
$stderr.puts(out)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
if @file
|
43
|
+
super
|
44
|
+
else
|
45
|
+
$stdout.puts(out)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
return if $PROGRAM_NAME != __FILE__
|
52
|
+
|
53
|
+
require 'minitest/autorun'
|
54
|
+
|
55
|
+
class StdOutErrLoggerTest < Minitest::Test
|
56
|
+
# Redirect STDOUT and STDERR to capture them for assertions
|
57
|
+
def setup
|
58
|
+
@original_stdout = $stdout
|
59
|
+
@original_stderr = $stderr
|
60
|
+
$stdout = StringIO.new
|
61
|
+
$stderr = StringIO.new
|
62
|
+
end
|
63
|
+
|
64
|
+
def teardown
|
65
|
+
$stdout = @original_stdout
|
66
|
+
$stderr = @original_stderr
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_initialize_without_file
|
70
|
+
logger = StdOutErrLogger.new
|
71
|
+
assert_nil logger.file
|
72
|
+
assert_equal Logger::DEBUG, logger.level
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_initialize_with_file
|
76
|
+
Tempfile.open do |file|
|
77
|
+
logger = StdOutErrLogger.new(file.path)
|
78
|
+
assert_equal file.path, logger.file
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_logging_info
|
83
|
+
logger = StdOutErrLogger.new
|
84
|
+
logger.info("Info message")
|
85
|
+
assert_equal "Info message\n", $stdout.string
|
86
|
+
assert_empty $stderr.string
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_logging_warning
|
90
|
+
logger = StdOutErrLogger.new
|
91
|
+
logger.warn("Warning message")
|
92
|
+
assert_empty $stdout.string
|
93
|
+
assert_equal "Warning message\n", $stderr.string
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_logging_error
|
97
|
+
logger = StdOutErrLogger.new
|
98
|
+
logger.error("Error message")
|
99
|
+
assert_empty $stdout.string
|
100
|
+
assert_equal "Error message\n", $stderr.string
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_logging_with_array
|
104
|
+
logger = StdOutErrLogger.new
|
105
|
+
logger.info(["Message line 1", "Message line 2"])
|
106
|
+
assert_equal "Message line 1\nMessage line 2\n", $stdout.string
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_logging_with_block
|
110
|
+
logger = StdOutErrLogger.new
|
111
|
+
logger.info { "Block message" }
|
112
|
+
assert_equal "Block message\n", $stdout.string
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_logging_unknown_severity
|
116
|
+
logger = StdOutErrLogger.new
|
117
|
+
logger.add(Logger::UNKNOWN, "Unknown severity message")
|
118
|
+
assert_empty $stdout.string
|
119
|
+
assert_equal "Unknown severity message\n", $stderr.string
|
120
|
+
end
|
121
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: markdown_exec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fareed Stevenson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-04-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clipboard
|
@@ -169,6 +169,7 @@ files:
|
|
169
169
|
- lib/saved_assets.rb
|
170
170
|
- lib/saved_files_matcher.rb
|
171
171
|
- lib/shared.rb
|
172
|
+
- lib/std_out_err_logger.rb
|
172
173
|
- lib/string_util.rb
|
173
174
|
- lib/tap.rb
|
174
175
|
homepage: https://rubygems.org/gems/markdown_exec
|