Skip to content

Commit

Permalink
Make background tasks lazy
Browse files Browse the repository at this point in the history
Previously when a background task was defined it would perform work on initialization, this fails if the first task is not actually executed yet:

```term
:::>- pre.erb background.start('heroku run:inside <%= dyno_ps_name %> "bash"', wait: "$", timeout: 120, name: "heroku_run")
:::-- background.stdin_write("ls -lah", name: "heroku_run", wait: "$", timeout: 30)
:::-- background.stdin_write("exit", name: "heroku_run", wait: "exit")
:::-> background.stop(name: "heroku_run")
```

Gives:

```
/Users/rschneeman/.gem/ruby/3.3.1/gems/rundoc-4.1.1/lib/rundoc/code_command/background/process_spawn.rb:41:in `find': Could not find task with name "heroku_run", known task names: [] (RuntimeError)
	from /Users/rschneeman/.gem/ruby/3.3.1/gems/rundoc-4.1.1/lib/rundoc/code_command/background/stdin_write.rb:9:in `initialize'
	from /Users/rschneeman/.gem/ruby/3.3.1/gems/rundoc-4.1.1/lib/rundoc.rb:13:in `new'
	from /Users/rschneeman/.gem/ruby/3.3.1/gems/rundoc-4.1.1/lib/rundoc.rb:13:in `code_command_from_keyword'
	from /Users/rschneeman/.gem/ruby/3.3.1/gems/rundoc-4.1.1/lib/rundoc/peg_parser.rb:210:in `block in <class:PegTransformer>'
	from /Users/rschneeman/.gem/ruby/3.3.1/gems/parslet-2.0.0/lib/parslet/transform.rb:217:in `instance_eval'
	from /Users/rschneeman/.gem/ruby/3.3.1/gems/parslet-2.0.0/lib/parslet/transform.rb:217:in `call_on_match'
	from /Users/rschneeman/.gem/ruby/3.3.1/gems/parslet-2.0.0/lib/parslet/transform.rb:235:in `block in transform_elt'
	from /Users/rschneeman/.gem/ruby/3.3.1/gems/parslet-2.0.0/lib/parslet/transform.rb:232:in `each'
```

Because the `background.stdin_write` call to `new()` happens at parse time, but the `background.start` call to `new()` happens at runtime, (Because `pre.erb` is the command, and it hasn't yet created the start command and pushed it onto the stack).

This change makes the lookup for background tasks lazy, so as long as the task is started by execution (runtime) of the command it will succeed.
  • Loading branch information
schneems committed Dec 16, 2024
1 parent 29d9278 commit daa914b
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 26 deletions.
9 changes: 7 additions & 2 deletions lib/rundoc/code_command/background/log/clear.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
class Rundoc::CodeCommand::Background::Log
class Clear < Rundoc::CodeCommand
def initialize(name:)
@spawn = Rundoc::CodeCommand::Background::ProcessSpawn.find(name)
@name = name
@background = nil
end

def background
@background ||= Rundoc::CodeCommand::Background::ProcessSpawn.find(@name)
end

def to_md(env = {})
""
end

def call(env = {})
@spawn.log.truncate(0)
background.log.truncate(0)
""
end
end
Expand Down
9 changes: 7 additions & 2 deletions lib/rundoc/code_command/background/log/read.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
class Rundoc::CodeCommand::Background::Log
class Read < Rundoc::CodeCommand
def initialize(name:)
@spawn = Rundoc::CodeCommand::Background::ProcessSpawn.find(name)
@name = name
@background = nil
end

def background
@background ||= Rundoc::CodeCommand::Background::ProcessSpawn.find(@name)
end

def to_md(env = {})
""
end

def call(env = {})
@spawn.log.read
background.log.read
end
end
end
Expand Down
38 changes: 24 additions & 14 deletions lib/rundoc/code_command/background/start.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,45 @@
class Rundoc::CodeCommand::Background
class Start < Rundoc::CodeCommand
def initialize(command, name:, wait: nil, timeout: 5, log: Tempfile.new("log"), out: "2>&1", allow_fail: false)
@timeout = timeout
@command = command
@name = name
@wait = wait
@allow_fail = allow_fail
FileUtils.touch(log)

@spawn = ProcessSpawn.new(
@command,
timeout: timeout,
log: log,
out: out
)
puts "Spawning commmand: `#{@spawn.command}`"
ProcessSpawn.add(@name, @spawn)
@log = log
@redirect = out
FileUtils.touch(@log)

@background = nil
end

def background
@background ||= begin
ProcessSpawn.new(
@command,
timeout: @timeout,
log: @log,
out: @redirect
).tap do |spawn|
puts "Spawning commmand: `#{spawn.command}`"
ProcessSpawn.add(@name, spawn)
end
end
end

def to_md(env = {})
"$ #{@command}"
end

def call(env = {})
@spawn.wait(@wait)
@spawn.check_alive! unless @allow_fail
background.wait(@wait)
background.check_alive! unless @allow_fail

@spawn.log.read
background.log.read
end

def alive?
!!@spawn.alive?
!!background.alive?
end
end
end
Expand Down
11 changes: 8 additions & 3 deletions lib/rundoc/code_command/background/stdin_write.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ class StdinWrite < Rundoc::CodeCommand
def initialize(contents, name:, wait:, timeout: 5, ending: $/)
@contents = contents
@ending = ending
@spawn = Rundoc::CodeCommand::Background::ProcessSpawn.find(name)
@wait = wait
@name = name
@timeout_value = Integer(timeout)
@contents_written = nil
@background = nil
end

def background
@background ||= Rundoc::CodeCommand::Background::ProcessSpawn.find(@name)
end

# The command is rendered (`:::>-`) by the output of the `def call` method.
Expand All @@ -20,11 +25,11 @@ def to_md(env = {})
# The contents produced by the command (`:::->`) are rendered by the `def to_md` method.
def call(env = {})
writecontents
@spawn.log.read
background.log.read
end

def writecontents
@contents_written ||= @spawn.stdin_write(
@contents_written ||= background.stdin_write(
contents,
wait: @wait,
ending: @ending,
Expand Down
11 changes: 8 additions & 3 deletions lib/rundoc/code_command/background/stop.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
class Rundoc::CodeCommand::Background
class Stop < Rundoc::CodeCommand
def initialize(name:)
@spawn = Rundoc::CodeCommand::Background::ProcessSpawn.find(name)
@name = name
@background = nil
end

def background
@background ||= Rundoc::CodeCommand::Background::ProcessSpawn.find(@name)
end

def to_md(env = {})
""
end

def call(env = {})
@spawn.stop
@spawn.log.read
background.stop
background.log.read
end
end
end
Expand Down
9 changes: 7 additions & 2 deletions lib/rundoc/code_command/background/wait.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
class Rundoc::CodeCommand::Background
class Wait < Rundoc::CodeCommand
def initialize(name:, wait:, timeout: 5)
@spawn = Rundoc::CodeCommand::Background::ProcessSpawn.find(name)
@name = name
@wait = wait
@timeout_value = Integer(timeout)
@background = nil
end

def background
@background ||= Rundoc::CodeCommand::Background::ProcessSpawn.find(name)
end

def to_md(env = {})
""
end

def call(env = {})
@spawn.wait(@wait, @timeout_value)
background.wait(@wait, @timeout_value)
""
end
end
Expand Down

0 comments on commit daa914b

Please sign in to comment.