rundoc 1.1.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/check_changelog.yml +16 -7
- data/.github/workflows/ci.yml +48 -0
- data/.standard.yml +6 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +1 -1
- data/README.md +98 -5
- data/Rakefile +9 -10
- data/lib/rundoc/cli.rb +15 -17
- data/lib/rundoc/code_command/background/log/clear.rb +1 -1
- data/lib/rundoc/code_command/background/log/read.rb +1 -1
- data/lib/rundoc/code_command/background/process_spawn.rb +8 -9
- data/lib/rundoc/code_command/background/start.rb +7 -7
- data/lib/rundoc/code_command/background/stop.rb +1 -1
- data/lib/rundoc/code_command/background/wait.rb +2 -2
- data/lib/rundoc/code_command/background.rb +6 -6
- data/lib/rundoc/code_command/bash/cd.rb +6 -7
- data/lib/rundoc/code_command/bash.rb +12 -13
- data/lib/rundoc/code_command/file_command/append.rb +12 -16
- data/lib/rundoc/code_command/file_command/remove.rb +6 -9
- data/lib/rundoc/code_command/no_such_command.rb +0 -1
- data/lib/rundoc/code_command/pipe.rb +2 -5
- data/lib/rundoc/code_command/print/erb.rb +48 -0
- data/lib/rundoc/code_command/print/text.rb +33 -0
- data/lib/rundoc/code_command/raw.rb +1 -1
- data/lib/rundoc/code_command/rundoc/depend_on.rb +0 -1
- data/lib/rundoc/code_command/rundoc/require.rb +2 -3
- data/lib/rundoc/code_command/rundoc_command.rb +3 -4
- data/lib/rundoc/code_command/website/driver.rb +17 -17
- data/lib/rundoc/code_command/website/navigate.rb +2 -2
- data/lib/rundoc/code_command/website/screenshot.rb +1 -1
- data/lib/rundoc/code_command/website/visit.rb +4 -5
- data/lib/rundoc/code_command/website.rb +4 -4
- data/lib/rundoc/code_command/write.rb +10 -11
- data/lib/rundoc/code_command.rb +28 -17
- data/lib/rundoc/code_section.rb +42 -25
- data/lib/rundoc/parser.rb +17 -19
- data/lib/rundoc/peg_parser.rb +57 -59
- data/lib/rundoc/version.rb +1 -1
- data/lib/rundoc.rb +10 -14
- data/rundoc.gemspec +19 -21
- data/test/fixtures/rails_4/rundoc.md +100 -33
- data/test/fixtures/rails_5/rundoc.md +77 -14
- data/test/fixtures/rails_6/rundoc.md +231 -167
- data/test/fixtures/rails_7/rundoc.md +477 -0
- data/test/integration/print_test.rb +194 -0
- data/test/rundoc/code_commands/append_file_test.rb +5 -8
- data/test/rundoc/code_commands/background_test.rb +3 -6
- data/test/rundoc/code_commands/bash_test.rb +12 -7
- data/test/rundoc/code_commands/pipe_test.rb +9 -9
- data/test/rundoc/code_commands/print_test.rb +94 -0
- data/test/rundoc/code_commands/remove_contents_test.rb +4 -5
- data/test/rundoc/code_section_test.rb +50 -56
- data/test/rundoc/parser_test.rb +28 -61
- data/test/rundoc/peg_parser_test.rb +49 -53
- data/test/rundoc/regex_test.rb +120 -127
- data/test/rundoc/test_parse_java.rb +1 -3
- data/test/test_helper.rb +4 -6
- metadata +39 -42
- data/.travis.yml +0 -8
- data/lib/rundoc/code_command/repl.rb +0 -37
@@ -3,11 +3,9 @@ class Rundoc::CodeCommand::FileCommand
|
|
3
3
|
include FileUtil
|
4
4
|
|
5
5
|
def initialize(filename)
|
6
|
-
@filename, line = filename.split(
|
7
|
-
if line
|
8
|
-
|
9
|
-
else
|
10
|
-
@line_number = nil
|
6
|
+
@filename, line = filename.split("#")
|
7
|
+
@line_number = if line
|
8
|
+
Integer(line)
|
11
9
|
end
|
12
10
|
end
|
13
11
|
|
@@ -15,17 +13,18 @@ class Rundoc::CodeCommand::FileCommand
|
|
15
13
|
return unless render_command?
|
16
14
|
|
17
15
|
raise "must call write in its own code section" unless env[:commands].empty?
|
18
|
-
|
19
|
-
if @line_number
|
20
|
-
|
16
|
+
|
17
|
+
env[:before] << if @line_number
|
18
|
+
"In file `#{filename}`, on line #{@line_number} add:"
|
21
19
|
else
|
22
|
-
|
20
|
+
"At the end of `#{filename}` add:"
|
23
21
|
end
|
22
|
+
env[:before] << NEWLINE
|
24
23
|
nil
|
25
24
|
end
|
26
25
|
|
27
26
|
def last_char_of(string)
|
28
|
-
string[-1,1]
|
27
|
+
string[-1, 1]
|
29
28
|
end
|
30
29
|
|
31
30
|
def ends_in_newline?(string)
|
@@ -53,7 +52,7 @@ class Rundoc::CodeCommand::FileCommand
|
|
53
52
|
end
|
54
53
|
result << line
|
55
54
|
end
|
56
|
-
|
55
|
+
result.flatten.join("")
|
57
56
|
end
|
58
57
|
|
59
58
|
def call(env = {})
|
@@ -67,13 +66,10 @@ class Rundoc::CodeCommand::FileCommand
|
|
67
66
|
doc = concat_with_newline(doc, contents)
|
68
67
|
end
|
69
68
|
|
70
|
-
File.
|
71
|
-
f.write(doc)
|
72
|
-
end
|
69
|
+
File.write(filename, doc)
|
73
70
|
contents
|
74
71
|
end
|
75
72
|
end
|
76
73
|
end
|
77
74
|
|
78
|
-
|
79
|
-
Rundoc.register_code_command(:'file.append', Rundoc::CodeCommand::FileCommand::Append)
|
75
|
+
Rundoc.register_code_command(:"file.append", Rundoc::CodeCommand::FileCommand::Append)
|
@@ -8,8 +8,8 @@ class Rundoc::CodeCommand::FileCommand
|
|
8
8
|
|
9
9
|
def to_md(env)
|
10
10
|
raise "must call write in its own code section" unless env[:commands].empty?
|
11
|
-
|
12
|
-
env[:before]
|
11
|
+
env[:before] << "In file `#{filename}` remove:"
|
12
|
+
env[:before] << NEWLINE
|
13
13
|
nil
|
14
14
|
end
|
15
15
|
|
@@ -18,16 +18,13 @@ class Rundoc::CodeCommand::FileCommand
|
|
18
18
|
raise "#{filename} does not exist" unless File.exist?(filename)
|
19
19
|
|
20
20
|
regex = /^\s*#{Regexp.quote(contents)}/
|
21
|
-
doc
|
22
|
-
doc.sub!(regex,
|
21
|
+
doc = File.read(filename)
|
22
|
+
doc.sub!(regex, "")
|
23
23
|
|
24
|
-
File.
|
25
|
-
f.write(doc)
|
26
|
-
end
|
24
|
+
File.write(filename, doc)
|
27
25
|
contents
|
28
26
|
end
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
32
|
-
|
33
|
-
Rundoc.register_code_command(:'file.remove', Rundoc::CodeCommand::FileCommand::Remove)
|
30
|
+
Rundoc.register_code_command(:"file.remove", Rundoc::CodeCommand::FileCommand::Remove)
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module Rundoc
|
2
2
|
class CodeCommand
|
3
3
|
class Pipe < Rundoc::CodeCommand
|
4
|
-
|
5
4
|
# ::: ls
|
6
5
|
# ::: | tail -n 2
|
7
6
|
# => "test\ntmp.file\n"
|
@@ -33,7 +32,7 @@ module Rundoc
|
|
33
32
|
actual = actual.first if actual.is_a?(Array)
|
34
33
|
|
35
34
|
actual = Rundoc::CodeCommand::Bash.new(code) if actual.is_a?(Rundoc::CodeCommand::NoSuchCommand)
|
36
|
-
|
35
|
+
actual
|
37
36
|
|
38
37
|
# Since `| tail -n 2` does not start with a `$` assume any "naked" commands
|
39
38
|
# are bash
|
@@ -44,7 +43,5 @@ module Rundoc
|
|
44
43
|
end
|
45
44
|
end
|
46
45
|
|
47
|
-
|
48
46
|
Rundoc.register_code_command(:pipe, Rundoc::CodeCommand::Pipe)
|
49
|
-
Rundoc.register_code_command(:|,
|
50
|
-
|
47
|
+
Rundoc.register_code_command(:|, Rundoc::CodeCommand::Pipe)
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "erb"
|
2
|
+
|
3
|
+
class EmptyBinding
|
4
|
+
def self.create
|
5
|
+
new.empty_binding
|
6
|
+
end
|
7
|
+
|
8
|
+
def empty_binding
|
9
|
+
binding
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
RUNDOC_ERB_BINDINGS = Hash.new { |h, k| h[k] = EmptyBinding.create }
|
14
|
+
|
15
|
+
class Rundoc::CodeCommand
|
16
|
+
class PrintERB < Rundoc::CodeCommand
|
17
|
+
def initialize(line = nil, binding: "default")
|
18
|
+
@line = line
|
19
|
+
@binding = RUNDOC_ERB_BINDINGS[binding]
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_md(env)
|
23
|
+
if render_before?
|
24
|
+
env[:before] << render
|
25
|
+
end
|
26
|
+
|
27
|
+
""
|
28
|
+
end
|
29
|
+
|
30
|
+
def render
|
31
|
+
@render ||= ERB.new([@line, contents].compact.join("\n")).result(@binding)
|
32
|
+
end
|
33
|
+
|
34
|
+
def call(erb = {})
|
35
|
+
if render_before?
|
36
|
+
""
|
37
|
+
else
|
38
|
+
render
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def render_before?
|
43
|
+
!render_command? && render_result?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Rundoc.register_code_command(:"print.erb", Rundoc::CodeCommand::PrintERB)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Rundoc::CodeCommand
|
2
|
+
class PrintText < Rundoc::CodeCommand
|
3
|
+
def initialize(line)
|
4
|
+
@line = line
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_md(env)
|
8
|
+
if render_before?
|
9
|
+
env[:before] << [@line, contents].compact.join("\n")
|
10
|
+
end
|
11
|
+
|
12
|
+
""
|
13
|
+
end
|
14
|
+
|
15
|
+
def hidden?
|
16
|
+
!render_result?
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env = {})
|
20
|
+
if render_before?
|
21
|
+
""
|
22
|
+
else
|
23
|
+
[@line, contents].compact.join("\n")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def render_before?
|
28
|
+
!render_command? && render_result?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Rundoc.register_code_command(:"print.text", Rundoc::CodeCommand::PrintText)
|
@@ -1,7 +1,6 @@
|
|
1
1
|
class ::Rundoc::CodeCommand
|
2
2
|
class RundocCommand
|
3
3
|
class Require < ::Rundoc::CodeCommand
|
4
|
-
|
5
4
|
# Pass in the relative path of another rundoc document in order to
|
6
5
|
# run all of it's commands. Resulting contents will be displayed
|
7
6
|
# in current document
|
@@ -15,7 +14,7 @@ class ::Rundoc::CodeCommand
|
|
15
14
|
end
|
16
15
|
|
17
16
|
def call(env = {})
|
18
|
-
env[:replace] ||=
|
17
|
+
env[:replace] ||= +""
|
19
18
|
current_path = Pathname.new(env[:document_path]).dirname
|
20
19
|
document_path = @path.expand_path(current_path)
|
21
20
|
|
@@ -24,7 +23,7 @@ class ::Rundoc::CodeCommand
|
|
24
23
|
puts "rundoc.require: Done executing #{@path.to_s.inspect}, putting contents into document"
|
25
24
|
|
26
25
|
env[:replace] << output
|
27
|
-
|
26
|
+
""
|
28
27
|
end
|
29
28
|
|
30
29
|
def hidden?
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module ::Rundoc
|
2
2
|
class CodeCommand
|
3
3
|
class ::RundocCommand < ::Rundoc::CodeCommand
|
4
|
-
|
5
4
|
def initialize(contents = "")
|
6
5
|
@contents = contents
|
7
6
|
end
|
@@ -12,7 +11,7 @@ module ::Rundoc
|
|
12
11
|
|
13
12
|
def call(env = {})
|
14
13
|
puts "Running: #{contents}"
|
15
|
-
eval(contents)
|
14
|
+
eval(contents) # rubocop:disable Security/Eval
|
16
15
|
""
|
17
16
|
end
|
18
17
|
end
|
@@ -22,5 +21,5 @@ end
|
|
22
21
|
Rundoc.register_code_command(:rundoc, RundocCommand)
|
23
22
|
Rundoc.register_code_command(:"rundoc.configure", RundocCommand)
|
24
23
|
|
25
|
-
require
|
26
|
-
require
|
24
|
+
require "rundoc/code_command/rundoc/depend_on"
|
25
|
+
require "rundoc/code_command/rundoc/require"
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "capybara"
|
2
2
|
|
3
3
|
Capybara::Selenium::Driver.load_selenium
|
4
4
|
|
@@ -6,17 +6,17 @@ class Rundoc::CodeCommand::Website
|
|
6
6
|
class Driver
|
7
7
|
attr_reader :session
|
8
8
|
|
9
|
-
def initialize(name
|
9
|
+
def initialize(name:, url:, width: 1024, height: 720, visible: false)
|
10
10
|
browser_options = ::Selenium::WebDriver::Chrome::Options.new
|
11
|
-
browser_options.args <<
|
12
|
-
browser_options.args <<
|
13
|
-
browser_options.args <<
|
11
|
+
browser_options.args << "--headless" unless visible
|
12
|
+
browser_options.args << "--disable-gpu" if Gem.win_platform?
|
13
|
+
browser_options.args << "--hide-scrollbars"
|
14
14
|
# browser_options.args << "--window-size=#{width},#{height}"
|
15
15
|
@width = width
|
16
16
|
@height = height
|
17
17
|
|
18
18
|
@driver = Capybara::Selenium::Driver.new(nil, browser: :chrome, options: browser_options)
|
19
|
-
driver_name = "rundoc_driver_#{name}"
|
19
|
+
driver_name = :"rundoc_driver_#{name}"
|
20
20
|
Capybara.register_driver(driver_name) do |app|
|
21
21
|
@driver
|
22
22
|
end
|
@@ -44,14 +44,14 @@ class Rundoc::CodeCommand::Website
|
|
44
44
|
session.reset_session!
|
45
45
|
end
|
46
46
|
|
47
|
-
|
48
|
-
|
47
|
+
class << self
|
48
|
+
attr_reader :tasks
|
49
49
|
end
|
50
50
|
|
51
51
|
def safe_eval(code)
|
52
52
|
@driver.send(:eval, code)
|
53
53
|
rescue => e
|
54
|
-
msg =
|
54
|
+
msg = +""
|
55
55
|
msg << "Error running code #{code.inspect} at #{current_url.inspect}\n"
|
56
56
|
msg << "saving a screenshot to: `tmp/error.png`"
|
57
57
|
puts msg
|
@@ -69,19 +69,19 @@ class Rundoc::CodeCommand::Website
|
|
69
69
|
return file_path unless upload
|
70
70
|
|
71
71
|
case upload
|
72
|
-
when
|
72
|
+
when "s3", "aws"
|
73
73
|
puts "Uploading screenshot to S3"
|
74
|
-
require
|
75
|
-
ENV.fetch(
|
76
|
-
s3 = Aws::S3::Resource.new(region: ENV.fetch(
|
74
|
+
require "aws-sdk-s3"
|
75
|
+
ENV.fetch("AWS_ACCESS_KEY_ID")
|
76
|
+
s3 = Aws::S3::Resource.new(region: ENV.fetch("AWS_REGION"))
|
77
77
|
|
78
78
|
key = "#{timestamp}/#{file_name}"
|
79
|
-
obj = s3.bucket(ENV.fetch(
|
79
|
+
obj = s3.bucket(ENV.fetch("AWS_BUCKET_NAME")).object(key)
|
80
80
|
obj.upload_file(file_path)
|
81
81
|
|
82
82
|
obj.client.put_object_acl(
|
83
|
-
acl:
|
84
|
-
bucket: ENV.fetch(
|
83
|
+
acl: "public-read",
|
84
|
+
bucket: ENV.fetch("AWS_BUCKET_NAME"),
|
85
85
|
key: key
|
86
86
|
)
|
87
87
|
|
@@ -105,7 +105,7 @@ class Rundoc::CodeCommand::Website
|
|
105
105
|
def self.next_screenshot_name
|
106
106
|
@count ||= 0
|
107
107
|
@count += 1
|
108
|
-
|
108
|
+
"screenshot_#{@count}.png"
|
109
109
|
end
|
110
110
|
end
|
111
111
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
class Rundoc::CodeCommand::Website
|
2
2
|
class Visit < Rundoc::CodeCommand
|
3
|
-
def initialize(name
|
3
|
+
def initialize(name:, url: nil, scroll: nil, height: 720, width: 1024, visible: false)
|
4
4
|
@name = name
|
5
|
-
@url
|
5
|
+
@url = url
|
6
6
|
@scroll = scroll
|
7
7
|
@driver = Driver.new(
|
8
8
|
name: name,
|
@@ -19,7 +19,7 @@ class Rundoc::CodeCommand::Website
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def call(env = {})
|
22
|
-
message =
|
22
|
+
message = +"Visting: #{@url}"
|
23
23
|
message << "and executing:\n#{contents}" unless contents.nil? || contents.empty?
|
24
24
|
|
25
25
|
puts message
|
@@ -27,11 +27,10 @@ class Rundoc::CodeCommand::Website
|
|
27
27
|
@driver.visit(@url) if @url
|
28
28
|
@driver.scroll(@scroll) if @scroll
|
29
29
|
|
30
|
-
|
31
30
|
return "" if contents.nil? || contents.empty?
|
32
31
|
@driver.safe_eval(contents)
|
33
32
|
|
34
|
-
|
33
|
+
""
|
35
34
|
end
|
36
35
|
|
37
36
|
def hidden?
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class Rundoc::CodeCommand::Website
|
2
2
|
end
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
4
|
+
require "rundoc/code_command/website/driver"
|
5
|
+
require "rundoc/code_command/website/screenshot"
|
6
|
+
require "rundoc/code_command/website/visit"
|
7
|
+
require "rundoc/code_command/website/navigate"
|
@@ -23,27 +23,26 @@ module Rundoc
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def to_md(env)
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
if render_command?
|
27
|
+
raise "must call write in its own code section" unless env[:commands].empty?
|
28
|
+
env[:before] << "In file `#{filename}` write:"
|
29
|
+
env[:before] << NEWLINE
|
30
|
+
end
|
29
31
|
nil
|
30
32
|
end
|
31
33
|
|
32
34
|
def call(env = {})
|
33
35
|
puts "Writing to: '#{filename}'"
|
34
36
|
mkdir_p
|
35
|
-
File.
|
36
|
-
f.write(contents)
|
37
|
-
end
|
37
|
+
File.write(filename, contents)
|
38
38
|
contents
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
Rundoc.register_code_command(:write, Rundoc::CodeCommand::Write)
|
45
|
+
Rundoc.register_code_command(:"file.write", Rundoc::CodeCommand::Write)
|
44
46
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
require 'rundoc/code_command/file_command/append'
|
49
|
-
require 'rundoc/code_command/file_command/remove'
|
47
|
+
require "rundoc/code_command/file_command/append"
|
48
|
+
require "rundoc/code_command/file_command/remove"
|
data/lib/rundoc/code_command.rb
CHANGED
@@ -2,12 +2,23 @@ module Rundoc
|
|
2
2
|
# Generic CodeCommand class to be inherited
|
3
3
|
#
|
4
4
|
class CodeCommand
|
5
|
+
# Newlines are stripped and re-added, this tells the project that
|
6
|
+
# we're intentionally wanting an extra newline
|
7
|
+
NEWLINE = Object.new
|
8
|
+
def NEWLINE.to_s
|
9
|
+
""
|
10
|
+
end
|
11
|
+
|
12
|
+
def NEWLINE.empty?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
5
16
|
attr_accessor :render_result, :render_command,
|
6
|
-
|
7
|
-
|
17
|
+
:command, :contents, :keyword,
|
18
|
+
:original_args
|
8
19
|
|
9
|
-
|
10
|
-
|
20
|
+
alias_method :render_result?, :render_result
|
21
|
+
alias_method :render_command?, :render_command
|
11
22
|
|
12
23
|
def initialize(*args)
|
13
24
|
end
|
@@ -24,28 +35,28 @@ module Rundoc
|
|
24
35
|
@contents ||= ""
|
25
36
|
@contents << contents
|
26
37
|
end
|
27
|
-
|
38
|
+
alias_method :<<, :push
|
28
39
|
|
29
40
|
# Executes command to build project
|
30
41
|
# Is expected to return the result of the command
|
31
42
|
def call(env = {})
|
32
|
-
raise "not implemented on #{
|
43
|
+
raise "not implemented on #{inspect}"
|
33
44
|
end
|
34
45
|
|
35
46
|
# the output of the command, i.e. `$ cat foo.txt`
|
36
47
|
def to_md(env = {})
|
37
|
-
raise "not implemented on #{
|
48
|
+
raise "not implemented on #{inspect}"
|
38
49
|
end
|
39
50
|
end
|
40
51
|
end
|
41
52
|
|
42
|
-
|
43
|
-
require
|
44
|
-
require
|
45
|
-
require
|
46
|
-
require
|
47
|
-
require
|
48
|
-
require
|
49
|
-
require
|
50
|
-
require
|
51
|
-
require
|
53
|
+
require "rundoc/code_command/bash"
|
54
|
+
require "rundoc/code_command/pipe"
|
55
|
+
require "rundoc/code_command/write"
|
56
|
+
require "rundoc/code_command/rundoc_command"
|
57
|
+
require "rundoc/code_command/no_such_command"
|
58
|
+
require "rundoc/code_command/raw"
|
59
|
+
require "rundoc/code_command/background"
|
60
|
+
require "rundoc/code_command/website"
|
61
|
+
require "rundoc/code_command/print/text"
|
62
|
+
require "rundoc/code_command/print/erb"
|