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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/Gemfile.lock +1 -1
  4. data/Rakefile +33 -23
  5. data/bats/{block-types.bats → block-type-bash.bats} +0 -25
  6. data/bats/block-type-link.bats +9 -0
  7. data/bats/block-type-port.bats +16 -0
  8. data/bats/block-type-ux-allowed.bats +29 -0
  9. data/bats/block-type-ux-auto.bats +1 -1
  10. data/bats/block-type-ux-chained.bats +9 -0
  11. data/bats/block-type-ux-echo-hash.bats +14 -0
  12. data/bats/block-type-ux-echo.bats +2 -2
  13. data/bats/block-type-ux-exec.bats +1 -1
  14. data/bats/block-type-ux-hidden.bats +9 -0
  15. data/bats/block-type-ux-invalid.bats +8 -0
  16. data/bats/block-type-ux-transform.bats +1 -1
  17. data/bats/indented-block-type-vars.bats +9 -0
  18. data/bats/line-decor-dynamic.bats +1 -1
  19. data/bats/test_helper.bash +9 -2
  20. data/bats/variable-expansion-multiline.bats +8 -0
  21. data/bats/variable-expansion.bats +1 -1
  22. data/docs/dev/block-type-ux-allowed.md +80 -0
  23. data/docs/dev/block-type-ux-chained.md +21 -0
  24. data/docs/dev/block-type-ux-echo-hash.md +72 -0
  25. data/docs/dev/block-type-ux-hidden.md +21 -0
  26. data/docs/dev/block-type-ux-invalid.md +5 -0
  27. data/docs/dev/indented-block-type-vars.md +6 -0
  28. data/docs/dev/line-decor-dynamic.md +2 -1
  29. data/docs/dev/variable-expansion-multiline.md +31 -0
  30. data/lib/ansi_formatter.rb +0 -1
  31. data/lib/ansi_string.rb +1 -1
  32. data/lib/array.rb +0 -1
  33. data/lib/array_util.rb +0 -1
  34. data/lib/block_label.rb +1 -1
  35. data/lib/cached_nested_file_reader.rb +1 -1
  36. data/lib/constants.rb +18 -0
  37. data/lib/directory_searcher.rb +1 -1
  38. data/lib/exceptions.rb +0 -1
  39. data/lib/fcb.rb +36 -5
  40. data/lib/filter.rb +1 -2
  41. data/lib/format_table.rb +1 -0
  42. data/lib/fout.rb +1 -1
  43. data/lib/hash.rb +0 -1
  44. data/lib/hash_delegator.rb +310 -162
  45. data/lib/link_history.rb +17 -17
  46. data/lib/logged_struct.rb +429 -0
  47. data/lib/markdown_exec/version.rb +1 -1
  48. data/lib/markdown_exec.rb +3 -3
  49. data/lib/mdoc.rb +5 -17
  50. data/lib/menu.src.yml +3 -1
  51. data/lib/menu.yml +1 -1
  52. data/lib/namer.rb +4 -5
  53. data/lib/null_result.rb +131 -0
  54. data/lib/object_present.rb +1 -1
  55. data/lib/option_value.rb +1 -1
  56. data/lib/resize_terminal.rb +1 -2
  57. data/lib/saved_assets.rb +1 -1
  58. data/lib/saved_files_matcher.rb +1 -1
  59. data/lib/shell_session.rb +439 -0
  60. data/lib/streams_out.rb +0 -1
  61. data/lib/string_util.rb +11 -1
  62. data/lib/success_result.rb +112 -0
  63. data/lib/text_analyzer.rb +1 -0
  64. data/lib/ww.rb +9 -7
  65. 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 { |ret|
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 { |ret|
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 { |ret|
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 { |ret|
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 { |ret|
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 |line|
86
- yield line
87
- end.tap { |ret| pp ['LinkState.inherited_lines_map() ->', ret] if $pd }
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 { |ret|
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
@@ -7,5 +7,5 @@ module MarkdownExec
7
7
  BIN_NAME = 'mde'
8
8
  GEM_NAME = 'markdown_exec'
9
9
  TAP_DEBUG = 'MDE_DEBUG'
10
- VERSION = '2.8.3'
10
+ VERSION = '2.8.4'
11
11
  end
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
- # 2024-08-04 match nickname
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
- # 2024-08-04 match oname for long block names
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.nickname == name ||
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
- OpenStruct.new(nickname: nil, **row)
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
- :default: "^(?<indent>[ \t]*)(?<line>(?!/)(?<text>.*?)(?<trailing>[ \t]*))?$"
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: "^(?<indent>[ \t]*)(?<line>(?!/)(?<text>.*?)(?<trailing>[ \t]*))?$"
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 { |ret|
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 { |ret|
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