rundoc 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab49039bd205d3d147c1de91935b0deb1ffa095311eead39c750b426db1d7c87
4
- data.tar.gz: f6a45b2b7a7b858ce09c8464ab891f9b3e7afe272de9d00768e5760a36f4533d
3
+ metadata.gz: 8ca738c51da79babb0fba6093b9d2055e6efd6d435d56b3397aed926d002701d
4
+ data.tar.gz: be7e9bd2c06a3a0e92dd078a3cd66d67f77b7cf1f4490190281fb7e573f8696e
5
5
  SHA512:
6
- metadata.gz: 3d946cf5081696a80ef34867e6559cb51f2191682e74ec21d44d524c41c11bdc05f73fe0f832afe9a89e5c85e6d6f588dfb85f857dbe141f2da278fc60df2527
7
- data.tar.gz: 2c0fd644519f2cde9ca67ef6d56c9f1fa17727883149ebb0a437a9e163b70c41b478f1be39f50560fd7c7bf59ba77dab305d4654c2737fdfdc350098f094a549
6
+ metadata.gz: 7db578e4f26a9b347ca93d8ae5b4013ae9605feaff56ba937d1b22c1a78316764af8f96274d9f449c5640282df277a3ac06e0530e8f760e8153c029aa2f05a27
7
+ data.tar.gz: 4b71fb1cfd4851d58759ca443e9d17a365d485621d7c1763b570171557a89c79a52e67080f53c25f1d7c4057a2a4decd43e46df7aa34909dc627b2717668a77b
data/.gitignore CHANGED
@@ -11,6 +11,7 @@ coverage/
11
11
  .DS_Store
12
12
  Gemfile.lock
13
13
  .env
14
+ .ruby-version
14
15
 
15
16
  # IDEs
16
17
  .idea/
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## HEAD
2
2
 
3
+ ## 4.1.0
4
+
5
+ - Add: Rundoc command `pre.erb` command used for dynamically templating any command using ERB syntax. (https://github.com/zombocom/rundoc/pull/90)
6
+
3
7
  ## 4.0.0
4
8
 
5
9
  - Add: Rundoc command `background.stdin_write` to send a string to a backtround process' STDIN. This allows driving REPL interfaces (https://github.com/zombocom/rundoc/pull/79)
data/README.md CHANGED
@@ -73,6 +73,8 @@ This will generate a project folder with your project in it, and a markdown `REA
73
73
  - Execute Bash Commands
74
74
  - [$](#shell-commands)
75
75
  - [fail.$](#shell-commands)
76
+ - Dynamic command templating
77
+ - [pre.erb](#pre.erb)
76
78
  - Printing
77
79
  - [print.text](#print)
78
80
  - [print.erb](#print)
@@ -257,6 +259,66 @@ These custom commands are kept to a minimum, and for the most part behave as you
257
259
 
258
260
  Running shell commands like this can be very powerful, you'll likely want more control of how you manipulate files in your project. To do this you can use the `file.` namespace:
259
261
 
262
+ ## Dynamic command templating
263
+
264
+ Meta commands that produce no output but instead allow for generating commands via dynamic templates.
265
+
266
+ Current Commands:
267
+
268
+ - `pre.erb`
269
+
270
+ ### pre.erb
271
+
272
+ Placing `pre.erb` in-front of another command will allow dynmaic templating via Ruby's [ERB](https://rubyapi.org/3.3/o/erb) syntax.
273
+
274
+ For example:
275
+
276
+ ```
277
+ :::>> pre.erb $ echo "The answer to everything is <%= 6*7 %>"
278
+ ```
279
+
280
+ When this runs, it will first replace the template with the result of the ERB. It would be the same as this:
281
+
282
+ ```
283
+ :::>> $ echo "The answer to everything is 42"
284
+ ```
285
+
286
+ The binding (variable and method scope) for `pre.erb` is shared across all executions and the default `print.erb` command. That means you can use it to persist data or logic and re-use it:
287
+
288
+ ```ruby
289
+ :::-- print.erb <%
290
+ # Won't be rendered because it's using `--` visibility
291
+ def lol
292
+ "haha"
293
+ end
294
+
295
+ user = "Schneems"
296
+ %>
297
+ ```
298
+
299
+ ```
300
+ :::>> pre.erb $ echo <%= user %> said <%= lol() %> | tr '[:lower:]' '[:upper:]'
301
+ ```
302
+
303
+ When run, this would produce:
304
+
305
+ ```
306
+ $ echo Schneems said haha | tr '[:lower:]' '[:upper:]'
307
+ SCHNEEMS SAID HAHA
308
+ ```
309
+
310
+ Multi-line commands are also supported
311
+
312
+ ```
313
+ :::>> pre.erb file.write "lol.txt"
314
+ Super secret key:
315
+ <%= "#{key}" %>
316
+ ```
317
+
318
+ The only thing to watch out for is if the resulting template contains a `:::>>` (or similar) rundoc marker at the beginning of the line; Rundoc will think it is a new command rather than a part of `pre.erb` template.
319
+
320
+ The visibility of the `pre.erb` is forwarded to whatever command is run.
321
+
260
322
  ## Print
261
323
 
262
324
  Current commands:
data/lib/rundoc/cli.rb CHANGED
@@ -157,7 +157,7 @@ module Rundoc
157
157
 
158
158
  io.puts "## Working dir is #{execution_context.output_dir}"
159
159
  Dir.chdir(execution_context.output_dir) do
160
- parser = Rundoc::Parser.new(
160
+ parser = Rundoc::Document.new(
161
161
  source_contents,
162
162
  context: execution_context
163
163
  )
@@ -203,7 +203,7 @@ module Rundoc
203
203
  to: on_failure_dir
204
204
  )
205
205
 
206
- on_failure_dir.join("RUNDOC_FAILED.md").write(Rundoc::Parser.partial_result_to_doc)
206
+ on_failure_dir.join("RUNDOC_FAILED.md").write(Rundoc::Document.partial_result_to_doc)
207
207
  end
208
208
 
209
209
  private def on_success(output)
@@ -12,7 +12,9 @@ class Rundoc::CodeCommand::FileCommand
12
12
  def to_md(env)
13
13
  return unless render_command?
14
14
 
15
- raise "must call write in its own code section" unless env[:commands].empty?
15
+ if env[:commands].any? { |c| c[:object].not_hidden? }
16
+ raise "Must call append in its own code section"
17
+ end
16
18
 
17
19
  env[:before] << if @line_number
18
20
  "In file `#{filename}`, on line #{@line_number} add:"
@@ -7,7 +7,10 @@ class Rundoc::CodeCommand::FileCommand
7
7
  end
8
8
 
9
9
  def to_md(env)
10
- raise "must call write in its own code section" unless env[:commands].empty?
10
+ if env[:commands].any? { |c| c[:object].not_hidden? }
11
+ raise "Must call remove in its own code section"
12
+ end
13
+
11
14
  env[:before] << "In file `#{filename}` remove:"
12
15
  env[:before] << NEWLINE
13
16
  nil
@@ -0,0 +1,63 @@
1
+ require_relative "../print/erb"
2
+
3
+ class Rundoc::CodeCommand
4
+ class PreErb < Rundoc::CodeCommand
5
+ def initialize(line)
6
+ @line = line
7
+ @binding = RUNDOC_ERB_BINDINGS[RUNDOC_DEFAULT_ERB_BINDING]
8
+ @code = nil
9
+ @command = nil
10
+ @template = nil
11
+ @render_delegate_result = nil
12
+ @render_delegate_command = nil
13
+ # Hide self, pass visibility onto delegate
14
+ @render_result = false
15
+ @render_command = false
16
+ end
17
+
18
+ # Visibility is injected by the parser, capture it and pass it to the delegate
19
+ def render_result=(value)
20
+ @render_delegate_result = value
21
+ end
22
+
23
+ def render_command=(value)
24
+ @render_delegate_command = value
25
+ end
26
+
27
+ def code
28
+ @code ||= begin
29
+ vis = +""
30
+ vis += @render_delegate_result ? ">" : "-"
31
+ vis += @render_delegate_command ? ">" : "-"
32
+ code = [@line, @contents]
33
+ .compact
34
+ .reject(&:empty?)
35
+ .join("\n")
36
+ @template = ":::#{vis} #{code}"
37
+
38
+ puts "pre.erb: Applying ERB, template:\n#{@template}"
39
+ result = ERB.new(@template).result(@binding)
40
+ puts "pre.erb: ERB result:\n#{result}"
41
+ puts "pre.erb: done, ready to delegate"
42
+ result
43
+ end
44
+ end
45
+
46
+ def command
47
+ @command ||= Rundoc::FencedCodeBlock.parse_code_commands(code).first
48
+ end
49
+
50
+ def to_md(env = {})
51
+ ""
52
+ end
53
+
54
+ def call(env = {})
55
+ # Defer running ERB until as late as possible
56
+ delegate = command
57
+ # Delegate will be executed by the caller working through the stack
58
+ env[:stack].push(delegate)
59
+ ""
60
+ end
61
+ end
62
+ end
63
+ Rundoc.register_code_command(:"pre.erb", Rundoc::CodeCommand::PreErb)
@@ -10,11 +10,12 @@ class EmptyBinding
10
10
  end
11
11
  end
12
12
 
13
- RUNDOC_ERB_BINDINGS = Hash.new { |h, k| h[k] = EmptyBinding.create }
14
-
15
13
  class Rundoc::CodeCommand
14
+ RUNDOC_ERB_BINDINGS = Hash.new { |h, k| h[k] = EmptyBinding.create }
15
+ RUNDOC_DEFAULT_ERB_BINDING = "default"
16
+
16
17
  class PrintERB < Rundoc::CodeCommand
17
- def initialize(line = nil, binding: "default")
18
+ def initialize(line = nil, binding: RUNDOC_DEFAULT_ERB_BINDING)
18
19
  @line = line
19
20
  @binding = RUNDOC_ERB_BINDINGS[binding]
20
21
  end
@@ -31,7 +32,7 @@ class Rundoc::CodeCommand
31
32
  @render ||= ERB.new([@line, contents].compact.join("\n")).result(@binding)
32
33
  end
33
34
 
34
- def call(erb = {})
35
+ def call(env = {})
35
36
  if render_before?
36
37
  ""
37
38
  else
@@ -44,5 +45,4 @@ class Rundoc::CodeCommand
44
45
  end
45
46
  end
46
47
  end
47
-
48
48
  Rundoc.register_code_command(:"print.erb", Rundoc::CodeCommand::PrintERB)
@@ -17,7 +17,7 @@ class ::Rundoc::CodeCommand
17
17
  execution_context = env[:context]
18
18
  document_path = @path.expand_path(execution_context.source_dir)
19
19
 
20
- output = Rundoc::Parser.new(
20
+ output = Rundoc::Document.new(
21
21
  document_path.read,
22
22
  context: Rundoc::Context::Execution.new(
23
23
  source_path: document_path,
@@ -24,7 +24,9 @@ module Rundoc
24
24
 
25
25
  def to_md(env)
26
26
  if render_command?
27
- raise "must call write in its own code section" unless env[:commands].empty?
27
+ if env[:commands].any? { |c| c[:object].not_hidden? }
28
+ raise "must call write in its own code section"
29
+ end
28
30
  env[:before] << "In file `#{filename}` write:"
29
31
  env[:before] << NEWLINE
30
32
  end
@@ -60,3 +60,4 @@ require "rundoc/code_command/background"
60
60
  require "rundoc/code_command/website"
61
61
  require "rundoc/code_command/print/text"
62
62
  require "rundoc/code_command/print/erb"
63
+ require "rundoc/code_command/pre/erb"
@@ -1,25 +1,20 @@
1
1
  module Rundoc
2
- # This poorly named class is responsible for taking in the raw markdown and running it
2
+ # Represents a single rundoc file on disk,
3
3
  #
4
- # It works by pulling out the code blocks (CodeSection), and putting them onto a stack.
5
- # It then executes each in turn and records the results.
6
- class Parser
7
- DEFAULT_KEYWORD = ":::"
8
- INDENT_BLOCK = '(?<before_indent>(^\s*$\n|\A)(^(?:[ ]{4}|\t))(?<indent_contents>.*)(?<after_indent>[^\s].*$\n?(?:(?:^\s*$\n?)*^(?:[ ]{4}|\t).*[^\s].*$\n?)*))'
4
+ # Each document contains one or more fenced code blocks.
5
+ # Those are parsed as `FencedCodeBlock` instances and then
6
+ # executed.
7
+ class Document
9
8
  GITHUB_BLOCK = '^(?<fence>(?<fence_char>~|`){3,})\s*?(?<lang>\w+)?\s*?\n(?<contents>.*?)^\g<fence>\g<fence_char>*\s*?\n?'
10
9
  CODEBLOCK_REGEX = /(#{GITHUB_BLOCK})/m
11
- COMMAND_REGEX = ->(keyword) {
12
- /^#{keyword}(?<tag>(\s|=|-|>)?(=|-|>)?)\s*(?<command>(\S)+)\s+(?<statement>.*)$/
13
- }
14
10
  PARTIAL_RESULT = []
15
11
 
16
- attr_reader :contents, :keyword, :stack, :context
12
+ attr_reader :contents, :stack, :context
17
13
 
18
- def initialize(contents, context:, keyword: DEFAULT_KEYWORD)
14
+ def initialize(contents, context:)
19
15
  @context = context
20
16
  @contents = contents
21
17
  @original = contents.dup
22
- @keyword = keyword
23
18
  @stack = []
24
19
  partition
25
20
  PARTIAL_RESULT.clear
@@ -44,7 +39,7 @@ module Rundoc
44
39
 
45
40
  def self.partial_result_to_doc
46
41
  out = to_doc(result: PARTIAL_RESULT)
47
- unfinished = CodeSection.partial_result_to_doc
42
+ unfinished = FencedCodeBlock.partial_result_to_doc
48
43
  out << unfinished if unfinished
49
44
  out
50
45
  end
@@ -60,9 +55,10 @@ module Rundoc
60
55
  @stack << head unless head.empty?
61
56
  unless code.empty?
62
57
  match = code.match(CODEBLOCK_REGEX)
63
- @stack << CodeSection.new(
64
- match,
65
- keyword: keyword,
58
+ @stack << FencedCodeBlock.new(
59
+ fence: match[:fence],
60
+ lang: match[:lang],
61
+ code: match[:contents],
66
62
  context: context
67
63
  )
68
64
  end
@@ -71,5 +67,3 @@ module Rundoc
71
67
  end
72
68
  end
73
69
  end
74
-
75
- # convert string of markdown to array of strings and code_command
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rundoc
4
+ # A code secttion respesents a block of fenced code
5
+ #
6
+ # A document can have multiple code sections
7
+ class FencedCodeBlock
8
+ AUTOGEN_WARNING = "\n<!-- STOP. This document is autogenerated. Do not manually modify. See the top of the doc for more details. -->"
9
+ attr_accessor :fence, :lang, :code
10
+
11
+ PARTIAL_RESULT = []
12
+ PARTIAL_ENV = {}
13
+
14
+ # Used for tests to inspect the command that was executed
15
+ def executed_commands
16
+ raise "Nothing executed" unless @env[:commands].any?
17
+
18
+ @env[:commands].map { |c| c[:object] }
19
+ end
20
+
21
+ # @param fence [String] the fence used to start the code block like "```".
22
+ # @param lang [String] any extra string after the fence like for example
23
+ # a fence of "```ruby" the lang would be "ruby".
24
+ # @param code [String] the code block contents inside the fence.
25
+ # @param context [Context::Execution] The details about where
26
+ # the code block came from.
27
+ def initialize(fence:, lang:, code:, context:)
28
+ @fence = fence
29
+ @lang = lang
30
+ @code = code
31
+ @executed = false
32
+ @env = {}
33
+ @stack = []
34
+ @context = context
35
+ @rendered = ""
36
+ self.class.parse_code_commands(@code).each do |code_command|
37
+ @stack.unshift(code_command)
38
+ end
39
+
40
+ PARTIAL_RESULT.clear
41
+ PARTIAL_ENV.clear
42
+ end
43
+
44
+ def call
45
+ return self if @executed
46
+ @executed = true
47
+
48
+ result = []
49
+ env = @env
50
+ env[:commands] = []
51
+ env[:fence_start] = "#{fence}#{lang}"
52
+ env[:fence_end] = "#{fence}#{AUTOGEN_WARNING}"
53
+ env[:before] = []
54
+ env[:after] = []
55
+ env[:context] = @context
56
+ env[:stack] = @stack
57
+
58
+ while (code_command = @stack.pop)
59
+ code_output = code_command.call(env) || ""
60
+ code_line = code_command.to_md(env) || ""
61
+ result << code_line if code_command.render_command?
62
+ result << code_output if code_command.render_result?
63
+
64
+ PARTIAL_RESULT.replace(result)
65
+ PARTIAL_ENV.replace(env)
66
+
67
+ env[:commands] << {
68
+ object: code_command,
69
+ output: code_output,
70
+ command: code_line
71
+ }
72
+ end
73
+
74
+ if env[:commands].any? { |c| c[:object].not_hidden? }
75
+ @rendered = self.class.to_doc(result: result, env: env)
76
+ end
77
+ self
78
+ end
79
+
80
+ def render
81
+ call
82
+ @rendered
83
+ end
84
+
85
+ def self.partial_result_to_doc
86
+ to_doc(result: PARTIAL_RESULT, env: PARTIAL_ENV)
87
+ end
88
+
89
+ def self.to_doc(result:, env:)
90
+ array = [env[:before]]
91
+
92
+ result.flatten!
93
+ result.compact!
94
+ result.map! { |s| s.respond_to?(:rstrip) ? s.rstrip : s }
95
+ result.reject!(&:empty?)
96
+ result.map!(&:to_s)
97
+
98
+ if !result.empty?
99
+ array << env[:fence_start]
100
+ array << result
101
+ array << env[:fence_end]
102
+ end
103
+ array << env[:after]
104
+
105
+ array.flatten!
106
+ array.compact!
107
+ array.map! { |s| s.respond_to?(:rstrip) ? s.rstrip : s }
108
+ array.reject!(&:empty?)
109
+ array.map!(&:to_s)
110
+
111
+ array.join("\n") << "\n"
112
+ end
113
+
114
+ def self.parse_code_commands(code)
115
+ parser = Rundoc::PegParser.new.code_block
116
+ tree = parser.parse(code)
117
+ commands = Rundoc::PegTransformer.new.apply(tree)
118
+ commands = [commands] unless commands.is_a?(Array)
119
+ commands
120
+ rescue ::Parslet::ParseFailed => e
121
+ raise "Could not compile code:\n#{code}\nReason: #{e.message}"
122
+ end
123
+ end
124
+ end
@@ -1,3 +1,3 @@
1
1
  module Rundoc
2
- VERSION = "4.0.0"
2
+ VERSION = "4.1.0"
3
3
  end
data/lib/rundoc.rb CHANGED
@@ -87,8 +87,8 @@ module Rundoc
87
87
  end
88
88
  end
89
89
 
90
- require "rundoc/parser"
91
- require "rundoc/code_section"
90
+ require "rundoc/document"
91
+ require "rundoc/fenced_code_block"
92
92
  require "rundoc/code_command"
93
93
  require "rundoc/peg_parser"
94
94
  require "rundoc/cli_argument_parser"
@@ -0,0 +1,56 @@
1
+ require "test_helper"
2
+
3
+ class IntegrationPreErb < Minitest::Test
4
+ def test_file_write
5
+ key = SecureRandom.hex
6
+ contents = <<~RUBY
7
+ ```
8
+ :::>> pre.erb file.write "lol.txt"
9
+ Multi line
10
+ <%= "#{key}" %>
11
+ ```
12
+ RUBY
13
+
14
+ Dir.mktmpdir do |dir|
15
+ Dir.chdir(dir) do
16
+ expected = <<~EOF
17
+ In file `lol.txt` write:
18
+
19
+ ```
20
+ Multi line
21
+ #{key}
22
+ ```
23
+ EOF
24
+
25
+ parsed = parse_contents(contents)
26
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "").strip
27
+ assert_equal expected.strip, actual.strip
28
+ end
29
+ end
30
+ end
31
+
32
+ def test_erb_shared_binding_persists_values
33
+ key = SecureRandom.hex
34
+ contents = <<~RUBY
35
+ ```
36
+ :::-- print.erb <% secret = "#{key}" %>
37
+ :::>> pre.erb $ echo <%= secret %>
38
+ ```
39
+ RUBY
40
+
41
+ Dir.mktmpdir do |dir|
42
+ Dir.chdir(dir) do
43
+ expected = <<~EOF
44
+ ```
45
+ $ echo #{key}
46
+ #{key}
47
+ ```
48
+ EOF
49
+
50
+ parsed = parse_contents(contents)
51
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "").strip
52
+ assert_equal expected.strip, actual.strip
53
+ end
54
+ end
55
+ end
56
+ end
@@ -21,7 +21,7 @@ class IntegrationPrintTest < Minitest::Test
21
21
  #{key}
22
22
  EOF
23
23
  parsed = parse_contents(contents)
24
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
24
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
25
25
  assert_equal expected, actual
26
26
  end
27
27
  end
@@ -44,7 +44,7 @@ class IntegrationPrintTest < Minitest::Test
44
44
  one
45
45
  EOF
46
46
  parsed = parse_contents(contents)
47
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
47
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
48
48
  assert_equal expected, actual
49
49
  end
50
50
  end
@@ -70,7 +70,7 @@ class IntegrationPrintTest < Minitest::Test
70
70
  ```
71
71
  EOF
72
72
  parsed = parse_contents(contents)
73
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
73
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
74
74
  assert_equal expected, actual
75
75
  end
76
76
  end
@@ -94,7 +94,7 @@ class IntegrationPrintTest < Minitest::Test
94
94
  there
95
95
  EOF
96
96
  parsed = parse_contents(contents)
97
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
97
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
98
98
  assert_equal expected, actual
99
99
  end
100
100
  end
@@ -118,7 +118,7 @@ class IntegrationPrintTest < Minitest::Test
118
118
  there
119
119
  EOF
120
120
  parsed = parse_contents(contents)
121
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
121
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
122
122
  assert_equal expected, actual
123
123
  end
124
124
  end
@@ -142,7 +142,7 @@ class IntegrationPrintTest < Minitest::Test
142
142
  there
143
143
  EOF
144
144
  parsed = parse_contents(contents)
145
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
145
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
146
146
  assert_equal expected, actual
147
147
  end
148
148
  end
@@ -163,7 +163,7 @@ class IntegrationPrintTest < Minitest::Test
163
163
  Hello there
164
164
  EOF
165
165
  parsed = parse_contents(contents)
166
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
166
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
167
167
  assert_equal expected, actual
168
168
  end
169
169
  end
@@ -186,7 +186,7 @@ class IntegrationPrintTest < Minitest::Test
186
186
  ```
187
187
  EOF
188
188
  parsed = parse_contents(contents)
189
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
189
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
190
190
  assert_equal expected, actual
191
191
  end
192
192
  end
@@ -22,7 +22,7 @@ class IntegrationRequireTest < Minitest::Test
22
22
  source_path.read,
23
23
  source_path: source_path
24
24
  )
25
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
25
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
26
26
  assert_equal "Hello World!", actual.strip
27
27
  end
28
28
  end
@@ -50,7 +50,7 @@ class IntegrationRequireTest < Minitest::Test
50
50
  source_path.read,
51
51
  source_path: source_path
52
52
  )
53
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
53
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
54
54
  # Command was run
55
55
  assert dir.join("foo.txt").exist?
56
56
  assert "echo hello world", dir.join("foo.txt").read.strip
@@ -89,7 +89,7 @@ class IntegrationRequireTest < Minitest::Test
89
89
  source_path.read,
90
90
  source_path: source_path
91
91
  )
92
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
92
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
93
93
  assert_equal "Hello World!", actual.strip
94
94
  end
95
95
  end
@@ -23,7 +23,7 @@ class IntegrationWebsiteTest < Minitest::Test
23
23
  output_dir: screenshots_dir.parent,
24
24
  screenshots_dirname: screenshots_dirname
25
25
  )
26
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
26
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
27
27
 
28
28
  expected = "![Screenshot of https://example.com/](screenshots/screenshot_1.png)"
29
29
  assert_equal expected, actual.strip
@@ -14,10 +14,11 @@ class CodeSectionTest < Minitest::Test
14
14
 
15
15
  Dir.mktmpdir do |dir|
16
16
  Dir.chdir(dir) do
17
- match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
18
- result = Rundoc::CodeSection.new(
19
- match,
20
- keyword: ":::",
17
+ match = contents.match(Rundoc::Document::CODEBLOCK_REGEX)
18
+ result = Rundoc::FencedCodeBlock.new(
19
+ fence: match[:fence],
20
+ lang: match[:lang],
21
+ code: match[:contents],
21
22
  context: default_context
22
23
  ).render
23
24
  assert_equal "", result
@@ -33,13 +34,14 @@ class CodeSectionTest < Minitest::Test
33
34
 
34
35
  RUBY
35
36
 
36
- match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
37
- result = Rundoc::CodeSection.new(
38
- match,
39
- keyword: ":::",
37
+ match = contents.match(Rundoc::Document::CODEBLOCK_REGEX)
38
+ result = Rundoc::FencedCodeBlock.new(
39
+ fence: match[:fence],
40
+ lang: match[:lang],
41
+ code: match[:contents],
40
42
  context: default_context
41
43
  ).render
42
- assert_equal contents, result.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "\n")
44
+ assert_equal contents, result.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "\n")
43
45
  end
44
46
 
45
47
  def test_show_command_hide_render
@@ -49,15 +51,16 @@ class CodeSectionTest < Minitest::Test
49
51
  ```
50
52
  RUBY
51
53
 
52
- match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
53
- code_section = Rundoc::CodeSection.new(
54
- match,
55
- keyword: ":::",
54
+ match = contents.match(Rundoc::Document::CODEBLOCK_REGEX)
55
+ code_section = Rundoc::FencedCodeBlock.new(
56
+ fence: match[:fence],
57
+ lang: match[:lang],
58
+ code: match[:contents],
56
59
  context: default_context
57
60
  )
58
61
  code_section.render
59
62
 
60
- code_command = code_section.commands.first
63
+ code_command = code_section.executed_commands.first
61
64
  assert_equal true, code_command.render_command
62
65
  assert_equal false, code_command.render_result
63
66
 
@@ -67,15 +70,16 @@ class CodeSectionTest < Minitest::Test
67
70
  ```
68
71
  RUBY
69
72
 
70
- match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
71
- code_section = Rundoc::CodeSection.new(
72
- match,
73
- keyword: ":::",
73
+ match = contents.match(Rundoc::Document::CODEBLOCK_REGEX)
74
+ code_section = Rundoc::FencedCodeBlock.new(
75
+ fence: match[:fence],
76
+ lang: match[:lang],
77
+ code: match[:contents],
74
78
  context: default_context
75
79
  )
76
80
  code_section.render
77
81
 
78
- code_command = code_section.commands.first
82
+ code_command = code_section.executed_commands.first
79
83
  assert_equal true, code_command.render_command
80
84
  assert_equal false, code_command.render_result
81
85
  end
@@ -87,16 +91,17 @@ class CodeSectionTest < Minitest::Test
87
91
  ```
88
92
  RUBY
89
93
 
90
- match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
91
- code_section = Rundoc::CodeSection.new(
92
- match,
93
- keyword: ":::",
94
+ match = contents.match(Rundoc::Document::CODEBLOCK_REGEX)
95
+ code_section = Rundoc::FencedCodeBlock.new(
96
+ fence: match[:fence],
97
+ lang: match[:lang],
98
+ code: match[:contents],
94
99
  context: default_context
95
100
  )
96
101
  code_section.render
97
102
 
98
- puts code_section.commands.inspect
99
- code_command = code_section.commands.first
103
+ puts code_section.executed_commands.inspect
104
+ code_command = code_section.executed_commands.first
100
105
  assert_equal true, code_command.render_command
101
106
  assert_equal true, code_command.render_result
102
107
  end
@@ -108,15 +113,16 @@ class CodeSectionTest < Minitest::Test
108
113
  ```
109
114
  RUBY
110
115
 
111
- match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
112
- code_section = Rundoc::CodeSection.new(
113
- match,
114
- keyword: ":::",
116
+ match = contents.match(Rundoc::Document::CODEBLOCK_REGEX)
117
+ code_section = Rundoc::FencedCodeBlock.new(
118
+ fence: match[:fence],
119
+ lang: match[:lang],
120
+ code: match[:contents],
115
121
  context: default_context
116
122
  )
117
123
  code_section.render
118
124
 
119
- code_command = code_section.commands.first
125
+ code_command = code_section.executed_commands.first
120
126
  assert_equal false, code_command.render_command
121
127
  assert_equal false, code_command.render_result
122
128
 
@@ -126,15 +132,16 @@ class CodeSectionTest < Minitest::Test
126
132
  ```
127
133
  RUBY
128
134
 
129
- match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
130
- code_section = Rundoc::CodeSection.new(
131
- match,
132
- keyword: ":::",
135
+ match = contents.match(Rundoc::Document::CODEBLOCK_REGEX)
136
+ code_section = Rundoc::FencedCodeBlock.new(
137
+ fence: match[:fence],
138
+ lang: match[:lang],
139
+ code: match[:contents],
133
140
  context: default_context
134
141
  )
135
142
  code_section.render
136
143
 
137
- code_command = code_section.commands.first
144
+ code_command = code_section.executed_commands.first
138
145
  assert_equal false, code_command.render_command
139
146
  assert_equal false, code_command.render_result
140
147
  end
@@ -146,15 +153,16 @@ class CodeSectionTest < Minitest::Test
146
153
  ```
147
154
  RUBY
148
155
 
149
- match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
150
- code_section = Rundoc::CodeSection.new(
151
- match,
152
- keyword: ":::",
156
+ match = contents.match(Rundoc::Document::CODEBLOCK_REGEX)
157
+ code_section = Rundoc::FencedCodeBlock.new(
158
+ fence: match[:fence],
159
+ lang: match[:lang],
160
+ code: match[:contents],
153
161
  context: default_context
154
162
  )
155
163
  code_section.render
156
164
 
157
- code_command = code_section.commands.first
165
+ code_command = code_section.executed_commands.first
158
166
  assert_equal false, code_command.render_command
159
167
  assert_equal true, code_command.render_result
160
168
  end
@@ -17,7 +17,7 @@ class ParserTest < Minitest::Test
17
17
  Dir.chdir(dir) do
18
18
  expected = "sup\n\n```\n$ mkdir foo\n$ ls\nfoo\n```\n\nyo\n"
19
19
  parsed = parse_contents(contents)
20
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
20
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
21
21
  assert_equal expected, actual
22
22
  end
23
23
  end
@@ -39,7 +39,7 @@ class ParserTest < Minitest::Test
39
39
  Dir.chdir(dir) do
40
40
  expected = "sup\n\nIn file `foo/code.rb` write:\n\n```\na = 1 + 1\nb = a * 2\n```\nyo\n"
41
41
  parsed = parse_contents(contents)
42
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
42
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
43
43
  assert_equal expected, actual
44
44
  end
45
45
  end
@@ -57,7 +57,7 @@ class ParserTest < Minitest::Test
57
57
  Dir.chdir(dir) do
58
58
  expected = "\nIn file `foo/newb.rb` write:\n\n```\nputs 'hello world'\n$ cat foo/newb.rb\nputs 'hello world'\n```\n"
59
59
  parsed = parse_contents(contents)
60
- actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
60
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
61
61
  assert_equal expected, actual
62
62
  end
63
63
  end
@@ -240,22 +240,6 @@ class PegParserTest < Minitest::Test
240
240
  end
241
241
 
242
242
  def test_no_args
243
- # input = String.new
244
- # input << %Q{rundoc}
245
- # parser = Rundoc::PegParser.new.no_args_method
246
- # tree = parser.parse_with_debug(input)
247
- # actual = @transformer.apply(tree)
248
- # assert_equal("rundoc", actual.to_s)
249
-
250
- # input = String.new
251
- # input << %Q{:::-- rundoc\n}
252
- # parser = Rundoc::PegParser.new.command
253
- # tree = parser.parse_with_debug(input)
254
-
255
- # actual = @transformer.apply(tree)
256
- # assert_equal :rundoc, actual.keyword
257
- # assert_nil(actual.original_args)
258
-
259
243
  input = +""
260
244
  input << %(:::-- rundoc\n)
261
245
  input << %(email = ENV['HEROKU_EMAIL'] || `heroku auth:whoami`\n)
@@ -333,19 +317,7 @@ class PegParserTest < Minitest::Test
333
317
  actual = @transformer.apply(tree)
334
318
  assert_equal ["rails server", {name: "server"}], actual.original_args
335
319
 
336
- # input = +""
337
- # input << %Q{background.start("rails server", name: "server")}
338
- # parser = Rundoc::PegParser.new.method_call
339
-
340
- # tree = parser.parse_with_debug(input)
341
-
342
- # puts tree.inspect
343
-
344
- # actual = @transformer.apply(tree)
345
- # assert_equal :"background.start", actual.keyword
346
-
347
320
  # ================
348
-
349
321
  input = +""
350
322
  input << %{call("foo", "bar")}
351
323
 
@@ -358,7 +330,6 @@ class PegParserTest < Minitest::Test
358
330
  assert_equal ["foo", "bar"], actual.original_args
359
331
 
360
332
  # ======================
361
-
362
333
  input = +""
363
334
  input << %{call("foo", "bar", biz: "baz")}
364
335
 
@@ -372,14 +343,10 @@ class PegParserTest < Minitest::Test
372
343
  def test_positional_args_code_block
373
344
  input = +""
374
345
  input << %{:::>> background.start("rails server", name: "server")\n}
375
- # input << %Q{:::-- background.stop(name: "server")\n}
376
346
 
377
347
  parser = Rundoc::PegParser.new.command
378
348
 
379
349
  tree = parser.parse_with_debug(input)
380
-
381
- # puts tree.inspect
382
-
383
350
  actual = @transformer.apply(tree)
384
351
  assert_equal :"background.start", actual.keyword
385
352
  assert_equal ["rails server", {name: "server"}], actual.original_args
@@ -4,36 +4,20 @@ class RegexTest < Minitest::Test
4
4
  def setup
5
5
  end
6
6
 
7
- def test_indent_regex
8
- contents = <<~RUBY
9
- foo
10
-
11
- $ cd
12
- yo
13
- sup
14
-
15
- bar
16
- RUBY
17
-
18
- regex = Rundoc::Parser::INDENT_BLOCK
19
- parsed = contents.match(/#{regex}/).to_s
20
- assert_equal "\n $ cd\n yo\n sup\n", parsed
21
- end
22
-
23
7
  def test_github_regex
24
8
  contents = <<~RUBY
25
9
  foo
26
-
10
+
27
11
  ```
28
12
  $ cd
29
13
  yo
30
14
  sup
31
15
  ```
32
-
16
+
33
17
  bar
34
18
  RUBY
35
19
 
36
- regex = Rundoc::Parser::GITHUB_BLOCK
20
+ regex = Rundoc::Document::GITHUB_BLOCK
37
21
  parsed = contents.match(/#{regex}/m).to_s
38
22
  assert_equal "```\n$ cd\nyo\nsup\n```\n", parsed
39
23
  end
@@ -41,74 +25,40 @@ class RegexTest < Minitest::Test
41
25
  def test_github_tagged_regex
42
26
  contents = <<~RUBY
43
27
  foo
44
-
28
+
45
29
  ```ruby
46
30
  $ cd
47
31
  yo
48
32
  sup
49
33
  ```
50
-
34
+
51
35
  bar
52
36
  RUBY
53
37
 
54
- regex = Rundoc::Parser::GITHUB_BLOCK
38
+ regex = Rundoc::Document::GITHUB_BLOCK
55
39
  parsed = contents.match(/#{regex}/m).to_s
56
40
  assert_equal "```ruby\n$ cd\nyo\nsup\n```\n", parsed
57
41
  end
58
42
 
59
- def test_command_regex
60
- regex = Rundoc::Parser::COMMAND_REGEX.call(":::")
61
-
62
- contents = ":::$ mkdir schneems"
63
- match = contents.match(regex)
64
- assert_equal "", match[:tag]
65
- assert_equal "$", match[:command]
66
- assert_equal "mkdir schneems", match[:statement]
67
-
68
- contents = ":::=$ mkdir schneems"
69
- match = contents.match(regex)
70
- assert_equal "=", match[:tag]
71
- assert_equal "$", match[:command]
72
- assert_equal "mkdir schneems", match[:statement]
73
-
74
- contents = ":::= $ mkdir schneems"
75
- match = contents.match(regex)
76
- assert_equal "=", match[:tag]
77
- assert_equal "$", match[:command]
78
- assert_equal "mkdir schneems", match[:statement]
79
-
80
- contents = ":::-$ mkdir schneems"
81
- match = contents.match(regex)
82
- assert_equal "-", match[:tag]
83
- assert_equal "$", match[:command]
84
- assert_equal "mkdir schneems", match[:statement]
85
-
86
- contents = ":::- $ mkdir schneems"
87
- match = contents.match(regex)
88
- assert_equal "-", match[:tag]
89
- assert_equal "$", match[:command]
90
- assert_equal "mkdir schneems", match[:statement]
91
- end
92
-
93
43
  def test_codeblock_regex
94
44
  contents = <<~RUBY
95
45
  foo
96
-
46
+
97
47
  ```
98
48
  :::>$ mkdir
99
49
  ```
100
-
50
+
101
51
  zoo
102
-
52
+
103
53
  ```
104
54
  :::>$ cd ..
105
55
  something
106
56
  ```
107
-
57
+
108
58
  bar
109
59
  RUBY
110
60
 
111
- regex = Rundoc::Parser::CODEBLOCK_REGEX
61
+ regex = Rundoc::Document::CODEBLOCK_REGEX
112
62
 
113
63
  actual = contents.partition(regex)
114
64
  expected = ["foo\n\n",
@@ -133,7 +83,7 @@ class RegexTest < Minitest::Test
133
83
  ```java
134
84
  :::>> write app/controllers/Application.java
135
85
  package controllers;
136
-
86
+
137
87
  import static java.util.concurrent.TimeUnit.SECONDS;
138
88
  import models.Pinger;
139
89
  import play.libs.Akka;
@@ -146,7 +96,7 @@ class RegexTest < Minitest::Test
146
96
  import akka.actor.ActorRef;
147
97
  import akka.actor.Cancellable;
148
98
  import akka.actor.Props;
149
-
99
+
150
100
  public class Application extends Controller {
151
101
  public static WebSocket<String> pingWs() {
152
102
  return new WebSocket<String>() {
@@ -159,7 +109,7 @@ class RegexTest < Minitest::Test
159
109
  Akka.system().dispatcher(),
160
110
  null
161
111
  );
162
-
112
+
163
113
  in.onClose(new Callback0() {
164
114
  @Override
165
115
  public void invoke() throws Throwable {
@@ -167,14 +117,14 @@ class RegexTest < Minitest::Test
167
117
  }
168
118
  });
169
119
  }
170
-
120
+
171
121
  };
172
122
  }
173
-
123
+
174
124
  public static Result pingJs() {
175
125
  return ok(views.js.ping.render());
176
126
  }
177
-
127
+
178
128
  public static Result index() {
179
129
  return ok(index.render());
180
130
  }
@@ -182,7 +132,7 @@ class RegexTest < Minitest::Test
182
132
  ```
183
133
  RUBY
184
134
 
185
- regex = Rundoc::Parser::CODEBLOCK_REGEX
135
+ regex = Rundoc::Document::CODEBLOCK_REGEX
186
136
  match = contents.match(regex)
187
137
  assert_equal "java", match[:lang]
188
138
  assert_equal "```", match[:fence]
@@ -205,7 +155,7 @@ class RegexTest < Minitest::Test
205
155
  :::>> $ echo "hello"
206
156
  ```
207
157
  MD
208
- regex = Rundoc::Parser::CODEBLOCK_REGEX
158
+ regex = Rundoc::Document::CODEBLOCK_REGEX
209
159
  match = code_block_with_newline.match(regex)
210
160
  assert_equal expected, match.to_s.strip
211
161
 
data/test/test_helper.rb CHANGED
@@ -19,12 +19,11 @@ class Minitest::Test
19
19
  source_path: nil,
20
20
  screenshots_dirname: nil
21
21
  )
22
-
23
22
  Rundoc::Context::Execution.new(
24
- output_dir: output_dir || Pathname("/dev/null"),
25
- source_path: source_path || Pathname("/dev/null"),
23
+ output_dir: output_dir || Pathname(File::NULL),
24
+ source_path: source_path || Pathname(File::NULL),
26
25
  with_contents_dir: nil,
27
- screenshots_dirname: screenshots_dirname || Pathname("/dev/null")
26
+ screenshots_dirname: screenshots_dirname || Pathname(File::NULL)
28
27
  )
29
28
  end
30
29
 
@@ -39,7 +38,7 @@ class Minitest::Test
39
38
  source_path: source_path,
40
39
  screenshots_dirname: screenshots_dirname
41
40
  )
42
- Rundoc::Parser.new(
41
+ Rundoc::Document.new(
43
42
  contents,
44
43
  context: context
45
44
  )
@@ -60,7 +59,7 @@ class Minitest::Test
60
59
  end
61
60
 
62
61
  def strip_autogen_warning(string)
63
- string.gsub!(Rundoc::CodeSection::AUTOGEN_WARNING, "")
62
+ string.gsub!(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
64
63
  string.gsub!(/<!-- STOP.*STOP -->/m, "")
65
64
  string
66
65
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rundoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Schneeman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-11-30 00:00:00.000000000 Z
11
+ date: 2024-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -215,6 +215,7 @@ files:
215
215
  - lib/rundoc/code_command/file_command/remove.rb
216
216
  - lib/rundoc/code_command/no_such_command.rb
217
217
  - lib/rundoc/code_command/pipe.rb
218
+ - lib/rundoc/code_command/pre/erb.rb
218
219
  - lib/rundoc/code_command/print/erb.rb
219
220
  - lib/rundoc/code_command/print/text.rb
220
221
  - lib/rundoc/code_command/raw.rb
@@ -227,10 +228,10 @@ files:
227
228
  - lib/rundoc/code_command/website/screenshot.rb
228
229
  - lib/rundoc/code_command/website/visit.rb
229
230
  - lib/rundoc/code_command/write.rb
230
- - lib/rundoc/code_section.rb
231
231
  - lib/rundoc/context/after_build.rb
232
232
  - lib/rundoc/context/execution.rb
233
- - lib/rundoc/parser.rb
233
+ - lib/rundoc/document.rb
234
+ - lib/rundoc/fenced_code_block.rb
234
235
  - lib/rundoc/peg_parser.rb
235
236
  - lib/rundoc/version.rb
236
237
  - rundoc.gemspec
@@ -264,6 +265,7 @@ files:
264
265
  - test/integration/after_build_test.rb
265
266
  - test/integration/background_stdin_test.rb
266
267
  - test/integration/failure_test.rb
268
+ - test/integration/pre_erb_test.rb
267
269
  - test/integration/print_test.rb
268
270
  - test/integration/require_test.rb
269
271
  - test/integration/website_test.rb
@@ -1,155 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Rundoc
4
- # A code secttion respesents a block of fenced code
5
- #
6
- # A document can have multiple code sections
7
- class CodeSection
8
- class ParseError < StandardError
9
- def initialize(options = {})
10
- keyword = options[:keyword]
11
- command = options[:command]
12
- line_number = options[:line_number]
13
- block = options[:block].lines.map do |line|
14
- if line == command
15
- " > #{line}"
16
- else
17
- " #{line}"
18
- end
19
- end.join("")
20
-
21
- msg = "Error parsing (line:#{line_number}):\n"
22
- msg << "> '#{command.strip}'\n"
23
- msg << "No such registered command: '#{keyword}'\n"
24
- msg << "registered commands: #{Rundoc.known_commands.inspect}\n\n"
25
- msg << block
26
- msg << "\n"
27
- super(msg)
28
- end
29
- end
30
-
31
- COMMAND_REGEX = Rundoc::Parser::COMMAND_REGEX # todo: move whole thing
32
- AUTOGEN_WARNING = "\n<!-- STOP. This document is autogenerated. Do not manually modify. See the top of the doc for more details. -->"
33
- attr_accessor :original, :fence, :lang, :code, :commands, :keyword
34
-
35
- PARTIAL_RESULT = []
36
- PARTIAL_ENV = {}
37
-
38
- def initialize(match, keyword:, context:)
39
- @original = match.to_s
40
- @commands = []
41
- @stack = []
42
- @keyword = keyword
43
- @context = context
44
- @fence = match[:fence]
45
- @lang = match[:lang]
46
- @code = match[:contents]
47
- parse_code_command
48
- PARTIAL_RESULT.clear
49
- PARTIAL_ENV.clear
50
- end
51
-
52
- def render
53
- result = []
54
- env = {}
55
- env[:commands] = []
56
- env[:fence_start] = "#{fence}#{lang}"
57
- env[:fence_end] = "#{fence}#{AUTOGEN_WARNING}"
58
- env[:before] = []
59
- env[:after] = []
60
- env[:context] = @context
61
-
62
- @stack.each do |s|
63
- unless s.respond_to?(:call)
64
- result << s
65
- next
66
- end
67
-
68
- code_command = s
69
- code_output = code_command.call(env) || ""
70
- code_line = code_command.to_md(env) || ""
71
-
72
- env[:commands] << {object: code_command, output: code_output, command: code_line}
73
-
74
- tmp_result = []
75
- tmp_result << code_line if code_command.render_command?
76
- tmp_result << code_output if code_command.render_result?
77
-
78
- result << tmp_result unless code_command.hidden?
79
-
80
- PARTIAL_RESULT.replace(result)
81
- PARTIAL_ENV.replace(env)
82
- end
83
-
84
- return "" if hidden?
85
-
86
- self.class.to_doc(result: result, env: env)
87
- end
88
-
89
- def self.partial_result_to_doc
90
- to_doc(result: PARTIAL_RESULT, env: PARTIAL_ENV)
91
- end
92
-
93
- def self.to_doc(result:, env:)
94
- array = [env[:before]]
95
-
96
- result.flatten!
97
- result.compact!
98
- result.map! { |s| s.respond_to?(:rstrip) ? s.rstrip : s }
99
- result.reject!(&:empty?)
100
- result.map!(&:to_s)
101
-
102
- if !result.empty?
103
- array << env[:fence_start]
104
- array << result
105
- array << env[:fence_end]
106
- end
107
- array << env[:after]
108
-
109
- array.flatten!
110
- array.compact!
111
- array.map! { |s| s.respond_to?(:rstrip) ? s.rstrip : s }
112
- array.reject!(&:empty?)
113
- array.map!(&:to_s)
114
-
115
- array.join("\n") << "\n"
116
- end
117
-
118
- # all of the commands are hidden
119
- def hidden?
120
- !not_hidden?
121
- end
122
-
123
- # one or more of the commands are not hidden
124
- def not_hidden?
125
- return true if commands.empty?
126
- commands.map(&:not_hidden?).detect { |c| c }
127
- end
128
-
129
- def parse_code_command
130
- parser = Rundoc::PegParser.new.code_block
131
- tree = parser.parse(@code)
132
- actual = Rundoc::PegTransformer.new.apply(tree)
133
- actual = [actual] unless actual.is_a?(Array)
134
- actual.each do |code_command|
135
- @stack << "\n" if commands.last.is_a?(Rundoc::CodeCommand)
136
- @stack << code_command
137
- commands << code_command
138
- end
139
- rescue ::Parslet::ParseFailed => e
140
- raise "Could not compile code:\n#{@code}\nReason: #{e.message}"
141
- end
142
-
143
- # def check_parse_error(command, code_block)
144
- # return unless code_command = @stack.last
145
- # return unless code_command.is_a?(Rundoc::CodeCommand::NoSuchCommand)
146
- # @original.lines.each_with_index do |line, index|
147
- # next unless line == command
148
- # raise ParseError.new(keyword: code_command.keyword,
149
- # block: code_block,
150
- # command: command,
151
- # line_number: index.next)
152
- # end
153
- # end
154
- end
155
- end