Class: Caliph::CommandRunResult

Inherits:
Object
  • Object
show all
Defined in:
lib/caliph/command-run-result.rb

Overview

This is the Caliph handle on a run process - it can be used to send signals to running processes, wait for them to complete, get their exit status once they have, and watch their streams in either case.

Direct Known Subclasses

MockCommandResult

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pid, command, shell) ⇒ CommandRunResult

Returns a new instance of CommandRunResult.



6
7
8
9
10
11
12
13
14
15
# File 'lib/caliph/command-run-result.rb', line 6

def initialize(pid, command, shell)
  @command = command
  @pid = pid
  @shell = shell

  #####
  @process_status = nil
  @streams = {}
  @consume_timeout = nil
end

Instance Attribute Details

#commandObject (readonly)

Returns the value of attribute command.



19
20
21
# File 'lib/caliph/command-run-result.rb', line 19

def command
  @command
end

#consume_timeoutObject

Returns the value of attribute consume_timeout.



20
21
22
# File 'lib/caliph/command-run-result.rb', line 20

def consume_timeout
  @consume_timeout
end

#pidObject (readonly)

Returns the value of attribute pid.



19
20
21
# File 'lib/caliph/command-run-result.rb', line 19

def pid
  @pid
end

#process_statusObject (readonly)

Returns the value of attribute process_status.



17
18
19
# File 'lib/caliph/command-run-result.rb', line 17

def process_status
  @process_status
end

#streamsObject

Returns the value of attribute streams.



20
21
22
# File 'lib/caliph/command-run-result.rb', line 20

def streams
  @streams
end

Instance Method Details

#consume_buffers(readable) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/caliph/command-run-result.rb', line 151

def consume_buffers(readable)
  if not(readable.nil? or readable.empty?)
    readable.each do |io|
      begin
        while chunk = io.read_nonblock(4096)
          if @buffered_echo.nil?
            @shell.report chunk, false
          else
            @buffered_echo << chunk
          end
          @accumulators[io] <<  chunk
        end
      rescue IO::WaitReadable => ex
      rescue EOFError => ex
        @live_ioes.delete(io)
      end
    end
  end
end

#exit_codeexit_code? Also known as: exit_status

Returns:

  • (exit_code)

    the raw exit of the process

  • (nil)

    the process is still running



34
35
36
37
38
39
40
# File 'lib/caliph/command-run-result.rb', line 34

def exit_code
  if @process_status.nil?
    return nil
  else
    @process_status.exitstatus
  end
end

#format_streamsObject

Nicely formatted output of stdout and stderr - won’t be intermixed, which may be different than what you’d see live in the shell



63
64
65
66
# File 'lib/caliph/command-run-result.rb', line 63

def format_streams
  "stdout:#{stdout.nil? || stdout.empty? ? "[empty]\n" : "\n#{stdout}"}" +
  "stderr:#{stderr.nil? || stderr.empty? ? "[empty]\n" : "\n#{stderr}"}---"
end

#kill(signal = nil) ⇒ Object

Stop a running process. Sends SIGINT by default which about like hitting Control-C.

Parameters:

  • signal (defaults to: nil)

    the Unix signal to send to the process



81
82
83
84
85
# File 'lib/caliph/command-run-result.rb', line 81

def kill(signal = nil)
  Process.kill(signal || "INT", pid)
rescue Errno::ESRCH
  warn "Couldn't find process #{pid} to kill it"
end

#must_succeed!Object

Demands that the process succeed, or else raises and error



69
70
71
72
73
74
75
76
# File 'lib/caliph/command-run-result.rb', line 69

def must_succeed!
  case exit_code
  when 0
    return exit_code
  else
    fail "Command '#{@command.string_format}' failed with exit status #{exit_code}: \n#{format_streams}"
  end
end

#running?true, false

Check whether the process is still running

Returns:

  • (true)

    the process is still running

  • (false)

    the process has completed



46
47
48
# File 'lib/caliph/command-run-result.rb', line 46

def running?
  !@process_status.nil?
end

#stderrObject

Access the stderr of the process



28
29
30
# File 'lib/caliph/command-run-result.rb', line 28

def stderr
  @streams[2]
end

#stdoutObject

Access the stdout of the process



23
24
25
# File 'lib/caliph/command-run-result.rb', line 23

def stdout
  @streams[1]
end

#succeeded?Boolean Also known as: succeeds?

Confirm that the process exited with a successful exit code (i.e. 0). This is pretty reliable, but many applications return bad exit statuses - 0 when they failed, usually.

Returns:

  • (Boolean)


53
54
55
56
57
58
# File 'lib/caliph/command-run-result.rb', line 53

def succeeded?
  must_succeed!
  return true
rescue
  return false
end

#waitObject

Waits for the process to complete. If this takes longer that #consume_timeout, output on the process’s streams will be output via Shell#report - very useful when compilation or network transfers are taking a long time.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/caliph/command-run-result.rb', line 91

def wait
  @accumulators = {}
  waits = {}
  @buffered_echo = []

  ioes = streams.values
  ioes.each do |io|
    @accumulators[io] = []
    waits[io] = 3
  end
  begin_echoing = Time.now + (@consume_timeout || 3)

  @live_ioes = ioes.dup

  until @live_ioes.empty? do
    newpid, @process_status = Process.waitpid2(pid, Process::WNOHANG)

    unless @process_status.nil?
      consume_buffers(@live_ioes)
      break
    end

    timeout = 0

    if !@buffered_echo.nil?
      timeout = begin_echoing - Time.now
      if timeout < 0
        @shell.report ""
        @shell.report "Long running command output:"
        @shell.report @buffered_echo.join
        @buffered_echo = nil
      end
    end

    if timeout > 0
      result = IO::select(@live_ioes, [], @live_ioes, timeout)
    else
      result = IO::select(@live_ioes, [], @live_ioes, 1)
    end

    unless result.nil? #timeout
      readable, _writeable, errored = *result
      unless errored.empty?
        raise "Error on IO: #{errored.inspect}"
      end

      consume_buffers(readable)
    end
  end

  if @process_status.nil?
    newpid, @process_status = Process.waitpid2(pid)
  end

  ioes.each do |io|
    io.close
  end
  @streams = Hash[ioes.each_with_index.map{|io, index| [index + 1, @accumulators[io].join]}]
end