Class: CemAcpt::Utils::Shell::Output

Inherits:
Object
  • Object
show all
Defined in:
lib/cem_acpt/utils/shell.rb

Overview

Class to handle output from shell commands, allowing for real-time logging and buffering of output data. The Output class captures both stdout and stderr, and can be configured to combine them or keep them separate. It also supports logging debug messages if the provided output object supports it.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(output: $stdout, raise_on_fail: true, combine_out_err: true) ⇒ Output

Initializes a new Output instance.

Parameters:

  • output (IO) (defaults to: $stdout)

    An IO object that implements ‘:<<` to write the output of the command to in real time. Typically this is a Logger object. Defaults to $stdout. If the object responds to :debug, the command will be logged at the debug level.

  • raise_on_fail (Boolean) (defaults to: true)

    Whether to raise an error if writing to the output fails. Defaults to true.

  • combine_out_err (Boolean) (defaults to: true)

    Whether to combine the output and error streams into the output. If false, the stderr stream will not be written to the output stream or returned with the output string. Defaults to true.



48
49
50
51
52
53
54
55
# File 'lib/cem_acpt/utils/shell.rb', line 48

def initialize(output: $stdout, raise_on_fail: true, combine_out_err: true)
  @output = output
  validate_output_object!
  @buffer = []
  @closed = false
  @raise_on_fail = raise_on_fail
  @combine_out_err = combine_out_err
end

Instance Attribute Details

#log_titleString

Returns The log title used for logging, defaults to “Output”.

Returns:

  • (String)

    The log title used for logging, defaults to “Output”



68
69
70
# File 'lib/cem_acpt/utils/shell.rb', line 68

def log_title
  @log_title ||= "Output"
end

Instance Method Details

#closeObject

Closes the output, flushing any remaining buffered data. If raise_on_fail is true, any errors during flushing will be raised.



133
134
135
136
# File 'lib/cem_acpt/utils/shell.rb', line 133

def close
  flush unless @buffer.empty?
  @closed = true
end

#closed?Boolean

Returns Whether the output is closed.

Returns:

  • (Boolean)

    Whether the output is closed



73
74
75
# File 'lib/cem_acpt/utils/shell.rb', line 73

def closed?
  @closed
end

#debug(msg) ⇒ Object

Logs a debug message to the output if it supports :debug, otherwise writes it to the output stream.

Parameters:

  • msg (String)

    The message to log as debug



79
80
81
82
83
84
85
# File 'lib/cem_acpt/utils/shell.rb', line 79

def debug(msg)
  if @output.respond_to? :debug
    @output.debug(log_title) { msg }
  else
    write("DEBUG: #{msg}\n", :stderr)
  end
end

#flushObject

Flushes the buffer to the output stream in order. If combine_out_err is true, stderr data will be written to the same stream as stdout.



120
121
122
123
124
125
126
127
128
129
130
# File 'lib/cem_acpt/utils/shell.rb', line 120

def flush
  return if @buffer.empty?

  @buffer.each do |data_obj|
    data = data_obj.to_s
    stream = @combine_out_err ? :stdout : data_obj.stream
    @output << data if @output
    send(stream).write(data)
  end
  @buffer.clear
end

#stderrStringIO

Returns The StringIO object capturing stderr.

Returns:

  • (StringIO)

    The StringIO object capturing stderr



63
64
65
# File 'lib/cem_acpt/utils/shell.rb', line 63

def stderr
  @io_err ||= StringIO.new
end

#stdoutStringIO

Returns The StringIO object capturing stdout.

Returns:

  • (StringIO)

    The StringIO object capturing stdout



58
59
60
# File 'lib/cem_acpt/utils/shell.rb', line 58

def stdout
  @io_out ||= StringIO.new
end

#write(data, stream = :stdout) ⇒ Object Also known as: <<

Writes data to the output stream and captures it in the buffer. Handles both stdout and stderr based on the stream parameter.

Parameters:

  • data (String)

    The data to write to the output

  • stream (Symbol) (defaults to: :stdout)

    The stream to write to, either :stdout or :stderr. Defaults to :stdout.

Raises:

  • (IOError)


90
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
# File 'lib/cem_acpt/utils/shell.rb', line 90

def write(data, stream = :stdout)
  raise IOError, "Cannot write to closed output" if closed?
  begin
    validate_stream!(stream)
    newlines = data.count("\n")
    if newlines == 1 && data.end_with?("\n")
      data_obj = OutputData.new(data, stream)
      @buffer << data_obj
      flush
    elsif newlines > 0
      data.split("\n").each do |part|
        data_obj = OutputData.new("#{part}\n", stream)
        @buffer << data_obj
        flush
      end
    else
      @buffer << OutputData.new(data, stream)
    end
  rescue StandardError => e
    raise e if @raise_on_fail

    # If there's an error writing to the output, we still want to capture the output in the buffer and not lose it.
    msg = "Error writing to output: #{e.message}. Captured output: #{data}"
    @buffer << OutputData.new(msg, :stderr)
    flush
  end
end