markdown_exec 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2336bb2091df5bf018181e832fb9c5e7a5b7ee542b6f71cde38a3afb3aa88ab4
4
- data.tar.gz: '072654942ddf5897784c71724a7f9fa6fb5bea9b395d54ca18a3f3fd9b02c5af'
3
+ metadata.gz: 95d097cfcf93e09ba3bf487989923d4be03dd9fde0521854ea0660850b157d01
4
+ data.tar.gz: 22bc0e8bc8cd35d0d1dd52b6275c1609fb4cb95d69bd125064f0ca16f2f931f7
5
5
  SHA512:
6
- metadata.gz: 2f28a18aea943a7fec92b16d232b8c3722a30f6960fbd32bf84e95762b4ac322f0fb53f75563118a15136da72e49f16c580a8c7fbf05d2001857e068403834ef
7
- data.tar.gz: 8bb762715d790e37906d61cf8834e86dfe7e4f9611b8f7477da508d2ff658d496c6e1e0c89fc42e9e5f2cf937e29dae3cddf062cabda867f3a5bb1466986f2c3
6
+ metadata.gz: 591c09a221c0f5a34f8523d99d8e416bb022219fee38a7a05e3541600b03132e5ae3cc6aee02048eda2170a408d9e86090dedadff7c16f1f79ea98469826c27a
7
+ data.tar.gz: 74a76b6a6b10b165f8e79e59d25db78bcba173d730fd1a696e73c2012794bcfedd7c1b648cabe6b15d4d0a70995e5303b02c58d0b16c35a03ffbfea0b4efc5f4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ## [ToDo]
4
+
5
+ - pipe stdin to script
6
+ - yes/no/write/clipboard/record/edit/history
7
+ - present timestamp, result of last exec for each command
8
+ - user settings
9
+ - hidden w , w/o () in names
10
+ - fix regexp in pathnames
11
+ - tab completion from md file
12
+ - read file once to allow for tempdoc stream
13
+ - include md or blocks file
14
+
15
+ - tree display
16
+
17
+ - mde options in md file or included file
18
+
19
+ - include blocks from local md file
20
+
21
+ - chmod a+x logged script
22
+
23
+ - add shebang to saved script
24
+
25
+ ## [0.2.5] - 2022-04
26
+
27
+ ### Added
28
+
29
+ - Command `--list-default-env` to show default configuration as environment variables.
30
+ - Command `--list-default-yaml` to show default configuration as YAML.
31
+ - Option to exit program when selecting files or blocks.
32
+
33
+ ### Changed
34
+
35
+ - Composition of menu to facilitate reports.
36
+ - List default values in menu help.
3
37
 
4
38
  ## [0.2.4] - 2022-04-01
5
39
 
@@ -11,7 +45,7 @@
11
45
 
12
46
  | YAML Name | Environment Variable | Option Name | Default | Purpose |
13
47
  | :--- | :--- | :--- | :--- | :--- |
14
- | list_count | MDE_LIST_COUNT | `--list_count` | `16` | Max. items to return in list |
48
+ | list_count | MDE_LIST_COUNT | `--list-count` | `16` | Max. items to return in list |
15
49
  | logged_stdout_filename_prefix | MDE_LOGGED_STDOUT_FILENAME_PREFIX | | `mde` | Name prefix for stdout files |
16
50
  | save_execution_output | MDE_SAVE_EXECUTION_OUTPUT | `--save-execution-output` | False | Save standard output of the executed script |
17
51
  | saved_script_filename_prefix | MDE_SAVED_SCRIPT_FILENAME_PREFIX | | `mde` | Name prefix for saved scripts |
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- markdown_exec (0.2.4)
4
+ markdown_exec (0.2.5)
5
5
  open3 (~> 0.1.1)
6
6
  optparse (~> 0.1.1)
7
7
  tty-prompt (~> 0.23.1)
data/README.md CHANGED
@@ -1,30 +1,35 @@
1
1
  # MarkdownExec
2
2
 
3
- Interactively select and execute fenced code blocks in markdown files. Build complex scripts by naming and requiring blocks.
3
+ Interactively select and execute fenced code blocks in markdown files. Build complex scripts by naming and requiring blocks. Log resulting scripts and output. Re-run scripts.
4
4
 
5
- * Code blocks may be named.
5
+ * Code blocks may be named. Named blocks can be required by other blocks.
6
6
 
7
- * Named blocks can be required by other blocks.
7
+ * The user-selected code block, and all required blocks, are arranged into a script in the order they appear in the markdown file. The script can be presented for approval prior to execution.
8
8
 
9
- * The user-selected code block, and all required blocks, are arranged in the order they appear in the markdown file.
9
+ * Executed scripts can be saved. Saved scripts can be listed, selected, and executed.
10
10
 
11
- * The code is presented for approval prior to execution.
11
+ * Output from executed scripts can be saved.
12
12
 
13
13
  ## Screenshots
14
14
 
15
15
  ### Select a file
16
+
16
17
  ![Select a file](/assets/select_a_file.png)
17
18
 
18
19
  ### Select a block
20
+
19
21
  ![Select a block](/assets/select_a_block.png)
20
22
 
21
23
  ### Approve code
24
+
22
25
  ![Approve code](/assets/approve_code.png)
23
26
 
24
27
  ### Output
28
+
25
29
  ![Output of execution](/assets/output_of_execution.png)
26
30
 
27
31
  ### Example blocks
32
+
28
33
  ![Example blocks](/assets/example_blocks.png)
29
34
 
30
35
  ## Installation
@@ -34,39 +39,111 @@ Install:
34
39
 
35
40
  ## Usage
36
41
 
37
- ### `mde --help`
42
+ ### Help
43
+
44
+ #### `mde --help`
45
+
38
46
  Displays help information.
39
47
 
40
- ### `mde`
48
+ ### Basic
49
+
50
+ #### `mde`
51
+
41
52
  Process `README.md` file in the current folder. Displays all the blocks in the file and allows you to select using [up], [down], and [return]. Press [ctrl]-c to abort selection.
42
53
 
43
- ### `mde my.md` or `mde -f my.md`
54
+ #### `mde my.md` or `mde -f my.md`
55
+
44
56
  Select a block to execute from `my.md`.
45
57
 
46
- ### `mde .` or `mde -p .`
58
+ #### `mde .` or `mde -p .`
47
59
 
48
60
  Select a markdown file in the current folder. Select a block to execute from that file.
49
61
 
50
- ### `mde --list-blocks`
62
+ ### Report documents and blocks
63
+
64
+ #### `mde --list-blocks`
65
+
51
66
  List all blocks in the all the markdown documents in the current folder.
52
67
 
53
- ### `mde --list-docs`
68
+ #### `mde --list-docs`
69
+
54
70
  List all markdown documents in the current folder.
55
71
 
72
+ ### Configuration
73
+
74
+ #### `mde --list-default-env` or `mde --list-default-yaml`
75
+
76
+ List default values that can be set in configuration file, environment, and command line.
77
+
78
+ #### `mde -0`
79
+
80
+ Show current configuation values that will be applied to the current run. Does not interrupt processing.
81
+
82
+ ### Save scripts
83
+
84
+ #### `mde --save-executed-script 1`
85
+
86
+ Save executed script in saved script folder.
87
+
88
+ #### `mde --list-recent-scripts`
89
+
90
+ List recent saved scripts in saved script folder.
91
+
92
+ #### `mde --select-recent-script`
93
+
94
+ Select and execute a recently saved script in saved script folder.
95
+
96
+ ### Save output
97
+
98
+ #### `mde --save-execution-output 1`
99
+
100
+ Save execution output in saved output folder.
101
+
56
102
  ## Behavior
103
+
57
104
  * If no file and no folder are specified, blocks within `./README.md` are presented.
58
105
  * If a file is specified, its blocks are presented.
59
106
  * If a folder is specified, its files are presented. When a file is selected, its blocks are presented.
60
107
 
61
108
  ## Configuration
62
- While starting up, reads the YAML configuration file `.mde.yml` in the current folder if it exists.
63
109
 
64
- e.g. Use to set the default file for the current folder.
110
+ ### Environment Variables
111
+
112
+ When executed, `mde` reads the current environment.
113
+ * Configuration in current and children shells, e.g. `export MDE_SAVE_EXECUTED_SCRIPT=1`.
114
+ * Configuration for the current command, e.g. `MDE_SAVE_EXECUTED_SCRIPT=1 mde`.
115
+
116
+ ### Configuration Files
117
+
118
+ * Configuration in all shells, e.g. environment variables set in your user's `~/.bashrc` or `~/.bash_profile` files.
119
+ * Configuration in the optional file `.mde.yml` in the current folder. .e.g. `save_executed_script: true`
120
+ * Configuration in a YAML file and read while parsing the inputs, e.g. `--config my_path/my_file.yml`
65
121
 
66
- * `filename: CHANGELOG.md` sets the file to open.
67
- * `folder: documents` sets the folder to search for default or specified files.
122
+ ### Program Arguments
123
+
124
+ * Configuration in command options, e.g. `mde --save-executed-script 1`
125
+
126
+ ## Representing boolean values
127
+
128
+ Boolean values expressed as strings are interpreted as:
129
+ | String | Boolean |
130
+ | :---: | :---: |
131
+ | *empty string* | False |
132
+ | `0` | False |
133
+ | `1` | True |
134
+ | *anything else* | True |
135
+
136
+ E.g. `opt1=1` will set option `opt1` to True.
137
+
138
+ Boolean options configured with environment variables:
139
+ - Set to `1` or non-empty value to save executed scripts; empty or `0` to disable saving.
140
+ e.g. `export MDE_SAVE_EXECUTED_SCRIPT=1`
141
+ e.g. `export MDE_SAVE_EXECUTED_SCRIPT=`
142
+ - Specify variable on command line.
143
+ e.g. `MDE_SAVE_EXECUTED_SCRIPT=1 mde`
68
144
 
69
145
  # Example blocks
146
+
70
147
  When prompted, select either the `awake` or `asleep` block.
71
148
 
72
149
  ``` :(day)
@@ -89,6 +166,17 @@ export ACTIVITY=asleep
89
166
  echo "$TIME -> $ACTIVITY"
90
167
  ```
91
168
 
169
+ ``` :missing_command
170
+ fail
171
+ ```
172
+
173
+ ``` :exit_value
174
+ echo "a"
175
+ echo "b"
176
+ echo "c" >>/dev/stderr
177
+ grep nx Gemfile
178
+ ```
179
+
92
180
  # License
93
181
 
94
182
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -2,7 +2,7 @@
2
2
 
3
3
  module MarkdownExec
4
4
  APP_NAME = 'MDE'
5
- APP_DESC = 'Markdown block executor'
5
+ APP_DESC = 'Markdown Executor'
6
6
  GEM_NAME = 'markdown_exec'
7
- VERSION = '0.2.4'
7
+ VERSION = '0.2.5'
8
8
  end
data/lib/markdown_exec.rb CHANGED
@@ -14,21 +14,23 @@ require 'yaml'
14
14
  # else true
15
15
 
16
16
  def env_bool(name, default: false)
17
- return default if (val = ENV[name]).nil?
17
+ return default if name.nil? || (val = ENV[name]).nil?
18
18
  return false if val.empty? || val == '0'
19
19
 
20
20
  true
21
21
  end
22
22
 
23
23
  def env_int(name, default: 0)
24
- return default if (val = ENV[name]).nil?
24
+ return default if name.nil? || (val = ENV[name]).nil?
25
25
  return default if val.empty?
26
26
 
27
27
  val.to_i
28
28
  end
29
29
 
30
30
  def env_str(name, default: '')
31
- ENV[name] || default
31
+ return default if name.nil? || (val = ENV[name]).nil?
32
+
33
+ val || default
32
34
  end
33
35
 
34
36
  $pdebug = env_bool 'MDE_DEBUG'
@@ -42,7 +44,12 @@ BLOCK_SIZE = 1024
42
44
 
43
45
  class Object # rubocop:disable Style/Documentation
44
46
  def present?
45
- self && !blank?
47
+ case self.class.to_s
48
+ when 'FalseClass', 'TrueClass'
49
+ true
50
+ else
51
+ self && (!respond_to?(:blank?) || !blank?)
52
+ end
46
53
  end
47
54
  end
48
55
 
@@ -94,44 +101,17 @@ module MarkdownExec
94
101
  # options necessary to start, parse input, defaults for cli options
95
102
 
96
103
  def base_options
97
- {
98
- # commands
99
- list_blocks: false, # command
100
- list_docs: false, # command
101
- list_recent_scripts: false, # command
102
- run_last_script: false, # command
103
- select_recent_script: false, # command
104
-
105
- # command options
106
- filename: env_str('MDE_FILENAME', default: nil), # option Filename to open
107
- list_count: 16,
108
- logged_stdout_filename_prefix: 'mde',
109
- output_execution_summary: env_bool('MDE_OUTPUT_EXECUTION_SUMMARY', default: false), # option
110
- output_script: env_bool('MDE_OUTPUT_SCRIPT', default: false), # option
111
- output_stdout: env_bool('MDE_OUTPUT_STDOUT', default: true), # option
112
- path: env_str('MDE_PATH', default: nil), # option Folder to search for files
113
- save_executed_script: env_bool('MDE_SAVE_EXECUTED_SCRIPT', default: false), # option
114
- save_execution_output: env_bool('MDE_SAVE_EXECUTION_OUTPUT', default: false), # option
115
- saved_script_filename_prefix: 'mde',
116
- saved_script_folder: env_str('MDE_SAVED_SCRIPT_FOLDER', default: 'logs'), # option
117
- saved_script_glob: 'mde_*.sh',
118
- saved_stdout_folder: env_str('MDE_SAVED_STDOUT_FOLDER', default: 'logs'), # option
119
- user_must_approve: env_bool('MDE_USER_MUST_APPROVE', default: true), # option Pause for user to approve script
120
-
121
- # configuration options
122
- block_name_excluded_match: env_str('MDE_BLOCK_NAME_EXCLUDED_MATCH', default: '^\(.+\)$'),
123
- block_name_match: env_str('MDE_BLOCK_NAME_MATCH', default: ':(?<title>\S+)( |$)'),
124
- block_required_scan: env_str('MDE_BLOCK_REQUIRED_SCAN', default: '\+\S+'),
125
- fenced_start_and_end_match: env_str('MDE_FENCED_START_AND_END_MATCH', default: '^`{3,}'),
126
- fenced_start_ex_match: env_str('MDE_FENCED_START_EX_MATCH', default: '^`{3,}(?<shell>[^`\s]*) *(?<name>.*)$'),
127
- heading1_match: env_str('MDE_HEADING1_MATCH', default: '^# *(?<name>[^#]*?) *$'),
128
- heading2_match: env_str('MDE_HEADING2_MATCH', default: '^## *(?<name>[^#]*?) *$'),
129
- heading3_match: env_str('MDE_HEADING3_MATCH', default: '^### *(?<name>.+?) *$'),
130
- md_filename_glob: env_str('MDE_MD_FILENAME_GLOB', default: '*.[Mm][Dd]'),
131
- md_filename_match: env_str('MDE_MD_FILENAME_MATCH', default: '.+\\.md'),
132
- mdheadings: true, # use headings (levels 1,2,3) in block lable
133
- select_page_height: env_int('MDE_SELECT_PAGE_HEIGHT', default: 12)
134
- }
104
+ menu_data
105
+ .map do |_long_name, _short_name, env_var, _arg_name, _description, opt_name, default, _proc1| # rubocop:disable Metrics/ParameterLists
106
+ next unless opt_name.present?
107
+
108
+ [opt_name, env_bool(env_var, default: value_for_hash(default))]
109
+ end.compact.to_h.merge(
110
+ {
111
+ mdheadings: true, # use headings (levels 1,2,3) in block lable
112
+ menu_with_exit: true
113
+ }
114
+ ).tap_inspect format: :yaml
135
115
  end
136
116
 
137
117
  def default_options
@@ -248,11 +228,21 @@ module MarkdownExec
248
228
  return
249
229
  end
250
230
 
231
+ if @options[:list_default_yaml]
232
+ fout_list list_default_yaml
233
+ return
234
+ end
235
+
251
236
  if @options[:list_docs]
252
237
  fout_list files
253
238
  return
254
239
  end
255
240
 
241
+ if @options[:list_default_env]
242
+ fout_list list_default_env
243
+ return
244
+ end
245
+
256
246
  if @options[:list_recent_scripts]
257
247
  fout_list list_recent_scripts
258
248
  return
@@ -376,14 +366,37 @@ module MarkdownExec
376
366
  blocks.tap_inspect
377
367
  end
378
368
 
369
+ def list_default_env
370
+ menu_data
371
+ .map do |_long_name, _short_name, env_var, _arg_name, description, _opt_name, default, _proc1| # rubocop:disable Metrics/ParameterLists
372
+ next unless env_var.present?
373
+
374
+ [
375
+ "#{env_var}=#{value_for_cli default}",
376
+ description.present? ? description : nil
377
+ ].compact.join(' # ')
378
+ end.compact.sort
379
+ end
380
+
381
+ def list_default_yaml
382
+ menu_data
383
+ .map do |_long_name, _short_name, _env_var, _arg_name, description, opt_name, default, _proc1| # rubocop:disable Metrics/ParameterLists
384
+ next unless opt_name.present? && default.present?
385
+
386
+ [
387
+ "#{opt_name}: #{value_for_yaml default}",
388
+ description.present? ? description : nil
389
+ ].compact.join(' # ')
390
+ end.compact.sort
391
+ end
392
+
379
393
  def list_files_per_options(options)
380
- default_filename = 'README.md'
381
- default_folder = '.'
382
- if options[:filename]&.present?
383
- list_files_specified(options[:filename], options[:path], default_filename, default_folder)
384
- else
385
- list_files_specified(nil, options[:path], default_filename, default_folder)
386
- end.tap_inspect
394
+ list_files_specified(
395
+ options[:filename]&.present? ? options[:filename] : nil,
396
+ options[:path],
397
+ 'README.md',
398
+ '.'
399
+ ).tap_inspect
387
400
  end
388
401
 
389
402
  def list_files_specified(specified_filename, specified_folder, default_filename, default_folder, filetree = nil)
@@ -462,6 +475,92 @@ module MarkdownExec
462
475
  end.compact.tap_inspect
463
476
  end
464
477
 
478
+ def menu_data
479
+ proc_self = ->(value) { value }
480
+ proc_to_i = ->(value) { value.to_i != 0 }
481
+ proc_true = ->(_) { true }
482
+
483
+ summary_head = [
484
+ ['config', nil, nil, 'PATH', 'Read configuration file',
485
+ nil, '.', ->(value) { read_configuration_file! options, value }],
486
+ ['debug', 'd', 'MDE_DEBUG', 'BOOL', 'Debug output',
487
+ nil, false, ->(value) { $pdebug = value.to_i != 0 }]
488
+ ]
489
+
490
+ summary_body = [
491
+ ['filename', 'f', 'MDE_FILENAME', 'RELATIVE', 'Name of document',
492
+ :filename, nil, proc_self],
493
+ ['list-blocks', nil, nil, nil, 'List blocks',
494
+ :list_blocks, nil, proc_true],
495
+ ['list-default-env', nil, nil, nil, 'List default configuration as environment variables',
496
+ :list_default_env, nil, proc_true],
497
+ ['list-default-yaml', nil, nil, nil, 'List default configuration as YAML',
498
+ :list_default_yaml, nil, proc_true],
499
+ ['list-docs', nil, nil, nil, 'List docs in current folder',
500
+ :list_docs, nil, proc_true],
501
+ ['list-recent-scripts', nil, nil, nil, 'List recent saved scripts',
502
+ :list_recent_scripts, nil, proc_true],
503
+ ['output-execution-summary', nil, 'MDE_OUTPUT_EXECUTION_SUMMARY', 'BOOL', 'Display summary for execution',
504
+ :output_execution_summary, false, proc_to_i],
505
+ ['output-script', nil, 'MDE_OUTPUT_SCRIPT', 'BOOL', 'Display script prior to execution',
506
+ :output_script, false, proc_to_i],
507
+ ['output-stdout', nil, 'MDE_OUTPUT_STDOUT', 'BOOL', 'Display standard output from execution',
508
+ :output_stdout, true, proc_to_i],
509
+ ['path', 'p', 'MDE_PATH', 'PATH', 'Path to documents',
510
+ :path, nil, proc_self],
511
+ ['run-last-script', nil, nil, nil, 'Run most recently saved script',
512
+ :run_last_script, nil, proc_true],
513
+ ['select-recent-script', nil, nil, nil, 'Select and execute a recently saved script',
514
+ :select_recent_script, nil, proc_true],
515
+ ['save-execution-output', nil, 'MDE_SAVE_EXECUTION_OUTPUT', 'BOOL', 'Save execution output',
516
+ :save_execution_output, false, proc_to_i],
517
+ ['save-executed-script', nil, 'MDE_SAVE_EXECUTED_SCRIPT', 'BOOL', 'Save executed script',
518
+ :save_executed_script, false, proc_to_i],
519
+ ['saved-script-folder', nil, 'MDE_SAVED_SCRIPT_FOLDER', 'SPEC', 'Saved script folder',
520
+ :saved_script_folder, 'logs', proc_self],
521
+ ['saved-stdout-folder', nil, 'MDE_SAVED_STDOUT_FOLDER', 'SPEC', 'Saved stdout folder',
522
+ :saved_stdout_folder, 'logs', proc_self],
523
+ ['user-must-approve', nil, 'MDE_USER_MUST_APPROVE', 'BOOL', 'Pause for user to approve script',
524
+ :user_must_approve, true, proc_to_i]
525
+ ]
526
+
527
+ # rubocop:disable Style/Semicolon
528
+ summary_tail = [
529
+ [nil, '0', nil, nil, 'Show current configuration values',
530
+ nil, nil, ->(_) { options_finalize options; fout sorted_keys(options).to_yaml }],
531
+ ['help', 'h', nil, nil, 'App help',
532
+ nil, nil, ->(_) { fout menu_help; exit }],
533
+ ['version', 'v', nil, nil, 'App version',
534
+ nil, nil, ->(_) { fout MarkdownExec::VERSION; exit }],
535
+ ['exit', 'x', nil, nil, 'Exit app',
536
+ nil, nil, ->(_) { exit }]
537
+ ]
538
+ # rubocop:enable Style/Semicolon
539
+
540
+ env_vars = [
541
+ [nil, nil, 'MDE_BLOCK_NAME_EXCLUDED_MATCH', nil, 'Pattern for blocks to hide from user-selection',
542
+ :block_name_excluded_match, '^\(.+\)$', nil],
543
+ [nil, nil, 'MDE_BLOCK_NAME_MATCH', nil, '', :block_name_match, ':(?<title>\S+)( |$)', nil],
544
+ [nil, nil, 'MDE_BLOCK_REQUIRED_SCAN', nil, '', :block_required_scan, '\+\S+', nil],
545
+ [nil, nil, 'MDE_FENCED_START_AND_END_MATCH', nil, '', :fenced_start_and_end_match, '^`{3,}', nil],
546
+ [nil, nil, 'MDE_FENCED_START_EX_MATCH', nil, '', :fenced_start_ex_match,
547
+ '^`{3,}(?<shell>[^`\s]*) *(?<name>.*)$', nil],
548
+ [nil, nil, 'MDE_HEADING1_MATCH', nil, '', :heading1_match, '^# *(?<name>[^#]*?) *$', nil],
549
+ [nil, nil, 'MDE_HEADING2_MATCH', nil, '', :heading2_match, '^## *(?<name>[^#]*?) *$', nil],
550
+ [nil, nil, 'MDE_HEADING3_MATCH', nil, '', :heading3_match, '^### *(?<name>.+?) *$', nil],
551
+ [nil, nil, 'MDE_MD_FILENAME_GLOB', nil, '', :md_filename_glob, '*.[Mm][Dd]', nil],
552
+ [nil, nil, 'MDE_MD_FILENAME_MATCH', nil, '', :md_filename_match, '.+\\.md', nil],
553
+ [nil, nil, 'MDE_SELECT_PAGE_HEIGHT', nil, '', :select_page_height, 12, nil]
554
+ # [nil, nil, 'MDE_', nil, '', nil, '', nil],
555
+ ]
556
+
557
+ summary_head + summary_body + summary_tail + env_vars
558
+ end
559
+
560
+ def menu_help
561
+ @option_parser.help
562
+ end
563
+
465
564
  def option_exclude_blocks(opts, blocks)
466
565
  block_name_excluded_match = Regexp.new opts[:block_name_excluded_match]
467
566
  if opts[:hide_blocks_by_name]
@@ -471,6 +570,26 @@ module MarkdownExec
471
570
  end
472
571
  end
473
572
 
573
+ ## post-parse options configuration
574
+ #
575
+ def options_finalize(rest)
576
+ ## position 0: file or folder (optional)
577
+ #
578
+ if (pos = rest.fetch(0, nil))&.present?
579
+ if Dir.exist?(pos)
580
+ @options[:path] = pos
581
+ elsif File.exist?(pos)
582
+ @options[:filename] = pos
583
+ else
584
+ raise "Invalid parameter: #{pos}"
585
+ end
586
+ end
587
+
588
+ ## position 1: block name (optional)
589
+ #
590
+ @options[:block_name] = rest.fetch(1, nil)
591
+ end
592
+
474
593
  def optsmerge(call_options = {}, options_block = nil)
475
594
  class_call_options = @options.merge(call_options || {})
476
595
  if options_block
@@ -495,6 +614,14 @@ module MarkdownExec
495
614
  }
496
615
  end
497
616
 
617
+ def prompt_with_quit(prompt_text, items, opts = {})
618
+ exit_option = '* Exit'
619
+ sel = @prompt.select prompt_text,
620
+ items + (@options[:menu_with_exit] ? [exit_option] : []),
621
+ opts
622
+ sel == exit_option ? nil : sel
623
+ end
624
+
498
625
  def read_configuration_file!(options, configuration_path)
499
626
  return unless File.exist?(configuration_path)
500
627
 
@@ -526,19 +653,11 @@ module MarkdownExec
526
653
  #
527
654
  @options = base_options
528
655
 
529
- ## post-parse options configuration
530
- #
531
- options_finalize = ->(_options) {}
532
-
533
- proc_self = ->(value) { value }
534
- proc_to_i = ->(value) { value.to_i != 0 }
535
- proc_true = ->(_) { true }
536
-
537
- # read local configuration file
656
+ ## read local configuration file
538
657
  #
539
658
  read_configuration_file! @options, ".#{MarkdownExec::APP_NAME.downcase}.yml"
540
659
 
541
- option_parser = OptionParser.new do |opts|
660
+ @option_parser = option_parser = OptionParser.new do |opts|
542
661
  executable_name = File.basename($PROGRAM_NAME)
543
662
  opts.banner = [
544
663
  "#{MarkdownExec::APP_NAME}" \
@@ -546,67 +665,16 @@ module MarkdownExec
546
665
  "Usage: #{executable_name} [path] [filename] [options]"
547
666
  ].join("\n")
548
667
 
549
- summary_head = [
550
- ['config', nil, nil, 'PATH', 'Read configuration file',
551
- nil, ->(value) { read_configuration_file! options, value }],
552
- ['debug', 'd', 'MDE_DEBUG', 'BOOL', 'Debug output',
553
- nil, ->(value) { $pdebug = value.to_i != 0 }]
554
- ]
555
-
556
- summary_body = [
557
- ['filename', 'f', 'MDE_FILENAME', 'RELATIVE', 'Name of document',
558
- :filename, proc_self],
559
- ['list-blocks', nil, nil, nil, 'List blocks',
560
- :list_blocks, proc_true],
561
- ['list-docs', nil, nil, nil, 'List docs in current folder',
562
- :list_docs, proc_true],
563
- ['list-recent-scripts', nil, nil, nil, 'List recent saved scripts',
564
- :list_recent_scripts, proc_true],
565
- ['output-execution-summary', nil, 'MDE_OUTPUT_EXECUTION_SUMMARY', 'BOOL', 'Display summary for execution',
566
- :output_execution_summary, proc_to_i],
567
- ['output-script', nil, 'MDE_OUTPUT_SCRIPT', 'BOOL', 'Display script',
568
- :output_script, proc_to_i],
569
- ['output-stdout', nil, 'MDE_OUTPUT_STDOUT', 'BOOL', 'Display standard output from execution',
570
- :output_stdout, proc_to_i],
571
- ['path', 'p', 'MDE_PATH', 'PATH', 'Path to documents',
572
- :path, proc_self],
573
- ['run-last-script', nil, nil, nil, 'Run most recently saved script',
574
- :run_last_script, proc_true],
575
- ['select-recent-script', nil, nil, nil, 'Select and execute a recently saved script',
576
- :select_recent_script, proc_true],
577
- ['save-execution-output', nil, 'MDE_SAVE_EXECUTION_OUTPUT', 'BOOL', 'Save execution output',
578
- :save_execution_output, proc_to_i],
579
- ['save-executed-script', nil, 'MDE_SAVE_EXECUTED_SCRIPT', 'BOOL', 'Save executed script',
580
- :save_executed_script, proc_to_i],
581
- ['saved-script-folder', nil, 'MDE_SAVED_SCRIPT_FOLDER', 'SPEC', 'Saved script folder',
582
- :saved_script_folder, proc_self],
583
- ['saved-stdout-folder', nil, 'MDE_SAVED_STDOUT_FOLDER', 'SPEC', 'Saved stdout folder',
584
- :saved_stdout_folder, proc_self],
585
- ['user-must-approve', nil, 'MDE_USER_MUST_APPROVE', 'BOOL', 'Pause to approve execution',
586
- :user_must_approve, proc_to_i]
587
- ]
588
-
589
- # rubocop:disable Style/Semicolon
590
- summary_tail = [
591
- [nil, '0', nil, nil, 'Show configuration',
592
- nil, ->(_) { options_finalize.call options; fout sorted_keys(options).to_yaml }],
593
- ['help', 'h', nil, nil, 'App help',
594
- nil, ->(_) { fout option_parser.help; exit }],
595
- ['version', 'v', nil, nil, 'App version',
596
- nil, ->(_) { fout MarkdownExec::VERSION; exit }],
597
- ['exit', 'x', nil, nil, 'Exit app',
598
- nil, ->(_) { exit }]
599
- ]
600
- # rubocop:enable Style/Semicolon
601
-
602
- (summary_head + summary_body + summary_tail)
603
- .map do |long_name, short_name, env_var, arg_name, description, opt_name, proc1| # rubocop:disable Metrics/ParameterLists
668
+ menu_data
669
+ .map do |long_name, short_name, _env_var, arg_name, description, opt_name, default, proc1| # rubocop:disable Metrics/ParameterLists
670
+ next unless long_name.present? || short_name.present?
671
+
604
672
  opts.on(*[if long_name.present?
605
673
  "--#{long_name}#{arg_name.present? ? " #{arg_name}" : ''}"
606
674
  end,
607
675
  short_name.present? ? "-#{short_name}" : nil,
608
676
  [description,
609
- env_var.present? ? "env: #{env_var}" : nil].compact.join(' - '),
677
+ default.present? ? "[#{value_for_cli default}]" : nil].compact.join(' '),
610
678
  lambda { |value|
611
679
  ret = proc1.call(value)
612
680
  options[opt_name] = ret if opt_name
@@ -618,27 +686,9 @@ module MarkdownExec
618
686
  option_parser.environment # env defaults to the basename of the program.
619
687
  rest = option_parser.parse! # (into: options)
620
688
 
621
- ## finalize configuration
622
- #
623
- options_finalize.call options
624
-
625
- ## position 0: file or folder (optional)
626
- #
627
- if (pos = rest.fetch(0, nil))&.present?
628
- if Dir.exist?(pos)
629
- options[:path] = pos
630
- elsif File.exist?(pos)
631
- options[:filename] = pos
632
- else
633
- raise "Invalid parameter: #{pos}"
634
- end
635
- end
689
+ options_finalize rest
636
690
 
637
- ## position 1: block name (optional)
638
- #
639
- block_name = rest.fetch(1, nil)
640
-
641
- exec_block options, block_name
691
+ exec_block options, options[:block_name]
642
692
  end
643
693
 
644
694
  def run_last_script
@@ -667,6 +717,12 @@ module MarkdownExec
667
717
  dirname = File.dirname(@options[:logged_stdout_filespec])
668
718
  Dir.mkdir dirname unless File.exist?(dirname)
669
719
  File.write(@options[:logged_stdout_filespec], @execute_files&.fetch(0, ''))
720
+ # @options[:logged_stderr_filename] =
721
+ # "#{[@options[:logged_stdout_filename_prefix], Time.now.utc.strftime('%F-%H-%M-%S'), fne,
722
+ # @options[:block_name]].join('_')}.err.txt"
723
+ # @options[:logged_stderr_filespec] = File.join @options[:saved_stdout_folder], @options[:logged_stderr_filename]
724
+ # @logged_stderr_filespec = @options[:logged_stderr_filespec]
725
+ # File.write(@options[:logged_stderr_filespec], @execute_files&.fetch(1, ''))
670
726
  end
671
727
 
672
728
  def select_and_approve_block(call_options = {}, &options_block)
@@ -680,7 +736,9 @@ module MarkdownExec
680
736
 
681
737
  return nil if block_labels.count.zero?
682
738
 
683
- sel = @prompt.select pt, block_labels, per_page: opts[:select_page_height]
739
+ sel = prompt_with_quit pt, block_labels, per_page: opts[:select_page_height]
740
+ return nil if sel.nil?
741
+
684
742
  label_block = blocks_in_file.select { |block| block[:label] == sel }.fetch(0, nil)
685
743
  opts[:block_name] = @options[:block_name] = label_block[:name]
686
744
  end
@@ -694,13 +752,15 @@ module MarkdownExec
694
752
  if files.count == 1
695
753
  files[0]
696
754
  elsif files.count >= 2
697
- @prompt.select opts[:prompt_select_md].to_s, files, per_page: opts[:select_page_height]
755
+ prompt_with_quit opts[:prompt_select_md].to_s, files, per_page: opts[:select_page_height]
698
756
  end
699
757
  end
700
758
 
701
759
  def select_recent_script
702
- filename = @prompt.select @options[:prompt_select_md].to_s, list_recent_scripts,
703
- per_page: @options[:select_page_height]
760
+ filename = prompt_with_quit @options[:prompt_select_md].to_s, list_recent_scripts,
761
+ per_page: @options[:select_page_height]
762
+ return if filename.nil?
763
+
704
764
  mf = filename.match(/#{@options[:saved_script_filename_prefix]}_(?<time>[0-9\-]+)_(?<file>.+)_(?<block>.+)\.sh/)
705
765
 
706
766
  @options[:block_name] = mf[:block]
@@ -728,7 +788,50 @@ module MarkdownExec
728
788
  else
729
789
  @options.merge! opts
730
790
  end
731
- @options
791
+ @options.tap_inspect format: :yaml
792
+ end
793
+
794
+ def value_for_cli(value)
795
+ case value.class.to_s
796
+ when 'String'
797
+ "'#{value}'"
798
+ when 'FalseClass', 'TrueClass'
799
+ value ? '1' : '0'
800
+ when 'Integer'
801
+ value
802
+ else
803
+ value.to_s
804
+ end
805
+ end
806
+
807
+ def value_for_hash(value, default = nil)
808
+ return default if value.nil?
809
+
810
+ case value.class.to_s
811
+ when 'String', 'Integer', 'FalseClass', 'TrueClass'
812
+ value
813
+ when value.empty?
814
+ default
815
+ else
816
+ value.to_s
817
+ end
818
+ end
819
+
820
+ def value_for_yaml(value)
821
+ return default if value.nil?
822
+
823
+ case value.class.to_s
824
+ when 'String'
825
+ "'#{value}'"
826
+ when 'Integer'
827
+ value
828
+ when 'FalseClass', 'TrueClass'
829
+ value ? true : false
830
+ when value.empty?
831
+ default
832
+ else
833
+ value.to_s
834
+ end
732
835
  end
733
836
 
734
837
  def write_command_file(opts, required_blocks)
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: 0.2.4
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fareed Stevenson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-02 00:00:00.000000000 Z
11
+ date: 2022-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: open3
@@ -67,7 +67,8 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.2.0
69
69
  description: Interactively select and execute fenced code blocks in markdown files.
70
- Build complex scripts by naming and requiring blocks.
70
+ Build complex scripts by naming and requiring blocks. Log resulting scripts and
71
+ output. Re-run scripts.
71
72
  email:
72
73
  - fareed@phomento.com
73
74
  executables:
@@ -127,5 +128,5 @@ requirements: []
127
128
  rubygems_version: 3.2.32
128
129
  signing_key:
129
130
  specification_version: 4
130
- summary: Execute shell blocks in markdown files.
131
+ summary: Interactively select and execute fenced code blocks in markdown files.
131
132
  test_files: []