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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +11 -2
  3. data/CHANGELOG.md +19 -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 +2 -2
  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 +39 -11
  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 +888 -603
  50. data/lib/hierarchy_string.rb +113 -25
  51. data/lib/input_sequencer.rb +16 -10
  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 +413 -165
  57. data/lib/mdoc.rb +27 -34
  58. data/lib/menu.src.yml +825 -710
  59. data/lib/menu.yml +799 -703
  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 +144 -8
  71. metadata +26 -3
  72. 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
+ ```
@@ -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