markdown_exec 1.6 → 1.8

Sign up to get free protection for your applications and to get access to all the features.
data/lib/mdoc.rb CHANGED
@@ -21,7 +21,7 @@ module MarkdownExec
21
21
  #
22
22
  # @param table [Array<Hash>] An array of hashes representing markdown sections.
23
23
  #
24
- def initialize(table)
24
+ def initialize(table = [])
25
25
  @table = table
26
26
  end
27
27
 
@@ -47,8 +47,8 @@ module MarkdownExec
47
47
  # write named variables to block at top of script
48
48
  #
49
49
  fcb[:body].join(' ').split.compact.map do |key|
50
- format(opts[:block_type_port_set_format],
51
- { key: key, value: ENV.fetch(key, nil) })
50
+ ### format(opts[:block_type_port_set_format], { key: key, value: ENV.fetch(key, nil) })
51
+ "key: #{key}, value: #{ENV.fetch(key, nil)}"
52
52
  end
53
53
  end
54
54
 
@@ -72,22 +72,30 @@ module MarkdownExec
72
72
  def collect_recursively_required_blocks(name)
73
73
  name_block = get_block_by_anyname(name)
74
74
  if name_block.nil? || name_block.keys.empty?
75
- raise "Named code block `#{name}` not found."
75
+ raise "Named code block `#{name}` not found. (@#{__LINE__})"
76
76
  end
77
77
 
78
- all = [name_block.oname] + recursively_required(name_block[:reqs])
78
+ # all_dependency_names = [name_block.oname] + recursively_required(name_block[:reqs])
79
+ dependencies = collect_dependencies(name_block[:oname])
80
+ all_dependency_names = collect_unique_names(dependencies).push(name_block[:oname]).uniq
81
+ unmet_dependencies = all_dependency_names.dup
79
82
 
80
- # in order of appearance in document
83
+ # in order of appearance in document (@table)
81
84
  # insert function blocks
82
- @table.select { |fcb| all.include? fcb.oname }
83
- .map do |fcb|
85
+ blocks = @table.select { |fcb| all_dependency_names.include? fcb[:oname] }
86
+ .map do |fcb|
87
+ unmet_dependencies.delete(fcb[:oname]) # may not exist if block name is duplicated
84
88
  if (call = fcb[:call])
85
89
  [get_block_by_oname("[#{call.match(/^%\((\S+) |\)/)[1]}]")
86
90
  .merge({ cann: call })]
87
91
  else
88
92
  []
89
93
  end + [fcb]
90
- end.flatten(1)
94
+ end.flatten(1) #.tap { rbp }
95
+ { all_dependency_names: all_dependency_names,
96
+ blocks: blocks,
97
+ dependencies: dependencies,
98
+ unmet_dependencies: unmet_dependencies }
91
99
  end
92
100
 
93
101
  # Collects recursively required code blocks and returns them as an array of strings.
@@ -95,10 +103,9 @@ module MarkdownExec
95
103
  # @param name [String] The name of the code block to start the collection from.
96
104
  # @return [Array<String>] An array of strings containing the collected code blocks.
97
105
  #
98
- def collect_recursively_required_code(name, opts: {})
99
- code = collect_wrapped_blocks(
100
- blocks = collect_recursively_required_blocks(name)
101
- ).map do |fcb|
106
+ def collect_recursively_required_code(name, label_body: true, label_format_above: nil, label_format_below: nil)
107
+ block_search = collect_recursively_required_blocks(name)
108
+ block_search.merge({ code: collect_wrapped_blocks(block_search[:blocks]).map do |fcb|
102
109
  if fcb[:cann]
103
110
  collect_block_code_cann(fcb)
104
111
  elsif fcb[:stdout]
@@ -108,11 +115,18 @@ module MarkdownExec
108
115
  nil
109
116
  elsif fcb[:shell] == BlockType::PORT
110
117
  collect_block_code_shell(fcb)
111
- else
118
+ elsif label_body
119
+ [label_format_above && format(label_format_above, { name: fcb[:oname] })] +
120
+ fcb[:body] +
121
+ [label_format_below && format(label_format_below, { name: fcb[:oname] })]
122
+ else # raw body
112
123
  fcb[:body]
113
124
  end
114
- end.compact.flatten(1)
115
- { blocks: blocks, code: code }
125
+ end.compact.flatten(1).compact })
126
+ end
127
+
128
+ def collect_unique_names(hash)
129
+ hash.values.flatten.uniq
116
130
  end
117
131
 
118
132
  # Retrieves code blocks that are wrapped
@@ -209,9 +223,7 @@ module MarkdownExec
209
223
  end
210
224
  end
211
225
 
212
- # def load_auto_blocks(opts)
213
- # end
214
-
226
+ # if tr ue
215
227
  # Recursively fetches required code blocks for a given list of requirements.
216
228
  #
217
229
  # @param reqs [Array<String>] An array of requirements to start the recursion from.
@@ -222,7 +234,7 @@ module MarkdownExec
222
234
 
223
235
  rem = reqs
224
236
  memo = []
225
- while rem.count.positive?
237
+ while rem && rem.count.positive?
226
238
  rem = rem.map do |req|
227
239
  next if memo.include? req
228
240
 
@@ -235,6 +247,57 @@ module MarkdownExec
235
247
  memo
236
248
  end
237
249
 
250
+ # else
251
+ # Recursively fetches required code blocks for a given list of requirements.
252
+ #
253
+ # @param source [String] The name of the code block to start the recursion from.
254
+ # @return [Hash] A list of code blocks required by each source code block.
255
+ #
256
+ def recursively_required_hash(source, memo = Hash.new([]))
257
+ return memo unless source
258
+ return memo if memo.keys.include? source
259
+
260
+ block = get_block_by_anyname(source)
261
+ if block.nil? || block.keys.empty?
262
+ raise "Named code block `#{source}` not found. (@#{__LINE__})"
263
+ end
264
+
265
+ memo[source] = block[:reqs]
266
+ return memo unless memo[source]&.count&.positive?
267
+
268
+ memo[source].each do |req|
269
+ next if memo.keys.include? req
270
+
271
+ recursively_required_hash(req, memo)
272
+ end
273
+ memo
274
+ end
275
+
276
+ # end
277
+
278
+ # Recursively collects dependencies of a given source.
279
+ # @param source [String] The name of the initial source block.
280
+ # @param memo [Hash] A memoization hash to store resolved dependencies.
281
+ # @return [Hash] A hash mapping sources to their respective dependencies.
282
+ def collect_dependencies(source, memo = {})
283
+ return memo unless source
284
+
285
+ if (block = get_block_by_anyname(source)).nil? || block.keys.empty?
286
+ if true
287
+ return memo
288
+ else
289
+ raise "Named code block `#{source}` not found. (@#{__LINE__})"
290
+ end
291
+ end
292
+
293
+ return memo unless block[:reqs]
294
+
295
+ memo[source] = block[:reqs]
296
+
297
+ block[:reqs].each { |req| collect_dependencies(req, memo) unless memo.key?(req) }
298
+ memo
299
+ end
300
+
238
301
  def select_elements_with_neighbor_conditions(array,
239
302
  last_selected_placeholder = nil, next_selected_placeholder = nil)
240
303
  selected_elements = []
@@ -286,16 +349,64 @@ if $PROGRAM_NAME == __FILE__
286
349
  Bundler.require(:default)
287
350
 
288
351
  require 'minitest/autorun'
352
+ require 'mocha/minitest'
289
353
 
290
354
  module MarkdownExec
355
+ class TestMDocCollectDependencies < Minitest::Test
356
+ def setup
357
+ @mdoc = MDoc.new
358
+ end
359
+
360
+ def test_collect_dependencies_with_no_source
361
+ assert_empty @mdoc.collect_dependencies(nil)
362
+ end
363
+
364
+ if false # must raise error
365
+ def test_collect_dependencies_with_nonexistent_source
366
+ assert_raises(RuntimeError) { @mdoc.collect_dependencies('nonexistent') }
367
+ end
368
+ end
369
+
370
+ def test_collect_dependencies_with_valid_source
371
+ @mdoc.stubs(:get_block_by_anyname).with('source1').returns({ reqs: ['source2'] })
372
+ @mdoc.stubs(:get_block_by_anyname).with('source2').returns({ reqs: [] })
373
+
374
+ expected = { 'source1' => ['source2'], 'source2' => [] }
375
+ assert_equal expected, @mdoc.collect_dependencies('source1')
376
+ end
377
+ end
378
+
379
+ class TestCollectUniqueNames < Minitest::Test
380
+ def setup
381
+ @mdoc = MDoc.new
382
+ end
383
+
384
+ def test_empty_hash
385
+ assert_empty @mdoc.collect_unique_names({})
386
+ end
387
+
388
+ def test_single_key
389
+ input = { group1: %w[Alice Bob Charlie] }
390
+ assert_equal %w[Alice Bob Charlie], @mdoc.collect_unique_names(input)
391
+ end
392
+
393
+ def test_multiple_keys
394
+ input = { group1: %w[Alice Bob], group2: %w[Charlie Alice] }
395
+ assert_equal %w[Alice Bob Charlie], @mdoc.collect_unique_names(input)
396
+ end
397
+
398
+ def test_no_unique_names
399
+ input = { group1: ['Alice'], group2: ['Alice'] }
400
+ assert_equal ['Alice'], @mdoc.collect_unique_names(input)
401
+ end
402
+ end
403
+
291
404
  class TestMDoc < Minitest::Test
292
405
  def setup
293
406
  @table = [
294
- { oname: 'block1', body: ['code for block1'],
295
- reqs: ['block2'] },
407
+ { oname: 'block1', body: ['code for block1'], reqs: ['block2'] },
296
408
  { oname: 'block2', body: ['code for block2'], reqs: nil },
297
- { oname: 'block3', body: ['code for block3'],
298
- reqs: ['block1'] }
409
+ { oname: 'block3', body: ['code for block3'], reqs: ['block1'] }
299
410
  ]
300
411
  @doc = MDoc.new(@table)
301
412
  end
@@ -315,15 +426,15 @@ if $PROGRAM_NAME == __FILE__
315
426
  end
316
427
 
317
428
  ### broken test
318
- # def test_collect_recursively_required_blocks
319
- # result = @doc.collect_recursively_required_blocks('block3')
320
- # expected_result = [@table[0], @table[1], @table[2]]
321
- # assert_equal expected_result, result
429
+ def test_collect_recursively_required_blocks
430
+ result = @doc.collect_recursively_required_blocks('block3')[:blocks]
431
+ expected_result = [@table[0], @table[1], @table[2]]
432
+ assert_equal expected_result, result
322
433
 
323
- # assert_raises(RuntimeError) do
324
- # @doc.collect_recursively_required_blocks('missing_block')
325
- # end
326
- # end
434
+ assert_raises(RuntimeError) do
435
+ @doc.collect_recursively_required_blocks('missing_block')
436
+ end
437
+ end
327
438
 
328
439
  def test_hide_menu_block_per_options
329
440
  opts = { hide_blocks_by_name: true,
@@ -341,11 +452,12 @@ if $PROGRAM_NAME == __FILE__
341
452
  # end
342
453
 
343
454
  def test_recursively_required
344
- result = @doc.recursively_required(['block3'])
345
- assert_equal %w[block3 block1 block2], result
455
+ result = @doc.recursively_required_hash('block3')
456
+ assert_equal ({ 'block3' => ['block1'], 'block1' => ['block2'], 'block2' => nil }),
457
+ result
346
458
 
347
- result_no_reqs = @doc.recursively_required(nil)
348
- assert_equal [], result_no_reqs
459
+ result_no_reqs = @doc.recursively_required_hash(nil)
460
+ assert_equal ({}), result_no_reqs
349
461
  end
350
462
  end
351
463