markdown_exec 2.5.0 → 2.6.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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/Gemfile.lock +2 -2
  4. data/Rakefile +3 -3
  5. data/bats/block-types.bats +13 -7
  6. data/bats/import.bats +6 -0
  7. data/bats/markup.bats +6 -15
  8. data/bats/options-collapse.bats +26 -0
  9. data/bats/options.bats +1 -1
  10. data/bats/table.bats +8 -0
  11. data/bats/test_helper.bash +74 -49
  12. data/bats/variable-expansion.bats +46 -0
  13. data/bin/tab_completion.sh +1 -1
  14. data/docs/dev/bats-document-configuration.md +8 -1
  15. data/docs/dev/block-type-bash.md +1 -1
  16. data/docs/dev/block-type-opts.md +1 -5
  17. data/docs/dev/block-type-vars.md +4 -0
  18. data/docs/dev/import-missing.md +2 -0
  19. data/docs/dev/menu-cli.md +1 -1
  20. data/docs/dev/options-collapse.md +47 -0
  21. data/docs/dev/requiring-blocks.md +3 -0
  22. data/docs/dev/specs.md +2 -1
  23. data/docs/dev/table-crash.md +39 -0
  24. data/docs/dev/table-indent.md +26 -0
  25. data/docs/dev/text-decoration.md +2 -5
  26. data/docs/dev/variable-expansion.md +2 -4
  27. data/examples/bash-blocks.md +1 -1
  28. data/examples/block-names.md +1 -1
  29. data/examples/block-types.md +1 -1
  30. data/examples/data-files.md +1 -1
  31. data/examples/document_options.md +2 -2
  32. data/examples/indent.md +1 -1
  33. data/examples/interrupt.md +1 -1
  34. data/examples/link-blocks-vars.md +1 -1
  35. data/examples/linked.md +1 -1
  36. data/examples/linked1.md +1 -1
  37. data/examples/nickname.md +1 -1
  38. data/examples/opts-blocks-require.md +1 -1
  39. data/examples/opts-blocks.md +1 -1
  40. data/examples/opts_output_execution.md +1 -1
  41. data/examples/pass-through-arguments.md +1 -1
  42. data/examples/pause-after-execution.md +1 -1
  43. data/examples/port-blocks.md +1 -1
  44. data/examples/save.md +1 -1
  45. data/examples/text-markup.md +1 -1
  46. data/examples/variable-expansion.md +6 -2
  47. data/examples/vars-blocks.md +1 -1
  48. data/examples/wrap.md +1 -1
  49. data/lib/block_types.rb +4 -0
  50. data/lib/cached_nested_file_reader.rb +7 -4
  51. data/lib/collapser.rb +302 -0
  52. data/lib/constants.rb +10 -0
  53. data/lib/evaluate_shell_expressions.rb +0 -3
  54. data/lib/fcb.rb +13 -17
  55. data/lib/format_table.rb +11 -7
  56. data/lib/hash_delegator.rb +461 -272
  57. data/lib/hierarchy_string.rb +5 -1
  58. data/lib/markdown_exec/version.rb +1 -1
  59. data/lib/markdown_exec.rb +16 -32
  60. data/lib/mdoc.rb +100 -35
  61. data/lib/menu.src.yml +124 -17
  62. data/lib/menu.yml +102 -16
  63. data/lib/ww.rb +75 -22
  64. metadata +12 -9
  65. data/lib/append_to_bash_history.rb +0 -303
  66. data/lib/ce_get_cost_and_usage.rb +0 -23
  67. data/lib/doh.rb +0 -190
  68. data/lib/layered_hash.rb +0 -143
  69. data/lib/poly.rb +0 -171
@@ -1,303 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # encoding=utf-8
5
-
6
- def append_to_bash_history(line)
7
- # Get the bash history file from the HISTFILE environment variable or default to ~/.bash_history
8
- histfile = ENV['HISTFILE'] || File.expand_path("~/.bash_history")
9
-
10
- if File.exist?(histfile)
11
- # Open the file in append mode and write the line
12
- File.open(histfile, 'a') do |file|
13
- file.puts line
14
- end
15
- puts "Appended to bash history: #{line}"
16
- else
17
- puts "Bash history file does not exist."
18
- end
19
- end
20
-
21
- return if $PROGRAM_NAME != __FILE__
22
-
23
- # Example usage:
24
- append_to_bash_history('echo "Hello from Ruby!"')
25
-
26
- # require 'minitest/autorun'
27
- # require_relative 'colorize'
28
-
29
- # class TestMarkdownTableFormatter < Minitest::Test
30
- # def setup
31
- # @lines = [
32
- # '| Header 1 | Header 2 | Header 3 |',
33
- # '|----------|:--------:|---------:|',
34
- # '| Row 1 Col 1 | Row 1 Col 2 | Row 1 Col 3 |',
35
- # '| Row 2 Col 1 | Row 2 Col 2 | Row 2 Col 3 |'
36
- # ]
37
- # @column_count = 3
38
- # end
39
-
40
- # def test_format_table
41
- # result = MarkdownTableFormatter.format_table(
42
- # column_count: @column_count, lines: @lines
43
- # )
44
- # expected = [
45
- # '| Header 1 | Header 2 | Header 3 |',
46
- # '| ----------- | ----------- | ----------- |',
47
- # '| Row 1 Col 1 | Row 1 Col 2 | Row 1 Col 3 |',
48
- # '| Row 2 Col 1 | Row 2 Col 2 | Row 2 Col 3 |'
49
- # ]
50
- # assert_equal expected, result
51
- # end
52
-
53
- # def test_format_table_with_decoration
54
- # decorate = { header_row: :upcase, row: %i[downcase upcase] }
55
- # result = MarkdownTableFormatter.format_table(
56
- # column_count: @column_count,
57
- # decorate: decorate,
58
- # lines: @lines
59
- # )
60
- # expected = [
61
- # '| HEADER 1 | HEADER 2 | HEADER 3 |',
62
- # '| ----------- | ----------- | ----------- |',
63
- # '| ROW 1 COL 1 | ROW 1 COL 2 | ROW 1 COL 3 |',
64
- # '| row 2 col 1 | row 2 col 2 | row 2 col 3 |'
65
- # ]
66
- # assert_equal expected, result
67
- # end
68
-
69
- # def test_format_table_with_empty_lines
70
- # lines_with_empty = [
71
- # '| Header 1 | Header 2 | Header 3 |',
72
- # '|----------|:--------:|---------:|',
73
- # '| Row 1 Col 1 | Row 1 Col 2 | Row 1 Col 3 |',
74
- # '',
75
- # '| Row 2 Col 1 | Row 2 Col 2 | Row 2 Col 3 |'
76
- # ]
77
- # result = MarkdownTableFormatter.format_table(
78
- # lines: lines_with_empty,
79
- # column_count: @column_count
80
- # )
81
- # expected = [
82
- # '| Header 1 | Header 2 | Header 3 |',
83
- # '| ----------- | ----------- | ----------- |',
84
- # '| Row 1 Col 1 | Row 1 Col 2 | Row 1 Col 3 |',
85
- # '',
86
- # '| Row 2 Col 1 | Row 2 Col 2 | Row 2 Col 3 |'
87
- # ]
88
- # assert_equal expected, result
89
- # end
90
-
91
- # def test_alignment_detection
92
- # lines_with_alignment = [
93
- # '| Header 1 | Header 2 | Header 3 |',
94
- # '|:-------- |:--------:| --------:|'
95
- # ]
96
- # result = MarkdownTableFormatter.format_table(
97
- # lines: lines_with_alignment,
98
- # column_count: @column_count
99
- # )
100
- # expected = [
101
- # '| Header 1 | Header 2 | Header 3 |',
102
- # '| --------- | ---------- | --------- |'
103
- # ]
104
- # assert_equal expected, result[0..1] # only checking the header and separator
105
- # end
106
- # end
107
-
108
- # class TestFormatTable < Minitest::Test
109
- # def test_basic_formatting
110
- # lines = [
111
- # '| Species| Genus| Family',
112
- # '|-|-|-',
113
- # '| Pongo tapanuliensis| Pongo| Hominidae',
114
- # '| | Histiophryne| Antennariidae'
115
- # ]
116
- # column_count = 3
117
- # expected = [
118
- # '| Species | Genus | Family |',
119
- # '| ------------------- | ------------ | ------------- |',
120
- # '| Pongo tapanuliensis | Pongo | Hominidae |',
121
- # '| | Histiophryne | Antennariidae |'
122
- # ]
123
- # assert_equal expected, MarkdownTableFormatter.format_table(
124
- # lines: lines,
125
- # column_count: column_count
126
- # )
127
- # end
128
-
129
- # def test_missing_column_count
130
- # lines = [
131
- # '| A| B| C',
132
- # '| 1| 2',
133
- # '| X'
134
- # ]
135
- # column_count = 3
136
- # expected = [
137
- # '| A | B | C |',
138
- # '| 1 | 2 | |',
139
- # '| X | | |'
140
- # ]
141
- # assert_equal expected, MarkdownTableFormatter.format_table(
142
- # lines: lines,
143
- # column_count: column_count
144
- # )
145
- # end
146
-
147
- # # def test_extra_column_count
148
- # # lines = [
149
- # # "| A| B| C| D",
150
- # # "| 1| 2| 3| 4| 5"
151
- # # ]
152
- # # column_count = 3
153
- # # expected = [
154
- # # "| A | B | C ",
155
- # # "| 1 | 2 | 3 "
156
- # # ]
157
- # # assert_equal expected, MarkdownTableFormatter.format_table(lines, column_count)
158
- # # end
159
-
160
- # def test_empty_input
161
- # assert_equal [], MarkdownTableFormatter.format_table(
162
- # lines: [],
163
- # column_count: 3
164
- # )
165
- # end
166
-
167
- # def test_single_column
168
- # lines = [
169
- # '| A',
170
- # '| Longer text',
171
- # '| Short'
172
- # ]
173
- # column_count = 1
174
- # expected = [
175
- # '| A |',
176
- # '| Longer text |',
177
- # '| Short |'
178
- # ]
179
- # assert_equal expected, MarkdownTableFormatter.format_table(
180
- # lines: lines,
181
- # column_count: column_count
182
- # )
183
- # end
184
-
185
- # def test_no_pipe_at_end
186
- # lines = [
187
- # '| A| B| C',
188
- # '| 1| 2| 3'
189
- # ]
190
- # column_count = 3
191
- # expected = [
192
- # '| A | B | C |',
193
- # '| 1 | 2 | 3 |'
194
- # ]
195
- # assert_equal expected, MarkdownTableFormatter.format_table(
196
- # lines: lines,
197
- # column_count: column_count
198
- # )
199
- # end
200
- # end
201
-
202
- # class TestFormatTable2 < Minitest::Test
203
- # def test_basic_formatting
204
- # lines = [
205
- # '| Name | Age | City |',
206
- # '| John | 30 | New York |',
207
- # '| Jane | 25 | Los Angeles |'
208
- # ]
209
- # expected_output = [
210
- # '| Name | Age | City |',
211
- # '| John | 30 | New York |',
212
- # '| Jane | 25 | Los Angeles |'
213
- # ]
214
- # assert_equal expected_output, MarkdownTableFormatter.format_table(
215
- # lines: lines,
216
- # column_count: 3
217
- # )
218
- # end
219
-
220
- # def test_incomplete_column_count
221
- # lines = [
222
- # '| Name | Age |',
223
- # '| John | 30 | New York |',
224
- # '| Jane | 25 | Los Angeles |'
225
- # ]
226
- # expected_output = [
227
- # '| Name | Age | |',
228
- # '| John | 30 | New York |',
229
- # '| Jane | 25 | Los Angeles |'
230
- # ]
231
- # assert_equal expected_output, MarkdownTableFormatter.format_table(
232
- # lines: lines,
233
- # column_count: 3
234
- # )
235
- # end
236
-
237
- # def test_extra_column_count
238
- # lines = [
239
- # '| Name | Age | City | Country |',
240
- # '| John | 30 | New York | USA |',
241
- # '| Jane | 25 | Los Angeles | USA |'
242
- # ]
243
- # expected_output = [
244
- # '| Name | Age | City | Country |',
245
- # '| John | 30 | New York | USA |',
246
- # '| Jane | 25 | Los Angeles | USA |'
247
- # ]
248
- # assert_equal expected_output, MarkdownTableFormatter.format_table(
249
- # lines: lines,
250
- # column_count: 4
251
- # )
252
- # end
253
-
254
- # def test_varied_column_lengths
255
- # lines = [
256
- # '| Name | Age |',
257
- # '| Johnathan | 30 | New York |',
258
- # '| Jane | 25 | LA |'
259
- # ]
260
- # expected_output = [
261
- # '| Name | Age | |',
262
- # '| Johnathan | 30 | New York |',
263
- # '| Jane | 25 | LA |'
264
- # ]
265
- # assert_equal expected_output, MarkdownTableFormatter.format_table(
266
- # lines: lines,
267
- # column_count: 3
268
- # )
269
- # end
270
-
271
- # def test_single_line
272
- # lines = ['| Name | Age | City |']
273
- # expected_output = ['| Name | Age | City |']
274
- # assert_equal expected_output, MarkdownTableFormatter.format_table(
275
- # lines: lines,
276
- # column_count: 3
277
- # )
278
- # end
279
-
280
- # def test_empty_lines
281
- # lines = []
282
- # expected_output = []
283
- # assert_equal expected_output, MarkdownTableFormatter.format_table(
284
- # lines: lines,
285
- # column_count: 3
286
- # )
287
- # end
288
-
289
- # def test_complete_rows
290
- # lines = [
291
- # '| Name | Age |',
292
- # '| John | 30 |'
293
- # ]
294
- # expected_output = [
295
- # '| Name | Age |',
296
- # '| John | 30 |'
297
- # ]
298
- # assert_equal expected_output, MarkdownTableFormatter.format_table(
299
- # lines: lines,
300
- # column_count: 3
301
- # )
302
- # end
303
- # end
@@ -1,23 +0,0 @@
1
- require 'yaml'
2
- require 'terminal-table'
3
-
4
- cmd = 'aws ce get-cost-and-usage --time-period Start=2023-12-01,End=2023-12-31 --granularity MONTHLY --metrics "UnblendedCost" "UsageQuantity" --group-by Type=DIMENSION,Key=SERVICE'
5
-
6
- # Parse the YAML content
7
- text = system(cmd)
8
- data = YAML.load(text)
9
-
10
- # Extracting the relevant information
11
- services = data['ResultsByTime'][0]['Groups'].map do |group|
12
- service_name = group['Keys'][0]
13
- unblended_cost = "#{group['Metrics']['UnblendedCost']['Amount']} #{group['Metrics']['UnblendedCost']['Unit']}"
14
- usage_quantity = "#{group['Metrics']['UsageQuantity']['Amount']} #{group['Metrics']['UsageQuantity']['Unit']}"
15
- [service_name, unblended_cost, usage_quantity]
16
- end
17
-
18
- # Create a table
19
- table = Terminal::Table.new headings: ['Service', 'Unblended Cost', 'Usage Quantity'],
20
- rows: services
21
-
22
- # Output the table
23
- puts table
data/lib/doh.rb DELETED
@@ -1,190 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # encoding=utf-8
5
-
6
- class DOH < Hash
7
- # Initialize with an optional hash and a block
8
- def initialize(initial_hash = {}, &block)
9
- super() # Initialize the parent Hash
10
- self.merge!(initial_hash) # Merge the initial hash into the current hash
11
- self.default_proc = block if block_given?
12
- end
13
-
14
- # Override method_missing to pass undefined methods to the parent Hash
15
- def method_missing(method_name, *args, &block)
16
- # sc = caller[0].split; pp ['DOH.mm',method_name,sc[-1],sc[-2].split('/')[-1].split(':')[0..1].join(':')]
17
- if Hash.instance_methods.include?(method_name)
18
- super
19
- else
20
- raise NoMethodError, "undefined method `#{method_name}` for #{self}:#{self.class}"
21
- end
22
- end
23
-
24
- # Ensure respond_to_missing? is correctly implemented
25
- def respond_to_missing?(method_name, include_private = false)
26
- # sc = caller[0].split; pp ['DOH.rtm?',method_name,sc[-1],sc[-2].split('/')[-1].split(':')[0..1].join(':')]
27
- Hash.instance_methods.include?(method_name) || super
28
- end
29
- end
30
-
31
- # class DOH < Hash
32
- # # Initialize with an optional hash and a block
33
- # def initialize(initial_hash = {}, &block)
34
- # super() # Initialize the parent Hash
35
- # self.merge!(initial_hash) # Merge the initial hash into the current hash
36
- # self.default_proc = block if block_given?
37
- # end
38
-
39
- # # Override method_missing to pass undefined methods to the parent Hash
40
- # def method_missing(method_name, *args, &block)
41
- # # pp ['DOH.mm',method_name]
42
- # if self.respond_to?(method_name)
43
- # self.send(method_name, *args, &block)
44
- # else
45
- # super
46
- # end
47
- # end
48
-
49
- # # Ensure respond_to_missing? is correctly implemented
50
- # # pp ['DOH.rtm?',method_name]
51
-
52
- # def respond_to_missing?(method_name, include_private = false)
53
- # self.respond_to?(method_name) || super
54
- # end
55
- # end
56
-
57
- # class LayeredHash
58
- # def initialize(table = {}, layers: %i[main])
59
- # @layers = layers.map { |layer| [layer, {}] }.to_h
60
- # @current_layer = layers.first
61
- # @layers[@current_layer] = table
62
- # end
63
-
64
- # private
65
-
66
- # def method_missing(method, *args, &block)
67
- # method_name = method.to_s
68
- # if @layers.respond_to?(method_name)
69
- # @layers.send(method_name, *args, &block)
70
- # elsif method_name[-1] == '='
71
- # @layers[method_name.chop.to_sym] = args[0]
72
- # elsif @layers.respond_to?(method_name)
73
- # @layers.send(method_name, *args)
74
- # else
75
- # @layers[method_name.to_sym]
76
- # end
77
- # rescue StandardError => err
78
- # warn("ERROR ** LayeredHash.method_missing(method: #{method_name}," \
79
- # " *args: #{args.inspect}, &block)")
80
- # warn err.inspect
81
- # warn(caller[0..4])
82
- # raise err
83
- # end
84
-
85
- # public
86
-
87
- # # Retrieves the value of a key from the current layer using hash notation
88
- # def [](key)
89
- # raise "Current layer not set" unless @current_layer
90
-
91
- # get_from_layer(@current_layer, key)
92
- # end
93
-
94
- # # Sets a key-value pair in the current layer using hash notation
95
- # def []=(key, value)
96
- # raise "Current layer not set" unless @current_layer
97
-
98
- # set(@current_layer, key, value)
99
- # end
100
-
101
- # def fetch(key, *args)
102
- # key_sym = key.to_sym
103
- # if respond_to?("get_#{key}")
104
- # send("get_#{key}")
105
- # # elsif @table.key?(key_sym)
106
- # elsif @layers[@current_layer].key?(key_sym)
107
- # # @table[key_sym]
108
- # @layers[@current_layer][key_sym]
109
- # elsif block_given?
110
- # yield key_sym
111
- # elsif args.count.positive?
112
- # # binding.irb
113
- # args.first
114
- # else
115
- # binding.irb
116
- # raise KeyError, "key not found: #{key}"
117
- # end.tap{|ret| pp([__LINE__,"Poly.fetch #{key} #{args}",'->',ret]) if $pd }
118
- # end
119
-
120
- # # Retrieves the value of a key from the highest priority layer that has a value
121
- # def get(key)
122
- # @layers.reverse_each do |_, hash|
123
- # return hash[key] if hash.key?(key)
124
- # end
125
- # nil
126
- # end
127
-
128
- # # Retrieves the value of a key from the specified layer
129
- # def get_from_layer(layer, key)
130
- # if @layers.key?(layer)
131
- # @layers[layer][key]
132
- # else
133
- # raise ArgumentError, "Layer #{layer} does not exist"
134
- # end
135
- # end
136
-
137
- # def merge(*args)
138
- # @layers.merge(*args).tap{|ret| pp([__LINE__,"LayeredHash.merge",'->',ret]) if $pd }
139
- # end
140
-
141
- # def respond_to_missing?(method_name, include_private = false)
142
- # @layers.key?(method_name.to_sym) || super
143
- # end
144
-
145
- # # Sets a key-value pair in the specified layer
146
- # def set(layer, key, value)
147
- # if @layers.key?(layer)
148
- # @layers[layer][key] = value
149
- # else
150
- # raise ArgumentError, "Layer #{layer} does not exist"
151
- # end
152
- # end
153
-
154
- # # Sets the current layer for use with hash notation ([])
155
- # def set_current_layer(layer)
156
- # if @layers.key?(layer)
157
- # @current_layer = layer
158
- # else
159
- # raise ArgumentError, "Layer #{layer} does not exist"
160
- # end
161
- # end
162
-
163
- # def to_h
164
- # @layers.to_h
165
- # end
166
- # end
167
-
168
- return if $PROGRAM_NAME != __FILE__
169
-
170
- # layered_hash = LayeredHash.new(layers: %i[low high])
171
-
172
- # # Set current layer
173
- # layered_hash.set_current_layer(:low)
174
-
175
- # # Set values in the current layer using hash notation
176
- # layered_hash[:key1] = 'low_value'
177
- # layered_hash[:key2] = 'low_only_value'
178
-
179
- # # Switch current layer
180
- # layered_hash.set_current_layer(:high)
181
-
182
- # # Set values in the new current layer using hash notation
183
- # layered_hash[:key1] = 'high_value'
184
-
185
- # # Get value from the specific current layer using hash notation
186
- # puts layered_hash[:key1] # Output: 'high_value'
187
-
188
- # # Get value from the highest priority layer
189
- # puts layered_hash.get(:key1) # Output: 'high_value'
190
- # puts layered_hash.get(:key2) # Output: 'low_only_value'
data/lib/layered_hash.rb DELETED
@@ -1,143 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # encoding=utf-8
5
-
6
- class LayeredHash
7
- def initialize(table = {}, layers: %i[main])
8
- @layers = layers.map { |layer| [layer, {}] }.to_h
9
- @current_layer = layers.first
10
- @layers[@current_layer] = table
11
- end
12
-
13
- private
14
-
15
- def method_missing(method, *args, &block)
16
- method_name = method.to_s
17
- if @layers.respond_to?(method_name)
18
- @layers.send(method_name, *args, &block)
19
- elsif method_name[-1] == '='
20
- @layers[method_name.chop.to_sym] = args[0]
21
- elsif @layers.respond_to?(method_name)
22
- @layers.send(method_name, *args)
23
- else
24
- @layers[method_name.to_sym]
25
- end
26
- rescue StandardError => err
27
- warn("ERROR ** LayeredHash.method_missing(method: #{method_name}," \
28
- " *args: #{args.inspect}, &block)")
29
- warn err.inspect
30
- warn(caller[0..4])
31
- raise err
32
- end
33
-
34
- public
35
-
36
- # Retrieves the value of a key from the current layer using hash notation
37
- def [](key)
38
- raise "Current layer not set" unless @current_layer
39
-
40
- get_from_layer(@current_layer, key)
41
- end
42
-
43
- # Sets a key-value pair in the current layer using hash notation
44
- def []=(key, value)
45
- raise "Current layer not set" unless @current_layer
46
-
47
- set(@current_layer, key, value)
48
- end
49
-
50
- def fetch(key, *args)
51
- key_sym = key.to_sym
52
- if respond_to?("get_#{key}")
53
- send("get_#{key}")
54
- # elsif @table.key?(key_sym)
55
- elsif @layers[@current_layer].key?(key_sym)
56
- # @table[key_sym]
57
- @layers[@current_layer][key_sym]
58
- elsif block_given?
59
- yield key_sym
60
- elsif args.count.positive?
61
- args.first
62
- else
63
- binding.irb
64
- raise KeyError, "key not found: #{key}"
65
- end.tap { |ret|
66
- pp([__LINE__, "Poly.fetch #{key} #{args}", '->',
67
- ret]) if $pd
68
- }
69
- end
70
-
71
- # Retrieves the value of a key from the highest priority layer that has a value
72
- def get(key)
73
- @layers.reverse_each do |_, hash|
74
- return hash[key] if hash.key?(key)
75
- end
76
- nil
77
- end
78
-
79
- # Retrieves the value of a key from the specified layer
80
- def get_from_layer(layer, key)
81
- if @layers.key?(layer)
82
- @layers[layer][key]
83
- else
84
- raise ArgumentError, "Layer #{layer} does not exist"
85
- end
86
- end
87
-
88
- def merge(*args)
89
- @layers.merge(*args).tap { |ret|
90
- pp([__LINE__, "LayeredHash.merge", '->', ret]) if $pd
91
- }
92
- end
93
-
94
- def respond_to_missing?(method_name, include_private = false)
95
- @layers.key?(method_name.to_sym) || super
96
- end
97
-
98
- # Sets a key-value pair in the specified layer
99
- def set(layer, key, value)
100
- if @layers.key?(layer)
101
- @layers[layer][key] = value
102
- else
103
- raise ArgumentError, "Layer #{layer} does not exist"
104
- end
105
- end
106
-
107
- # Sets the current layer for use with hash notation ([])
108
- def set_current_layer(layer)
109
- if @layers.key?(layer)
110
- @current_layer = layer
111
- else
112
- raise ArgumentError, "Layer #{layer} does not exist"
113
- end
114
- end
115
-
116
- def to_h
117
- @layers.to_h
118
- end
119
- end
120
-
121
- return if $PROGRAM_NAME != __FILE__
122
-
123
- layered_hash = LayeredHash.new(layers: %i[low high])
124
-
125
- # Set current layer
126
- layered_hash.set_current_layer(:low)
127
-
128
- # Set values in the current layer using hash notation
129
- layered_hash[:key1] = 'low_value'
130
- layered_hash[:key2] = 'low_only_value'
131
-
132
- # Switch current layer
133
- layered_hash.set_current_layer(:high)
134
-
135
- # Set values in the new current layer using hash notation
136
- layered_hash[:key1] = 'high_value'
137
-
138
- # Get value from the specific current layer using hash notation
139
- puts layered_hash[:key1] # Output: 'high_value'
140
-
141
- # Get value from the highest priority layer
142
- puts layered_hash.get(:key1) # Output: 'high_value'
143
- puts layered_hash.get(:key2) # Output: 'low_only_value'