Class: LCR::Runner

Inherits:
Object
  • Object
show all
Defined in:
lib/long-command-runner/runner.rb

Overview

This class aims to manage an external process through a parallele excecution thread. This means it will not stop the main thread of your program.

Your process can output a progress indicator of the matching the format of PERCENT_INDICATOR constant.

It is possible to re-launch the same command, the out/err buffers won’t be emptied before running unless asked.

Constant Summary collapse

PERCENT_INDICATOR =

The constant used to get the percetange of completion of the command.

/(.*\s|^)((\d+)([,.]\d*)?)%.*/.freeze
TIMES_INDICATORS =

The constant to match a ‘time -p` output:

/\A(real|user|sys)\s+(\d+\.\d\d)\z/.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(command) {|nl_stdout, nl_stderr| ... } ⇒ Runner

Initializer takes the command as a plain string. You can optionaly pass a block that will be called when the command generate outputs:

Yields:

  • (nl_stdout, nl_stderr)

    call when at least a line comes on stdout or stderr.

Yield Parameters:

  • nl_stdout (String | nil)

    The new line without the return line character. This may be nil if only a new line has been found on stderr.

  • nl_stderr (String | nil)

    The new line without the return line character. This may be nil if only a new line has been found on stdout.

Yield Returns:

  • (void)

    It is ignored.



34
35
36
37
38
39
40
41
# File 'lib/long-command-runner/runner.rb', line 34

def initialize(command, &on_input)
  @command = command
  @container = nil
  @launch_lock = Mutex.new
  @on_input = on_input
  @progress = 0.0
  @times = { real: nil, user: nil, sys: nil }
end

Instance Attribute Details

#progressObject (readonly)

Get the mesured progress in percentage. This is just the result of parsing stdout lines with PERCENT_INDICATOR. So this percentage is comming from the thread not this library.



23
24
25
# File 'lib/long-command-runner/runner.rb', line 23

def progress
  @progress
end

Instance Method Details

#kill(signal) ⇒ Integer | nil

Send a signal to the running process.

Returns:

  • (Integer | nil)

    The number of signaled process (= 1) or nil if the Process is no more running.



118
119
120
121
122
123
124
# File 'lib/long-command-runner/runner.rb', line 118

def kill(signal)
  return nil if @container.nil?

  Process.kill(signal, @container.pid)
rescue Errno::ESRCH
  nil
end

#launchObject

Actually launch the process.



56
57
58
59
60
61
62
63
64
65
# File 'lib/long-command-runner/runner.rb', line 56

def launch
  stderr, stderr_in = IO.pipe
  @container = Container.new("bash -c 'time -p #{@command} 2>&3'", 3 => stderr_in)
  @line_reader = LineReader.new([@container.stdout, stderr, @container.stderr]) do |*new_lines|
    on_newline(*new_lines)
  end
  @reader_thr = Thread.new { @line_reader.read }
  stderr_in.close
  Thread.pass
end

#launched?Boolean

Tells if the command has been launched at least once.

Returns:

  • (Boolean)


51
52
53
# File 'lib/long-command-runner/runner.rb', line 51

def launched?
  !@container.nil?
end

#output(separator = "\n") ⇒ String

Get the output lines as separated lines

Parameters:

  • separator (String) (defaults to: "\n")

    the separator to put between lines. by default it will be ‘“n”`

Returns:

  • (String)


73
74
75
# File 'lib/long-command-runner/runner.rb', line 73

def output(separator = "\n")
  @line_reader[0].join(separator)
end

#output_error(separator = "\n") ⇒ String

Get the error output lines as separated lines

Parameters:

  • separator (String) (defaults to: "\n")

    the separator to put between lines. by default it will be ‘“n”`

Returns:

  • (String)


83
84
85
# File 'lib/long-command-runner/runner.rb', line 83

def output_error(separator = "\n")
  @line_reader[1].join(separator)
end

#pidInteger | nil

Get the pid of the process launched

Returns:

  • (Integer | nil)


90
91
92
# File 'lib/long-command-runner/runner.rb', line 90

def pid
  @container.pid
end

#running?Boolean

Is the last launched command is still running.

Returns:

  • (Boolean)


44
45
46
47
48
# File 'lib/long-command-runner/runner.rb', line 44

def running?
  return false if @container.nil?

  @container.running?
end

#statusProcess:Status | nil

Get the status of the process without blocking.

Returns:

  • (Process:Status | nil)

    The exit status of the process if it is finished. if the Process isn’t finished it return nil.



108
109
110
111
112
# File 'lib/long-command-runner/runner.rb', line 108

def status
  return nil if @container.nil?

  @container.status
end

#time_realFloat | nil

Get the time really spend (‘real’ part of ‘time` command)

Returns:

  • (Float | nil)

    The elapsed seconds (using POSIX.2 standard see ‘time -p` for more information) if the Process isn’t finished it return nil.



130
131
132
# File 'lib/long-command-runner/runner.rb', line 130

def time_real
  @times[:real]
end

#time_sysFloat | nil

Get the time system space spend (‘sys’ part of ‘time` command)

Returns:

  • (Float | nil)

    The elapsed seconds (using POSIX.2 standard see ‘time -p` for more information) if the Process isn’t finished it return nil.



146
147
148
# File 'lib/long-command-runner/runner.rb', line 146

def time_sys
  @times[:sys]
end

#time_userFloat | nil

Get the time in user space spend (‘user’ part of ‘time` command)

Returns:

  • (Float | nil)

    The elapsed seconds (using POSIX.2 standard see ‘time -p` for more information) if the Process isn’t finished it return nil.



138
139
140
# File 'lib/long-command-runner/runner.rb', line 138

def time_user
  @times[:user]
end

#waitProcess::Status

Wait and return the process exit status. This method is blocking until the process if finished.

Returns:

  • (Process::Status)


98
99
100
101
102
# File 'lib/long-command-runner/runner.rb', line 98

def wait
  p = @container.wait
  sleep 0.01 until @line_reader.streams.all?(&:eof?)
  p
end