markdown_exec 2.8.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -1
  3. data/Gemfile.lock +1 -1
  4. data/Rakefile +0 -33
  5. data/bats/bats.bats +2 -0
  6. data/bats/block-type-link.bats +1 -1
  7. data/bats/block-type-ux-allowed.bats +2 -2
  8. data/bats/block-type-ux-default.bats +8 -0
  9. data/bats/block-type-ux-invalid.bats +1 -1
  10. data/bats/{block-type-ux-preconditions.bats → block-type-ux-required-variables.bats} +1 -1
  11. data/bats/block-type-ux-row-format.bats +1 -1
  12. data/bats/block-type-ux-sources.bats +36 -0
  13. data/bats/border.bats +1 -1
  14. data/bats/cli.bats +2 -2
  15. data/bats/command-substitution-options.bats +14 -0
  16. data/bats/command-substitution.bats +1 -1
  17. data/bats/fail.bats +5 -2
  18. data/bats/indented-block-type-vars.bats +1 -1
  19. data/bats/markup.bats +1 -1
  20. data/bats/option-expansion.bats +8 -0
  21. data/bats/table-column-truncate.bats +1 -1
  22. data/bats/test_helper.bash +50 -5
  23. data/docs/dev/bats-document-configuration.md +1 -1
  24. data/docs/dev/block-type-ux-allowed.md +5 -5
  25. data/docs/dev/block-type-ux-auto.md +9 -5
  26. data/docs/dev/block-type-ux-chained.md +4 -2
  27. data/docs/dev/block-type-ux-default.md +42 -0
  28. data/docs/dev/block-type-ux-echo-hash.md +6 -1
  29. data/docs/dev/block-type-ux-echo.md +3 -1
  30. data/docs/dev/block-type-ux-exec.md +3 -4
  31. data/docs/dev/block-type-ux-hidden.md +3 -0
  32. data/docs/dev/block-type-ux-require.md +9 -18
  33. data/docs/dev/{block-type-ux-preconditions.md → block-type-ux-required-variables.md} +1 -2
  34. data/docs/dev/block-type-ux-row-format.md +3 -4
  35. data/docs/dev/block-type-ux-sources.md +57 -0
  36. data/docs/dev/block-type-ux-transform.md +0 -4
  37. data/docs/dev/command-substitution-options.md +61 -0
  38. data/docs/dev/indented-block-type-vars.md +1 -0
  39. data/docs/dev/menu-pagination-indent.md +123 -0
  40. data/docs/dev/menu-pagination.md +111 -0
  41. data/docs/dev/option-expansion.md +10 -0
  42. data/lib/ansi_formatter.rb +2 -0
  43. data/lib/block_cache.rb +197 -0
  44. data/lib/command_result.rb +57 -0
  45. data/lib/constants.rb +18 -0
  46. data/lib/error_reporting.rb +38 -0
  47. data/lib/evaluate_shell_expressions.rb +43 -18
  48. data/lib/fcb.rb +114 -11
  49. data/lib/hash_delegator.rb +595 -359
  50. data/lib/markdown_exec/version.rb +1 -1
  51. data/lib/markdown_exec.rb +136 -45
  52. data/lib/mdoc.rb +74 -23
  53. data/lib/menu.src.yml +27 -9
  54. data/lib/menu.yml +23 -8
  55. data/lib/namer.rb +1 -3
  56. data/lib/value_or_exception.rb +76 -0
  57. metadata +18 -4
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env -S bundle exec ruby
2
+ # frozen_string_literal: true
3
+
4
+ # encoding=utf-8
5
+
6
+ # a ruby class contains a list of objects
7
+ class BlockCache
8
+ attr_accessor :cache
9
+
10
+ def initialize(cache = [])
11
+ @cache = cache
12
+ end
13
+
14
+ # Find an object by id
15
+ def find_by_id(id)
16
+ @cache.find { |obj| obj.id == id }
17
+ end
18
+
19
+ # Get an existing object by id or create a new one
20
+ def get_or_create(id, new_obj_proc = nil)
21
+ obj = find_by_id(id)
22
+ return obj if obj
23
+
24
+ # Create a new object if not found
25
+ if block_given?
26
+ obj = yield(id)
27
+ elsif new_obj_proc
28
+ obj = new_obj_proc.call(id)
29
+ else
30
+ raise ArgumentError, 'Block or proc required to create new object'
31
+ end
32
+
33
+ @cache << obj
34
+ obj
35
+ end
36
+
37
+ # Add a new object to the cache
38
+ def add(obj)
39
+ raise ArgumentError,
40
+ 'Object must respond to id method' unless obj.respond_to?(:id)
41
+
42
+ # Replace existing object with same id
43
+ if (existing = find_by_id(obj.id))
44
+ @cache[@cache.index(existing)] = obj
45
+ else
46
+ @cache << obj
47
+ end
48
+ obj
49
+ end
50
+
51
+ # Remove an object from the cache by id
52
+ def remove(id)
53
+ @cache.delete_if { |obj| obj.id == id }
54
+ end
55
+
56
+ # Clear the entire cache
57
+ def clear
58
+ @cache = []
59
+ end
60
+
61
+ # Get the size of the cache
62
+ def size
63
+ @cache.size
64
+ end
65
+ end
66
+
67
+ return unless $PROGRAM_NAME == __FILE__
68
+
69
+ require 'bundler/setup'
70
+ Bundler.require(:default)
71
+
72
+ require 'minitest/autorun'
73
+ require 'mocha/minitest'
74
+
75
+ # require_relative 'ww'
76
+
77
+ # A simple dummy object that responds to #id
78
+ class DummyObject
79
+ attr_reader :id
80
+
81
+ def initialize(id)
82
+ @id = id
83
+ end
84
+ end
85
+
86
+ # Unit tests for the BlockCache class
87
+ #
88
+ # This test suite verifies correct behavior of:
89
+ # - initialization (with and without initial cache)
90
+ # - finding objects by id
91
+ # - getting or creating objects via block or proc
92
+ # - adding objects (including replacing existing ones)
93
+ # - removing objects by id
94
+ # - clearing the cache
95
+ # - reporting cache size
96
+ class TestBlockCache < Minitest::Test
97
+ def setup
98
+ @obj1 = DummyObject.new(1)
99
+ @obj2 = DummyObject.new(2)
100
+ @initial_cache = [@obj1]
101
+ @cache = BlockCache.new(@initial_cache.dup)
102
+ end
103
+
104
+ def test_initialize_defaults_to_empty_array
105
+ empty_cache = BlockCache.new
106
+ assert_equal [], empty_cache.cache
107
+ end
108
+
109
+ def test_initialize_with_provided_array
110
+ assert_equal @initial_cache, @cache.cache
111
+ end
112
+
113
+ def test_find_by_id_returns_matching_object
114
+ assert_equal @obj1, @cache.find_by_id(1)
115
+ end
116
+
117
+ def test_find_by_id_returns_nil_when_not_found
118
+ assert_nil @cache.find_by_id(999)
119
+ end
120
+
121
+ def test_get_or_create_returns_existing_object
122
+ found = @cache.get_or_create(1) { |id| DummyObject.new(id) }
123
+ assert_equal @obj1, found
124
+ assert_equal 1, @cache.size
125
+ end
126
+
127
+ def test_get_or_create_creates_with_block
128
+ new_obj = @cache.get_or_create(3) { |id| DummyObject.new(id) }
129
+ assert_instance_of DummyObject, new_obj
130
+ assert_equal 3, new_obj.id
131
+ assert_includes @cache.cache, new_obj
132
+ assert_equal 2, @cache.size
133
+ end
134
+
135
+ def test_get_or_create_creates_with_proc
136
+ factory = proc { |id| DummyObject.new(id * 10) }
137
+ new_obj = @cache.get_or_create(5, factory)
138
+ assert_instance_of DummyObject, new_obj
139
+ assert_equal 50, new_obj.id
140
+ assert_equal new_obj, @cache.find_by_id(50)
141
+ assert_equal 2, @cache.size
142
+ end
143
+
144
+ def test_get_or_create_raises_without_block_or_proc
145
+ assert_raises(ArgumentError) do
146
+ @cache.get_or_create(7)
147
+ end
148
+ end
149
+
150
+ def test_add_appends_new_object
151
+ result = @cache.add(@obj2)
152
+ assert_equal @obj2, result
153
+ assert_equal 2, @cache.size
154
+ assert_equal [@obj1, @obj2], @cache.cache
155
+ end
156
+
157
+ def test_add_replaces_existing_object_with_same_id
158
+ replacement = DummyObject.new(1)
159
+ result = @cache.add(replacement)
160
+ assert_equal replacement, result
161
+ assert_equal 1, @cache.size
162
+ assert_equal replacement, @cache.find_by_id(1)
163
+ end
164
+
165
+ def test_add_raises_for_object_without_id_method
166
+ invalid = Object.new
167
+ assert_raises(ArgumentError) do
168
+ @cache.add(invalid)
169
+ end
170
+ end
171
+
172
+ def test_remove_deletes_object_by_id
173
+ @cache.add(@obj2)
174
+ @cache.remove(1)
175
+ assert_nil @cache.find_by_id(1)
176
+ assert_equal 1, @cache.size
177
+ assert_equal [@obj2], @cache.cache
178
+ end
179
+
180
+ def test_remove_nonexistent_id_leaves_cache_unchanged
181
+ original = @cache.cache.dup
182
+ @cache.remove(999)
183
+ assert_equal original, @cache.cache
184
+ end
185
+
186
+ def test_clear_empties_the_cache
187
+ @cache.clear
188
+ assert_equal [], @cache.cache
189
+ assert_equal 0, @cache.size
190
+ end
191
+
192
+ def test_size_returns_number_of_cached_objects
193
+ assert_equal 1, @cache.size
194
+ @cache.add(@obj2)
195
+ assert_equal 2, @cache.size
196
+ end
197
+ end
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env -S bundle exec ruby
2
+ # frozen_string_literal: true
3
+
4
+ # encoding=utf-8
5
+
6
+ # Encapsulates the result of executing a system command, storing its output,
7
+ # exit status, and any number of additional, arbitrary attributes.
8
+ #
9
+ # @example
10
+ # result = CommandResult.new(stdout: output, exit_status: $?.exitstatus, duration: 1.23)
11
+ # result.stdout # => output
12
+ # result.exit_status # => 0
13
+ # result.duration # => 1.23
14
+ # result.new_field = 42
15
+ # result.new_field # => 42
16
+ # result.success? # => true
17
+ class CommandResult
18
+ # @param attributes [Hash{Symbol=>Object}] initial named attributes
19
+ def initialize(**attributes)
20
+ @attributes = {}
21
+ @attributes[:exit_status] = 0
22
+ @attributes[:stdout] = ''
23
+ attributes.each { |name, value| @attributes[name] = value }
24
+ end
25
+
26
+ def failure?
27
+ !success?
28
+ end
29
+
30
+ # @return [Boolean] true if the exit status is zero
31
+ def success?
32
+ exit_status.zero?
33
+ end
34
+
35
+ def method_missing(name, *args)
36
+ key = name.to_s.chomp('=').to_sym
37
+
38
+ if name.to_s.end_with?('=') # setter
39
+ @attributes[key] = args.first
40
+ elsif attribute?(name) # getter
41
+ @attributes[name]
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ def respond_to_missing?(name, include_private = false)
48
+ key = name.to_s.chomp('=').to_sym
49
+ attribute?(key) || super
50
+ end
51
+
52
+ private
53
+
54
+ def attribute?(name)
55
+ @attributes.key?(name)
56
+ end
57
+ end
data/lib/constants.rb CHANGED
@@ -125,3 +125,21 @@ class TtyMenu
125
125
  ENABLE = nil
126
126
  DISABLE = ''
127
127
  end
128
+
129
+ class ExportValueSource
130
+ ALLOW = ':allow'
131
+ DEFAULT = ':default'
132
+ ECHO = ':echo'
133
+ EDIT = ':edit'
134
+ EXEC = ':exec'
135
+ FALSE = false
136
+ end
137
+
138
+ class UxActSource
139
+ ALLOW = :allow
140
+ DEFAULT = :default
141
+ ECHO = :echo
142
+ EDIT = :edit
143
+ EXEC = :exec
144
+ FALSE = 'false'
145
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # encoding=utf-8
4
+
5
+ ##
6
+ # Module providing standardized error‐reporting:
7
+ # logs either an Exception’s details or a simple
8
+ # string message—optionally with context—and then
9
+ # re‐raises either the original Exception or a new
10
+ # RuntimeError for string messages.
11
+ #
12
+ # Including this module gives you:
13
+ # • instance method → report_and_reraise(...)
14
+ # • class method → report_and_reraise(...)
15
+ module ErrorReporting
16
+ def self.included(base)
17
+ base.extend(self)
18
+ end
19
+
20
+ def report_and_reraise(error_or_message, context: nil)
21
+ if error_or_message.is_a?(Exception)
22
+ header = +"#{error_or_message.class}: #{error_or_message.message}"
23
+ header << " (#{context})" if context
24
+
25
+ ww header
26
+ ww error_or_message.backtrace.join("\n") if error_or_message.backtrace
27
+
28
+ raise error_or_message
29
+ else
30
+ header = +error_or_message.to_s
31
+ header << " (#{context})" if context
32
+
33
+ ww header
34
+
35
+ raise error_or_message.to_s
36
+ end
37
+ end
38
+ end
@@ -10,12 +10,11 @@ class EvaluateShellExpression
10
10
  end
11
11
 
12
12
  def evaluate_shell_expressions(initial_code, expressions, shell: '/bin/bash',
13
- key_format: '%%<%s>',
14
- initial_code_required: false)
13
+ initial_code_required: false,
14
+ occurrence_expressions: nil)
15
15
  # !!p initial_code expressions key_format shell
16
16
  return if (initial_code_required && (initial_code.nil? || initial_code.empty?)) ||
17
- expressions.nil? || expressions.empty? ||
18
- key_format.nil? || key_format.empty?
17
+ expressions.nil? || expressions.empty?
19
18
 
20
19
  # token to separate output
21
20
  token = "__TOKEN__#{Time.now.to_i}__"
@@ -28,7 +27,7 @@ def evaluate_shell_expressions(initial_code, expressions, shell: '/bin/bash',
28
27
  end
29
28
 
30
29
  # Execute
31
- stdout_str, stderr_str, status = Open3.capture3(shell, '-c', script)
30
+ stdout_str, _, status = Open3.capture3(shell, '-c', script)
32
31
 
33
32
  unless status.success?
34
33
  return EvaluateShellExpression::StatusFail
@@ -40,7 +39,7 @@ def evaluate_shell_expressions(initial_code, expressions, shell: '/bin/bash',
40
39
  unless part.empty?
41
40
  part[1..-1].tap do |output_parts|
42
41
  expressions.each_with_index do |(key, _expression), index|
43
- result_hash[format(key_format, key)] = output_parts[index].chomp
42
+ result_hash[occurrence_expressions[key]] = output_parts[index].chomp
44
43
  end
45
44
  end
46
45
  end
@@ -54,7 +53,6 @@ require 'bundler/setup'
54
53
  Bundler.require(:default)
55
54
 
56
55
  require 'minitest/autorun'
57
- require 'open3'
58
56
 
59
57
  class TestShellExpressionEvaluator < Minitest::Test
60
58
  def setup
@@ -66,7 +64,11 @@ class TestShellExpressionEvaluator < Minitest::Test
66
64
 
67
65
  def test_single_expression
68
66
  expressions = { 'greeting' => "echo 'Hello, World!'" }
69
- result = evaluate_shell_expressions(@initial_code, expressions)
67
+ occurrence_expressions = { 'greeting' => '%<greeting>' }
68
+ result = evaluate_shell_expressions(
69
+ @initial_code, expressions,
70
+ occurrence_expressions: occurrence_expressions
71
+ )
70
72
 
71
73
  assert_equal 'Hello, World!', result['%<greeting>']
72
74
  end
@@ -77,7 +79,15 @@ class TestShellExpressionEvaluator < Minitest::Test
77
79
  'date' => 'date +%Y-%m-%d',
78
80
  'kernel' => 'uname -r'
79
81
  }
80
- result = evaluate_shell_expressions(@initial_code, expressions)
82
+ occurrence_expressions = {
83
+ 'date' => '%<date>',
84
+ 'greeting' => '%<greeting>',
85
+ 'kernel' => '%<kernel>'
86
+ }
87
+ result = evaluate_shell_expressions(
88
+ @initial_code, expressions,
89
+ occurrence_expressions: occurrence_expressions
90
+ )
81
91
 
82
92
  assert_equal 'Hello, World!', result['%<greeting>']
83
93
  assert_match(/\d{4}-\d{2}-\d{2}/, result['%<date>'])
@@ -86,15 +96,22 @@ class TestShellExpressionEvaluator < Minitest::Test
86
96
 
87
97
  def test_empty_expressions_list
88
98
  expressions = {}
89
- result = evaluate_shell_expressions(@initial_code, expressions)
99
+ occurrence_expressions = {}
100
+ result = evaluate_shell_expressions(
101
+ @initial_code, expressions,
102
+ occurrence_expressions: occurrence_expressions
103
+ )
90
104
 
91
105
  assert_nil result
92
106
  end
93
107
 
94
108
  def test_invalid_expression
95
109
  expressions = { 'invalid' => 'invalid_command' }
96
-
97
- result = evaluate_shell_expressions(@initial_code, expressions)
110
+ occurrence_expressions = {}
111
+ result = evaluate_shell_expressions(
112
+ @initial_code, expressions,
113
+ occurrence_expressions: occurrence_expressions
114
+ )
98
115
 
99
116
  assert_equal EvaluateShellExpression::StatusFail, result
100
117
  end
@@ -105,18 +122,26 @@ class TestShellExpressionEvaluator < Minitest::Test
105
122
  echo "Custom setup message"
106
123
  BASH
107
124
  expressions = { 'test' => 'echo Test after initial setup' }
108
-
109
- result = evaluate_shell_expressions(initial_code, expressions)
125
+ occurrence_expressions = { 'test' => '%<test>' }
126
+ result = evaluate_shell_expressions(
127
+ @initial_code, expressions,
128
+ occurrence_expressions: occurrence_expressions
129
+ )
110
130
 
111
131
  assert_equal 'Test after initial setup', result['%<test>']
112
132
  end
113
133
 
114
134
  def test_large_number_of_expressions
115
- expressions = (1..100).map do |i|
135
+ expressions = (1..100).to_h do |i|
116
136
  ["expr_#{i}", "echo Expression #{i}"]
117
- end.to_h
118
-
119
- result = evaluate_shell_expressions(@initial_code, expressions)
137
+ end
138
+ occurrence_expressions = (1..100).to_h do |i|
139
+ ["expr_#{i}", "%<expr_#{i}>"]
140
+ end
141
+ result = evaluate_shell_expressions(
142
+ @initial_code, expressions,
143
+ occurrence_expressions: occurrence_expressions
144
+ )
120
145
 
121
146
  expressions.each_with_index do |(key, _expression), index|
122
147
  assert_equal "Expression #{index + 1}", result["%<#{key}>"]
data/lib/fcb.rb CHANGED
@@ -4,7 +4,7 @@
4
4
  # encoding=utf-8
5
5
  require 'digest'
6
6
  require_relative 'namer'
7
-
7
+ BT_UX_FLD_REQUIRED = 'required'
8
8
  def parse_yaml_of_ux_block(
9
9
  data,
10
10
  menu_format: nil,
@@ -18,15 +18,17 @@ def parse_yaml_of_ux_block(
18
18
  raise "Name is missing in UX block: #{data.inspect}" unless name.present?
19
19
 
20
20
  OpenStruct.new(
21
- allowed: export['allowed'],
21
+ act: export['act'],
22
+ allow: export['allow'] || export['allowed'],
22
23
  default: export['default'],
23
24
  echo: export['echo'],
24
25
  exec: export['exec'],
25
- menu_format: export['menu_format'] || menu_format,
26
+ init: export['init'],
27
+ menu_format: export['format'] || export['menu_format'] || menu_format,
26
28
  name: name,
27
- preconditions: export['preconditions'],
28
29
  prompt: export['prompt'] || prompt,
29
30
  readonly: export['readonly'].nil? ? false : export['readonly'],
31
+ required: export['require'] || export['required'] || export[BT_UX_FLD_REQUIRED],
30
32
  transform: export['transform'],
31
33
  validate: export['validate'] || validate
32
34
  )
@@ -48,6 +50,7 @@ module MarkdownExec
48
50
  call: nil,
49
51
  dname: nil,
50
52
  headings: [],
53
+ id: object_id,
51
54
  indent: '',
52
55
  name: nil,
53
56
  nickname: nil,
@@ -62,6 +65,15 @@ module MarkdownExec
62
65
  }.merge(options)
63
66
  end
64
67
 
68
+ def pub_name(**kwargs)
69
+ self.class.pub_name(@attrs, **kwargs)
70
+ end
71
+
72
+ def self.pub_name(attrs, **kwargs)
73
+ full = attrs.fetch(:nickname, nil) || attrs.fetch(:oname, nil)
74
+ full&.to_s&.pub_name(**kwargs)
75
+ end
76
+
65
77
  def code_name_included?(*names)
66
78
  names.include?(@attrs[:oname])
67
79
  end
@@ -127,8 +139,10 @@ module MarkdownExec
127
139
  )&.fetch(1, nil)
128
140
  titlexcall = call ? @attrs[:title].sub("%#{call}", '') : @attrs[:title]
129
141
 
130
- oname = if block_name_nick_match.present? &&
131
- @attrs[:oname] =~ Regexp.new(block_name_nick_match)
142
+ oname = if is_split?
143
+ @attrs[:text]
144
+ elsif block_name_nick_match.present? &&
145
+ @attrs[:oname] =~ Regexp.new(block_name_nick_match)
132
146
  @attrs[:nickname] = $~[0]
133
147
  derive_title_from_body
134
148
  else
@@ -175,7 +189,61 @@ module MarkdownExec
175
189
  def self.format_multiline_body_as_title(body_lines)
176
190
  body_lines.map.with_index do |line, index|
177
191
  index.zero? ? line : " #{line}"
178
- end.join("\n") << "\n"
192
+ end.join("\n")
193
+ end
194
+
195
+ def self.act_source(export)
196
+ # If `false`, the UX block is not activated.
197
+ # If one of `:allow`, `:echo`, `:edit`, or `:exec` is specified,
198
+ # the value is calculated or the user is prompted.
199
+ # If not present, the default value is `:edit`.
200
+ if export.act.nil?
201
+ export.act = if export.init.to_s == 'false'
202
+ if export.allow.present?
203
+ UxActSource::ALLOW
204
+ elsif export.echo.present?
205
+ UxActSource::ECHO
206
+ elsif export.edit.present?
207
+ UxActSource::EDIT
208
+ elsif export.exec.present?
209
+ UxActSource::EXEC
210
+ else
211
+ UxActSource::EDIT
212
+ end
213
+ elsif export.allow.present?
214
+ UxActSource::ALLOW
215
+ else
216
+ UxActSource::EDIT
217
+ end
218
+ end
219
+
220
+ export.act
221
+ end
222
+
223
+ def self.init_source(export)
224
+ # If `false`, there is no initial value set.
225
+ # If a string, it is the initial value of the object variable.
226
+ # Otherwise, if one of `:allow`, `:echo`, or `:exec` is specified,
227
+ # the value is the output of the `echo` or `exec` evaluation
228
+ # or the first allowed value.
229
+ # If not present, the default value is whichever of
230
+ # `:allow`, `:default`, `:echo`, or `:exec` is present.
231
+ if export.init.nil?
232
+ export.init = case
233
+ when export.allow.present?
234
+ UxActSource::ALLOW
235
+ when export.default.present?
236
+ UxActSource::DEFAULT
237
+ when export.echo.present?
238
+ UxActSource::ECHO
239
+ when export.exec.present?
240
+ UxActSource::EXEC
241
+ else
242
+ UxActSource::FALSE
243
+ end
244
+ end
245
+
246
+ export.init
179
247
  end
180
248
 
181
249
  # :reek:ManualDispatch
@@ -188,6 +256,14 @@ module MarkdownExec
188
256
  dependency_names.include?(@attrs[:s2title])
189
257
  end
190
258
 
259
+ def is_disabled?
260
+ @attrs[:disabled] == TtyMenu::DISABLE
261
+ end
262
+
263
+ def is_enabled?
264
+ !is_disabled?
265
+ end
266
+
191
267
  def is_named?(name)
192
268
  @attrs[:dname] == name ||
193
269
  @attrs[:nickname] == name ||
@@ -196,6 +272,31 @@ module MarkdownExec
196
272
  @attrs[:s2title] == name
197
273
  end
198
274
 
275
+ # true if this is a line split block
276
+ def is_split?
277
+ is_split_first? || is_split_rest?
278
+ end
279
+
280
+ # true if this block displays its split body
281
+ # names and nicknames are displayed instead of the body
282
+ # ux blocks display a single line for the named variable
283
+ # split blocks are: opts, shell, vars
284
+ def is_split_displayed?(opts)
285
+ @attrs[:type] != BlockType::UX &&
286
+ !(@attrs[:start_line] =~ Regexp.new(opts[:block_name_nick_match]) ||
287
+ @attrs[:start_line] =~ Regexp.new(opts[:block_name_match]))
288
+ end
289
+
290
+ # true if this is the first line in a split block
291
+ def is_split_first?
292
+ @attrs.fetch(:is_split_first, false)
293
+ end
294
+
295
+ # true if this is the second or later line in a split block
296
+ def is_split_rest?
297
+ @attrs.fetch(:is_split_rest, false)
298
+ end
299
+
199
300
  # :reek:ManualDispatch
200
301
  def method_missing(method, *args, &block)
201
302
  method_name = method.to_s
@@ -331,13 +432,15 @@ if $PROGRAM_NAME == __FILE__
331
432
  end
332
433
 
333
434
  def test_to_h_method
334
- assert_equal_hash @fcb_data.merge({ random: @fcb.random }), @fcb.to_h
435
+ assert_equal_hash @fcb_data.merge(
436
+ { id: @fcb.id, random: @fcb.random }
437
+ ), @fcb.to_h
335
438
  end
336
439
 
337
440
  def test_to_yaml_method
338
- assert_equal_hash YAML.load(@fcb_data.merge({ random: @fcb.random })
339
- .to_yaml),
340
- YAML.load(@fcb.to_yaml)
441
+ assert_equal_hash YAML.load(@fcb_data.merge(
442
+ { id: @fcb.id, random: @fcb.random }
443
+ ).to_yaml), YAML.load(@fcb.to_yaml)
341
444
  end
342
445
 
343
446
  def test_method_missing_getter