markdown_exec 1.3.7 → 1.3.9
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 +53 -2
- data/Gemfile +1 -0
- data/Gemfile.lock +5 -1
- data/Rakefile +11 -6
- data/bin/colorize_env_vars.sh +7 -0
- data/bin/tab_completion.sh +19 -19
- data/examples/duplicate_block.md +10 -0
- data/examples/import0.md +8 -0
- data/examples/import1.md +10 -0
- data/examples/include.md +12 -0
- data/examples/infile_config.md +10 -0
- data/examples/linked1.md +28 -0
- data/examples/linked2.md +28 -0
- data/examples/opts.md +13 -0
- data/examples/pass-through.md +14 -0
- data/examples/plant.md +23 -0
- data/examples/port.md +23 -0
- data/examples/vars.md +20 -0
- data/examples/wrap.md +33 -0
- data/lib/block_types.rb +5 -0
- data/lib/cached_nested_file_reader.rb +0 -1
- data/lib/colorize.rb +37 -23
- data/lib/fcb.rb +12 -30
- data/lib/filter.rb +147 -71
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +523 -235
- data/lib/mdoc.rb +190 -58
- data/lib/menu.src.yml +323 -257
- data/lib/menu.yml +324 -258
- metadata +17 -6
- data/lib/env_opts.rb +0 -242
- data/lib/markdown_block_manager.rb +0 -195
- data/lib/menu_options.rb +0 -0
- data/lib/menu_options.yml +0 -0
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: 1.3.
|
4
|
+
version: 1.3.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fareed Stevenson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clipboard
|
@@ -105,28 +105,39 @@ files:
|
|
105
105
|
- assets/output_of_execution.png
|
106
106
|
- assets/select_a_block.png
|
107
107
|
- assets/select_a_file.png
|
108
|
+
- bin/colorize_env_vars.sh
|
108
109
|
- bin/console
|
109
110
|
- bin/mde
|
110
111
|
- bin/setup
|
111
112
|
- bin/tab_completion.sh
|
112
113
|
- bin/tab_completion.sh.erb
|
114
|
+
- examples/duplicate_block.md
|
115
|
+
- examples/import0.md
|
116
|
+
- examples/import1.md
|
117
|
+
- examples/include.md
|
118
|
+
- examples/infile_config.md
|
119
|
+
- examples/linked1.md
|
120
|
+
- examples/linked2.md
|
121
|
+
- examples/opts.md
|
122
|
+
- examples/pass-through.md
|
123
|
+
- examples/plant.md
|
124
|
+
- examples/port.md
|
125
|
+
- examples/vars.md
|
126
|
+
- examples/wrap.md
|
113
127
|
- lib/block_label.rb
|
128
|
+
- lib/block_types.rb
|
114
129
|
- lib/cached_nested_file_reader.rb
|
115
130
|
- lib/cli.rb
|
116
131
|
- lib/colorize.rb
|
117
132
|
- lib/env.rb
|
118
|
-
- lib/env_opts.rb
|
119
133
|
- lib/environment_opt_parse.rb
|
120
134
|
- lib/fcb.rb
|
121
135
|
- lib/filter.rb
|
122
|
-
- lib/markdown_block_manager.rb
|
123
136
|
- lib/markdown_exec.rb
|
124
137
|
- lib/markdown_exec/version.rb
|
125
138
|
- lib/mdoc.rb
|
126
139
|
- lib/menu.src.yml
|
127
140
|
- lib/menu.yml
|
128
|
-
- lib/menu_options.rb
|
129
|
-
- lib/menu_options.yml
|
130
141
|
- lib/object_present.rb
|
131
142
|
- lib/option_value.rb
|
132
143
|
- lib/regexp.rb
|
data/lib/env_opts.rb
DELETED
@@ -1,242 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# encoding=utf-8
|
4
|
-
|
5
|
-
require_relative 'tap'
|
6
|
-
|
7
|
-
include Tap #; tap_config
|
8
|
-
|
9
|
-
# define options with initial values
|
10
|
-
# option to read value from environmnt variables
|
11
|
-
# option to cast input values
|
12
|
-
# value priority: default < environment < argument
|
13
|
-
#
|
14
|
-
# :reek:TooManyMethods
|
15
|
-
class EnvOpts
|
16
|
-
attr_reader :opts, :values
|
17
|
-
|
18
|
-
def initialize(opts_raw = {}, argv = ARGV)
|
19
|
-
@opts = {}
|
20
|
-
@values = {}
|
21
|
-
add_options(opts_raw)
|
22
|
-
# parse(argv, &block) if block_given?
|
23
|
-
block_given? ? parse(argv, &block) : parse(argv)
|
24
|
-
|
25
|
-
self # rubocop:disable Lint/Void
|
26
|
-
end
|
27
|
-
|
28
|
-
# add options to menu
|
29
|
-
# calculate help text
|
30
|
-
#
|
31
|
-
# :reek:NestedIterators
|
32
|
-
def add_options(opts_raw)
|
33
|
-
return self if opts_raw.nil?
|
34
|
-
|
35
|
-
help_rows = opts_raw.map do |key, opt_raw|
|
36
|
-
opt_name = key_name_to_option_name(key)
|
37
|
-
|
38
|
-
# set_per_options(opt_name, opt_raw)
|
39
|
-
@opts[opt_name] = (opt_raw ||= {})
|
40
|
-
set_key_value_as_cast(opt_name, EnvOpts.optdefault(opt_raw))
|
41
|
-
set_key_value_per_environment_as_cast(opt_name, opt_raw)
|
42
|
-
|
43
|
-
[
|
44
|
-
[20, '-', "--#{opt_name}"],
|
45
|
-
[16, '-',
|
46
|
-
if @opts[opt_name][:env].present?
|
47
|
-
option_name_to_environment_name(opt_name, @opts[opt_name])
|
48
|
-
else
|
49
|
-
''
|
50
|
-
end],
|
51
|
-
# [24, '-', get_environment_value_from_option(opt_name, @opts[opt_name])],
|
52
|
-
[24, '-', @opts[opt_name][:default]],
|
53
|
-
[6, '-', if (fixed = opt_raw.fetch(:fixed, nil)).nil?
|
54
|
-
":#{option_cast(@opts[opt_name])}"
|
55
|
-
else
|
56
|
-
fixed.to_s
|
57
|
-
end]
|
58
|
-
]
|
59
|
-
end
|
60
|
-
|
61
|
-
max_widths = help_rows.reduce([0, 0, 0, 0]) do |memo, vals|
|
62
|
-
vals.map.with_index do |val, ind|
|
63
|
-
[memo[ind], val[2].to_s.length].max
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
@values['help'] = help_rows.map do |row|
|
68
|
-
row.map.with_index do |cell, ind|
|
69
|
-
format("%#{cell[1]}#{max_widths[ind]}s", cell[2])
|
70
|
-
end.join(' ')
|
71
|
-
end.join("\n")
|
72
|
-
|
73
|
-
self
|
74
|
-
end
|
75
|
-
|
76
|
-
# accept :d or :default option
|
77
|
-
#
|
78
|
-
def self.optdefault(opt_raw)
|
79
|
-
return opt_raw[:d] unless opt_raw[:d].nil?
|
80
|
-
|
81
|
-
opt_raw[:default]
|
82
|
-
end
|
83
|
-
|
84
|
-
def output_help
|
85
|
-
puts @values['help']
|
86
|
-
end
|
87
|
-
|
88
|
-
# process arguments as mostly pairs of option name and value
|
89
|
-
#
|
90
|
-
def parse(argv = ARGV)
|
91
|
-
return self if argv.nil? || !(argv&.count || 0).positive?
|
92
|
-
|
93
|
-
args_ind = 0
|
94
|
-
while args_ind < argv.count
|
95
|
-
args_consumed = 0
|
96
|
-
arg = argv.fetch(args_ind, '')
|
97
|
-
if arg.start_with? '--'
|
98
|
-
opt_name = arg[2..-1]
|
99
|
-
args_consumed = consume_arguments(opt_name,
|
100
|
-
argv.fetch(args_ind + 1, nil))
|
101
|
-
end
|
102
|
-
|
103
|
-
if args_consumed.zero?
|
104
|
-
if arg == '--help'
|
105
|
-
output_help
|
106
|
-
exit
|
107
|
-
elsif block_given?
|
108
|
-
yield 'NAO', [arg]
|
109
|
-
args_consumed = 1
|
110
|
-
else
|
111
|
-
warn "Invalid argument: #{arg.inspect} in #{argv.inspect}"
|
112
|
-
exit 1
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
args_ind += args_consumed
|
117
|
-
end
|
118
|
-
|
119
|
-
self
|
120
|
-
end
|
121
|
-
|
122
|
-
# set option current values per environment values
|
123
|
-
#
|
124
|
-
def options_per_environment_as_cast(opts_raw)
|
125
|
-
return self if opts_raw.nil?
|
126
|
-
|
127
|
-
opts_raw.each do |key, opt_raw|
|
128
|
-
set_key_value_per_environment_as_cast(key_name_to_option_name(key),
|
129
|
-
opt_raw)
|
130
|
-
end
|
131
|
-
|
132
|
-
self
|
133
|
-
end
|
134
|
-
|
135
|
-
# symbol name to option name
|
136
|
-
# option names use hyphens
|
137
|
-
#
|
138
|
-
def self.symbol_name_to_option_name(name)
|
139
|
-
name.to_s.gsub('_', '-')
|
140
|
-
end
|
141
|
-
|
142
|
-
private
|
143
|
-
|
144
|
-
# convert key name or symbol to an option name
|
145
|
-
#
|
146
|
-
def key_name_to_option_name(key)
|
147
|
-
(key.is_a?(Symbol) ? EnvOpts.symbol_name_to_option_name(key) : key)
|
148
|
-
end
|
149
|
-
|
150
|
-
# get cast of environment variable
|
151
|
-
#
|
152
|
-
def option_cast(opt_raw)
|
153
|
-
(opt_raw[:cast].present? ? opt_raw[:cast].to_s : 'to_s')
|
154
|
-
end
|
155
|
-
|
156
|
-
# update value for named option
|
157
|
-
# return number of arguments used
|
158
|
-
#
|
159
|
-
def consume_arguments(opt_name, value)
|
160
|
-
return 0 if (opt_raw = @opts.fetch(opt_name, nil)).nil?
|
161
|
-
|
162
|
-
return 0 unless opt_raw.fetch(:option, true)
|
163
|
-
|
164
|
-
if !(fixed = opt_raw.fetch(:fixed, nil)).nil?
|
165
|
-
set_key_value_as_cast(opt_name, fixed)
|
166
|
-
1
|
167
|
-
elsif value.nil?
|
168
|
-
0
|
169
|
-
else
|
170
|
-
set_key_value_as_cast(opt_name, value)
|
171
|
-
2
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
# option names use hyphens
|
176
|
-
#
|
177
|
-
def method_name_to_option_name(name)
|
178
|
-
name.to_s.gsub('_', '-')
|
179
|
-
end
|
180
|
-
|
181
|
-
# read and write options using the option name as a method
|
182
|
-
#
|
183
|
-
def method_missing(method_name, *args)
|
184
|
-
if method_name.to_s.end_with?('=')
|
185
|
-
value = args.first
|
186
|
-
name = method_name_to_option_name(method_name.to_s[0..-2])
|
187
|
-
set_key_value_as_cast(name, value)
|
188
|
-
else
|
189
|
-
@values[method_name_to_option_name(method_name)]
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
# option name to environment name
|
194
|
-
# if true or empty, compute from option name
|
195
|
-
#
|
196
|
-
def option_name_to_environment_name(opt_name, opt_raw)
|
197
|
-
case env_name = opt_raw.fetch(:env, '')
|
198
|
-
when true, ''
|
199
|
-
"#{@values['env-prefix']}#{opt_name.upcase.gsub('-', '_')}"
|
200
|
-
else
|
201
|
-
env_name
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
# get environment value from option
|
206
|
-
#
|
207
|
-
def get_environment_value_from_option(opt_name, opt_raw)
|
208
|
-
ENV.fetch(option_name_to_environment_name(opt_name, opt_raw),
|
209
|
-
nil)
|
210
|
-
end
|
211
|
-
|
212
|
-
# option names are available as methods
|
213
|
-
#
|
214
|
-
# :reek:BooleanParameter
|
215
|
-
def respond_to_missing?(method_name, include_private = false)
|
216
|
-
(@opts.keys.include?(method_name_to_option_name(method_name)) || super)
|
217
|
-
end
|
218
|
-
|
219
|
-
def set_key_value_as_cast(key, value)
|
220
|
-
opt = @opts[key]
|
221
|
-
set_key_value_raw(key, (opt[:cast] ? value.send(opt[:cast]) : value))
|
222
|
-
end
|
223
|
-
|
224
|
-
# set key value_per environment as cast
|
225
|
-
#
|
226
|
-
def set_key_value_per_environment_as_cast(key, opt_raw)
|
227
|
-
return if opt_raw[:env].nil?
|
228
|
-
|
229
|
-
value = get_environment_value_from_option(key, opt_raw)
|
230
|
-
|
231
|
-
return unless value
|
232
|
-
|
233
|
-
set_key_value_as_cast(key,
|
234
|
-
opt_raw[:cast] ? value.send(opt_raw[:cast]) : value)
|
235
|
-
end
|
236
|
-
|
237
|
-
# set key value (raw)
|
238
|
-
#
|
239
|
-
def set_key_value_raw(key, value)
|
240
|
-
@values[key] = value
|
241
|
-
end
|
242
|
-
end
|
@@ -1,195 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
# encoding=utf-8
|
5
|
-
|
6
|
-
require_relative 'filter'
|
7
|
-
|
8
|
-
module MarkdownExec
|
9
|
-
##
|
10
|
-
# MarkdownBlockManager represents an imported markdown document.
|
11
|
-
#
|
12
|
-
# It provides methods to extract and manipulate specific sections
|
13
|
-
# of the document, such as code blocks. It also supports recursion
|
14
|
-
# to fetch related or dependent blocks.
|
15
|
-
#
|
16
|
-
class MarkdownBlockManager
|
17
|
-
attr_reader :block_table
|
18
|
-
|
19
|
-
def initialize(block_table)
|
20
|
-
@block_table = block_table
|
21
|
-
end
|
22
|
-
|
23
|
-
def collect_required_code(name)
|
24
|
-
gather_required_blocks(name)
|
25
|
-
.map do |block|
|
26
|
-
process_block_code(block)
|
27
|
-
end.flatten(1)
|
28
|
-
end
|
29
|
-
|
30
|
-
def get_block(name, default = {})
|
31
|
-
@block_table.select { |block| block.fetch(:name, '') == name }.fetch(0, default)
|
32
|
-
end
|
33
|
-
|
34
|
-
def gather_required_blocks(name)
|
35
|
-
named_block = get_block(name)
|
36
|
-
if named_block.nil? || named_block.keys.empty?
|
37
|
-
raise "Named code block `#{name}` not found."
|
38
|
-
end
|
39
|
-
|
40
|
-
all_blocks = [named_block.fetch(:name, '')] + required_blocks(named_block[:reqs])
|
41
|
-
@block_table.select { |block| all_blocks.include? block.fetch(:name, '') }
|
42
|
-
.map do |block|
|
43
|
-
process_block_references(block)
|
44
|
-
end.flatten(1)
|
45
|
-
end
|
46
|
-
|
47
|
-
def hide_block_given_options(opts, block)
|
48
|
-
(opts[:hide_blocks_by_name] &&
|
49
|
-
block[:name]&.match(Regexp.new(opts[:block_name_hidden_match])) &&
|
50
|
-
(block[:name]&.present? || block[:label]&.present?)
|
51
|
-
)
|
52
|
-
end
|
53
|
-
|
54
|
-
def blocks_per_options(opts = {})
|
55
|
-
filtered_blocks = @block_table.select do |block_group|
|
56
|
-
Filter.block_selected? opts, block_group
|
57
|
-
end
|
58
|
-
if opts[:hide_blocks_by_name]
|
59
|
-
filtered_blocks.reject { |block| hide_block_given_options opts, block }
|
60
|
-
else
|
61
|
-
filtered_blocks
|
62
|
-
end.map do |block|
|
63
|
-
block
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def required_blocks(reqs)
|
68
|
-
return [] unless reqs
|
69
|
-
|
70
|
-
remaining = reqs
|
71
|
-
collected = []
|
72
|
-
while remaining.count.positive?
|
73
|
-
remaining = remaining.map do |req|
|
74
|
-
next if collected.include? req
|
75
|
-
|
76
|
-
collected += [req]
|
77
|
-
get_block(req).fetch(:reqs, [])
|
78
|
-
end
|
79
|
-
.compact
|
80
|
-
.flatten(1)
|
81
|
-
end
|
82
|
-
collected
|
83
|
-
end
|
84
|
-
|
85
|
-
# Helper method to process block code
|
86
|
-
def process_block_code(block)
|
87
|
-
body = block[:body].join("\n")
|
88
|
-
|
89
|
-
if block[:cann]
|
90
|
-
xcall = block[:cann][1..-2]
|
91
|
-
mstdin = xcall.match(/<(?<type>\$)?(?<name>[A-Za-z_\-.\w]+)/)
|
92
|
-
mstdout = xcall.match(/>(?<type>\$)?(?<name>[A-Za-z_\-.\w]+)/)
|
93
|
-
|
94
|
-
yqcmd = if mstdin[:type]
|
95
|
-
"echo \"$#{mstdin[:name]}\" | yq '#{body}'"
|
96
|
-
else
|
97
|
-
"yq e '#{body}' '#{mstdin[:name]}'"
|
98
|
-
end
|
99
|
-
if mstdout[:type]
|
100
|
-
"export #{mstdout[:name]}=$(#{yqcmd})"
|
101
|
-
else
|
102
|
-
"#{yqcmd} > '#{mstdout[:name]}'"
|
103
|
-
end
|
104
|
-
elsif block[:stdout]
|
105
|
-
stdout = block[:stdout]
|
106
|
-
body = block[:body].join("\n")
|
107
|
-
if stdout[:type]
|
108
|
-
%(export #{stdout[:name]}=$(cat <<"EOF"\n#{body}\nEOF\n))
|
109
|
-
else
|
110
|
-
"cat > '#{stdout[:name]}' <<\"EOF\"\n" \
|
111
|
-
"#{body}\n" \
|
112
|
-
"EOF\n"
|
113
|
-
end
|
114
|
-
else
|
115
|
-
block[:body]
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
# Helper method to process block references
|
120
|
-
def process_block_references(block)
|
121
|
-
if (call = block[:call])
|
122
|
-
[get_block("[#{call.match(/^%\((\S+) |\)/)[1]}]")
|
123
|
-
.merge({ cann: call })]
|
124
|
-
else
|
125
|
-
[]
|
126
|
-
end + [block]
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
if $PROGRAM_NAME == __FILE__
|
132
|
-
# require 'bundler/setup'
|
133
|
-
# Bundler.require(:default)
|
134
|
-
|
135
|
-
require 'minitest/autorun'
|
136
|
-
|
137
|
-
require_relative 'tap'
|
138
|
-
include Tap
|
139
|
-
|
140
|
-
module MarkdownExec
|
141
|
-
class TestMDoc < Minitest::Test
|
142
|
-
def setup
|
143
|
-
@table = [
|
144
|
-
{ name: 'block1', body: ['code for block1'], reqs: ['block2'] },
|
145
|
-
{ name: 'block2', body: ['code for block2'], reqs: nil },
|
146
|
-
{ name: 'block3', body: ['code for block3'], reqs: ['block1'] }
|
147
|
-
]
|
148
|
-
@doc = MDoc.new(@table)
|
149
|
-
end
|
150
|
-
|
151
|
-
def test_collect_recursively_required_code
|
152
|
-
result = @doc.collect_recursively_required_code('block1')
|
153
|
-
expected_result = @table[0][:body] + @table[1][:body]
|
154
|
-
assert_equal expected_result, result
|
155
|
-
end
|
156
|
-
|
157
|
-
def test_get_block_by_name
|
158
|
-
result = @doc.get_block_by_name('block1')
|
159
|
-
assert_equal @table[0], result
|
160
|
-
|
161
|
-
result_missing = @doc.get_block_by_name('missing_block')
|
162
|
-
assert_equal({}, result_missing)
|
163
|
-
end
|
164
|
-
|
165
|
-
def test_get_required_blocks
|
166
|
-
result = @doc.get_required_blocks('block3')
|
167
|
-
expected_result = [@table[0], @table[1], @table[2]]
|
168
|
-
assert_equal expected_result, result
|
169
|
-
|
170
|
-
assert_raises(RuntimeError) { @doc.get_required_blocks('missing_block') }
|
171
|
-
end
|
172
|
-
|
173
|
-
def test_hide_menu_block_per_options
|
174
|
-
opts = { hide_blocks_by_name: true, block_name_hidden_match: 'block1' }
|
175
|
-
block = { name: 'block1' }
|
176
|
-
result = @doc.hide_menu_block_per_options(opts, block)
|
177
|
-
assert result # this should be true based on the given logic
|
178
|
-
end
|
179
|
-
|
180
|
-
def test_fcbs_per_options
|
181
|
-
opts = { hide_blocks_by_name: true, block_name_hidden_match: 'block1' }
|
182
|
-
result = @doc.fcbs_per_options(opts)
|
183
|
-
assert_equal [@table[1], @table[2]], result
|
184
|
-
end
|
185
|
-
|
186
|
-
def test_recursively_required
|
187
|
-
result = @doc.recursively_required(['block3'])
|
188
|
-
assert_equal %w[block3 block1 block2], result
|
189
|
-
|
190
|
-
result_no_reqs = @doc.recursively_required(nil)
|
191
|
-
assert_equal [], result_no_reqs
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
data/lib/menu_options.rb
DELETED
File without changes
|
data/lib/menu_options.yml
DELETED
File without changes
|