markdown_exec 2.3.0 → 2.4.0
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 +11 -2
- data/CHANGELOG.md +19 -0
- data/Gemfile.lock +1 -1
- data/Rakefile +32 -8
- data/bats/bats.bats +33 -0
- data/bats/block-types.bats +56 -0
- data/bats/cli.bats +74 -0
- data/bats/fail.bats +11 -0
- data/bats/history.bats +34 -0
- data/bats/markup.bats +66 -0
- data/bats/mde.bats +29 -0
- data/bats/options.bats +92 -0
- data/bats/test_helper.bash +152 -0
- data/bin/tab_completion.sh +44 -20
- data/docs/dev/block-type-opts.md +10 -0
- data/docs/dev/block-type-port.md +24 -0
- data/docs/dev/block-type-vars.md +7 -0
- data/docs/dev/pass-through-arguments.md +8 -0
- data/docs/dev/specs-import.md +9 -0
- data/docs/dev/specs.md +83 -0
- data/docs/dev/text-decoration.md +7 -0
- data/examples/bash-blocks.md +4 -4
- data/examples/block-names.md +2 -2
- data/examples/import0.md +23 -0
- data/examples/import1.md +13 -0
- data/examples/link-blocks-vars.md +3 -3
- data/examples/opts-blocks-require.md +6 -6
- data/examples/table-markup.md +31 -0
- data/examples/text-markup.md +58 -0
- data/examples/vars-blocks.md +2 -2
- data/examples/wrap.md +87 -9
- data/lib/ansi_formatter.rb +12 -6
- data/lib/ansi_string.rb +153 -0
- data/lib/argument_processor.rb +160 -0
- data/lib/cached_nested_file_reader.rb +4 -2
- data/lib/ce_get_cost_and_usage.rb +4 -3
- data/lib/cli.rb +1 -1
- data/lib/colorize.rb +39 -11
- data/lib/constants.rb +17 -0
- data/lib/directory_searcher.rb +4 -2
- data/lib/doh.rb +190 -0
- data/lib/env.rb +1 -1
- data/lib/exceptions.rb +9 -6
- data/lib/fcb.rb +0 -199
- data/lib/filter.rb +18 -5
- data/lib/find_files.rb +8 -3
- data/lib/format_table.rb +406 -0
- data/lib/hash_delegator.rb +888 -603
- data/lib/hierarchy_string.rb +113 -25
- data/lib/input_sequencer.rb +16 -10
- data/lib/instance_method_wrapper.rb +2 -1
- data/lib/layered_hash.rb +143 -0
- data/lib/link_history.rb +22 -8
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +413 -165
- data/lib/mdoc.rb +27 -34
- data/lib/menu.src.yml +825 -710
- data/lib/menu.yml +799 -703
- data/lib/namer.rb +6 -12
- data/lib/object_present.rb +1 -1
- data/lib/option_value.rb +7 -3
- data/lib/poly.rb +33 -14
- data/lib/resize_terminal.rb +60 -52
- data/lib/saved_assets.rb +45 -34
- data/lib/saved_files_matcher.rb +6 -3
- data/lib/streams_out.rb +7 -1
- data/lib/table_extractor.rb +166 -0
- data/lib/tap.rb +5 -6
- data/lib/text_analyzer.rb +144 -8
- metadata +26 -3
- data/lib/std_out_err_logger.rb +0 -119
@@ -0,0 +1,58 @@
|
|
1
|
+
# Demonstrate Decoration of Text
|
2
|
+
|
3
|
+
## LINE_DECOR_MAIN
|
4
|
+
These are the Main decorations predefined:
|
5
|
+
|
6
|
+
- **_Bold-Underline_**
|
7
|
+
- **Bold**
|
8
|
+
- **~Bold-Italic~**
|
9
|
+
- __Underline__
|
10
|
+
- _~Underline-Italic~_
|
11
|
+
- `Italic`
|
12
|
+
- ~~Strikethrough~~
|
13
|
+
|
14
|
+
## LINE_DECOR_PRE
|
15
|
+
These decorations are performed before Main, allowing for overrides without redefining Main.
|
16
|
+
|
17
|
+
### Override Bold Underline
|
18
|
+
This **_text_** is bold and underlined by default.
|
19
|
+
::: Select below to trigger. If it changes to yellow, the option was processed.
|
20
|
+
```opts :[bold-underline]
|
21
|
+
line_decor_pre:
|
22
|
+
- :color_method: :yellow
|
23
|
+
:pattern: '\*\*_([^_]{0,64})_\*\*'
|
24
|
+
```
|
25
|
+
This **_text_** is yellow when the rule takes precedence over the Main decorations.
|
26
|
+
|
27
|
+
## LINE_DECOR_POST
|
28
|
+
These decorations are performed after the Main decorations.
|
29
|
+
This !!text!! is not decorated by default.
|
30
|
+
::: Select below to trigger. If it changes to green, the option was processed.
|
31
|
+
```opts :[green]
|
32
|
+
line_decor_post:
|
33
|
+
- :color_method: :green
|
34
|
+
:pattern: '!!([^!]{0,64})!!'
|
35
|
+
```
|
36
|
+
|
37
|
+
## MDE Non-standard Markdown Configuration
|
38
|
+
- `_text_`: A single underscore delimeter is a standard way of underlining text. Because this occurs in code often, this decoration is not a default for MDE.
|
39
|
+
|
40
|
+
## Precedence
|
41
|
+
The order decorations are processed affects results.
|
42
|
+
A Bold delimiter `__` has to be processed before the Underline delimeter `_`. If reversed, bold `text` appears as underlined `_text_`.
|
43
|
+
|
44
|
+
# Related MDE Options
|
45
|
+
line_decor_main | Line-oriented text decoration (Main)
|
46
|
+
line_decor_post | Line-oriented text decoration (Post)
|
47
|
+
line_decor_pre | Line-oriented text decoration (Pre)
|
48
|
+
menu_note_match | Pattern for notes in block selection menu
|
49
|
+
|
50
|
+
```opts :(document_options)
|
51
|
+
line_decor_post:
|
52
|
+
- :color_method: blue
|
53
|
+
:pattern: '!([^!]{0,64})!blue!'
|
54
|
+
- :color_method: green
|
55
|
+
:pattern: '!([^!]{0,64})!green!'
|
56
|
+
- :color_method: red
|
57
|
+
:pattern: '!([^!]{0,64})!red!'
|
58
|
+
```
|
data/examples/vars-blocks.md
CHANGED
@@ -9,7 +9,7 @@ The hidden block "(defaults)" sets the environment variable VAULT to "default" i
|
|
9
9
|
: ${VAULT:=default}
|
10
10
|
```
|
11
11
|
|
12
|
-
:::
|
12
|
+
::: Select below to trigger. If it prints "VAULT: default", the shell block was processed.
|
13
13
|
The named block prints the environment variable VAULT. It requires hidden block "(defaults)" before printing.
|
14
14
|
```bash :show_vars +(defaults)
|
15
15
|
source bin/colorize_env_vars.sh
|
@@ -24,7 +24,7 @@ When clicked, it adds the variable to the inherited code. It does not output.
|
|
24
24
|
|
25
25
|
# DOES NOT WORK 2024-07-20
|
26
26
|
## This does not evaluate the shell block.
|
27
|
-
:::
|
27
|
+
::: Select below to trigger. If it prints "VAULT: 22", the shell block was processed.
|
28
28
|
The block sets the environment variable VAULT to "22". It requires block "show_vars". Notice block "show_vars" is called after the variable is set.
|
29
29
|
```vars :[set_with_show] +show_vars
|
30
30
|
VAULT: 22
|
data/examples/wrap.md
CHANGED
@@ -1,17 +1,27 @@
|
|
1
1
|
# Demo block wrapping
|
2
2
|
|
3
|
-
|
3
|
+
## Wrapped block
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
This block is wrapped by the `{outer*}` pair of blocks.
|
6
|
+
Expect output: "outer-before", "single body", and "outer-after".
|
7
|
+
::: Select below to test a block wrapped by a named pair of blocks.
|
8
|
+
```bash :[single] +{outer}
|
9
|
+
echo single body
|
10
|
+
```
|
8
11
|
|
9
|
-
|
10
|
-
::: and nested inside, the `{inner*}` pair of blocks.
|
12
|
+
## Nested wraps
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
This block is wrapped first by the `{outer*}` pair of blocks and then the `{inner*}` pair of blocks.
|
15
|
+
Expect output: "outer-before", "inner-before", "nested body", "inner-after", and "outer-after".
|
16
|
+
Blocks for the left-most included wrapper are first and last.
|
17
|
+
::: Select below to test a block wrapped by nested named pair of blocks.
|
18
|
+
```bash :[nested] +{outer} +{inner}
|
19
|
+
echo nested body
|
20
|
+
```
|
21
|
+
Expect output: "inner-before", "outer-before", "nested body", "outer-after", and "inner-after".
|
22
|
+
```bash :[inverted-nesting] +{inner} +{outer}
|
23
|
+
echo inverted nesting
|
24
|
+
```
|
15
25
|
|
16
26
|
::: This pair of hidden blocks are the `{inner*}` set.
|
17
27
|
```bash :{inner}
|
@@ -31,3 +41,71 @@ echo outer-before
|
|
31
41
|
```bash :{outer-after}
|
32
42
|
echo outer-after
|
33
43
|
```
|
44
|
+
|
45
|
+
## Requiring additional Bash blocks
|
46
|
+
|
47
|
+
```bash :(inc1)
|
48
|
+
echo included1
|
49
|
+
```
|
50
|
+
```bash :inc2
|
51
|
+
echo included2
|
52
|
+
```
|
53
|
+
Main block without a name.
|
54
|
+
Inc2 + Outer
|
55
|
+
```bash +{outer} +inc2
|
56
|
+
echo expecting11
|
57
|
+
```
|
58
|
+
Inc1 + Outer
|
59
|
+
```bash +{outer} +(inc1)
|
60
|
+
echo expecting12
|
61
|
+
```
|
62
|
+
Main block with a name.
|
63
|
+
Inc2 + Outer
|
64
|
+
```bash :ex21 +{outer} +inc2
|
65
|
+
echo expecting21
|
66
|
+
```
|
67
|
+
Inc1 + Outer
|
68
|
+
```bash :ex22 +{outer} +(inc1)
|
69
|
+
echo expecting22
|
70
|
+
```
|
71
|
+
|
72
|
+
## Requiring additional Bash blocks from wrapper block
|
73
|
+
|
74
|
+
::: Does not work
|
75
|
+
|
76
|
+
Inc1 + wrap-with-req
|
77
|
+
```bash :ex31 +{wrap-with-req}
|
78
|
+
echo expecting31
|
79
|
+
```
|
80
|
+
|
81
|
+
```bash :{wrap-with-req} +(inc1)
|
82
|
+
echo wrap-with-req-before
|
83
|
+
```
|
84
|
+
|
85
|
+
```bash :{wrap-with-req-after}
|
86
|
+
echo wrap-with-req-after
|
87
|
+
```
|
88
|
+
|
89
|
+
::: Debug Inherited Code
|
90
|
+
```opts
|
91
|
+
dump_blocks_in_file: true
|
92
|
+
dump_dependencies: true
|
93
|
+
dump_inherited_block_names: true
|
94
|
+
dump_inherited_dependencies: true
|
95
|
+
dump_menu_blocks: true
|
96
|
+
```
|
97
|
+
|
98
|
+
```opts :add_shell_code_labels
|
99
|
+
shell_code_label_format_above: "# -^-"
|
100
|
+
shell_code_label_format_below: "# -v- +%{block_name} -o- %{document_filename} -o- %{time_now_date} -v-"
|
101
|
+
```
|
102
|
+
|
103
|
+
```opts :(document_options)
|
104
|
+
execute_in_own_window: false
|
105
|
+
output_execution_report: false
|
106
|
+
output_execution_summary: false
|
107
|
+
pause_after_script_execution: true
|
108
|
+
line_decor_pre:
|
109
|
+
- :color_method: :underline_italic
|
110
|
+
:pattern: '"([^"]{0,64})"'
|
111
|
+
```
|
data/lib/ansi_formatter.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
# encoding=utf-8
|
5
5
|
|
6
|
+
require_relative 'ansi_string'
|
7
|
+
|
6
8
|
class AnsiFormatter
|
7
9
|
def initialize(options = {})
|
8
10
|
@options = options
|
@@ -18,10 +20,10 @@ class AnsiFormatter
|
|
18
20
|
line_postfix: '',
|
19
21
|
detail_sep: ''
|
20
22
|
)
|
21
|
-
|
23
|
+
data&.map do |item|
|
22
24
|
scan_and_process_multiple_substrings(item, highlight, plain_color_sym,
|
23
25
|
highlight_color_sym).join
|
24
|
-
end || []
|
26
|
+
end || []
|
25
27
|
end
|
26
28
|
|
27
29
|
# Function to scan a string and process its segments based on multiple substrings
|
@@ -30,8 +32,10 @@ class AnsiFormatter
|
|
30
32
|
# @param plain_sym [Symbol] The symbol for non-matching segments.
|
31
33
|
# @param color_sym [Symbol] The symbol for matching segments.
|
32
34
|
# @return [Array<String>] The processed segments.
|
33
|
-
def scan_and_process_multiple_substrings(str, substrings, plain_sym,
|
34
|
-
|
35
|
+
def scan_and_process_multiple_substrings(str, substrings, plain_sym,
|
36
|
+
color_sym)
|
37
|
+
return string_send_color(str,
|
38
|
+
plain_sym) if substrings.empty? || substrings.any?(&:empty?)
|
35
39
|
|
36
40
|
substring_patterns = substrings.map do |value|
|
37
41
|
[value, Regexp.new(value, Regexp::IGNORECASE)]
|
@@ -41,7 +45,9 @@ class AnsiFormatter
|
|
41
45
|
remaining_str = str.dup
|
42
46
|
|
43
47
|
while remaining_str.length.positive?
|
44
|
-
match_indices = substring_patterns.map
|
48
|
+
match_indices = substring_patterns.map do |_, pattern|
|
49
|
+
remaining_str.index(pattern)
|
50
|
+
end.compact
|
45
51
|
earliest_match = match_indices.min
|
46
52
|
|
47
53
|
if earliest_match
|
@@ -112,6 +118,6 @@ class AnsiFormatter
|
|
112
118
|
# @return [String] The string with the applied color method.
|
113
119
|
def string_send_color(string, color_sym, default: 'plain')
|
114
120
|
color_method = @options.fetch(color_sym, default).to_sym
|
115
|
-
string.to_s.send(color_method)
|
121
|
+
AnsiString.new(string.to_s).send(color_method)
|
116
122
|
end
|
117
123
|
end
|
data/lib/ansi_string.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# encoding=utf-8
|
4
|
+
|
5
|
+
# Extends Ruby's native String class to include ANSI coloring functionality.
|
6
|
+
# Adds methods to apply RGB colors, named colors, and other formatting to strings.
|
7
|
+
class AnsiString < String
|
8
|
+
# Handles dynamic method calls to create RGB colors.
|
9
|
+
#
|
10
|
+
# @param method_name [Symbol] The name of the method being called.
|
11
|
+
# @param args [Array] The arguments passed to the method.
|
12
|
+
# @param block [Proc] An optional block.
|
13
|
+
# @return [AnsiString] The formatted string.
|
14
|
+
def method_missing(method_name, *args, &block)
|
15
|
+
if dynamic_color_method?(method_name)
|
16
|
+
case method_name.to_s
|
17
|
+
when /^fg_bg_rgb_/
|
18
|
+
bytes = $'.split('_')
|
19
|
+
fg_bg_rgb_color(bytes[0..2].join(';'), bytes[3..5].join(';'))
|
20
|
+
when /^fg_bg_rgbh_/
|
21
|
+
hex_to_fg_bg_rgb($')
|
22
|
+
when /^fg_rgb_/
|
23
|
+
fg_rgb_color($'.gsub('_', ';'))
|
24
|
+
when /^fg_rgbh_/
|
25
|
+
hex_to_rgb($')
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Checks if the AnsiString instance responds to a particular method.
|
35
|
+
#
|
36
|
+
# @param method_name [Symbol] The name of the method being checked.
|
37
|
+
# @param include_private [Boolean] Whether to include private methods in the check.
|
38
|
+
# @return [Boolean] True if the method is supported, otherwise false.
|
39
|
+
def respond_to_missing?(method_name, include_private = false)
|
40
|
+
dynamic_color_method?(method_name) || super
|
41
|
+
end
|
42
|
+
|
43
|
+
# Generates an ANSI control sequence for the string.
|
44
|
+
#
|
45
|
+
# @return [AnsiString] The string wrapped in an ANSI control sequence.
|
46
|
+
def ansi_control_sequence
|
47
|
+
self.class.new("\033[#{self}\033[0m")
|
48
|
+
end
|
49
|
+
|
50
|
+
# Applies a 24-bit RGB foreground and background color to the string.
|
51
|
+
#
|
52
|
+
# @param fg_rgb [String] The RGB foreground color, expressed as a string like "1;2;3".
|
53
|
+
# @param bg_rgb [String] The RGB background color, expressed as a string like "4;5;6".
|
54
|
+
# @return [AnsiString] The string with the applied RGB foreground and background colors.
|
55
|
+
def fg_bg_rgb_color(fg_rgb, bg_rgb)
|
56
|
+
self.class.new("38;2;#{fg_rgb}m\033[48;2;#{bg_rgb}m#{self}").ansi_control_sequence
|
57
|
+
end
|
58
|
+
|
59
|
+
# Applies a 24-bit RGB foreground color to the string.
|
60
|
+
#
|
61
|
+
# @param rgb [String] The RGB color, expressed as a string like "1;2;3".
|
62
|
+
# @return [AnsiString] The string with the applied RGB foreground color.
|
63
|
+
def fg_rgb_color(rgb)
|
64
|
+
self.class.new("38;2;#{rgb}m#{self}").ansi_control_sequence
|
65
|
+
end
|
66
|
+
|
67
|
+
# Converts hex color codes to RGB and applies them to the string.
|
68
|
+
#
|
69
|
+
# @param hex_str [String] The RGB color, expressed as a hex string like "FF00FF".
|
70
|
+
# @return [AnsiString] The string with the applied RGB foreground color.
|
71
|
+
def hex_to_fg_bg_rgb(hex_str)
|
72
|
+
values = hex_str.split('_').map { |hex| hex.to_i(16).to_s }
|
73
|
+
fg_bg_rgb_color(
|
74
|
+
values[0..2].join(';'),
|
75
|
+
values[3..5].join(';')
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Converts hex color codes to RGB and applies them to the string.
|
80
|
+
#
|
81
|
+
# @param hex_str [String] The RGB color, expressed as a hex string like "FF00FF".
|
82
|
+
# @return [AnsiString] The string with the applied RGB foreground color.
|
83
|
+
def hex_to_rgb(hex_str)
|
84
|
+
self.class.new(
|
85
|
+
fg_rgb_color(
|
86
|
+
hex_str.split('_').map { |hex| hex.to_i(16).to_s }.join(';')
|
87
|
+
)
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Provides a plain, unmodified version of the string.
|
92
|
+
#
|
93
|
+
# @return [AnsiString] The original string.
|
94
|
+
def plain
|
95
|
+
self.class.new(self)
|
96
|
+
end
|
97
|
+
|
98
|
+
# A collection of methods for applying named colors.
|
99
|
+
#
|
100
|
+
# For example, #black applies a black foreground color to the string.
|
101
|
+
# These are provided for convenience and easy readability.
|
102
|
+
def black; self.class.new("30m#{self}").ansi_control_sequence; end
|
103
|
+
def bred; self.class.new("1;31m#{self}").ansi_control_sequence; end
|
104
|
+
def bgreen; self.class.new("1;32m#{self}").ansi_control_sequence; end
|
105
|
+
def byellow; self.class.new("1;33m#{self}").ansi_control_sequence; end
|
106
|
+
def magenta; self.class.new("35m#{self}").ansi_control_sequence; end
|
107
|
+
def cyan; self.class.new("36m#{self}").ansi_control_sequence; end
|
108
|
+
def white; self.class.new("37m#{self}").ansi_control_sequence; end
|
109
|
+
def bwhite; self.class.new("1;37m#{self}").ansi_control_sequence; end
|
110
|
+
|
111
|
+
# More named colors using RGB hex values
|
112
|
+
def blue; fg_rgbh_00_00_FF; end
|
113
|
+
def green; fg_rgbh_00_FF_00; end
|
114
|
+
def indigo; fg_rgbh_4B_00_82; end
|
115
|
+
def orange; fg_rgbh_FF_7F_00; end
|
116
|
+
def red; fg_rgbh_FF_00_00; end
|
117
|
+
def violet; fg_rgbh_94_00_D3; end
|
118
|
+
def yellow; fg_rgbh_FF_FF_00; end
|
119
|
+
|
120
|
+
# Graphics modes
|
121
|
+
def bold; self.class.new("\033[1m#{self}\033[22m"); end
|
122
|
+
|
123
|
+
def bold_italic;
|
124
|
+
self.class.new("\033[1m\033[3m#{self}\033[22m\033[23m");
|
125
|
+
end
|
126
|
+
|
127
|
+
def bold_underline;
|
128
|
+
self.class.new("\033[1m\033[4m#{self}\033[22m\033[24m");
|
129
|
+
end
|
130
|
+
|
131
|
+
def dim; self.class.new("\033[2m#{self}\033[22m"); end
|
132
|
+
def italic; self.class.new("\033[3m#{self}\033[23m"); end
|
133
|
+
def underline; self.class.new("\033[4m#{self}\033[24m"); end
|
134
|
+
|
135
|
+
def underline_italic;
|
136
|
+
self.class.new("\033[4m\033[3m#{self}\033[23m\033[24m");
|
137
|
+
end
|
138
|
+
|
139
|
+
def blinking; self.class.new("\033[5m#{self}\033[25m"); end
|
140
|
+
def inverse; self.class.new("\033[7m#{self}\033[27m"); end
|
141
|
+
def hidden; self.class.new("\033[8m#{self}\033[28m"); end
|
142
|
+
def strikethrough; self.class.new("\033[9m#{self}\033[29m"); end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
# Checks if the method name matches any of the dynamic color methods.
|
147
|
+
#
|
148
|
+
# @param method_name [Symbol] The name of the method being checked.
|
149
|
+
# @return [Boolean] True if the method name matches a dynamic color method.
|
150
|
+
def dynamic_color_method?(method_name)
|
151
|
+
method_name.to_s =~ /^(fg_bg_rgb_|fg_bg_rgbh_|fg_rgb_|fg_rgbh_)/
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# encoding=utf-8
|
4
|
+
require_relative 'constants'
|
5
|
+
require_relative 'object_present'
|
6
|
+
|
7
|
+
def process_arguments(arguments, loose_args, options_parsed)
|
8
|
+
# &bt arguments, loose_args, options_parsed
|
9
|
+
# loose_args will be empty first command contains pass-through arguments
|
10
|
+
while loose_args.any?
|
11
|
+
if arguments.first == loose_args.first
|
12
|
+
yield ArgPro::ArgIsPosition, arguments.shift
|
13
|
+
|
14
|
+
loose_args.shift
|
15
|
+
next
|
16
|
+
end
|
17
|
+
|
18
|
+
yield ArgPro::ArgIsOption, options_parsed.first
|
19
|
+
|
20
|
+
arguments.shift(options_parsed.first[:procname].present? ? 2 : 1)
|
21
|
+
options_parsed.shift
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def process_commands(options_parsed:, arguments:, enable_search:,
|
26
|
+
named_procs:, rest:)
|
27
|
+
# &bt arguments,options_parsed
|
28
|
+
command_processed = false
|
29
|
+
block_executed = false
|
30
|
+
requested_menu = false
|
31
|
+
position = 0
|
32
|
+
|
33
|
+
process_arguments(arguments.dup, rest.dup,
|
34
|
+
options_parsed.dup) do |type, item|
|
35
|
+
# &bt type,item
|
36
|
+
case type
|
37
|
+
when ArgPro::ArgIsOption
|
38
|
+
if named_procs.include?(item[:name])
|
39
|
+
command_processed = true
|
40
|
+
yield ArgPro::CallProcess, item[:name]
|
41
|
+
else
|
42
|
+
converted = if item[:proccode]
|
43
|
+
yield ArgPro::ConvertValue, [item[:proccode],
|
44
|
+
item[:value]]
|
45
|
+
else
|
46
|
+
item[:value]
|
47
|
+
end
|
48
|
+
if item[:name]
|
49
|
+
yield ArgPro::ActSetOption, [item[:name], converted]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
when ArgPro::ArgIsPosition
|
53
|
+
case position
|
54
|
+
when 0
|
55
|
+
# position 0: file, folder, or search term (optional)
|
56
|
+
if Dir.exist?(item)
|
57
|
+
yield ArgPro::ActSetPath, item
|
58
|
+
elsif File.exist?(item)
|
59
|
+
yield ArgPro::ActSetFileName, item
|
60
|
+
elsif enable_search
|
61
|
+
yield ArgPro::ActFind, item
|
62
|
+
else
|
63
|
+
yield ArgPro::ActFileIsMissing, item
|
64
|
+
end
|
65
|
+
else
|
66
|
+
# position 1: block (optional)
|
67
|
+
if item == '.'
|
68
|
+
requested_menu = true
|
69
|
+
else
|
70
|
+
block_executed = true
|
71
|
+
yield ArgPro::ActSetBlockName, item
|
72
|
+
end
|
73
|
+
end
|
74
|
+
position += 1
|
75
|
+
rest.shift
|
76
|
+
else
|
77
|
+
raise
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
return if $PROGRAM_NAME != __FILE__
|
83
|
+
|
84
|
+
require 'minitest/autorun'
|
85
|
+
|
86
|
+
class ArgumentProcessorTest < Minitest::Test
|
87
|
+
def setup
|
88
|
+
@arguments = ['fixtures/sample1.md', 'block', '--option', 'value',
|
89
|
+
'ignored']
|
90
|
+
@rest = ['fixtures/sample1.md', 'block', 'ignored']
|
91
|
+
@options_parsed = [{ name: '--option', procname: 'VAL', value: 'value' }]
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_process_arguments_with_position
|
95
|
+
result = []
|
96
|
+
process_arguments(@arguments,
|
97
|
+
@rest, @options_parsed) do |type, item|
|
98
|
+
result << [type, item]
|
99
|
+
end
|
100
|
+
|
101
|
+
expected = [
|
102
|
+
[ArgPro::ArgIsPosition, 'fixtures/sample1.md'],
|
103
|
+
[ArgPro::ArgIsPosition, 'block'],
|
104
|
+
[ArgPro::ArgIsOption, { name: '--option', procname: 'VAL', value: 'value' }],
|
105
|
+
[ArgPro::ArgIsPosition, 'ignored']
|
106
|
+
]
|
107
|
+
assert_equal expected, result
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class CommandProcessorTest < Minitest::Test
|
112
|
+
def setup
|
113
|
+
@exisiting_file_name = Dir.glob('fixtures/*').first
|
114
|
+
@missing_file_name = 'missing-file.tmp'
|
115
|
+
@arguments = [@exisiting_file_name, 'process', '--option', 'value',
|
116
|
+
'ignored']
|
117
|
+
@named_procs = []
|
118
|
+
@options_parsed = [{ name: '--option', procname: 'VAL', value: 'value' }]
|
119
|
+
@rest = [@exisiting_file_name, 'process', 'ignored']
|
120
|
+
@enable_search = true
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_process_commands_with_valid_file
|
124
|
+
result = []
|
125
|
+
process_commands(
|
126
|
+
arguments: @arguments, named_procs: @named_procs,
|
127
|
+
options_parsed: @options_parsed,
|
128
|
+
rest: @rest, enable_search: @enable_search
|
129
|
+
) do |type, item|
|
130
|
+
result << [type, item]
|
131
|
+
end
|
132
|
+
|
133
|
+
expected = [
|
134
|
+
[ArgPro::ActSetFileName, @exisiting_file_name],
|
135
|
+
[ArgPro::ActSetBlockName, 'process'],
|
136
|
+
[ArgPro::ActSetOption, ['--option', 'value']],
|
137
|
+
[ArgPro::ActSetBlockName, 'ignored']
|
138
|
+
]
|
139
|
+
|
140
|
+
assert_equal expected, result
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_process_commands_with_search
|
144
|
+
result = []
|
145
|
+
process_commands(arguments: [@missing_file_name, 'process'],
|
146
|
+
named_procs: @named_procs,
|
147
|
+
options_parsed: [],
|
148
|
+
rest: [@missing_file_name, 'process'],
|
149
|
+
enable_search: @enable_search) do |type, item|
|
150
|
+
result << [type, item]
|
151
|
+
end
|
152
|
+
|
153
|
+
expected = [
|
154
|
+
[ArgPro::ActFind, @missing_file_name],
|
155
|
+
[ArgPro::ActSetBlockName, 'process']
|
156
|
+
]
|
157
|
+
|
158
|
+
assert_equal expected, result
|
159
|
+
end
|
160
|
+
end
|
@@ -53,7 +53,8 @@ class CachedNestedFileReader
|
|
53
53
|
included_file_path = if name_strip =~ %r{^/}
|
54
54
|
name_strip
|
55
55
|
elsif import_paths
|
56
|
-
find_files(name_strip,
|
56
|
+
find_files(name_strip,
|
57
|
+
import_paths + [directory_path])&.first
|
57
58
|
else
|
58
59
|
File.join(directory_path, name_strip)
|
59
60
|
end
|
@@ -72,7 +73,8 @@ class CachedNestedFileReader
|
|
72
73
|
@file_cache[filename] = processed_lines
|
73
74
|
rescue Errno::ENOENT
|
74
75
|
# Exceptions.error_handler('readlines', { abort: true })
|
75
|
-
warn_format('readlines', "No such file -- #{filename} @@ #{context}",
|
76
|
+
warn_format('readlines', "No such file -- #{filename} @@ #{context}",
|
77
|
+
{ abort: true })
|
76
78
|
end
|
77
79
|
end
|
78
80
|
|
@@ -8,15 +8,16 @@ text = system(cmd)
|
|
8
8
|
data = YAML.load(text)
|
9
9
|
|
10
10
|
# Extracting the relevant information
|
11
|
-
services = data[
|
12
|
-
service_name = group[
|
11
|
+
services = data['ResultsByTime'][0]['Groups'].map do |group|
|
12
|
+
service_name = group['Keys'][0]
|
13
13
|
unblended_cost = "#{group['Metrics']['UnblendedCost']['Amount']} #{group['Metrics']['UnblendedCost']['Unit']}"
|
14
14
|
usage_quantity = "#{group['Metrics']['UsageQuantity']['Amount']} #{group['Metrics']['UsageQuantity']['Unit']}"
|
15
15
|
[service_name, unblended_cost, usage_quantity]
|
16
16
|
end
|
17
17
|
|
18
18
|
# Create a table
|
19
|
-
table = Terminal::Table.new :
|
19
|
+
table = Terminal::Table.new headings: ['Service', 'Unblended Cost', 'Usage Quantity'],
|
20
|
+
rows: services
|
20
21
|
|
21
22
|
# Output the table
|
22
23
|
puts table
|