rundoc 4.1.4 → 5.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -1
- data/.standard.yml +1 -1
- data/CHANGELOG.md +7 -0
- data/README.md +36 -9
- data/lib/rundoc/cli.rb +4 -1
- data/lib/rundoc/code_command/background/log/clear.rb +12 -2
- data/lib/rundoc/code_command/background/log/read.rb +12 -2
- data/lib/rundoc/code_command/background/process_spawn.rb +3 -1
- data/lib/rundoc/code_command/background/start.rb +25 -6
- data/lib/rundoc/code_command/background/stdin_write.rb +21 -8
- data/lib/rundoc/code_command/background/stop.rb +12 -2
- data/lib/rundoc/code_command/background/wait.rb +15 -3
- data/lib/rundoc/code_command/background.rb +2 -0
- data/lib/rundoc/code_command/bash/cd.rb +7 -7
- data/lib/rundoc/code_command/bash.rb +43 -19
- data/lib/rundoc/code_command/comment.rb +33 -0
- data/lib/rundoc/code_command/deferred.rb +66 -0
- data/lib/rundoc/code_command/file_command/append.rb +29 -8
- data/lib/rundoc/code_command/file_command/remove.rb +27 -5
- data/lib/rundoc/code_command/no_such_command.rb +8 -3
- data/lib/rundoc/code_command/pipe.rb +36 -16
- data/lib/rundoc/code_command/pre/erb.rb +28 -18
- data/lib/rundoc/code_command/print/erb.rb +28 -4
- data/lib/rundoc/code_command/print/text.rb +27 -8
- data/lib/rundoc/code_command/raw.rb +17 -5
- data/lib/rundoc/code_command/rundoc/require.rb +25 -17
- data/lib/rundoc/code_command/rundoc_command.rb +21 -8
- data/lib/rundoc/code_command/website/driver.rb +2 -0
- data/lib/rundoc/code_command/website/navigate.rb +18 -12
- data/lib/rundoc/code_command/website/screenshot.rb +17 -11
- data/lib/rundoc/code_command/website/visit.rb +23 -12
- data/lib/rundoc/code_command/website.rb +2 -0
- data/lib/rundoc/code_command/write.rb +37 -9
- data/lib/rundoc/code_command.rb +5 -48
- data/lib/rundoc/context/after_build.rb +2 -0
- data/lib/rundoc/context/execution.rb +2 -0
- data/lib/rundoc/document.rb +6 -2
- data/lib/rundoc/fenced_code_block.rb +10 -7
- data/lib/rundoc/peg_parser.rb +17 -9
- data/lib/rundoc/version.rb +3 -1
- data/lib/rundoc.rb +52 -17
- data/rundoc.gemspec +2 -0
- data/test/rundoc/code_commands/append_file_test.rb +35 -10
- data/test/rundoc/code_commands/background_test.rb +24 -22
- data/test/rundoc/code_commands/bash_test.rb +10 -5
- data/test/rundoc/code_commands/comment_test.rb +116 -0
- data/test/rundoc/code_commands/pipe_test.rb +2 -2
- data/test/rundoc/code_commands/print_test.rb +13 -25
- data/test/rundoc/code_commands/remove_contents_test.rb +8 -3
- data/test/rundoc/code_section_test.rb +28 -21
- data/test/rundoc/peg_parser_test.rb +17 -1
- data/test/test_helper.rb +4 -2
- metadata +6 -6
- data/lib/rundoc/code_command/rundoc/depend_on.rb +0 -13
- data/test/fixtures/depend_on/dependency/rundoc.md +0 -5
- data/test/fixtures/depend_on/main/rundoc.md +0 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 85fbfa9f84b4e98eb72d487721e7f35001f7820404e30a138de4b4505d788f53
|
|
4
|
+
data.tar.gz: 1ff08ba1cdd468b190fc54eef124b7ab434d700ffe87ba2b4c8e676d03fb515e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8e87c610bcd8c8589407e68b503eee0578a1dc23b959baa138258185f7d08e6daa3ee17097e4b66b02fea896ee9e5be2bde936e2a601a2b18d1b1a2fc24b36a2
|
|
7
|
+
data.tar.gz: 9c9f4d7446beaedc74d61354794327cd30f372f7e5e16500678247f7bc549bb7b04992d0875c06c1fa06ae894a36c35902e07930cf55ab4e9fc01bb8b29c44d3
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -11,9 +11,9 @@ jobs:
|
|
|
11
11
|
fail-fast: false
|
|
12
12
|
matrix:
|
|
13
13
|
ruby:
|
|
14
|
-
- 3.1
|
|
15
14
|
- 3.2
|
|
16
15
|
- 3.3
|
|
16
|
+
- "4.0"
|
|
17
17
|
- head
|
|
18
18
|
steps:
|
|
19
19
|
- name: Checkout code
|
|
@@ -34,6 +34,7 @@ jobs:
|
|
|
34
34
|
matrix:
|
|
35
35
|
ruby:
|
|
36
36
|
- 3.3
|
|
37
|
+
- "4.0"
|
|
37
38
|
- head
|
|
38
39
|
steps:
|
|
39
40
|
- name: Checkout code
|
data/.standard.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
## HEAD
|
|
2
2
|
|
|
3
|
+
## 5.0.0
|
|
4
|
+
|
|
5
|
+
- Added comment syntax. Use an octothorpe (`#`) after the visibility markers to comment out any commands and make them a no-op.
|
|
6
|
+
- Changed: Minimum Ruby version is now 3.2 (Ruby 3.1 reached EOL in March 2025).
|
|
7
|
+
- Changed: All code commands now use an Args + Runner class pattern. `register_code_command` now requires keyword arguments: `keyword:`, `args_klass:`, and `runner_klass:`.
|
|
8
|
+
- Fix: `seattle_method` parser rule no longer matches across newlines. Previously, a command with no same-line arguments (e.g. `:::>> rundoc`) would consume the next line as its argument, losing the newline between content lines. (https://github.com/zombocom/rundoc/pull/118)
|
|
9
|
+
|
|
3
10
|
## 4.1.4
|
|
4
11
|
|
|
5
12
|
- Fix: Net::ReadTimeout errors on `website.visit` are now retried by default (https://github.com/zombocom/rundoc/pull/103)
|
data/README.md
CHANGED
|
@@ -660,36 +660,63 @@ This command `filter_sensitive` can be called multiple times with different valu
|
|
|
660
660
|
|
|
661
661
|
## Writing a new command
|
|
662
662
|
|
|
663
|
+
> Note: This is an advanced topic and this interface is unstable.
|
|
664
|
+
|
|
663
665
|
Rundoc does not have a stable internal command interface. You can define your own commands, but unless it is committed in this repo, it may break on a minor version change.
|
|
664
666
|
|
|
665
667
|
To add a new command it needs to be parsed and called. Examples of commands being implemented are seen in `lib/rundoc/code_command`.
|
|
666
668
|
|
|
667
|
-
|
|
669
|
+
Each command is split into two classes: an **Args** class that handles argument parsing/validation and a **Runner** class that handles execution. Both must be registered:
|
|
668
670
|
|
|
671
|
+
```ruby
|
|
672
|
+
Rundoc.register_code_command(
|
|
673
|
+
keyword: :lol,
|
|
674
|
+
args_klass: Rundoc::CodeCommand::LolArgs,
|
|
675
|
+
runner_klass: Rundoc::CodeCommand::LolRunner
|
|
676
|
+
)
|
|
669
677
|
```
|
|
670
|
-
Rundoc.register_code_command(:lol, Rundoc::CodeCommand::Lol)
|
|
671
|
-
```
|
|
672
678
|
|
|
673
|
-
|
|
679
|
+
The Args class is a plain Ruby class. Its initialize method receives input from the document and exposes parsed values via `attr_reader`:
|
|
680
|
+
|
|
681
|
+
```ruby
|
|
682
|
+
class Rundoc::CodeCommand::LolArgs
|
|
683
|
+
attr_reader :message
|
|
674
684
|
|
|
685
|
+
def initialize(message)
|
|
686
|
+
@message = message
|
|
687
|
+
end
|
|
688
|
+
end
|
|
675
689
|
```
|
|
676
|
-
|
|
677
|
-
|
|
690
|
+
|
|
691
|
+
The Runner class is namespaced under `Rundoc::CodeCommand` and receives the args instance via `user_args:`:
|
|
692
|
+
|
|
693
|
+
```ruby
|
|
694
|
+
class Rundoc::CodeCommand::LolRunner
|
|
695
|
+
def initialize(user_args:, **)
|
|
696
|
+
@message = user_args.message
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
def to_md(env = {})
|
|
700
|
+
""
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
def call(env = {})
|
|
704
|
+
@message
|
|
678
705
|
end
|
|
679
706
|
end
|
|
680
707
|
```
|
|
681
708
|
|
|
682
|
-
The
|
|
709
|
+
The command is rendered (`:::>-`) by the output of the `def call` method. The contents produced by the command (`:::->`) are rendered by the `def to_md` method.
|
|
683
710
|
|
|
684
711
|
The syntax for commands is ruby-ish but it is a custom grammar implemented in `lib/peg_parser.rb` for more info on manipulating the grammar see this tutorial on how I added keword-like/hash-like syntax https://github.com/schneems/implement_ruby_hash_syntax_with_parslet_example.
|
|
685
712
|
|
|
686
|
-
|
|
713
|
+
Args class initialize methods natively support:
|
|
687
714
|
|
|
688
715
|
- Barewords as a single string input
|
|
689
716
|
- Keyword arguments
|
|
690
717
|
- A combination of the two
|
|
691
718
|
|
|
692
|
-
Anything that is passed to the command via "stdin" is available via a method `self.contents`. The interplay between the input and `self.contents` is not strongly defined.
|
|
719
|
+
Anything that is passed to the command via "stdin" is available on the Runner via a method `self.contents`. The interplay between the input and `self.contents` is not strongly defined.
|
|
693
720
|
|
|
694
721
|
## Copyright
|
|
695
722
|
|
data/lib/rundoc/cli.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Rundoc
|
|
2
4
|
class CLI
|
|
3
5
|
module DEFAULTS
|
|
@@ -159,7 +161,8 @@ module Rundoc
|
|
|
159
161
|
Dir.chdir(execution_context.output_dir) do
|
|
160
162
|
parser = Rundoc::Document.new(
|
|
161
163
|
source_contents,
|
|
162
|
-
context: execution_context
|
|
164
|
+
context: execution_context,
|
|
165
|
+
io: io
|
|
163
166
|
)
|
|
164
167
|
output = begin
|
|
165
168
|
parser.to_md
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class Rundoc::CodeCommand::Background::Log
|
|
2
|
-
class
|
|
4
|
+
class ClearArgs
|
|
5
|
+
attr_reader :name
|
|
6
|
+
|
|
3
7
|
def initialize(name:)
|
|
4
8
|
@name = name
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class ClearRunner
|
|
13
|
+
def initialize(user_args:, render_command:, render_result:, io: nil, contents: nil)
|
|
14
|
+
@name = user_args.name
|
|
5
15
|
@background = nil
|
|
6
16
|
end
|
|
7
17
|
|
|
@@ -19,4 +29,4 @@ class Rundoc::CodeCommand::Background::Log
|
|
|
19
29
|
end
|
|
20
30
|
end
|
|
21
31
|
end
|
|
22
|
-
Rundoc.register_code_command(:"background.log.clear", Rundoc::CodeCommand::Background::Log::
|
|
32
|
+
Rundoc.register_code_command(keyword: :"background.log.clear", args_klass: Rundoc::CodeCommand::Background::Log::ClearArgs, runner_klass: Rundoc::CodeCommand::Background::Log::ClearRunner)
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class Rundoc::CodeCommand::Background::Log
|
|
2
|
-
class
|
|
4
|
+
class ReadArgs
|
|
5
|
+
attr_reader :name
|
|
6
|
+
|
|
3
7
|
def initialize(name:)
|
|
4
8
|
@name = name
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class ReadRunner
|
|
13
|
+
def initialize(user_args:, render_command:, render_result:, io: nil, contents: nil)
|
|
14
|
+
@name = user_args.name
|
|
5
15
|
@background = nil
|
|
6
16
|
end
|
|
7
17
|
|
|
@@ -18,4 +28,4 @@ class Rundoc::CodeCommand::Background::Log
|
|
|
18
28
|
end
|
|
19
29
|
end
|
|
20
30
|
end
|
|
21
|
-
Rundoc.register_code_command(:"background.log.read", Rundoc::CodeCommand::Background::Log::
|
|
31
|
+
Rundoc.register_code_command(keyword: :"background.log.read", args_klass: Rundoc::CodeCommand::Background::Log::ReadArgs, runner_klass: Rundoc::CodeCommand::Background::Log::ReadRunner)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "shellwords"
|
|
2
4
|
require "timeout"
|
|
3
5
|
require "fileutils"
|
|
@@ -126,7 +128,7 @@ class Rundoc::CodeCommand::Background
|
|
|
126
128
|
Process.kill("TERM", -Process.getpgid(@pid))
|
|
127
129
|
Process.wait(@pid)
|
|
128
130
|
rescue Errno::ESRCH => e
|
|
129
|
-
puts "Error stopping process (command: #{command}): #{e}"
|
|
131
|
+
print_io&.puts "Error stopping process (command: #{command}): #{e}"
|
|
130
132
|
ensure
|
|
131
133
|
print_io&.puts "Log contents for `#{command}`:\n#{@log.read}"
|
|
132
134
|
end
|
|
@@ -1,18 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "tempfile"
|
|
2
4
|
|
|
3
5
|
class Rundoc::CodeCommand::Background
|
|
4
|
-
class
|
|
6
|
+
class StartArgs
|
|
7
|
+
attr_reader :command, :name, :wait, :timeout, :log, :out, :allow_fail
|
|
8
|
+
|
|
5
9
|
def initialize(command, name:, wait: nil, timeout: 5, log: Tempfile.new("log"), out: "2>&1", allow_fail: false)
|
|
6
|
-
@timeout = timeout
|
|
7
10
|
@command = command
|
|
8
11
|
@name = name
|
|
9
12
|
@wait = wait
|
|
10
|
-
@
|
|
13
|
+
@timeout = timeout
|
|
11
14
|
@log = log
|
|
12
|
-
@
|
|
15
|
+
@out = out
|
|
16
|
+
@allow_fail = allow_fail
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class StartRunner
|
|
21
|
+
attr_reader :io
|
|
22
|
+
|
|
23
|
+
def initialize(user_args:, render_command:, render_result:, io:, contents: nil)
|
|
24
|
+
@timeout = user_args.timeout
|
|
25
|
+
@command = user_args.command
|
|
26
|
+
@name = user_args.name
|
|
27
|
+
@wait = user_args.wait
|
|
28
|
+
@allow_fail = user_args.allow_fail
|
|
29
|
+
@log = user_args.log
|
|
30
|
+
@redirect = user_args.out
|
|
13
31
|
FileUtils.touch(@log)
|
|
14
32
|
|
|
15
33
|
@background = nil
|
|
34
|
+
@io = io
|
|
16
35
|
end
|
|
17
36
|
|
|
18
37
|
def background
|
|
@@ -22,7 +41,7 @@ class Rundoc::CodeCommand::Background
|
|
|
22
41
|
log: @log,
|
|
23
42
|
out: @redirect
|
|
24
43
|
).tap do |spawn|
|
|
25
|
-
puts "Spawning commmand: `#{spawn.command}`"
|
|
44
|
+
io.puts "Spawning commmand: `#{spawn.command}`"
|
|
26
45
|
ProcessSpawn.add(@name, spawn)
|
|
27
46
|
end
|
|
28
47
|
end
|
|
@@ -44,4 +63,4 @@ class Rundoc::CodeCommand::Background
|
|
|
44
63
|
end
|
|
45
64
|
end
|
|
46
65
|
|
|
47
|
-
Rundoc.register_code_command(:"background.start", Rundoc::CodeCommand::Background::
|
|
66
|
+
Rundoc.register_code_command(keyword: :"background.start", args_klass: Rundoc::CodeCommand::Background::StartArgs, runner_klass: Rundoc::CodeCommand::Background::StartRunner)
|
|
@@ -1,14 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class Rundoc::CodeCommand::Background
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class StdinWrite < Rundoc::CodeCommand
|
|
4
|
+
class StdinWriteArgs
|
|
5
|
+
attr_reader :contents, :name, :wait, :timeout, :ending
|
|
6
|
+
|
|
6
7
|
def initialize(contents, name:, wait:, timeout: 5, ending: $/)
|
|
7
8
|
@contents = contents
|
|
8
|
-
@ending = ending
|
|
9
|
-
@wait = wait
|
|
10
9
|
@name = name
|
|
11
|
-
@
|
|
10
|
+
@wait = wait
|
|
11
|
+
@timeout = Integer(timeout)
|
|
12
|
+
@ending = ending
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class StdinWriteRunner
|
|
17
|
+
attr_reader :contents
|
|
18
|
+
|
|
19
|
+
def initialize(user_args:, render_command:, render_result:, io: nil, contents: nil)
|
|
20
|
+
@contents = user_args.contents
|
|
21
|
+
@ending = user_args.ending
|
|
22
|
+
@wait = user_args.wait
|
|
23
|
+
@name = user_args.name
|
|
24
|
+
@timeout_value = user_args.timeout
|
|
12
25
|
@contents_written = nil
|
|
13
26
|
@background = nil
|
|
14
27
|
end
|
|
@@ -38,4 +51,4 @@ class Rundoc::CodeCommand::Background
|
|
|
38
51
|
end
|
|
39
52
|
end
|
|
40
53
|
end
|
|
41
|
-
Rundoc.register_code_command(:"background.stdin_write", Rundoc::CodeCommand::Background::
|
|
54
|
+
Rundoc.register_code_command(keyword: :"background.stdin_write", args_klass: Rundoc::CodeCommand::Background::StdinWriteArgs, runner_klass: Rundoc::CodeCommand::Background::StdinWriteRunner)
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class Rundoc::CodeCommand::Background
|
|
2
|
-
class
|
|
4
|
+
class StopArgs
|
|
5
|
+
attr_reader :name
|
|
6
|
+
|
|
3
7
|
def initialize(name:)
|
|
4
8
|
@name = name
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class StopRunner
|
|
13
|
+
def initialize(user_args:, render_command:, render_result:, io: nil, contents: nil)
|
|
14
|
+
@name = user_args.name
|
|
5
15
|
@background = nil
|
|
6
16
|
end
|
|
7
17
|
|
|
@@ -19,4 +29,4 @@ class Rundoc::CodeCommand::Background
|
|
|
19
29
|
end
|
|
20
30
|
end
|
|
21
31
|
end
|
|
22
|
-
Rundoc.register_code_command(:"background.stop", Rundoc::CodeCommand::Background::
|
|
32
|
+
Rundoc.register_code_command(keyword: :"background.stop", args_klass: Rundoc::CodeCommand::Background::StopArgs, runner_klass: Rundoc::CodeCommand::Background::StopRunner)
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class Rundoc::CodeCommand::Background
|
|
2
|
-
class
|
|
4
|
+
class WaitArgs
|
|
5
|
+
attr_reader :name, :wait, :timeout
|
|
6
|
+
|
|
3
7
|
def initialize(name:, wait:, timeout: 5)
|
|
4
8
|
@name = name
|
|
5
9
|
@wait = wait
|
|
6
|
-
@
|
|
10
|
+
@timeout = Integer(timeout)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class WaitRunner
|
|
15
|
+
def initialize(user_args:, render_command:, render_result:, io: nil, contents: nil)
|
|
16
|
+
@name = user_args.name
|
|
17
|
+
@wait = user_args.wait
|
|
18
|
+
@timeout_value = user_args.timeout
|
|
7
19
|
@background = nil
|
|
8
20
|
end
|
|
9
21
|
|
|
@@ -21,4 +33,4 @@ class Rundoc::CodeCommand::Background
|
|
|
21
33
|
end
|
|
22
34
|
end
|
|
23
35
|
end
|
|
24
|
-
Rundoc.register_code_command(:"background.wait", Rundoc::CodeCommand::Background::
|
|
36
|
+
Rundoc.register_code_command(keyword: :"background.wait", args_klass: Rundoc::CodeCommand::Background::WaitArgs, runner_klass: Rundoc::CodeCommand::Background::WaitRunner)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Rundoc::CodeCommand::BashRunner
|
|
4
|
+
class Cd < Rundoc::CodeCommand::BashRunner
|
|
5
|
+
def initialize(line, io: $stdout)
|
|
6
|
+
@io = io
|
|
7
7
|
@line = line
|
|
8
8
|
end
|
|
9
9
|
|
|
@@ -23,7 +23,7 @@ class Rundoc::CodeCommand::Bash
|
|
|
23
23
|
|
|
24
24
|
def call(env)
|
|
25
25
|
line = @line.sub("cd", "").strip
|
|
26
|
-
puts "running $ cd #{line}"
|
|
26
|
+
@io.puts "running $ cd #{line}"
|
|
27
27
|
|
|
28
28
|
supress_chdir_warning do
|
|
29
29
|
Dir.chdir(line)
|
|
@@ -1,18 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Rundoc::CodeCommand::BashArgs
|
|
4
|
+
attr_reader :line
|
|
5
|
+
|
|
5
6
|
def initialize(line)
|
|
6
7
|
@line = line
|
|
7
|
-
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class Rundoc::CodeCommand::BashRunner
|
|
12
|
+
attr_reader :io, :contents
|
|
13
|
+
|
|
14
|
+
def initialize(user_args:, render_command:, render_result:, io:, contents: nil)
|
|
15
|
+
@io = io
|
|
16
|
+
@contents = contents.dup if contents && !contents.empty?
|
|
17
|
+
@line = user_args.line
|
|
8
18
|
@delegate = case @line.split(" ").first.downcase
|
|
9
19
|
when "cd"
|
|
10
|
-
Cd.new(@line)
|
|
20
|
+
Cd.new(@line, io: io)
|
|
11
21
|
else
|
|
12
22
|
false
|
|
13
23
|
end
|
|
14
24
|
end
|
|
15
25
|
|
|
26
|
+
def raise_on_error?
|
|
27
|
+
true
|
|
28
|
+
end
|
|
29
|
+
|
|
16
30
|
def to_md(env = {})
|
|
17
31
|
return @delegate.to_md(env) if @delegate
|
|
18
32
|
|
|
@@ -34,30 +48,40 @@ class Rundoc::CodeCommand::Bash < Rundoc::CodeCommand
|
|
|
34
48
|
cmd = "(#{cmd}) 2>&1"
|
|
35
49
|
msg = "Running: $ '#{cmd}'"
|
|
36
50
|
msg << " with stdin: '#{stdin.inspect}'" if stdin && !stdin.empty?
|
|
37
|
-
puts msg
|
|
51
|
+
io.puts msg
|
|
38
52
|
|
|
39
|
-
result = ""
|
|
40
|
-
IO.popen(cmd, "w+") do |
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
result = +""
|
|
54
|
+
IO.popen(cmd, "w+") do |pipe|
|
|
55
|
+
pipe << stdin if stdin
|
|
56
|
+
pipe.close_write
|
|
43
57
|
|
|
44
|
-
until
|
|
45
|
-
buffer =
|
|
46
|
-
puts " #{buffer}"
|
|
58
|
+
until pipe.eof?
|
|
59
|
+
buffer = pipe.gets
|
|
60
|
+
io.puts " #{buffer}"
|
|
47
61
|
|
|
48
62
|
result << sanitize_escape_chars(buffer)
|
|
49
63
|
end
|
|
50
64
|
end
|
|
51
65
|
|
|
52
|
-
|
|
53
|
-
raise "Command `#{@line}` exited with non zero status: #{result}"
|
|
66
|
+
if raise_on_error? && !$?.success?
|
|
67
|
+
raise "Command `#{@line}` exited with non zero status: #{result}"
|
|
54
68
|
end
|
|
55
69
|
result
|
|
56
70
|
end
|
|
57
71
|
end
|
|
58
72
|
|
|
59
|
-
Rundoc
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
class Rundoc::CodeCommand::BashRunnerFailOk < Rundoc::CodeCommand::BashRunner
|
|
74
|
+
def raise_on_error?
|
|
75
|
+
false
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
Rundoc.register_code_command(keyword: :bash, args_klass: Rundoc::CodeCommand::BashArgs, runner_klass: Rundoc::CodeCommand::BashRunner)
|
|
80
|
+
Rundoc.register_code_command(keyword: :"$", args_klass: Rundoc::CodeCommand::BashArgs, runner_klass: Rundoc::CodeCommand::BashRunner)
|
|
81
|
+
Rundoc.register_code_command(
|
|
82
|
+
keyword: :"fail.$",
|
|
83
|
+
args_klass: Rundoc::CodeCommand::BashArgs,
|
|
84
|
+
runner_klass: Rundoc::CodeCommand::BashRunnerFailOk
|
|
85
|
+
)
|
|
62
86
|
|
|
63
87
|
require "rundoc/code_command/bash/cd"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Rundoc::CodeCommand::CommentArgs
|
|
4
|
+
attr_reader :line
|
|
5
|
+
|
|
6
|
+
def initialize(line = nil)
|
|
7
|
+
@line = line
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class Rundoc::CodeCommand::CommentRunner
|
|
12
|
+
def initialize(user_args:, render_command:, render_result:, io:, contents: nil)
|
|
13
|
+
@io = io
|
|
14
|
+
@line = user_args&.line
|
|
15
|
+
@contents = contents
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(env = {})
|
|
19
|
+
@io.puts "Skipping command (commented out): # #{@line}\n#{@contents}".strip
|
|
20
|
+
""
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_md(env = {})
|
|
24
|
+
""
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Rundoc.register_code_command(
|
|
29
|
+
keyword: :"#",
|
|
30
|
+
args_klass: Rundoc::CodeCommand::CommentArgs,
|
|
31
|
+
runner_klass: Rundoc::CodeCommand::CommentRunner,
|
|
32
|
+
always_hidden: true
|
|
33
|
+
)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rundoc
|
|
4
|
+
module CodeCommand
|
|
5
|
+
# Hold enough information to construct commands, but don't yet
|
|
6
|
+
#
|
|
7
|
+
# Allows us to separate parse time constructs from runtime injectables
|
|
8
|
+
# (such as IO). Which gives us a cleaner running model.
|
|
9
|
+
class Deferred
|
|
10
|
+
attr_accessor :render_result, :render_command,
|
|
11
|
+
:contents, :keyword, :original_args
|
|
12
|
+
|
|
13
|
+
alias_method :render_result?, :render_result
|
|
14
|
+
alias_method :render_command?, :render_command
|
|
15
|
+
|
|
16
|
+
attr_reader :runner_klass
|
|
17
|
+
|
|
18
|
+
def initialize(args_instance:, runner_klass:, always_hidden: false)
|
|
19
|
+
@args_instance = args_instance
|
|
20
|
+
@runner_klass = runner_klass
|
|
21
|
+
@always_hidden = always_hidden
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def hidden?
|
|
25
|
+
!render_command? && !render_result?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def not_hidden?
|
|
29
|
+
!hidden?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def push(contents)
|
|
33
|
+
@contents ||= +""
|
|
34
|
+
@contents << contents
|
|
35
|
+
end
|
|
36
|
+
alias_method :<<, :push
|
|
37
|
+
|
|
38
|
+
def build(io: $stdout)
|
|
39
|
+
@built ||= begin
|
|
40
|
+
runner = @runner_klass.new(
|
|
41
|
+
user_args: @args_instance,
|
|
42
|
+
render_command: render_command,
|
|
43
|
+
render_result: render_result,
|
|
44
|
+
contents: @contents,
|
|
45
|
+
io: io
|
|
46
|
+
)
|
|
47
|
+
if @always_hidden
|
|
48
|
+
@render_command = false
|
|
49
|
+
@render_result = false
|
|
50
|
+
end
|
|
51
|
+
runner
|
|
52
|
+
end
|
|
53
|
+
rescue UnknownCommand
|
|
54
|
+
raise "No such command registered with rundoc #{keyword.inspect} for `#{keyword} #{original_args}`"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def call(env = {})
|
|
58
|
+
build.call(env)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def to_md(env = {})
|
|
62
|
+
build.to_md(env)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -1,18 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class Rundoc::CodeCommand::FileCommand
|
|
2
|
-
class
|
|
3
|
-
|
|
4
|
+
class AppendArgs
|
|
5
|
+
attr_reader :filename
|
|
4
6
|
|
|
5
7
|
def initialize(filename)
|
|
6
|
-
@filename
|
|
8
|
+
@filename = filename
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class AppendRunner
|
|
13
|
+
NEWLINE = Rundoc::CodeCommand::WriteRunner::NEWLINE
|
|
14
|
+
|
|
15
|
+
include Rundoc::CodeCommand::FileUtil
|
|
16
|
+
|
|
17
|
+
attr_reader :io, :contents
|
|
18
|
+
|
|
19
|
+
def initialize(user_args:, render_command:, render_result:, io:, contents: nil)
|
|
20
|
+
@filename, line = user_args.filename.split("#")
|
|
7
21
|
@line_number = if line
|
|
8
22
|
Integer(line)
|
|
9
23
|
end
|
|
24
|
+
@io = io
|
|
25
|
+
@render_command = render_command
|
|
26
|
+
@contents = contents.dup if contents && !contents.empty?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def render_command?
|
|
30
|
+
@render_command
|
|
10
31
|
end
|
|
11
32
|
|
|
12
33
|
def to_md(env)
|
|
13
34
|
return unless render_command?
|
|
14
35
|
|
|
15
|
-
if env[:commands].any? { |c| c[:
|
|
36
|
+
if env[:commands].any? { |c| c[:visibility].not_hidden? }
|
|
16
37
|
raise "Must call append in its own code section"
|
|
17
38
|
end
|
|
18
39
|
|
|
@@ -34,7 +55,7 @@ class Rundoc::CodeCommand::FileCommand
|
|
|
34
55
|
end
|
|
35
56
|
|
|
36
57
|
def concat_with_newline(str1, str2)
|
|
37
|
-
result = ""
|
|
58
|
+
result = +""
|
|
38
59
|
result << str1
|
|
39
60
|
result << "\n" unless ends_in_newline?(result)
|
|
40
61
|
result << str2
|
|
@@ -61,10 +82,10 @@ class Rundoc::CodeCommand::FileCommand
|
|
|
61
82
|
mkdir_p
|
|
62
83
|
doc = File.read(filename)
|
|
63
84
|
if @line_number
|
|
64
|
-
puts "Writing to: '#{filename}' line #{@line_number} with: #{contents.inspect}"
|
|
85
|
+
io.puts "Writing to: '#{filename}' line #{@line_number} with: #{contents.inspect}"
|
|
65
86
|
doc = insert_contents_into_at_line(doc)
|
|
66
87
|
else
|
|
67
|
-
puts "Appending to file: '#{filename}' with: #{contents.inspect}"
|
|
88
|
+
io.puts "Appending to file: '#{filename}' with: #{contents.inspect}"
|
|
68
89
|
doc = concat_with_newline(doc, contents)
|
|
69
90
|
end
|
|
70
91
|
|
|
@@ -74,4 +95,4 @@ class Rundoc::CodeCommand::FileCommand
|
|
|
74
95
|
end
|
|
75
96
|
end
|
|
76
97
|
|
|
77
|
-
Rundoc.register_code_command(:"file.append", Rundoc::CodeCommand::FileCommand::
|
|
98
|
+
Rundoc.register_code_command(keyword: :"file.append", args_klass: Rundoc::CodeCommand::FileCommand::AppendArgs, runner_klass: Rundoc::CodeCommand::FileCommand::AppendRunner)
|