Class: ExecutedShellCommand

Inherits:
Object show all
Defined in:
lib/executed_shell_command.rb

Overview

ExecutedShellCommand wraps the execution of a shell command and captures:

  • The original command string

  • STDOUT and STDERR

  • Exit status (Process::Status and numeric exit code)

  • Success / failure convenience predicates

  • Start and finish timestamps and derived duration

  • PID of the spawned process

  • Optional environment and working directory

The command is executed automatically during initialization. The result is memoized and can be accessed immediately. Subsequent calls to ‘run` will re-execute by default (force: true), unless `force: false` is explicitly passed to use the memoized result.

Basic usage:

cmd = ExecutedShellCommand.new("ls -l /tmp")  # executes immediately
result = cmd.result                            # access memoized result
fresh  = cmd.run                              # re-executes (force: true by default)
cached = cmd.run(force: false)                # returns memoized result

Defined Under Namespace

Classes: Result

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(command, chdir: nil, env: {}) ⇒ ExecutedShellCommand

Returns a new instance of ExecutedShellCommand.



89
90
91
92
93
94
95
# File 'lib/executed_shell_command.rb', line 89

def initialize(command, chdir: nil, env: {})
  @command = command
  @chdir   = chdir
  @env     = env
  @result  = nil
  run # Execute command immediately during initialization
end

Instance Attribute Details

#chdirObject (readonly)

Returns the value of attribute chdir.



87
88
89
# File 'lib/executed_shell_command.rb', line 87

def chdir
  @chdir
end

#commandObject (readonly)

Returns the value of attribute command.



87
88
89
# File 'lib/executed_shell_command.rb', line 87

def command
  @command
end

#envObject (readonly)

Returns the value of attribute env.



87
88
89
# File 'lib/executed_shell_command.rb', line 87

def env
  @env
end

#resultObject (readonly)

Returns the value of attribute result.



87
88
89
# File 'lib/executed_shell_command.rb', line 87

def result
  @result
end

Instance Method Details

#durationObject



174
175
176
# File 'lib/executed_shell_command.rb', line 174

def duration
  fetch_result.duration
end

#exit_codeObject



162
163
164
# File 'lib/executed_shell_command.rb', line 162

def exit_code
  fetch_result.exit_code
end

#failure?Boolean

Returns:

  • (Boolean)


170
171
172
# File 'lib/executed_shell_command.rb', line 170

def failure?
  !fetch_result.success?
end

#fetch_resultObject

Return the memoized result. Since the command runs at initialization, this will always return the memoized result unless run(force: true) was called to update it.



148
149
150
# File 'lib/executed_shell_command.rb', line 148

def fetch_result
  @result
end

#finished_atObject



182
183
184
# File 'lib/executed_shell_command.rb', line 182

def finished_at
  fetch_result.finished_at
end

#pidObject



186
187
188
# File 'lib/executed_shell_command.rb', line 186

def pid
  fetch_result.pid
end

#run(force: true) ⇒ Object

Execute the command, capture all outputs, and return a Result.

By default (force: true), the command is executed again and the memoized Result is replaced.

If ‘force: false` is passed and a result already exists, the memoized Result is returned without re-executing.

Raises:

  • (ArgumentError)


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
# File 'lib/executed_shell_command.rb', line 106

def run(force: true)
  return @result if @result && !force

  raise ArgumentError, 'command cannot be nil' if command.nil?

  started_at  = Time.now
  stdout_str  = +''
  stderr_str  = +''
  status      = nil
  pid         = nil

  popen_args = env.empty? ? [command] : [env, command]
  popen_opts = chdir ? { chdir: chdir } : {}

  Open3.popen3(*popen_args, popen_opts) do |stdin, stdout, stderr, wait_thr|
    stdin.close
    stdout_str = stdout.read
    stderr_str = stderr.read
    pid        = wait_thr.pid
    status     = wait_thr.value
  end

  finished_at = Time.now

  @result = Result.new(
    command: command,
    stdout: stdout_str,
    stderr: stderr_str,
    status: status,
    started_at: started_at,
    finished_at: finished_at,
    pid: pid,
    env: env,
    chdir: chdir
  )
end

#started_atObject



178
179
180
# File 'lib/executed_shell_command.rb', line 178

def started_at
  fetch_result.started_at
end

#stderrObject



158
159
160
# File 'lib/executed_shell_command.rb', line 158

def stderr
  fetch_result.stderr
end

#stdoutObject

Convenience delegators to the last / memoized result:



154
155
156
# File 'lib/executed_shell_command.rb', line 154

def stdout
  fetch_result.stdout
end

#success?Boolean

Returns:

  • (Boolean)


166
167
168
# File 'lib/executed_shell_command.rb', line 166

def success?
  fetch_result.success?
end