markdown_exec 1.8.6 → 1.8.8
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 +3 -2
- data/CHANGELOG.md +45 -3
- data/Gemfile +1 -0
- data/Gemfile.lock +7 -1
- data/Rakefile +1 -0
- data/bin/bmde +1 -1
- data/bin/tab_completion.sh +4 -4
- data/examples/colors.md +2 -1
- data/examples/indent.md +66 -0
- data/examples/linked.md +20 -1
- data/examples/load1.sh +5 -0
- data/examples/load2.sh +5 -0
- data/examples/nickname.md +26 -0
- data/examples/opts.md +2 -0
- data/lib/ansi_formatter.rb +1 -5
- data/lib/array.rb +4 -0
- data/lib/cached_nested_file_reader.rb +47 -43
- data/lib/ce_get_cost_and_usage.rb +22 -0
- data/lib/constants.rb +7 -0
- data/lib/exceptions.rb +6 -4
- data/lib/fcb.rb +5 -2
- data/lib/filter.rb +21 -11
- data/lib/find_files.rb +99 -0
- data/lib/hash_delegator.rb +1358 -1179
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/mdoc.rb +59 -39
- data/lib/menu.src.yml +201 -21
- data/lib/menu.yml +155 -11
- data/lib/saved_assets.rb +10 -8
- metadata +10 -8
- data/lib/method_sorter.rb +0 -78
- data/lib/pty1.rb +0 -26
- data/lib/regexp_replacer.rb +0 -58
- data/lib/sort_yaml_gpt4.rb +0 -32
data/lib/hash_delegator.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
#!/usr/bin/env bundle exec ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
# encoding=utf-8
|
5
|
+
|
4
6
|
require 'English'
|
5
7
|
require 'clipboard'
|
6
8
|
require 'fileutils'
|
@@ -37,6 +39,273 @@ class String
|
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
42
|
+
module HashDelegatorSelf
|
43
|
+
# def add_back_option(menu_blocks)
|
44
|
+
# append_chrome_block(menu_blocks, MenuState::BACK)
|
45
|
+
# end
|
46
|
+
|
47
|
+
# Applies an ANSI color method to a string using a specified color key.
|
48
|
+
# The method retrieves the color method from the provided hash. If the color key
|
49
|
+
# is not present in the hash, it uses a default color method.
|
50
|
+
# @param string [String] The string to be colored.
|
51
|
+
# @param color_methods [Hash] A hash where keys are color names (String/Symbol) and values are color methods.
|
52
|
+
# @param color_key [String, Symbol] The key representing the desired color method in the color_methods hash.
|
53
|
+
# @param default_method [String] (optional) Default color method to use if color_key is not found in color_methods. Defaults to 'plain'.
|
54
|
+
# @return [String] The colored string.
|
55
|
+
def apply_color_from_hash(string, color_methods, color_key, default_method: 'plain')
|
56
|
+
color_method = color_methods.fetch(color_key, default_method).to_sym
|
57
|
+
string.to_s.send(color_method)
|
58
|
+
end
|
59
|
+
|
60
|
+
# # Enhanced `apply_color_from_hash` method to support dynamic color transformations
|
61
|
+
# # @param string [String] The string to be colored.
|
62
|
+
# # @param color_transformations [Hash] A hash mapping color names to lambdas that apply color transformations.
|
63
|
+
# # @param color_key [String, Symbol] The key representing the desired color transformation in the color_transformations hash.
|
64
|
+
# # @param default_transformation [Proc] Default color transformation to use if color_key is not found in color_transformations.
|
65
|
+
# # @return [String] The colored string.
|
66
|
+
# def apply_color_from_hash(string, color_transformations, color_key, default_transformation: ->(str) { str })
|
67
|
+
# transformation = color_transformations.fetch(color_key.to_sym, default_transformation)
|
68
|
+
# transformation.call(string)
|
69
|
+
# end
|
70
|
+
# color_transformations = {
|
71
|
+
# red: ->(str) { "\e[31m#{str}\e[0m" }, # ANSI color code for red
|
72
|
+
# green: ->(str) { "\e[32m#{str}\e[0m" }, # ANSI color code for green
|
73
|
+
# # Add more color transformations as needed
|
74
|
+
# }
|
75
|
+
# string = "Hello, World!"
|
76
|
+
# colored_string = apply_color_from_hash(string, color_transformations, :red)
|
77
|
+
# puts colored_string # This will print the string in red
|
78
|
+
|
79
|
+
# Searches for the first element in a collection where the specified key matches a given value.
|
80
|
+
# This method is particularly useful for finding a specific hash-like object within an enumerable collection.
|
81
|
+
# If no match is found, it returns a specified default value.
|
82
|
+
#
|
83
|
+
# @param blocks [Enumerable] The collection of hash-like objects to search.
|
84
|
+
# @param key [Object] The key to search for in each element of the collection.
|
85
|
+
# @param value [Object] The value to match against each element's corresponding key value.
|
86
|
+
# @param default [Object, nil] The default value to return if no match is found (optional).
|
87
|
+
# @return [Object, nil] The first matching element or the default value if no match is found.
|
88
|
+
def block_find(blocks, key, value, default = nil)
|
89
|
+
blocks.find { |item| item[key] == value } || default
|
90
|
+
end
|
91
|
+
|
92
|
+
def code_merge(*bodies)
|
93
|
+
merge_lists(*bodies)
|
94
|
+
end
|
95
|
+
|
96
|
+
def count_matches_in_lines(lines, regex)
|
97
|
+
lines.count { |line| line.to_s.match(regex) }
|
98
|
+
end
|
99
|
+
|
100
|
+
def create_directory_for_file(file_path)
|
101
|
+
FileUtils.mkdir_p(File.dirname(file_path))
|
102
|
+
end
|
103
|
+
|
104
|
+
# Creates a file at the specified path, writes the given content to it,
|
105
|
+
# and sets file permissions if required. Handles any errors encountered during the process.
|
106
|
+
#
|
107
|
+
# @param file_path [String] The path where the file will be created.
|
108
|
+
# @param content [String] The content to write into the file.
|
109
|
+
# @param chmod_value [Integer] The file permission value to set; skips if zero.
|
110
|
+
def create_file_and_write_string_with_permissions(file_path, content,
|
111
|
+
chmod_value)
|
112
|
+
create_directory_for_file(file_path)
|
113
|
+
File.write(file_path, content)
|
114
|
+
set_file_permissions(file_path, chmod_value) unless chmod_value.zero?
|
115
|
+
rescue StandardError
|
116
|
+
error_handler('create_file_and_write_string_with_permissions')
|
117
|
+
end
|
118
|
+
|
119
|
+
# def create_temp_file
|
120
|
+
# Dir::Tmpname.create(self.class.to_s) { |path| path }
|
121
|
+
# end
|
122
|
+
|
123
|
+
# Updates the title of an FCB object from its body content if the title is nil or empty.
|
124
|
+
def default_block_title_from_body(fcb)
|
125
|
+
return unless fcb.title.nil? || fcb.title.empty?
|
126
|
+
|
127
|
+
fcb.derive_title_from_body
|
128
|
+
end
|
129
|
+
|
130
|
+
# delete the current line if it is empty and the previous is also empty
|
131
|
+
def delete_consecutive_blank_lines!(blocks_menu)
|
132
|
+
blocks_menu.process_and_conditionally_delete! do |prev_item, current_item, _next_item|
|
133
|
+
prev_item&.fetch(:chrome, nil) && !prev_item&.fetch(:oname).present? &&
|
134
|
+
current_item&.fetch(:chrome, nil) && !current_item&.fetch(:oname).present?
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# # Deletes a temporary file specified by an environment variable.
|
139
|
+
# # Checks if the file exists before attempting to delete it and clears the environment variable afterward.
|
140
|
+
# # Any errors encountered during deletion are handled gracefully.
|
141
|
+
# def delete_required_temp_file(temp_blocks_file_path)
|
142
|
+
# return if temp_blocks_file_path.nil? || temp_blocks_file_path.empty?
|
143
|
+
|
144
|
+
# HashDelegator.remove_file_without_standard_errors(temp_blocks_file_path)
|
145
|
+
# end
|
146
|
+
|
147
|
+
def error_handler(name = '', opts = {}, error: $!)
|
148
|
+
Exceptions.error_handler(
|
149
|
+
"HashDelegator.#{name} -- #{error}",
|
150
|
+
opts
|
151
|
+
)
|
152
|
+
end
|
153
|
+
|
154
|
+
# # DebugHelper.d ["HDmm method_name: #{method_name}", "#{first_n_caller_items 1}"]
|
155
|
+
# def first_n_caller_items(n)
|
156
|
+
# call_stack = caller
|
157
|
+
# base_path = File.realpath('.')
|
158
|
+
|
159
|
+
# # Modify the call stack to remove the base path and keep only the first n items
|
160
|
+
# call_stack.take(n + 1)[1..].map do |line|
|
161
|
+
# " . #{line.sub(/^#{Regexp.escape(base_path)}\//, '')}"
|
162
|
+
# end.join("\n")
|
163
|
+
# end
|
164
|
+
|
165
|
+
# Formats and returns the execution streams (like stdin, stdout, stderr) for a given key.
|
166
|
+
# It concatenates the array of strings found under the specified key in the run_state's files.
|
167
|
+
#
|
168
|
+
# @param key [Symbol] The key corresponding to the desired execution stream.
|
169
|
+
# @return [String] A concatenated string of the execution stream's contents.
|
170
|
+
def format_execution_streams(key, files = {})
|
171
|
+
(files || {}).fetch(key, []).join
|
172
|
+
end
|
173
|
+
|
174
|
+
# Indents all lines in a given string with a specified indentation string.
|
175
|
+
# @param body [String] A multi-line string to be indented.
|
176
|
+
# @param indent [String] The string used for indentation (default is an empty string).
|
177
|
+
# @return [String] A single string with each line indented as specified.
|
178
|
+
def indent_all_lines(body, indent = nil)
|
179
|
+
return body unless indent&.non_empty?
|
180
|
+
|
181
|
+
body.lines.map { |line| indent + line.chomp }.join("\n")
|
182
|
+
end
|
183
|
+
|
184
|
+
def initialize_fcb_names(fcb)
|
185
|
+
fcb.oname = fcb.dname = fcb.title || ''
|
186
|
+
end
|
187
|
+
|
188
|
+
def merge_lists(*args)
|
189
|
+
# Filters out nil values, flattens the arrays, and ensures an empty list is returned if no valid lists are provided
|
190
|
+
merged = args.compact.flatten
|
191
|
+
merged.empty? ? [] : merged
|
192
|
+
end
|
193
|
+
|
194
|
+
def next_link_state(block_name_from_cli, was_using_cli, block_state)
|
195
|
+
# &bsp 'next_link_state', block_name_from_cli, was_using_cli, block_state
|
196
|
+
# Set block_name based on block_name_from_cli
|
197
|
+
block_name = block_name_from_cli ? @cli_block_name : nil
|
198
|
+
# &bsp 'block_name:', block_name
|
199
|
+
|
200
|
+
# Determine the state of breaker based on was_using_cli and the block type
|
201
|
+
breaker = !block_name && !block_name_from_cli && was_using_cli && block_state.block[:shell] == BlockType::BASH
|
202
|
+
# &bsp 'breaker:', breaker
|
203
|
+
|
204
|
+
# Reset block_name_from_cli if the conditions are not met
|
205
|
+
block_name_from_cli ||= false
|
206
|
+
# &bsp 'block_name_from_cli:', block_name_from_cli
|
207
|
+
|
208
|
+
[block_name, block_name_from_cli, breaker]
|
209
|
+
end
|
210
|
+
|
211
|
+
def parse_yaml_data_from_body(body)
|
212
|
+
body.any? ? YAML.load(body.join("\n")) : {}
|
213
|
+
end
|
214
|
+
|
215
|
+
# Reads required code blocks from a temporary file specified by an environment variable.
|
216
|
+
# @return [Array<String>] Lines read from the temporary file, or an empty array if file is not found or path is empty.
|
217
|
+
def read_required_blocks_from_temp_file(temp_blocks_file_path)
|
218
|
+
return [] if temp_blocks_file_path.to_s.empty?
|
219
|
+
|
220
|
+
if File.exist?(temp_blocks_file_path)
|
221
|
+
File.readlines(
|
222
|
+
temp_blocks_file_path, chomp: true
|
223
|
+
)
|
224
|
+
else
|
225
|
+
[]
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def remove_file_without_standard_errors(path)
|
230
|
+
FileUtils.rm_f(path)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Evaluates the given string as Ruby code and rescues any StandardErrors.
|
234
|
+
# If an error occurs, it calls the error_handler method with 'safeval'.
|
235
|
+
# @param str [String] The string to be evaluated.
|
236
|
+
# @return [Object] The result of evaluating the string.
|
237
|
+
def safeval(str)
|
238
|
+
eval(str)
|
239
|
+
rescue StandardError # catches NameError, StandardError
|
240
|
+
error_handler('safeval')
|
241
|
+
end
|
242
|
+
|
243
|
+
def set_file_permissions(file_path, chmod_value)
|
244
|
+
File.chmod(chmod_value, file_path)
|
245
|
+
end
|
246
|
+
|
247
|
+
# Creates a TTY prompt with custom settings. Specifically, it disables the default 'cross' symbol and
|
248
|
+
# defines a lambda function to handle interrupts.
|
249
|
+
# @return [TTY::Prompt] A new TTY::Prompt instance with specified configurations.
|
250
|
+
def tty_prompt_without_disabled_symbol
|
251
|
+
TTY::Prompt.new(
|
252
|
+
interrupt: lambda {
|
253
|
+
puts
|
254
|
+
raise TTY::Reader::InputInterrupt
|
255
|
+
},
|
256
|
+
symbols: { cross: ' ' }
|
257
|
+
)
|
258
|
+
end
|
259
|
+
|
260
|
+
# Updates the attributes of the given fcb object and conditionally yields to a block.
|
261
|
+
# It initializes fcb names and sets the default block title from fcb's body.
|
262
|
+
# If the fcb has a body and meets certain conditions, it yields to the given block.
|
263
|
+
#
|
264
|
+
# @param fcb [Object] The fcb object whose attributes are to be updated.
|
265
|
+
# @param selected_messages [Array<Symbol>] A list of message types to determine if yielding is applicable.
|
266
|
+
# @param block [Block] An optional block to yield to if conditions are met.
|
267
|
+
def update_menu_attrib_yield_selected(fcb, selected_messages, configuration = {}, &block)
|
268
|
+
initialize_fcb_names(fcb)
|
269
|
+
return unless fcb.body
|
270
|
+
|
271
|
+
default_block_title_from_body(fcb)
|
272
|
+
MarkdownExec::Filter.yield_to_block_if_applicable(fcb, selected_messages, configuration,
|
273
|
+
&block)
|
274
|
+
end
|
275
|
+
|
276
|
+
# Writes the provided code blocks to a file.
|
277
|
+
# @param code_blocks [String] Code blocks to write into the file.
|
278
|
+
def write_code_to_file(content, path)
|
279
|
+
File.write(path, content)
|
280
|
+
end
|
281
|
+
|
282
|
+
def write_execution_output_to_file(files, filespec)
|
283
|
+
FileUtils.mkdir_p File.dirname(filespec)
|
284
|
+
|
285
|
+
File.write(
|
286
|
+
filespec,
|
287
|
+
["-STDOUT-\n",
|
288
|
+
format_execution_streams(ExecutionStreams::StdOut, files),
|
289
|
+
"-STDERR-\n",
|
290
|
+
format_execution_streams(ExecutionStreams::StdErr, files),
|
291
|
+
"-STDIN-\n",
|
292
|
+
format_execution_streams(ExecutionStreams::StdIn, files),
|
293
|
+
"\n"].join
|
294
|
+
)
|
295
|
+
end
|
296
|
+
|
297
|
+
# Yields a line as a new block if the selected message type includes :line.
|
298
|
+
# @param [String] line The line to be processed.
|
299
|
+
# @param [Array<Symbol>] selected_messages A list of message types to check.
|
300
|
+
# @param [Proc] block The block to be called with the line data.
|
301
|
+
def yield_line_if_selected(line, selected_messages, &block)
|
302
|
+
return unless block && selected_messages.include?(:line)
|
303
|
+
|
304
|
+
block.call(:line, MarkdownExec::FCB.new(body: [line]))
|
305
|
+
end
|
306
|
+
end
|
307
|
+
### require_relative 'hash_delegator_self'
|
308
|
+
|
40
309
|
# This module provides methods for compacting and converting data structures.
|
41
310
|
module CompactionHelpers
|
42
311
|
# Converts an array of key-value pairs into a hash, applying compaction to the values.
|
@@ -106,11 +375,12 @@ module MarkdownExec
|
|
106
375
|
class HashDelegator
|
107
376
|
attr_accessor :most_recent_loaded_filename, :pass_args, :run_state
|
108
377
|
|
378
|
+
extend HashDelegatorSelf
|
109
379
|
include CompactionHelpers
|
110
380
|
|
111
381
|
def initialize(delegate_object = {})
|
112
382
|
@delegate_object = delegate_object
|
113
|
-
@prompt = tty_prompt_without_disabled_symbol
|
383
|
+
@prompt = HashDelegator.tty_prompt_without_disabled_symbol
|
114
384
|
|
115
385
|
@most_recent_loaded_filename = nil
|
116
386
|
@pass_args = []
|
@@ -138,11 +408,20 @@ module MarkdownExec
|
|
138
408
|
# along with initial and final dividers, based on the delegate object's configuration.
|
139
409
|
#
|
140
410
|
# @param menu_blocks [Array] The array of menu block elements to be modified.
|
141
|
-
def add_menu_chrome_blocks!(menu_blocks)
|
411
|
+
def add_menu_chrome_blocks!(menu_blocks, link_state)
|
142
412
|
return unless @delegate_object[:menu_link_format].present?
|
143
413
|
|
414
|
+
if @delegate_object[:menu_with_inherited_lines]
|
415
|
+
add_inherited_lines(menu_blocks,
|
416
|
+
link_state)
|
417
|
+
end
|
418
|
+
|
419
|
+
# back before exit
|
144
420
|
add_back_option(menu_blocks) if should_add_back_option?
|
421
|
+
|
422
|
+
# exit after other options
|
145
423
|
add_exit_option(menu_blocks) if @delegate_object[:menu_with_exit]
|
424
|
+
|
146
425
|
add_dividers(menu_blocks)
|
147
426
|
end
|
148
427
|
|
@@ -152,13 +431,17 @@ module MarkdownExec
|
|
152
431
|
append_chrome_block(menu_blocks, MenuState::BACK)
|
153
432
|
end
|
154
433
|
|
434
|
+
def add_dividers(menu_blocks)
|
435
|
+
append_divider(menu_blocks, :initial)
|
436
|
+
append_divider(menu_blocks, :final)
|
437
|
+
end
|
438
|
+
|
155
439
|
def add_exit_option(menu_blocks)
|
156
440
|
append_chrome_block(menu_blocks, MenuState::EXIT)
|
157
441
|
end
|
158
442
|
|
159
|
-
def
|
160
|
-
|
161
|
-
append_divider(menu_blocks, :final)
|
443
|
+
def add_inherited_lines(menu_blocks, link_state)
|
444
|
+
append_inherited_lines(menu_blocks, link_state)
|
162
445
|
end
|
163
446
|
|
164
447
|
public
|
@@ -179,7 +462,7 @@ module MarkdownExec
|
|
179
462
|
end
|
180
463
|
|
181
464
|
formatted_name = format(@delegate_object[:menu_link_format],
|
182
|
-
safeval(option_name))
|
465
|
+
HashDelegator.safeval(option_name))
|
183
466
|
chrome_block = FCB.new(
|
184
467
|
chrome: true,
|
185
468
|
dname: HashDelegator.new(@delegate_object).string_send_color(
|
@@ -195,6 +478,39 @@ module MarkdownExec
|
|
195
478
|
end
|
196
479
|
end
|
197
480
|
|
481
|
+
# Appends a formatted divider to the specified position in a menu block array.
|
482
|
+
# The method checks for the presence of formatting options before appending.
|
483
|
+
#
|
484
|
+
# @param menu_blocks [Array] The array of menu block elements.
|
485
|
+
# @param position [Symbol] The position to insert the divider (:initial or :final).
|
486
|
+
def append_inherited_lines(menu_blocks, link_state, position: top)
|
487
|
+
return unless link_state.inherited_lines.present?
|
488
|
+
|
489
|
+
insert_at_top = @delegate_object[:menu_inherited_lines_at_top]
|
490
|
+
chrome_blocks = link_state.inherited_lines.map do |line|
|
491
|
+
formatted = format(@delegate_object[:menu_inherited_lines_format],
|
492
|
+
{ line: line })
|
493
|
+
FCB.new(
|
494
|
+
chrome: true,
|
495
|
+
disabled: '',
|
496
|
+
dname: HashDelegator.new(@delegate_object).string_send_color(
|
497
|
+
formatted, :menu_inherited_lines_color
|
498
|
+
),
|
499
|
+
oname: formatted
|
500
|
+
)
|
501
|
+
end
|
502
|
+
|
503
|
+
if insert_at_top
|
504
|
+
# Prepend an array of elements to the beginning
|
505
|
+
menu_blocks.unshift(*chrome_blocks)
|
506
|
+
else
|
507
|
+
# Append an array of elements to the end
|
508
|
+
menu_blocks.concat(chrome_blocks)
|
509
|
+
end
|
510
|
+
rescue StandardError
|
511
|
+
HashDelegator.error_handler('append_inherited_lines')
|
512
|
+
end
|
513
|
+
|
198
514
|
# Appends a formatted divider to the specified position in a menu block array.
|
199
515
|
# The method checks for the presence of formatting options before appending.
|
200
516
|
#
|
@@ -224,19 +540,6 @@ module MarkdownExec
|
|
224
540
|
|
225
541
|
# private
|
226
542
|
|
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
543
|
# Iterates through nested files to collect various types of blocks, including dividers, tasks, and others.
|
241
544
|
# The method categorizes blocks based on their type and processes them accordingly.
|
242
545
|
#
|
@@ -246,13 +549,30 @@ module MarkdownExec
|
|
246
549
|
iter_blocks_from_nested_files do |btype, fcb|
|
247
550
|
process_block_based_on_type(blocks, btype, fcb)
|
248
551
|
end
|
552
|
+
# &bc 'blocks.count:', blocks.count
|
249
553
|
blocks
|
250
554
|
rescue StandardError
|
251
|
-
error_handler('blocks_from_nested_files')
|
555
|
+
HashDelegator.error_handler('blocks_from_nested_files')
|
252
556
|
end
|
253
557
|
|
254
558
|
# private
|
255
559
|
|
560
|
+
def calc_logged_stdout_filename
|
561
|
+
return unless @delegate_object[:saved_stdout_folder]
|
562
|
+
|
563
|
+
@delegate_object[:logged_stdout_filename] =
|
564
|
+
SavedAsset.stdout_name(blockname: @delegate_object[:block_name],
|
565
|
+
filename: File.basename(@delegate_object[:filename],
|
566
|
+
'.*'),
|
567
|
+
prefix: @delegate_object[:logged_stdout_filename_prefix],
|
568
|
+
time: Time.now.utc)
|
569
|
+
|
570
|
+
@logged_stdout_filespec =
|
571
|
+
@delegate_object[:logged_stdout_filespec] =
|
572
|
+
File.join @delegate_object[:saved_stdout_folder],
|
573
|
+
@delegate_object[:logged_stdout_filename]
|
574
|
+
end
|
575
|
+
|
256
576
|
def cfile
|
257
577
|
@cfile ||= CachedNestedFileReader.new(
|
258
578
|
import_pattern: @delegate_object.fetch(:import_pattern) #, "^ *@import +(?<name>.+?) *$")
|
@@ -273,15 +593,6 @@ module MarkdownExec
|
|
273
593
|
true
|
274
594
|
end
|
275
595
|
|
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
596
|
# Collects required code lines based on the selected block and the delegate object's configuration.
|
286
597
|
# If the block type is VARS, it also sets environment variables based on the block's content.
|
287
598
|
#
|
@@ -292,7 +603,7 @@ module MarkdownExec
|
|
292
603
|
set_environment_variables_for_block(selected) if selected[:shell] == BlockType::VARS
|
293
604
|
|
294
605
|
required = mdoc.collect_recursively_required_code(
|
295
|
-
|
606
|
+
selected[:nickname] || selected[:oname],
|
296
607
|
label_format_above: @delegate_object[:shell_code_label_format_above],
|
297
608
|
label_format_below: @delegate_object[:shell_code_label_format_below],
|
298
609
|
block_source: block_source
|
@@ -306,13 +617,14 @@ module MarkdownExec
|
|
306
617
|
warn format_and_highlight_dependencies(dependencies,
|
307
618
|
highlight: required[:unmet_dependencies])
|
308
619
|
runtime_exception(:runtime_exception_error_level,
|
309
|
-
'unmet_dependencies, flag: runtime_exception_error_level',
|
620
|
+
'unmet_dependencies, flag: runtime_exception_error_level',
|
621
|
+
required[:unmet_dependencies])
|
310
622
|
elsif true
|
311
623
|
warn format_and_highlight_dependencies(dependencies,
|
312
624
|
highlight: [@delegate_object[:block_name]])
|
313
625
|
end
|
314
626
|
|
315
|
-
code_merge
|
627
|
+
HashDelegator.code_merge(link_state&.inherited_lines, required[:code])
|
316
628
|
end
|
317
629
|
|
318
630
|
def command_execute(command, args: [])
|
@@ -320,26 +632,54 @@ module MarkdownExec
|
|
320
632
|
@run_state.options = @delegate_object
|
321
633
|
@run_state.started_at = Time.now.utc
|
322
634
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
635
|
+
if @delegate_object[:execute_in_own_window] &&
|
636
|
+
@delegate_object[:execute_command_format].present? &&
|
637
|
+
@run_state.saved_filespec.present?
|
638
|
+
@run_state.in_own_window = true
|
639
|
+
system(
|
640
|
+
format(
|
641
|
+
@delegate_object[:execute_command_format],
|
642
|
+
{
|
643
|
+
batch_index: @run_state.batch_index,
|
644
|
+
batch_random: @run_state.batch_random,
|
645
|
+
block_name: @delegate_object[:block_name],
|
646
|
+
document_filename: File.basename(@delegate_object[:filename]),
|
647
|
+
document_filespec: @delegate_object[:filename],
|
648
|
+
home: Dir.pwd,
|
649
|
+
output_filename: File.basename(@delegate_object[:logged_stdout_filespec]),
|
650
|
+
output_filespec: @delegate_object[:logged_stdout_filespec],
|
651
|
+
script_filename: @run_state.saved_filespec,
|
652
|
+
script_filespec: File.join(Dir.pwd, @run_state.saved_filespec),
|
653
|
+
started_at: @run_state.started_at.strftime(
|
654
|
+
@delegate_object[:execute_command_title_time_format]
|
655
|
+
)
|
656
|
+
}
|
657
|
+
)
|
658
|
+
)
|
333
659
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
660
|
+
else
|
661
|
+
@run_state.in_own_window = false
|
662
|
+
Open3.popen3(@delegate_object[:shell],
|
663
|
+
'-c', command,
|
664
|
+
@delegate_object[:filename],
|
665
|
+
*args) do |stdin, stdout, stderr, exec_thr|
|
666
|
+
handle_stream(stdout, ExecutionStreams::StdOut) do |line|
|
667
|
+
yield nil, line, nil, exec_thr if block_given?
|
668
|
+
end
|
669
|
+
handle_stream(stderr, ExecutionStreams::StdErr) do |line|
|
670
|
+
yield nil, nil, line, exec_thr if block_given?
|
671
|
+
end
|
672
|
+
|
673
|
+
in_thr = handle_stream($stdin, ExecutionStreams::StdIn) do |line|
|
674
|
+
stdin.puts(line)
|
675
|
+
yield line, nil, nil, exec_thr if block_given?
|
676
|
+
end
|
338
677
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
678
|
+
wait_for_stream_processing
|
679
|
+
exec_thr.join
|
680
|
+
sleep 0.1
|
681
|
+
in_thr.kill if in_thr&.alive?
|
682
|
+
end
|
343
683
|
end
|
344
684
|
|
345
685
|
@run_state.completed_at = Time.now.utc
|
@@ -363,17 +703,17 @@ module MarkdownExec
|
|
363
703
|
if @delegate_object[:block_name].present?
|
364
704
|
block = all_blocks.find do |item|
|
365
705
|
item[:oname] == @delegate_object[:block_name]
|
366
|
-
end
|
706
|
+
end&.merge(block_name_from_ui: false)
|
367
707
|
else
|
368
708
|
block_state = wait_for_user_selected_block(all_blocks, menu_blocks,
|
369
709
|
default)
|
370
|
-
block = block_state.block
|
710
|
+
block = block_state.block&.merge(block_name_from_ui: true)
|
371
711
|
state = block_state.state
|
372
712
|
end
|
373
713
|
|
374
714
|
SelectedBlockMenuState.new(block, state)
|
375
715
|
rescue StandardError
|
376
|
-
error_handler('load_cli_or_user_selected_block')
|
716
|
+
HashDelegator.error_handler('load_cli_or_user_selected_block')
|
377
717
|
end
|
378
718
|
|
379
719
|
# This method is responsible for handling the execution of generic blocks in a markdown document.
|
@@ -388,9 +728,13 @@ module MarkdownExec
|
|
388
728
|
block_source: block_source)
|
389
729
|
output_or_approval = @delegate_object[:output_script] || @delegate_object[:user_must_approve]
|
390
730
|
display_required_code(required_lines) if output_or_approval
|
391
|
-
allow_execution = @delegate_object[:user_must_approve]
|
731
|
+
allow_execution = if @delegate_object[:user_must_approve]
|
732
|
+
prompt_for_user_approval(required_lines, selected)
|
733
|
+
else
|
734
|
+
true
|
735
|
+
end
|
392
736
|
|
393
|
-
execute_required_lines(required_lines) if allow_execution
|
737
|
+
execute_required_lines(required_lines, selected) if allow_execution
|
394
738
|
|
395
739
|
link_state.block_name = nil
|
396
740
|
LoadFileLinkState.new(LoadFile::Reuse, link_state)
|
@@ -411,18 +755,11 @@ module MarkdownExec
|
|
411
755
|
# @return [Integer] The count of fenced code blocks in the file.
|
412
756
|
def count_blocks_in_filename
|
413
757
|
regex = Regexp.new(@delegate_object[:fenced_start_and_end_regex])
|
414
|
-
lines = cfile.readlines(@delegate_object[:filename]
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
# private
|
419
|
-
|
420
|
-
def count_matches_in_lines(lines, regex)
|
421
|
-
lines.count { |line| line.to_s.match(regex) }
|
758
|
+
lines = cfile.readlines(@delegate_object[:filename],
|
759
|
+
import_paths: @delegate_object[:import_paths]&.split(':'))
|
760
|
+
HashDelegator.count_matches_in_lines(lines, regex) / 2
|
422
761
|
end
|
423
762
|
|
424
|
-
# private
|
425
|
-
|
426
763
|
##
|
427
764
|
# Creates and adds a formatted block to the blocks array based on the provided match and format options.
|
428
765
|
# @param blocks [Array] The array of blocks to add the new block to.
|
@@ -449,13 +786,14 @@ module MarkdownExec
|
|
449
786
|
# @param use_chrome [Boolean] Indicates if the chrome styling should be applied.
|
450
787
|
def create_and_add_chrome_blocks(blocks, fcb)
|
451
788
|
match_criteria = [
|
452
|
-
{
|
453
|
-
|
454
|
-
{
|
455
|
-
|
456
|
-
{
|
457
|
-
|
789
|
+
{ color: :menu_heading1_color, format: :menu_heading1_format, match: :heading1_match },
|
790
|
+
{ color: :menu_heading2_color, format: :menu_heading2_format, match: :heading2_match },
|
791
|
+
{ color: :menu_heading3_color, format: :menu_heading3_format, match: :heading3_match },
|
792
|
+
{ color: :menu_divider_color, format: :menu_divider_format, match: :menu_divider_match },
|
793
|
+
{ color: :menu_note_color, format: :menu_note_format, match: :menu_note_match },
|
794
|
+
{ color: :menu_task_color, format: :menu_task_format, match: :menu_task_match }
|
458
795
|
]
|
796
|
+
# rubocop:enable Style/UnlessElse
|
459
797
|
match_criteria.each do |criteria|
|
460
798
|
unless @delegate_object[criteria[:match]].present? &&
|
461
799
|
(mbody = fcb.body[0].match @delegate_object[criteria[:match]])
|
@@ -468,31 +806,10 @@ module MarkdownExec
|
|
468
806
|
end
|
469
807
|
end
|
470
808
|
|
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
809
|
def create_divider(position)
|
493
810
|
divider_key = position == :initial ? :menu_initial_divider : :menu_final_divider
|
494
811
|
oname = format(@delegate_object[:menu_divider_format],
|
495
|
-
safeval(@delegate_object[divider_key]))
|
812
|
+
HashDelegator.safeval(@delegate_object[divider_key]))
|
496
813
|
|
497
814
|
FCB.new(
|
498
815
|
chrome: true,
|
@@ -502,36 +819,27 @@ module MarkdownExec
|
|
502
819
|
)
|
503
820
|
end
|
504
821
|
|
505
|
-
#
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
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?
|
822
|
+
# Prompts user if named block is the same as the prior execution.
|
823
|
+
#
|
824
|
+
# @return [Boolean] Execute the named block.
|
825
|
+
def debounce_allows
|
826
|
+
return true unless @delegate_object[:debounce_execution]
|
514
827
|
|
515
|
-
|
516
|
-
|
828
|
+
# filter block if selected in menu
|
829
|
+
return true if @run_state.block_name_from_cli
|
517
830
|
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
prev_item&.fetch(:chrome, nil) && !prev_item&.fetch(:oname).present? &&
|
522
|
-
current_item&.fetch(:chrome, nil) && !current_item&.fetch(:oname).present?
|
831
|
+
# return false if @prior_execution_block == @delegate_object[:block_name]
|
832
|
+
if @prior_execution_block == @delegate_object[:block_name]
|
833
|
+
return @allowed_execution_block == @prior_execution_block || prompt_approve_repeat
|
523
834
|
end
|
524
|
-
end
|
525
835
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
return if temp_blocks_file_path.nil? || temp_blocks_file_path.empty?
|
836
|
+
@prior_execution_block = @delegate_object[:block_name]
|
837
|
+
@allowed_execution_block = nil
|
838
|
+
true
|
839
|
+
end
|
531
840
|
|
532
|
-
|
533
|
-
|
534
|
-
error_handler('delete_required_temp_file')
|
841
|
+
def debounce_reset
|
842
|
+
@prior_execution_block = nil
|
535
843
|
end
|
536
844
|
|
537
845
|
# Determines the state of a selected block in the menu based on the selected option.
|
@@ -570,14 +878,13 @@ module MarkdownExec
|
|
570
878
|
@delegate_object[:menu_divider_format].present? && @delegate_object[divider_key].present?
|
571
879
|
end
|
572
880
|
|
573
|
-
def
|
574
|
-
|
575
|
-
|
576
|
-
opts
|
577
|
-
)
|
578
|
-
end
|
881
|
+
def do_save_execution_output
|
882
|
+
return unless @delegate_object[:save_execution_output]
|
883
|
+
return if @run_state.in_own_window
|
579
884
|
|
580
|
-
|
885
|
+
HashDelegator.write_execution_output_to_file(@run_state.files,
|
886
|
+
@delegate_object[:logged_stdout_filespec])
|
887
|
+
end
|
581
888
|
|
582
889
|
# Executes a block of code that has been approved for execution.
|
583
890
|
# It sets the script block name, writes command files if required, and handles the execution
|
@@ -585,9 +892,9 @@ module MarkdownExec
|
|
585
892
|
#
|
586
893
|
# @param required_lines [Array<String>] The lines of code to be executed.
|
587
894
|
# @param selected [FCB] The selected functional code block object.
|
588
|
-
def execute_required_lines(required_lines = [])
|
589
|
-
|
590
|
-
|
895
|
+
def execute_required_lines(required_lines = [], selected = FCB.new)
|
896
|
+
write_command_file(required_lines, selected) if @delegate_object[:save_executed_script]
|
897
|
+
calc_logged_stdout_filename
|
591
898
|
format_and_execute_command(required_lines)
|
592
899
|
post_execution_process
|
593
900
|
end
|
@@ -604,21 +911,26 @@ module MarkdownExec
|
|
604
911
|
def execute_shell_type(selected, mdoc, link_state = LinkState.new,
|
605
912
|
block_source:)
|
606
913
|
if selected.fetch(:shell, '') == BlockType::LINK
|
914
|
+
debounce_reset
|
607
915
|
push_link_history_and_trigger_load(selected.fetch(:body, ''), mdoc, selected,
|
608
916
|
link_state)
|
609
917
|
|
610
918
|
elsif @menu_user_clicked_back_link
|
919
|
+
debounce_reset
|
611
920
|
pop_link_history_and_trigger_load
|
612
921
|
|
613
922
|
elsif selected[:shell] == BlockType::OPTS
|
923
|
+
debounce_reset
|
614
924
|
options_state = read_show_options_and_trigger_reuse(selected, link_state)
|
615
925
|
@menu_base_options.merge!(options_state.options)
|
616
926
|
@delegate_object.merge!(options_state.options)
|
617
927
|
options_state.load_file_link_state
|
618
928
|
|
619
|
-
|
929
|
+
elsif debounce_allows
|
620
930
|
compile_execute_and_trigger_reuse(mdoc, selected, link_state,
|
621
931
|
block_source: block_source)
|
932
|
+
else
|
933
|
+
LoadFileLinkState.new(LoadFile::Reuse, link_state)
|
622
934
|
end
|
623
935
|
end
|
624
936
|
|
@@ -636,17 +948,6 @@ module MarkdownExec
|
|
636
948
|
string_send_color(data_string, color_sym)
|
637
949
|
end
|
638
950
|
|
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
951
|
def format_and_execute_command(lines)
|
651
952
|
formatted_command = lines.flatten.join("\n")
|
652
953
|
@fout.fout fetch_color(data_sym: :script_execution_head,
|
@@ -672,16 +973,6 @@ module MarkdownExec
|
|
672
973
|
string_send_color(formatted_string, color_sym)
|
673
974
|
end
|
674
975
|
|
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
976
|
# Processes a block to generate its summary, modifying its attributes based on various matching criteria.
|
686
977
|
# It handles special formatting for bash blocks, extracting and setting properties like call, stdin, stdout, and dname.
|
687
978
|
#
|
@@ -701,17 +992,36 @@ module MarkdownExec
|
|
701
992
|
@delegate_object[:block_stdout_scan])
|
702
993
|
|
703
994
|
shell_color_option = SHELL_COLOR_OPTIONS[fcb[:shell]]
|
704
|
-
fcb.title = fcb.oname = bm && bm[1] ? bm[:title] : titlexcall
|
705
|
-
fcb.dname = apply_shell_color_option(fcb.oname, shell_color_option)
|
706
995
|
|
996
|
+
if @delegate_object[:block_name_nick_match].present? && fcb.oname =~ Regexp.new(@delegate_object[:block_name_nick_match])
|
997
|
+
fcb.nickname = $~[0]
|
998
|
+
fcb.title = fcb.oname = format_multiline_body_as_title(fcb.body)
|
999
|
+
else
|
1000
|
+
fcb.title = fcb.oname = bm && bm[1] ? bm[:title] : titlexcall
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
fcb.dname = HashDelegator.indent_all_lines(
|
1004
|
+
apply_shell_color_option(fcb.oname, shell_color_option),
|
1005
|
+
fcb.fetch(:indent, nil)
|
1006
|
+
)
|
707
1007
|
fcb
|
708
1008
|
end
|
709
1009
|
|
1010
|
+
# Formats multiline body content as a title string.
|
1011
|
+
# indents all but first line with two spaces so it displays correctly in menu
|
1012
|
+
# @param body_lines [Array<String>] The lines of body content.
|
1013
|
+
# @return [String] Formatted title.
|
1014
|
+
def format_multiline_body_as_title(body_lines)
|
1015
|
+
body_lines.map.with_index do |line, index|
|
1016
|
+
index.zero? ? line : " #{line}"
|
1017
|
+
end.join("\n") + "\n"
|
1018
|
+
end
|
1019
|
+
|
710
1020
|
# Updates the delegate object's state based on the provided block state.
|
711
1021
|
# It sets the block name and determines if the user clicked the back link in the menu.
|
712
1022
|
#
|
713
1023
|
# @param block_state [Object] An object representing the state of a block in the menu.
|
714
|
-
def
|
1024
|
+
def handle_back_or_continue(block_state)
|
715
1025
|
return if block_state.nil?
|
716
1026
|
unless [MenuState::BACK,
|
717
1027
|
MenuState::CONTINUE].include?(block_state.state)
|
@@ -744,16 +1054,6 @@ module MarkdownExec
|
|
744
1054
|
end
|
745
1055
|
end
|
746
1056
|
|
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
1057
|
# Initializes variables for regex and other states
|
758
1058
|
def initial_state
|
759
1059
|
{
|
@@ -769,28 +1069,6 @@ module MarkdownExec
|
|
769
1069
|
}
|
770
1070
|
end
|
771
1071
|
|
772
|
-
def initialize_and_save_execution_output
|
773
|
-
return unless @delegate_object[:save_execution_output]
|
774
|
-
|
775
|
-
@delegate_object[:logged_stdout_filename] =
|
776
|
-
SavedAsset.stdout_name(blockname: @delegate_object[:block_name],
|
777
|
-
filename: File.basename(@delegate_object[:filename],
|
778
|
-
'.*'),
|
779
|
-
prefix: @delegate_object[:logged_stdout_filename_prefix],
|
780
|
-
time: Time.now.utc)
|
781
|
-
|
782
|
-
@logged_stdout_filespec =
|
783
|
-
@delegate_object[:logged_stdout_filespec] =
|
784
|
-
File.join @delegate_object[:saved_stdout_folder],
|
785
|
-
@delegate_object[:logged_stdout_filename]
|
786
|
-
@logged_stdout_filespec = @delegate_object[:logged_stdout_filespec]
|
787
|
-
write_execution_output_to_file
|
788
|
-
end
|
789
|
-
|
790
|
-
def initialize_fcb_names(fcb)
|
791
|
-
fcb.oname = fcb.dname = fcb.title || ''
|
792
|
-
end
|
793
|
-
|
794
1072
|
# Iterates through blocks in a file, applying the provided block to each line.
|
795
1073
|
# The iteration only occurs if the file exists.
|
796
1074
|
# @yield [Symbol] :filter Yields to obtain selected messages for processing.
|
@@ -799,8 +1077,8 @@ module MarkdownExec
|
|
799
1077
|
|
800
1078
|
state = initial_state
|
801
1079
|
selected_messages = yield :filter
|
802
|
-
|
803
|
-
|
1080
|
+
cfile.readlines(@delegate_object[:filename],
|
1081
|
+
import_paths: @delegate_object[:import_paths]&.split(':')).each do |nested_line|
|
804
1082
|
if nested_line
|
805
1083
|
update_line_and_block_state(nested_line, state, selected_messages,
|
806
1084
|
&block)
|
@@ -808,6 +1086,65 @@ module MarkdownExec
|
|
808
1086
|
end
|
809
1087
|
end
|
810
1088
|
|
1089
|
+
def link_block_data_eval(link_state, code_lines, selected, link_block_data)
|
1090
|
+
all_code = HashDelegator.code_merge(link_state&.inherited_lines, code_lines)
|
1091
|
+
|
1092
|
+
if link_block_data.fetch(LinkDataKeys::Exec, false)
|
1093
|
+
@run_state.files = Hash.new([])
|
1094
|
+
output_lines = []
|
1095
|
+
|
1096
|
+
Open3.popen3(
|
1097
|
+
@delegate_object[:shell],
|
1098
|
+
'-c', all_code.join("\n")
|
1099
|
+
) do |stdin, stdout, stderr, _exec_thr|
|
1100
|
+
handle_stream(stdout, ExecutionStreams::StdOut) do |line|
|
1101
|
+
output_lines.push(line)
|
1102
|
+
end
|
1103
|
+
handle_stream(stderr, ExecutionStreams::StdErr) do |line|
|
1104
|
+
output_lines.push(line)
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
in_thr = handle_stream($stdin, ExecutionStreams::StdIn) do |line|
|
1108
|
+
stdin.puts(line)
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
wait_for_stream_processing
|
1112
|
+
sleep 0.1
|
1113
|
+
in_thr.kill if in_thr&.alive?
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
## select output_lines that look like assignment or match other specs
|
1117
|
+
#
|
1118
|
+
output_lines = process_string_array(
|
1119
|
+
output_lines,
|
1120
|
+
begin_pattern: @delegate_object.fetch(:output_assignment_begin, nil),
|
1121
|
+
end_pattern: @delegate_object.fetch(:output_assignment_end, nil),
|
1122
|
+
scan1: @delegate_object.fetch(:output_assignment_match, nil),
|
1123
|
+
format1: @delegate_object.fetch(:output_assignment_format, nil)
|
1124
|
+
)
|
1125
|
+
|
1126
|
+
else
|
1127
|
+
output_lines = `#{all_code.join("\n")}`.split("\n")
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
unless output_lines
|
1131
|
+
HashDelegator.error_handler('all_code eval output_lines is nil', { abort: true })
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
label_format_above = @delegate_object[:shell_code_label_format_above]
|
1135
|
+
label_format_below = @delegate_object[:shell_code_label_format_below]
|
1136
|
+
block_source = { document_filename: link_state&.document_filename }
|
1137
|
+
|
1138
|
+
[label_format_above && format(label_format_above,
|
1139
|
+
block_source.merge({ block_name: selected[:oname] }))] +
|
1140
|
+
output_lines.map do |line|
|
1141
|
+
re = Regexp.new(link_block_data.fetch('pattern', '(?<line>.*)'))
|
1142
|
+
re.gsub_format(line, link_block_data.fetch('format', '%{line}')) if re =~ line
|
1143
|
+
end.compact +
|
1144
|
+
[label_format_below && format(label_format_below,
|
1145
|
+
block_source.merge({ block_name: selected[:oname] }))]
|
1146
|
+
end
|
1147
|
+
|
811
1148
|
def link_history_push_and_next(
|
812
1149
|
curr_block_name:, curr_document_filename:,
|
813
1150
|
inherited_block_names:, inherited_dependencies:, inherited_lines:,
|
@@ -845,7 +1182,7 @@ module MarkdownExec
|
|
845
1182
|
return
|
846
1183
|
end
|
847
1184
|
|
848
|
-
block = block_find(all_blocks, :oname, block_name)
|
1185
|
+
block = HashDelegator.block_find(all_blocks, :oname, block_name)
|
849
1186
|
return unless block
|
850
1187
|
|
851
1188
|
options_state = read_show_options_and_trigger_reuse(block)
|
@@ -866,7 +1203,7 @@ module MarkdownExec
|
|
866
1203
|
|
867
1204
|
## Handles the file loading and returns the blocks in the file and MDoc instance
|
868
1205
|
#
|
869
|
-
def mdoc_menu_and_blocks_from_nested_files
|
1206
|
+
def mdoc_menu_and_blocks_from_nested_files(link_state)
|
870
1207
|
all_blocks, mdoc = mdoc_and_blocks_from_nested_files
|
871
1208
|
|
872
1209
|
# recreate menu with new options
|
@@ -874,8 +1211,9 @@ module MarkdownExec
|
|
874
1211
|
all_blocks, mdoc = mdoc_and_blocks_from_nested_files if load_auto_blocks(all_blocks)
|
875
1212
|
|
876
1213
|
menu_blocks = mdoc.fcbs_per_options(@delegate_object)
|
877
|
-
add_menu_chrome_blocks!(menu_blocks)
|
878
|
-
|
1214
|
+
add_menu_chrome_blocks!(menu_blocks, link_state)
|
1215
|
+
### compress empty lines
|
1216
|
+
HashDelegator.delete_consecutive_blank_lines!(menu_blocks) if true
|
879
1217
|
[all_blocks, menu_blocks, mdoc]
|
880
1218
|
end
|
881
1219
|
|
@@ -894,7 +1232,7 @@ module MarkdownExec
|
|
894
1232
|
# @param option_symbol [Symbol] The symbol key for the menu option in the delegate object.
|
895
1233
|
# @return [String] The formatted or original value of the menu option.
|
896
1234
|
def menu_chrome_formatted_option(option_symbol = :menu_option_back_name)
|
897
|
-
option_value = safeval(@delegate_object.fetch(option_symbol, ''))
|
1235
|
+
option_value = HashDelegator.safeval(@delegate_object.fetch(option_symbol, ''))
|
898
1236
|
|
899
1237
|
if @delegate_object[:menu_chrome_format]
|
900
1238
|
format(@delegate_object[:menu_chrome_format], option_value)
|
@@ -903,12 +1241,6 @@ module MarkdownExec
|
|
903
1241
|
end
|
904
1242
|
end
|
905
1243
|
|
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
1244
|
# If a method is missing, treat it as a key for the @delegate_object.
|
913
1245
|
def method_missing(method_name, *args, &block)
|
914
1246
|
if @delegate_object.respond_to?(method_name)
|
@@ -921,17 +1253,13 @@ module MarkdownExec
|
|
921
1253
|
end
|
922
1254
|
end
|
923
1255
|
|
924
|
-
def shift_cli_argument
|
925
|
-
return
|
1256
|
+
def shift_cli_argument
|
1257
|
+
return true unless @menu_base_options[:input_cli_rest].present?
|
926
1258
|
|
927
1259
|
@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
|
1260
|
+
false
|
931
1261
|
end
|
932
1262
|
|
933
|
-
# private
|
934
|
-
|
935
1263
|
def output_color_formatted(data_sym, color_sym)
|
936
1264
|
formatted_string = string_send_color(@delegate_object[data_sym],
|
937
1265
|
color_sym)
|
@@ -984,29 +1312,38 @@ module MarkdownExec
|
|
984
1312
|
), level: level
|
985
1313
|
end
|
986
1314
|
|
987
|
-
|
988
|
-
|
989
|
-
def parse_yaml_data_from_body(body)
|
990
|
-
body.any? ? YAML.load(body.join("\n")) : {}
|
991
|
-
end
|
992
|
-
|
993
|
-
def pop_add_current_code_to_head_and_trigger_load(_link_state, block_names, code_lines,
|
994
|
-
dependencies)
|
1315
|
+
def pop_add_current_code_to_head_and_trigger_load(link_state, block_names, code_lines,
|
1316
|
+
dependencies, selected)
|
995
1317
|
pop = @link_history.pop # updatable
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1318
|
+
if pop.document_filename
|
1319
|
+
next_state = LinkState.new(
|
1320
|
+
block_name: pop.block_name,
|
1321
|
+
document_filename: pop.document_filename,
|
1322
|
+
inherited_block_names:
|
1323
|
+
(pop.inherited_block_names + block_names).sort.uniq,
|
1324
|
+
inherited_dependencies:
|
1325
|
+
dependencies.merge(pop.inherited_dependencies || {}), ### merge, not replace, key data
|
1326
|
+
inherited_lines:
|
1327
|
+
HashDelegator.code_merge(pop.inherited_lines, code_lines)
|
1328
|
+
)
|
1329
|
+
@link_history.push(next_state)
|
1007
1330
|
|
1008
|
-
|
1009
|
-
|
1331
|
+
next_state.block_name = nil
|
1332
|
+
LoadFileLinkState.new(LoadFile::Load, next_state)
|
1333
|
+
else
|
1334
|
+
# no history exists; must have been called independently => retain script
|
1335
|
+
link_history_push_and_next(
|
1336
|
+
curr_block_name: selected[:oname],
|
1337
|
+
curr_document_filename: @delegate_object[:filename],
|
1338
|
+
inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
1339
|
+
inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
1340
|
+
inherited_lines: HashDelegator.code_merge(link_state&.inherited_lines, code_lines),
|
1341
|
+
next_block_name: '', # not link_block_data['block'] || ''
|
1342
|
+
next_document_filename: @delegate_object[:filename], # not next_document_filename
|
1343
|
+
next_load_file: LoadFile::Reuse # not next_document_filename == @delegate_object[:filename] ? LoadFile::Reuse : LoadFile::Load
|
1344
|
+
)
|
1345
|
+
# LoadFileLinkState.new(LoadFile::Reuse, link_state)
|
1346
|
+
end
|
1010
1347
|
end
|
1011
1348
|
|
1012
1349
|
# This method handles the back-link operation in the Markdown execution context.
|
@@ -1025,7 +1362,7 @@ module MarkdownExec
|
|
1025
1362
|
end
|
1026
1363
|
|
1027
1364
|
def post_execution_process
|
1028
|
-
|
1365
|
+
do_save_execution_output
|
1029
1366
|
output_execution_summary
|
1030
1367
|
output_execution_result
|
1031
1368
|
end
|
@@ -1041,7 +1378,7 @@ module MarkdownExec
|
|
1041
1378
|
%i[block_name_include_match block_name_wrapper_match])
|
1042
1379
|
|
1043
1380
|
fcb.merge!(
|
1044
|
-
name:
|
1381
|
+
name: fcb.dname,
|
1045
1382
|
label: BlockLabel.make(
|
1046
1383
|
body: fcb[:body],
|
1047
1384
|
filename: @delegate_object[:filename],
|
@@ -1071,11 +1408,63 @@ module MarkdownExec
|
|
1071
1408
|
when :filter
|
1072
1409
|
%i[blocks line]
|
1073
1410
|
when :line
|
1074
|
-
unless @delegate_object[:no_chrome]
|
1075
|
-
|
1076
|
-
|
1411
|
+
create_and_add_chrome_blocks(blocks, fcb) unless @delegate_object[:no_chrome]
|
1412
|
+
end
|
1413
|
+
end
|
1414
|
+
|
1415
|
+
def process_string_array(arr, begin_pattern: nil, end_pattern: nil, scan1: nil,
|
1416
|
+
format1: nil)
|
1417
|
+
in_block = !begin_pattern.present?
|
1418
|
+
collected_lines = []
|
1419
|
+
|
1420
|
+
arr.each do |line|
|
1421
|
+
if in_block
|
1422
|
+
if end_pattern.present? && line.match?(end_pattern)
|
1423
|
+
in_block = false
|
1424
|
+
elsif scan1.present?
|
1425
|
+
if format1.present?
|
1426
|
+
caps = extract_named_captures_from_option(line, scan1)
|
1427
|
+
if caps
|
1428
|
+
formatted = format(format1, caps)
|
1429
|
+
collected_lines << formatted
|
1430
|
+
end
|
1431
|
+
else
|
1432
|
+
caps = line.match(scan1)
|
1433
|
+
if caps
|
1434
|
+
formatted = caps[0]
|
1435
|
+
collected_lines << formatted
|
1436
|
+
end
|
1437
|
+
end
|
1438
|
+
else
|
1439
|
+
collected_lines << line
|
1440
|
+
end
|
1441
|
+
elsif begin_pattern.present? && line.match?(begin_pattern)
|
1442
|
+
in_block = true
|
1077
1443
|
end
|
1078
1444
|
end
|
1445
|
+
|
1446
|
+
collected_lines
|
1447
|
+
end
|
1448
|
+
|
1449
|
+
def prompt_approve_repeat
|
1450
|
+
sel = @prompt.select(
|
1451
|
+
string_send_color(@delegate_object[:prompt_debounce],
|
1452
|
+
:prompt_color_after_script_execution),
|
1453
|
+
default: @delegate_object[:prompt_no],
|
1454
|
+
filter: true,
|
1455
|
+
quiet: true
|
1456
|
+
) do |menu|
|
1457
|
+
menu.choice @delegate_object[:prompt_yes]
|
1458
|
+
menu.choice @delegate_object[:prompt_no]
|
1459
|
+
menu.choice @delegate_object[:prompt_uninterrupted]
|
1460
|
+
end
|
1461
|
+
return false if sel == @delegate_object[:prompt_no]
|
1462
|
+
return true if sel == @delegate_object[:prompt_yes]
|
1463
|
+
|
1464
|
+
@allowed_execution_block = @prior_execution_block
|
1465
|
+
true
|
1466
|
+
rescue TTY::Reader::InputInterrupt
|
1467
|
+
exit 1
|
1079
1468
|
end
|
1080
1469
|
|
1081
1470
|
##
|
@@ -1093,7 +1482,7 @@ module MarkdownExec
|
|
1093
1482
|
#
|
1094
1483
|
# @return [Boolean] Returns true if the user approves (selects 'Yes'), false otherwise.
|
1095
1484
|
##
|
1096
|
-
def prompt_for_user_approval(required_lines)
|
1485
|
+
def prompt_for_user_approval(required_lines, selected)
|
1097
1486
|
# Present a selection menu for user approval.
|
1098
1487
|
sel = @prompt.select(
|
1099
1488
|
string_send_color(@delegate_object[:prompt_approve_block],
|
@@ -1113,7 +1502,7 @@ module MarkdownExec
|
|
1113
1502
|
if sel == MenuOptions::SCRIPT_TO_CLIPBOARD
|
1114
1503
|
copy_to_clipboard(required_lines)
|
1115
1504
|
elsif sel == MenuOptions::SAVE_SCRIPT
|
1116
|
-
save_to_file(required_lines)
|
1505
|
+
save_to_file(required_lines, selected)
|
1117
1506
|
end
|
1118
1507
|
|
1119
1508
|
sel == MenuOptions::YES
|
@@ -1148,12 +1537,13 @@ module MarkdownExec
|
|
1148
1537
|
# @return [LoadFileLinkState] Object indicating the next action for file loading.
|
1149
1538
|
def push_link_history_and_trigger_load(link_block_body, mdoc, selected,
|
1150
1539
|
link_state = LinkState.new)
|
1151
|
-
link_block_data = parse_yaml_data_from_body(link_block_body)
|
1540
|
+
link_block_data = HashDelegator.parse_yaml_data_from_body(link_block_body)
|
1152
1541
|
|
1153
1542
|
# load key and values from link block into current environment
|
1154
1543
|
#
|
1155
1544
|
(link_block_data['vars'] || []).each do |(key, value)|
|
1156
1545
|
ENV[key] = value.to_s
|
1546
|
+
### add to inherited_lines
|
1157
1547
|
end
|
1158
1548
|
|
1159
1549
|
## collect blocks specified by block
|
@@ -1175,31 +1565,22 @@ module MarkdownExec
|
|
1175
1565
|
end
|
1176
1566
|
next_document_filename = link_block_data['file'] || @delegate_object[:filename]
|
1177
1567
|
|
1178
|
-
|
1568
|
+
## append blocks loaded per LinkDataKeys::Load
|
1179
1569
|
#
|
1180
|
-
if link_block_data.fetch('
|
1181
|
-
|
1182
|
-
output = `#{all_code.join("\n")}`.split("\n")
|
1183
|
-
label_format_above = @delegate_object[:shell_code_label_format_above]
|
1184
|
-
label_format_below = @delegate_object[:shell_code_label_format_below]
|
1185
|
-
block_source = { document_filename: link_state&.document_filename }
|
1186
|
-
|
1187
|
-
code_lines = [label_format_above && format(label_format_above,
|
1188
|
-
block_source.merge({ block_name: selected[:oname] }))] +
|
1189
|
-
output.map do |line|
|
1190
|
-
re = Regexp.new(link_block_data.fetch('pattern', '(?<line>.*)'))
|
1191
|
-
if re =~ line
|
1192
|
-
re.gsub_format(line, link_block_data.fetch('format', '%{line}'))
|
1193
|
-
end
|
1194
|
-
end.compact +
|
1195
|
-
[label_format_below && format(label_format_below,
|
1196
|
-
block_source.merge({ block_name: selected[:oname] }))]
|
1197
|
-
|
1570
|
+
if (load_filespec = link_block_data.fetch(LinkDataKeys::Load, '')).present?
|
1571
|
+
code_lines += File.readlines(load_filespec, chomp: true)
|
1198
1572
|
end
|
1199
1573
|
|
1200
|
-
if
|
1574
|
+
# if an eval link block, evaluate code_lines and return its standard output
|
1575
|
+
#
|
1576
|
+
if link_block_data.fetch(LinkDataKeys::Eval,
|
1577
|
+
false) || link_block_data.fetch(LinkDataKeys::Exec, false)
|
1578
|
+
code_lines = link_block_data_eval(link_state, code_lines, selected, link_block_data)
|
1579
|
+
end
|
1580
|
+
|
1581
|
+
if link_block_data[LinkDataKeys::Return]
|
1201
1582
|
pop_add_current_code_to_head_and_trigger_load(link_state, block_names, code_lines,
|
1202
|
-
dependencies)
|
1583
|
+
dependencies, selected)
|
1203
1584
|
|
1204
1585
|
else
|
1205
1586
|
link_history_push_and_next(
|
@@ -1207,7 +1588,7 @@ module MarkdownExec
|
|
1207
1588
|
curr_document_filename: @delegate_object[:filename],
|
1208
1589
|
inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
1209
1590
|
inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
1210
|
-
inherited_lines: code_merge(link_state&.inherited_lines, code_lines),
|
1591
|
+
inherited_lines: HashDelegator.code_merge(link_state&.inherited_lines, code_lines),
|
1211
1592
|
next_block_name: link_block_data['block'] || '',
|
1212
1593
|
next_document_filename: next_document_filename,
|
1213
1594
|
next_load_file: next_document_filename == @delegate_object[:filename] ? LoadFile::Reuse : LoadFile::Load
|
@@ -1215,20 +1596,6 @@ module MarkdownExec
|
|
1215
1596
|
end
|
1216
1597
|
end
|
1217
1598
|
|
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
1599
|
def runtime_exception(exception_sym, name, items)
|
1233
1600
|
if @delegate_object[exception_sym] != 0
|
1234
1601
|
data = { name: name, detail: items.join(', ') }
|
@@ -1248,22 +1615,8 @@ module MarkdownExec
|
|
1248
1615
|
exit @delegate_object[exception_sym]
|
1249
1616
|
end
|
1250
1617
|
|
1251
|
-
def
|
1252
|
-
|
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
|
-
def save_to_file(required_lines)
|
1266
|
-
write_command_file(required_lines)
|
1618
|
+
def save_to_file(required_lines, selected)
|
1619
|
+
write_command_file(required_lines, selected)
|
1267
1620
|
@fout.fout "File saved: #{@run_state.saved_filespec}"
|
1268
1621
|
end
|
1269
1622
|
|
@@ -1275,51 +1628,57 @@ module MarkdownExec
|
|
1275
1628
|
# @return [Nil] Returns nil if no code block is selected or an error occurs.
|
1276
1629
|
def document_menu_loop
|
1277
1630
|
@menu_base_options = @delegate_object
|
1278
|
-
link_state
|
1631
|
+
link_state = LinkState.new(
|
1632
|
+
block_name: @delegate_object[:block_name],
|
1633
|
+
document_filename: @delegate_object[:filename]
|
1634
|
+
)
|
1635
|
+
@run_state.block_name_from_cli = link_state.block_name.present?
|
1636
|
+
@cli_block_name = link_state.block_name
|
1637
|
+
now_using_cli = @run_state.block_name_from_cli
|
1279
1638
|
menu_default_dname = nil
|
1280
1639
|
|
1640
|
+
@run_state.batch_random = Random.new.rand
|
1641
|
+
@run_state.batch_index = 0
|
1642
|
+
|
1281
1643
|
loop do
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1644
|
+
@run_state.batch_index += 1
|
1645
|
+
@run_state.in_own_window = false
|
1646
|
+
|
1647
|
+
# &bsp 'loop', block_name_from_cli, @cli_block_name
|
1648
|
+
@run_state.block_name_from_cli, now_using_cli, blocks_in_file, menu_blocks, mdoc = \
|
1649
|
+
set_delobj_menu_loop_vars(@run_state.block_name_from_cli, now_using_cli, link_state)
|
1285
1650
|
|
1286
1651
|
# cli or user selection
|
1287
1652
|
#
|
1288
1653
|
block_state = load_cli_or_user_selected_block(blocks_in_file, menu_blocks,
|
1289
1654
|
menu_default_dname)
|
1290
|
-
|
1291
|
-
|
1655
|
+
# &bsp '@run_state.block_name_from_cli:',@run_state.block_name_from_cli
|
1656
|
+
if !block_state
|
1657
|
+
HashDelegator.error_handler('block_state missing', { abort: true })
|
1658
|
+
elsif block_state.state == MenuState::EXIT
|
1659
|
+
# &bsp 'load_cli_or_user_selected_block -> break'
|
1292
1660
|
break
|
1293
1661
|
end
|
1294
1662
|
|
1295
1663
|
dump_and_warn_block_state(block_state.block)
|
1296
1664
|
link_state, menu_default_dname = exec_bash_next_state(block_state.block, mdoc,
|
1297
1665
|
link_state)
|
1298
|
-
if prompt_user_exit(block_name_from_cli, block_state.block)
|
1299
|
-
#
|
1666
|
+
if prompt_user_exit(@run_state.block_name_from_cli, block_state.block)
|
1667
|
+
# &bsp 'prompt_user_exit -> break'
|
1300
1668
|
break
|
1301
1669
|
end
|
1302
1670
|
|
1303
|
-
link_state.block_name, block_name_from_cli, cli_break = \
|
1304
|
-
|
1671
|
+
link_state.block_name, @run_state.block_name_from_cli, cli_break = \
|
1672
|
+
HashDelegator.next_link_state(!shift_cli_argument, now_using_cli, block_state)
|
1305
1673
|
|
1306
|
-
if cli_break
|
1307
|
-
#
|
1674
|
+
if !block_state.block[:block_name_from_ui] && cli_break
|
1675
|
+
# &bsp '!block_name_from_ui + cli_break -> break'
|
1308
1676
|
break
|
1309
1677
|
end
|
1310
1678
|
end
|
1311
1679
|
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]
|
1680
|
+
HashDelegator.error_handler('document_menu_loop',
|
1681
|
+
{ abort: true })
|
1323
1682
|
end
|
1324
1683
|
|
1325
1684
|
def exec_bash_next_state(block_state_block, mdoc, link_state)
|
@@ -1342,7 +1701,7 @@ module MarkdownExec
|
|
1342
1701
|
|
1343
1702
|
# update @delegate_object and @menu_base_options in auto_load
|
1344
1703
|
#
|
1345
|
-
blocks_in_file, menu_blocks, mdoc = mdoc_menu_and_blocks_from_nested_files
|
1704
|
+
blocks_in_file, menu_blocks, mdoc = mdoc_menu_and_blocks_from_nested_files(link_state)
|
1346
1705
|
dump_delobj(blocks_in_file, menu_blocks, link_state)
|
1347
1706
|
|
1348
1707
|
[block_name_from_cli, now_using_cli, blocks_in_file, menu_blocks, mdoc]
|
@@ -1358,8 +1717,8 @@ module MarkdownExec
|
|
1358
1717
|
end
|
1359
1718
|
|
1360
1719
|
def manage_cli_selection_state(block_name_from_cli, now_using_cli, link_state)
|
1361
|
-
if block_name_from_cli && @cli_block_name ==
|
1362
|
-
#
|
1720
|
+
if block_name_from_cli && @cli_block_name == @menu_base_options[:menu_persist_block_name]
|
1721
|
+
# &bsp 'pause cli control, allow user to select block'
|
1363
1722
|
block_name_from_cli = false
|
1364
1723
|
now_using_cli = false
|
1365
1724
|
@menu_base_options[:block_name] = \
|
@@ -1373,45 +1732,6 @@ module MarkdownExec
|
|
1373
1732
|
[block_name_from_cli, now_using_cli]
|
1374
1733
|
end
|
1375
1734
|
|
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
1735
|
# Update the block name in the link state and delegate object.
|
1416
1736
|
#
|
1417
1737
|
# This method updates the block name based on whether it was specified
|
@@ -1425,21 +1745,6 @@ module MarkdownExec
|
|
1425
1745
|
block_name_from_cli ? @cli_block_name : link_state.block_name
|
1426
1746
|
end
|
1427
1747
|
|
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
1748
|
# Outputs warnings based on the delegate object's configuration
|
1444
1749
|
#
|
1445
1750
|
# @param delegate_object [Hash] The delegate object containing configuration flags.
|
@@ -1482,11 +1787,16 @@ module MarkdownExec
|
|
1482
1787
|
selection = @prompt.select(prompt_text,
|
1483
1788
|
names,
|
1484
1789
|
opts.merge(filter: true))
|
1790
|
+
|
1485
1791
|
item = if names.first.instance_of?(String)
|
1486
1792
|
{ dname: selection }
|
1487
1793
|
else
|
1488
1794
|
names.find { |item| item[:dname] == selection }
|
1489
1795
|
end
|
1796
|
+
unless item
|
1797
|
+
HashDelegator.error_handler('select_option_with_metadata', error: 'menu item not found')
|
1798
|
+
exit 1
|
1799
|
+
end
|
1490
1800
|
|
1491
1801
|
item.merge(
|
1492
1802
|
if selection == menu_chrome_colored_option(:menu_option_back_name)
|
@@ -1500,11 +1810,11 @@ module MarkdownExec
|
|
1500
1810
|
rescue TTY::Reader::InputInterrupt
|
1501
1811
|
exit 1
|
1502
1812
|
rescue StandardError
|
1503
|
-
error_handler('select_option_with_metadata')
|
1813
|
+
HashDelegator.error_handler('select_option_with_metadata')
|
1504
1814
|
end
|
1505
1815
|
|
1506
1816
|
def set_environment_variables_for_block(selected)
|
1507
|
-
YAML.load(selected[:body].join("\n"))
|
1817
|
+
YAML.load(selected[:body].join("\n"))&.each do |key, value|
|
1508
1818
|
ENV[key] = value.to_s
|
1509
1819
|
next unless @delegate_object[:menu_vars_set_format].present?
|
1510
1820
|
|
@@ -1514,22 +1824,8 @@ module MarkdownExec
|
|
1514
1824
|
end
|
1515
1825
|
end
|
1516
1826
|
|
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
1827
|
def should_add_back_option?
|
1531
1828
|
@delegate_object[:menu_with_back] && @link_history.prior_state_exist?
|
1532
|
-
# @delegate_object[:menu_with_back] && link_history_prior_state_exist?
|
1533
1829
|
end
|
1534
1830
|
|
1535
1831
|
# Initializes a new fenced code block (FCB) object based on the provided line and heading information.
|
@@ -1547,13 +1843,22 @@ module MarkdownExec
|
|
1547
1843
|
!name.match(Regexp.new(@delegate_object[:block_name_wrapper_match]))
|
1548
1844
|
end
|
1549
1845
|
|
1846
|
+
dname = oname = title = ''
|
1847
|
+
nickname = nil
|
1848
|
+
if @delegate_object[:block_name_nick_match].present? && oname =~ Regexp.new(@delegate_object[:block_name_nick_match])
|
1849
|
+
nickname = $~[0]
|
1850
|
+
else
|
1851
|
+
dname = oname = title = fcb_title_groups.fetch(:name, '')
|
1852
|
+
end
|
1853
|
+
|
1550
1854
|
MarkdownExec::FCB.new(
|
1551
1855
|
body: [],
|
1552
1856
|
call: rest.match(Regexp.new(@delegate_object[:block_calls_scan]))&.to_a&.first,
|
1553
|
-
dname:
|
1857
|
+
dname: dname,
|
1554
1858
|
headings: headings,
|
1555
1859
|
indent: fcb_title_groups.fetch(:indent, ''),
|
1556
|
-
|
1860
|
+
nickname: nickname,
|
1861
|
+
oname: oname,
|
1557
1862
|
reqs: reqs,
|
1558
1863
|
shell: fcb_title_groups.fetch(:shell, ''),
|
1559
1864
|
stdin: if (tn = rest.match(/<(?<type>\$)?(?<name>[A-Za-z_-]\S+)/))
|
@@ -1562,7 +1867,7 @@ module MarkdownExec
|
|
1562
1867
|
stdout: if (tn = rest.match(/>(?<type>\$)?(?<name>[A-Za-z_\-.\w]+)/))
|
1563
1868
|
tn.named_captures.sym_keys
|
1564
1869
|
end,
|
1565
|
-
title:
|
1870
|
+
title: title,
|
1566
1871
|
wraps: wraps
|
1567
1872
|
)
|
1568
1873
|
end
|
@@ -1573,44 +1878,8 @@ module MarkdownExec
|
|
1573
1878
|
# @param color_sym [Symbol] The symbol representing the color method.
|
1574
1879
|
# @param default [String] Default color method to use if color_sym is not found in @delegate_object.
|
1575
1880
|
# @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
|
1881
|
+
def string_send_color(string, color_sym)
|
1882
|
+
HashDelegator.apply_color_from_hash(string, @delegate_object, color_sym)
|
1614
1883
|
end
|
1615
1884
|
|
1616
1885
|
##
|
@@ -1635,16 +1904,12 @@ module MarkdownExec
|
|
1635
1904
|
def update_line_and_block_state(nested_line, state, selected_messages,
|
1636
1905
|
&block)
|
1637
1906
|
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
1907
|
if line.match(@delegate_object[:fenced_start_and_end_regex])
|
1643
1908
|
if state[:in_fenced_block]
|
1644
1909
|
## end of code block
|
1645
1910
|
#
|
1646
|
-
update_menu_attrib_yield_selected(state[:fcb], selected_messages,
|
1647
|
-
|
1911
|
+
HashDelegator.update_menu_attrib_yield_selected(state[:fcb], selected_messages, @delegate_object,
|
1912
|
+
&block)
|
1648
1913
|
state[:in_fenced_block] = false
|
1649
1914
|
else
|
1650
1915
|
## start of code block
|
@@ -1665,29 +1930,14 @@ module MarkdownExec
|
|
1665
1930
|
elsif nested_line[:depth].zero? || @delegate_object[:menu_include_imported_notes]
|
1666
1931
|
# add line if it is depth 0 or option allows it
|
1667
1932
|
#
|
1668
|
-
yield_line_if_selected(line, selected_messages, &block)
|
1933
|
+
HashDelegator.yield_line_if_selected(line, selected_messages, &block)
|
1669
1934
|
|
1670
1935
|
else
|
1671
|
-
#
|
1936
|
+
# &bsp 'line is not recognized for block state'
|
1672
1937
|
|
1673
1938
|
end
|
1674
1939
|
end
|
1675
1940
|
|
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
1941
|
# Processes YAML data from the selected menu item, updating delegate objects and optionally printing formatted output.
|
1692
1942
|
# @param selected [Hash] Selected item from the menu containing a YAML body.
|
1693
1943
|
# @param tgt2 [Hash, nil] An optional target hash to update with YAML data.
|
@@ -1717,10 +1967,10 @@ module MarkdownExec
|
|
1717
1967
|
|
1718
1968
|
def wait_for_user_selected_block(all_blocks, menu_blocks, default)
|
1719
1969
|
block_state = wait_for_user_selection(all_blocks, menu_blocks, default)
|
1720
|
-
|
1970
|
+
handle_back_or_continue(block_state)
|
1721
1971
|
block_state
|
1722
1972
|
rescue StandardError
|
1723
|
-
error_handler('wait_for_user_selected_block')
|
1973
|
+
HashDelegator.error_handler('wait_for_user_selected_block')
|
1724
1974
|
end
|
1725
1975
|
|
1726
1976
|
def wait_for_user_selection(_all_blocks, menu_blocks, default)
|
@@ -1746,15 +1996,17 @@ module MarkdownExec
|
|
1746
1996
|
end
|
1747
1997
|
|
1748
1998
|
# Handles the core logic for generating the command file's metadata and content.
|
1749
|
-
def write_command_file(required_lines)
|
1999
|
+
def write_command_file(required_lines, selected)
|
1750
2000
|
return unless @delegate_object[:save_executed_script]
|
1751
2001
|
|
1752
2002
|
time_now = Time.now.utc
|
1753
2003
|
@run_state.saved_script_filename =
|
1754
|
-
SavedAsset.script_name(
|
1755
|
-
|
1756
|
-
|
1757
|
-
|
2004
|
+
SavedAsset.script_name(
|
2005
|
+
blockname: selected[:nickname] || selected[:oname],
|
2006
|
+
filename: @delegate_object[:filename],
|
2007
|
+
prefix: @delegate_object[:saved_script_filename_prefix],
|
2008
|
+
time: time_now
|
2009
|
+
)
|
1758
2010
|
@run_state.saved_filespec =
|
1759
2011
|
File.join(@delegate_object[:saved_script_folder],
|
1760
2012
|
@run_state.saved_script_filename)
|
@@ -1771,32 +2023,13 @@ module MarkdownExec
|
|
1771
2023
|
"# time: #{time_now}\n" \
|
1772
2024
|
"#{required_lines.flatten.join("\n")}\n"
|
1773
2025
|
|
1774
|
-
create_file_and_write_string_with_permissions(
|
2026
|
+
HashDelegator.create_file_and_write_string_with_permissions(
|
1775
2027
|
@run_state.saved_filespec,
|
1776
2028
|
content,
|
1777
2029
|
@delegate_object[:saved_script_chmod]
|
1778
2030
|
)
|
1779
2031
|
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
|
-
)
|
2032
|
+
HashDelegator.error_handler('write_command_file')
|
1800
2033
|
end
|
1801
2034
|
|
1802
2035
|
# Writes required code blocks to a temporary file and sets an environment variable with its path.
|
@@ -1814,912 +2047,858 @@ module MarkdownExec
|
|
1814
2047
|
[]
|
1815
2048
|
end
|
1816
2049
|
|
1817
|
-
code_blocks = (read_required_blocks_from_temp_file(import_filename) +
|
2050
|
+
code_blocks = (HashDelegator.read_required_blocks_from_temp_file(import_filename) +
|
1818
2051
|
c1).join("\n")
|
1819
2052
|
|
1820
|
-
write_code_to_file(code_blocks, temp_file_path)
|
2053
|
+
HashDelegator.write_code_to_file(code_blocks, temp_file_path)
|
1821
2054
|
end
|
2055
|
+
end
|
2056
|
+
end
|
2057
|
+
|
2058
|
+
return if $PROGRAM_NAME != __FILE__
|
2059
|
+
|
2060
|
+
require 'bundler/setup'
|
2061
|
+
Bundler.require(:default)
|
2062
|
+
|
2063
|
+
require 'minitest/autorun'
|
2064
|
+
require 'mocha/minitest'
|
1822
2065
|
|
1823
|
-
|
1824
|
-
|
1825
|
-
def
|
1826
|
-
|
2066
|
+
module MarkdownExec
|
2067
|
+
class TestHashDelegator < Minitest::Test
|
2068
|
+
def setup
|
2069
|
+
@hd = HashDelegator.new
|
2070
|
+
@mdoc = mock('MarkdownDocument')
|
2071
|
+
end
|
2072
|
+
|
2073
|
+
def test_calling_execute_required_lines_calls_command_execute_with_argument_args_value
|
2074
|
+
pigeon = 'E'
|
2075
|
+
obj = {
|
2076
|
+
output_execution_label_format: '',
|
2077
|
+
output_execution_label_name_color: 'plain',
|
2078
|
+
output_execution_label_value_color: 'plain'
|
2079
|
+
}
|
2080
|
+
|
2081
|
+
c = MarkdownExec::HashDelegator.new(obj)
|
2082
|
+
c.pass_args = pigeon
|
2083
|
+
|
2084
|
+
# Expect that method opts_command_execute is called with argument args having value pigeon
|
2085
|
+
c.expects(:command_execute).with(
|
2086
|
+
'',
|
2087
|
+
args: pigeon
|
2088
|
+
)
|
2089
|
+
|
2090
|
+
# Call method opts_execute_required_lines
|
2091
|
+
c.execute_required_lines
|
1827
2092
|
end
|
1828
2093
|
|
1829
|
-
#
|
1830
|
-
|
1831
|
-
|
1832
|
-
|
1833
|
-
|
1834
|
-
return unless block && selected_messages.include?(:line)
|
2094
|
+
# Test case for empty body
|
2095
|
+
def test_push_link_history_and_trigger_load_with_empty_body
|
2096
|
+
assert_equal LoadFile::Reuse,
|
2097
|
+
@hd.push_link_history_and_trigger_load([], nil, FCB.new).load_file
|
2098
|
+
end
|
1835
2099
|
|
1836
|
-
|
2100
|
+
# Test case for non-empty body without 'file' key
|
2101
|
+
def test_push_link_history_and_trigger_load_without_file_key
|
2102
|
+
body = ["vars:\n KEY: VALUE"]
|
2103
|
+
assert_equal LoadFile::Reuse,
|
2104
|
+
@hd.push_link_history_and_trigger_load(body, nil, FCB.new).load_file
|
1837
2105
|
end
|
1838
2106
|
|
1839
|
-
#
|
1840
|
-
|
1841
|
-
|
1842
|
-
|
1843
|
-
|
1844
|
-
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1848
|
-
|
1849
|
-
|
1850
|
-
end
|
2107
|
+
# Test case for non-empty body with 'file' key
|
2108
|
+
def test_push_link_history_and_trigger_load_with_file_key
|
2109
|
+
body = ["file: sample_file\nblock: sample_block\nvars:\n KEY: VALUE"]
|
2110
|
+
expected_result = LoadFileLinkState.new(LoadFile::Load,
|
2111
|
+
LinkState.new(block_name: 'sample_block',
|
2112
|
+
document_filename: 'sample_file',
|
2113
|
+
inherited_dependencies: {},
|
2114
|
+
inherited_lines: []))
|
2115
|
+
assert_equal expected_result,
|
2116
|
+
@hd.push_link_history_and_trigger_load(body, nil, FCB.new(block_name: 'sample_block',
|
2117
|
+
filename: 'sample_file'))
|
1851
2118
|
end
|
1852
|
-
end
|
1853
|
-
end
|
1854
2119
|
|
1855
|
-
|
1856
|
-
|
1857
|
-
|
2120
|
+
def test_indent_all_lines_with_indent
|
2121
|
+
body = "Line 1\nLine 2"
|
2122
|
+
indent = ' ' # Two spaces
|
2123
|
+
expected_result = " Line 1\n Line 2"
|
2124
|
+
assert_equal expected_result, HashDelegator.indent_all_lines(body, indent)
|
2125
|
+
end
|
1858
2126
|
|
1859
|
-
|
1860
|
-
|
2127
|
+
def test_indent_all_lines_without_indent
|
2128
|
+
body = "Line 1\nLine 2"
|
2129
|
+
indent = nil
|
1861
2130
|
|
1862
|
-
|
1863
|
-
|
1864
|
-
def setup
|
1865
|
-
@hd = HashDelegator.new
|
1866
|
-
@mdoc = mock('MarkdownDocument')
|
1867
|
-
end
|
2131
|
+
assert_equal body, HashDelegator.indent_all_lines(body, indent)
|
2132
|
+
end
|
1868
2133
|
|
1869
|
-
|
1870
|
-
|
1871
|
-
|
1872
|
-
output_execution_label_format: '',
|
1873
|
-
output_execution_label_name_color: 'plain',
|
1874
|
-
output_execution_label_value_color: 'plain'
|
1875
|
-
}
|
2134
|
+
def test_indent_all_lines_with_empty_indent
|
2135
|
+
body = "Line 1\nLine 2"
|
2136
|
+
indent = ''
|
1876
2137
|
|
1877
|
-
|
1878
|
-
|
2138
|
+
assert_equal body, HashDelegator.indent_all_lines(body, indent)
|
2139
|
+
end
|
1879
2140
|
|
1880
|
-
|
1881
|
-
|
1882
|
-
|
1883
|
-
args: pigeon
|
1884
|
-
)
|
2141
|
+
def test_safeval_successful_evaluation
|
2142
|
+
assert_equal 4, HashDelegator.safeval('2 + 2')
|
2143
|
+
end
|
1885
2144
|
|
1886
|
-
|
1887
|
-
|
1888
|
-
|
2145
|
+
def test_safeval_rescue_from_error
|
2146
|
+
HashDelegator.stubs(:error_handler).with('safeval')
|
2147
|
+
assert_nil HashDelegator.safeval('invalid code')
|
2148
|
+
end
|
1889
2149
|
|
1890
|
-
|
1891
|
-
|
1892
|
-
|
1893
|
-
|
1894
|
-
|
2150
|
+
def test_set_fcb_title
|
2151
|
+
# sample input and output data for testing default_block_title_from_body method
|
2152
|
+
input_output_data = [
|
2153
|
+
{
|
2154
|
+
input: MarkdownExec::FCB.new(title: nil,
|
2155
|
+
body: ["puts 'Hello, world!'"]),
|
2156
|
+
output: "puts 'Hello, world!'"
|
2157
|
+
},
|
2158
|
+
{
|
2159
|
+
input: MarkdownExec::FCB.new(title: '',
|
2160
|
+
body: ['def add(x, y)',
|
2161
|
+
' x + y', 'end']),
|
2162
|
+
output: "def add(x, y)\n x + y\n end\n"
|
2163
|
+
},
|
2164
|
+
{
|
2165
|
+
input: MarkdownExec::FCB.new(title: 'foo', body: %w[bar baz]),
|
2166
|
+
output: 'foo' # expect the title to remain unchanged
|
2167
|
+
}
|
2168
|
+
]
|
1895
2169
|
|
1896
|
-
#
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1900
|
-
|
2170
|
+
# iterate over the input and output data and
|
2171
|
+
# assert that the method sets the title as expected
|
2172
|
+
input_output_data.each do |data|
|
2173
|
+
input = data[:input]
|
2174
|
+
output = data[:output]
|
2175
|
+
HashDelegator.default_block_title_from_body(input)
|
2176
|
+
assert_equal output, input.title
|
1901
2177
|
end
|
2178
|
+
end
|
1902
2179
|
|
1903
|
-
|
1904
|
-
def
|
1905
|
-
|
1906
|
-
|
1907
|
-
|
1908
|
-
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
1912
|
-
|
1913
|
-
|
2180
|
+
class TestHashDelegatorAppendDivider < Minitest::Test
|
2181
|
+
def setup
|
2182
|
+
@hd = HashDelegator.new
|
2183
|
+
@hd.instance_variable_set(:@delegate_object, {
|
2184
|
+
menu_divider_format: 'Format',
|
2185
|
+
menu_initial_divider: 'Initial Divider',
|
2186
|
+
menu_final_divider: 'Final Divider',
|
2187
|
+
menu_divider_color: :color
|
2188
|
+
})
|
2189
|
+
@hd.stubs(:string_send_color).returns('Formatted Divider')
|
2190
|
+
HashDelegator.stubs(:safeval).returns('Safe Value')
|
1914
2191
|
end
|
1915
2192
|
|
1916
|
-
def
|
1917
|
-
|
1918
|
-
|
1919
|
-
|
1920
|
-
assert_equal
|
2193
|
+
def test_append_divider_initial
|
2194
|
+
menu_blocks = []
|
2195
|
+
@hd.append_divider(menu_blocks, :initial)
|
2196
|
+
|
2197
|
+
assert_equal 1, menu_blocks.size
|
2198
|
+
assert_equal 'Formatted Divider', menu_blocks.first.dname
|
1921
2199
|
end
|
1922
2200
|
|
1923
|
-
def
|
1924
|
-
|
1925
|
-
|
2201
|
+
def test_append_divider_final
|
2202
|
+
menu_blocks = []
|
2203
|
+
@hd.append_divider(menu_blocks, :final)
|
1926
2204
|
|
1927
|
-
assert_equal
|
2205
|
+
assert_equal 1, menu_blocks.size
|
2206
|
+
assert_equal 'Formatted Divider', menu_blocks.last.dname
|
1928
2207
|
end
|
1929
2208
|
|
1930
|
-
def
|
1931
|
-
|
1932
|
-
|
2209
|
+
def test_append_divider_without_format
|
2210
|
+
@hd.instance_variable_set(:@delegate_object, {})
|
2211
|
+
menu_blocks = []
|
2212
|
+
@hd.append_divider(menu_blocks, :initial)
|
1933
2213
|
|
1934
|
-
|
2214
|
+
assert_empty menu_blocks
|
1935
2215
|
end
|
2216
|
+
end
|
1936
2217
|
|
1937
|
-
|
1938
|
-
|
2218
|
+
class TestHashDelegatorBlockFind < Minitest::Test
|
2219
|
+
def setup
|
2220
|
+
@hd = HashDelegator.new
|
1939
2221
|
end
|
1940
2222
|
|
1941
|
-
def
|
1942
|
-
|
1943
|
-
|
2223
|
+
def test_block_find_with_match
|
2224
|
+
blocks = [{ key: 'value1' }, { key: 'value2' }]
|
2225
|
+
result = HashDelegator.block_find(blocks, :key, 'value1')
|
2226
|
+
assert_equal({ key: 'value1' }, result)
|
1944
2227
|
end
|
1945
2228
|
|
1946
|
-
def
|
1947
|
-
|
1948
|
-
|
1949
|
-
|
1950
|
-
input: MarkdownExec::FCB.new(title: nil,
|
1951
|
-
body: ["puts 'Hello, world!'"]),
|
1952
|
-
output: "puts 'Hello, world!'"
|
1953
|
-
},
|
1954
|
-
{
|
1955
|
-
input: MarkdownExec::FCB.new(title: '',
|
1956
|
-
body: ['def add(x, y)',
|
1957
|
-
' x + y', 'end']),
|
1958
|
-
output: "def add(x, y)\n x + y\n end\n"
|
1959
|
-
},
|
1960
|
-
{
|
1961
|
-
input: MarkdownExec::FCB.new(title: 'foo', body: %w[bar baz]),
|
1962
|
-
output: 'foo' # expect the title to remain unchanged
|
1963
|
-
}
|
1964
|
-
]
|
1965
|
-
|
1966
|
-
# iterate over the input and output data and
|
1967
|
-
# assert that the method sets the title as expected
|
1968
|
-
input_output_data.each do |data|
|
1969
|
-
input = data[:input]
|
1970
|
-
output = data[:output]
|
1971
|
-
@hd.default_block_title_from_body(input)
|
1972
|
-
assert_equal output, input.title
|
1973
|
-
end
|
2229
|
+
def test_block_find_without_match
|
2230
|
+
blocks = [{ key: 'value1' }, { key: 'value2' }]
|
2231
|
+
result = HashDelegator.block_find(blocks, :key, 'value3')
|
2232
|
+
assert_nil result
|
1974
2233
|
end
|
1975
2234
|
|
1976
|
-
|
1977
|
-
|
1978
|
-
|
1979
|
-
|
1980
|
-
menu_divider_format: 'Format',
|
1981
|
-
menu_initial_divider: 'Initial Divider',
|
1982
|
-
menu_final_divider: 'Final Divider',
|
1983
|
-
menu_divider_color: :color
|
1984
|
-
})
|
1985
|
-
@hd.stubs(:string_send_color).returns('Formatted Divider')
|
1986
|
-
@hd.stubs(:safeval).returns('Safe Value')
|
1987
|
-
end
|
1988
|
-
|
1989
|
-
def test_append_divider_initial
|
1990
|
-
menu_blocks = []
|
1991
|
-
@hd.append_divider(menu_blocks, :initial)
|
1992
|
-
|
1993
|
-
assert_equal 1, menu_blocks.size
|
1994
|
-
assert_equal 'Formatted Divider', menu_blocks.first.dname
|
1995
|
-
end
|
1996
|
-
|
1997
|
-
def test_append_divider_final
|
1998
|
-
menu_blocks = []
|
1999
|
-
@hd.append_divider(menu_blocks, :final)
|
2000
|
-
|
2001
|
-
assert_equal 1, menu_blocks.size
|
2002
|
-
assert_equal 'Formatted Divider', menu_blocks.last.dname
|
2003
|
-
end
|
2004
|
-
|
2005
|
-
def test_append_divider_without_format
|
2006
|
-
@hd.instance_variable_set(:@delegate_object, {})
|
2007
|
-
menu_blocks = []
|
2008
|
-
@hd.append_divider(menu_blocks, :initial)
|
2009
|
-
|
2010
|
-
assert_empty menu_blocks
|
2011
|
-
end
|
2235
|
+
def test_block_find_with_default
|
2236
|
+
blocks = [{ key: 'value1' }, { key: 'value2' }]
|
2237
|
+
result = HashDelegator.block_find(blocks, :key, 'value3', 'default')
|
2238
|
+
assert_equal 'default', result
|
2012
2239
|
end
|
2240
|
+
end
|
2013
2241
|
|
2014
|
-
|
2015
|
-
|
2016
|
-
|
2017
|
-
|
2018
|
-
|
2019
|
-
|
2020
|
-
|
2021
|
-
|
2022
|
-
assert_equal({ key: 'value1' }, result)
|
2023
|
-
end
|
2024
|
-
|
2025
|
-
def test_block_find_without_match
|
2026
|
-
blocks = [{ key: 'value1' }, { key: 'value2' }]
|
2027
|
-
result = @hd.block_find(blocks, :key, 'value3')
|
2028
|
-
assert_nil result
|
2029
|
-
end
|
2030
|
-
|
2031
|
-
def test_block_find_with_default
|
2032
|
-
blocks = [{ key: 'value1' }, { key: 'value2' }]
|
2033
|
-
result = @hd.block_find(blocks, :key, 'value3', 'default')
|
2034
|
-
assert_equal 'default', result
|
2035
|
-
end
|
2242
|
+
class TestHashDelegatorBlocksFromNestedFiles < Minitest::Test
|
2243
|
+
def setup
|
2244
|
+
@hd = HashDelegator.new
|
2245
|
+
@hd.stubs(:iter_blocks_from_nested_files).yields(:blocks, FCB.new)
|
2246
|
+
@hd.stubs(:get_block_summary).returns(FCB.new)
|
2247
|
+
@hd.stubs(:create_and_add_chrome_blocks)
|
2248
|
+
@hd.instance_variable_set(:@delegate_object, {})
|
2249
|
+
HashDelegator.stubs(:error_handler)
|
2036
2250
|
end
|
2037
2251
|
|
2038
|
-
|
2039
|
-
|
2040
|
-
@hd = HashDelegator.new
|
2041
|
-
@hd.stubs(:iter_blocks_from_nested_files).yields(:blocks, FCB.new)
|
2042
|
-
@hd.stubs(:get_block_summary).returns(FCB.new)
|
2043
|
-
@hd.stubs(:create_and_add_chrome_blocks)
|
2044
|
-
@hd.instance_variable_set(:@delegate_object, {})
|
2045
|
-
@hd.stubs(:error_handler)
|
2046
|
-
end
|
2047
|
-
|
2048
|
-
def test_blocks_from_nested_files
|
2049
|
-
result = @hd.blocks_from_nested_files
|
2050
|
-
|
2051
|
-
assert_kind_of Array, result
|
2052
|
-
assert_kind_of FCB, result.first
|
2053
|
-
end
|
2054
|
-
|
2055
|
-
def test_blocks_from_nested_files_with_no_chrome
|
2056
|
-
@hd.instance_variable_set(:@delegate_object, { no_chrome: true })
|
2057
|
-
@hd.expects(:create_and_add_chrome_blocks).never
|
2252
|
+
def test_blocks_from_nested_files
|
2253
|
+
result = @hd.blocks_from_nested_files
|
2058
2254
|
|
2059
|
-
|
2060
|
-
|
2061
|
-
assert_kind_of Array, result
|
2062
|
-
end
|
2255
|
+
assert_kind_of Array, result
|
2256
|
+
assert_kind_of FCB, result.first
|
2063
2257
|
end
|
2064
2258
|
|
2065
|
-
|
2066
|
-
|
2067
|
-
|
2068
|
-
@hd.instance_variable_set(:@delegate_object, {})
|
2069
|
-
@mdoc = mock('YourMDocClass')
|
2070
|
-
@selected = { shell: BlockType::VARS, body: ['key: value'] }
|
2071
|
-
@hd.stubs(:read_required_blocks_from_temp_file).returns([])
|
2072
|
-
@hd.stubs(:string_send_color)
|
2073
|
-
@hd.stubs(:print)
|
2074
|
-
end
|
2259
|
+
def test_blocks_from_nested_files_with_no_chrome
|
2260
|
+
@hd.instance_variable_set(:@delegate_object, { no_chrome: true })
|
2261
|
+
@hd.expects(:create_and_add_chrome_blocks).never
|
2075
2262
|
|
2076
|
-
|
2077
|
-
YAML.stubs(:load).returns({ 'key' => 'value' })
|
2078
|
-
@mdoc.stubs(:collect_recursively_required_code).returns({ code: ['code line'] })
|
2079
|
-
result = @hd.collect_required_code_lines(@mdoc, @selected, block_source: {})
|
2263
|
+
result = @hd.blocks_from_nested_files
|
2080
2264
|
|
2081
|
-
|
2082
|
-
end
|
2265
|
+
assert_kind_of Array, result
|
2083
2266
|
end
|
2267
|
+
end
|
2084
2268
|
|
2085
|
-
|
2086
|
-
|
2087
|
-
|
2088
|
-
|
2089
|
-
|
2090
|
-
|
2091
|
-
|
2092
|
-
|
2093
|
-
|
2094
|
-
|
2095
|
-
@hd.instance_variable_set(:@delegate_object,
|
2096
|
-
{ block_name: 'block1' })
|
2097
|
-
|
2098
|
-
result = @hd.load_cli_or_user_selected_block(all_blocks, [], nil)
|
2099
|
-
|
2100
|
-
assert_equal all_blocks.first, result.block
|
2101
|
-
assert_nil result.state
|
2102
|
-
end
|
2103
|
-
|
2104
|
-
def test_user_selected_block
|
2105
|
-
block_state = SelectedBlockMenuState.new({ oname: 'block2' },
|
2106
|
-
:some_state)
|
2107
|
-
@hd.stubs(:wait_for_user_selected_block).returns(block_state)
|
2269
|
+
class TestHashDelegatorCollectRequiredCodeLines < Minitest::Test
|
2270
|
+
def setup
|
2271
|
+
@hd = HashDelegator.new
|
2272
|
+
@hd.instance_variable_set(:@delegate_object, {})
|
2273
|
+
@mdoc = mock('YourMDocClass')
|
2274
|
+
@selected = { shell: BlockType::VARS, body: ['key: value'] }
|
2275
|
+
HashDelegator.stubs(:read_required_blocks_from_temp_file).returns([])
|
2276
|
+
@hd.stubs(:string_send_color)
|
2277
|
+
@hd.stubs(:print)
|
2278
|
+
end
|
2108
2279
|
|
2109
|
-
|
2280
|
+
def test_collect_required_code_lines_with_vars
|
2281
|
+
YAML.stubs(:load).returns({ 'key' => 'value' })
|
2282
|
+
@mdoc.stubs(:collect_recursively_required_code).returns({ code: ['code line'] })
|
2283
|
+
result = @hd.collect_required_code_lines(@mdoc, @selected, block_source: {})
|
2110
2284
|
|
2111
|
-
|
2112
|
-
assert_equal :some_state, result.state
|
2113
|
-
end
|
2285
|
+
assert_equal ['code line'], result
|
2114
2286
|
end
|
2287
|
+
end
|
2115
2288
|
|
2116
|
-
|
2117
|
-
|
2118
|
-
|
2119
|
-
|
2120
|
-
|
2121
|
-
|
2122
|
-
|
2123
|
-
end
|
2289
|
+
class TestHashDelegatorCommandOrUserSelectedBlock < Minitest::Test
|
2290
|
+
def setup
|
2291
|
+
@hd = HashDelegator.new
|
2292
|
+
@hd.instance_variable_set(:@delegate_object, {})
|
2293
|
+
HashDelegator.stubs(:error_handler)
|
2294
|
+
@hd.stubs(:wait_for_user_selected_block)
|
2295
|
+
end
|
2124
2296
|
|
2125
|
-
|
2126
|
-
|
2127
|
-
|
2128
|
-
|
2297
|
+
def test_command_selected_block
|
2298
|
+
all_blocks = [{ oname: 'block1' }, { oname: 'block2' }]
|
2299
|
+
@hd.instance_variable_set(:@delegate_object,
|
2300
|
+
{ block_name: 'block1' })
|
2129
2301
|
|
2130
|
-
|
2302
|
+
result = @hd.load_cli_or_user_selected_block(all_blocks, [], nil)
|
2131
2303
|
|
2132
|
-
|
2133
|
-
|
2304
|
+
assert_equal all_blocks.first.merge(block_name_from_ui: false), result.block
|
2305
|
+
assert_nil result.state
|
2306
|
+
end
|
2134
2307
|
|
2135
|
-
|
2136
|
-
|
2137
|
-
|
2308
|
+
def test_user_selected_block
|
2309
|
+
block_state = SelectedBlockMenuState.new({ oname: 'block2' },
|
2310
|
+
:some_state)
|
2311
|
+
@hd.stubs(:wait_for_user_selected_block).returns(block_state)
|
2138
2312
|
|
2139
|
-
|
2313
|
+
result = @hd.load_cli_or_user_selected_block([], [], nil)
|
2140
2314
|
|
2141
|
-
|
2142
|
-
|
2315
|
+
assert_equal block_state.block.merge(block_name_from_ui: true), result.block
|
2316
|
+
assert_equal :some_state, result.state
|
2143
2317
|
end
|
2318
|
+
end
|
2144
2319
|
|
2145
|
-
|
2146
|
-
|
2147
|
-
|
2148
|
-
|
2149
|
-
|
2150
|
-
|
2151
|
-
|
2152
|
-
|
2153
|
-
|
2154
|
-
def test_create_file_and_write_string_with_permissions
|
2155
|
-
file_path = '/path/to/file'
|
2156
|
-
content = 'sample content'
|
2157
|
-
chmod_value = 0o644
|
2158
|
-
|
2159
|
-
FileUtils.expects(:mkdir_p).with('/path/to').once
|
2160
|
-
File.expects(:write).with(file_path, content).once
|
2161
|
-
File.expects(:chmod).with(chmod_value, file_path).once
|
2320
|
+
class TestHashDelegatorCountBlockInFilename < Minitest::Test
|
2321
|
+
def setup
|
2322
|
+
@hd = HashDelegator.new
|
2323
|
+
@hd.instance_variable_set(:@delegate_object,
|
2324
|
+
{ fenced_start_and_end_regex: '^```',
|
2325
|
+
filename: '/path/to/file' })
|
2326
|
+
@hd.stubs(:cfile).returns(mock('cfile'))
|
2327
|
+
end
|
2162
2328
|
|
2163
|
-
|
2164
|
-
|
2329
|
+
def test_count_blocks_in_filename
|
2330
|
+
file_content = ["```ruby\n", "puts 'Hello'\n", "```\n",
|
2331
|
+
"```python\n", "print('Hello')\n", "```\n"]
|
2332
|
+
@hd.cfile.stubs(:readlines).with('/path/to/file',
|
2333
|
+
import_paths: nil).returns(file_content)
|
2165
2334
|
|
2166
|
-
|
2167
|
-
end
|
2335
|
+
count = @hd.count_blocks_in_filename
|
2168
2336
|
|
2169
|
-
|
2170
|
-
|
2171
|
-
content = 'sample content'
|
2172
|
-
chmod_value = 0
|
2337
|
+
assert_equal 2, count
|
2338
|
+
end
|
2173
2339
|
|
2174
|
-
|
2175
|
-
|
2176
|
-
|
2340
|
+
def test_count_blocks_in_filename_with_no_matches
|
2341
|
+
file_content = ["puts 'Hello'\n", "print('Hello')\n"]
|
2342
|
+
@hd.cfile.stubs(:readlines).with('/path/to/file',
|
2343
|
+
import_paths: nil).returns(file_content)
|
2177
2344
|
|
2178
|
-
|
2179
|
-
chmod_value)
|
2345
|
+
count = @hd.count_blocks_in_filename
|
2180
2346
|
|
2181
|
-
|
2182
|
-
end
|
2347
|
+
assert_equal 0, count
|
2183
2348
|
end
|
2349
|
+
end
|
2184
2350
|
|
2185
|
-
|
2186
|
-
|
2187
|
-
|
2188
|
-
|
2189
|
-
|
2351
|
+
class TestHashDelegatorCreateAndWriteFile < Minitest::Test
|
2352
|
+
def setup
|
2353
|
+
@hd = HashDelegator.new
|
2354
|
+
HashDelegator.stubs(:error_handler)
|
2355
|
+
FileUtils.stubs(:mkdir_p)
|
2356
|
+
File.stubs(:write)
|
2357
|
+
File.stubs(:chmod)
|
2358
|
+
end
|
2190
2359
|
|
2191
|
-
|
2192
|
-
|
2193
|
-
|
2360
|
+
def test_create_file_and_write_string_with_permissions
|
2361
|
+
file_path = '/path/to/file'
|
2362
|
+
content = 'sample content'
|
2363
|
+
chmod_value = 0o644
|
2194
2364
|
|
2195
|
-
|
2365
|
+
FileUtils.expects(:mkdir_p).with('/path/to').once
|
2366
|
+
File.expects(:write).with(file_path, content).once
|
2367
|
+
File.expects(:chmod).with(chmod_value, file_path).once
|
2196
2368
|
|
2197
|
-
|
2198
|
-
|
2199
|
-
end
|
2369
|
+
HashDelegator.create_file_and_write_string_with_permissions(file_path, content,
|
2370
|
+
chmod_value)
|
2200
2371
|
|
2201
|
-
|
2202
|
-
|
2203
|
-
@hd.stubs(:menu_chrome_formatted_option).with(:menu_option_back_name).returns('Formatted Back Option')
|
2204
|
-
result = @hd.determine_block_state(selected_option)
|
2372
|
+
assert true # Placeholder for actual test assertions
|
2373
|
+
end
|
2205
2374
|
|
2206
|
-
|
2207
|
-
|
2208
|
-
|
2375
|
+
def test_create_and_write_file_without_chmod
|
2376
|
+
file_path = '/path/to/file'
|
2377
|
+
content = 'sample content'
|
2378
|
+
chmod_value = 0
|
2209
2379
|
|
2210
|
-
|
2211
|
-
|
2380
|
+
FileUtils.expects(:mkdir_p).with('/path/to').once
|
2381
|
+
File.expects(:write).with(file_path, content).once
|
2382
|
+
File.expects(:chmod).never
|
2212
2383
|
|
2213
|
-
|
2384
|
+
HashDelegator.create_file_and_write_string_with_permissions(file_path, content,
|
2385
|
+
chmod_value)
|
2214
2386
|
|
2215
|
-
|
2216
|
-
assert_equal selected_option, result.block
|
2217
|
-
end
|
2387
|
+
assert true # Placeholder for actual test assertions
|
2218
2388
|
end
|
2389
|
+
end
|
2219
2390
|
|
2220
|
-
|
2221
|
-
|
2222
|
-
|
2223
|
-
|
2224
|
-
|
2225
|
-
@hd.stubs(:string_send_color)
|
2226
|
-
end
|
2391
|
+
class TestHashDelegatorDetermineBlockState < Minitest::Test
|
2392
|
+
def setup
|
2393
|
+
@hd = HashDelegator.new
|
2394
|
+
@hd.stubs(:menu_chrome_formatted_option).returns('Formatted Option')
|
2395
|
+
end
|
2227
2396
|
|
2228
|
-
|
2229
|
-
|
2230
|
-
|
2231
|
-
@hd.instance_variable_get(:@delegate_object).stubs(:[]).with(:script_preview_tail).returns('Footer')
|
2232
|
-
@hd.instance_variable_get(:@fout).expects(:fout).times(4)
|
2397
|
+
def test_determine_block_state_exit
|
2398
|
+
selected_option = { oname: 'Formatted Option' }
|
2399
|
+
@hd.stubs(:menu_chrome_formatted_option).with(:menu_option_exit_name).returns('Formatted Option')
|
2233
2400
|
|
2234
|
-
|
2401
|
+
result = @hd.determine_block_state(selected_option)
|
2235
2402
|
|
2236
|
-
|
2237
|
-
|
2238
|
-
end
|
2403
|
+
assert_equal MenuState::EXIT, result.state
|
2404
|
+
assert_nil result.block
|
2239
2405
|
end
|
2240
2406
|
|
2241
|
-
|
2242
|
-
|
2243
|
-
|
2244
|
-
|
2245
|
-
end
|
2407
|
+
def test_determine_block_state_back
|
2408
|
+
selected_option = { oname: 'Formatted Back Option' }
|
2409
|
+
@hd.stubs(:menu_chrome_formatted_option).with(:menu_option_back_name).returns('Formatted Back Option')
|
2410
|
+
result = @hd.determine_block_state(selected_option)
|
2246
2411
|
|
2247
|
-
|
2248
|
-
|
2249
|
-
|
2250
|
-
).returns('Data String')
|
2251
|
-
@hd.stubs(:string_send_color).with('Data String',
|
2252
|
-
:execution_report_preview_frame_color).returns('Colored Data String')
|
2253
|
-
|
2254
|
-
result = @hd.fetch_color
|
2412
|
+
assert_equal MenuState::BACK, result.state
|
2413
|
+
assert_equal selected_option, result.block
|
2414
|
+
end
|
2255
2415
|
|
2256
|
-
|
2257
|
-
|
2416
|
+
def test_determine_block_state_continue
|
2417
|
+
selected_option = { oname: 'Other Option' }
|
2258
2418
|
|
2259
|
-
|
2260
|
-
@hd.instance_variable_get(:@delegate_object).stubs(:fetch).with(
|
2261
|
-
:execution_report_preview_head, ''
|
2262
|
-
).returns('')
|
2263
|
-
@hd.stubs(:string_send_color).with('',
|
2264
|
-
:execution_report_preview_frame_color).returns('Default Colored String')
|
2419
|
+
result = @hd.determine_block_state(selected_option)
|
2265
2420
|
|
2266
|
-
|
2421
|
+
assert_equal MenuState::CONTINUE, result.state
|
2422
|
+
assert_equal selected_option, result.block
|
2423
|
+
end
|
2424
|
+
end
|
2267
2425
|
|
2268
|
-
|
2269
|
-
|
2426
|
+
class TestHashDelegatorDisplayRequiredCode < Minitest::Test
|
2427
|
+
def setup
|
2428
|
+
@hd = HashDelegator.new
|
2429
|
+
@hd.instance_variable_set(:@fout, mock('fout'))
|
2430
|
+
@hd.instance_variable_set(:@delegate_object, {})
|
2431
|
+
@hd.stubs(:string_send_color)
|
2270
2432
|
end
|
2271
2433
|
|
2272
|
-
|
2273
|
-
|
2274
|
-
|
2275
|
-
|
2276
|
-
|
2434
|
+
def test_display_required_code
|
2435
|
+
required_lines = %w[line1 line2]
|
2436
|
+
@hd.instance_variable_get(:@delegate_object).stubs(:[]).with(:script_preview_head).returns('Header')
|
2437
|
+
@hd.instance_variable_get(:@delegate_object).stubs(:[]).with(:script_preview_tail).returns('Footer')
|
2438
|
+
@hd.instance_variable_get(:@fout).expects(:fout).times(4)
|
2277
2439
|
|
2278
|
-
|
2279
|
-
@hd.instance_variable_get(:@delegate_object).stubs(:fetch).with(
|
2280
|
-
:output_execution_label_format, ''
|
2281
|
-
).returns('Formatted: %{key}')
|
2282
|
-
@hd.stubs(:string_send_color).returns('Colored String')
|
2440
|
+
@hd.display_required_code(required_lines)
|
2283
2441
|
|
2284
|
-
|
2285
|
-
|
2442
|
+
# Verifying that fout is called for each line and for header & footer
|
2443
|
+
assert true # Placeholder for actual test assertions
|
2444
|
+
end
|
2445
|
+
end
|
2286
2446
|
|
2287
|
-
|
2288
|
-
|
2447
|
+
class TestHashDelegatorFetchColor < Minitest::Test
|
2448
|
+
def setup
|
2449
|
+
@hd = HashDelegator.new
|
2450
|
+
@hd.instance_variable_set(:@delegate_object, {})
|
2451
|
+
end
|
2289
2452
|
|
2290
|
-
|
2291
|
-
|
2292
|
-
|
2293
|
-
|
2294
|
-
|
2453
|
+
def test_fetch_color_with_valid_data
|
2454
|
+
@hd.instance_variable_get(:@delegate_object).stubs(:fetch).with(
|
2455
|
+
:execution_report_preview_head, ''
|
2456
|
+
).returns('Data String')
|
2457
|
+
@hd.stubs(:string_send_color).with('Data String',
|
2458
|
+
:execution_report_preview_frame_color).returns('Colored Data String')
|
2295
2459
|
|
2296
|
-
|
2297
|
-
color_sym: :execution_report_preview_frame_color)
|
2460
|
+
result = @hd.fetch_color
|
2298
2461
|
|
2299
|
-
|
2300
|
-
end
|
2462
|
+
assert_equal 'Colored Data String', result
|
2301
2463
|
end
|
2302
2464
|
|
2303
|
-
|
2304
|
-
|
2305
|
-
|
2306
|
-
|
2307
|
-
|
2465
|
+
def test_fetch_color_with_missing_data
|
2466
|
+
@hd.instance_variable_get(:@delegate_object).stubs(:fetch).with(
|
2467
|
+
:execution_report_preview_head, ''
|
2468
|
+
).returns('')
|
2469
|
+
@hd.stubs(:string_send_color).with('',
|
2470
|
+
:execution_report_preview_frame_color).returns('Default Colored String')
|
2308
2471
|
|
2309
|
-
|
2310
|
-
@hd.instance_variable_get(:@run_state).stubs(:files).returns({ stdout: %w[
|
2311
|
-
output1 output2
|
2312
|
-
] })
|
2472
|
+
result = @hd.fetch_color
|
2313
2473
|
|
2314
|
-
|
2474
|
+
assert_equal 'Default Colored String', result
|
2475
|
+
end
|
2476
|
+
end
|
2315
2477
|
|
2316
|
-
|
2317
|
-
|
2478
|
+
class TestHashDelegatorFormatReferencesSendColor < Minitest::Test
|
2479
|
+
def setup
|
2480
|
+
@hd = HashDelegator.new
|
2481
|
+
@hd.instance_variable_set(:@delegate_object, {})
|
2482
|
+
end
|
2318
2483
|
|
2319
|
-
|
2320
|
-
|
2484
|
+
def test_format_references_send_color_with_valid_data
|
2485
|
+
@hd.instance_variable_get(:@delegate_object).stubs(:fetch).with(
|
2486
|
+
:output_execution_label_format, ''
|
2487
|
+
).returns('Formatted: %{key}')
|
2488
|
+
@hd.stubs(:string_send_color).returns('Colored String')
|
2321
2489
|
|
2322
|
-
|
2490
|
+
result = @hd.format_references_send_color(context: { key: 'value' },
|
2491
|
+
color_sym: :execution_report_preview_frame_color)
|
2323
2492
|
|
2324
|
-
|
2325
|
-
|
2493
|
+
assert_equal 'Colored String', result
|
2494
|
+
end
|
2326
2495
|
|
2327
|
-
|
2328
|
-
|
2496
|
+
def test_format_references_send_color_with_missing_format
|
2497
|
+
@hd.instance_variable_get(:@delegate_object).stubs(:fetch).with(
|
2498
|
+
:output_execution_label_format, ''
|
2499
|
+
).returns('')
|
2500
|
+
@hd.stubs(:string_send_color).returns('Default Colored String')
|
2329
2501
|
|
2330
|
-
|
2502
|
+
result = @hd.format_references_send_color(context: { key: 'value' },
|
2503
|
+
color_sym: :execution_report_preview_frame_color)
|
2331
2504
|
|
2332
|
-
|
2333
|
-
end
|
2505
|
+
assert_equal 'Default Colored String', result
|
2334
2506
|
end
|
2507
|
+
end
|
2335
2508
|
|
2336
|
-
|
2337
|
-
|
2338
|
-
|
2339
|
-
|
2340
|
-
|
2341
|
-
|
2342
|
-
def test_pop_link_history_and_trigger_load
|
2343
|
-
# Verifying that history_state_pop is called
|
2344
|
-
# @hd.expects(:history_state_pop).once
|
2509
|
+
class TestHashDelegatorFormatExecutionStreams < Minitest::Test
|
2510
|
+
def setup
|
2511
|
+
@hd = HashDelegator.new
|
2512
|
+
@hd.instance_variable_set(:@run_state, mock('run_state'))
|
2513
|
+
end
|
2345
2514
|
|
2346
|
-
|
2515
|
+
def test_format_execution_streams_with_valid_key
|
2516
|
+
result = HashDelegator.format_execution_streams(:stdout,
|
2517
|
+
{ stdout: %w[output1 output2] })
|
2347
2518
|
|
2348
|
-
|
2349
|
-
assert_instance_of LoadFileLinkState, result
|
2350
|
-
assert_equal LoadFile::Load, result.load_file
|
2351
|
-
assert_nil result.link_state.block_name
|
2352
|
-
end
|
2519
|
+
assert_equal 'output1output2', result
|
2353
2520
|
end
|
2354
2521
|
|
2355
|
-
|
2356
|
-
|
2357
|
-
@hd = HashDelegator.new
|
2358
|
-
@mock_block_state = mock('block_state')
|
2359
|
-
end
|
2522
|
+
def test_format_execution_streams_with_empty_key
|
2523
|
+
@hd.instance_variable_get(:@run_state).stubs(:files).returns({})
|
2360
2524
|
|
2361
|
-
|
2362
|
-
@mock_block_state.stubs(:state).returns(MenuState::BACK)
|
2363
|
-
@mock_block_state.stubs(:block).returns({ oname: 'sample_block' })
|
2525
|
+
result = HashDelegator.format_execution_streams(:stderr)
|
2364
2526
|
|
2365
|
-
|
2527
|
+
assert_equal '', result
|
2528
|
+
end
|
2366
2529
|
|
2367
|
-
|
2368
|
-
|
2369
|
-
assert @hd.instance_variable_get(:@menu_user_clicked_back_link)
|
2370
|
-
end
|
2530
|
+
def test_format_execution_streams_with_nil_files
|
2531
|
+
@hd.instance_variable_get(:@run_state).stubs(:files).returns(nil)
|
2371
2532
|
|
2372
|
-
|
2373
|
-
@mock_block_state.stubs(:state).returns(MenuState::CONTINUE)
|
2374
|
-
@mock_block_state.stubs(:block).returns({ oname: 'another_block' })
|
2533
|
+
result = HashDelegator.format_execution_streams(:stdin)
|
2375
2534
|
|
2376
|
-
|
2535
|
+
assert_equal '', result
|
2536
|
+
end
|
2537
|
+
end
|
2377
2538
|
|
2378
|
-
|
2379
|
-
|
2380
|
-
|
2381
|
-
|
2539
|
+
class TestHashDelegatorHandleBackLink < Minitest::Test
|
2540
|
+
def setup
|
2541
|
+
@hd = HashDelegator.new
|
2542
|
+
@hd.stubs(:history_state_pop)
|
2543
|
+
end
|
2382
2544
|
|
2383
|
-
|
2384
|
-
|
2385
|
-
|
2545
|
+
def test_pop_link_history_and_trigger_load
|
2546
|
+
# Verifying that history_state_pop is called
|
2547
|
+
# @hd.expects(:history_state_pop).once
|
2386
2548
|
|
2387
|
-
|
2549
|
+
result = @hd.pop_link_history_and_trigger_load
|
2388
2550
|
|
2389
|
-
|
2390
|
-
|
2391
|
-
|
2551
|
+
# Asserting the result is an instance of LoadFileLinkState
|
2552
|
+
assert_instance_of LoadFileLinkState, result
|
2553
|
+
assert_equal LoadFile::Load, result.load_file
|
2554
|
+
assert_nil result.link_state.block_name
|
2392
2555
|
end
|
2556
|
+
end
|
2393
2557
|
|
2394
|
-
|
2395
|
-
|
2396
|
-
|
2397
|
-
|
2398
|
-
|
2399
|
-
end
|
2558
|
+
class TestHashDelegatorHandleBlockState < Minitest::Test
|
2559
|
+
def setup
|
2560
|
+
@hd = HashDelegator.new
|
2561
|
+
@mock_block_state = mock('block_state')
|
2562
|
+
end
|
2400
2563
|
|
2401
|
-
|
2402
|
-
|
2403
|
-
|
2404
|
-
{ output_script: false,
|
2405
|
-
user_must_approve: false })
|
2564
|
+
def test_handle_back_or_continue_with_back
|
2565
|
+
@mock_block_state.stubs(:state).returns(MenuState::BACK)
|
2566
|
+
@mock_block_state.stubs(:block).returns({ oname: 'sample_block' })
|
2406
2567
|
|
2407
|
-
|
2408
|
-
# Expectations and assertions go here
|
2409
|
-
end
|
2568
|
+
@hd.handle_back_or_continue(@mock_block_state)
|
2410
2569
|
|
2411
|
-
|
2412
|
-
|
2413
|
-
|
2414
|
-
|
2415
|
-
user_must_approve: true })
|
2570
|
+
assert_equal 'sample_block',
|
2571
|
+
@hd.instance_variable_get(:@delegate_object)[:block_name]
|
2572
|
+
assert @hd.instance_variable_get(:@menu_user_clicked_back_link)
|
2573
|
+
end
|
2416
2574
|
|
2417
|
-
|
2418
|
-
|
2419
|
-
|
2575
|
+
def test_handle_back_or_continue_with_continue
|
2576
|
+
@mock_block_state.stubs(:state).returns(MenuState::CONTINUE)
|
2577
|
+
@mock_block_state.stubs(:block).returns({ oname: 'another_block' })
|
2420
2578
|
|
2421
|
-
|
2422
|
-
# Mock the delegate object configuration
|
2423
|
-
@hd.instance_variable_set(:@delegate_object,
|
2424
|
-
{ output_script: true,
|
2425
|
-
user_must_approve: false })
|
2579
|
+
@hd.handle_back_or_continue(@mock_block_state)
|
2426
2580
|
|
2427
|
-
|
2428
|
-
|
2429
|
-
|
2581
|
+
assert_equal 'another_block',
|
2582
|
+
@hd.instance_variable_get(:@delegate_object)[:block_name]
|
2583
|
+
refute @hd.instance_variable_get(:@menu_user_clicked_back_link)
|
2430
2584
|
end
|
2431
2585
|
|
2432
|
-
|
2586
|
+
def test_handle_back_or_continue_with_other
|
2587
|
+
@mock_block_state.stubs(:state).returns(nil) # MenuState::OTHER
|
2588
|
+
@mock_block_state.stubs(:block).returns({ oname: 'other_block' })
|
2433
2589
|
|
2434
|
-
|
2435
|
-
def setup
|
2436
|
-
@hd = HashDelegator.new
|
2437
|
-
@hd.instance_variable_set(:@run_state,
|
2438
|
-
OpenStruct.new(files: { stdout: [] }))
|
2439
|
-
@hd.instance_variable_set(:@delegate_object,
|
2440
|
-
{ output_stdout: true })
|
2441
|
-
end
|
2590
|
+
@hd.handle_back_or_continue(@mock_block_state)
|
2442
2591
|
|
2443
|
-
|
2444
|
-
|
2445
|
-
|
2592
|
+
assert_nil @hd.instance_variable_get(:@delegate_object)[:block_name]
|
2593
|
+
assert_nil @hd.instance_variable_get(:@menu_user_clicked_back_link)
|
2594
|
+
end
|
2595
|
+
end
|
2446
2596
|
|
2447
|
-
|
2597
|
+
class TestHashDelegatorHandleGenericBlock < Minitest::Test
|
2598
|
+
def setup
|
2599
|
+
@hd = HashDelegator.new
|
2600
|
+
@mock_document = mock('MarkdownDocument')
|
2601
|
+
@selected_item = mock('FCB')
|
2602
|
+
end
|
2448
2603
|
|
2449
|
-
|
2604
|
+
def test_compile_execute_and_trigger_reuse_without_user_approval
|
2605
|
+
# Mock the delegate object configuration
|
2606
|
+
@hd.instance_variable_set(:@delegate_object,
|
2607
|
+
{ output_script: false,
|
2608
|
+
user_must_approve: false })
|
2450
2609
|
|
2451
|
-
|
2452
|
-
|
2453
|
-
|
2610
|
+
# Test the method without user approval
|
2611
|
+
# Expectations and assertions go here
|
2612
|
+
end
|
2454
2613
|
|
2455
|
-
|
2456
|
-
|
2457
|
-
|
2458
|
-
|
2614
|
+
def test_compile_execute_and_trigger_reuse_with_user_approval
|
2615
|
+
# Mock the delegate object configuration
|
2616
|
+
@hd.instance_variable_set(:@delegate_object,
|
2617
|
+
{ output_script: false,
|
2618
|
+
user_must_approve: true })
|
2459
2619
|
|
2460
|
-
|
2620
|
+
# Test the method with user approval
|
2621
|
+
# Expectations and assertions go here
|
2622
|
+
end
|
2461
2623
|
|
2462
|
-
|
2624
|
+
def test_compile_execute_and_trigger_reuse_with_output_script
|
2625
|
+
# Mock the delegate object configuration
|
2626
|
+
@hd.instance_variable_set(:@delegate_object,
|
2627
|
+
{ output_script: true,
|
2628
|
+
user_must_approve: false })
|
2463
2629
|
|
2464
|
-
|
2465
|
-
|
2466
|
-
end
|
2630
|
+
# Test the method with output script option
|
2631
|
+
# Expectations and assertions go here
|
2467
2632
|
end
|
2633
|
+
end
|
2468
2634
|
|
2469
|
-
|
2470
|
-
def setup
|
2471
|
-
@hd = HashDelegator.new
|
2472
|
-
@hd.instance_variable_set(:@delegate_object,
|
2473
|
-
{ filename: 'test.md' })
|
2474
|
-
@hd.stubs(:check_file_existence).with('test.md').returns(true)
|
2475
|
-
@hd.stubs(:initial_state).returns({})
|
2476
|
-
@hd.stubs(:cfile).returns(Minitest::Mock.new)
|
2477
|
-
@hd.stubs(:update_line_and_block_state)
|
2478
|
-
end
|
2635
|
+
# require 'stringio'
|
2479
2636
|
|
2480
|
-
|
2481
|
-
|
2482
|
-
|
2637
|
+
class TestHashDelegatorHandleStream < Minitest::Test
|
2638
|
+
def setup
|
2639
|
+
@hd = HashDelegator.new
|
2640
|
+
@hd.instance_variable_set(:@run_state,
|
2641
|
+
OpenStruct.new(files: { stdout: [] }))
|
2642
|
+
@hd.instance_variable_set(:@delegate_object,
|
2643
|
+
{ output_stdout: true })
|
2644
|
+
end
|
2483
2645
|
|
2484
|
-
|
2485
|
-
|
2646
|
+
def test_handle_stream
|
2647
|
+
stream = StringIO.new("line 1\nline 2\n")
|
2648
|
+
file_type = :stdout
|
2486
2649
|
|
2487
|
-
|
2488
|
-
end
|
2650
|
+
Thread.new { @hd.handle_stream(stream, file_type) }
|
2489
2651
|
|
2490
|
-
|
2491
|
-
@hd.stubs(:check_file_existence).with('test.md').returns(false)
|
2652
|
+
@hd.wait_for_stream_processing
|
2492
2653
|
|
2493
|
-
|
2494
|
-
|
2495
|
-
end)
|
2496
|
-
end
|
2654
|
+
assert_equal ['line 1', 'line 2'],
|
2655
|
+
@hd.instance_variable_get(:@run_state).files[:stdout]
|
2497
2656
|
end
|
2498
2657
|
|
2499
|
-
|
2500
|
-
|
2501
|
-
|
2502
|
-
|
2503
|
-
menu_option_back_name: 'Back',
|
2504
|
-
menu_chrome_color: :red,
|
2505
|
-
menu_chrome_format: '-- %s --'
|
2506
|
-
})
|
2507
|
-
@hd.stubs(:menu_chrome_formatted_option).with(:menu_option_back_name).returns('-- Back --')
|
2508
|
-
@hd.stubs(:string_send_color).with('-- Back --',
|
2509
|
-
:menu_chrome_color).returns('-- Back --'.red)
|
2510
|
-
end
|
2658
|
+
def test_handle_stream_with_io_error
|
2659
|
+
stream = StringIO.new("line 1\nline 2\n")
|
2660
|
+
file_type = :stdout
|
2661
|
+
stream.stubs(:each_line).raises(IOError)
|
2511
2662
|
|
2512
|
-
|
2513
|
-
assert_equal '-- Back --'.red,
|
2514
|
-
@hd.menu_chrome_colored_option(:menu_option_back_name)
|
2515
|
-
end
|
2663
|
+
Thread.new { @hd.handle_stream(stream, file_type) }
|
2516
2664
|
|
2517
|
-
|
2518
|
-
@hd.instance_variable_set(:@delegate_object,
|
2519
|
-
{ menu_option_back_name: 'Back' })
|
2520
|
-
assert_equal '-- Back --',
|
2521
|
-
@hd.menu_chrome_colored_option(:menu_option_back_name)
|
2522
|
-
end
|
2523
|
-
end
|
2524
|
-
|
2525
|
-
class TestHashDelegatorMenuChromeFormattedOptionWithoutFormat < Minitest::Test
|
2526
|
-
def setup
|
2527
|
-
@hd = HashDelegator.new
|
2528
|
-
@hd.instance_variable_set(:@delegate_object, {
|
2529
|
-
menu_option_back_name: "'Back'",
|
2530
|
-
menu_chrome_format: '-- %s --'
|
2531
|
-
})
|
2532
|
-
@hd.stubs(:safeval).with("'Back'").returns('Back')
|
2533
|
-
end
|
2534
|
-
|
2535
|
-
def test_menu_chrome_formatted_option_with_format
|
2536
|
-
assert_equal '-- Back --',
|
2537
|
-
@hd.menu_chrome_formatted_option(:menu_option_back_name)
|
2538
|
-
end
|
2665
|
+
@hd.wait_for_stream_processing
|
2539
2666
|
|
2540
|
-
|
2541
|
-
|
2542
|
-
{ menu_option_back_name: "'Back'" })
|
2543
|
-
assert_equal 'Back',
|
2544
|
-
@hd.menu_chrome_formatted_option(:menu_option_back_name)
|
2545
|
-
end
|
2667
|
+
assert_equal [],
|
2668
|
+
@hd.instance_variable_get(:@run_state).files[:stdout]
|
2546
2669
|
end
|
2670
|
+
end
|
2547
2671
|
|
2548
|
-
|
2549
|
-
|
2550
|
-
|
2551
|
-
|
2552
|
-
|
2553
|
-
|
2554
|
-
|
2672
|
+
class TestHashDelegatorIterBlocksFromNestedFiles < Minitest::Test
|
2673
|
+
def setup
|
2674
|
+
@hd = HashDelegator.new
|
2675
|
+
@hd.instance_variable_set(:@delegate_object,
|
2676
|
+
{ filename: 'test.md' })
|
2677
|
+
@hd.stubs(:check_file_existence).with('test.md').returns(true)
|
2678
|
+
@hd.stubs(:initial_state).returns({})
|
2679
|
+
@hd.stubs(:cfile).returns(Minitest::Mock.new)
|
2680
|
+
@hd.stubs(:update_line_and_block_state)
|
2681
|
+
end
|
2555
2682
|
|
2556
|
-
|
2557
|
-
|
2558
|
-
|
2559
|
-
regex = /```(?<name>\w+)(?<rest>.*)/
|
2683
|
+
def test_iter_blocks_from_nested_files
|
2684
|
+
@hd.cfile.expect(:readlines, ['line 1', 'line 2'], ['test.md'], import_paths: nil)
|
2685
|
+
selected_messages = ['filtered message']
|
2560
2686
|
|
2561
|
-
|
2687
|
+
result = @hd.iter_blocks_from_nested_files { selected_messages }
|
2688
|
+
assert_equal ['line 1', 'line 2'], result
|
2562
2689
|
|
2563
|
-
|
2564
|
-
assert_equal headings, fcb.headings
|
2565
|
-
assert_equal 'fenced', fcb.dname
|
2566
|
-
end
|
2690
|
+
@hd.cfile.verify
|
2567
2691
|
end
|
2568
2692
|
|
2569
|
-
|
2570
|
-
|
2571
|
-
@hd = HashDelegator.new
|
2572
|
-
@hd.instance_variable_set(:@delegate_object,
|
2573
|
-
{ red: 'red', green: 'green' })
|
2574
|
-
end
|
2693
|
+
def test_iter_blocks_from_nested_files_with_no_file
|
2694
|
+
@hd.stubs(:check_file_existence).with('test.md').returns(false)
|
2575
2695
|
|
2576
|
-
|
2577
|
-
|
2578
|
-
|
2579
|
-
@hd.string_send_color('World', :green)
|
2580
|
-
assert_equal 'Default'.plain,
|
2581
|
-
@hd.string_send_color('Default', :blue)
|
2582
|
-
end
|
2696
|
+
assert_nil(@hd.iter_blocks_from_nested_files do
|
2697
|
+
['filtered message']
|
2698
|
+
end)
|
2583
2699
|
end
|
2700
|
+
end
|
2584
2701
|
|
2585
|
-
|
2586
|
-
|
2587
|
-
@hd
|
2588
|
-
|
2589
|
-
|
2590
|
-
|
2591
|
-
|
2592
|
-
|
2702
|
+
class TestHashDelegatorMenuChromeColoredOption < Minitest::Test
|
2703
|
+
def setup
|
2704
|
+
@hd = HashDelegator.new
|
2705
|
+
@hd.instance_variable_set(:@delegate_object, {
|
2706
|
+
menu_option_back_name: 'Back',
|
2707
|
+
menu_chrome_color: :red,
|
2708
|
+
menu_chrome_format: '-- %s --'
|
2709
|
+
})
|
2710
|
+
@hd.stubs(:menu_chrome_formatted_option).with(:menu_option_back_name).returns('-- Back --')
|
2711
|
+
@hd.stubs(:string_send_color).with('-- Back --',
|
2712
|
+
:menu_chrome_color).returns('-- Back --'.red)
|
2593
2713
|
end
|
2594
2714
|
|
2595
|
-
def
|
2596
|
-
|
2597
|
-
|
2598
|
-
block_called = true
|
2599
|
-
end
|
2600
|
-
refute block_called
|
2715
|
+
def test_menu_chrome_colored_option_with_color
|
2716
|
+
assert_equal '-- Back --'.red,
|
2717
|
+
@hd.menu_chrome_colored_option(:menu_option_back_name)
|
2601
2718
|
end
|
2602
2719
|
|
2603
|
-
def
|
2604
|
-
|
2605
|
-
|
2720
|
+
def test_menu_chrome_colored_option_without_color
|
2721
|
+
@hd.instance_variable_set(:@delegate_object,
|
2722
|
+
{ menu_option_back_name: 'Back' })
|
2723
|
+
assert_equal '-- Back --',
|
2724
|
+
@hd.menu_chrome_colored_option(:menu_option_back_name)
|
2606
2725
|
end
|
2607
2726
|
end
|
2608
2727
|
|
2609
|
-
class
|
2728
|
+
class TestHashDelegatorMenuChromeFormattedOptionWithoutFormat < Minitest::Test
|
2610
2729
|
def setup
|
2611
2730
|
@hd = HashDelegator.new
|
2612
2731
|
@hd.instance_variable_set(:@delegate_object, {
|
2613
|
-
|
2614
|
-
|
2615
|
-
heading3_match: '^### (?<name>.+)$'
|
2732
|
+
menu_option_back_name: "'Back'",
|
2733
|
+
menu_chrome_format: '-- %s --'
|
2616
2734
|
})
|
2735
|
+
HashDelegator.stubs(:safeval).with("'Back'").returns('Back')
|
2617
2736
|
end
|
2618
2737
|
|
2619
|
-
def
|
2620
|
-
assert_equal
|
2621
|
-
@hd.
|
2622
|
-
|
2623
|
-
|
2624
|
-
|
2625
|
-
|
2626
|
-
|
2627
|
-
|
2628
|
-
|
2738
|
+
def test_menu_chrome_formatted_option_with_format
|
2739
|
+
assert_equal '-- Back --',
|
2740
|
+
@hd.menu_chrome_formatted_option(:menu_option_back_name)
|
2741
|
+
end
|
2742
|
+
|
2743
|
+
def test_menu_chrome_formatted_option_without_format
|
2744
|
+
@hd.instance_variable_set(:@delegate_object,
|
2745
|
+
{ menu_option_back_name: "'Back'" })
|
2746
|
+
assert_equal 'Back',
|
2747
|
+
@hd.menu_chrome_formatted_option(:menu_option_back_name)
|
2629
2748
|
end
|
2630
2749
|
end
|
2631
2750
|
|
2632
|
-
class
|
2751
|
+
class TestHashDelegatorStartFencedBlock < Minitest::Test
|
2633
2752
|
def setup
|
2634
|
-
@hd = HashDelegator.new
|
2635
|
-
|
2636
|
-
|
2637
|
-
|
2638
|
-
@hd.stubs(:default_block_title_from_body)
|
2639
|
-
@hd.stubs(:yield_to_block_if_applicable)
|
2753
|
+
@hd = HashDelegator.new({
|
2754
|
+
block_name_wrapper_match: 'WRAPPER_REGEX',
|
2755
|
+
block_calls_scan: 'CALLS_REGEX'
|
2756
|
+
})
|
2640
2757
|
end
|
2641
2758
|
|
2642
|
-
def
|
2643
|
-
|
2644
|
-
|
2645
|
-
|
2759
|
+
def test_start_fenced_block
|
2760
|
+
line = '```fenced'
|
2761
|
+
headings = ['Heading 1']
|
2762
|
+
regex = /```(?<name>\w+)(?<rest>.*)/
|
2646
2763
|
|
2647
|
-
@hd.
|
2648
|
-
end
|
2764
|
+
fcb = @hd.start_fenced_block(line, headings, regex)
|
2649
2765
|
|
2650
|
-
|
2651
|
-
|
2652
|
-
|
2653
|
-
@hd.update_menu_attrib_yield_selected(@fcb, [:some_message])
|
2766
|
+
assert_instance_of MarkdownExec::FCB, fcb
|
2767
|
+
assert_equal headings, fcb.headings
|
2768
|
+
assert_equal 'fenced', fcb.dname
|
2654
2769
|
end
|
2655
2770
|
end
|
2656
2771
|
|
2657
|
-
class
|
2772
|
+
class TestHashDelegatorStringSendColor < Minitest::Test
|
2658
2773
|
def setup
|
2659
2774
|
@hd = HashDelegator.new
|
2660
|
-
@hd.
|
2775
|
+
@hd.instance_variable_set(:@delegate_object,
|
2776
|
+
{ red: 'red', green: 'green' })
|
2661
2777
|
end
|
2662
2778
|
|
2663
|
-
def
|
2664
|
-
|
2665
|
-
|
2666
|
-
|
2779
|
+
def test_string_send_color
|
2780
|
+
assert_equal 'Hello'.red, @hd.string_send_color('Hello', :red)
|
2781
|
+
assert_equal 'World'.green,
|
2782
|
+
@hd.string_send_color('World', :green)
|
2783
|
+
assert_equal 'Default'.plain,
|
2784
|
+
@hd.string_send_color('Default', :blue)
|
2785
|
+
end
|
2786
|
+
end
|
2667
2787
|
|
2668
|
-
|
2669
|
-
|
2788
|
+
def test_yield_line_if_selected_with_line
|
2789
|
+
block_called = false
|
2790
|
+
HashDelegator.yield_line_if_selected('Test line', [:line]) do |type, content|
|
2791
|
+
block_called = true
|
2792
|
+
assert_equal :line, type
|
2793
|
+
assert_equal 'Test line', content.body[0]
|
2794
|
+
end
|
2795
|
+
assert block_called
|
2796
|
+
end
|
2670
2797
|
|
2671
|
-
|
2672
|
-
|
2673
|
-
|
2674
|
-
|
2798
|
+
def test_yield_line_if_selected_without_line
|
2799
|
+
block_called = false
|
2800
|
+
HashDelegator.yield_line_if_selected('Test line', [:other]) do |_|
|
2801
|
+
block_called = true
|
2675
2802
|
end
|
2803
|
+
refute block_called
|
2804
|
+
end
|
2676
2805
|
|
2677
|
-
|
2678
|
-
|
2679
|
-
|
2680
|
-
|
2681
|
-
|
2806
|
+
def test_yield_line_if_selected_without_block
|
2807
|
+
result = HashDelegator.yield_line_if_selected('Test line', [:line])
|
2808
|
+
assert_nil result
|
2809
|
+
end
|
2810
|
+
end
|
2682
2811
|
|
2683
|
-
|
2684
|
-
|
2812
|
+
class TestHashDelegatorUpdateMenuAttribYieldSelectedWithBody < Minitest::Test
|
2813
|
+
def setup
|
2814
|
+
@hd = HashDelegator.new
|
2815
|
+
@fcb = mock('Fcb')
|
2816
|
+
@fcb.stubs(:body).returns(true)
|
2817
|
+
HashDelegator.stubs(:initialize_fcb_names)
|
2818
|
+
HashDelegator.stubs(:default_block_title_from_body)
|
2819
|
+
Filter.stubs(:yield_to_block_if_applicable)
|
2820
|
+
end
|
2685
2821
|
|
2686
|
-
|
2687
|
-
|
2688
|
-
|
2689
|
-
|
2690
|
-
|
2822
|
+
def test_update_menu_attrib_yield_selected_with_body
|
2823
|
+
HashDelegator.expects(:initialize_fcb_names).with(@fcb)
|
2824
|
+
HashDelegator.expects(:default_block_title_from_body).with(@fcb)
|
2825
|
+
Filter.expects(:yield_to_block_if_applicable).with(@fcb, [:some_message], {})
|
2826
|
+
|
2827
|
+
HashDelegator.update_menu_attrib_yield_selected(@fcb, [:some_message])
|
2691
2828
|
end
|
2692
2829
|
|
2693
|
-
|
2694
|
-
|
2695
|
-
|
2696
|
-
|
2697
|
-
|
2698
|
-
|
2830
|
+
def test_update_menu_attrib_yield_selected_without_body
|
2831
|
+
@fcb.stubs(:body).returns(nil)
|
2832
|
+
HashDelegator.expects(:initialize_fcb_names).with(@fcb)
|
2833
|
+
HashDelegator.update_menu_attrib_yield_selected(@fcb, [:some_message])
|
2834
|
+
end
|
2835
|
+
end
|
2699
2836
|
|
2700
|
-
|
2701
|
-
|
2702
|
-
|
2703
|
-
|
2704
|
-
|
2705
|
-
assert_equal @fcb, fcb
|
2706
|
-
end
|
2707
|
-
assert block_called
|
2708
|
-
end
|
2837
|
+
class TestHashDelegatorWaitForUserSelectedBlock < Minitest::Test
|
2838
|
+
def setup
|
2839
|
+
@hd = HashDelegator.new
|
2840
|
+
HashDelegator.stubs(:error_handler)
|
2841
|
+
end
|
2709
2842
|
|
2710
|
-
|
2711
|
-
|
2712
|
-
|
2843
|
+
def test_wait_for_user_selected_block_with_back_state
|
2844
|
+
mock_block_state = Struct.new(:state, :block).new(MenuState::BACK,
|
2845
|
+
{ oname: 'back_block' })
|
2846
|
+
@hd.stubs(:wait_for_user_selection).returns(mock_block_state)
|
2847
|
+
|
2848
|
+
result = @hd.wait_for_user_selected_block([], ['Block 1', 'Block 2'],
|
2849
|
+
nil)
|
2850
|
+
|
2851
|
+
assert_equal 'back_block',
|
2852
|
+
@hd.instance_variable_get(:@delegate_object)[:block_name]
|
2853
|
+
assert @hd.instance_variable_get(:@menu_user_clicked_back_link)
|
2854
|
+
assert_equal mock_block_state, result
|
2855
|
+
end
|
2856
|
+
|
2857
|
+
def test_wait_for_user_selected_block_with_continue_state
|
2858
|
+
mock_block_state = Struct.new(:state, :block).new(
|
2859
|
+
MenuState::CONTINUE, { oname: 'continue_block' }
|
2860
|
+
)
|
2861
|
+
@hd.stubs(:wait_for_user_selection).returns(mock_block_state)
|
2862
|
+
|
2863
|
+
result = @hd.wait_for_user_selected_block([], ['Block 1', 'Block 2'],
|
2864
|
+
nil)
|
2865
|
+
|
2866
|
+
assert_equal 'continue_block',
|
2867
|
+
@hd.instance_variable_get(:@delegate_object)[:block_name]
|
2868
|
+
refute @hd.instance_variable_get(:@menu_user_clicked_back_link)
|
2869
|
+
assert_equal mock_block_state, result
|
2870
|
+
end
|
2871
|
+
end
|
2872
|
+
|
2873
|
+
class TestHashDelegatorYieldToBlock < Minitest::Test
|
2874
|
+
def setup
|
2875
|
+
@hd = HashDelegator.new
|
2876
|
+
@fcb = mock('Fcb')
|
2877
|
+
MarkdownExec::Filter.stubs(:fcb_select?).returns(true)
|
2878
|
+
end
|
2879
|
+
|
2880
|
+
def test_yield_to_block_if_applicable_with_correct_conditions
|
2881
|
+
block_called = false
|
2882
|
+
Filter.yield_to_block_if_applicable(@fcb, [:blocks]) do |type, fcb|
|
2883
|
+
block_called = true
|
2884
|
+
assert_equal :blocks, type
|
2885
|
+
assert_equal @fcb, fcb
|
2713
2886
|
end
|
2887
|
+
assert block_called
|
2888
|
+
end
|
2714
2889
|
|
2715
|
-
|
2716
|
-
|
2717
|
-
|
2718
|
-
|
2719
|
-
|
2720
|
-
|
2721
|
-
|
2890
|
+
def test_yield_to_block_if_applicable_without_block
|
2891
|
+
result = Filter.yield_to_block_if_applicable(@fcb, [:blocks])
|
2892
|
+
assert_nil result
|
2893
|
+
end
|
2894
|
+
|
2895
|
+
def test_yield_to_block_if_applicable_with_incorrect_conditions
|
2896
|
+
block_called = false
|
2897
|
+
MarkdownExec::Filter.stubs(:fcb_select?).returns(false)
|
2898
|
+
Filter.yield_to_block_if_applicable(@fcb, [:non_blocks]) do |_|
|
2899
|
+
block_called = true
|
2722
2900
|
end
|
2901
|
+
refute block_called
|
2723
2902
|
end
|
2724
|
-
end
|
2725
|
-
end
|
2903
|
+
end
|
2904
|
+
end # module MarkdownExec
|