rundoc 5.0.0 → 6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85fbfa9f84b4e98eb72d487721e7f35001f7820404e30a138de4b4505d788f53
4
- data.tar.gz: 1ff08ba1cdd468b190fc54eef124b7ab434d700ffe87ba2b4c8e676d03fb515e
3
+ metadata.gz: 498d5109daa7abb733fc5c27cf583bb1c84cc7b92bbccaaf9e005a51a6ec0555
4
+ data.tar.gz: fc72016241033a945486b5fa893b3f2cad0c8b3c7a2c44eab3f964adb7308f9e
5
5
  SHA512:
6
- metadata.gz: 8e87c610bcd8c8589407e68b503eee0578a1dc23b959baa138258185f7d08e6daa3ee17097e4b66b02fea896ee9e5be2bde936e2a601a2b18d1b1a2fc24b36a2
7
- data.tar.gz: 9c9f4d7446beaedc74d61354794327cd30f372f7e5e16500678247f7bc549bb7b04992d0875c06c1fa06ae894a36c35902e07930cf55ab4e9fc01bb8b29c44d3
6
+ metadata.gz: 5af654672de3e6051ec46377052c55b55207ce13eb29cf977d1cf793291e953d28e842fd79b0f0972f8eb83a98c6671c1834a5ac75f20da795eb686a99a02c52
7
+ data.tar.gz: b2acdf08759aa0c6f73410a00a4ad387da198f1f4497228c69d27dd799799e12107cc421c8f3fd5242f9517dbfd2565e71b56e2b3a6ffd77ffd879906c902d65
@@ -13,6 +13,7 @@ jobs:
13
13
  ruby:
14
14
  - 3.2
15
15
  - 3.3
16
+ - 3.4
16
17
  - "4.0"
17
18
  - head
18
19
  steps:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## HEAD
2
2
 
3
+ ## 6.0.0
4
+
5
+ - Added: `rundoc.ensure_later(dir: :cwd)` command to register cleanup blocks that run on every build (success and failure). Useful for guaranteed resource teardown (e.g., destroying Heroku apps). Multiple blocks execute in order; failures in one block do not stop the rest.
6
+ - Changed: The ruby context in `rundoc` and `rundoc.configure` commands is now the same binding as the default ERB evaluation. This means that methods can be defined in one and used in both.
7
+
3
8
  ## 5.0.0
4
9
 
5
10
  - Added comment syntax. Use an octothorpe (`#`) after the visibility markers to comment out any commands and make them a no-op.
data/README.md CHANGED
@@ -98,6 +98,8 @@ This will generate a project folder with your project in it, and a markdown `REA
98
98
  - [website.screenshot](#screenshots)
99
99
  - Configure RunDOC
100
100
  - [rundoc.configure](#configure)
101
+ - [rundoc.ensure_later](#ensure_later)
102
+ - [rundoc](#configure) an alias for `rundoc.configure`
101
103
  - Import and compose documents
102
104
  - [rundoc.require](#compose-multiple-rundoc-documents)
103
105
 
@@ -616,7 +618,7 @@ If you need to specify project specific environment variables create a file call
616
618
 
617
619
  ## Configure
618
620
 
619
- You can configure your docs in your docs use the `RunDOC` command
621
+ You can configure your docs in your docs use the `RunDOC` command via `rundoc.configure` (or alias `rundoc`):
620
622
 
621
623
  ```
622
624
  :::-- rundoc.configure
@@ -624,6 +626,30 @@ You can configure your docs in your docs use the `RunDOC` command
624
626
 
625
627
  Note: Make sure you run this as a hidden command (with `-`).
626
628
 
629
+ This will give you a Ruby codeblock that executes and gives you access to `Rundoc.configure do |config|` to configure things about your build (such as modifying your markdown document after successful builds).
630
+
631
+ **Define and Re-use logic**
632
+
633
+ Since it's **just ruby** :tm: you can also use it to define shared logic that can be re-used in ERB templates. For example:
634
+
635
+ ```
636
+ :::-- rundoc
637
+ def run!(command, quiet: false, error_on_fail: true)
638
+ puts "Running `#{command}`" unless quiet
639
+ output = `#{command}`
640
+ puts "Command `#{command}` output:\n#{output}" unless quiet
641
+ if error_on_fail && !$?.success?
642
+ raise "Error running #{command}. Output:\n#{output}"
643
+ end
644
+ output
645
+ end
646
+ ```
647
+
648
+ ```
649
+ :::-> print.erb
650
+ Hello <%= run!("heroku whoami") %>!
651
+ ```
652
+
627
653
  **After Build**
628
654
 
629
655
  This will eval any code you put under that line (in Ruby) when the build was successful but before the contents are finalized on disk. If you want to run some code after you're done building your docs you could use `Rundoc.configure` block and call the `after_build` method like this:
@@ -658,6 +684,41 @@ Sometimes sensitive info like usernames, email addresses, or passwords may be in
658
684
 
659
685
  This command `filter_sensitive` can be called multiple times with different values. Since the config is in Ruby you could iterate over an array of sensitive data
660
686
 
687
+ ## Ensure Later
688
+
689
+ Run a script on EVERY build (success and failure). Used to guarantee resources are cleaned up.
690
+
691
+ - Arguments
692
+ - `dir:` The directory where the script will be run. Must be one of these values:
693
+ - `:cwd` The directory where the command is first invoked. If this directory does not exist when the `rundoc.ensure_later` is invoked, it will raise an error.
694
+ - `:rundoc_root` The tmp directory where the script is being executed. Useful if the directory where the `rundoc.ensure_later` is defined, is deleted by the rundoc script.
695
+
696
+ For example:
697
+
698
+ ```
699
+ :::>> $ heroku create
700
+ :::-- rundoc
701
+ @app_name = run!("heroku info --json | jq -r '.app.name'").strip
702
+
703
+ def app_name
704
+ @app_name
705
+ end
706
+ ```
707
+
708
+ ```ruby
709
+ :::-- rundoc.ensure_later(dir: :cwd)
710
+ if run!("heroku apps").include?(app_name)
711
+ puts "Cleaning up web app #{app_name}"
712
+ run!("heroku apps:destroy #{app_name} --confirm #{app_name}")
713
+ else
714
+ puts "App `#{app_name}` already cleaned, nothing to do"
715
+ end
716
+ ```
717
+
718
+ The output of this will not be included in the document. Multiple ensure blocks can be defined and will execute in the order of their definition. Since they'll be executed EVERY time, logic must handle both success and failure cases. This execution occurs before the temp directory is removed, and before any background task shutdown ensure blocks are triggered.
719
+
720
+ If a build is successful, but an `ensure_later` block fails, the build will be considered a failure. A failure in one block will not stop the rest from executing.
721
+
661
722
  ## Writing a new command
662
723
 
663
724
  > Note: This is an advanced topic and this interface is unstable.
data/lib/rundoc/cli.rb CHANGED
@@ -134,6 +134,9 @@ module Rundoc
134
134
  end
135
135
 
136
136
  def call
137
+ build_success = false
138
+ ensure_later_errors = []
139
+
137
140
  io.puts "## Running your docs"
138
141
  load_dotenv
139
142
  check_directories_empty!
@@ -165,7 +168,11 @@ module Rundoc
165
168
  io: io
166
169
  )
167
170
  output = begin
168
- parser.to_md
171
+ begin
172
+ parser.to_md
173
+ ensure
174
+ ensure_later_errors = Rundoc.run_ensure_later(io: io)
175
+ end
169
176
  rescue StandardError, SignalException => e
170
177
  io.puts "Received exception: #{e.inspect}, cleaning up before re-raise"
171
178
  on_fail
@@ -174,6 +181,8 @@ module Rundoc
174
181
 
175
182
  on_success(output)
176
183
  end
184
+
185
+ build_success = true
177
186
  ensure
178
187
  # Stop any hanging background tasks
179
188
  Rundoc::CodeCommand::Background::ProcessSpawn.tasks.each do |name, task|
@@ -189,6 +198,10 @@ module Rundoc
189
198
  description: "tmp working directory"
190
199
  )
191
200
  end
201
+
202
+ if ensure_later_errors.any? && build_success
203
+ raise ensure_later_errors.first
204
+ end
192
205
  end
193
206
 
194
207
  private def clean_dir(dir:, description:)
@@ -7,25 +7,19 @@ class Rundoc::CodeCommand::BashRunner
7
7
  @line = line
8
8
  end
9
9
 
10
- # Ignore duplicate chdir warnings "warning: conflicting chdir during another chdir block"
11
- def supress_chdir_warning
12
- old_stderr = $stderr
13
- capture_stderr = StringIO.new
14
- $stderr = capture_stderr
10
+ def suppress_chdir_warning
11
+ old_verbose = $VERBOSE
12
+ $VERBOSE = nil
15
13
  yield
16
14
  ensure
17
- if old_stderr
18
- $stderr = old_stderr
19
- capture_string = capture_stderr.string
20
- warn capture_string if capture_string.each_line.count > 1 || !capture_string.include?("conflicting chdir")
21
- end
15
+ $VERBOSE = old_verbose
22
16
  end
23
17
 
24
18
  def call(env)
25
19
  line = @line.sub("cd", "").strip
26
20
  @io.puts "running $ cd #{line}"
27
21
 
28
- supress_chdir_warning do
22
+ suppress_chdir_warning do
29
23
  Dir.chdir(line)
30
24
  end
31
25
 
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+
5
+ class EmptyBinding
6
+ def self.create
7
+ new.empty_binding
8
+ end
9
+
10
+ def empty_binding
11
+ binding
12
+ end
13
+ end
14
+
15
+ module Rundoc::CodeCommand
16
+ RUNDOC_ERB_BINDINGS = Hash.new { |h, k| h[k] = EmptyBinding.create }
17
+ RUNDOC_DEFAULT_ERB_BINDING = "default"
18
+ end
@@ -1,21 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "erb"
4
-
5
- class EmptyBinding
6
- def self.create
7
- new.empty_binding
8
- end
9
-
10
- def empty_binding
11
- binding
12
- end
13
- end
3
+ require_relative "../empty_binding"
14
4
 
15
5
  module Rundoc::CodeCommand
16
- RUNDOC_ERB_BINDINGS = Hash.new { |h, k| h[k] = EmptyBinding.create }
17
- RUNDOC_DEFAULT_ERB_BINDING = "default"
18
-
19
6
  class PrintERBArgs
20
7
  attr_reader :line, :binding_name
21
8
 
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ::Rundoc::CodeCommand
4
+ class RundocCommand
5
+ class EnsureLaterArgs
6
+ MAPPING = {
7
+ cwd: ->(context:) {
8
+ Dir.pwd
9
+ },
10
+ rundoc_root: ->(context:) {
11
+ context.output_dir.to_s
12
+ }
13
+ }.freeze
14
+
15
+ def initialize(dir:)
16
+ @dir = dir
17
+ @logic = MAPPING[dir] or raise ArgumentError, "Invalid argument dir: #{dir} must be one of #{MAPPING.keys}"
18
+ end
19
+
20
+ def call(context:)
21
+ @logic.call(context: context)
22
+ end
23
+
24
+ def to_s
25
+ @dir
26
+ end
27
+ end
28
+
29
+ class EnsureLaterRunner
30
+ attr_reader :io, :contents
31
+
32
+ def initialize(user_args:, render_command:, render_result:, io:, contents: nil)
33
+ @io = io
34
+ @contents = contents.dup if contents && !contents.empty?
35
+ @dir = user_args
36
+ @binding = RUNDOC_ERB_BINDINGS[RUNDOC_DEFAULT_ERB_BINDING]
37
+ end
38
+
39
+ def to_md(env = {})
40
+ ""
41
+ end
42
+
43
+ def call(env = {})
44
+ resolved_dir = @dir.call(context: env[:context])
45
+
46
+ io.puts "Registering ensure_later block (dir: #{@dir} => #{resolved_dir})"
47
+ Rundoc.add_ensure_later(dir: resolved_dir, code: @contents, binding: @binding)
48
+ ""
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ Rundoc.register_code_command(
55
+ keyword: :"rundoc.ensure_later",
56
+ args_klass: Rundoc::CodeCommand::RundocCommand::EnsureLaterArgs,
57
+ runner_klass: Rundoc::CodeCommand::RundocCommand::EnsureLaterRunner,
58
+ always_hidden: true
59
+ )
@@ -17,6 +17,7 @@ module ::Rundoc
17
17
  @io = io
18
18
  @contents = contents.dup if contents && !contents.empty?
19
19
  @contents = user_args.code + (@contents || +"")
20
+ @binding = RUNDOC_ERB_BINDINGS[RUNDOC_DEFAULT_ERB_BINDING]
20
21
  end
21
22
 
22
23
  def to_md(env = {})
@@ -25,7 +26,9 @@ module ::Rundoc
25
26
 
26
27
  def call(env = {})
27
28
  io.puts "Running: #{contents}"
28
- eval(contents) # rubocop:disable Security/Eval
29
+ Rundoc.capture_stdout_stderr(io) do
30
+ eval(contents, @binding) # rubocop:disable Security/Eval
31
+ end
29
32
  ""
30
33
  end
31
34
  end
@@ -36,3 +39,4 @@ Rundoc.register_code_command(keyword: :rundoc, args_klass: Rundoc::CodeCommand::
36
39
  Rundoc.register_code_command(keyword: :"rundoc.configure", args_klass: Rundoc::CodeCommand::RundocCommandArgs, runner_klass: Rundoc::CodeCommand::RundocCommandRunner)
37
40
 
38
41
  require "rundoc/code_command/rundoc/require"
42
+ require "rundoc/code_command/rundoc/ensure_later"
@@ -5,6 +5,7 @@ module Rundoc
5
5
  end
6
6
  end
7
7
 
8
+ require "rundoc/code_command/empty_binding"
8
9
  require "rundoc/code_command/deferred"
9
10
  require "rundoc/code_command/bash"
10
11
  require "rundoc/code_command/pipe"
@@ -39,9 +39,16 @@ module Rundoc
39
39
  ).as(:number)
40
40
  }
41
41
 
42
+ rule(:symbol) {
43
+ str(":") >> (
44
+ match("[a-zA-Z_]") >> match("[a-zA-Z0-9_]").repeat
45
+ ).as(:symbol)
46
+ }
47
+
42
48
  rule(:value) {
43
49
  string |
44
50
  number |
51
+ symbol |
45
52
  str("true").as(true) |
46
53
  str("false").as(false) |
47
54
  str("nil").as(:nil)
@@ -175,6 +182,7 @@ module Rundoc
175
182
  rule(true => simple(:tr)) { true }
176
183
  rule(false => simple(:fa)) { false }
177
184
  rule(string: simple(:st)) { st.to_s }
185
+ rule(symbol: simple(:sy)) { sy.to_s.to_sym }
178
186
 
179
187
  rule(number: simple(:nb)) {
180
188
  /[eE.]/.match?(nb) ? Float(nb) : Integer(nb)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rundoc
4
- VERSION = "5.0.0"
4
+ VERSION = "6.0.0"
5
5
  end
data/lib/rundoc.rb CHANGED
@@ -97,6 +97,43 @@ module Rundoc
97
97
  yield self
98
98
  end
99
99
 
100
+ def ensure_later_blocks
101
+ @ensure_later_blocks ||= []
102
+ end
103
+
104
+ def add_ensure_later(dir:, code:, binding:)
105
+ ensure_later_blocks << {dir: dir, code: code, binding: binding}
106
+ end
107
+
108
+ def run_ensure_later(io:)
109
+ errors = []
110
+ ensure_later_blocks.each do |block|
111
+ io.puts "Running ensure_later block in #{block[:dir]}:\n#{block[:code]}"
112
+ Dir.chdir(block[:dir]) do
113
+ capture_stdout_stderr(io) do
114
+ eval(block[:code], block[:binding]) # rubocop:disable Security/Eval
115
+ end
116
+ end
117
+ rescue => e
118
+ io.puts "ensure_later block failed in #{block[:dir]}: #{e.message}"
119
+ io.puts e.backtrace.join("\n")
120
+ errors << e
121
+ end
122
+ ensure_later_blocks.clear
123
+ errors
124
+ end
125
+
126
+ def capture_stdout_stderr(io)
127
+ old_stdout = $stdout
128
+ old_stderr = $stderr
129
+ $stdout = io
130
+ $stderr = io
131
+ yield
132
+ ensure
133
+ $stdout = old_stdout
134
+ $stderr = old_stderr
135
+ end
136
+
100
137
  def filter_sensitive(sensitive)
101
138
  raise "Expecting #{sensitive} to be a hash" unless sensitive.is_a?(Hash)
102
139
  @sensitive ||= {}
data/rundoc.gemspec CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |gem|
26
26
 
27
27
  gem.add_dependency "aws-sdk-s3", "~> 1"
28
28
  gem.add_dependency "dotenv"
29
+ gem.add_dependency "cgi", ">= 0.3.6"
29
30
 
30
31
  gem.add_development_dependency "rake"
31
32
  gem.add_development_dependency "mocha"
@@ -0,0 +1,335 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class IntegrationEnsureLaterTest < Minitest::Test
6
+ def test_runs_on_success
7
+ Dir.mktmpdir do |dir|
8
+ Dir.chdir(dir) do
9
+ dir = Pathname(dir)
10
+ marker = dir.join("ensure_ran.txt")
11
+
12
+ source_path = dir.join("RUNDOC.md")
13
+ source_path.write <<~EOF
14
+ ```
15
+ :::-- rundoc.ensure_later(dir: :cwd)
16
+ File.write("#{marker}", "yes")
17
+ ```
18
+
19
+ ```
20
+ :::>> $ echo "hello"
21
+ ```
22
+ EOF
23
+
24
+ Rundoc::CLI.new(
25
+ io: StringIO.new,
26
+ source_path: source_path,
27
+ on_success_dir: dir.join(SUCCESS_DIRNAME)
28
+ ).call
29
+
30
+ assert marker.exist?, "ensure_later block should have run on success"
31
+ assert_equal "yes", marker.read
32
+ end
33
+ end
34
+ end
35
+
36
+ def test_runs_on_failure
37
+ Dir.mktmpdir do |dir|
38
+ Dir.chdir(dir) do
39
+ dir = Pathname(dir)
40
+ marker = dir.join("ensure_ran.txt")
41
+
42
+ source_path = dir.join("RUNDOC.md")
43
+ source_path.write <<~EOF
44
+ ```
45
+ :::-- rundoc.ensure_later(dir: :cwd)
46
+ File.write("#{marker}", "cleaned")
47
+ ```
48
+
49
+ ```
50
+ :::>> $ exit 1
51
+ ```
52
+ EOF
53
+
54
+ assert_raises do
55
+ Rundoc::CLI.new(
56
+ io: StringIO.new,
57
+ source_path: source_path,
58
+ on_success_dir: dir.join(SUCCESS_DIRNAME),
59
+ on_failure_dir: dir.join(FAILURE_DIRNAME)
60
+ ).call
61
+ end
62
+
63
+ assert marker.exist?, "ensure_later block should have run on failure"
64
+ assert_equal "cleaned", marker.read
65
+ end
66
+ end
67
+ end
68
+
69
+ def test_multiple_blocks_run_in_order
70
+ Dir.mktmpdir do |dir|
71
+ Dir.chdir(dir) do
72
+ dir = Pathname(dir)
73
+ marker = dir.join("order.txt")
74
+
75
+ source_path = dir.join("RUNDOC.md")
76
+ source_path.write <<~EOF
77
+ ```
78
+ :::-- rundoc.ensure_later(dir: :cwd)
79
+ File.write("#{marker}", "first")
80
+ ```
81
+
82
+ ```
83
+ :::-- rundoc.ensure_later(dir: :cwd)
84
+ File.write("#{marker}", File.read("#{marker}") + ",second")
85
+ ```
86
+
87
+ ```
88
+ :::>> $ echo "hello"
89
+ ```
90
+ EOF
91
+
92
+ Rundoc::CLI.new(
93
+ io: StringIO.new,
94
+ source_path: source_path,
95
+ on_success_dir: dir.join(SUCCESS_DIRNAME)
96
+ ).call
97
+
98
+ assert_equal "first,second", marker.read
99
+ end
100
+ end
101
+ end
102
+
103
+ def test_one_failure_does_not_stop_others
104
+ Dir.mktmpdir do |dir|
105
+ Dir.chdir(dir) do
106
+ dir = Pathname(dir)
107
+ marker = dir.join("second_ran.txt")
108
+
109
+ source_path = dir.join("RUNDOC.md")
110
+ source_path.write <<~EOF
111
+ ```
112
+ :::-- rundoc.ensure_later(dir: :cwd)
113
+ raise "intentional failure"
114
+ ```
115
+
116
+ ```
117
+ :::-- rundoc.ensure_later(dir: :cwd)
118
+ File.write("#{marker}", "yes")
119
+ ```
120
+
121
+ ```
122
+ :::>> $ echo "hello"
123
+ ```
124
+ EOF
125
+
126
+ error = assert_raises(RuntimeError) do
127
+ Rundoc::CLI.new(
128
+ io: StringIO.new,
129
+ source_path: source_path,
130
+ on_success_dir: dir.join(SUCCESS_DIRNAME)
131
+ ).call
132
+ end
133
+ assert_match(/intentional failure/, error.message)
134
+
135
+ assert marker.exist?, "second ensure_later should still run after first fails"
136
+ end
137
+ end
138
+ end
139
+
140
+ def test_success_plus_ensure_failure_is_overall_failure
141
+ Dir.mktmpdir do |dir|
142
+ Dir.chdir(dir) do
143
+ dir = Pathname(dir)
144
+
145
+ source_path = dir.join("RUNDOC.md")
146
+ source_path.write <<~EOF
147
+ ```
148
+ :::-- rundoc.ensure_later(dir: :cwd)
149
+ raise "cleanup failed"
150
+ ```
151
+
152
+ ```
153
+ :::>> $ echo "hello"
154
+ ```
155
+ EOF
156
+
157
+ error = assert_raises(RuntimeError) do
158
+ Rundoc::CLI.new(
159
+ io: StringIO.new,
160
+ source_path: source_path,
161
+ on_success_dir: dir.join(SUCCESS_DIRNAME)
162
+ ).call
163
+ end
164
+
165
+ assert_match(/cleanup failed/, error.message)
166
+ end
167
+ end
168
+ end
169
+
170
+ def test_dir_rundoc_root
171
+ Dir.mktmpdir do |dir|
172
+ Dir.chdir(dir) do
173
+ dir = Pathname(dir)
174
+ marker = dir.join("root_marker.txt")
175
+
176
+ source_path = dir.join("RUNDOC.md")
177
+ source_path.write <<~EOF
178
+ ```
179
+ :::>> $ mkdir subdir
180
+ :::>> $ cd subdir
181
+ ```
182
+
183
+ ```
184
+ :::-- rundoc.ensure_later(dir: :rundoc_root)
185
+ File.write("#{marker}", Dir.pwd)
186
+ ```
187
+
188
+ ```
189
+ :::>> $ echo "hello"
190
+ ```
191
+ EOF
192
+
193
+ cli = Rundoc::CLI.new(
194
+ io: StringIO.new,
195
+ source_path: source_path,
196
+ on_success_dir: dir.join(SUCCESS_DIRNAME)
197
+ )
198
+ output_dir = cli.execution_context.output_dir.realpath.to_s
199
+
200
+ cli.call
201
+
202
+ assert marker.exist?, "ensure_later with dir: :rundoc_root should run"
203
+ assert_equal output_dir, marker.read
204
+ end
205
+ end
206
+ end
207
+
208
+ def test_output_not_in_document
209
+ Dir.mktmpdir do |dir|
210
+ Dir.chdir(dir) do
211
+ dir = Pathname(dir)
212
+
213
+ source_path = dir.join("RUNDOC.md")
214
+ source_path.write <<~EOF
215
+ ```
216
+ :::-- rundoc.ensure_later(dir: :cwd)
217
+ puts "THIS SHOULD NOT APPEAR"
218
+ ```
219
+
220
+ ```
221
+ :::>> $ echo "visible"
222
+ ```
223
+ EOF
224
+
225
+ Rundoc::CLI.new(
226
+ io: StringIO.new,
227
+ source_path: source_path,
228
+ on_success_dir: dir.join(SUCCESS_DIRNAME)
229
+ ).call
230
+
231
+ readme = dir.join(SUCCESS_DIRNAME).join("README.md").read
232
+ refute_match(/THIS SHOULD NOT APPEAR/, readme)
233
+ assert_match(/visible/, readme)
234
+ end
235
+ end
236
+ end
237
+
238
+ def test_invalid_dir_raises
239
+ Dir.mktmpdir do |dir|
240
+ Dir.chdir(dir) do
241
+ dir = Pathname(dir)
242
+
243
+ source_path = dir.join("RUNDOC.md")
244
+ source_path.write <<~EOF
245
+ ```
246
+ :::-- rundoc.ensure_later(dir: :invalid)
247
+ puts "should not run"
248
+ ```
249
+ EOF
250
+
251
+ assert_raises(ArgumentError) do
252
+ Rundoc::CLI.new(
253
+ io: StringIO.new,
254
+ source_path: source_path,
255
+ on_success_dir: dir.join(SUCCESS_DIRNAME)
256
+ ).call
257
+ end
258
+ end
259
+ end
260
+ end
261
+
262
+ def test_shared_binding_with_rundoc
263
+ Dir.mktmpdir do |dir|
264
+ Dir.chdir(dir) do
265
+ dir = Pathname(dir)
266
+ marker = dir.join("binding_test.txt")
267
+
268
+ source_path = dir.join("RUNDOC.md")
269
+ source_path.write <<~EOF
270
+ ```
271
+ :::-- rundoc
272
+ def my_helper
273
+ "from_rundoc"
274
+ end
275
+ ```
276
+
277
+ ```
278
+ :::-- rundoc.ensure_later(dir: :cwd)
279
+ File.write("#{marker}", my_helper)
280
+ ```
281
+
282
+ ```
283
+ :::>> $ echo "hello"
284
+ ```
285
+ EOF
286
+
287
+ Rundoc::CLI.new(
288
+ io: StringIO.new,
289
+ source_path: source_path,
290
+ on_success_dir: dir.join(SUCCESS_DIRNAME)
291
+ ).call
292
+
293
+ assert_equal "from_rundoc", marker.read
294
+ end
295
+ end
296
+ end
297
+
298
+ def test_runs_on_failure_from_subdirectory
299
+ Dir.mktmpdir do |dir|
300
+ Dir.chdir(dir) do
301
+ dir = Pathname(dir)
302
+ marker = dir.join("ensure_ran.txt")
303
+
304
+ source_path = dir.join("RUNDOC.md")
305
+ source_path.write <<~EOF
306
+ ```
307
+ :::>> $ mkdir myapp
308
+ :::>> $ cd myapp
309
+ ```
310
+
311
+ ```
312
+ :::-- rundoc.ensure_later(dir: :cwd)
313
+ File.write("#{marker}", "cleaned")
314
+ ```
315
+
316
+ ```
317
+ :::>> $ exit 1
318
+ ```
319
+ EOF
320
+
321
+ assert_raises do
322
+ Rundoc::CLI.new(
323
+ io: StringIO.new,
324
+ source_path: source_path,
325
+ on_success_dir: dir.join(SUCCESS_DIRNAME),
326
+ on_failure_dir: dir.join(FAILURE_DIRNAME)
327
+ ).call
328
+ end
329
+
330
+ assert marker.exist?, "ensure_later from subdirectory should run even on failure"
331
+ assert_equal "cleaned", marker.read
332
+ end
333
+ end
334
+ end
335
+ end
@@ -50,6 +50,57 @@ class IntegrationPrintTest < Minitest::Test
50
50
  end
51
51
  end
52
52
 
53
+ def test_rundoc_configure_defines_variable_accessible_from_erb
54
+ key = SecureRandom.hex
55
+ contents = <<~RUBY
56
+ ```
57
+ :::-- rundoc.configure
58
+ @shared_value = "#{key}"
59
+ ```
60
+
61
+ ```
62
+ :::-> print.erb
63
+ <%= @shared_value %>
64
+ ```
65
+ RUBY
66
+
67
+ Dir.mktmpdir do |dir|
68
+ Dir.chdir(dir) do
69
+ parsed = parse_contents(contents)
70
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
71
+ assert_includes actual, key
72
+ end
73
+ end
74
+ end
75
+
76
+ def test_erb_defines_variable_accessible_from_rundoc_configure
77
+ key = SecureRandom.hex
78
+ contents = <<~RUBY
79
+ ```
80
+ :::-> print.erb
81
+ <% @from_erb = "#{key}" %>
82
+ ```
83
+
84
+ ```
85
+ :::-- rundoc.configure
86
+ @roundtripped = @from_erb + "_via_configure"
87
+ ```
88
+
89
+ ```
90
+ :::-> print.erb
91
+ <%= @roundtripped %>
92
+ ```
93
+ RUBY
94
+
95
+ Dir.mktmpdir do |dir|
96
+ Dir.chdir(dir) do
97
+ parsed = parse_contents(contents)
98
+ actual = parsed.to_md.gsub(Rundoc::FencedCodeBlock::AUTOGEN_WARNING, "")
99
+ assert_includes actual, "#{key}_via_configure"
100
+ end
101
+ end
102
+ end
103
+
53
104
  def test_erb_in_block
54
105
  contents = <<~RUBY
55
106
  ```
@@ -367,4 +367,29 @@ class PegParserTest < Minitest::Test
367
367
  assert_equal :rundoc, actual.keyword
368
368
  assert_equal "first = 1 # comment\nsecond = 2".strip, actual.contents.strip
369
369
  end
370
+
371
+ def test_symbol_value
372
+ input = %(:cwd)
373
+ parser = Rundoc::PegParser.new.symbol
374
+ tree = parser.parse_with_debug(input)
375
+ actual = @transformer.apply(tree)
376
+ assert_equal :cwd, actual
377
+ end
378
+
379
+ def test_symbol_in_named_args
380
+ input = %(dir: :cwd)
381
+ parser = Rundoc::PegParser.new.named_args
382
+ tree = parser.parse_with_debug(input)
383
+ actual = @transformer.apply(tree)
384
+ assert_equal({dir: :cwd}, actual)
385
+ end
386
+
387
+ def test_symbol_in_method_call
388
+ input = %(rundoc.ensure_later(dir: :cwd))
389
+ parser = Rundoc::PegParser.new.method_call
390
+ tree = parser.parse_with_debug(input)
391
+ actual = @transformer.apply(tree)
392
+ assert_equal :"rundoc.ensure_later", actual.keyword
393
+ assert_equal({dir: :cwd}, actual.original_args)
394
+ end
370
395
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rundoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Schneeman
@@ -107,6 +107,20 @@ dependencies:
107
107
  - - ">="
108
108
  - !ruby/object:Gem::Version
109
109
  version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: cgi
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 0.3.6
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: 0.3.6
110
124
  - !ruby/object:Gem::Dependency
111
125
  name: rake
112
126
  requirement: !ruby/object:Gem::Requirement
@@ -212,6 +226,7 @@ files:
212
226
  - lib/rundoc/code_command/bash/cd.rb
213
227
  - lib/rundoc/code_command/comment.rb
214
228
  - lib/rundoc/code_command/deferred.rb
229
+ - lib/rundoc/code_command/empty_binding.rb
215
230
  - lib/rundoc/code_command/file_command/append.rb
216
231
  - lib/rundoc/code_command/file_command/remove.rb
217
232
  - lib/rundoc/code_command/no_such_command.rb
@@ -220,6 +235,7 @@ files:
220
235
  - lib/rundoc/code_command/print/erb.rb
221
236
  - lib/rundoc/code_command/print/text.rb
222
237
  - lib/rundoc/code_command/raw.rb
238
+ - lib/rundoc/code_command/rundoc/ensure_later.rb
223
239
  - lib/rundoc/code_command/rundoc/require.rb
224
240
  - lib/rundoc/code_command/rundoc_command.rb
225
241
  - lib/rundoc/code_command/website.rb
@@ -262,6 +278,7 @@ files:
262
278
  - test/fixtures/simple_git/rundoc.md
263
279
  - test/integration/after_build_test.rb
264
280
  - test/integration/background_stdin_test.rb
281
+ - test/integration/ensure_later_test.rb
265
282
  - test/integration/failure_test.rb
266
283
  - test/integration/pre_erb_test.rb
267
284
  - test/integration/print_test.rb
@@ -301,7 +318,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
301
318
  - !ruby/object:Gem::Version
302
319
  version: '0'
303
320
  requirements: []
304
- rubygems_version: 3.6.9
321
+ rubygems_version: 4.0.10
305
322
  specification_version: 4
306
323
  summary: RunDOC generates runable code from docs
307
324
  test_files: []