markdown_exec 2.8.3 → 2.8.4
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 +26 -0
- data/Gemfile.lock +1 -1
- data/Rakefile +33 -23
- data/bats/{block-types.bats → block-type-bash.bats} +0 -25
- data/bats/block-type-link.bats +9 -0
- data/bats/block-type-port.bats +16 -0
- data/bats/block-type-ux-allowed.bats +29 -0
- data/bats/block-type-ux-auto.bats +1 -1
- data/bats/block-type-ux-chained.bats +9 -0
- data/bats/block-type-ux-echo-hash.bats +14 -0
- data/bats/block-type-ux-echo.bats +2 -2
- data/bats/block-type-ux-exec.bats +1 -1
- data/bats/block-type-ux-hidden.bats +9 -0
- data/bats/block-type-ux-invalid.bats +8 -0
- data/bats/block-type-ux-transform.bats +1 -1
- data/bats/indented-block-type-vars.bats +9 -0
- data/bats/line-decor-dynamic.bats +1 -1
- data/bats/test_helper.bash +9 -2
- data/bats/variable-expansion-multiline.bats +8 -0
- data/bats/variable-expansion.bats +1 -1
- data/docs/dev/block-type-ux-allowed.md +80 -0
- data/docs/dev/block-type-ux-chained.md +21 -0
- data/docs/dev/block-type-ux-echo-hash.md +72 -0
- data/docs/dev/block-type-ux-hidden.md +21 -0
- data/docs/dev/block-type-ux-invalid.md +5 -0
- data/docs/dev/indented-block-type-vars.md +6 -0
- data/docs/dev/line-decor-dynamic.md +2 -1
- data/docs/dev/variable-expansion-multiline.md +31 -0
- data/lib/ansi_formatter.rb +0 -1
- data/lib/ansi_string.rb +1 -1
- data/lib/array.rb +0 -1
- data/lib/array_util.rb +0 -1
- data/lib/block_label.rb +1 -1
- data/lib/cached_nested_file_reader.rb +1 -1
- data/lib/constants.rb +18 -0
- data/lib/directory_searcher.rb +1 -1
- data/lib/exceptions.rb +0 -1
- data/lib/fcb.rb +36 -5
- data/lib/filter.rb +1 -2
- data/lib/format_table.rb +1 -0
- data/lib/fout.rb +1 -1
- data/lib/hash.rb +0 -1
- data/lib/hash_delegator.rb +310 -162
- data/lib/link_history.rb +17 -17
- data/lib/logged_struct.rb +429 -0
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +3 -3
- data/lib/mdoc.rb +5 -17
- data/lib/menu.src.yml +3 -1
- data/lib/menu.yml +1 -1
- data/lib/namer.rb +4 -5
- data/lib/null_result.rb +131 -0
- data/lib/object_present.rb +1 -1
- data/lib/option_value.rb +1 -1
- data/lib/resize_terminal.rb +1 -2
- data/lib/saved_assets.rb +1 -1
- data/lib/saved_files_matcher.rb +1 -1
- data/lib/shell_session.rb +439 -0
- data/lib/streams_out.rb +0 -1
- data/lib/string_util.rb +11 -1
- data/lib/success_result.rb +112 -0
- data/lib/text_analyzer.rb +1 -0
- data/lib/ww.rb +9 -7
- metadata +23 -3
data/lib/link_history.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
1
|
+
#!/usr/bin/env -S bundle exec ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# encoding=utf-8
|
@@ -52,45 +52,45 @@ module MarkdownExec
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def inherited_lines
|
55
|
-
@inherited_lines.tap
|
55
|
+
@inherited_lines.tap do |ret|
|
56
56
|
pp ['LinkState.inherited_lines() ->', ret] if $pd
|
57
|
-
|
57
|
+
end
|
58
58
|
end
|
59
59
|
|
60
60
|
def inherited_lines=(value)
|
61
|
-
@inherited_lines = value.tap
|
61
|
+
@inherited_lines = value.tap do |ret|
|
62
62
|
pp ['LinkState.inherited_lines=() ->', ret] if $pd
|
63
|
-
|
63
|
+
end
|
64
64
|
end
|
65
65
|
|
66
66
|
def inherited_lines_append(value)
|
67
|
-
@inherited_lines = ((@inherited_lines || []) + value).tap
|
67
|
+
@inherited_lines = ((@inherited_lines || []) + value).tap do |ret|
|
68
68
|
pp ['LinkState.inherited_lines_append() ->', ret] if $pd
|
69
|
-
|
69
|
+
end
|
70
70
|
end
|
71
71
|
|
72
72
|
def inherited_lines_block
|
73
|
-
(@inherited_lines || []).join("\n").tap
|
73
|
+
(@inherited_lines || []).join("\n").tap do |ret|
|
74
74
|
pp ['LinkState.inherited_lines_block() ->', ret] if $pd
|
75
|
-
|
75
|
+
end
|
76
76
|
end
|
77
77
|
|
78
78
|
def inherited_lines_count
|
79
|
-
(@inherited_lines&.count || 0).tap
|
79
|
+
(@inherited_lines&.count || 0).tap do |ret|
|
80
80
|
pp ['LinkState.inherited_lines_count() ->', ret] if $pd
|
81
|
-
|
81
|
+
end
|
82
82
|
end
|
83
83
|
|
84
|
-
def inherited_lines_map
|
85
|
-
@inherited_lines.map do |
|
86
|
-
|
87
|
-
end
|
84
|
+
def inherited_lines_map(&block)
|
85
|
+
@inherited_lines.map(&block).tap do |ret|
|
86
|
+
pp ['LinkState.inherited_lines_map() ->', ret] if $pd
|
87
|
+
end
|
88
88
|
end
|
89
89
|
|
90
90
|
def inherited_lines_present?
|
91
|
-
@inherited_lines.present?.tap
|
91
|
+
@inherited_lines.present?.tap do |ret|
|
92
92
|
pp ['LinkState.inherited_lines_present?() ->', ret] if $pd
|
93
|
-
|
93
|
+
end
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
@@ -0,0 +1,429 @@
|
|
1
|
+
#!/usr/bin/env -S bundle exec ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# encoding=utf-8
|
5
|
+
|
6
|
+
require 'logger'
|
7
|
+
require 'ostruct'
|
8
|
+
require 'stringio'
|
9
|
+
|
10
|
+
# LoggedStruct behaves like OpenStruct but logs all get and set operations
|
11
|
+
# on its attributes, providing visibility into how the object is used.
|
12
|
+
class LoggedStruct < OpenStruct
|
13
|
+
attr_reader :logger, :log_buffer
|
14
|
+
|
15
|
+
# Initializes a new LoggedStruct.
|
16
|
+
# @param hash [Hash] initial values.
|
17
|
+
# @param logger [Logger] logger instance; one is created if not provided.
|
18
|
+
# @param log_level [Symbol] logging level (:debug, :info, etc.).
|
19
|
+
def initialize(hash = nil, logger: nil, log_level: :debug)
|
20
|
+
@log_buffer = StringIO.new
|
21
|
+
@custom_log_level = log_level.to_sym
|
22
|
+
@logger = logger || Logger.new($stdout)
|
23
|
+
@logger.level = Logger.const_get(@custom_log_level.to_s.upcase)
|
24
|
+
@logger.formatter ||= proc { |severity, datetime, progname, msg| "#{msg}\n" }
|
25
|
+
|
26
|
+
super({})
|
27
|
+
|
28
|
+
(hash || {}).each { |key, value| self[key] = value }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Logs attribute retrieval.
|
32
|
+
def [](name)
|
33
|
+
value = super
|
34
|
+
log_get_operation(name.to_s, value)
|
35
|
+
value
|
36
|
+
end
|
37
|
+
|
38
|
+
# Logs attribute setting.
|
39
|
+
def []=(name, value)
|
40
|
+
log_set_operation(name.to_s, value)
|
41
|
+
super
|
42
|
+
ensure_logging_methods(name.to_sym)
|
43
|
+
value
|
44
|
+
end
|
45
|
+
|
46
|
+
# Overrides OpenStruct’s member creation to install logging methods.
|
47
|
+
def new_ostruct_member(name)
|
48
|
+
name = name.to_sym
|
49
|
+
result = super
|
50
|
+
ensure_logging_methods(name)
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
# Installs logging versions of getters and setters.
|
55
|
+
# @param name [Symbol] attribute name.
|
56
|
+
def ensure_logging_methods(name)
|
57
|
+
eigen = singleton_class
|
58
|
+
if eigen.method_defined?(name) && eigen.instance_method(name).owner == eigen
|
59
|
+
eigen.send(:remove_method, name)
|
60
|
+
end
|
61
|
+
if eigen.method_defined?("#{name}=") && eigen.instance_method("#{name}=").owner == eigen
|
62
|
+
eigen.send(:remove_method, "#{name}=")
|
63
|
+
end
|
64
|
+
|
65
|
+
eigen.define_method(name) do
|
66
|
+
value = @table[name]
|
67
|
+
log_get_operation(name.to_s, value)
|
68
|
+
value
|
69
|
+
end
|
70
|
+
|
71
|
+
eigen.define_method("#{name}=") do |value|
|
72
|
+
log_set_operation(name.to_s, value)
|
73
|
+
@table[name] = value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Delegates missing method calls.
|
78
|
+
def method_missing(method, *args, &block)
|
79
|
+
method_name = method.to_s
|
80
|
+
if method_name.end_with?('=')
|
81
|
+
attribute = method_name.chop
|
82
|
+
self[attribute] = args.first
|
83
|
+
else
|
84
|
+
# If arguments are provided, delegate to super to raise NoMethodError.
|
85
|
+
if args.empty?
|
86
|
+
self[method_name]
|
87
|
+
else
|
88
|
+
super
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Checks if a method exists.
|
94
|
+
def respond_to_missing?(method, include_private = false)
|
95
|
+
method_name = method.to_s
|
96
|
+
return true if method_name.end_with?('=')
|
97
|
+
@table.key?(method) || super
|
98
|
+
end
|
99
|
+
|
100
|
+
# Logs conversion to a hash.
|
101
|
+
def to_h
|
102
|
+
hash = super
|
103
|
+
log_message("Converting LoggedStruct to Hash: #{hash.inspect}")
|
104
|
+
hash
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns the internal log buffer.
|
108
|
+
def log_content
|
109
|
+
@log_buffer.string
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def log_get_operation(attribute, value)
|
115
|
+
message = "GET: #{attribute} => #{value.inspect}"
|
116
|
+
@logger.send(@custom_log_level, message)
|
117
|
+
formatted = @logger.formatter.call(@custom_log_level.to_s.upcase, Time.now, nil, message)
|
118
|
+
@log_buffer.puts(formatted.chomp)
|
119
|
+
end
|
120
|
+
|
121
|
+
def log_set_operation(attribute, value)
|
122
|
+
message = "SET: #{attribute} = #{value.inspect}"
|
123
|
+
@logger.send(@custom_log_level, message)
|
124
|
+
formatted = @logger.formatter.call(@custom_log_level.to_s.upcase, Time.now, nil, message)
|
125
|
+
@log_buffer.puts(formatted.chomp)
|
126
|
+
end
|
127
|
+
|
128
|
+
def log_message(message)
|
129
|
+
@logger.send(@custom_log_level, message)
|
130
|
+
formatted = @logger.formatter.call(@custom_log_level.to_s.upcase, Time.now, nil, message)
|
131
|
+
@log_buffer.puts(formatted.chomp)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
return unless $PROGRAM_NAME == __FILE__
|
136
|
+
|
137
|
+
require 'bundler/setup'
|
138
|
+
Bundler.require(:default)
|
139
|
+
|
140
|
+
require 'minitest/autorun'
|
141
|
+
require 'mocha/minitest'
|
142
|
+
|
143
|
+
require_relative 'ww'
|
144
|
+
|
145
|
+
require 'stringio'
|
146
|
+
|
147
|
+
class LoggedStructTest < Minitest::Test
|
148
|
+
def setup
|
149
|
+
@log_output = StringIO.new
|
150
|
+
@logger = Logger.new(@log_output)
|
151
|
+
@logger.level = Logger::DEBUG
|
152
|
+
@logger.formatter = proc { |_, _, _, msg| "#{msg}\n" }
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_initialization_with_hash
|
156
|
+
struct = LoggedStruct.new({ name: 'John', age: 30 }, logger: @logger)
|
157
|
+
|
158
|
+
name = struct.name
|
159
|
+
assert_equal 'John', name
|
160
|
+
assert_includes struct.log_content, 'GET: name => "John"'
|
161
|
+
|
162
|
+
age = struct.age
|
163
|
+
assert_equal 30, age
|
164
|
+
assert_includes struct.log_content, 'GET: age => 30'
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_initialization_without_hash
|
168
|
+
struct = LoggedStruct.new(logger: @logger)
|
169
|
+
|
170
|
+
assert_nil struct.name
|
171
|
+
assert_includes struct.log_content, 'GET: name => nil'
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_method_style_getter
|
175
|
+
struct = LoggedStruct.new({ name: 'John' }, logger: @logger)
|
176
|
+
|
177
|
+
result = struct.name
|
178
|
+
|
179
|
+
assert_equal 'John', result
|
180
|
+
assert_includes struct.log_content, 'GET: name => "John"'
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_method_style_setter
|
184
|
+
struct = LoggedStruct.new(logger: @logger)
|
185
|
+
|
186
|
+
struct.name = 'John'
|
187
|
+
assert_includes struct.log_content, 'SET: name = "John"'
|
188
|
+
|
189
|
+
result = struct.name
|
190
|
+
assert_equal 'John', result
|
191
|
+
assert_includes struct.log_content, 'GET: name => "John"'
|
192
|
+
end
|
193
|
+
|
194
|
+
def test_hash_style_getter
|
195
|
+
struct = LoggedStruct.new({ name: 'John' }, logger: @logger)
|
196
|
+
@log_output.truncate(0)
|
197
|
+
@log_output.rewind
|
198
|
+
|
199
|
+
result = struct[:name]
|
200
|
+
|
201
|
+
assert_equal 'John', result
|
202
|
+
assert_includes struct.log_content, 'GET: name => "John"'
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_hash_style_setter
|
206
|
+
struct = LoggedStruct.new(logger: @logger)
|
207
|
+
@log_output.truncate(0)
|
208
|
+
@log_output.rewind
|
209
|
+
|
210
|
+
struct[:name] = 'John'
|
211
|
+
|
212
|
+
assert_equal 'John', struct[:name]
|
213
|
+
assert_includes struct.log_content, 'SET: name = "John"'
|
214
|
+
# The second line should be from the getter assertion
|
215
|
+
assert_includes struct.log_content, 'GET: name => "John"'
|
216
|
+
end
|
217
|
+
|
218
|
+
def test_to_h_method
|
219
|
+
struct = LoggedStruct.new({ name: 'John', age: 30 }, logger: @logger)
|
220
|
+
@log_output.truncate(0)
|
221
|
+
@log_output.rewind
|
222
|
+
|
223
|
+
result = struct.to_h
|
224
|
+
|
225
|
+
assert_equal({ name: 'John', age: 30 }, result)
|
226
|
+
assert_includes struct.log_content, 'Converting LoggedStruct to Hash: {:name=>"John", :age=>30}'
|
227
|
+
end
|
228
|
+
|
229
|
+
def test_custom_log_level
|
230
|
+
logger = Logger.new(@log_output)
|
231
|
+
logger.formatter = proc { |severity, _, _, msg| "#{severity}: #{msg}\n" }
|
232
|
+
|
233
|
+
struct = LoggedStruct.new({ name: 'John' }, logger: logger, log_level: :info)
|
234
|
+
@log_output.truncate(0)
|
235
|
+
@log_output.rewind
|
236
|
+
|
237
|
+
struct.name
|
238
|
+
|
239
|
+
assert_includes struct.log_content, 'INFO: GET: name => "John"'
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_method_missing_for_undefined_method
|
243
|
+
struct = LoggedStruct.new(logger: @logger)
|
244
|
+
|
245
|
+
assert_raises(NoMethodError) do
|
246
|
+
struct.undefined_method('argument')
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def test_nested_values
|
251
|
+
struct = LoggedStruct.new({ user: { name: 'John', age: 30 } }, logger: @logger)
|
252
|
+
@log_output.truncate(0)
|
253
|
+
@log_output.rewind
|
254
|
+
|
255
|
+
result = struct.user
|
256
|
+
|
257
|
+
assert_equal({ name: 'John', age: 30 }, result)
|
258
|
+
assert_includes struct.log_content, 'GET: user => {:name=>"John", :age=>30}'
|
259
|
+
end
|
260
|
+
|
261
|
+
def test_array_values
|
262
|
+
struct = LoggedStruct.new({ tags: ['ruby', 'testing'] }, logger: @logger)
|
263
|
+
@log_output.truncate(0)
|
264
|
+
@log_output.rewind
|
265
|
+
|
266
|
+
result = struct.tags
|
267
|
+
|
268
|
+
assert_equal(['ruby', 'testing'], result)
|
269
|
+
assert_includes struct.log_content, 'GET: tags => ["ruby", "testing"]'
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_boolean_values
|
273
|
+
struct = LoggedStruct.new({ active: true }, logger: @logger)
|
274
|
+
@log_output.truncate(0)
|
275
|
+
@log_output.rewind
|
276
|
+
|
277
|
+
result = struct.active
|
278
|
+
|
279
|
+
assert_equal true, result
|
280
|
+
assert_includes struct.log_content, 'GET: active => true'
|
281
|
+
end
|
282
|
+
|
283
|
+
def test_nil_values
|
284
|
+
struct = LoggedStruct.new({ data: nil }, logger: @logger)
|
285
|
+
@log_output.truncate(0)
|
286
|
+
@log_output.rewind
|
287
|
+
|
288
|
+
result = struct.data
|
289
|
+
|
290
|
+
assert_nil result
|
291
|
+
assert_includes struct.log_content, 'GET: data => nil'
|
292
|
+
end
|
293
|
+
|
294
|
+
def test_numeric_values
|
295
|
+
struct = LoggedStruct.new({ count: 42, price: 99.99 }, logger: @logger)
|
296
|
+
@log_output.truncate(0)
|
297
|
+
@log_output.rewind
|
298
|
+
|
299
|
+
count = struct.count
|
300
|
+
price = struct.price
|
301
|
+
|
302
|
+
assert_equal 42, count
|
303
|
+
assert_equal 99.99, price
|
304
|
+
assert_includes struct.log_content, 'GET: count => 42'
|
305
|
+
assert_includes struct.log_content, 'GET: price => 99.99'
|
306
|
+
end
|
307
|
+
|
308
|
+
def test_debug_logging
|
309
|
+
# Create a struct with direct logging to STDOUT for visibility
|
310
|
+
stdout_logger = Logger.new($stdout)
|
311
|
+
stdout_logger.level = Logger::DEBUG
|
312
|
+
stdout_logger.formatter = proc { |_, _, _, msg| "STDOUT_LOG: #{msg}\n" }
|
313
|
+
|
314
|
+
struct = LoggedStruct.new({ value: 'test' }, logger: stdout_logger)
|
315
|
+
|
316
|
+
# Check the initial state of the log buffer
|
317
|
+
puts "Initial log buffer content: #{struct.log_buffer.string.inspect}"
|
318
|
+
|
319
|
+
# Get a value to trigger logging
|
320
|
+
value = struct.value
|
321
|
+
puts "After get - log buffer: #{struct.log_buffer.string.inspect}"
|
322
|
+
|
323
|
+
# Set a value to trigger logging
|
324
|
+
struct.new_value = 'hello world'
|
325
|
+
puts "After set - log buffer: #{struct.log_buffer.string.inspect}"
|
326
|
+
|
327
|
+
# Make a direct assertion we can verify
|
328
|
+
assert_includes struct.log_buffer.string, "GET: value => \"test\""
|
329
|
+
assert_includes struct.log_buffer.string, "SET: new_value = \"hello world\""
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
__END__
|
334
|
+
|
335
|
+
# ExampleUsage1 demonstrates basic usage with default logger settings.
|
336
|
+
class ExampleUsage1
|
337
|
+
# Runs the basic usage example.
|
338
|
+
def self.run
|
339
|
+
puts "Example 1: Basic Usage with Default Logger"
|
340
|
+
logged = LoggedStruct.new(name: "John", age: 30)
|
341
|
+
# Method-style getter; logs GET operation.
|
342
|
+
puts "Name: #{logged.name}"
|
343
|
+
# Method-style setter; logs SET operation.
|
344
|
+
logged.age = 31
|
345
|
+
# Hash-style setter and getter.
|
346
|
+
logged[:email] = "john@example.com"
|
347
|
+
puts "Email: #{logged[:email]}"
|
348
|
+
# Conversion to hash; logs the conversion.
|
349
|
+
puts "Hash: #{logged.to_h.inspect}"
|
350
|
+
puts "Internal Log Buffer:"
|
351
|
+
puts logged.log_content
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
ExampleUsage1.run
|
356
|
+
|
357
|
+
# ExampleUsage2 demonstrates using a custom logger configured at INFO level.
|
358
|
+
class ExampleUsage2
|
359
|
+
# Runs the custom logger example.
|
360
|
+
def self.run
|
361
|
+
puts "\nExample 2: Custom Logger with INFO Level"
|
362
|
+
custom_logger = Logger.new($stdout)
|
363
|
+
custom_logger.level = Logger::INFO
|
364
|
+
custom_logger.formatter = proc { |severity, _datetime, _progname, msg| "#{severity}: #{msg}\n" }
|
365
|
+
logged = LoggedStruct.new({ count: 0 }, logger: custom_logger, log_level: :info)
|
366
|
+
# Setter call logs at INFO level.
|
367
|
+
logged.count = 1
|
368
|
+
# Getter call logs at INFO level.
|
369
|
+
puts "Count: #{logged.count}"
|
370
|
+
puts "Internal Log Buffer:"
|
371
|
+
puts logged.log_content
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
ExampleUsage2.run
|
376
|
+
|
377
|
+
# ExampleUsage3 demonstrates dynamic attribute creation and proper method_missing behavior.
|
378
|
+
class ExampleUsage3
|
379
|
+
# Runs the dynamic attribute and method_missing example.
|
380
|
+
def self.run
|
381
|
+
puts "\nExample 3: Dynamic Attribute Creation and Method Missing Handling"
|
382
|
+
custom_logger = Logger.new($stdout)
|
383
|
+
custom_logger.level = Logger::INFO
|
384
|
+
custom_logger.formatter = proc { |severity, _datetime, _progname, msg| "#{severity}: #{msg}\n" }
|
385
|
+
logged = LoggedStruct.new(logger: custom_logger, log_level: :info)
|
386
|
+
# Dynamic method call creates a new attribute and logs SET.
|
387
|
+
logged.city = "New York"
|
388
|
+
# Dynamic getter call logs GET.
|
389
|
+
puts "City: #{logged.city}"
|
390
|
+
# Calling an undefined method with arguments triggers NoMethodError.
|
391
|
+
begin
|
392
|
+
logged.undefined_method("argument")
|
393
|
+
rescue NoMethodError => e
|
394
|
+
puts "Caught expected NoMethodError: #{e.message}"
|
395
|
+
end
|
396
|
+
puts "Internal Log Buffer:"
|
397
|
+
puts logged.log_content
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
ExampleUsage3.run
|
402
|
+
|
403
|
+
# ExampleUsage4 demonstrates handling of complex values such as nested hashes, arrays, booleans, numbers, and nil.
|
404
|
+
class ExampleUsage4
|
405
|
+
# Runs the complex values example.
|
406
|
+
def self.run
|
407
|
+
puts "\nExample 4: Handling Complex Data Types"
|
408
|
+
custom_logger = Logger.new($stdout)
|
409
|
+
custom_logger.level = Logger::INFO
|
410
|
+
custom_logger.formatter = proc { |severity, _datetime, _progname, msg| "#{severity}: #{msg}\n" }
|
411
|
+
data = {
|
412
|
+
user: { name: "Alice", age: 28 },
|
413
|
+
tags: ["developer", "blogger"],
|
414
|
+
active: true,
|
415
|
+
score: 98.5,
|
416
|
+
data: nil
|
417
|
+
}
|
418
|
+
logged = LoggedStruct.new(data, logger: custom_logger, log_level: :info)
|
419
|
+
puts "User: #{logged.user}"
|
420
|
+
puts "Tags: #{logged.tags.inspect}"
|
421
|
+
puts "Active: #{logged.active}"
|
422
|
+
puts "Score: #{logged.score}"
|
423
|
+
puts "Data: #{logged.data.inspect}"
|
424
|
+
puts "Internal Log Buffer:"
|
425
|
+
puts logged.log_content
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
ExampleUsage4.run
|
data/lib/markdown_exec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
1
|
+
#!/usr/bin/env -S bundle exec ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# encoding=utf-8
|
@@ -30,10 +30,12 @@ require_relative 'hash_delegator'
|
|
30
30
|
require_relative 'input_sequencer'
|
31
31
|
require_relative 'markdown_exec/version'
|
32
32
|
require_relative 'mdoc'
|
33
|
+
require_relative 'null_result'
|
33
34
|
require_relative 'option_value'
|
34
35
|
require_relative 'saved_assets'
|
35
36
|
require_relative 'saved_files_matcher'
|
36
37
|
require_relative 'shared'
|
38
|
+
require_relative 'success_result'
|
37
39
|
require_relative 'tap'
|
38
40
|
|
39
41
|
include CLI
|
@@ -826,7 +828,6 @@ module MarkdownExec
|
|
826
828
|
def opts_menu_option_append(opts, options, item, options_parsed)
|
827
829
|
return unless item[:long_name].present? || item[:short_name].present?
|
828
830
|
|
829
|
-
optname = "-#{item[:short_name]}"
|
830
831
|
switches = [
|
831
832
|
# - argument style = :NONE, :REQUIRED, :OPTIONAL
|
832
833
|
case item[:procname]&.to_s
|
@@ -840,7 +841,6 @@ module MarkdownExec
|
|
840
841
|
|
841
842
|
# - long name
|
842
843
|
if item[:long_name].present?
|
843
|
-
optname = "--#{item[:long_name]}"
|
844
844
|
"--#{item[:long_name]}" \
|
845
845
|
"#{item[:arg_name].present? ? " #{item[:arg_name]}" : ''}"
|
846
846
|
end,
|
data/lib/mdoc.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
1
|
+
#!/usr/bin/env -S bundle exec ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# encoding=utf-8
|
@@ -92,10 +92,7 @@ module MarkdownExec
|
|
92
92
|
# select blocks in order of appearance in source documents
|
93
93
|
#
|
94
94
|
blocks = @table.select do |fcb|
|
95
|
-
|
96
|
-
all_dependency_names.include?(fcb.pub_name) ||
|
97
|
-
all_dependency_names.include?(fcb.nickname) ||
|
98
|
-
all_dependency_names.include?(fcb.oname)
|
95
|
+
fcb.is_dependency_of?(all_dependency_names)
|
99
96
|
end
|
100
97
|
# !!t blocks.count
|
101
98
|
|
@@ -103,12 +100,7 @@ module MarkdownExec
|
|
103
100
|
#
|
104
101
|
unmet_dependencies = all_dependency_names.dup
|
105
102
|
blocks = blocks.map do |fcb|
|
106
|
-
|
107
|
-
# 2024-08-04 match nickname
|
108
|
-
# may not exist if block name is duplicated
|
109
|
-
unmet_dependencies.delete(fcb.pub_name) ||
|
110
|
-
unmet_dependencies.delete(fcb.nickname) ||
|
111
|
-
unmet_dependencies.delete(fcb.oname)
|
103
|
+
fcb.delete_matching_name!(unmet_dependencies)
|
112
104
|
if (call = fcb.call)
|
113
105
|
fcb1 = get_block_by_anyname("[#{call.match(/^%\((\S+) |\)/)[1]}]")
|
114
106
|
fcb1.cann = call
|
@@ -333,12 +325,8 @@ module MarkdownExec
|
|
333
325
|
# @return [Hash] The code block as a hash or the default value if not found.
|
334
326
|
#
|
335
327
|
def get_block_by_anyname(name, default = {})
|
336
|
-
# !!t name
|
337
328
|
@table.select do |fcb|
|
338
|
-
fcb.
|
339
|
-
fcb.dname == name ||
|
340
|
-
fcb.oname == name ||
|
341
|
-
fcb.pub_name == name
|
329
|
+
fcb.is_named?(name)
|
342
330
|
end.fetch(0, default)
|
343
331
|
end
|
344
332
|
|
@@ -528,7 +516,7 @@ if $PROGRAM_NAME == __FILE__
|
|
528
516
|
{ oname: 'block2', body: ['code for block2'], reqs: nil },
|
529
517
|
{ oname: 'block3', body: ['code for block3'], reqs: ['block1'] }
|
530
518
|
].map do |row|
|
531
|
-
|
519
|
+
FCB.new(nickname: nil, **row)
|
532
520
|
end
|
533
521
|
@doc = MDoc.new(@table)
|
534
522
|
end
|
data/lib/menu.src.yml
CHANGED
@@ -914,7 +914,9 @@
|
|
914
914
|
- :opt_name: menu_note_match
|
915
915
|
:env_var: MDE_MENU_NOTE_MATCH
|
916
916
|
:description: Pattern for notes in block selection menu
|
917
|
-
|
917
|
+
# 'line' excludes 'indent' and 'trailing'
|
918
|
+
# 'text' group matches multiline from variable expansion
|
919
|
+
:default: "^(?'indent'[ \t]*)(?'line'(?!/)(?'text'.*))(?'trailing'[ \t]*)?$"
|
918
920
|
:procname: val_as_str
|
919
921
|
|
920
922
|
- :opt_name: menu_option_back_name
|
data/lib/menu.yml
CHANGED
@@ -772,7 +772,7 @@
|
|
772
772
|
- :opt_name: menu_note_match
|
773
773
|
:env_var: MDE_MENU_NOTE_MATCH
|
774
774
|
:description: Pattern for notes in block selection menu
|
775
|
-
:default: "^(
|
775
|
+
:default: "^(?'indent'[ \t]*)(?'line'(?!/)(?'text'.*))(?'trailing'[ \t]*)?$"
|
776
776
|
:procname: val_as_str
|
777
777
|
- :opt_name: menu_option_back_name
|
778
778
|
:env_var: MDE_MENU_OPTION_BACK_NAME
|
data/lib/namer.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
# encoding=utf-8
|
@@ -10,9 +9,9 @@ class Hash
|
|
10
9
|
# block name in commands and documents
|
11
10
|
def pub_name(**kwargs)
|
12
11
|
full = fetch(:nickname, nil) || fetch(:oname, nil)
|
13
|
-
full&.to_s&.pub_name(**kwargs).tap
|
12
|
+
full&.to_s&.pub_name(**kwargs).tap do |ret|
|
14
13
|
pp [__LINE__, 'Hash.pub_name() ->', ret] if $pd
|
15
|
-
|
14
|
+
end
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
@@ -37,8 +36,8 @@ class String
|
|
37
36
|
self
|
38
37
|
end
|
39
38
|
|
40
|
-
trimmed.gsub(pattern, replacement).tap
|
39
|
+
trimmed.gsub(pattern, replacement).tap do |ret|
|
41
40
|
pp [__LINE__, 'String.pub_name() ->', ret] if $pd
|
42
|
-
|
41
|
+
end
|
43
42
|
end
|
44
43
|
end
|