Class: Rundoc::CodeCommand::Background::ProcessSpawn
- Inherits:
-
Object
- Object
- Rundoc::CodeCommand::Background::ProcessSpawn
- Defined in:
- lib/rundoc/code_command/background/process_spawn.rb
Overview
This class is responsible for running processes in the background
By default it logs output to a file. This can be used to “wait” for a specific output before continuing:
server = ProcessSpawn("rails server")
server.wait("Use Ctrl-C to stop")
The process can be queried for it’s status to check if it is still booted or not. the process can also be manually stopped:
server = ProcessSpawn("rails server")
server.alive? # => true
server.stop
server.alive? # => false
There are class level methods that can be used to “name” and record background processes. They can be used like this:
server = ProcessSpawn("rails server")
ProcessSpawn.add("muh_server", server)
ProcessSpawn.find("muh_server") # => <# ProcessSpawn instance >
ProcessSpawn.find("foo") # => RuntimeError "Could not find task with name 'foo', ..."
Class Attribute Summary collapse
-
.tasks ⇒ Object
readonly
Returns the value of attribute tasks.
Instance Attribute Summary collapse
-
#command ⇒ Object
readonly
Returns the value of attribute command.
-
#log ⇒ Object
readonly
Returns the value of attribute log.
-
#pid ⇒ Object
readonly
Returns the value of attribute pid.
Class Method Summary collapse
Instance Method Summary collapse
- #alive? ⇒ Boolean
- #check_alive! ⇒ Object
-
#initialize(command, timeout: 5, log: Tempfile.new("log"), out: "2>&1") ⇒ ProcessSpawn
constructor
A new instance of ProcessSpawn.
-
#stdin_write(contents, ending: $/, timeout: timeout_value, wait: nil) ⇒ Object
Writes the contents along with an optional ending character to the STDIN of the backtround process.
- #stop(print_io: nil) ⇒ Object
-
#wait(wait_value = nil, timeout_value = @timeout_value, file: @log) ⇒ Object
Wait until a given string is found in the logs.
Constructor Details
#initialize(command, timeout: 5, log: Tempfile.new("log"), out: "2>&1") ⇒ ProcessSpawn
Returns a new instance of ProcessSpawn.
49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 49 def initialize(command, timeout: 5, log: Tempfile.new("log"), out: "2>&1") @original_command = command @timeout_value = timeout @log_reference = log # Need to keep a reference to `Tempfile` or it will be deleted. Pathname does not retain the passed in reference @log = Pathname.new(log) @log.dirname.mkpath FileUtils.touch(@log) @pipe_output, @pipe_input = IO.pipe @command = "/usr/bin/env bash -c #{@original_command.shellescape} >> #{@log} #{out}" @pid = nil end |
Class Attribute Details
.tasks ⇒ Object (readonly)
Returns the value of attribute tasks.
33 34 35 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 33 def tasks @tasks end |
Instance Attribute Details
#command ⇒ Object (readonly)
Returns the value of attribute command.
47 48 49 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 47 def command @command end |
#log ⇒ Object (readonly)
Returns the value of attribute log.
47 48 49 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 47 def log @log end |
#pid ⇒ Object (readonly)
Returns the value of attribute pid.
47 48 49 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 47 def pid @pid end |
Class Method Details
.add(name, value) ⇒ Object
37 38 39 40 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 37 def self.add(name, value) raise "Task named #{name.inspect} is already started, choose a different name" if @tasks[name] @tasks[name] = value end |
.find(name) ⇒ Object
42 43 44 45 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 42 def self.find(name) raise "Could not find task with name #{name.inspect}, known task names: #{@tasks.keys.inspect}" unless @tasks[name] @tasks[name] end |
Instance Method Details
#alive? ⇒ Boolean
89 90 91 92 93 94 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 89 def alive? return false unless @pid Process.kill(0, @pid) rescue Errno::ESRCH, Errno::EPERM false end |
#check_alive! ⇒ Object
136 137 138 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 136 def check_alive! raise "#{@original_command} has exited unexpectedly: #{@log.read}" unless alive? end |
#stdin_write(contents, ending: $/, timeout: timeout_value, wait: nil) ⇒ Object
Writes the contents along with an optional ending character to the STDIN of the backtround process
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 107 def stdin_write(contents, ending: $/, timeout: timeout_value, wait: nil) log_file = File.new(@log) before_write_bytes = log_file.size begin Timeout.timeout(Integer(timeout)) do @pipe_input.print(contents + ending) @pipe_input.flush end rescue Timeout::Error raise "Timeout (#{timeout}s) waiting to write #{contents} to stdin. Log contents:\n'#{log.read}'" end # Ignore bytes written before we sent the STDIN message log_file.seek(before_write_bytes) wait(wait, timeout, file: log_file) contents end |
#stop(print_io: nil) ⇒ Object
125 126 127 128 129 130 131 132 133 134 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 125 def stop(print_io: nil) return unless alive? @pipe_input.close Process.kill("TERM", -Process.getpgid(@pid)) Process.wait(@pid) rescue Errno::ESRCH => e print_io&.puts "Error stopping process (command: #{command}): #{e}" ensure print_io&.puts "Log contents for `#{command}`:\n#{@log.read}" end |
#wait(wait_value = nil, timeout_value = @timeout_value, file: @log) ⇒ Object
Wait until a given string is found in the logs
If the string is not found within the timeout, a Timeout::Error is raised
Caution: The logs will not be cleared before waiting, so if the string is already present from a prior operation, then it will not wait at all.
To ensure you’re waiting for a brand new string, call ‘log.truncate(0)` first. which is accessible via `:::– background.log.clear` in rundoc syntax.
76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/rundoc/code_command/background/process_spawn.rb', line 76 def wait(wait_value = nil, timeout_value = @timeout_value, file: @log) call return unless wait_value Timeout.timeout(Integer(timeout_value)) do until file.read.include?(wait_value) sleep 0.01 end end rescue Timeout::Error raise "Timeout (#{timeout_value}s) waiting for #{@command.inspect} to find a match using #{wait_value.inspect} in \n'#{log.read}'" end |