rundoc 4.1.3 → 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 +12 -0
- data/README.md +36 -9
- data/lib/rundoc/cli.rb +6 -3
- 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 +9 -5
- 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 +25 -7
- 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 +26 -14
- 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/integration/background_stdin_test.rb +65 -15
- data/test/integration/website_test.rb +19 -0
- data/test/rundoc/code_commands/append_file_test.rb +35 -10
- data/test/rundoc/code_commands/background_test.rb +26 -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 +32 -2
- metadata +7 -10
- 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
|
@@ -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)
|
|
@@ -1,13 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class Rundoc::CodeCommand::FileCommand
|
|
2
|
-
class
|
|
3
|
-
|
|
4
|
+
class RemoveArgs
|
|
5
|
+
attr_reader :filename
|
|
4
6
|
|
|
5
7
|
def initialize(filename)
|
|
6
8
|
@filename = filename
|
|
7
9
|
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class RemoveRunner
|
|
13
|
+
NEWLINE = Object.new
|
|
14
|
+
def NEWLINE.to_s
|
|
15
|
+
""
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def NEWLINE.empty?
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
include Rundoc::CodeCommand::FileUtil
|
|
22
|
+
|
|
23
|
+
attr_reader :io, :contents
|
|
24
|
+
|
|
25
|
+
def initialize(user_args:, render_command:, render_result:, io:, contents: nil)
|
|
26
|
+
@filename = user_args.filename
|
|
27
|
+
@io = io
|
|
28
|
+
@contents = contents.dup if contents && !contents.empty?
|
|
29
|
+
end
|
|
8
30
|
|
|
9
31
|
def to_md(env)
|
|
10
|
-
if env[:commands].any? { |c| c[:
|
|
32
|
+
if env[:commands].any? { |c| c[:visibility].not_hidden? }
|
|
11
33
|
raise "Must call remove in its own code section"
|
|
12
34
|
end
|
|
13
35
|
|
|
@@ -17,7 +39,7 @@ class Rundoc::CodeCommand::FileCommand
|
|
|
17
39
|
end
|
|
18
40
|
|
|
19
41
|
def call(env = {})
|
|
20
|
-
puts "Deleting '#{contents.strip}' from #{filename}"
|
|
42
|
+
io.puts "Deleting '#{contents.strip}' from #{filename}"
|
|
21
43
|
raise "#{filename} does not exist" unless File.exist?(filename)
|
|
22
44
|
|
|
23
45
|
regex = /^\s*#{Regexp.quote(contents)}/
|
|
@@ -30,4 +52,4 @@ class Rundoc::CodeCommand::FileCommand
|
|
|
30
52
|
end
|
|
31
53
|
end
|
|
32
54
|
|
|
33
|
-
Rundoc.register_code_command(:"file.remove", Rundoc::CodeCommand::FileCommand::
|
|
55
|
+
Rundoc.register_code_command(keyword: :"file.remove", args_klass: Rundoc::CodeCommand::FileCommand::RemoveArgs, runner_klass: Rundoc::CodeCommand::FileCommand::RemoveRunner)
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Rundoc
|
|
2
|
-
|
|
3
|
-
class NoSuchCommand
|
|
4
|
+
module CodeCommand
|
|
5
|
+
class NoSuchCommand
|
|
6
|
+
def initialize(user_args: nil, render_command: false, render_result: false, io: nil, contents: nil)
|
|
7
|
+
end
|
|
8
|
+
|
|
4
9
|
def call(env = {})
|
|
5
|
-
raise
|
|
10
|
+
raise UnknownCommand
|
|
6
11
|
end
|
|
7
12
|
end
|
|
8
13
|
end
|
|
@@ -1,11 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Rundoc
|
|
2
|
-
|
|
3
|
-
class
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# => "test\ntmp.file\n"
|
|
4
|
+
module CodeCommand
|
|
5
|
+
class PipeArgs
|
|
6
|
+
attr_reader :line
|
|
7
|
+
|
|
7
8
|
def initialize(line)
|
|
8
|
-
@
|
|
9
|
+
@line = line
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class PipeRunner
|
|
14
|
+
attr_reader :io
|
|
15
|
+
|
|
16
|
+
def initialize(user_args:, render_command:, render_result:, io:, contents: nil)
|
|
17
|
+
@io = io
|
|
18
|
+
@delegate = parse(user_args.line)
|
|
9
19
|
end
|
|
10
20
|
|
|
11
21
|
# before: "",
|
|
@@ -14,10 +24,10 @@ module Rundoc
|
|
|
14
24
|
# [[cmd, output], [cmd, output]]
|
|
15
25
|
def call(env = {})
|
|
16
26
|
last_command = env[:commands].last
|
|
17
|
-
puts "Piping: results of '#{last_command[:command]}' to '#{@delegate}'"
|
|
27
|
+
io.puts "Piping: results of '#{last_command[:command]}' to '#{@delegate}'"
|
|
18
28
|
|
|
19
29
|
@delegate.push(last_command[:output])
|
|
20
|
-
@delegate.call(env)
|
|
30
|
+
@delegate.build(io: io).call(env)
|
|
21
31
|
end
|
|
22
32
|
|
|
23
33
|
def to_md(env = {})
|
|
@@ -31,17 +41,27 @@ module Rundoc
|
|
|
31
41
|
|
|
32
42
|
actual = actual.first if actual.is_a?(Array)
|
|
33
43
|
|
|
34
|
-
actual
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
if actual.runner_klass == Rundoc::CodeCommand::NoSuchCommand
|
|
45
|
+
bash_deferred(code)
|
|
46
|
+
else
|
|
47
|
+
actual
|
|
48
|
+
end
|
|
39
49
|
rescue Parslet::ParseFailed
|
|
40
|
-
|
|
50
|
+
bash_deferred(code)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private def bash_deferred(code)
|
|
54
|
+
deferred = Rundoc::CodeCommand::Deferred.new(
|
|
55
|
+
args_instance: Rundoc::CodeCommand::BashArgs.new(code),
|
|
56
|
+
runner_klass: Rundoc::CodeCommand::BashRunner
|
|
57
|
+
)
|
|
58
|
+
deferred.render_command = false
|
|
59
|
+
deferred.render_result = false
|
|
60
|
+
deferred
|
|
41
61
|
end
|
|
42
62
|
end
|
|
43
63
|
end
|
|
44
64
|
end
|
|
45
65
|
|
|
46
|
-
Rundoc.register_code_command(:pipe, Rundoc::CodeCommand::
|
|
47
|
-
Rundoc.register_code_command(:|, Rundoc::CodeCommand::
|
|
66
|
+
Rundoc.register_code_command(keyword: :pipe, args_klass: Rundoc::CodeCommand::PipeArgs, runner_klass: Rundoc::CodeCommand::PipeRunner)
|
|
67
|
+
Rundoc.register_code_command(keyword: :|, args_klass: Rundoc::CodeCommand::PipeArgs, runner_klass: Rundoc::CodeCommand::PipeRunner)
|
|
@@ -1,44 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative "../print/erb"
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
class
|
|
5
|
+
module Rundoc::CodeCommand
|
|
6
|
+
class PreErbArgs
|
|
7
|
+
attr_reader :line
|
|
8
|
+
|
|
5
9
|
def initialize(line)
|
|
6
10
|
@line = line
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class PreErbRunner
|
|
15
|
+
attr_reader :io, :contents
|
|
16
|
+
|
|
17
|
+
def initialize(user_args:, render_command:, render_result:, io:, contents: nil)
|
|
18
|
+
@line = user_args.line
|
|
7
19
|
@binding = RUNDOC_ERB_BINDINGS[RUNDOC_DEFAULT_ERB_BINDING]
|
|
8
20
|
@code = nil
|
|
9
21
|
@command = nil
|
|
10
22
|
@template = nil
|
|
11
|
-
@
|
|
12
|
-
@
|
|
13
|
-
|
|
14
|
-
@
|
|
15
|
-
@render_command = false
|
|
23
|
+
@render_command = render_command
|
|
24
|
+
@render_result = render_result
|
|
25
|
+
@io = io
|
|
26
|
+
@contents = contents.dup if contents && !contents.empty?
|
|
16
27
|
end
|
|
17
28
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@render_delegate_result = value
|
|
29
|
+
def render_command?
|
|
30
|
+
@render_command
|
|
21
31
|
end
|
|
22
32
|
|
|
23
|
-
def
|
|
24
|
-
@
|
|
33
|
+
def render_result?
|
|
34
|
+
@render_result
|
|
25
35
|
end
|
|
26
36
|
|
|
27
37
|
def code
|
|
28
38
|
@code ||= begin
|
|
29
39
|
vis = +""
|
|
30
|
-
vis +=
|
|
31
|
-
vis +=
|
|
40
|
+
vis += render_command? ? ">" : "-"
|
|
41
|
+
vis += render_result? ? ">" : "-"
|
|
32
42
|
code = [@line, @contents]
|
|
33
43
|
.compact
|
|
34
44
|
.reject(&:empty?)
|
|
35
45
|
.join("\n")
|
|
36
46
|
@template = ":::#{vis} #{code}"
|
|
37
47
|
|
|
38
|
-
puts "pre.erb: Applying ERB, template:\n#{@template}"
|
|
48
|
+
io.puts "pre.erb: Applying ERB, template:\n#{@template}"
|
|
39
49
|
result = ERB.new(@template).result(@binding)
|
|
40
|
-
puts "pre.erb: ERB result:\n#{result}"
|
|
41
|
-
puts "pre.erb: done, ready to delegate"
|
|
50
|
+
io.puts "pre.erb: ERB result:\n#{result}"
|
|
51
|
+
io.puts "pre.erb: done, ready to delegate"
|
|
42
52
|
result
|
|
43
53
|
end
|
|
44
54
|
end
|
|
@@ -60,4 +70,4 @@ class Rundoc::CodeCommand
|
|
|
60
70
|
end
|
|
61
71
|
end
|
|
62
72
|
end
|
|
63
|
-
Rundoc.register_code_command(:"pre.erb", Rundoc::CodeCommand::
|
|
73
|
+
Rundoc.register_code_command(keyword: :"pre.erb", args_klass: Rundoc::CodeCommand::PreErbArgs, runner_klass: Rundoc::CodeCommand::PreErbRunner, always_hidden: true)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "erb"
|
|
2
4
|
|
|
3
5
|
class EmptyBinding
|
|
@@ -10,14 +12,36 @@ class EmptyBinding
|
|
|
10
12
|
end
|
|
11
13
|
end
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
module Rundoc::CodeCommand
|
|
14
16
|
RUNDOC_ERB_BINDINGS = Hash.new { |h, k| h[k] = EmptyBinding.create }
|
|
15
17
|
RUNDOC_DEFAULT_ERB_BINDING = "default"
|
|
16
18
|
|
|
17
|
-
class
|
|
19
|
+
class PrintERBArgs
|
|
20
|
+
attr_reader :line, :binding_name
|
|
21
|
+
|
|
18
22
|
def initialize(line = nil, binding: RUNDOC_DEFAULT_ERB_BINDING)
|
|
19
23
|
@line = line
|
|
20
|
-
@
|
|
24
|
+
@binding_name = binding
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class PrintERBRunner
|
|
29
|
+
attr_reader :contents
|
|
30
|
+
|
|
31
|
+
def initialize(user_args:, render_command:, render_result:, io: nil, contents: nil)
|
|
32
|
+
@line = user_args.line
|
|
33
|
+
@binding = RUNDOC_ERB_BINDINGS[user_args.binding_name]
|
|
34
|
+
@render_command = render_command
|
|
35
|
+
@render_result = render_result
|
|
36
|
+
@contents = contents.dup if contents && !contents.empty?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def render_command?
|
|
40
|
+
@render_command
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def render_result?
|
|
44
|
+
@render_result
|
|
21
45
|
end
|
|
22
46
|
|
|
23
47
|
def to_md(env)
|
|
@@ -45,4 +69,4 @@ class Rundoc::CodeCommand
|
|
|
45
69
|
end
|
|
46
70
|
end
|
|
47
71
|
end
|
|
48
|
-
Rundoc.register_code_command(:"print.erb", Rundoc::CodeCommand::
|
|
72
|
+
Rundoc.register_code_command(keyword: :"print.erb", args_klass: Rundoc::CodeCommand::PrintERBArgs, runner_klass: Rundoc::CodeCommand::PrintERBRunner)
|
|
@@ -1,8 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rundoc::CodeCommand
|
|
4
|
+
class PrintTextArgs
|
|
5
|
+
attr_reader :line
|
|
6
|
+
|
|
7
|
+
def initialize(line = nil)
|
|
4
8
|
@line = line
|
|
5
9
|
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class PrintTextRunner
|
|
13
|
+
attr_reader :contents
|
|
14
|
+
|
|
15
|
+
def initialize(user_args:, render_command:, render_result:, io: nil, contents: nil)
|
|
16
|
+
@line = user_args.line
|
|
17
|
+
@render_command = render_command
|
|
18
|
+
@render_result = render_result
|
|
19
|
+
@contents = contents.dup if contents && !contents.empty?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def render_command?
|
|
23
|
+
@render_command
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def render_result?
|
|
27
|
+
@render_result
|
|
28
|
+
end
|
|
6
29
|
|
|
7
30
|
def to_md(env)
|
|
8
31
|
if render_before?
|
|
@@ -12,10 +35,6 @@ class Rundoc::CodeCommand
|
|
|
12
35
|
""
|
|
13
36
|
end
|
|
14
37
|
|
|
15
|
-
def hidden?
|
|
16
|
-
!render_result?
|
|
17
|
-
end
|
|
18
|
-
|
|
19
38
|
def call(env = {})
|
|
20
39
|
if render_before?
|
|
21
40
|
""
|
|
@@ -30,4 +49,4 @@ class Rundoc::CodeCommand
|
|
|
30
49
|
end
|
|
31
50
|
end
|
|
32
51
|
|
|
33
|
-
Rundoc.register_code_command(:"print.text", Rundoc::CodeCommand::
|
|
52
|
+
Rundoc.register_code_command(keyword: :"print.text", args_klass: Rundoc::CodeCommand::PrintTextArgs, runner_klass: Rundoc::CodeCommand::PrintTextRunner)
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Rundoc
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
module CodeCommand
|
|
5
|
+
# Wraps lines inside a fenced code block that are not rundoc commands.
|
|
6
|
+
# These are rendered as-is without executing any code.
|
|
7
|
+
#
|
|
8
|
+
# Example:
|
|
9
|
+
#
|
|
10
|
+
# ```ruby
|
|
11
|
+
# gem 'sqlite3' <- parsed as Raw
|
|
12
|
+
# :::>> $ echo "hi" <- parsed as a code command
|
|
13
|
+
# ```
|
|
14
|
+
class Raw
|
|
15
|
+
attr_reader :contents
|
|
16
|
+
|
|
17
|
+
def initialize(user_args: nil, render_command: true, render_result: true, io: nil, contents: nil)
|
|
18
|
+
@contents = contents.dup if contents && !contents.empty?
|
|
7
19
|
end
|
|
8
20
|
|
|
9
21
|
def call(env = {})
|
|
@@ -1,13 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ::Rundoc::CodeCommand
|
|
2
4
|
class RundocCommand
|
|
3
|
-
class
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# in current document
|
|
5
|
+
class RequireArgs
|
|
6
|
+
attr_reader :path
|
|
7
|
+
|
|
7
8
|
def initialize(path)
|
|
8
9
|
raise "Path must be relative (i.e. start with `.` or `..`. #{path.inspect} does not" unless path.start_with?(".")
|
|
9
10
|
@path = Pathname.new(path)
|
|
10
11
|
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class RequireRunner
|
|
15
|
+
attr_reader :io
|
|
16
|
+
|
|
17
|
+
def initialize(user_args:, render_command:, render_result:, io:, contents: nil)
|
|
18
|
+
@path = user_args.path
|
|
19
|
+
@io = io
|
|
20
|
+
@render_result = render_result
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def render_result?
|
|
24
|
+
@render_result
|
|
25
|
+
end
|
|
11
26
|
|
|
12
27
|
def to_md(env = {})
|
|
13
28
|
""
|
|
@@ -24,28 +39,21 @@ class ::Rundoc::CodeCommand
|
|
|
24
39
|
output_dir: execution_context.output_dir,
|
|
25
40
|
screenshots_dirname: execution_context.screenshots_dir,
|
|
26
41
|
with_contents_dir: execution_context.with_contents_dir
|
|
27
|
-
)
|
|
42
|
+
),
|
|
43
|
+
io: io
|
|
28
44
|
).to_md
|
|
29
45
|
|
|
30
46
|
if render_result?
|
|
31
|
-
puts "rundoc.require: Done executing #{@path.to_s.inspect}, putting contents into document"
|
|
47
|
+
io.puts "rundoc.require: Done executing #{@path.to_s.inspect}, putting contents into document"
|
|
32
48
|
env[:before] << output
|
|
33
49
|
else
|
|
34
|
-
puts "rundoc.require: Done executing #{@path.to_s.inspect}, quietly"
|
|
50
|
+
io.puts "rundoc.require: Done executing #{@path.to_s.inspect}, quietly"
|
|
35
51
|
end
|
|
36
52
|
|
|
37
53
|
""
|
|
38
54
|
end
|
|
39
|
-
|
|
40
|
-
def hidden?
|
|
41
|
-
!render_result?
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def not_hidden?
|
|
45
|
-
!hidden?
|
|
46
|
-
end
|
|
47
55
|
end
|
|
48
56
|
end
|
|
49
57
|
end
|
|
50
58
|
|
|
51
|
-
Rundoc.register_code_command(:"rundoc.require", ::Rundoc::CodeCommand::RundocCommand::
|
|
59
|
+
Rundoc.register_code_command(keyword: :"rundoc.require", args_klass: ::Rundoc::CodeCommand::RundocCommand::RequireArgs, runner_klass: ::Rundoc::CodeCommand::RundocCommand::RequireRunner)
|
|
@@ -1,8 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ::Rundoc
|
|
2
|
-
|
|
3
|
-
class
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
module CodeCommand
|
|
5
|
+
class RundocCommandArgs
|
|
6
|
+
attr_reader :code
|
|
7
|
+
|
|
8
|
+
def initialize(code = "")
|
|
9
|
+
@code = code
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class RundocCommandRunner
|
|
14
|
+
attr_reader :io, :contents
|
|
15
|
+
|
|
16
|
+
def initialize(user_args:, render_command:, render_result:, io:, contents: nil)
|
|
17
|
+
@io = io
|
|
18
|
+
@contents = contents.dup if contents && !contents.empty?
|
|
19
|
+
@contents = user_args.code + (@contents || +"")
|
|
6
20
|
end
|
|
7
21
|
|
|
8
22
|
def to_md(env = {})
|
|
@@ -10,7 +24,7 @@ module ::Rundoc
|
|
|
10
24
|
end
|
|
11
25
|
|
|
12
26
|
def call(env = {})
|
|
13
|
-
puts "Running: #{contents}"
|
|
27
|
+
io.puts "Running: #{contents}"
|
|
14
28
|
eval(contents) # rubocop:disable Security/Eval
|
|
15
29
|
""
|
|
16
30
|
end
|
|
@@ -18,8 +32,7 @@ module ::Rundoc
|
|
|
18
32
|
end
|
|
19
33
|
end
|
|
20
34
|
|
|
21
|
-
Rundoc.register_code_command(:rundoc,
|
|
22
|
-
Rundoc.register_code_command(:"rundoc.configure",
|
|
35
|
+
Rundoc.register_code_command(keyword: :rundoc, args_klass: Rundoc::CodeCommand::RundocCommandArgs, runner_klass: Rundoc::CodeCommand::RundocCommandRunner)
|
|
36
|
+
Rundoc.register_code_command(keyword: :"rundoc.configure", args_klass: Rundoc::CodeCommand::RundocCommandArgs, runner_klass: Rundoc::CodeCommand::RundocCommandRunner)
|
|
23
37
|
|
|
24
|
-
require "rundoc/code_command/rundoc/depend_on"
|
|
25
38
|
require "rundoc/code_command/rundoc/require"
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "capybara"
|
|
2
4
|
|
|
3
5
|
Capybara::Selenium::Driver.load_selenium
|
|
@@ -6,7 +8,8 @@ class Rundoc::CodeCommand::Website
|
|
|
6
8
|
class Driver
|
|
7
9
|
attr_reader :session
|
|
8
10
|
|
|
9
|
-
def initialize(name:, url:, width: 1024, height: 720, visible: false)
|
|
11
|
+
def initialize(name:, url:, width: 1024, height: 720, visible: false, io: $stdout, read_timeout: 60)
|
|
12
|
+
@io = io
|
|
10
13
|
browser_options = ::Selenium::WebDriver::Chrome::Options.new
|
|
11
14
|
browser_options.args << "--headless" unless visible
|
|
12
15
|
browser_options.args << "--disable-gpu" if Gem.win_platform?
|
|
@@ -15,7 +18,10 @@ class Rundoc::CodeCommand::Website
|
|
|
15
18
|
@width = width
|
|
16
19
|
@height = height
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
client = Selenium::WebDriver::Remote::Http::Default.new
|
|
22
|
+
client.read_timeout = read_timeout
|
|
23
|
+
|
|
24
|
+
@driver = Capybara::Selenium::Driver.new(nil, browser: :chrome, options: browser_options, http_client: client)
|
|
19
25
|
driver_name = :"rundoc_driver_#{name}"
|
|
20
26
|
Capybara.register_driver(driver_name) do |app|
|
|
21
27
|
@driver
|
|
@@ -24,8 +30,20 @@ class Rundoc::CodeCommand::Website
|
|
|
24
30
|
@session = Capybara::Session.new(driver_name)
|
|
25
31
|
end
|
|
26
32
|
|
|
27
|
-
def visit(url)
|
|
28
|
-
|
|
33
|
+
def visit(url, max_attempts: 3, delay: 1)
|
|
34
|
+
attempts = 0
|
|
35
|
+
begin
|
|
36
|
+
@session.visit(url)
|
|
37
|
+
rescue ::Net::ReadTimeout => e
|
|
38
|
+
attempts += 1
|
|
39
|
+
if attempts > max_attempts
|
|
40
|
+
raise e
|
|
41
|
+
else
|
|
42
|
+
@io.puts "Error visiting url (#{attempts}/#{max_attempts}) `#{url}`:\n#{e}"
|
|
43
|
+
sleep delay
|
|
44
|
+
retry
|
|
45
|
+
end
|
|
46
|
+
end
|
|
29
47
|
end
|
|
30
48
|
|
|
31
49
|
def timestamp
|
|
@@ -54,7 +72,7 @@ class Rundoc::CodeCommand::Website
|
|
|
54
72
|
msg = +""
|
|
55
73
|
msg << "Error running code #{code.inspect} at #{current_url.inspect}\n"
|
|
56
74
|
msg << "saving a screenshot to: `tmp/error.png`"
|
|
57
|
-
puts msg
|
|
75
|
+
@io.puts msg
|
|
58
76
|
error_path = env[:context].screenshots_dir.join("error.png")
|
|
59
77
|
session.save_screenshot(error_path)
|
|
60
78
|
raise e
|
|
@@ -66,13 +84,13 @@ class Rundoc::CodeCommand::Website
|
|
|
66
84
|
file_name = self.class.next_screenshot_name
|
|
67
85
|
file_path = screenshots_dir.join(file_name)
|
|
68
86
|
session.save_screenshot(file_path)
|
|
69
|
-
puts "Screenshot saved to #{file_path}"
|
|
87
|
+
@io.puts "Screenshot saved to #{file_path}"
|
|
70
88
|
|
|
71
89
|
return file_path unless upload
|
|
72
90
|
|
|
73
91
|
case upload
|
|
74
92
|
when "s3", "aws"
|
|
75
|
-
puts "Uploading screenshot to S3"
|
|
93
|
+
@io.puts "Uploading screenshot to S3"
|
|
76
94
|
require "aws-sdk-s3"
|
|
77
95
|
ENV.fetch("AWS_ACCESS_KEY_ID")
|
|
78
96
|
s3 = Aws::S3::Resource.new(region: ENV.fetch("AWS_REGION"))
|
|
@@ -1,8 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class Rundoc::CodeCommand::Website
|
|
2
|
-
class
|
|
4
|
+
class NavigateArgs
|
|
5
|
+
attr_reader :name
|
|
6
|
+
|
|
3
7
|
def initialize(name:)
|
|
4
8
|
@name = name
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class NavigateRunner
|
|
13
|
+
attr_reader :io, :contents
|
|
14
|
+
|
|
15
|
+
def initialize(user_args:, render_command:, render_result:, io:, contents: nil)
|
|
16
|
+
@name = user_args.name
|
|
5
17
|
@driver = nil
|
|
18
|
+
@io = io
|
|
19
|
+
@contents = contents.dup if contents && !contents.empty?
|
|
6
20
|
end
|
|
7
21
|
|
|
8
22
|
def driver
|
|
@@ -14,20 +28,12 @@ class Rundoc::CodeCommand::Website
|
|
|
14
28
|
end
|
|
15
29
|
|
|
16
30
|
def call(env = {})
|
|
17
|
-
puts "website.navigate [#{@name}]: #{contents}"
|
|
31
|
+
io.puts "website.navigate [#{@name}]: #{contents}"
|
|
18
32
|
driver.safe_eval(contents, env)
|
|
19
33
|
""
|
|
20
34
|
end
|
|
21
|
-
|
|
22
|
-
def hidden?
|
|
23
|
-
true
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def not_hidden?
|
|
27
|
-
!hidden?
|
|
28
|
-
end
|
|
29
35
|
end
|
|
30
36
|
end
|
|
31
37
|
|
|
32
|
-
Rundoc.register_code_command(:"website.nav", Rundoc::CodeCommand::Website::
|
|
33
|
-
Rundoc.register_code_command(:"website.navigate", Rundoc::CodeCommand::Website::
|
|
38
|
+
Rundoc.register_code_command(keyword: :"website.nav", args_klass: Rundoc::CodeCommand::Website::NavigateArgs, runner_klass: Rundoc::CodeCommand::Website::NavigateRunner)
|
|
39
|
+
Rundoc.register_code_command(keyword: :"website.navigate", args_klass: Rundoc::CodeCommand::Website::NavigateArgs, runner_klass: Rundoc::CodeCommand::Website::NavigateRunner)
|