markdown_exec 2.2.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +16 -4
  3. data/CHANGELOG.md +28 -0
  4. data/Gemfile.lock +1 -1
  5. data/Rakefile +32 -8
  6. data/bats/bats.bats +33 -0
  7. data/bats/block-types.bats +56 -0
  8. data/bats/cli.bats +74 -0
  9. data/bats/fail.bats +11 -0
  10. data/bats/history.bats +34 -0
  11. data/bats/markup.bats +66 -0
  12. data/bats/mde.bats +29 -0
  13. data/bats/options.bats +92 -0
  14. data/bats/test_helper.bash +152 -0
  15. data/bin/tab_completion.sh +44 -20
  16. data/docs/dev/block-type-opts.md +10 -0
  17. data/docs/dev/block-type-port.md +24 -0
  18. data/docs/dev/block-type-vars.md +7 -0
  19. data/docs/dev/pass-through-arguments.md +8 -0
  20. data/docs/dev/specs-import.md +9 -0
  21. data/docs/dev/specs.md +83 -0
  22. data/docs/dev/text-decoration.md +7 -0
  23. data/examples/bash-blocks.md +4 -4
  24. data/examples/block-names.md +40 -5
  25. data/examples/import0.md +23 -0
  26. data/examples/import1.md +13 -0
  27. data/examples/link-blocks-vars.md +3 -3
  28. data/examples/opts-blocks-require.md +6 -6
  29. data/examples/table-markup.md +31 -0
  30. data/examples/text-markup.md +58 -0
  31. data/examples/vars-blocks.md +2 -2
  32. data/examples/wrap.md +87 -9
  33. data/lib/ansi_formatter.rb +12 -6
  34. data/lib/ansi_string.rb +153 -0
  35. data/lib/argument_processor.rb +160 -0
  36. data/lib/cached_nested_file_reader.rb +4 -2
  37. data/lib/ce_get_cost_and_usage.rb +4 -3
  38. data/lib/cli.rb +1 -1
  39. data/lib/colorize.rb +41 -0
  40. data/lib/constants.rb +17 -0
  41. data/lib/directory_searcher.rb +4 -2
  42. data/lib/doh.rb +190 -0
  43. data/lib/env.rb +1 -1
  44. data/lib/exceptions.rb +9 -6
  45. data/lib/fcb.rb +0 -199
  46. data/lib/filter.rb +18 -5
  47. data/lib/find_files.rb +8 -3
  48. data/lib/format_table.rb +406 -0
  49. data/lib/hash_delegator.rb +939 -611
  50. data/lib/hierarchy_string.rb +221 -0
  51. data/lib/input_sequencer.rb +19 -11
  52. data/lib/instance_method_wrapper.rb +2 -1
  53. data/lib/layered_hash.rb +143 -0
  54. data/lib/link_history.rb +22 -8
  55. data/lib/markdown_exec/version.rb +1 -1
  56. data/lib/markdown_exec.rb +420 -165
  57. data/lib/mdoc.rb +38 -38
  58. data/lib/menu.src.yml +832 -680
  59. data/lib/menu.yml +814 -689
  60. data/lib/namer.rb +6 -12
  61. data/lib/object_present.rb +1 -1
  62. data/lib/option_value.rb +7 -3
  63. data/lib/poly.rb +33 -14
  64. data/lib/resize_terminal.rb +60 -52
  65. data/lib/saved_assets.rb +45 -34
  66. data/lib/saved_files_matcher.rb +6 -3
  67. data/lib/streams_out.rb +7 -1
  68. data/lib/table_extractor.rb +166 -0
  69. data/lib/tap.rb +5 -6
  70. data/lib/text_analyzer.rb +236 -0
  71. metadata +28 -3
  72. data/lib/std_out_err_logger.rb +0 -119
@@ -0,0 +1,31 @@
1
+ # Demonstrate Tables
2
+
3
+ Table flush at left.
4
+ Centered columns.
5
+ | Common Name| Species| Genus| Family| Year Discovered
6
+ |:-:|:-:|:-:|:-:|:-:
7
+ | Tapanuli Orangutan| Pongo tapanuliensis| Pongo| Hominidae| 2017
8
+ | Psychedelic Frogfish| Histiophryne psychedelica| Histiophryne| Antennariidae| 2009
9
+ | Ruby Seadragon| Phyllopteryx dewysea| Phyllopteryx| Syngnathidae| 2015
10
+
11
+ Table indented with two spaces.
12
+ Left-justified columns.
13
+ | Common Name| Species| Genus| Family| Year Discovered
14
+ |:-|:-|:-|:-|:-
15
+ | Illacme tobini (Millipede)| Illacme tobini| Illacme| Siphonorhinidae| 2016
16
+ | Cappuccino Snake| Hydrodynastes bicinctus| Hydrodynastes| Colubridae| 2021
17
+ | Homo luzonensis| Homo luzonensis| Homo| Hominidae| 2019
18
+
19
+ Table indented with one tab.
20
+ Right-justified columns.
21
+ | Common Name| Species| Genus| Family| Year Discovered
22
+ |-:|-:|-:|-:|-:
23
+ | Spiny Dandelion| Taraxacum japonicum| Taraxacum| Asteraceae| 2022
24
+ | Mythical Monkey| Cercopithecus lomamiensis| Cercopithecus| Cercopithecidae| 2012
25
+ | Yeti Crab| Kiwa hirsuta| Kiwa| Kiwaidae| 2005
26
+
27
+ ## Non-table text is not modified
28
+ 1. |
29
+ 2. ||
30
+ |
31
+ ||
@@ -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
+ ```
@@ -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
- ::: Click below to trigger. If it prints "VAULT: default", the shell block was processed.
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
- ::: Click below to trigger. If it prints "VAULT: 22", the shell block was processed.
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
- ::: This block is wrapped by the `{outer*}` pair of blocks.
3
+ ## Wrapped block
4
4
 
5
- ```bash :single +{outer}
6
- echo single body - wrapped by outer
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
- ::: This block is wrapped first by the `{outer*}` pair of blocks
10
- ::: and nested inside, the `{inner*}` pair of blocks.
12
+ ## Nested wraps
11
13
 
12
- ```bash :nested +{outer} +{inner}
13
- echo nested body - wrapped by outer and then inner
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
+ ```
@@ -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
- (data&.map do |item|
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, color_sym)
34
- return string_send_color(str, plain_sym) if substrings.empty? || substrings.any?(&:empty?)
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 { |_, pattern| remaining_str.index(pattern) }.compact
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
@@ -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, import_paths + [directory_path])&.first
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}", { abort: true })
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["ResultsByTime"][0]["Groups"].map do |group|
12
- service_name = group["Keys"][0]
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 :headings => ['Service', 'Unblended Cost', 'Usage Quantity'], :rows => services
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
data/lib/cli.rb CHANGED
@@ -11,7 +11,7 @@ module CLI
11
11
  when 'String'
12
12
  Shellwords.escape value
13
13
  when 'FalseClass', 'TrueClass'
14
- value ? '1' : '0'
14
+ value ? 't' : 'f'
15
15
  else
16
16
  Shellwords.escape value.to_s
17
17
  end