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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/Gemfile.lock +2 -2
- data/Rakefile +3 -3
- data/bats/block-types.bats +13 -7
- data/bats/import.bats +6 -0
- data/bats/markup.bats +6 -15
- data/bats/options-collapse.bats +26 -0
- data/bats/options.bats +1 -1
- data/bats/table.bats +8 -0
- data/bats/test_helper.bash +74 -49
- data/bats/variable-expansion.bats +46 -0
- data/bin/tab_completion.sh +1 -1
- data/docs/dev/bats-document-configuration.md +8 -1
- data/docs/dev/block-type-bash.md +1 -1
- data/docs/dev/block-type-opts.md +1 -5
- data/docs/dev/block-type-vars.md +4 -0
- data/docs/dev/import-missing.md +2 -0
- data/docs/dev/menu-cli.md +1 -1
- data/docs/dev/options-collapse.md +47 -0
- data/docs/dev/requiring-blocks.md +3 -0
- data/docs/dev/specs.md +2 -1
- data/docs/dev/table-crash.md +39 -0
- data/docs/dev/table-indent.md +26 -0
- data/docs/dev/text-decoration.md +2 -5
- data/docs/dev/variable-expansion.md +2 -4
- data/examples/bash-blocks.md +1 -1
- data/examples/block-names.md +1 -1
- data/examples/block-types.md +1 -1
- data/examples/data-files.md +1 -1
- data/examples/document_options.md +2 -2
- data/examples/indent.md +1 -1
- data/examples/interrupt.md +1 -1
- data/examples/link-blocks-vars.md +1 -1
- data/examples/linked.md +1 -1
- data/examples/linked1.md +1 -1
- data/examples/nickname.md +1 -1
- data/examples/opts-blocks-require.md +1 -1
- data/examples/opts-blocks.md +1 -1
- data/examples/opts_output_execution.md +1 -1
- data/examples/pass-through-arguments.md +1 -1
- data/examples/pause-after-execution.md +1 -1
- data/examples/port-blocks.md +1 -1
- data/examples/save.md +1 -1
- data/examples/text-markup.md +1 -1
- data/examples/variable-expansion.md +6 -2
- data/examples/vars-blocks.md +1 -1
- data/examples/wrap.md +1 -1
- data/lib/block_types.rb +4 -0
- data/lib/cached_nested_file_reader.rb +7 -4
- data/lib/collapser.rb +302 -0
- data/lib/constants.rb +10 -0
- data/lib/evaluate_shell_expressions.rb +0 -3
- data/lib/fcb.rb +13 -17
- data/lib/format_table.rb +11 -7
- data/lib/hash_delegator.rb +461 -272
- data/lib/hierarchy_string.rb +5 -1
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +16 -32
- data/lib/mdoc.rb +100 -35
- data/lib/menu.src.yml +124 -17
- data/lib/menu.yml +102 -16
- data/lib/ww.rb +75 -22
- metadata +12 -9
- data/lib/append_to_bash_history.rb +0 -303
- data/lib/ce_get_cost_and_usage.rb +0 -23
- data/lib/doh.rb +0 -190
- data/lib/layered_hash.rb +0 -143
- 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'
|