markdown_exec 1.8.6 → 1.8.7
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/.pryrc +0 -1
- data/.rubocop.yml +0 -2
- data/CHANGELOG.md +14 -2
- data/Gemfile.lock +1 -1
- data/bin/tab_completion.sh +2 -2
- data/examples/opts.md +2 -0
- data/lib/ansi_formatter.rb +1 -5
- data/lib/filter.rb +15 -3
- data/lib/hash_delegator.rb +440 -494
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/mdoc.rb +33 -12
- data/lib/menu.src.yml +72 -4
- data/lib/menu.yml +62 -5
- data/lib/regexp_replacer.rb +14 -14
- data/lib/saved_assets.rb +10 -8
- metadata +4 -4
data/lib/hash_delegator.rb
CHANGED
@@ -37,6 +37,273 @@ class String
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
+
module HashDelegatorSelf
|
41
|
+
# def add_back_option(menu_blocks)
|
42
|
+
# append_chrome_block(menu_blocks, MenuState::BACK)
|
43
|
+
# end
|
44
|
+
|
45
|
+
# Applies an ANSI color method to a string using a specified color key.
|
46
|
+
# The method retrieves the color method from the provided hash. If the color key
|
47
|
+
# is not present in the hash, it uses a default color method.
|
48
|
+
# @param string [String] The string to be colored.
|
49
|
+
# @param color_methods [Hash] A hash where keys are color names (String/Symbol) and values are color methods.
|
50
|
+
# @param color_key [String, Symbol] The key representing the desired color method in the color_methods hash.
|
51
|
+
# @param default_method [String] (optional) Default color method to use if color_key is not found in color_methods. Defaults to 'plain'.
|
52
|
+
# @return [String] The colored string.
|
53
|
+
def apply_color_from_hash(string, color_methods, color_key, default_method: 'plain')
|
54
|
+
color_method = color_methods.fetch(color_key, default_method).to_sym
|
55
|
+
string.to_s.send(color_method)
|
56
|
+
end
|
57
|
+
|
58
|
+
# # Enhanced `apply_color_from_hash` method to support dynamic color transformations
|
59
|
+
# # @param string [String] The string to be colored.
|
60
|
+
# # @param color_transformations [Hash] A hash mapping color names to lambdas that apply color transformations.
|
61
|
+
# # @param color_key [String, Symbol] The key representing the desired color transformation in the color_transformations hash.
|
62
|
+
# # @param default_transformation [Proc] Default color transformation to use if color_key is not found in color_transformations.
|
63
|
+
# # @return [String] The colored string.
|
64
|
+
# def apply_color_from_hash(string, color_transformations, color_key, default_transformation: ->(str) { str })
|
65
|
+
# transformation = color_transformations.fetch(color_key.to_sym, default_transformation)
|
66
|
+
# transformation.call(string)
|
67
|
+
# end
|
68
|
+
# color_transformations = {
|
69
|
+
# red: ->(str) { "\e[31m#{str}\e[0m" }, # ANSI color code for red
|
70
|
+
# green: ->(str) { "\e[32m#{str}\e[0m" }, # ANSI color code for green
|
71
|
+
# # Add more color transformations as needed
|
72
|
+
# }
|
73
|
+
# string = "Hello, World!"
|
74
|
+
# colored_string = apply_color_from_hash(string, color_transformations, :red)
|
75
|
+
# puts colored_string # This will print the string in red
|
76
|
+
|
77
|
+
# Searches for the first element in a collection where the specified key matches a given value.
|
78
|
+
# This method is particularly useful for finding a specific hash-like object within an enumerable collection.
|
79
|
+
# If no match is found, it returns a specified default value.
|
80
|
+
#
|
81
|
+
# @param blocks [Enumerable] The collection of hash-like objects to search.
|
82
|
+
# @param key [Object] The key to search for in each element of the collection.
|
83
|
+
# @param value [Object] The value to match against each element's corresponding key value.
|
84
|
+
# @param default [Object, nil] The default value to return if no match is found (optional).
|
85
|
+
# @return [Object, nil] The first matching element or the default value if no match is found.
|
86
|
+
def block_find(blocks, key, value, default = nil)
|
87
|
+
blocks.find { |item| item[key] == value } || default
|
88
|
+
end
|
89
|
+
|
90
|
+
def code_merge(*bodies)
|
91
|
+
merge_lists(*bodies)
|
92
|
+
end
|
93
|
+
|
94
|
+
def count_matches_in_lines(lines, regex)
|
95
|
+
lines.count { |line| line.to_s.match(regex) }
|
96
|
+
end
|
97
|
+
|
98
|
+
def create_directory_for_file(file_path)
|
99
|
+
FileUtils.mkdir_p(File.dirname(file_path))
|
100
|
+
end
|
101
|
+
|
102
|
+
# Creates a file at the specified path, writes the given content to it,
|
103
|
+
# and sets file permissions if required. Handles any errors encountered during the process.
|
104
|
+
#
|
105
|
+
# @param file_path [String] The path where the file will be created.
|
106
|
+
# @param content [String] The content to write into the file.
|
107
|
+
# @param chmod_value [Integer] The file permission value to set; skips if zero.
|
108
|
+
def create_file_and_write_string_with_permissions(file_path, content,
|
109
|
+
chmod_value)
|
110
|
+
create_directory_for_file(file_path)
|
111
|
+
File.write(file_path, content)
|
112
|
+
set_file_permissions(file_path, chmod_value) unless chmod_value.zero?
|
113
|
+
rescue StandardError
|
114
|
+
error_handler('create_file_and_write_string_with_permissions')
|
115
|
+
end
|
116
|
+
|
117
|
+
# def create_temp_file
|
118
|
+
# Dir::Tmpname.create(self.class.to_s) { |path| path }
|
119
|
+
# end
|
120
|
+
|
121
|
+
# Updates the title of an FCB object from its body content if the title is nil or empty.
|
122
|
+
def default_block_title_from_body(fcb)
|
123
|
+
return unless fcb.title.nil? || fcb.title.empty?
|
124
|
+
|
125
|
+
fcb.derive_title_from_body
|
126
|
+
end
|
127
|
+
|
128
|
+
# delete the current line if it is empty and the previous is also empty
|
129
|
+
def delete_consecutive_blank_lines!(blocks_menu)
|
130
|
+
blocks_menu.process_and_conditionally_delete! do |prev_item, current_item, _next_item|
|
131
|
+
prev_item&.fetch(:chrome, nil) && !prev_item&.fetch(:oname).present? &&
|
132
|
+
current_item&.fetch(:chrome, nil) && !current_item&.fetch(:oname).present?
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# # Deletes a temporary file specified by an environment variable.
|
137
|
+
# # Checks if the file exists before attempting to delete it and clears the environment variable afterward.
|
138
|
+
# # Any errors encountered during deletion are handled gracefully.
|
139
|
+
# def delete_required_temp_file(temp_blocks_file_path)
|
140
|
+
# return if temp_blocks_file_path.nil? || temp_blocks_file_path.empty?
|
141
|
+
|
142
|
+
# HashDelegator.remove_file_without_standard_errors(temp_blocks_file_path)
|
143
|
+
# end
|
144
|
+
|
145
|
+
def error_handler(name = '', opts = {})
|
146
|
+
Exceptions.error_handler(
|
147
|
+
"HashDelegator.#{name} -- #{$!}",
|
148
|
+
opts
|
149
|
+
)
|
150
|
+
end
|
151
|
+
|
152
|
+
# # DebugHelper.d ["HDmm method_name: #{method_name}", "#{first_n_caller_items 1}"]
|
153
|
+
# def first_n_caller_items(n)
|
154
|
+
# call_stack = caller
|
155
|
+
# base_path = File.realpath('.')
|
156
|
+
|
157
|
+
# # Modify the call stack to remove the base path and keep only the first n items
|
158
|
+
# call_stack.take(n + 1)[1..].map do |line|
|
159
|
+
# " . #{line.sub(/^#{Regexp.escape(base_path)}\//, '')}"
|
160
|
+
# end.join("\n")
|
161
|
+
# end
|
162
|
+
|
163
|
+
# Formats and returns the execution streams (like stdin, stdout, stderr) for a given key.
|
164
|
+
# It concatenates the array of strings found under the specified key in the run_state's files.
|
165
|
+
#
|
166
|
+
# @param key [Symbol] The key corresponding to the desired execution stream.
|
167
|
+
# @return [String] A concatenated string of the execution stream's contents.
|
168
|
+
def format_execution_streams(key, files = {})
|
169
|
+
(files || {}).fetch(key, []).join
|
170
|
+
end
|
171
|
+
|
172
|
+
# Indents all lines in a given string with a specified indentation string.
|
173
|
+
# @param body [String] A multi-line string to be indented.
|
174
|
+
# @param indent [String] The string used for indentation (default is an empty string).
|
175
|
+
# @return [String] A single string with each line indented as specified.
|
176
|
+
def indent_all_lines(body, indent = nil)
|
177
|
+
return body unless indent&.non_empty?
|
178
|
+
|
179
|
+
body.lines.map { |line| indent + line.chomp }.join("\n")
|
180
|
+
end
|
181
|
+
|
182
|
+
def initialize_fcb_names(fcb)
|
183
|
+
fcb.oname = fcb.dname = fcb.title || ''
|
184
|
+
end
|
185
|
+
|
186
|
+
def merge_lists(*args)
|
187
|
+
# Filters out nil values, flattens the arrays, and ensures an empty list is returned if no valid lists are provided
|
188
|
+
merged = args.compact.flatten
|
189
|
+
merged.empty? ? [] : merged
|
190
|
+
end
|
191
|
+
|
192
|
+
def next_link_state(block_name_from_cli, was_using_cli, block_state)
|
193
|
+
# &bsp 'next_link_state', block_name_from_cli, was_using_cli, block_state
|
194
|
+
# Set block_name based on block_name_from_cli
|
195
|
+
block_name = block_name_from_cli ? @cli_block_name : nil
|
196
|
+
# &bsp 'block_name:', block_name
|
197
|
+
|
198
|
+
# Determine the state of breaker based on was_using_cli and the block type
|
199
|
+
breaker = !block_name && !block_name_from_cli && was_using_cli && block_state.block[:shell] == BlockType::BASH
|
200
|
+
# &bsp 'breaker:', breaker
|
201
|
+
|
202
|
+
# Reset block_name_from_cli if the conditions are not met
|
203
|
+
block_name_from_cli ||= false
|
204
|
+
# &bsp 'block_name_from_cli:', block_name_from_cli
|
205
|
+
|
206
|
+
[block_name, block_name_from_cli, breaker]
|
207
|
+
end
|
208
|
+
|
209
|
+
def parse_yaml_data_from_body(body)
|
210
|
+
body.any? ? YAML.load(body.join("\n")) : {}
|
211
|
+
end
|
212
|
+
|
213
|
+
# Reads required code blocks from a temporary file specified by an environment variable.
|
214
|
+
# @return [Array<String>] Lines read from the temporary file, or an empty array if file is not found or path is empty.
|
215
|
+
def read_required_blocks_from_temp_file(temp_blocks_file_path)
|
216
|
+
return [] if temp_blocks_file_path.to_s.empty?
|
217
|
+
|
218
|
+
if File.exist?(temp_blocks_file_path)
|
219
|
+
File.readlines(
|
220
|
+
temp_blocks_file_path, chomp: true
|
221
|
+
)
|
222
|
+
else
|
223
|
+
[]
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def remove_file_without_standard_errors(path)
|
228
|
+
FileUtils.rm_f(path)
|
229
|
+
end
|
230
|
+
|
231
|
+
# Evaluates the given string as Ruby code and rescues any StandardErrors.
|
232
|
+
# If an error occurs, it calls the error_handler method with 'safeval'.
|
233
|
+
# @param str [String] The string to be evaluated.
|
234
|
+
# @return [Object] The result of evaluating the string.
|
235
|
+
def safeval(str)
|
236
|
+
eval(str)
|
237
|
+
rescue StandardError # catches NameError, StandardError
|
238
|
+
error_handler('safeval')
|
239
|
+
end
|
240
|
+
|
241
|
+
def set_file_permissions(file_path, chmod_value)
|
242
|
+
File.chmod(chmod_value, file_path)
|
243
|
+
end
|
244
|
+
|
245
|
+
# Creates a TTY prompt with custom settings. Specifically, it disables the default 'cross' symbol and
|
246
|
+
# defines a lambda function to handle interrupts.
|
247
|
+
# @return [TTY::Prompt] A new TTY::Prompt instance with specified configurations.
|
248
|
+
def tty_prompt_without_disabled_symbol
|
249
|
+
TTY::Prompt.new(
|
250
|
+
interrupt: lambda {
|
251
|
+
puts
|
252
|
+
raise TTY::Reader::InputInterrupt
|
253
|
+
},
|
254
|
+
symbols: { cross: ' ' }
|
255
|
+
)
|
256
|
+
end
|
257
|
+
|
258
|
+
# Updates the attributes of the given fcb object and conditionally yields to a block.
|
259
|
+
# It initializes fcb names and sets the default block title from fcb's body.
|
260
|
+
# If the fcb has a body and meets certain conditions, it yields to the given block.
|
261
|
+
#
|
262
|
+
# @param fcb [Object] The fcb object whose attributes are to be updated.
|
263
|
+
# @param selected_messages [Array<Symbol>] A list of message types to determine if yielding is applicable.
|
264
|
+
# @param block [Block] An optional block to yield to if conditions are met.
|
265
|
+
def update_menu_attrib_yield_selected(fcb, selected_messages, configuration = {}, &block)
|
266
|
+
initialize_fcb_names(fcb)
|
267
|
+
return unless fcb.body
|
268
|
+
|
269
|
+
default_block_title_from_body(fcb)
|
270
|
+
MarkdownExec::Filter.yield_to_block_if_applicable(fcb, selected_messages, configuration,
|
271
|
+
&block)
|
272
|
+
end
|
273
|
+
|
274
|
+
# Writes the provided code blocks to a file.
|
275
|
+
# @param code_blocks [String] Code blocks to write into the file.
|
276
|
+
def write_code_to_file(content, path)
|
277
|
+
File.write(path, content)
|
278
|
+
end
|
279
|
+
|
280
|
+
def write_execution_output_to_file(files, filespec)
|
281
|
+
FileUtils.mkdir_p File.dirname(filespec)
|
282
|
+
|
283
|
+
File.write(
|
284
|
+
filespec,
|
285
|
+
["-STDOUT-\n",
|
286
|
+
format_execution_streams(ExecutionStreams::StdOut, files),
|
287
|
+
"-STDERR-\n",
|
288
|
+
format_execution_streams(ExecutionStreams::StdErr, files),
|
289
|
+
"-STDIN-\n",
|
290
|
+
format_execution_streams(ExecutionStreams::StdIn, files),
|
291
|
+
"\n"].join
|
292
|
+
)
|
293
|
+
end
|
294
|
+
|
295
|
+
# Yields a line as a new block if the selected message type includes :line.
|
296
|
+
# @param [String] line The line to be processed.
|
297
|
+
# @param [Array<Symbol>] selected_messages A list of message types to check.
|
298
|
+
# @param [Proc] block The block to be called with the line data.
|
299
|
+
def yield_line_if_selected(line, selected_messages, &block)
|
300
|
+
return unless block && selected_messages.include?(:line)
|
301
|
+
|
302
|
+
block.call(:line, MarkdownExec::FCB.new(body: [line]))
|
303
|
+
end
|
304
|
+
end
|
305
|
+
### require_relative 'hash_delegator_self'
|
306
|
+
|
40
307
|
# This module provides methods for compacting and converting data structures.
|
41
308
|
module CompactionHelpers
|
42
309
|
# Converts an array of key-value pairs into a hash, applying compaction to the values.
|
@@ -106,11 +373,12 @@ module MarkdownExec
|
|
106
373
|
class HashDelegator
|
107
374
|
attr_accessor :most_recent_loaded_filename, :pass_args, :run_state
|
108
375
|
|
376
|
+
extend HashDelegatorSelf
|
109
377
|
include CompactionHelpers
|
110
378
|
|
111
379
|
def initialize(delegate_object = {})
|
112
380
|
@delegate_object = delegate_object
|
113
|
-
@prompt = tty_prompt_without_disabled_symbol
|
381
|
+
@prompt = HashDelegator.tty_prompt_without_disabled_symbol
|
114
382
|
|
115
383
|
@most_recent_loaded_filename = nil
|
116
384
|
@pass_args = []
|
@@ -138,11 +406,20 @@ module MarkdownExec
|
|
138
406
|
# along with initial and final dividers, based on the delegate object's configuration.
|
139
407
|
#
|
140
408
|
# @param menu_blocks [Array] The array of menu block elements to be modified.
|
141
|
-
def add_menu_chrome_blocks!(menu_blocks)
|
409
|
+
def add_menu_chrome_blocks!(menu_blocks, link_state)
|
142
410
|
return unless @delegate_object[:menu_link_format].present?
|
143
411
|
|
412
|
+
if @delegate_object[:menu_with_inherited_lines]
|
413
|
+
add_inherited_lines(menu_blocks,
|
414
|
+
link_state)
|
415
|
+
end
|
416
|
+
|
417
|
+
# back before exit
|
144
418
|
add_back_option(menu_blocks) if should_add_back_option?
|
419
|
+
|
420
|
+
# exit after other options
|
145
421
|
add_exit_option(menu_blocks) if @delegate_object[:menu_with_exit]
|
422
|
+
|
146
423
|
add_dividers(menu_blocks)
|
147
424
|
end
|
148
425
|
|
@@ -152,13 +429,17 @@ module MarkdownExec
|
|
152
429
|
append_chrome_block(menu_blocks, MenuState::BACK)
|
153
430
|
end
|
154
431
|
|
432
|
+
def add_dividers(menu_blocks)
|
433
|
+
append_divider(menu_blocks, :initial)
|
434
|
+
append_divider(menu_blocks, :final)
|
435
|
+
end
|
436
|
+
|
155
437
|
def add_exit_option(menu_blocks)
|
156
438
|
append_chrome_block(menu_blocks, MenuState::EXIT)
|
157
439
|
end
|
158
440
|
|
159
|
-
def
|
160
|
-
|
161
|
-
append_divider(menu_blocks, :final)
|
441
|
+
def add_inherited_lines(menu_blocks, link_state)
|
442
|
+
append_inherited_lines(menu_blocks, link_state)
|
162
443
|
end
|
163
444
|
|
164
445
|
public
|
@@ -179,7 +460,7 @@ module MarkdownExec
|
|
179
460
|
end
|
180
461
|
|
181
462
|
formatted_name = format(@delegate_object[:menu_link_format],
|
182
|
-
safeval(option_name))
|
463
|
+
HashDelegator.safeval(option_name))
|
183
464
|
chrome_block = FCB.new(
|
184
465
|
chrome: true,
|
185
466
|
dname: HashDelegator.new(@delegate_object).string_send_color(
|
@@ -195,6 +476,39 @@ module MarkdownExec
|
|
195
476
|
end
|
196
477
|
end
|
197
478
|
|
479
|
+
# Appends a formatted divider to the specified position in a menu block array.
|
480
|
+
# The method checks for the presence of formatting options before appending.
|
481
|
+
#
|
482
|
+
# @param menu_blocks [Array] The array of menu block elements.
|
483
|
+
# @param position [Symbol] The position to insert the divider (:initial or :final).
|
484
|
+
def append_inherited_lines(menu_blocks, link_state, position: top)
|
485
|
+
return unless link_state.inherited_lines.present?
|
486
|
+
|
487
|
+
insert_at_top = @delegate_object[:menu_inherited_lines_at_top]
|
488
|
+
chrome_blocks = link_state.inherited_lines.map do |line|
|
489
|
+
formatted = format(@delegate_object[:menu_inherited_lines_format],
|
490
|
+
{ line: line })
|
491
|
+
FCB.new(
|
492
|
+
chrome: true,
|
493
|
+
disabled: '',
|
494
|
+
dname: HashDelegator.new(@delegate_object).string_send_color(
|
495
|
+
formatted, :menu_inherited_lines_color
|
496
|
+
),
|
497
|
+
oname: formatted
|
498
|
+
)
|
499
|
+
end
|
500
|
+
|
501
|
+
if insert_at_top
|
502
|
+
# Prepend an array of elements to the beginning
|
503
|
+
menu_blocks.unshift(*chrome_blocks)
|
504
|
+
else
|
505
|
+
# Append an array of elements to the end
|
506
|
+
menu_blocks.concat(chrome_blocks)
|
507
|
+
end
|
508
|
+
rescue StandardError
|
509
|
+
HashDelegator.error_handler('append_inherited_lines')
|
510
|
+
end
|
511
|
+
|
198
512
|
# Appends a formatted divider to the specified position in a menu block array.
|
199
513
|
# The method checks for the presence of formatting options before appending.
|
200
514
|
#
|
@@ -224,19 +538,6 @@ module MarkdownExec
|
|
224
538
|
|
225
539
|
# private
|
226
540
|
|
227
|
-
# Searches for the first element in a collection where the specified key matches a given value.
|
228
|
-
# This method is particularly useful for finding a specific hash-like object within an enumerable collection.
|
229
|
-
# If no match is found, it returns a specified default value.
|
230
|
-
#
|
231
|
-
# @param blocks [Enumerable] The collection of hash-like objects to search.
|
232
|
-
# @param key [Object] The key to search for in each element of the collection.
|
233
|
-
# @param value [Object] The value to match against each element's corresponding key value.
|
234
|
-
# @param default [Object, nil] The default value to return if no match is found (optional).
|
235
|
-
# @return [Object, nil] The first matching element or the default value if no match is found.
|
236
|
-
def block_find(blocks, key, value, default = nil)
|
237
|
-
blocks.find { |item| item[key] == value } || default
|
238
|
-
end
|
239
|
-
|
240
541
|
# Iterates through nested files to collect various types of blocks, including dividers, tasks, and others.
|
241
542
|
# The method categorizes blocks based on their type and processes them accordingly.
|
242
543
|
#
|
@@ -246,9 +547,10 @@ module MarkdownExec
|
|
246
547
|
iter_blocks_from_nested_files do |btype, fcb|
|
247
548
|
process_block_based_on_type(blocks, btype, fcb)
|
248
549
|
end
|
550
|
+
# &bc 'blocks.count:', blocks.count
|
249
551
|
blocks
|
250
552
|
rescue StandardError
|
251
|
-
error_handler('blocks_from_nested_files')
|
553
|
+
HashDelegator.error_handler('blocks_from_nested_files')
|
252
554
|
end
|
253
555
|
|
254
556
|
# private
|
@@ -273,15 +575,6 @@ module MarkdownExec
|
|
273
575
|
true
|
274
576
|
end
|
275
577
|
|
276
|
-
def code_join(*bodies)
|
277
|
-
bc = bodies&.compact
|
278
|
-
bc.count.positive? ? bc.join("\n") : nil
|
279
|
-
end
|
280
|
-
|
281
|
-
def code_merge(*bodies)
|
282
|
-
merge_lists(*bodies)
|
283
|
-
end
|
284
|
-
|
285
578
|
# Collects required code lines based on the selected block and the delegate object's configuration.
|
286
579
|
# If the block type is VARS, it also sets environment variables based on the block's content.
|
287
580
|
#
|
@@ -312,7 +605,7 @@ module MarkdownExec
|
|
312
605
|
highlight: [@delegate_object[:block_name]])
|
313
606
|
end
|
314
607
|
|
315
|
-
code_merge
|
608
|
+
HashDelegator.code_merge(link_state&.inherited_lines, required[:code])
|
316
609
|
end
|
317
610
|
|
318
611
|
def command_execute(command, args: [])
|
@@ -363,17 +656,17 @@ module MarkdownExec
|
|
363
656
|
if @delegate_object[:block_name].present?
|
364
657
|
block = all_blocks.find do |item|
|
365
658
|
item[:oname] == @delegate_object[:block_name]
|
366
|
-
end
|
659
|
+
end&.merge(block_name_from_ui: false)
|
367
660
|
else
|
368
661
|
block_state = wait_for_user_selected_block(all_blocks, menu_blocks,
|
369
662
|
default)
|
370
|
-
block = block_state.block
|
663
|
+
block = block_state.block&.merge(block_name_from_ui: true)
|
371
664
|
state = block_state.state
|
372
665
|
end
|
373
666
|
|
374
667
|
SelectedBlockMenuState.new(block, state)
|
375
668
|
rescue StandardError
|
376
|
-
error_handler('load_cli_or_user_selected_block')
|
669
|
+
HashDelegator.error_handler('load_cli_or_user_selected_block')
|
377
670
|
end
|
378
671
|
|
379
672
|
# This method is responsible for handling the execution of generic blocks in a markdown document.
|
@@ -412,17 +705,9 @@ module MarkdownExec
|
|
412
705
|
def count_blocks_in_filename
|
413
706
|
regex = Regexp.new(@delegate_object[:fenced_start_and_end_regex])
|
414
707
|
lines = cfile.readlines(@delegate_object[:filename])
|
415
|
-
count_matches_in_lines(lines, regex) / 2
|
416
|
-
end
|
417
|
-
|
418
|
-
# private
|
419
|
-
|
420
|
-
def count_matches_in_lines(lines, regex)
|
421
|
-
lines.count { |line| line.to_s.match(regex) }
|
708
|
+
HashDelegator.count_matches_in_lines(lines, regex) / 2
|
422
709
|
end
|
423
710
|
|
424
|
-
# private
|
425
|
-
|
426
711
|
##
|
427
712
|
# Creates and adds a formatted block to the blocks array based on the provided match and format options.
|
428
713
|
# @param blocks [Array] The array of blocks to add the new block to.
|
@@ -449,12 +734,13 @@ module MarkdownExec
|
|
449
734
|
# @param use_chrome [Boolean] Indicates if the chrome styling should be applied.
|
450
735
|
def create_and_add_chrome_blocks(blocks, fcb)
|
451
736
|
match_criteria = [
|
452
|
-
{ match: :
|
453
|
-
|
737
|
+
{ match: :heading1_match, format: :menu_heading1_format, color: :menu_heading1_color },
|
738
|
+
{ match: :heading2_match, format: :menu_heading2_format, color: :menu_heading2_color },
|
739
|
+
{ match: :heading3_match, format: :menu_heading3_format, color: :menu_heading3_color },
|
454
740
|
{ match: :menu_divider_match, format: :menu_divider_format,
|
455
741
|
color: :menu_divider_color },
|
456
|
-
{ match: :menu_note_match, format: :menu_note_format,
|
457
|
-
|
742
|
+
{ match: :menu_note_match, format: :menu_note_format, color: :menu_note_color },
|
743
|
+
{ match: :menu_task_match, format: :menu_task_format, color: :menu_task_color }
|
458
744
|
]
|
459
745
|
match_criteria.each do |criteria|
|
460
746
|
unless @delegate_object[criteria[:match]].present? &&
|
@@ -468,31 +754,10 @@ module MarkdownExec
|
|
468
754
|
end
|
469
755
|
end
|
470
756
|
|
471
|
-
# Creates a file at the specified path, writes the given content to it,
|
472
|
-
# and sets file permissions if required. Handles any errors encountered during the process.
|
473
|
-
#
|
474
|
-
# @param file_path [String] The path where the file will be created.
|
475
|
-
# @param content [String] The content to write into the file.
|
476
|
-
# @param chmod_value [Integer] The file permission value to set; skips if zero.
|
477
|
-
def create_file_and_write_string_with_permissions(file_path, content,
|
478
|
-
chmod_value)
|
479
|
-
create_directory_for_file(file_path)
|
480
|
-
File.write(file_path, content)
|
481
|
-
set_file_permissions(file_path, chmod_value) unless chmod_value.zero?
|
482
|
-
rescue StandardError
|
483
|
-
error_handler('create_file_and_write_string_with_permissions')
|
484
|
-
end
|
485
|
-
|
486
|
-
# private
|
487
|
-
|
488
|
-
def create_directory_for_file(file_path)
|
489
|
-
FileUtils.mkdir_p(File.dirname(file_path))
|
490
|
-
end
|
491
|
-
|
492
757
|
def create_divider(position)
|
493
758
|
divider_key = position == :initial ? :menu_initial_divider : :menu_final_divider
|
494
759
|
oname = format(@delegate_object[:menu_divider_format],
|
495
|
-
safeval(@delegate_object[divider_key]))
|
760
|
+
HashDelegator.safeval(@delegate_object[divider_key]))
|
496
761
|
|
497
762
|
FCB.new(
|
498
763
|
chrome: true,
|
@@ -502,38 +767,6 @@ module MarkdownExec
|
|
502
767
|
)
|
503
768
|
end
|
504
769
|
|
505
|
-
# private
|
506
|
-
|
507
|
-
def create_temp_file
|
508
|
-
Dir::Tmpname.create(self.class.to_s) { |path| path }
|
509
|
-
end
|
510
|
-
|
511
|
-
# Updates the title of an FCB object from its body content if the title is nil or empty.
|
512
|
-
def default_block_title_from_body(fcb)
|
513
|
-
return unless fcb.title.nil? || fcb.title.empty?
|
514
|
-
|
515
|
-
fcb.derive_title_from_body
|
516
|
-
end
|
517
|
-
|
518
|
-
# delete the current line if it is empty and the previous is also empty
|
519
|
-
def delete_consecutive_blank_lines!(blocks_menu)
|
520
|
-
blocks_menu.process_and_conditionally_delete! do |prev_item, current_item, _next_item|
|
521
|
-
prev_item&.fetch(:chrome, nil) && !prev_item&.fetch(:oname).present? &&
|
522
|
-
current_item&.fetch(:chrome, nil) && !current_item&.fetch(:oname).present?
|
523
|
-
end
|
524
|
-
end
|
525
|
-
|
526
|
-
# Deletes a temporary file specified by an environment variable.
|
527
|
-
# Checks if the file exists before attempting to delete it and clears the environment variable afterward.
|
528
|
-
# Any errors encountered during deletion are handled gracefully.
|
529
|
-
def delete_required_temp_file(temp_blocks_file_path)
|
530
|
-
return if temp_blocks_file_path.nil? || temp_blocks_file_path.empty?
|
531
|
-
|
532
|
-
safely_remove_file(temp_blocks_file_path)
|
533
|
-
rescue StandardError
|
534
|
-
error_handler('delete_required_temp_file')
|
535
|
-
end
|
536
|
-
|
537
770
|
# Determines the state of a selected block in the menu based on the selected option.
|
538
771
|
# It categorizes the selected option into either EXIT, BACK, or CONTINUE state.
|
539
772
|
#
|
@@ -570,15 +803,6 @@ module MarkdownExec
|
|
570
803
|
@delegate_object[:menu_divider_format].present? && @delegate_object[divider_key].present?
|
571
804
|
end
|
572
805
|
|
573
|
-
def error_handler(name = '', opts = {})
|
574
|
-
Exceptions.error_handler(
|
575
|
-
"HashDelegator.#{name} -- #{$!}",
|
576
|
-
opts
|
577
|
-
)
|
578
|
-
end
|
579
|
-
|
580
|
-
# public
|
581
|
-
|
582
806
|
# Executes a block of code that has been approved for execution.
|
583
807
|
# It sets the script block name, writes command files if required, and handles the execution
|
584
808
|
# including output formatting and summarization.
|
@@ -586,8 +810,8 @@ module MarkdownExec
|
|
586
810
|
# @param required_lines [Array<String>] The lines of code to be executed.
|
587
811
|
# @param selected [FCB] The selected functional code block object.
|
588
812
|
def execute_required_lines(required_lines = [])
|
589
|
-
#
|
590
|
-
|
813
|
+
# @run_state.script_block_name = selected[:oname]
|
814
|
+
write_command_file(required_lines) if @delegate_object[:save_executed_script]
|
591
815
|
format_and_execute_command(required_lines)
|
592
816
|
post_execution_process
|
593
817
|
end
|
@@ -636,17 +860,6 @@ module MarkdownExec
|
|
636
860
|
string_send_color(data_string, color_sym)
|
637
861
|
end
|
638
862
|
|
639
|
-
# DebugHelper.d ["HDmm method_name: #{method_name}", "#{first_n_caller_items 1}"]
|
640
|
-
def first_n_caller_items(n)
|
641
|
-
call_stack = caller
|
642
|
-
base_path = File.realpath('.')
|
643
|
-
|
644
|
-
# Modify the call stack to remove the base path and keep only the first n items
|
645
|
-
call_stack.take(n + 1)[1..].map do |line|
|
646
|
-
" . #{line.sub(/^#{Regexp.escape(base_path)}\//, '')}"
|
647
|
-
end.join("\n")
|
648
|
-
end
|
649
|
-
|
650
863
|
def format_and_execute_command(lines)
|
651
864
|
formatted_command = lines.flatten.join("\n")
|
652
865
|
@fout.fout fetch_color(data_sym: :script_execution_head,
|
@@ -672,16 +885,6 @@ module MarkdownExec
|
|
672
885
|
string_send_color(formatted_string, color_sym)
|
673
886
|
end
|
674
887
|
|
675
|
-
# Formats and returns the execution streams (like stdin, stdout, stderr) for a given key.
|
676
|
-
# It concatenates the array of strings found under the specified key in the run_state's files.
|
677
|
-
#
|
678
|
-
# @param key [Symbol] The key corresponding to the desired execution stream.
|
679
|
-
# @return [String] A concatenated string of the execution stream's contents.
|
680
|
-
def format_execution_streams(key)
|
681
|
-
files = @run_state.files || {}
|
682
|
-
files.fetch(key, []).join
|
683
|
-
end
|
684
|
-
|
685
888
|
# Processes a block to generate its summary, modifying its attributes based on various matching criteria.
|
686
889
|
# It handles special formatting for bash blocks, extracting and setting properties like call, stdin, stdout, and dname.
|
687
890
|
#
|
@@ -711,7 +914,7 @@ module MarkdownExec
|
|
711
914
|
# It sets the block name and determines if the user clicked the back link in the menu.
|
712
915
|
#
|
713
916
|
# @param block_state [Object] An object representing the state of a block in the menu.
|
714
|
-
def
|
917
|
+
def handle_back_or_continue(block_state)
|
715
918
|
return if block_state.nil?
|
716
919
|
unless [MenuState::BACK,
|
717
920
|
MenuState::CONTINUE].include?(block_state.state)
|
@@ -744,16 +947,6 @@ module MarkdownExec
|
|
744
947
|
end
|
745
948
|
end
|
746
949
|
|
747
|
-
# Indents all lines in a given string with a specified indentation string.
|
748
|
-
# @param body [String] A multi-line string to be indented.
|
749
|
-
# @param indent [String] The string used for indentation (default is an empty string).
|
750
|
-
# @return [String] A single string with each line indented as specified.
|
751
|
-
def indent_all_lines(body, indent = nil)
|
752
|
-
return body unless indent&.non_empty?
|
753
|
-
|
754
|
-
body.lines.map { |line| indent + line.chomp }.join("\n")
|
755
|
-
end
|
756
|
-
|
757
950
|
# Initializes variables for regex and other states
|
758
951
|
def initial_state
|
759
952
|
{
|
@@ -784,11 +977,8 @@ module MarkdownExec
|
|
784
977
|
File.join @delegate_object[:saved_stdout_folder],
|
785
978
|
@delegate_object[:logged_stdout_filename]
|
786
979
|
@logged_stdout_filespec = @delegate_object[:logged_stdout_filespec]
|
787
|
-
write_execution_output_to_file
|
788
|
-
|
789
|
-
|
790
|
-
def initialize_fcb_names(fcb)
|
791
|
-
fcb.oname = fcb.dname = fcb.title || ''
|
980
|
+
HashDelegator.write_execution_output_to_file(@run_state.files,
|
981
|
+
@delegate_object[:logged_stdout_filespec])
|
792
982
|
end
|
793
983
|
|
794
984
|
# Iterates through blocks in a file, applying the provided block to each line.
|
@@ -845,7 +1035,7 @@ module MarkdownExec
|
|
845
1035
|
return
|
846
1036
|
end
|
847
1037
|
|
848
|
-
block = block_find(all_blocks, :oname, block_name)
|
1038
|
+
block = HashDelegator.block_find(all_blocks, :oname, block_name)
|
849
1039
|
return unless block
|
850
1040
|
|
851
1041
|
options_state = read_show_options_and_trigger_reuse(block)
|
@@ -866,7 +1056,7 @@ module MarkdownExec
|
|
866
1056
|
|
867
1057
|
## Handles the file loading and returns the blocks in the file and MDoc instance
|
868
1058
|
#
|
869
|
-
def mdoc_menu_and_blocks_from_nested_files
|
1059
|
+
def mdoc_menu_and_blocks_from_nested_files(link_state)
|
870
1060
|
all_blocks, mdoc = mdoc_and_blocks_from_nested_files
|
871
1061
|
|
872
1062
|
# recreate menu with new options
|
@@ -874,8 +1064,9 @@ module MarkdownExec
|
|
874
1064
|
all_blocks, mdoc = mdoc_and_blocks_from_nested_files if load_auto_blocks(all_blocks)
|
875
1065
|
|
876
1066
|
menu_blocks = mdoc.fcbs_per_options(@delegate_object)
|
877
|
-
add_menu_chrome_blocks!(menu_blocks)
|
878
|
-
|
1067
|
+
add_menu_chrome_blocks!(menu_blocks, link_state)
|
1068
|
+
### compress empty lines
|
1069
|
+
HashDelegator.delete_consecutive_blank_lines!(menu_blocks) if true
|
879
1070
|
[all_blocks, menu_blocks, mdoc]
|
880
1071
|
end
|
881
1072
|
|
@@ -894,7 +1085,7 @@ module MarkdownExec
|
|
894
1085
|
# @param option_symbol [Symbol] The symbol key for the menu option in the delegate object.
|
895
1086
|
# @return [String] The formatted or original value of the menu option.
|
896
1087
|
def menu_chrome_formatted_option(option_symbol = :menu_option_back_name)
|
897
|
-
option_value = safeval(@delegate_object.fetch(option_symbol, ''))
|
1088
|
+
option_value = HashDelegator.safeval(@delegate_object.fetch(option_symbol, ''))
|
898
1089
|
|
899
1090
|
if @delegate_object[:menu_chrome_format]
|
900
1091
|
format(@delegate_object[:menu_chrome_format], option_value)
|
@@ -903,12 +1094,6 @@ module MarkdownExec
|
|
903
1094
|
end
|
904
1095
|
end
|
905
1096
|
|
906
|
-
def merge_lists(*args)
|
907
|
-
# Filters out nil values, flattens the arrays, and ensures an empty list is returned if no valid lists are provided
|
908
|
-
merged = args.compact.flatten
|
909
|
-
merged.empty? ? [] : merged
|
910
|
-
end
|
911
|
-
|
912
1097
|
# If a method is missing, treat it as a key for the @delegate_object.
|
913
1098
|
def method_missing(method_name, *args, &block)
|
914
1099
|
if @delegate_object.respond_to?(method_name)
|
@@ -921,17 +1106,13 @@ module MarkdownExec
|
|
921
1106
|
end
|
922
1107
|
end
|
923
1108
|
|
924
|
-
def shift_cli_argument
|
925
|
-
return
|
1109
|
+
def shift_cli_argument
|
1110
|
+
return true unless @menu_base_options[:input_cli_rest].present?
|
926
1111
|
|
927
1112
|
@cli_block_name = @menu_base_options[:input_cli_rest].shift
|
928
|
-
|
929
|
-
# p [__LINE__, @cli_block_name, @menu_base_options[:input_cli_rest]]
|
930
|
-
true
|
1113
|
+
false
|
931
1114
|
end
|
932
1115
|
|
933
|
-
# private
|
934
|
-
|
935
1116
|
def output_color_formatted(data_sym, color_sym)
|
936
1117
|
formatted_string = string_send_color(@delegate_object[data_sym],
|
937
1118
|
color_sym)
|
@@ -984,16 +1165,10 @@ module MarkdownExec
|
|
984
1165
|
), level: level
|
985
1166
|
end
|
986
1167
|
|
987
|
-
# private
|
988
|
-
|
989
|
-
def parse_yaml_data_from_body(body)
|
990
|
-
body.any? ? YAML.load(body.join("\n")) : {}
|
991
|
-
end
|
992
|
-
|
993
1168
|
def pop_add_current_code_to_head_and_trigger_load(_link_state, block_names, code_lines,
|
994
1169
|
dependencies)
|
995
1170
|
pop = @link_history.pop # updatable
|
996
|
-
|
1171
|
+
next_state = LinkState.new(
|
997
1172
|
block_name: pop.block_name,
|
998
1173
|
document_filename: pop.document_filename,
|
999
1174
|
inherited_block_names:
|
@@ -1001,12 +1176,12 @@ module MarkdownExec
|
|
1001
1176
|
inherited_dependencies:
|
1002
1177
|
dependencies.merge(pop.inherited_dependencies || {}), ### merge, not replace, key data
|
1003
1178
|
inherited_lines:
|
1004
|
-
code_merge(pop.inherited_lines, code_lines)
|
1179
|
+
HashDelegator.code_merge(pop.inherited_lines, code_lines)
|
1005
1180
|
)
|
1006
|
-
@link_history.push(
|
1181
|
+
@link_history.push(next_state)
|
1007
1182
|
|
1008
|
-
|
1009
|
-
LoadFileLinkState.new(LoadFile::Load,
|
1183
|
+
next_state.block_name = nil
|
1184
|
+
LoadFileLinkState.new(LoadFile::Load, next_state)
|
1010
1185
|
end
|
1011
1186
|
|
1012
1187
|
# This method handles the back-link operation in the Markdown execution context.
|
@@ -1041,7 +1216,7 @@ module MarkdownExec
|
|
1041
1216
|
%i[block_name_include_match block_name_wrapper_match])
|
1042
1217
|
|
1043
1218
|
fcb.merge!(
|
1044
|
-
name: indent_all_lines(fcb.dname, fcb.fetch(:indent, nil)),
|
1219
|
+
name: HashDelegator.indent_all_lines(fcb.dname, fcb.fetch(:indent, nil)),
|
1045
1220
|
label: BlockLabel.make(
|
1046
1221
|
body: fcb[:body],
|
1047
1222
|
filename: @delegate_object[:filename],
|
@@ -1071,10 +1246,7 @@ module MarkdownExec
|
|
1071
1246
|
when :filter
|
1072
1247
|
%i[blocks line]
|
1073
1248
|
when :line
|
1074
|
-
unless @delegate_object[:no_chrome]
|
1075
|
-
create_and_add_chrome_blocks(blocks,
|
1076
|
-
fcb)
|
1077
|
-
end
|
1249
|
+
create_and_add_chrome_blocks(blocks, fcb) unless @delegate_object[:no_chrome]
|
1078
1250
|
end
|
1079
1251
|
end
|
1080
1252
|
|
@@ -1148,7 +1320,7 @@ module MarkdownExec
|
|
1148
1320
|
# @return [LoadFileLinkState] Object indicating the next action for file loading.
|
1149
1321
|
def push_link_history_and_trigger_load(link_block_body, mdoc, selected,
|
1150
1322
|
link_state = LinkState.new)
|
1151
|
-
link_block_data = parse_yaml_data_from_body(link_block_body)
|
1323
|
+
link_block_data = HashDelegator.parse_yaml_data_from_body(link_block_body)
|
1152
1324
|
|
1153
1325
|
# load key and values from link block into current environment
|
1154
1326
|
#
|
@@ -1178,7 +1350,7 @@ module MarkdownExec
|
|
1178
1350
|
# if an eval link block, evaluate code_lines and return its standard output
|
1179
1351
|
#
|
1180
1352
|
if link_block_data.fetch('eval', false)
|
1181
|
-
all_code = code_merge(link_state&.inherited_lines, code_lines)
|
1353
|
+
all_code = HashDelegator.code_merge(link_state&.inherited_lines, code_lines)
|
1182
1354
|
output = `#{all_code.join("\n")}`.split("\n")
|
1183
1355
|
label_format_above = @delegate_object[:shell_code_label_format_above]
|
1184
1356
|
label_format_below = @delegate_object[:shell_code_label_format_below]
|
@@ -1207,7 +1379,7 @@ module MarkdownExec
|
|
1207
1379
|
curr_document_filename: @delegate_object[:filename],
|
1208
1380
|
inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
1209
1381
|
inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
1210
|
-
inherited_lines: code_merge(link_state&.inherited_lines, code_lines),
|
1382
|
+
inherited_lines: HashDelegator.code_merge(link_state&.inherited_lines, code_lines),
|
1211
1383
|
next_block_name: link_block_data['block'] || '',
|
1212
1384
|
next_document_filename: next_document_filename,
|
1213
1385
|
next_load_file: next_document_filename == @delegate_object[:filename] ? LoadFile::Reuse : LoadFile::Load
|
@@ -1215,20 +1387,6 @@ module MarkdownExec
|
|
1215
1387
|
end
|
1216
1388
|
end
|
1217
1389
|
|
1218
|
-
# Reads required code blocks from a temporary file specified by an environment variable.
|
1219
|
-
# @return [Array<String>] Lines read from the temporary file, or an empty array if file is not found or path is empty.
|
1220
|
-
def read_required_blocks_from_temp_file(temp_blocks_file_path)
|
1221
|
-
return [] if temp_blocks_file_path.to_s.empty?
|
1222
|
-
|
1223
|
-
if File.exist?(temp_blocks_file_path)
|
1224
|
-
File.readlines(
|
1225
|
-
temp_blocks_file_path, chomp: true
|
1226
|
-
)
|
1227
|
-
else
|
1228
|
-
[]
|
1229
|
-
end
|
1230
|
-
end
|
1231
|
-
|
1232
1390
|
def runtime_exception(exception_sym, name, items)
|
1233
1391
|
if @delegate_object[exception_sym] != 0
|
1234
1392
|
data = { name: name, detail: items.join(', ') }
|
@@ -1248,20 +1406,6 @@ module MarkdownExec
|
|
1248
1406
|
exit @delegate_object[exception_sym]
|
1249
1407
|
end
|
1250
1408
|
|
1251
|
-
def safely_remove_file(path)
|
1252
|
-
FileUtils.rm_f(path)
|
1253
|
-
end
|
1254
|
-
|
1255
|
-
# Evaluates the given string as Ruby code and rescues any StandardErrors.
|
1256
|
-
# If an error occurs, it calls the error_handler method with 'safeval'.
|
1257
|
-
# @param str [String] The string to be evaluated.
|
1258
|
-
# @return [Object] The result of evaluating the string.
|
1259
|
-
def safeval(str)
|
1260
|
-
eval(str)
|
1261
|
-
rescue StandardError
|
1262
|
-
error_handler('safeval')
|
1263
|
-
end
|
1264
|
-
|
1265
1409
|
def save_to_file(required_lines)
|
1266
1410
|
write_command_file(required_lines)
|
1267
1411
|
@fout.fout "File saved: #{@run_state.saved_filespec}"
|
@@ -1275,11 +1419,17 @@ module MarkdownExec
|
|
1275
1419
|
# @return [Nil] Returns nil if no code block is selected or an error occurs.
|
1276
1420
|
def document_menu_loop
|
1277
1421
|
@menu_base_options = @delegate_object
|
1278
|
-
link_state
|
1422
|
+
link_state = LinkState.new(
|
1423
|
+
block_name: @delegate_object[:block_name],
|
1424
|
+
document_filename: @delegate_object[:filename]
|
1425
|
+
)
|
1426
|
+
block_name_from_cli = link_state.block_name.present?
|
1427
|
+
@cli_block_name = link_state.block_name
|
1428
|
+
now_using_cli = block_name_from_cli
|
1279
1429
|
menu_default_dname = nil
|
1280
1430
|
|
1281
1431
|
loop do
|
1282
|
-
#
|
1432
|
+
# &bsp 'loop', block_name_from_cli, @cli_block_name
|
1283
1433
|
block_name_from_cli, now_using_cli, blocks_in_file, menu_blocks, mdoc = \
|
1284
1434
|
set_delobj_menu_loop_vars(block_name_from_cli, now_using_cli, link_state)
|
1285
1435
|
|
@@ -1287,8 +1437,11 @@ module MarkdownExec
|
|
1287
1437
|
#
|
1288
1438
|
block_state = load_cli_or_user_selected_block(blocks_in_file, menu_blocks,
|
1289
1439
|
menu_default_dname)
|
1290
|
-
|
1291
|
-
|
1440
|
+
# &bsp 'block_name_from_cli:',block_name_from_cli
|
1441
|
+
if !block_state
|
1442
|
+
HashDelegator.error_handler('block_state missing', { abort: true })
|
1443
|
+
elsif block_state.state == MenuState::EXIT
|
1444
|
+
# &bsp 'load_cli_or_user_selected_block -> break'
|
1292
1445
|
break
|
1293
1446
|
end
|
1294
1447
|
|
@@ -1296,30 +1449,21 @@ module MarkdownExec
|
|
1296
1449
|
link_state, menu_default_dname = exec_bash_next_state(block_state.block, mdoc,
|
1297
1450
|
link_state)
|
1298
1451
|
if prompt_user_exit(block_name_from_cli, block_state.block)
|
1299
|
-
#
|
1452
|
+
# &bsp 'prompt_user_exit -> break'
|
1300
1453
|
break
|
1301
1454
|
end
|
1302
1455
|
|
1303
1456
|
link_state.block_name, block_name_from_cli, cli_break = \
|
1304
|
-
|
1457
|
+
HashDelegator.next_link_state(!shift_cli_argument, now_using_cli, block_state)
|
1305
1458
|
|
1306
|
-
if cli_break
|
1307
|
-
#
|
1459
|
+
if !block_state.block[:block_name_from_ui] && cli_break
|
1460
|
+
# &bsp '!block_name_from_ui + cli_break -> break'
|
1308
1461
|
break
|
1309
1462
|
end
|
1310
1463
|
end
|
1311
1464
|
rescue StandardError
|
1312
|
-
error_handler('document_menu_loop',
|
1313
|
-
|
1314
|
-
end
|
1315
|
-
|
1316
|
-
def next_state_from_cli(now_using_cli, block_state)
|
1317
|
-
was_using_cli = now_using_cli
|
1318
|
-
block_name_from_cli, = read_block_name_from_cli(now_using_cli)
|
1319
|
-
block_name, block_name_from_cli, cli_break = \
|
1320
|
-
next_link_state(block_name_from_cli, was_using_cli, block_state)
|
1321
|
-
|
1322
|
-
[block_name, block_name_from_cli, cli_break]
|
1465
|
+
HashDelegator.error_handler('document_menu_loop',
|
1466
|
+
{ abort: true })
|
1323
1467
|
end
|
1324
1468
|
|
1325
1469
|
def exec_bash_next_state(block_state_block, mdoc, link_state)
|
@@ -1342,7 +1486,7 @@ module MarkdownExec
|
|
1342
1486
|
|
1343
1487
|
# update @delegate_object and @menu_base_options in auto_load
|
1344
1488
|
#
|
1345
|
-
blocks_in_file, menu_blocks, mdoc = mdoc_menu_and_blocks_from_nested_files
|
1489
|
+
blocks_in_file, menu_blocks, mdoc = mdoc_menu_and_blocks_from_nested_files(link_state)
|
1346
1490
|
dump_delobj(blocks_in_file, menu_blocks, link_state)
|
1347
1491
|
|
1348
1492
|
[block_name_from_cli, now_using_cli, blocks_in_file, menu_blocks, mdoc]
|
@@ -1358,8 +1502,8 @@ module MarkdownExec
|
|
1358
1502
|
end
|
1359
1503
|
|
1360
1504
|
def manage_cli_selection_state(block_name_from_cli, now_using_cli, link_state)
|
1361
|
-
if block_name_from_cli && @cli_block_name ==
|
1362
|
-
#
|
1505
|
+
if block_name_from_cli && @cli_block_name == @menu_base_options[:menu_persist_block_name]
|
1506
|
+
# &bsp 'pause cli control, allow user to select block'
|
1363
1507
|
block_name_from_cli = false
|
1364
1508
|
now_using_cli = false
|
1365
1509
|
@menu_base_options[:block_name] = \
|
@@ -1373,45 +1517,6 @@ module MarkdownExec
|
|
1373
1517
|
[block_name_from_cli, now_using_cli]
|
1374
1518
|
end
|
1375
1519
|
|
1376
|
-
def next_link_state(block_name_from_cli, was_using_cli, block_state)
|
1377
|
-
# @bsp 'next_link_state',block_name_from_cli, was_using_cli, block_state
|
1378
|
-
# Set block_name based on block_name_from_cli
|
1379
|
-
block_name = block_name_from_cli ? @cli_block_name : nil
|
1380
|
-
|
1381
|
-
# Determine the state of breaker based on was_using_cli and the block type
|
1382
|
-
breaker = !block_name_from_cli && was_using_cli && block_state.block[:shell] == BlockType::BASH
|
1383
|
-
|
1384
|
-
# Reset block_name_from_cli if the conditions are not met
|
1385
|
-
block_name_from_cli ||= false
|
1386
|
-
|
1387
|
-
[block_name, block_name_from_cli, breaker]
|
1388
|
-
end
|
1389
|
-
|
1390
|
-
# Initialize the selection states for the execution loop.
|
1391
|
-
def initialize_selection_states
|
1392
|
-
link_state = LinkState.new(
|
1393
|
-
block_name: @delegate_object[:block_name],
|
1394
|
-
document_filename: @delegate_object[:filename]
|
1395
|
-
)
|
1396
|
-
block_name_from_cli, now_using_cli = handle_cli_block_name(link_state)
|
1397
|
-
[link_state, block_name_from_cli, now_using_cli]
|
1398
|
-
end
|
1399
|
-
|
1400
|
-
# Update the state related to CLI block name.
|
1401
|
-
#
|
1402
|
-
# This method updates the flags indicating whether a CLI block name is being used
|
1403
|
-
# and if it was being used previously.
|
1404
|
-
#
|
1405
|
-
# @param block_name_from_cli [Boolean] Indicates if the block name is from CLI.
|
1406
|
-
# @return [Array] Returns the updated state of block name from CLI and its usage.
|
1407
|
-
def read_block_name_from_cli(block_name_from_cli)
|
1408
|
-
# was_using_cli = block_name_from_cli
|
1409
|
-
block_name_from_cli = shift_cli_argument!
|
1410
|
-
now_using_cli = block_name_from_cli
|
1411
|
-
|
1412
|
-
[block_name_from_cli, now_using_cli]
|
1413
|
-
end
|
1414
|
-
|
1415
1520
|
# Update the block name in the link state and delegate object.
|
1416
1521
|
#
|
1417
1522
|
# This method updates the block name based on whether it was specified
|
@@ -1425,21 +1530,6 @@ module MarkdownExec
|
|
1425
1530
|
block_name_from_cli ? @cli_block_name : link_state.block_name
|
1426
1531
|
end
|
1427
1532
|
|
1428
|
-
# Handle CLI block name and determine the current CLI usage state.
|
1429
|
-
#
|
1430
|
-
# This method processes the CLI block name from the link state and sets
|
1431
|
-
# the initial state for CLI usage.
|
1432
|
-
#
|
1433
|
-
# @param link_state [LinkState] The current link state object.
|
1434
|
-
# @return [Array] Returns the state of block name from CLI and current usage of CLI.
|
1435
|
-
def handle_cli_block_name(link_state)
|
1436
|
-
block_name_from_cli = link_state.block_name.present?
|
1437
|
-
@cli_block_name = link_state.block_name
|
1438
|
-
now_using_cli = block_name_from_cli
|
1439
|
-
|
1440
|
-
[block_name_from_cli, now_using_cli]
|
1441
|
-
end
|
1442
|
-
|
1443
1533
|
# Outputs warnings based on the delegate object's configuration
|
1444
1534
|
#
|
1445
1535
|
# @param delegate_object [Hash] The delegate object containing configuration flags.
|
@@ -1500,7 +1590,7 @@ module MarkdownExec
|
|
1500
1590
|
rescue TTY::Reader::InputInterrupt
|
1501
1591
|
exit 1
|
1502
1592
|
rescue StandardError
|
1503
|
-
error_handler('select_option_with_metadata')
|
1593
|
+
HashDelegator.error_handler('select_option_with_metadata')
|
1504
1594
|
end
|
1505
1595
|
|
1506
1596
|
def set_environment_variables_for_block(selected)
|
@@ -1514,22 +1604,8 @@ module MarkdownExec
|
|
1514
1604
|
end
|
1515
1605
|
end
|
1516
1606
|
|
1517
|
-
def set_environment_variables_per_array(vars)
|
1518
|
-
vars ||= []
|
1519
|
-
vars.each { |key, value| ENV[key] = value.to_s }
|
1520
|
-
end
|
1521
|
-
|
1522
|
-
def set_file_permissions(file_path, chmod_value)
|
1523
|
-
File.chmod(chmod_value, file_path)
|
1524
|
-
end
|
1525
|
-
|
1526
|
-
def set_script_block_name(selected)
|
1527
|
-
@run_state.script_block_name = selected[:oname]
|
1528
|
-
end
|
1529
|
-
|
1530
1607
|
def should_add_back_option?
|
1531
1608
|
@delegate_object[:menu_with_back] && @link_history.prior_state_exist?
|
1532
|
-
# @delegate_object[:menu_with_back] && link_history_prior_state_exist?
|
1533
1609
|
end
|
1534
1610
|
|
1535
1611
|
# Initializes a new fenced code block (FCB) object based on the provided line and heading information.
|
@@ -1573,44 +1649,8 @@ module MarkdownExec
|
|
1573
1649
|
# @param color_sym [Symbol] The symbol representing the color method.
|
1574
1650
|
# @param default [String] Default color method to use if color_sym is not found in @delegate_object.
|
1575
1651
|
# @return [String] The string with the applied color method.
|
1576
|
-
def string_send_color(string, color_sym
|
1577
|
-
|
1578
|
-
string.to_s.send(color_method)
|
1579
|
-
end
|
1580
|
-
|
1581
|
-
# Creates a TTY prompt with custom settings. Specifically, it disables the default 'cross' symbol and
|
1582
|
-
# defines a lambda function to handle interrupts.
|
1583
|
-
# @return [TTY::Prompt] A new TTY::Prompt instance with specified configurations.
|
1584
|
-
def tty_prompt_without_disabled_symbol
|
1585
|
-
TTY::Prompt.new(
|
1586
|
-
interrupt: lambda {
|
1587
|
-
puts
|
1588
|
-
raise TTY::Reader::InputInterrupt
|
1589
|
-
},
|
1590
|
-
symbols: { cross: ' ' }
|
1591
|
-
)
|
1592
|
-
end
|
1593
|
-
|
1594
|
-
# Updates the hierarchy of document headings based on the given line.
|
1595
|
-
# Utilizes regular expressions to identify heading levels.
|
1596
|
-
# @param line [String] The line of text to check for headings.
|
1597
|
-
# @param headings [Array<String>] Current headings hierarchy.
|
1598
|
-
# @return [Array<String>] Updated headings hierarchy.
|
1599
|
-
def update_document_headings(line, headings)
|
1600
|
-
heading3_match = Regexp.new(@delegate_object[:heading3_match])
|
1601
|
-
heading2_match = Regexp.new(@delegate_object[:heading2_match])
|
1602
|
-
heading1_match = Regexp.new(@delegate_object[:heading1_match])
|
1603
|
-
|
1604
|
-
case line
|
1605
|
-
when heading3_match
|
1606
|
-
[headings[0], headings[1], $~[:name]]
|
1607
|
-
when heading2_match
|
1608
|
-
[headings[0], $~[:name]]
|
1609
|
-
when heading1_match
|
1610
|
-
[$~[:name]]
|
1611
|
-
else
|
1612
|
-
headings
|
1613
|
-
end
|
1652
|
+
def string_send_color(string, color_sym)
|
1653
|
+
HashDelegator.apply_color_from_hash(string, @delegate_object, color_sym)
|
1614
1654
|
end
|
1615
1655
|
|
1616
1656
|
##
|
@@ -1635,16 +1675,12 @@ module MarkdownExec
|
|
1635
1675
|
def update_line_and_block_state(nested_line, state, selected_messages,
|
1636
1676
|
&block)
|
1637
1677
|
line = nested_line.to_s
|
1638
|
-
if @delegate_object[:menu_blocks_with_headings]
|
1639
|
-
state[:headings] = update_document_headings(line, state[:headings])
|
1640
|
-
end
|
1641
|
-
|
1642
1678
|
if line.match(@delegate_object[:fenced_start_and_end_regex])
|
1643
1679
|
if state[:in_fenced_block]
|
1644
1680
|
## end of code block
|
1645
1681
|
#
|
1646
|
-
update_menu_attrib_yield_selected(state[:fcb], selected_messages,
|
1647
|
-
|
1682
|
+
HashDelegator.update_menu_attrib_yield_selected(state[:fcb], selected_messages, @delegate_object,
|
1683
|
+
&block)
|
1648
1684
|
state[:in_fenced_block] = false
|
1649
1685
|
else
|
1650
1686
|
## start of code block
|
@@ -1665,29 +1701,14 @@ module MarkdownExec
|
|
1665
1701
|
elsif nested_line[:depth].zero? || @delegate_object[:menu_include_imported_notes]
|
1666
1702
|
# add line if it is depth 0 or option allows it
|
1667
1703
|
#
|
1668
|
-
yield_line_if_selected(line, selected_messages, &block)
|
1704
|
+
HashDelegator.yield_line_if_selected(line, selected_messages, &block)
|
1669
1705
|
|
1670
1706
|
else
|
1671
|
-
#
|
1707
|
+
# &bsp 'line is not recognized for block state'
|
1672
1708
|
|
1673
1709
|
end
|
1674
1710
|
end
|
1675
1711
|
|
1676
|
-
# Updates the attributes of the given fcb object and conditionally yields to a block.
|
1677
|
-
# It initializes fcb names and sets the default block title from fcb's body.
|
1678
|
-
# If the fcb has a body and meets certain conditions, it yields to the given block.
|
1679
|
-
#
|
1680
|
-
# @param fcb [Object] The fcb object whose attributes are to be updated.
|
1681
|
-
# @param selected_messages [Array<Symbol>] A list of message types to determine if yielding is applicable.
|
1682
|
-
# @param block [Block] An optional block to yield to if conditions are met.
|
1683
|
-
def update_menu_attrib_yield_selected(fcb, selected_messages, &block)
|
1684
|
-
initialize_fcb_names(fcb)
|
1685
|
-
return unless fcb.body
|
1686
|
-
|
1687
|
-
default_block_title_from_body(fcb)
|
1688
|
-
yield_to_block_if_applicable(fcb, selected_messages, &block)
|
1689
|
-
end
|
1690
|
-
|
1691
1712
|
# Processes YAML data from the selected menu item, updating delegate objects and optionally printing formatted output.
|
1692
1713
|
# @param selected [Hash] Selected item from the menu containing a YAML body.
|
1693
1714
|
# @param tgt2 [Hash, nil] An optional target hash to update with YAML data.
|
@@ -1717,10 +1738,10 @@ module MarkdownExec
|
|
1717
1738
|
|
1718
1739
|
def wait_for_user_selected_block(all_blocks, menu_blocks, default)
|
1719
1740
|
block_state = wait_for_user_selection(all_blocks, menu_blocks, default)
|
1720
|
-
|
1741
|
+
handle_back_or_continue(block_state)
|
1721
1742
|
block_state
|
1722
1743
|
rescue StandardError
|
1723
|
-
error_handler('wait_for_user_selected_block')
|
1744
|
+
HashDelegator.error_handler('wait_for_user_selected_block')
|
1724
1745
|
end
|
1725
1746
|
|
1726
1747
|
def wait_for_user_selection(_all_blocks, menu_blocks, default)
|
@@ -1771,32 +1792,13 @@ module MarkdownExec
|
|
1771
1792
|
"# time: #{time_now}\n" \
|
1772
1793
|
"#{required_lines.flatten.join("\n")}\n"
|
1773
1794
|
|
1774
|
-
create_file_and_write_string_with_permissions(
|
1795
|
+
HashDelegator.create_file_and_write_string_with_permissions(
|
1775
1796
|
@run_state.saved_filespec,
|
1776
1797
|
content,
|
1777
1798
|
@delegate_object[:saved_script_chmod]
|
1778
1799
|
)
|
1779
1800
|
rescue StandardError
|
1780
|
-
error_handler('write_command_file')
|
1781
|
-
end
|
1782
|
-
|
1783
|
-
def save_executed_script_if_specified(lines)
|
1784
|
-
write_command_file(lines) if @delegate_object[:save_executed_script]
|
1785
|
-
end
|
1786
|
-
|
1787
|
-
def write_execution_output_to_file
|
1788
|
-
FileUtils.mkdir_p File.dirname(@delegate_object[:logged_stdout_filespec])
|
1789
|
-
|
1790
|
-
File.write(
|
1791
|
-
@delegate_object[:logged_stdout_filespec],
|
1792
|
-
["-STDOUT-\n",
|
1793
|
-
format_execution_streams(ExecutionStreams::StdOut),
|
1794
|
-
"-STDERR-\n",
|
1795
|
-
format_execution_streams(ExecutionStreams::StdErr),
|
1796
|
-
"-STDIN-\n",
|
1797
|
-
format_execution_streams(ExecutionStreams::StdIn),
|
1798
|
-
"\n"].join
|
1799
|
-
)
|
1801
|
+
HashDelegator.error_handler('write_command_file')
|
1800
1802
|
end
|
1801
1803
|
|
1802
1804
|
# Writes required code blocks to a temporary file and sets an environment variable with its path.
|
@@ -1814,40 +1816,10 @@ module MarkdownExec
|
|
1814
1816
|
[]
|
1815
1817
|
end
|
1816
1818
|
|
1817
|
-
code_blocks = (read_required_blocks_from_temp_file(import_filename) +
|
1819
|
+
code_blocks = (HashDelegator.read_required_blocks_from_temp_file(import_filename) +
|
1818
1820
|
c1).join("\n")
|
1819
1821
|
|
1820
|
-
write_code_to_file(code_blocks, temp_file_path)
|
1821
|
-
end
|
1822
|
-
|
1823
|
-
# Writes the provided code blocks to a file.
|
1824
|
-
# @param code_blocks [String] Code blocks to write into the file.
|
1825
|
-
def write_code_to_file(content, path)
|
1826
|
-
File.write(path, content)
|
1827
|
-
end
|
1828
|
-
|
1829
|
-
# Yields a line as a new block if the selected message type includes :line.
|
1830
|
-
# @param [String] line The line to be processed.
|
1831
|
-
# @param [Array<Symbol>] selected_messages A list of message types to check.
|
1832
|
-
# @param [Proc] block The block to be called with the line data.
|
1833
|
-
def yield_line_if_selected(line, selected_messages, &block)
|
1834
|
-
return unless block && selected_messages.include?(:line)
|
1835
|
-
|
1836
|
-
block.call(:line, FCB.new(body: [line]))
|
1837
|
-
end
|
1838
|
-
|
1839
|
-
# Yields to the provided block with specified parameters if certain conditions are met.
|
1840
|
-
# The method checks if a block is given, if the selected_messages include :blocks,
|
1841
|
-
# and if the fcb_select? method from MarkdownExec::Filter returns true for the given fcb.
|
1842
|
-
#
|
1843
|
-
# @param fcb [Object] The object to be evaluated and potentially passed to the block.
|
1844
|
-
# @param selected_messages [Array<Symbol>] A collection of message types, one of which must be :blocks.
|
1845
|
-
# @param block [Block] A block to be called if conditions are met.
|
1846
|
-
def yield_to_block_if_applicable(fcb, selected_messages, &block)
|
1847
|
-
if block_given? && selected_messages.include?(:blocks) &&
|
1848
|
-
MarkdownExec::Filter.fcb_select?(@delegate_object, fcb)
|
1849
|
-
block.call :blocks, fcb
|
1850
|
-
end
|
1822
|
+
HashDelegator.write_code_to_file(code_blocks, temp_file_path)
|
1851
1823
|
end
|
1852
1824
|
end
|
1853
1825
|
end
|
@@ -1917,30 +1889,30 @@ if $PROGRAM_NAME == __FILE__
|
|
1917
1889
|
body = "Line 1\nLine 2"
|
1918
1890
|
indent = ' ' # Two spaces
|
1919
1891
|
expected_result = " Line 1\n Line 2"
|
1920
|
-
assert_equal expected_result,
|
1892
|
+
assert_equal expected_result, HashDelegator.indent_all_lines(body, indent)
|
1921
1893
|
end
|
1922
1894
|
|
1923
1895
|
def test_indent_all_lines_without_indent
|
1924
1896
|
body = "Line 1\nLine 2"
|
1925
1897
|
indent = nil
|
1926
1898
|
|
1927
|
-
assert_equal body,
|
1899
|
+
assert_equal body, HashDelegator.indent_all_lines(body, indent)
|
1928
1900
|
end
|
1929
1901
|
|
1930
1902
|
def test_indent_all_lines_with_empty_indent
|
1931
1903
|
body = "Line 1\nLine 2"
|
1932
1904
|
indent = ''
|
1933
1905
|
|
1934
|
-
assert_equal body,
|
1906
|
+
assert_equal body, HashDelegator.indent_all_lines(body, indent)
|
1935
1907
|
end
|
1936
1908
|
|
1937
1909
|
def test_safeval_successful_evaluation
|
1938
|
-
assert_equal 4,
|
1910
|
+
assert_equal 4, HashDelegator.safeval('2 + 2')
|
1939
1911
|
end
|
1940
1912
|
|
1941
1913
|
def test_safeval_rescue_from_error
|
1942
|
-
|
1943
|
-
assert_nil
|
1914
|
+
HashDelegator.stubs(:error_handler).with('safeval')
|
1915
|
+
assert_nil HashDelegator.safeval('invalid code')
|
1944
1916
|
end
|
1945
1917
|
|
1946
1918
|
def test_set_fcb_title
|
@@ -1968,7 +1940,7 @@ if $PROGRAM_NAME == __FILE__
|
|
1968
1940
|
input_output_data.each do |data|
|
1969
1941
|
input = data[:input]
|
1970
1942
|
output = data[:output]
|
1971
|
-
|
1943
|
+
HashDelegator.default_block_title_from_body(input)
|
1972
1944
|
assert_equal output, input.title
|
1973
1945
|
end
|
1974
1946
|
end
|
@@ -1983,7 +1955,7 @@ if $PROGRAM_NAME == __FILE__
|
|
1983
1955
|
menu_divider_color: :color
|
1984
1956
|
})
|
1985
1957
|
@hd.stubs(:string_send_color).returns('Formatted Divider')
|
1986
|
-
|
1958
|
+
HashDelegator.stubs(:safeval).returns('Safe Value')
|
1987
1959
|
end
|
1988
1960
|
|
1989
1961
|
def test_append_divider_initial
|
@@ -2018,19 +1990,19 @@ if $PROGRAM_NAME == __FILE__
|
|
2018
1990
|
|
2019
1991
|
def test_block_find_with_match
|
2020
1992
|
blocks = [{ key: 'value1' }, { key: 'value2' }]
|
2021
|
-
result =
|
1993
|
+
result = HashDelegator.block_find(blocks, :key, 'value1')
|
2022
1994
|
assert_equal({ key: 'value1' }, result)
|
2023
1995
|
end
|
2024
1996
|
|
2025
1997
|
def test_block_find_without_match
|
2026
1998
|
blocks = [{ key: 'value1' }, { key: 'value2' }]
|
2027
|
-
result =
|
1999
|
+
result = HashDelegator.block_find(blocks, :key, 'value3')
|
2028
2000
|
assert_nil result
|
2029
2001
|
end
|
2030
2002
|
|
2031
2003
|
def test_block_find_with_default
|
2032
2004
|
blocks = [{ key: 'value1' }, { key: 'value2' }]
|
2033
|
-
result =
|
2005
|
+
result = HashDelegator.block_find(blocks, :key, 'value3', 'default')
|
2034
2006
|
assert_equal 'default', result
|
2035
2007
|
end
|
2036
2008
|
end
|
@@ -2042,7 +2014,7 @@ if $PROGRAM_NAME == __FILE__
|
|
2042
2014
|
@hd.stubs(:get_block_summary).returns(FCB.new)
|
2043
2015
|
@hd.stubs(:create_and_add_chrome_blocks)
|
2044
2016
|
@hd.instance_variable_set(:@delegate_object, {})
|
2045
|
-
|
2017
|
+
HashDelegator.stubs(:error_handler)
|
2046
2018
|
end
|
2047
2019
|
|
2048
2020
|
def test_blocks_from_nested_files
|
@@ -2068,7 +2040,7 @@ if $PROGRAM_NAME == __FILE__
|
|
2068
2040
|
@hd.instance_variable_set(:@delegate_object, {})
|
2069
2041
|
@mdoc = mock('YourMDocClass')
|
2070
2042
|
@selected = { shell: BlockType::VARS, body: ['key: value'] }
|
2071
|
-
|
2043
|
+
HashDelegator.stubs(:read_required_blocks_from_temp_file).returns([])
|
2072
2044
|
@hd.stubs(:string_send_color)
|
2073
2045
|
@hd.stubs(:print)
|
2074
2046
|
end
|
@@ -2086,7 +2058,7 @@ if $PROGRAM_NAME == __FILE__
|
|
2086
2058
|
def setup
|
2087
2059
|
@hd = HashDelegator.new
|
2088
2060
|
@hd.instance_variable_set(:@delegate_object, {})
|
2089
|
-
|
2061
|
+
HashDelegator.stubs(:error_handler)
|
2090
2062
|
@hd.stubs(:wait_for_user_selected_block)
|
2091
2063
|
end
|
2092
2064
|
|
@@ -2097,7 +2069,7 @@ if $PROGRAM_NAME == __FILE__
|
|
2097
2069
|
|
2098
2070
|
result = @hd.load_cli_or_user_selected_block(all_blocks, [], nil)
|
2099
2071
|
|
2100
|
-
assert_equal all_blocks.first, result.block
|
2072
|
+
assert_equal all_blocks.first.merge(block_name_from_ui: false), result.block
|
2101
2073
|
assert_nil result.state
|
2102
2074
|
end
|
2103
2075
|
|
@@ -2108,7 +2080,7 @@ if $PROGRAM_NAME == __FILE__
|
|
2108
2080
|
|
2109
2081
|
result = @hd.load_cli_or_user_selected_block([], [], nil)
|
2110
2082
|
|
2111
|
-
assert_equal block_state.block, result.block
|
2083
|
+
assert_equal block_state.block.merge(block_name_from_ui: true), result.block
|
2112
2084
|
assert_equal :some_state, result.state
|
2113
2085
|
end
|
2114
2086
|
end
|
@@ -2145,7 +2117,7 @@ if $PROGRAM_NAME == __FILE__
|
|
2145
2117
|
class TestHashDelegatorCreateAndWriteFile < Minitest::Test
|
2146
2118
|
def setup
|
2147
2119
|
@hd = HashDelegator.new
|
2148
|
-
|
2120
|
+
HashDelegator.stubs(:error_handler)
|
2149
2121
|
FileUtils.stubs(:mkdir_p)
|
2150
2122
|
File.stubs(:write)
|
2151
2123
|
File.stubs(:chmod)
|
@@ -2160,8 +2132,8 @@ if $PROGRAM_NAME == __FILE__
|
|
2160
2132
|
File.expects(:write).with(file_path, content).once
|
2161
2133
|
File.expects(:chmod).with(chmod_value, file_path).once
|
2162
2134
|
|
2163
|
-
|
2164
|
-
|
2135
|
+
HashDelegator.create_file_and_write_string_with_permissions(file_path, content,
|
2136
|
+
chmod_value)
|
2165
2137
|
|
2166
2138
|
assert true # Placeholder for actual test assertions
|
2167
2139
|
end
|
@@ -2175,8 +2147,8 @@ if $PROGRAM_NAME == __FILE__
|
|
2175
2147
|
File.expects(:write).with(file_path, content).once
|
2176
2148
|
File.expects(:chmod).never
|
2177
2149
|
|
2178
|
-
|
2179
|
-
|
2150
|
+
HashDelegator.create_file_and_write_string_with_permissions(file_path, content,
|
2151
|
+
chmod_value)
|
2180
2152
|
|
2181
2153
|
assert true # Placeholder for actual test assertions
|
2182
2154
|
end
|
@@ -2307,11 +2279,8 @@ if $PROGRAM_NAME == __FILE__
|
|
2307
2279
|
end
|
2308
2280
|
|
2309
2281
|
def test_format_execution_streams_with_valid_key
|
2310
|
-
|
2311
|
-
|
2312
|
-
] })
|
2313
|
-
|
2314
|
-
result = @hd.format_execution_streams(:stdout)
|
2282
|
+
result = HashDelegator.format_execution_streams(:stdout,
|
2283
|
+
{ stdout: %w[output1 output2] })
|
2315
2284
|
|
2316
2285
|
assert_equal 'output1output2', result
|
2317
2286
|
end
|
@@ -2319,7 +2288,7 @@ if $PROGRAM_NAME == __FILE__
|
|
2319
2288
|
def test_format_execution_streams_with_empty_key
|
2320
2289
|
@hd.instance_variable_get(:@run_state).stubs(:files).returns({})
|
2321
2290
|
|
2322
|
-
result =
|
2291
|
+
result = HashDelegator.format_execution_streams(:stderr)
|
2323
2292
|
|
2324
2293
|
assert_equal '', result
|
2325
2294
|
end
|
@@ -2327,7 +2296,7 @@ if $PROGRAM_NAME == __FILE__
|
|
2327
2296
|
def test_format_execution_streams_with_nil_files
|
2328
2297
|
@hd.instance_variable_get(:@run_state).stubs(:files).returns(nil)
|
2329
2298
|
|
2330
|
-
result =
|
2299
|
+
result = HashDelegator.format_execution_streams(:stdin)
|
2331
2300
|
|
2332
2301
|
assert_equal '', result
|
2333
2302
|
end
|
@@ -2358,33 +2327,33 @@ if $PROGRAM_NAME == __FILE__
|
|
2358
2327
|
@mock_block_state = mock('block_state')
|
2359
2328
|
end
|
2360
2329
|
|
2361
|
-
def
|
2330
|
+
def test_handle_back_or_continue_with_back
|
2362
2331
|
@mock_block_state.stubs(:state).returns(MenuState::BACK)
|
2363
2332
|
@mock_block_state.stubs(:block).returns({ oname: 'sample_block' })
|
2364
2333
|
|
2365
|
-
@hd.
|
2334
|
+
@hd.handle_back_or_continue(@mock_block_state)
|
2366
2335
|
|
2367
2336
|
assert_equal 'sample_block',
|
2368
2337
|
@hd.instance_variable_get(:@delegate_object)[:block_name]
|
2369
2338
|
assert @hd.instance_variable_get(:@menu_user_clicked_back_link)
|
2370
2339
|
end
|
2371
2340
|
|
2372
|
-
def
|
2341
|
+
def test_handle_back_or_continue_with_continue
|
2373
2342
|
@mock_block_state.stubs(:state).returns(MenuState::CONTINUE)
|
2374
2343
|
@mock_block_state.stubs(:block).returns({ oname: 'another_block' })
|
2375
2344
|
|
2376
|
-
@hd.
|
2345
|
+
@hd.handle_back_or_continue(@mock_block_state)
|
2377
2346
|
|
2378
2347
|
assert_equal 'another_block',
|
2379
2348
|
@hd.instance_variable_get(:@delegate_object)[:block_name]
|
2380
2349
|
refute @hd.instance_variable_get(:@menu_user_clicked_back_link)
|
2381
2350
|
end
|
2382
2351
|
|
2383
|
-
def
|
2352
|
+
def test_handle_back_or_continue_with_other
|
2384
2353
|
@mock_block_state.stubs(:state).returns(nil) # MenuState::OTHER
|
2385
2354
|
@mock_block_state.stubs(:block).returns({ oname: 'other_block' })
|
2386
2355
|
|
2387
|
-
@hd.
|
2356
|
+
@hd.handle_back_or_continue(@mock_block_state)
|
2388
2357
|
|
2389
2358
|
assert_nil @hd.instance_variable_get(:@delegate_object)[:block_name]
|
2390
2359
|
assert_nil @hd.instance_variable_get(:@menu_user_clicked_back_link)
|
@@ -2529,7 +2498,7 @@ if $PROGRAM_NAME == __FILE__
|
|
2529
2498
|
menu_option_back_name: "'Back'",
|
2530
2499
|
menu_chrome_format: '-- %s --'
|
2531
2500
|
})
|
2532
|
-
|
2501
|
+
HashDelegator.stubs(:safeval).with("'Back'").returns('Back')
|
2533
2502
|
end
|
2534
2503
|
|
2535
2504
|
def test_menu_chrome_formatted_option_with_format
|
@@ -2584,7 +2553,7 @@ if $PROGRAM_NAME == __FILE__
|
|
2584
2553
|
|
2585
2554
|
def test_yield_line_if_selected_with_line
|
2586
2555
|
block_called = false
|
2587
|
-
|
2556
|
+
HashDelegator.yield_line_if_selected('Test line', [:line]) do |type, content|
|
2588
2557
|
block_called = true
|
2589
2558
|
assert_equal :line, type
|
2590
2559
|
assert_equal 'Test line', content.body[0]
|
@@ -2594,70 +2563,47 @@ if $PROGRAM_NAME == __FILE__
|
|
2594
2563
|
|
2595
2564
|
def test_yield_line_if_selected_without_line
|
2596
2565
|
block_called = false
|
2597
|
-
|
2566
|
+
HashDelegator.yield_line_if_selected('Test line', [:other]) do |_|
|
2598
2567
|
block_called = true
|
2599
2568
|
end
|
2600
2569
|
refute block_called
|
2601
2570
|
end
|
2602
2571
|
|
2603
2572
|
def test_yield_line_if_selected_without_block
|
2604
|
-
result =
|
2573
|
+
result = HashDelegator.yield_line_if_selected('Test line', [:line])
|
2605
2574
|
assert_nil result
|
2606
2575
|
end
|
2607
2576
|
end
|
2608
2577
|
|
2609
|
-
class TestHashDelegator < Minitest::Test
|
2610
|
-
def setup
|
2611
|
-
@hd = HashDelegator.new
|
2612
|
-
@hd.instance_variable_set(:@delegate_object, {
|
2613
|
-
heading1_match: '^# (?<name>.+)$',
|
2614
|
-
heading2_match: '^## (?<name>.+)$',
|
2615
|
-
heading3_match: '^### (?<name>.+)$'
|
2616
|
-
})
|
2617
|
-
end
|
2618
|
-
|
2619
|
-
def test_update_document_headings
|
2620
|
-
assert_equal(['Heading 1'],
|
2621
|
-
@hd.update_document_headings('# Heading 1', []))
|
2622
|
-
assert_equal(['Heading 1', 'Heading 2'],
|
2623
|
-
@hd.update_document_headings('## Heading 2',
|
2624
|
-
['Heading 1']))
|
2625
|
-
assert_equal(['Heading 1', 'Heading 2', 'Heading 3'],
|
2626
|
-
@hd.update_document_headings('### Heading 3',
|
2627
|
-
['Heading 1', 'Heading 2']))
|
2628
|
-
assert_equal([], @hd.update_document_headings('Regular text', []))
|
2629
|
-
end
|
2630
|
-
end
|
2631
|
-
|
2632
2578
|
class TestHashDelegatorUpdateMenuAttribYieldSelectedWithBody < Minitest::Test
|
2633
2579
|
def setup
|
2634
2580
|
@hd = HashDelegator.new
|
2635
2581
|
@fcb = mock('Fcb')
|
2636
2582
|
@fcb.stubs(:body).returns(true)
|
2637
|
-
|
2638
|
-
|
2639
|
-
|
2583
|
+
HashDelegator.stubs(:initialize_fcb_names)
|
2584
|
+
HashDelegator.stubs(:default_block_title_from_body)
|
2585
|
+
Filter.stubs(:yield_to_block_if_applicable)
|
2640
2586
|
end
|
2641
2587
|
|
2642
2588
|
def test_update_menu_attrib_yield_selected_with_body
|
2643
|
-
|
2644
|
-
|
2645
|
-
|
2589
|
+
HashDelegator.expects(:initialize_fcb_names).with(@fcb)
|
2590
|
+
HashDelegator.expects(:default_block_title_from_body).with(@fcb)
|
2591
|
+
Filter.expects(:yield_to_block_if_applicable).with(@fcb, [:some_message], {})
|
2646
2592
|
|
2647
|
-
|
2593
|
+
HashDelegator.update_menu_attrib_yield_selected(@fcb, [:some_message])
|
2648
2594
|
end
|
2649
2595
|
|
2650
2596
|
def test_update_menu_attrib_yield_selected_without_body
|
2651
2597
|
@fcb.stubs(:body).returns(nil)
|
2652
|
-
|
2653
|
-
|
2598
|
+
HashDelegator.expects(:initialize_fcb_names).with(@fcb)
|
2599
|
+
HashDelegator.update_menu_attrib_yield_selected(@fcb, [:some_message])
|
2654
2600
|
end
|
2655
2601
|
end
|
2656
2602
|
|
2657
2603
|
class TestHashDelegatorWaitForUserSelectedBlock < Minitest::Test
|
2658
2604
|
def setup
|
2659
2605
|
@hd = HashDelegator.new
|
2660
|
-
|
2606
|
+
HashDelegator.stubs(:error_handler)
|
2661
2607
|
end
|
2662
2608
|
|
2663
2609
|
def test_wait_for_user_selected_block_with_back_state
|
@@ -2699,7 +2645,7 @@ if $PROGRAM_NAME == __FILE__
|
|
2699
2645
|
|
2700
2646
|
def test_yield_to_block_if_applicable_with_correct_conditions
|
2701
2647
|
block_called = false
|
2702
|
-
|
2648
|
+
Filter.yield_to_block_if_applicable(@fcb, [:blocks]) do |type, fcb|
|
2703
2649
|
block_called = true
|
2704
2650
|
assert_equal :blocks, type
|
2705
2651
|
assert_equal @fcb, fcb
|
@@ -2708,14 +2654,14 @@ if $PROGRAM_NAME == __FILE__
|
|
2708
2654
|
end
|
2709
2655
|
|
2710
2656
|
def test_yield_to_block_if_applicable_without_block
|
2711
|
-
result =
|
2657
|
+
result = Filter.yield_to_block_if_applicable(@fcb, [:blocks])
|
2712
2658
|
assert_nil result
|
2713
2659
|
end
|
2714
2660
|
|
2715
2661
|
def test_yield_to_block_if_applicable_with_incorrect_conditions
|
2716
2662
|
block_called = false
|
2717
2663
|
MarkdownExec::Filter.stubs(:fcb_select?).returns(false)
|
2718
|
-
|
2664
|
+
Filter.yield_to_block_if_applicable(@fcb, [:non_blocks]) do |_|
|
2719
2665
|
block_called = true
|
2720
2666
|
end
|
2721
2667
|
refute block_called
|