markdown_exec 0.0.1 → 0.0.6
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/.rubocop.yml +30 -0
- data/Gemfile +4 -4
- data/Gemfile.lock +56 -0
- data/Rakefile +18 -14
- data/bin/console +3 -3
- data/bin/mde +4 -515
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +527 -5
- metadata +79 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c89f954d1fc114cb22e9222ebc2c7d0575848d597899e1385edbf7b5963f08b
|
4
|
+
data.tar.gz: f14d0e24a3c348c303e96014b68a477d560ed1c3c707ac486173ffe19513df1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b78cd2cfbef3c47acd318c173fa953891f83c29ea2ac59b062dafc2e2ce8573c4f862f2e681cacb6ebd827dc77b10b984dd8a33a1349849357e9c64ab564932d
|
7
|
+
data.tar.gz: 8f8282f7a5a171dee4c83f9fce9974f48b7ee55b43d25651bfebaec0f46b97028c1ed58bd23ea85a796f949cd1eeae923d6899a5efb448879de96aa37287915d
|
data/.rubocop.yml
CHANGED
@@ -1,6 +1,36 @@
|
|
1
1
|
AllCops:
|
2
2
|
TargetRubyVersion: 2.6
|
3
3
|
|
4
|
+
Metrics/AbcSize:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Metrics/BlockLength:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Metrics/BlockNesting:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Metrics/ClassLength:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
Metrics/CyclomaticComplexity:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
# Metrics/LayoutLength:
|
20
|
+
# Enabled: false
|
21
|
+
|
22
|
+
Metrics/MethodLength:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Metrics/PerceivedComplexity:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Style/GlobalVars:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
Style/MultilineBlockChain:
|
32
|
+
Enabled: false
|
33
|
+
|
4
34
|
Style/StringLiterals:
|
5
35
|
Enabled: true
|
6
36
|
EnforcedStyle: single_quotes
|
data/Gemfile
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
source
|
3
|
+
source 'https://rubygems.org'
|
4
4
|
|
5
5
|
# Specify your gem's dependencies in markdown_exec.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
-
gem
|
8
|
+
gem 'rake', '~> 13.0'
|
9
9
|
|
10
|
-
gem
|
10
|
+
gem 'minitest', '~> 5.0'
|
11
11
|
|
12
|
-
gem
|
12
|
+
gem 'rubocop', '~> 1.21'
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
markdown_exec (0.0.3)
|
5
|
+
tty-prompt (~> 0.23.1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ast (2.4.2)
|
11
|
+
minitest (5.15.0)
|
12
|
+
parallel (1.21.0)
|
13
|
+
parser (3.1.1.0)
|
14
|
+
ast (~> 2.4.1)
|
15
|
+
pastel (0.8.0)
|
16
|
+
tty-color (~> 0.5)
|
17
|
+
rainbow (3.1.1)
|
18
|
+
rake (13.0.6)
|
19
|
+
regexp_parser (2.2.1)
|
20
|
+
rexml (3.2.5)
|
21
|
+
rubocop (1.26.0)
|
22
|
+
parallel (~> 1.10)
|
23
|
+
parser (>= 3.1.0.0)
|
24
|
+
rainbow (>= 2.2.2, < 4.0)
|
25
|
+
regexp_parser (>= 1.8, < 3.0)
|
26
|
+
rexml
|
27
|
+
rubocop-ast (>= 1.16.0, < 2.0)
|
28
|
+
ruby-progressbar (~> 1.7)
|
29
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
30
|
+
rubocop-ast (1.16.0)
|
31
|
+
parser (>= 3.1.1.0)
|
32
|
+
ruby-progressbar (1.11.0)
|
33
|
+
tty-color (0.6.0)
|
34
|
+
tty-cursor (0.7.1)
|
35
|
+
tty-prompt (0.23.1)
|
36
|
+
pastel (~> 0.8)
|
37
|
+
tty-reader (~> 0.8)
|
38
|
+
tty-reader (0.9.0)
|
39
|
+
tty-cursor (~> 0.7)
|
40
|
+
tty-screen (~> 0.8)
|
41
|
+
wisper (~> 2.0)
|
42
|
+
tty-screen (0.8.1)
|
43
|
+
unicode-display_width (2.1.0)
|
44
|
+
wisper (2.0.1)
|
45
|
+
|
46
|
+
PLATFORMS
|
47
|
+
arm64-darwin-21
|
48
|
+
|
49
|
+
DEPENDENCIES
|
50
|
+
markdown_exec!
|
51
|
+
minitest (~> 5.0)
|
52
|
+
rake (~> 13.0)
|
53
|
+
rubocop (~> 1.21)
|
54
|
+
|
55
|
+
BUNDLED WITH
|
56
|
+
2.2.32
|
data/Rakefile
CHANGED
@@ -1,37 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rake/testtask'
|
5
5
|
|
6
6
|
Rake::TestTask.new(:test) do |t|
|
7
|
-
t.libs <<
|
8
|
-
t.libs <<
|
9
|
-
t.test_files = FileList[
|
7
|
+
t.libs << 'test'
|
8
|
+
t.libs << 'lib'
|
9
|
+
t.test_files = FileList['test/**/*_test.rb']
|
10
10
|
end
|
11
11
|
|
12
|
-
require
|
12
|
+
require 'rubocop/rake_task'
|
13
13
|
|
14
14
|
RuboCop::RakeTask.new
|
15
15
|
|
16
16
|
task default: %i[test rubocop]
|
17
17
|
|
18
|
-
GEM_NAME =
|
19
|
-
GEM_VERSION =
|
18
|
+
GEM_NAME = 'markdown_exec'
|
19
|
+
GEM_VERSION = '0.0.6'
|
20
20
|
|
21
21
|
# task :default => :build
|
22
22
|
|
23
23
|
task :build do
|
24
|
-
system "gem build
|
24
|
+
system "gem build #{GEM_NAME}.gemspec"
|
25
25
|
end
|
26
26
|
|
27
|
-
task :
|
28
|
-
system "gem install
|
27
|
+
task install: :build do
|
28
|
+
system "gem install #{GEM_NAME}-#{GEM_VERSION}.gem"
|
29
29
|
end
|
30
30
|
|
31
|
-
task :
|
32
|
-
system
|
31
|
+
task publish: :build do
|
32
|
+
system "gem push #{GEM_NAME}-#{GEM_VERSION}.gem"
|
33
|
+
end
|
34
|
+
|
35
|
+
task uninstall: :build do
|
36
|
+
system "gem uninstall #{GEM_NAME}"
|
33
37
|
end
|
34
38
|
|
35
39
|
task :clean do
|
36
|
-
system
|
40
|
+
system 'rm *.gem'
|
37
41
|
end
|
data/bin/console
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'markdown_exec'
|
6
6
|
|
7
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
8
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -11,5 +11,5 @@ require "markdown_exec"
|
|
11
11
|
# require "pry"
|
12
12
|
# Pry.start
|
13
13
|
|
14
|
-
require
|
14
|
+
require 'irb'
|
15
15
|
IRB.start(__FILE__)
|
data/bin/mde
CHANGED
@@ -3,519 +3,8 @@
|
|
3
3
|
|
4
4
|
# encoding=utf-8
|
5
5
|
|
6
|
-
|
7
|
-
$pdebug = !(env_debug || "").empty?
|
6
|
+
require 'markdown_exec'
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
require "markdown_exec"
|
14
|
-
# puts MarkdownExec::MDExec.echo(ARGV[0])
|
15
|
-
|
16
|
-
require "optparse"
|
17
|
-
require "pathname"
|
18
|
-
require "tty-prompt"
|
19
|
-
require "yaml"
|
20
|
-
|
21
|
-
# require_relative 'mdlib'
|
22
|
-
# #!/usr/bin/env ruby
|
23
|
-
# # frozen_string_literal: true
|
24
|
-
# # encoding=utf-8
|
25
|
-
|
26
|
-
# env_debug = ENV['MARKDOWN_EXEC_DEBUG']
|
27
|
-
# $pdebug = !(env_debug || '').empty?
|
28
|
-
|
29
|
-
require "open3"
|
30
|
-
require "tty-prompt"
|
31
|
-
# require 'yaml'
|
32
|
-
# require_relative 'deep_merge'
|
33
|
-
# require_relative 'object_odig'
|
34
|
-
# require_relative 'present'
|
35
|
-
|
36
|
-
BLOCK_SIZE = 1024
|
37
|
-
SELECT_PAGE_HEIGHT = 12
|
38
|
-
|
39
|
-
##
|
40
|
-
#
|
41
|
-
class MarkParse
|
42
|
-
attr_accessor :options
|
43
|
-
|
44
|
-
def initialize(options)
|
45
|
-
@options = options
|
46
|
-
end
|
47
|
-
|
48
|
-
def count_blocks
|
49
|
-
cnt = 0
|
50
|
-
File.readlines(options[:mdfilename]).each do |line|
|
51
|
-
cnt += 1 if line.match(/^```/)
|
52
|
-
end
|
53
|
-
cnt / 2
|
54
|
-
end
|
55
|
-
|
56
|
-
def find_files
|
57
|
-
puts "pwd: #{`pwd`}" if $pdebug
|
58
|
-
# `ls -1 *.md`.split("\n").tap { |ret| puts "find_files() ret: #{ret.inspect}" if $pdebug }
|
59
|
-
`ls -1 #{File.join options[:mdfolder], "*.md"}`.split("\n").tap do |ret|
|
60
|
-
puts "find_files() ret: #{ret.inspect}" if $pdebug
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def fout(str)
|
65
|
-
puts str # to stdout
|
66
|
-
end
|
67
|
-
|
68
|
-
def copts(call_options = {}, options_block = nil)
|
69
|
-
class_call_options = options.merge(call_options || {})
|
70
|
-
if options_block
|
71
|
-
options_block.call class_call_options
|
72
|
-
else
|
73
|
-
class_call_options
|
74
|
-
end.tap { |ret| puts "copts() ret: #{ret.inspect}" if $pdebug }
|
75
|
-
end
|
76
|
-
|
77
|
-
def bsr(headings, title)
|
78
|
-
# puts "bsr() headings: #{headings.inspect}"
|
79
|
-
{ headings: headings, name: title, title: title }
|
80
|
-
end
|
81
|
-
|
82
|
-
def block_summary(opts, headings, block_title, current)
|
83
|
-
puts "block_summary() block_title: #{block_title.inspect}" if $pdebug
|
84
|
-
return [current] unless opts[:struct]
|
85
|
-
|
86
|
-
# return [{ body: current, name: block_title, title: block_title }] unless opts[:bash]
|
87
|
-
return [bsr(headings, block_title).merge({ body: current })] unless opts[:bash]
|
88
|
-
|
89
|
-
bm = block_title.match(/:(\S+)( |$)/)
|
90
|
-
reqs = block_title.scan(/\+\S+/).map { |s| s[1..] }
|
91
|
-
|
92
|
-
if $pdebug
|
93
|
-
puts ["block_summary() bm: #{bm.inspect}",
|
94
|
-
"block_summary() reqs: #{reqs.inspect}"]
|
95
|
-
end
|
96
|
-
|
97
|
-
if bm && bm[1]
|
98
|
-
# [{ body: current, name: bm[1], reqs: reqs, title: bm[1] }]
|
99
|
-
[bsr(headings, bm[1]).merge({ body: current, reqs: reqs })]
|
100
|
-
else
|
101
|
-
# [{ body: current, name: block_title, reqs: reqs, title: block_title }]
|
102
|
-
[bsr(headings, block_title).merge({ body: current, reqs: reqs })]
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def get_blocks(call_options = {}, &options_block)
|
107
|
-
opts = copts call_options, options_block
|
108
|
-
|
109
|
-
blocks = []
|
110
|
-
current = nil
|
111
|
-
in_block = false
|
112
|
-
block_title = ""
|
113
|
-
|
114
|
-
headings = []
|
115
|
-
File.readlines(opts[:mdfilename]).each do |line|
|
116
|
-
puts "get_blocks() line: #{line.inspect}" if $pdebug
|
117
|
-
continue unless line
|
118
|
-
|
119
|
-
if opts[:mdheadings]
|
120
|
-
if lm = line.match(/^### *(.+?) *$/)
|
121
|
-
headings = [headings[0], headings[1], lm[1]]
|
122
|
-
elsif lm = line.match(/^## *([^#]*?) *$/)
|
123
|
-
headings = [headings[0], lm[1]]
|
124
|
-
elsif lm = line.match(/^# *([^#]*?) *$/)
|
125
|
-
headings = [lm[1]]
|
126
|
-
end
|
127
|
-
puts "get_blocks() headings: #{headings.inspect}" if $pdebug
|
128
|
-
end
|
129
|
-
|
130
|
-
if line.match(/^`{3,}/)
|
131
|
-
if in_block
|
132
|
-
puts "get_blocks() in_block" if $pdebug
|
133
|
-
if current
|
134
|
-
|
135
|
-
# block_title ||= current.join(' ').gsub(/ +/, ' ')[0..64]
|
136
|
-
block_title = current.join(" ").gsub(/ +/, " ")[0..64] if block_title.nil? || block_title.empty?
|
137
|
-
|
138
|
-
blocks += block_summary opts, headings, block_title, current
|
139
|
-
current = nil
|
140
|
-
end
|
141
|
-
in_block = false
|
142
|
-
block_title = ""
|
143
|
-
else
|
144
|
-
## new block
|
145
|
-
#
|
146
|
-
|
147
|
-
# lm = line.match(/^`{3,}([^`\s]+)( .+)?$/)
|
148
|
-
lm = line.match(/^`{3,}([^`\s]*) *(.*)$/)
|
149
|
-
|
150
|
-
do1 = false
|
151
|
-
if opts[:bash_only]
|
152
|
-
do1 = true if lm && (lm[1] == "bash")
|
153
|
-
elsif opts[:exclude_expect_blocks]
|
154
|
-
do1 = true unless lm && (lm[1] == "expect")
|
155
|
-
else
|
156
|
-
do1 = true
|
157
|
-
end
|
158
|
-
if $pdebug
|
159
|
-
puts ["get_blocks() lm: #{lm.inspect}",
|
160
|
-
"get_blocks() opts: #{opts.inspect}",
|
161
|
-
"get_blocks() do1: #{do1}"]
|
162
|
-
end
|
163
|
-
|
164
|
-
if do1 && (!opts[:title_match] || (lm && lm[2] && lm[2].match(opts[:title_match])))
|
165
|
-
current = []
|
166
|
-
in_block = true
|
167
|
-
block_title = (lm && lm[2])
|
168
|
-
end
|
169
|
-
|
170
|
-
end
|
171
|
-
elsif current
|
172
|
-
current += [line.chomp]
|
173
|
-
end
|
174
|
-
end
|
175
|
-
blocks.tap { |ret| puts "get_blocks() ret: #{ret.inspect}" if $pdebug }
|
176
|
-
end
|
177
|
-
|
178
|
-
def make_block_label(block, call_options = {})
|
179
|
-
opts = options.merge(call_options)
|
180
|
-
puts "make_block_label() opts: #{opts.inspect}" if $pdebug
|
181
|
-
puts "make_block_label() block: #{block.inspect}" if $pdebug
|
182
|
-
if opts[:mdheadings]
|
183
|
-
heads = block.fetch(:headings, []).compact.join(" # ")
|
184
|
-
"#{block[:title]} [#{heads}] (#{opts[:mdfilename]})"
|
185
|
-
else
|
186
|
-
"#{block[:title]} (#{opts[:mdfilename]})"
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
def make_block_labels(call_options = {})
|
191
|
-
opts = options.merge(call_options)
|
192
|
-
get_blocks(opts).map do |block|
|
193
|
-
make_block_label block, opts
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
def select_block(call_options = {}, &options_block)
|
198
|
-
opts = copts call_options, options_block
|
199
|
-
|
200
|
-
blocks = get_blocks(opts.merge(struct: true))
|
201
|
-
puts "select_block() blocks: #{blocks.to_yaml}" if $pdebug
|
202
|
-
|
203
|
-
prompt = TTY::Prompt.new(interrupt: :exit)
|
204
|
-
pt = "#{opts.fetch(:prompt, nil) || "Pick one"}:"
|
205
|
-
puts "select_block() pt: #{pt.inspect}" if $pdebug
|
206
|
-
|
207
|
-
blocks.each { |block| block.merge! label: make_block_label(block, opts) }
|
208
|
-
block_labels = blocks.map { |block| block[:label] }
|
209
|
-
puts "select_block() block_labels: #{block_labels.inspect}" if $pdebug
|
210
|
-
|
211
|
-
if opts[:preview_options]
|
212
|
-
select_per_page = 3
|
213
|
-
block_labels.each do |bn|
|
214
|
-
fout " - #{bn}"
|
215
|
-
end
|
216
|
-
else
|
217
|
-
select_per_page = SELECT_PAGE_HEIGHT
|
218
|
-
end
|
219
|
-
|
220
|
-
return nil if block_labels.count.zero?
|
221
|
-
|
222
|
-
sel = prompt.select(pt, block_labels, per_page: select_per_page)
|
223
|
-
puts "select_block() sel: #{sel.inspect}" if $pdebug
|
224
|
-
# catch
|
225
|
-
# # catch TTY::Reader::InputInterrupt
|
226
|
-
# puts "InputInterrupt"
|
227
|
-
# end
|
228
|
-
|
229
|
-
block = blocks.select { |block| block[:label] == sel }.fetch(0, nil)
|
230
|
-
puts "select_block() block: #{block.inspect}" if $pdebug
|
231
|
-
sel = block[:name]
|
232
|
-
puts "select_block() sel: #{sel.inspect}" if $pdebug
|
233
|
-
|
234
|
-
cbs = code_blocks(blocks, sel)
|
235
|
-
puts "select_block() cbs: #{cbs.inspect}" if $pdebug
|
236
|
-
|
237
|
-
## display code blocks for approval
|
238
|
-
#
|
239
|
-
cbs.each { |cb| fout cb } if opts[:display] || opts[:approve]
|
240
|
-
|
241
|
-
allow = true
|
242
|
-
allow = prompt.yes? "Process?" if opts[:approve]
|
243
|
-
|
244
|
-
selected = table_block blocks, sel
|
245
|
-
puts "select_block() selected: #{selected.inspect}" if $pdebug
|
246
|
-
if allow && opts[:execute]
|
247
|
-
|
248
|
-
## process in script, to handle line continuations
|
249
|
-
#
|
250
|
-
cmd2 = cbs.flatten.join("\n")
|
251
|
-
fout "$ #{cmd2.to_yaml}"
|
252
|
-
cnt = 0
|
253
|
-
|
254
|
-
# Open3.popen3(cmd2) do |stdin, stdout, stderr, wait_thr|
|
255
|
-
# cnt += 1
|
256
|
-
# # stdin.puts "This is sent to the command"
|
257
|
-
# # stdin.close # we're done
|
258
|
-
# stdout_str = stdout.read # read stdout to string. note that this will block until the command is done!
|
259
|
-
# stderr_str = stderr.read # read stderr to string
|
260
|
-
# status = wait_thr.value # will block until the command finishes; returns status that responds to .success? etc
|
261
|
-
# fout "#{stdout_str}"
|
262
|
-
# fout "#{cnt}: err: #{stderr_str}" if stderr_str != ''
|
263
|
-
# # fout "#{cnt}: stt: #{status}"
|
264
|
-
# end
|
265
|
-
|
266
|
-
Open3.popen3(cmd2) do |stdin, stdout, stderr|
|
267
|
-
stdin.close_write
|
268
|
-
begin
|
269
|
-
files = [stdout, stderr]
|
270
|
-
|
271
|
-
until all_eof(files)
|
272
|
-
ready = IO.select(files)
|
273
|
-
|
274
|
-
next unless ready
|
275
|
-
|
276
|
-
readable = ready[0]
|
277
|
-
# writable = ready[1]
|
278
|
-
# exceptions = ready[2]
|
279
|
-
|
280
|
-
readable.each do |f|
|
281
|
-
fileno = f.fileno
|
282
|
-
|
283
|
-
begin
|
284
|
-
data = f.read_nonblock(BLOCK_SIZE)
|
285
|
-
# fout "- fileno: #{fileno}\n#{data}"
|
286
|
-
fout data
|
287
|
-
rescue EOFError => e
|
288
|
-
# fout "fileno: #{fileno} EOF"
|
289
|
-
end
|
290
|
-
end
|
291
|
-
end
|
292
|
-
rescue IOError => e
|
293
|
-
fout "IOError: #{e}"
|
294
|
-
end
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
selected[:name]
|
299
|
-
end
|
300
|
-
|
301
|
-
def select_md_file
|
302
|
-
opts = options
|
303
|
-
files = find_files
|
304
|
-
if files.count == 1
|
305
|
-
sel = files[0]
|
306
|
-
elsif files.count >= 2
|
307
|
-
|
308
|
-
if opts[:preview_options]
|
309
|
-
select_per_page = 3
|
310
|
-
files.each do |file|
|
311
|
-
fout " - #{file}"
|
312
|
-
end
|
313
|
-
else
|
314
|
-
select_per_page = SELECT_PAGE_HEIGHT
|
315
|
-
end
|
316
|
-
|
317
|
-
prompt = TTY::Prompt.new
|
318
|
-
sel = prompt.select("#{opts.fetch(:prompt, "Pick one")}:", files, per_page: select_per_page)
|
319
|
-
end
|
320
|
-
|
321
|
-
sel
|
322
|
-
end
|
323
|
-
|
324
|
-
# Returns true if all files are EOF
|
325
|
-
#
|
326
|
-
def all_eof(files)
|
327
|
-
files.find { |f| !f.eof }.nil?
|
328
|
-
end
|
329
|
-
|
330
|
-
def code(table, block)
|
331
|
-
puts "code() table: #{table.inspect}" if $pdebug
|
332
|
-
puts "code() block: #{block.inspect}" if $pdebug
|
333
|
-
all = [block[:name]] + unroll(table, block[:reqs])
|
334
|
-
puts "code() all: #{all.inspect}" if $pdebug
|
335
|
-
all.reverse.map do |req|
|
336
|
-
puts "code() req: #{req.inspect}" if $pdebug
|
337
|
-
table_block(table, req).fetch(:body, "")
|
338
|
-
end.flatten(1).tap { |ret| puts "code() ret: #{ret.inspect}" if $pdebug }
|
339
|
-
end
|
340
|
-
|
341
|
-
def table_block(table, name, default = {})
|
342
|
-
table.select { |block| block[:name] == name }.fetch(0, default)
|
343
|
-
end
|
344
|
-
|
345
|
-
def code_blocks(table, name)
|
346
|
-
puts "code_blocks() table: #{table.inspect}" if $pdebug
|
347
|
-
puts "code_blocks() name: #{name.inspect}" if $pdebug
|
348
|
-
block = table_block(table, name)
|
349
|
-
puts "code_blocks() block: #{block.inspect}" if $pdebug
|
350
|
-
all = [block[:name]] + unroll(table, block[:reqs])
|
351
|
-
puts "code_blocks() all: #{all.inspect}" if $pdebug
|
352
|
-
all.reverse.map do |req|
|
353
|
-
puts "code_blocks() req: #{req.inspect}" if $pdebug
|
354
|
-
table_block(table, req).fetch(:body, "")
|
355
|
-
end.flatten(1).tap { |ret| puts "code_blocks() ret: #{ret.inspect}" if $pdebug }
|
356
|
-
end
|
357
|
-
|
358
|
-
def unroll(table, reqs)
|
359
|
-
puts "unroll() table: #{table.inspect}" if $pdebug
|
360
|
-
puts "unroll() reqs: #{reqs.inspect}" if $pdebug
|
361
|
-
all = []
|
362
|
-
rem = reqs
|
363
|
-
while rem.count.positive?
|
364
|
-
puts "unrol() rem: #{rem.inspect}" if $pdebug
|
365
|
-
rem = rem.map do |req|
|
366
|
-
puts "unrol() req: #{req.inspect}" if $pdebug
|
367
|
-
next if all.include? req
|
368
|
-
|
369
|
-
all += [req]
|
370
|
-
puts "unrol() all: #{all.inspect}" if $pdebug
|
371
|
-
table_block(table, req).fetch(:reqs, [])
|
372
|
-
end.compact.flatten(1).tap { |_ret| puts "unroll() rem: #{rem.inspect}" if $pdebug }
|
373
|
-
end
|
374
|
-
all.tap { |ret| puts "unroll() ret: #{ret.inspect}" if $pdebug }
|
375
|
-
end
|
376
|
-
end
|
377
|
-
|
378
|
-
$stderr.sync = true
|
379
|
-
$stdout.sync = true
|
380
|
-
|
381
|
-
def fout(str)
|
382
|
-
puts str # to stdout
|
383
|
-
end
|
384
|
-
|
385
|
-
## configuration file
|
386
|
-
#
|
387
|
-
def read_configuration!(options, configuration_path)
|
388
|
-
if Pathname.new(configuration_path).exist?
|
389
|
-
# rubocop:disable Security/YAMLLoad
|
390
|
-
options.merge!((YAML::load(open(configuration_path)) || {})
|
391
|
-
.transform_keys(&:to_sym))
|
392
|
-
# rubocop:enable Security/YAMLLoad
|
393
|
-
end
|
394
|
-
options
|
395
|
-
end
|
396
|
-
|
397
|
-
## default configuration
|
398
|
-
#
|
399
|
-
options = {
|
400
|
-
mdheadings: true,
|
401
|
-
list_blocks: false,
|
402
|
-
list_docs: false,
|
403
|
-
mdfilename: "README.md",
|
404
|
-
mdfolder: "."
|
405
|
-
}
|
406
|
-
|
407
|
-
def options_finalize!(options); end
|
408
|
-
|
409
|
-
# read local configuration file
|
410
|
-
#
|
411
|
-
read_configuration! options, ".#{APP_NAME.downcase}.yml"
|
412
|
-
|
413
|
-
## read current details for aws resources from app_data_file
|
414
|
-
#
|
415
|
-
# load_resources! options
|
416
|
-
# puts "q31 options: #{options.to_yaml}" if $pdebug
|
417
|
-
|
418
|
-
option_parser = OptionParser.new do |opts|
|
419
|
-
executable_name = File.basename($PROGRAM_NAME)
|
420
|
-
opts.banner = [
|
421
|
-
APP_VDESC = "#{APP_NAME} - #{APP_DESC} (#{VERSION})",
|
422
|
-
"Usage: #{executable_name} [options]"
|
423
|
-
].join("\n")
|
424
|
-
|
425
|
-
## menu top: on_head appear in reverse order added
|
426
|
-
#
|
427
|
-
opts.on("--config PATH", "Read configuration file") do |value|
|
428
|
-
read_configuration! options, value
|
429
|
-
end
|
430
|
-
|
431
|
-
## menu body: items appear in order added
|
432
|
-
#
|
433
|
-
opts.on("-f RELATIVE", "--mdfilename", "Name of document") do |value|
|
434
|
-
options[:mdfilename] = value
|
435
|
-
end
|
436
|
-
|
437
|
-
opts.on("-p PATH", "--mdfolder", "Path to documents") do |value|
|
438
|
-
options[:mdfolder] = value
|
439
|
-
end
|
440
|
-
|
441
|
-
opts.on("--list-blocks", "List blocks") do |_value|
|
442
|
-
options[:list_blocks] = true
|
443
|
-
end
|
444
|
-
|
445
|
-
opts.on("--list-docs", "List docs in current folder") do |_value|
|
446
|
-
options[:list_docs] = true
|
447
|
-
end
|
448
|
-
|
449
|
-
## menu bottom: items appear in order added
|
450
|
-
#
|
451
|
-
opts.on_tail("-h", "--help", "App help") do |_value|
|
452
|
-
puts option_parser.help
|
453
|
-
exit
|
454
|
-
end
|
455
|
-
|
456
|
-
opts.on_tail("-v", "--version", "App version") do |_value|
|
457
|
-
puts VERSION
|
458
|
-
exit
|
459
|
-
end
|
460
|
-
|
461
|
-
opts.on_tail("-x", "--exit", "Exit app") do |_value|
|
462
|
-
exit
|
463
|
-
end
|
464
|
-
|
465
|
-
opts.on_tail("-0", "Show configuration") do |_v|
|
466
|
-
options_finalize! options
|
467
|
-
puts options.to_yaml
|
468
|
-
end
|
469
|
-
end
|
470
|
-
|
471
|
-
option_parser.load # filename defaults to basename of the program without suffix in a directory ~/.options
|
472
|
-
option_parser.environment # env defaults to the basename of the program.
|
473
|
-
option_parser.parse! # (into: options)
|
474
|
-
options_finalize! options
|
475
|
-
|
476
|
-
## process
|
477
|
-
#
|
478
|
-
loop do # once
|
479
|
-
mp = MarkParse.new options
|
480
|
-
options.merge!(
|
481
|
-
{
|
482
|
-
approve: true,
|
483
|
-
bash: true,
|
484
|
-
display: true,
|
485
|
-
execute: true,
|
486
|
-
prompt: "Execute",
|
487
|
-
struct: true
|
488
|
-
}
|
489
|
-
)
|
490
|
-
|
491
|
-
## show
|
492
|
-
#
|
493
|
-
if options[:list_docs]
|
494
|
-
fout mp.find_files
|
495
|
-
break
|
496
|
-
end
|
497
|
-
|
498
|
-
if options[:list_blocks]
|
499
|
-
fout (mp.find_files.map do |file|
|
500
|
-
mp.make_block_labels(mdfilename: file, struct: true)
|
501
|
-
end).flatten(1).to_yaml
|
502
|
-
break
|
503
|
-
end
|
504
|
-
|
505
|
-
## process
|
506
|
-
#
|
507
|
-
mp.select_block(bash: true, struct: true) if options[:mdfilename]
|
508
|
-
|
509
|
-
=begin
|
510
|
-
# rescue ArgumentError => e
|
511
|
-
# puts "User abort: #{e}"
|
512
|
-
|
513
|
-
# rescue StandardError => e
|
514
|
-
# puts "ERROR: #{e}"
|
515
|
-
# raise StandardError, e
|
516
|
-
|
517
|
-
# ensure
|
518
|
-
# exit
|
519
|
-
=end
|
520
|
-
break unless false
|
521
|
-
end
|
8
|
+
mp = MarkdownExec::MarkParse.new
|
9
|
+
mp.fout Time.now
|
10
|
+
# mp.run
|
data/lib/markdown_exec.rb
CHANGED
@@ -1,14 +1,536 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative 'markdown_exec/version'
|
4
|
+
|
5
|
+
#!/usr/bin/env ruby
|
6
|
+
# frozen_string_literal: true
|
7
|
+
|
8
|
+
# encoding=utf-8
|
9
|
+
# rubocop:disable Style/GlobalVars
|
10
|
+
|
11
|
+
env_debug = ENV['MARKDOWN_EXEC_DEBUG']
|
12
|
+
$pdebug = !(env_debug || '').empty?
|
13
|
+
|
14
|
+
# APP_NAME = 'MDExec'
|
15
|
+
# APP_DESC = 'Markdown block executor'
|
16
|
+
# VERSION = '0.0.6'
|
17
|
+
|
18
|
+
# require 'markdown_exec'
|
19
|
+
# # puts MarkdownExec::MarkParse.echo(ARGV[0])
|
20
|
+
|
21
|
+
require 'optparse'
|
22
|
+
require 'pathname'
|
23
|
+
require 'tty-prompt'
|
24
|
+
require 'yaml'
|
25
|
+
|
26
|
+
# require_relative 'mdlib'
|
27
|
+
# #!/usr/bin/env ruby
|
28
|
+
# # frozen_string_literal: true
|
29
|
+
# # encoding=utf-8
|
30
|
+
|
31
|
+
# env_debug = ENV['MARKDOWN_EXEC_DEBUG']
|
32
|
+
# $pdebug = !(env_debug || '').empty?
|
33
|
+
|
34
|
+
require 'open3'
|
35
|
+
# require 'tty-prompt'
|
36
|
+
|
37
|
+
BLOCK_SIZE = 1024
|
38
|
+
SELECT_PAGE_HEIGHT = 12
|
4
39
|
|
5
40
|
module MarkdownExec
|
6
41
|
class Error < StandardError; end
|
7
|
-
# Your code goes here...
|
8
42
|
|
9
|
-
|
10
|
-
|
11
|
-
|
43
|
+
# Markdown Exec
|
44
|
+
# class MarkParse
|
45
|
+
# def self.echo(str = '')
|
46
|
+
# "#{str}#{str}"
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
##
|
50
|
+
#
|
51
|
+
class MarkParse
|
52
|
+
attr_accessor :options
|
53
|
+
|
54
|
+
def initialize(options = {})
|
55
|
+
@options = options
|
56
|
+
end
|
57
|
+
|
58
|
+
def count_blocks
|
59
|
+
cnt = 0
|
60
|
+
File.readlines(options[:mdfilename]).each do |line|
|
61
|
+
cnt += 1 if line.match(/^```/)
|
62
|
+
end
|
63
|
+
cnt / 2
|
64
|
+
end
|
65
|
+
|
66
|
+
def find_files
|
67
|
+
puts "pwd: #{`pwd`}" if $pdebug
|
68
|
+
# `ls -1 *.md`.split("\n").tap { |ret| puts "find_files() ret: #{ret.inspect}" if $pdebug }
|
69
|
+
`ls -1 #{File.join options[:mdfolder], '*.md'}`.split("\n").tap do |ret|
|
70
|
+
puts "find_files() ret: #{ret.inspect}" if $pdebug
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def fout(str)
|
75
|
+
puts str # to stdout
|
76
|
+
end
|
77
|
+
|
78
|
+
def copts(call_options = {}, options_block = nil)
|
79
|
+
class_call_options = options.merge(call_options || {})
|
80
|
+
if options_block
|
81
|
+
options_block.call class_call_options
|
82
|
+
else
|
83
|
+
class_call_options
|
84
|
+
end.tap { |ret| puts "copts() ret: #{ret.inspect}" if $pdebug }
|
85
|
+
end
|
86
|
+
|
87
|
+
def bsr(headings, title)
|
88
|
+
# puts "bsr() headings: #{headings.inspect}"
|
89
|
+
{ headings: headings, name: title, title: title }
|
90
|
+
end
|
91
|
+
|
92
|
+
def block_summary(opts, headings, block_title, current)
|
93
|
+
puts "block_summary() block_title: #{block_title.inspect}" if $pdebug
|
94
|
+
return [current] unless opts[:struct]
|
95
|
+
|
96
|
+
# return [{ body: current, name: block_title, title: block_title }] unless opts[:bash]
|
97
|
+
return [bsr(headings, block_title).merge({ body: current })] unless opts[:bash]
|
98
|
+
|
99
|
+
bm = block_title.match(/:(\S+)( |$)/)
|
100
|
+
reqs = block_title.scan(/\+\S+/).map { |s| s[1..] }
|
101
|
+
|
102
|
+
if $pdebug
|
103
|
+
puts ["block_summary() bm: #{bm.inspect}",
|
104
|
+
"block_summary() reqs: #{reqs.inspect}"]
|
105
|
+
end
|
106
|
+
|
107
|
+
if bm && bm[1]
|
108
|
+
# [{ body: current, name: bm[1], reqs: reqs, title: bm[1] }]
|
109
|
+
[bsr(headings, bm[1]).merge({ body: current, reqs: reqs })]
|
110
|
+
else
|
111
|
+
# [{ body: current, name: block_title, reqs: reqs, title: block_title }]
|
112
|
+
[bsr(headings, block_title).merge({ body: current, reqs: reqs })]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def get_blocks(call_options = {}, &options_block)
|
117
|
+
opts = copts call_options, options_block
|
118
|
+
|
119
|
+
blocks = []
|
120
|
+
current = nil
|
121
|
+
in_block = false
|
122
|
+
block_title = ''
|
123
|
+
|
124
|
+
headings = []
|
125
|
+
File.readlines(opts[:mdfilename]).each do |line|
|
126
|
+
puts "get_blocks() line: #{line.inspect}" if $pdebug
|
127
|
+
continue unless line
|
128
|
+
|
129
|
+
if opts[:mdheadings]
|
130
|
+
if (lm = line.match(/^### *(.+?) *$/))
|
131
|
+
headings = [headings[0], headings[1], lm[1]]
|
132
|
+
elsif (lm = line.match(/^## *([^#]*?) *$/))
|
133
|
+
headings = [headings[0], lm[1]]
|
134
|
+
elsif (lm = line.match(/^# *([^#]*?) *$/))
|
135
|
+
headings = [lm[1]]
|
136
|
+
end
|
137
|
+
puts "get_blocks() headings: #{headings.inspect}" if $pdebug
|
138
|
+
end
|
139
|
+
|
140
|
+
if line.match(/^`{3,}/)
|
141
|
+
if in_block
|
142
|
+
puts 'get_blocks() in_block' if $pdebug
|
143
|
+
if current
|
144
|
+
|
145
|
+
# block_title ||= current.join(' ').gsub(/ +/, ' ')[0..64]
|
146
|
+
block_title = current.join(' ').gsub(/ +/, ' ')[0..64] if block_title.nil? || block_title.empty?
|
147
|
+
|
148
|
+
blocks += block_summary opts, headings, block_title, current
|
149
|
+
current = nil
|
150
|
+
end
|
151
|
+
in_block = false
|
152
|
+
block_title = ''
|
153
|
+
else
|
154
|
+
## new block
|
155
|
+
#
|
156
|
+
|
157
|
+
# lm = line.match(/^`{3,}([^`\s]+)( .+)?$/)
|
158
|
+
lm = line.match(/^`{3,}([^`\s]*) *(.*)$/)
|
159
|
+
|
160
|
+
do1 = false
|
161
|
+
if opts[:bash_only]
|
162
|
+
do1 = true if lm && (lm[1] == 'bash')
|
163
|
+
elsif opts[:exclude_expect_blocks]
|
164
|
+
do1 = true unless lm && (lm[1] == 'expect')
|
165
|
+
else
|
166
|
+
do1 = true
|
167
|
+
end
|
168
|
+
if $pdebug
|
169
|
+
puts ["get_blocks() lm: #{lm.inspect}",
|
170
|
+
"get_blocks() opts: #{opts.inspect}",
|
171
|
+
"get_blocks() do1: #{do1}"]
|
172
|
+
end
|
173
|
+
|
174
|
+
if do1 && (!opts[:title_match] || (lm && lm[2] && lm[2].match(opts[:title_match])))
|
175
|
+
current = []
|
176
|
+
in_block = true
|
177
|
+
block_title = (lm && lm[2])
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
elsif current
|
182
|
+
current += [line.chomp]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
blocks.tap { |ret| puts "get_blocks() ret: #{ret.inspect}" if $pdebug }
|
186
|
+
end
|
187
|
+
|
188
|
+
def make_block_label(block, call_options = {})
|
189
|
+
opts = options.merge(call_options)
|
190
|
+
puts "make_block_label() opts: #{opts.inspect}" if $pdebug
|
191
|
+
puts "make_block_label() block: #{block.inspect}" if $pdebug
|
192
|
+
if opts[:mdheadings]
|
193
|
+
heads = block.fetch(:headings, []).compact.join(' # ')
|
194
|
+
"#{block[:title]} [#{heads}] (#{opts[:mdfilename]})"
|
195
|
+
else
|
196
|
+
"#{block[:title]} (#{opts[:mdfilename]})"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def make_block_labels(call_options = {})
|
201
|
+
opts = options.merge(call_options)
|
202
|
+
get_blocks(opts).map do |block|
|
203
|
+
make_block_label block, opts
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def select_block(call_options = {}, &options_block)
|
208
|
+
opts = copts call_options, options_block
|
209
|
+
|
210
|
+
blocks = get_blocks(opts.merge(struct: true))
|
211
|
+
puts "select_block() blocks: #{blocks.to_yaml}" if $pdebug
|
212
|
+
|
213
|
+
prompt = TTY::Prompt.new(interrupt: :exit)
|
214
|
+
pt = "#{opts.fetch(:prompt, nil) || 'Pick one'}:"
|
215
|
+
puts "select_block() pt: #{pt.inspect}" if $pdebug
|
216
|
+
|
217
|
+
blocks.each { |block| block.merge! label: make_block_label(block, opts) }
|
218
|
+
block_labels = blocks.map { |block| block[:label] }
|
219
|
+
puts "select_block() block_labels: #{block_labels.inspect}" if $pdebug
|
220
|
+
|
221
|
+
if opts[:preview_options]
|
222
|
+
select_per_page = 3
|
223
|
+
block_labels.each do |bn|
|
224
|
+
fout " - #{bn}"
|
225
|
+
end
|
226
|
+
else
|
227
|
+
select_per_page = SELECT_PAGE_HEIGHT
|
228
|
+
end
|
229
|
+
|
230
|
+
return nil if block_labels.count.zero?
|
231
|
+
|
232
|
+
sel = prompt.select(pt, block_labels, per_page: select_per_page)
|
233
|
+
puts "select_block() sel: #{sel.inspect}" if $pdebug
|
234
|
+
# catch
|
235
|
+
# # catch TTY::Reader::InputInterrupt
|
236
|
+
# puts "InputInterrupt"
|
237
|
+
# end
|
238
|
+
|
239
|
+
label_block = blocks.select { |block| block[:label] == sel }.fetch(0, nil)
|
240
|
+
puts "select_block() label_block: #{label_block.inspect}" if $pdebug
|
241
|
+
sel = label_block[:name]
|
242
|
+
puts "select_block() sel: #{sel.inspect}" if $pdebug
|
243
|
+
|
244
|
+
cbs = code_blocks(blocks, sel)
|
245
|
+
puts "select_block() cbs: #{cbs.inspect}" if $pdebug
|
246
|
+
|
247
|
+
## display code blocks for approval
|
248
|
+
#
|
249
|
+
cbs.each { |cb| fout cb } if opts[:display] || opts[:approve]
|
250
|
+
|
251
|
+
allow = true
|
252
|
+
allow = prompt.yes? 'Process?' if opts[:approve]
|
253
|
+
|
254
|
+
selected = block_by_name blocks, sel
|
255
|
+
puts "select_block() selected: #{selected.inspect}" if $pdebug
|
256
|
+
if allow && opts[:execute]
|
257
|
+
|
258
|
+
## process in script, to handle line continuations
|
259
|
+
#
|
260
|
+
cmd2 = cbs.flatten.join("\n")
|
261
|
+
fout "$ #{cmd2.to_yaml}"
|
262
|
+
|
263
|
+
# Open3.popen3(cmd2) do |stdin, stdout, stderr, wait_thr|
|
264
|
+
# cnt += 1
|
265
|
+
# # stdin.puts "This is sent to the command"
|
266
|
+
# # stdin.close # we're done
|
267
|
+
# stdout_str = stdout.read # read stdout to string. note that this will block until the command is done!
|
268
|
+
# stderr_str = stderr.read # read stderr to string
|
269
|
+
# status = wait_thr.value # will block until the command finishes; returns status that responds to .success?
|
270
|
+
# fout "#{stdout_str}"
|
271
|
+
# fout "#{cnt}: err: #{stderr_str}" if stderr_str != ''
|
272
|
+
# # fout "#{cnt}: stt: #{status}"
|
273
|
+
# end
|
274
|
+
|
275
|
+
Open3.popen3(cmd2) do |stdin, stdout, stderr|
|
276
|
+
stdin.close_write
|
277
|
+
begin
|
278
|
+
files = [stdout, stderr]
|
279
|
+
|
280
|
+
until all_eof(files)
|
281
|
+
ready = IO.select(files)
|
282
|
+
|
283
|
+
next unless ready
|
284
|
+
|
285
|
+
readable = ready[0]
|
286
|
+
# writable = ready[1]
|
287
|
+
# exceptions = ready[2]
|
288
|
+
|
289
|
+
readable.each do |f|
|
290
|
+
# fileno = f.fileno
|
291
|
+
|
292
|
+
data = f.read_nonblock(BLOCK_SIZE)
|
293
|
+
# fout "- fileno: #{fileno}\n#{data}"
|
294
|
+
fout data
|
295
|
+
rescue EOFError #=> e
|
296
|
+
# fout "fileno: #{fileno} EOF"
|
297
|
+
end
|
298
|
+
end
|
299
|
+
rescue IOError => e
|
300
|
+
fout "IOError: #{e}"
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
selected[:name]
|
306
|
+
end
|
307
|
+
|
308
|
+
def select_md_file
|
309
|
+
opts = options
|
310
|
+
files = find_files
|
311
|
+
if files.count == 1
|
312
|
+
sel = files[0]
|
313
|
+
elsif files.count >= 2
|
314
|
+
|
315
|
+
if opts[:preview_options]
|
316
|
+
select_per_page = 3
|
317
|
+
files.each do |file|
|
318
|
+
fout " - #{file}"
|
319
|
+
end
|
320
|
+
else
|
321
|
+
select_per_page = SELECT_PAGE_HEIGHT
|
322
|
+
end
|
323
|
+
|
324
|
+
prompt = TTY::Prompt.new
|
325
|
+
sel = prompt.select("#{opts.fetch(:prompt, 'Pick one')}:", files, per_page: select_per_page)
|
326
|
+
end
|
327
|
+
|
328
|
+
sel
|
329
|
+
end
|
330
|
+
|
331
|
+
# Returns true if all files are EOF
|
332
|
+
#
|
333
|
+
def all_eof(files)
|
334
|
+
files.find { |f| !f.eof }.nil?
|
335
|
+
end
|
336
|
+
|
337
|
+
def code(table, block)
|
338
|
+
puts "code() table: #{table.inspect}" if $pdebug
|
339
|
+
puts "code() block: #{block.inspect}" if $pdebug
|
340
|
+
all = [block[:name]] + unroll(table, block[:reqs])
|
341
|
+
puts "code() all: #{all.inspect}" if $pdebug
|
342
|
+
all.reverse.map do |req|
|
343
|
+
puts "code() req: #{req.inspect}" if $pdebug
|
344
|
+
block_by_name(table, req).fetch(:body, '')
|
345
|
+
end
|
346
|
+
.flatten(1)
|
347
|
+
.tap { |ret| puts "code() ret: #{ret.inspect}" if $pdebug }
|
348
|
+
end
|
349
|
+
|
350
|
+
def block_by_name(table, name, default = {})
|
351
|
+
table.select { |block| block[:name] == name }.fetch(0, default)
|
352
|
+
end
|
353
|
+
|
354
|
+
def code_blocks(table, name)
|
355
|
+
puts "code_blocks() table: #{table.inspect}" if $pdebug
|
356
|
+
puts "code_blocks() name: #{name.inspect}" if $pdebug
|
357
|
+
name_block = block_by_name(table, name)
|
358
|
+
puts "code_blocks() name_block: #{name_block.inspect}" if $pdebug
|
359
|
+
all = [name_block[:name]] + unroll(table, name_block[:reqs])
|
360
|
+
puts "code_blocks() all: #{all.inspect}" if $pdebug
|
361
|
+
|
362
|
+
# in order of appearance in document
|
363
|
+
table.select { |block| all.include? block[:name] }
|
364
|
+
.map { |block| block.fetch(:body, '') }
|
365
|
+
.flatten(1)
|
366
|
+
.tap { |ret| puts "code_blocks() ret: #{ret.inspect}" if $pdebug }
|
367
|
+
end
|
368
|
+
|
369
|
+
def unroll(table, reqs)
|
370
|
+
puts "unroll() table: #{table.inspect}" if $pdebug
|
371
|
+
puts "unroll() reqs: #{reqs.inspect}" if $pdebug
|
372
|
+
all = []
|
373
|
+
rem = reqs
|
374
|
+
while rem.count.positive?
|
375
|
+
puts "unrol() rem: #{rem.inspect}" if $pdebug
|
376
|
+
rem = rem.map do |req|
|
377
|
+
puts "unrol() req: #{req.inspect}" if $pdebug
|
378
|
+
next if all.include? req
|
379
|
+
|
380
|
+
all += [req]
|
381
|
+
puts "unrol() all: #{all.inspect}" if $pdebug
|
382
|
+
block_by_name(table, req).fetch(:reqs, [])
|
383
|
+
end
|
384
|
+
.compact
|
385
|
+
.flatten(1)
|
386
|
+
.tap { |_ret| puts "unroll() rem: #{rem.inspect}" if $pdebug }
|
387
|
+
end
|
388
|
+
all.tap { |ret| puts "unroll() ret: #{ret.inspect}" if $pdebug }
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
# $stderr.sync = true
|
393
|
+
# $stdout.sync = true
|
394
|
+
|
395
|
+
def fout(str)
|
396
|
+
puts str # to stdout
|
397
|
+
end
|
398
|
+
|
399
|
+
## configuration file
|
400
|
+
#
|
401
|
+
def read_configuration!(options, configuration_path)
|
402
|
+
if Pathname.new(configuration_path).exist?
|
403
|
+
# rubocop:disable Security/YAMLLoad
|
404
|
+
options.merge!((YAML.load(File.open(configuration_path)) || {})
|
405
|
+
.transform_keys(&:to_sym))
|
406
|
+
# rubocop:enable Security/YAMLLoad
|
407
|
+
end
|
408
|
+
options
|
409
|
+
end
|
410
|
+
|
411
|
+
def run
|
412
|
+
## default configuration
|
413
|
+
#
|
414
|
+
options = {
|
415
|
+
mdheadings: true,
|
416
|
+
list_blocks: false,
|
417
|
+
list_docs: false,
|
418
|
+
mdfilename: 'README.md',
|
419
|
+
mdfolder: '.'
|
420
|
+
}
|
421
|
+
|
422
|
+
def options_finalize!(options); end
|
423
|
+
|
424
|
+
# read local configuration file
|
425
|
+
#
|
426
|
+
read_configuration! options, ".#{APP_NAME.downcase}.yml"
|
427
|
+
|
428
|
+
## read current details for aws resources from app_data_file
|
429
|
+
#
|
430
|
+
# load_resources! options
|
431
|
+
# puts "q31 options: #{options.to_yaml}" if $pdebug
|
432
|
+
|
433
|
+
# rubocop:disable Metrics/BlockLength
|
434
|
+
option_parser = OptionParser.new do |opts|
|
435
|
+
executable_name = File.basename($PROGRAM_NAME)
|
436
|
+
opts.banner = [
|
437
|
+
"#{APP_NAME} - #{APP_DESC} (#{VERSION})".freeze,
|
438
|
+
"Usage: #{executable_name} [options]"
|
439
|
+
].join("\n")
|
440
|
+
|
441
|
+
## menu top: on_head appear in reverse order added
|
442
|
+
#
|
443
|
+
opts.on('--config PATH', 'Read configuration file') do |value|
|
444
|
+
read_configuration! options, value
|
445
|
+
end
|
446
|
+
|
447
|
+
## menu body: items appear in order added
|
448
|
+
#
|
449
|
+
opts.on('-f RELATIVE', '--mdfilename', 'Name of document') do |value|
|
450
|
+
options[:mdfilename] = value
|
451
|
+
end
|
452
|
+
|
453
|
+
opts.on('-p PATH', '--mdfolder', 'Path to documents') do |value|
|
454
|
+
options[:mdfolder] = value
|
455
|
+
end
|
456
|
+
|
457
|
+
opts.on('--list-blocks', 'List blocks') do |_value|
|
458
|
+
options[:list_blocks] = true
|
459
|
+
end
|
460
|
+
|
461
|
+
opts.on('--list-docs', 'List docs in current folder') do |_value|
|
462
|
+
options[:list_docs] = true
|
463
|
+
end
|
464
|
+
|
465
|
+
## menu bottom: items appear in order added
|
466
|
+
#
|
467
|
+
opts.on_tail('-h', '--help', 'App help') do |_value|
|
468
|
+
puts option_parser.help
|
469
|
+
exit
|
470
|
+
end
|
471
|
+
|
472
|
+
opts.on_tail('-v', '--version', 'App version') do |_value|
|
473
|
+
puts VERSION
|
474
|
+
exit
|
475
|
+
end
|
476
|
+
|
477
|
+
opts.on_tail('-x', '--exit', 'Exit app') do |_value|
|
478
|
+
exit
|
479
|
+
end
|
480
|
+
|
481
|
+
opts.on_tail('-0', 'Show configuration') do |_v|
|
482
|
+
options_finalize! options
|
483
|
+
puts options.to_yaml
|
484
|
+
end
|
485
|
+
end
|
486
|
+
# rubocop:enable Metrics/BlockLength
|
487
|
+
|
488
|
+
option_parser.load # filename defaults to basename of the program without suffix in a directory ~/.options
|
489
|
+
option_parser.environment # env defaults to the basename of the program.
|
490
|
+
option_parser.parse! # (into: options)
|
491
|
+
options_finalize! options
|
492
|
+
|
493
|
+
## process
|
494
|
+
#
|
495
|
+
# rubocop:disable Metrics/BlockLength
|
496
|
+
loop do # once
|
497
|
+
mp = MarkParse.new options
|
498
|
+
options.merge!(
|
499
|
+
{
|
500
|
+
approve: true,
|
501
|
+
bash: true,
|
502
|
+
display: true,
|
503
|
+
exclude_expect_blocks: true,
|
504
|
+
execute: true,
|
505
|
+
prompt: 'Execute',
|
506
|
+
struct: true
|
507
|
+
}
|
508
|
+
)
|
509
|
+
|
510
|
+
## show
|
511
|
+
#
|
512
|
+
if options[:list_docs]
|
513
|
+
fout mp.find_files
|
514
|
+
break
|
515
|
+
end
|
516
|
+
|
517
|
+
if options[:list_blocks]
|
518
|
+
fout (mp.find_files.map do |file|
|
519
|
+
mp.make_block_labels(mdfilename: file, struct: true)
|
520
|
+
end).flatten(1).to_yaml
|
521
|
+
break
|
522
|
+
end
|
523
|
+
|
524
|
+
## process
|
525
|
+
#
|
526
|
+
mp.select_block(bash: true, struct: true) if options[:mdfilename]
|
527
|
+
|
528
|
+
# rubocop:disable Style/BlockComments
|
529
|
+
# rubocop:enable Style/BlockComments
|
530
|
+
|
531
|
+
break unless false # rubocop:disable Lint/LiteralAsCondition
|
12
532
|
end
|
13
533
|
end
|
534
|
+
# rubocop:enable Metrics/BlockLength
|
535
|
+
# rubocop:enable Style/GlobalVars
|
14
536
|
end
|
metadata
CHANGED
@@ -1,20 +1,91 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: markdown_exec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fareed Stevenson
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
12
|
-
dependencies:
|
11
|
+
date: 2022-03-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: open3
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.1.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.1.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: optparse
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.1.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.1.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pathname
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.1.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.1.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: tty-prompt
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.23.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.23.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: yaml
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.2.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.2.0
|
13
83
|
description: Execute shell blocks in markdown files. Name blocks and require named
|
14
84
|
blocks.
|
15
85
|
email:
|
16
86
|
- fareed@phomento.com
|
17
|
-
executables:
|
87
|
+
executables:
|
88
|
+
- mde
|
18
89
|
extensions: []
|
19
90
|
extra_rdoc_files: []
|
20
91
|
files:
|
@@ -22,6 +93,7 @@ files:
|
|
22
93
|
- CHANGELOG.md
|
23
94
|
- CODE_OF_CONDUCT.md
|
24
95
|
- Gemfile
|
96
|
+
- Gemfile.lock
|
25
97
|
- LICENSE.txt
|
26
98
|
- README.md
|
27
99
|
- Rakefile
|
@@ -30,11 +102,11 @@ files:
|
|
30
102
|
- bin/setup
|
31
103
|
- lib/markdown_exec.rb
|
32
104
|
- lib/markdown_exec/version.rb
|
33
|
-
homepage: https://rubygems.org/gems/
|
105
|
+
homepage: https://rubygems.org/gems/markdown_exec
|
34
106
|
licenses:
|
35
107
|
- MIT
|
36
108
|
metadata:
|
37
|
-
homepage_uri: https://rubygems.org/gems/
|
109
|
+
homepage_uri: https://rubygems.org/gems/markdown_exec
|
38
110
|
post_install_message:
|
39
111
|
rdoc_options: []
|
40
112
|
require_paths:
|